diff --git a/krArc/krarc.cpp b/krArc/krarc.cpp index 8d760a22..275ac8f2 100644 --- a/krArc/krarc.cpp +++ b/krArc/krarc.cpp @@ -1,1932 +1,1932 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2004-2018 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 "krarc.h" #include "../krusader/defaults.h" // QtCore #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_IPC_SIZE (1024*32) #define TRIES_WITH_PASSWORDS 3 using namespace KIO; extern "C" { #ifdef KRARC_ENABLED /* This codec is for being able to handle files which encoding differs from the current locale. * * Unfortunately QProcess requires QString parameters for arguments which are encoded to Local8Bit * If we want to use unzip with ISO-8852-2 when the current locale is UTF-8, it will cause problems. * * Workaround: * 1. encode the QString to QByteArray ( according to the selected remote encoding, ISO-8852-2 ) * 2. encode QByteArray to QString again * unicode 0xE000-0xF7FF is for private use * the byte array is mapped to 0xE000-0xE0FF unicodes * 3. KrArcCodec maps 0xE000-0xE0FF to 0x0000-0x00FF, while calls the default encoding routine * for other unicodes. */ class KrArcCodec : public QTextCodec { public: KrArcCodec(QTextCodec * codec) : originalCodec(codec) {} ~KrArcCodec() override = default; QByteArray name() const Q_DECL_OVERRIDE { return originalCodec->name(); } QList aliases() const Q_DECL_OVERRIDE { return originalCodec->aliases(); } int mibEnum() const Q_DECL_OVERRIDE { return originalCodec->mibEnum(); } protected: QString convertToUnicode(const char *in, int length, ConverterState *state) const Q_DECL_OVERRIDE { return originalCodec->toUnicode(in, length, state); } QByteArray convertFromUnicode(const QChar *in, int length, ConverterState *state) const Q_DECL_OVERRIDE { // the QByteArray is embedded into the unicode charset (QProcess hell) QByteArray result; for (int i = 0; i != length; i++) { if (((in[ i ].unicode()) & 0xFF00) == 0xE000) // map 0xE000-0xE0FF to 0x0000-0x00FF result.append((char)(in[ i ].unicode() & 0xFF)); else result.append(originalCodec->fromUnicode(in + i, 1, state)); } return result; } private: QTextCodec * originalCodec; } *krArcCodec; #define SET_KRCODEC QTextCodec *origCodec = QTextCodec::codecForLocale(); \ QTextCodec::setCodecForLocale( krArcCodec ); #define RESET_KRCODEC QTextCodec::setCodecForLocale( origCodec ); #endif // KRARC_ENABLED class DummySlave : public KIO::SlaveBase { public: DummySlave(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_krarc", pool_socket, app_socket) { error((int)ERR_SLAVE_DEFINED, QString("krarc is disabled.")); } }; int Q_DECL_EXPORT kdemain(int argc, char **argv) { if (argc != 4) { qWarning() << "Usage: kio_krarc protocol domain-socket1 domain-socket2" << endl; exit(-1); } #ifdef KRARC_ENABLED kio_krarcProtocol slave(argv[2], argv[3]); #else DummySlave slave(argv[2], argv[3]); #endif slave.dispatchLoop(); return 0; } } // extern "C" #ifdef KRARC_ENABLED kio_krarcProtocol::kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_krarc", pool_socket, app_socket), archiveChanged(true), arcFile(nullptr), extArcReady(false), password(QString()), krConf("krusaderrc"), codec(nullptr) { KRFUNC; confGrp = KConfigGroup(&krConf, "Dependencies"); KConfigGroup group(&krConf, "General"); QString tmpDirPath = group.readEntry("Temp Directory", _TempDirectory); QDir tmpDir(tmpDirPath); if(!tmpDir.exists()) { for (int i = 1 ; i != -1 ; i = tmpDirPath.indexOf('/', i + 1)) QDir().mkdir(tmpDirPath.left(i)); QDir().mkdir(tmpDirPath); } arcTempDir = tmpDirPath + DIR_SEPARATOR; QString dirName = "krArc" + QDateTime::currentDateTime().toString(Qt::ISODate); dirName.replace(QRegExp(":"), "_"); tmpDir.mkdir(dirName); arcTempDir = arcTempDir + dirName + DIR_SEPARATOR; krArcCodec = new KrArcCodec(QTextCodec::codecForLocale()); } /* ---------------------------------------------------------------------------------- */ kio_krarcProtocol::~kio_krarcProtocol() { KRFUNC; // delete the temp directory KProcess proc; proc << fullPathName("rm") << "-rf" << arcTempDir; proc.start(); proc.waitForFinished(); } /* ---------------------------------------------------------------------------------- */ bool kio_krarcProtocol::checkWriteSupport() { KRFUNC; krConf.reparseConfiguration(); if (KConfigGroup(&krConf, "kio_krarc").readEntry("EnableWrite", false)) return true; else { error(ERR_UNSUPPORTED_ACTION, i18n("krarc: write support is disabled.\n" "You can enable it on the 'Archives' page in Konfigurator.")); return false; } } void kio_krarcProtocol::receivedData(KProcess *, QByteArray &d) { KRFUNC; QByteArray buf(d); data(buf); processedSize(d.length()); decompressedLen += d.length(); } void kio_krarcProtocol::mkdir(const QUrl &url, int permissions) { KRFUNC; const QString path = getPath(url); KRDEBUG(path); if (!checkWriteSupport()) return; // In case of KIO::mkpath call there is a mkdir call for every path element. // Therefore the path all the way up to our archive needs to be checked for existence // and reported as success. if (QDir().exists(path)) { finished(); return; } if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, path); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, path); return; } if (putCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Creating directories is not supported with %1 archives", arcType)); return; } const QString arcFilePath = getPath(arcFile->url()); if (arcType == "arj" || arcType == "lha") { QString arcDir = path.mid(arcFilePath.length()); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; if (dirDict.find(arcDir) == dirDict.end()) addNewDir(arcDir); finished(); return; } QString arcDir = findArcDirectory(url); QString tempDir = arcDir.mid(1) + path.mid(path.lastIndexOf(DIR_SEPARATOR) + 1); if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR; if (permissions == -1) permissions = 0777; //set default permissions QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit(); for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) { QByteArray newDirs = encodeString(tempDir.left(i)); newDirs.prepend(arcTempDirEnc); QT_MKDIR(newDirs, permissions); } if (tempDir.endsWith(DIR_SEPARATOR)) tempDir.truncate(tempDir.length() - 1); // pack the directory KrLinecountingProcess proc; proc << putCmd << arcFilePath << localeEncodedString(tempDir); infoMessage(i18n("Creating %1...", url.fileName())); QDir::setCurrent(arcTempDir); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); // delete the temp directory QDir().rmdir(arcTempDir); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, path + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags) { KRFUNC; KRDEBUG(getPath(url)); if (!checkWriteSupport()) return; bool overwrite = !!(flags & KIO::Overwrite); bool resume = !!(flags & KIO::Resume); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (putCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Writing to %1 archives is not supported", arcType)); return; } if (!overwrite && findFileEntry(url)) { error(ERR_FILE_ALREADY_EXIST, getPath(url)); return; } QString arcDir = findArcDirectory(url); if (arcDir.isEmpty()) KRDEBUG("arcDir is empty."); QString tempFile = arcDir.mid(1) + getPath(url).mid(getPath(url).lastIndexOf(DIR_SEPARATOR) + 1); QString tempDir = arcDir.mid(1); if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR; if (permissions == -1) permissions = 0777; //set default permissions QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit(); for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) { QByteArray newDirs = encodeString(tempDir.left(i)); newDirs.prepend(arcTempDirEnc); QT_MKDIR(newDirs, 0755); } int fd; if (resume) { QByteArray ba = encodeString(tempFile); ba.prepend(arcTempDirEnc); fd = QT_OPEN(ba, O_RDWR); // append if resuming QT_LSEEK(fd, 0, SEEK_END); // Seek to end } else { // WABA: Make sure that we keep writing permissions ourselves, // otherwise we can be in for a surprise on NFS. mode_t initialMode; if (permissions != -1) initialMode = permissions | S_IWUSR | S_IRUSR; else initialMode = 0666; QByteArray ba = encodeString(tempFile); ba.prepend(arcTempDirEnc); fd = QT_OPEN(ba, O_CREAT | O_TRUNC | O_WRONLY, initialMode); } QByteArray buffer; int readResult; bool isIncomplete = false; do { dataReq(); readResult = readData(buffer); auto bytesWritten = ::write(fd, buffer.data(), buffer.size()); if (bytesWritten < buffer.size()) { isIncomplete = true; break; } } while (readResult > 0); ::close(fd); if (isIncomplete) { error(ERR_COULD_NOT_WRITE, getPath(url)); return; } // pack the file KrLinecountingProcess proc; proc << putCmd << getPath(arcFile->url()) << localeEncodedString(tempFile); infoMessage(i18n("Packing %1...", url.fileName())); QDir::setCurrent(arcTempDir); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); // remove the files QDir().rmdir(arcTempDir); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::get(const QUrl &url) { KRFUNC; get(url, TRIES_WITH_PASSWORDS); } void kio_krarcProtocol::get(const QUrl &url, int tries) { KRFUNC; KRDEBUG(getPath(url)); bool decompressToFile = false; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (getCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Retrieving data from %1 archives is not supported", arcType)); return; } UDSEntry* entry = findFileEntry(url); if (!entry) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } if (KFileItem(*entry, url).isDir()) { error(KIO::ERR_IS_DIRECTORY, getPath(url)); return; } KIO::filesize_t expectedSize = KFileItem(*entry, url).size(); // for RPM files extract the cpio file first if (!extArcReady && arcType == "rpm") { KrLinecountingProcess cpio; cpio << "rpm2cpio" << getPath(arcFile->url(), QUrl::StripTrailingSlash); cpio.setStandardOutputFile(arcTempDir + "contents.cpio"); cpio.start(); cpio.waitForFinished(); if (cpio.exitStatus() != QProcess::NormalExit || !checkStatus(cpio.exitCode())) { error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + cpio.getErrorMsg()); return; } extArcReady = true; } // for DEB files extract the tar file first if (!extArcReady && arcType == "deb") { KrLinecountingProcess dpkg; dpkg << cmd << "--fsys-tarfile" << getPath(arcFile->url(), QUrl::StripTrailingSlash); dpkg.setStandardOutputFile(arcTempDir + "contents.cpio"); dpkg.start(); dpkg.waitForFinished(); if (dpkg.exitStatus() != QProcess::NormalExit || !checkStatus(dpkg.exitCode())) { error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + dpkg.getErrorMsg()); return; } extArcReady = true; } // Use the external unpacker to unpack the file QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); KrLinecountingProcess proc; if (extArcReady) { proc << getCmd << arcTempDir + "contents.cpio" << '*' + localeEncodedString(file); } else if (arcType == "arj" || arcType == "ace" || arcType == "7z") { proc << getCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << localeEncodedString(file); if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); file = url.fileName(); decompressToFile = true; } else { decompressedLen = 0; // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(arcTempDir + file); if (mt.isValid()) emit mimeType(mt.name()); QString escapedFilename = file; if(arcType == "zip") // left bracket needs to be escaped escapedFilename.replace('[', "[[]"); proc << getCmd << getPath(arcFile->url()); if (arcType != "gzip" && arcType != "bzip2" && arcType != "lzma" && arcType != "xz") proc << localeEncodedString(escapedFilename); connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::receivedData); proc.setMerge(false); } infoMessage(i18n("Unpacking %1...", url.fileName())); // change the working directory to our arcTempDir QDir::setCurrent(arcTempDir); SET_KRCODEC proc.setTextModeEnabled(false); proc.start(); RESET_KRCODEC proc.waitForFinished(); if (!extArcReady && !decompressToFile) { if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || (arcType != "bzip2" && arcType != "lzma" && arcType != "xz" && expectedSize != decompressedLen)) { if (encrypted && tries) { invalidatePassword(); get(url, tries - 1); return; } error(KIO::ERR_ACCESS_DENIED, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } } else { if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || !QFileInfo(arcTempDir + file).exists()) { if (decompressToFile) QFile(arcTempDir + file).remove(); if (encrypted && tries) { invalidatePassword(); get(url, tries - 1); return; } error(KIO::ERR_ACCESS_DENIED, getPath(url)); return; } // the following block is ripped from KDE file KIO::Slave // $Id: krarc.cpp,v 1.43 2007/01/13 13:39:51 ckarai Exp $ QByteArray _path(QFile::encodeName(arcTempDir + file)); QT_STATBUF buff; if (QT_LSTAT(_path.data(), &buff) == -1) { if (errno == EACCES) error(KIO::ERR_ACCESS_DENIED, getPath(url)); else error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } if (S_ISDIR(buff.st_mode)) { error(KIO::ERR_IS_DIRECTORY, getPath(url)); return; } if (!S_ISREG(buff.st_mode)) { error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url)); return; } int fd = QT_OPEN(_path.data(), O_RDONLY); if (fd < 0) { error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url)); return; } // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(arcTempDir + file); if (mt.isValid()) emit mimeType(mt.name()); KIO::filesize_t processed_size = 0; QString resumeOffset = metaData("resume"); if (!resumeOffset.isEmpty()) { bool ok; KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok); if (ok && (offset > 0) && (offset < buff.st_size)) { if (QT_LSEEK(fd, offset, SEEK_SET) == offset) { canResume(); processed_size = offset; } } } totalSize(buff.st_size); char buffer[ MAX_IPC_SIZE ]; while (1) { int n = ::read(fd, buffer, MAX_IPC_SIZE); if (n == -1) { if (errno == EINTR) continue; error(KIO::ERR_COULD_NOT_READ, getPath(url)); ::close(fd); return; } if (n == 0) break; // Finished { QByteArray array = QByteArray::fromRawData(buffer, n); data(array); } processed_size += n; } data(QByteArray()); ::close(fd); processedSize(buff.st_size); finished(); if (decompressToFile) QFile(arcTempDir + file).remove(); return; } // send empty buffer to mark EOF data(QByteArray()); finished(); } void kio_krarcProtocol::del(QUrl const & url, bool isFile) { KRFUNC; KRDEBUG(getPath(url)); if (!checkWriteSupport()) return; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (delCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Deleting files from %1 archives is not supported", arcType)); return; } if (!findFileEntry(url)) { if ((arcType != "arj" && arcType != "lha") || isFile) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } } QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); if (!isFile && file.right(1) != DIR_SEPARATOR) { if (arcType == "zip") file = file + DIR_SEPARATOR; } KrLinecountingProcess proc; proc << delCmd << getPath(arcFile->url()) << localeEncodedString(file); infoMessage(i18n("Deleting %1...", url.fileName())); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::stat(const QUrl &url) { KRFUNC; KRDEBUG(getPath(url)); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (listCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Accessing files is not supported with %1 archives", arcType)); return; } QString path = getPath(url, QUrl::StripTrailingSlash); QUrl newUrl = url; // but treat the archive itself as the archive root if (path == getPath(arcFile->url(), QUrl::StripTrailingSlash)) { newUrl.setPath(path + DIR_SEPARATOR); path = getPath(newUrl); } // we might be stating a real file if (QFileInfo(path).exists()) { QT_STATBUF buff; QT_STAT(path.toLocal8Bit(), &buff); QString mime; QMimeDatabase db; QMimeType result = db.mimeTypeForFile(path); if (result.isValid()) mime = result.name(); statEntry(KFileItem(QUrl::fromLocalFile(path), mime, buff.st_mode).entry()); finished(); return; } UDSEntry* entry = findFileEntry(newUrl); if (entry) { statEntry(*entry); finished(); } else error(KIO::ERR_DOES_NOT_EXIST, path); } void kio_krarcProtocol::copy(const QUrl &url, const QUrl &dest, int, KIO::JobFlags flags) { KRDEBUG("source: " << url.path() << " dest: " << dest.path()); if (!checkWriteSupport()) return; bool overwrite = !!(flags & KIO::Overwrite); // KDE HACK: opening the password dlg in copy causes error for the COPY, and further problems // that's why encrypted files are not allowed to copy if (!encrypted && dest.isLocalFile()) do { if (url.fileName() != dest.fileName()) break; if (QTextCodec::codecForLocale()->name() != codec->name()) break; //the file exists and we don't want to overwrite if ((!overwrite) && (QFile(getPath(dest)).exists())) { error((int)ERR_FILE_ALREADY_EXIST, QString(QFile::encodeName(getPath(dest)))); return; }; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } UDSEntry* entry = findFileEntry(url); if (copyCmd.isEmpty() || !entry) break; QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); QString destDir = getPath(dest, QUrl::StripTrailingSlash); if (!QDir(destDir).exists()) { int ndx = destDir.lastIndexOf(DIR_SEPARATOR_CHAR); if (ndx != -1) destDir.truncate(ndx + 1); } QDir::setCurrent(destDir); QString escapedFilename = file; if(arcType == "zip") { // left bracket needs to be escaped escapedFilename.replace('[', "[[]"); } KrLinecountingProcess proc; proc << copyCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << escapedFilename; if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect infoMessage(i18n("Unpacking %1...", url.fileName())); proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash) + "\n\n" + proc.getErrorMsg()); return; } if (!QFileInfo(getPath(dest, QUrl::StripTrailingSlash)).exists()) { error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash)); return; } processedSize(KFileItem(*entry, url).size()); finished(); QDir::setCurrent(QDir::rootPath()); /* for being able to umount devices after copying*/ return; } while (0); if (encrypted) KRDEBUG("ERROR: " << url.path() << " is encrypted."); if (!dest.isLocalFile()) KRDEBUG("ERROR: " << url.path() << " is not a local file."); // CMD_COPY is no more in KF5 - replaced with 74 value (as stated in kio/src/core/commands_p.h, which could be found in cgit.kde.org/kio.git/tree) error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, 74)); } void kio_krarcProtocol::rename(const QUrl& src, const QUrl& dest, KIO::JobFlags flags) { Q_UNUSED(flags); KRDEBUG("renaming from: " << src.path() << " to: " << dest.path()); KRDEBUG("command: " << arcPath); if (!checkWriteSupport()) { return; } if (renCmd.isEmpty()) { error(KIO::ERR_CANNOT_RENAME, src.fileName()); return; } if (src.fileName() == dest.fileName()) { return; } KrLinecountingProcess proc; proc << renCmd << arcPath << src.path().remove(arcPath + '/') << dest.path().remove(arcPath + '/'); proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(KIO::ERR_CANNOT_RENAME, src.fileName()); return; } finished(); } void kio_krarcProtocol::listDir(const QUrl &url) { KRFUNC; KRDEBUG(getPath(url)); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (listCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Listing directories is not supported for %1 archives", arcType)); return; } QString path = getPath(url); if (path.right(1) != DIR_SEPARATOR) path = path + DIR_SEPARATOR; // it might be a real dir ! if (QFileInfo(path).exists()) { if (QFileInfo(path).isDir()) { QUrl redir; redir.setPath(getPath(url)); redirection(redir); finished(); } else { // maybe it's an archive ! error(ERR_IS_FILE, path); } return; } if (!initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } QString arcDir = path.mid(getPath(arcFile->url()).length()); arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR)); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; if (dirDict.find(arcDir) == dirDict.end()) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } UDSEntryList* dirList = dirDict[ arcDir ]; totalSize(dirList->size()); listEntries(*dirList); finished(); } bool kio_krarcProtocol::setArcFile(const QUrl &url) { KRFUNC; KRDEBUG(url.fileName()); QString path = getPath(url); time_t currTime = time(nullptr); archiveChanged = true; newArchiveURL = true; // is the file already set ? if (arcFile && getPath(arcFile->url(), QUrl::StripTrailingSlash) == path.left(getPath(arcFile->url(), QUrl::StripTrailingSlash).length())) { newArchiveURL = false; // Has it changed ? KFileItem* newArcFile = new KFileItem(arcFile->url(), QString(), arcFile->mode()); if (metaData("Charset") != currentCharset || !newArcFile->cmp(*arcFile)) { currentCharset = metaData("Charset"); codec = QTextCodec::codecForName(currentCharset.toLatin1()); if (codec == nullptr) codec = QTextCodec::codecForMib(4 /* latin-1 */); delete arcFile; password.clear(); extArcReady = false; arcFile = newArcFile; } else { // same old file delete newArcFile; archiveChanged = false; if (encrypted && password.isNull()) initArcParameters(); } } else { // it's a new file... extArcReady = false; // new archive file means new dirDict, too dirDict.clear(); if (arcFile) { delete arcFile; password.clear(); arcFile = nullptr; } QString newPath = path; if (newPath.right(1) != DIR_SEPARATOR) newPath = newPath + DIR_SEPARATOR; for (int pos = 0; pos >= 0; pos = newPath.indexOf(DIR_SEPARATOR, pos + 1)) { QFileInfo qfi(newPath.left(pos)); if (qfi.exists() && !qfi.isDir()) { QT_STATBUF stat_p; QT_LSTAT(newPath.left(pos).toLocal8Bit(), &stat_p); arcFile = new KFileItem(QUrl::fromLocalFile(newPath.left(pos)), QString(), stat_p.st_mode); break; } } if (!arcFile) { // KRDEBUG("ERROR: " << path << " does not exist."); error(ERR_DOES_NOT_EXIST, path); return false; // file not found } currentCharset = metaData("Charset"); codec = QTextCodec::codecForName(currentCharset.toLatin1()); if (codec == nullptr) codec = QTextCodec::codecForMib(4 /* latin-1 */); } /* FIX: file change can only be detected if the timestamp between the two consequent changes is more than 1s. If the archive is continuously changing (check: move files inside the archive), krarc may errorneusly think, that the archive file is unchanged, because the timestamp is the same as the previous one. This situation can only occur if the modification time equals with the current time. While this condition is true, we can say, that the archive is changing, so content reread is always necessary during that period. */ if (archiveChanging) archiveChanged = true; archiveChanging = (currTime == (time_t)arcFile->time(KFileItem::ModificationTime).toTime_t()); arcPath = getPath(arcFile->url(), QUrl::StripTrailingSlash); arcType = detectArchive(encrypted, arcPath); if (arcType == "tbz") arcType = "bzip2"; else if (arcType == "tgz") arcType = "gzip"; else if (arcType == "tlz") arcType = "lzma"; else if (arcType == "txz") arcType = "xz"; if (arcType.isEmpty()) { arcType = arcFile->mimetype(); arcType = getShortTypeFromMime(arcType); if (arcType == "jar") arcType = "zip"; } return initArcParameters(); } bool kio_krarcProtocol::initDirDict(const QUrl &url, bool forced) { KRFUNC; KRDEBUG(getPath(url)); // set the archive location //if( !setArcFile(getPath(url)) ) return false; // no need to rescan the archive if it's not changed // KRDEBUG("achiveChanged: " << archiveChanged << " forced: " << forced); if (!archiveChanged && !forced) { // KRDEBUG("doing nothing."); return true; } extArcReady = false; if (!setArcFile(url)) return false; /* if the archive was changed refresh the file information */ // write the temp file KrLinecountingProcess proc; QTemporaryFile temp; // parse the temp file if (!temp.open()) { error(ERR_COULD_NOT_READ, temp.fileName()); return false; } if (arcType != "bzip2" && arcType != "lzma" && arcType != "xz") { if (arcType == "rpm") { proc << listCmd << arcPath; proc.setStandardOutputFile(temp.fileName()); } else { proc << listCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash); proc.setStandardOutputFile(temp.fileName()); } if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) return false; } // clear the dir dictionary QHashIterator< QString, KIO::UDSEntryList *> lit(dirDict); while (lit.hasNext()) delete lit.next().value(); dirDict.clear(); // add the "/" directory auto* root = new UDSEntryList(); dirDict.insert(DIR_SEPARATOR, root); // and the "/" UDSEntry UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_NAME, "."); mode_t mode = parsePermString("drwxr-xr-x"); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only entry.insert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only root->append(entry); if (arcType == "bzip2" || arcType == "lzma" || arcType == "xz") abort(); char buf[1000]; QString line; int lineNo = 0; bool invalidLine = false; // the rar list is started with a ------ line. if (arcType == "rar" || arcType == "arj" || arcType == "lha" || arcType == "7z") { while (temp.readLine(buf, 1000) != -1) { line = decodeString(buf); if (line.startsWith(QLatin1String("----------"))) break; } } while (temp.readLine(buf, 1000) != -1) { line = decodeString(buf); if (arcType == "rar") { // the rar list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) { invalidLine = !invalidLine; break; } if (invalidLine) continue; else { if (line[0] == '*') // encrypted archives starts with '*' line[0] = ' '; } } if (arcType == "ace") { // the ace list begins with a number. if (!line[0].isDigit()) continue; } if (arcType == "arj") { // the arj list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) { invalidLine = !invalidLine; continue; } if (invalidLine) continue; else { temp.readLine(buf, 1000); line = line + decodeString(buf); temp.readLine(buf, 1000); line = line + decodeString(buf); temp.readLine(buf, 1000); line = line + decodeString(buf); } } if (arcType == "lha" || arcType == "7z") { // the arj list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) break; } parseLine(lineNo++, line.trimmed()); } // close and delete our file temp.close(); archiveChanged = false; // KRDEBUG("done."); return true; } QString kio_krarcProtocol::findArcDirectory(const QUrl &url) { KRFUNC; KRDEBUG(url.fileName()); QString path = getPath(url); if (path.right(1) == DIR_SEPARATOR) path.truncate(path.length() - 1); if (!initDirDict(url)) { return QString(); } QString arcDir = path.mid(getPath(arcFile->url()).length()); arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR)); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; return arcDir; } UDSEntry* kio_krarcProtocol::findFileEntry(const QUrl &url) { KRFUNC; QString arcDir = findArcDirectory(url); if (arcDir.isEmpty()) return nullptr; QHash::iterator itef = dirDict.find(arcDir); if (itef == dirDict.end()) return nullptr; UDSEntryList* dirList = itef.value(); QString name = getPath(url); if (getPath(arcFile->url(), QUrl::StripTrailingSlash) == getPath(url, QUrl::StripTrailingSlash)) name = '.'; // the '/' case else { if (name.right(1) == DIR_SEPARATOR) name.truncate(name.length() - 1); name = name.mid(name.lastIndexOf(DIR_SEPARATOR) + 1); } UDSEntryList::iterator entry; for (entry = dirList->begin(); entry != dirList->end(); ++entry) { if ((entry->contains(KIO::UDSEntry::UDS_NAME)) && (entry->stringValue(KIO::UDSEntry::UDS_NAME) == name)) return &(*entry); } return nullptr; } QString kio_krarcProtocol::nextWord(QString &s, char d) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information s = s.trimmed(); int j = s.indexOf(d, 0); QString temp = s.left(j); // find the leftmost word. s.remove(0, j); return temp; } mode_t kio_krarcProtocol::parsePermString(QString perm) { KRFUNC; mode_t mode = 0; // file type if (perm[0] == 'd') mode |= S_IFDIR; #ifndef Q_WS_WIN if (perm[0] == 'l') mode |= S_IFLNK; #endif if (perm[0] == '-') mode |= S_IFREG; // owner permissions if (perm[1] != '-') mode |= S_IRUSR; if (perm[2] != '-') mode |= S_IWUSR; if (perm[3] != '-') mode |= S_IXUSR; #ifndef Q_WS_WIN // group permissions if (perm[4] != '-') mode |= S_IRGRP; if (perm[5] != '-') mode |= S_IWGRP; if (perm[6] != '-') mode |= S_IXGRP; // other permissions if (perm[7] != '-') mode |= S_IROTH; if (perm[8] != '-') mode |= S_IWOTH; if (perm[9] != '-') mode |= S_IXOTH; #endif return mode; } -UDSEntryList* kio_krarcProtocol::addNewDir(QString path) +UDSEntryList* kio_krarcProtocol::addNewDir(const QString& path) { KRFUNC; UDSEntryList* dir; // check if the current dir exists QHash::iterator itef = dirDict.find(path); if (itef != dirDict.end()) return itef.value(); // set dir to the parent dir dir = addNewDir(path.left(path.lastIndexOf(DIR_SEPARATOR, -2) + 1)); // add a new entry in the parent dir QString name = path.mid(path.lastIndexOf(DIR_SEPARATOR, -2) + 1); name = name.left(name.length() - 1); if (name == "." || name == "..") { // entries with these names wouldn't be displayed // don't translate since this is an internal error QString err = QString("Cannot handle path: ") + path; // KRDEBUG("ERROR: " << err); error(KIO::ERR_INTERNAL, err); exit(); } UDSEntry entry; entry.insert(KIO::UDSEntry::UDS_NAME, name); mode_t mode = parsePermString("drwxr-xr-x"); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only entry.insert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only entry.insert(KIO::UDSEntry::UDS_SIZE, 0); entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, arcFile->time(KFileItem::ModificationTime).toTime_t()); dir->append(entry); // create a new directory entry and add it.. dir = new UDSEntryList(); dirDict.insert(path, dir); return dir; } void kio_krarcProtocol::parseLine(int lineNo, QString line) { KRFUNC; UDSEntryList* dir; UDSEntry entry; QString owner; QString group; QString symlinkDest; QString perm; mode_t mode = 0666; size_t size = 0; time_t time = ::time(nullptr); QString fullName; if (arcType == "zip") { // permissions perm = nextWord(line); // ignore the next 2 fields nextWord(line); nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields nextWord(line);nextWord(line); // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(4, 2).toInt(), d.mid(6, 2).toInt()); QTime qtime(d.mid(9, 2).toInt(), d.mid(11, 2).toInt(), d.mid(13, 2).toInt()); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n'); if (perm.length() != 10) perm = (perm.at(0) == 'd' || fullName.endsWith(DIR_SEPARATOR)) ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "rar") { // permissions perm = nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields : packed size and compression ration nextWord(line); nextWord(line); // date & time QString d = nextWord(line); int year = 1900 + d.mid(6, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // checksum : ignored nextWord(line); // full name fullName = nextWord(line, '\n'); if (perm.length() == 7) { // windows rar permission format bool isDir = (perm.at(1).toLower() == 'd'); bool isReadOnly = (perm.at(2).toLower() == 'r'); perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; if (isReadOnly) perm[ 2 ] = '-'; } if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "arj") { nextWord(line); // full name fullName = nextWord(line, '\n'); // ignore the next 2 fields nextWord(line); nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields nextWord(line); nextWord(line); // date & time QString d = nextWord(line); int year = 1900 + d.mid(0, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(6, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // permissions perm = nextWord(line); if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "rpm") { // full name fullName = nextWord(line); // size size = nextWord(line).toULong(); // date & time time = nextWord(line).toULong(); // next field is md5sum, ignore it nextWord(line); // permissions mode = nextWord(line).toULong(nullptr, 8); // Owner & Group owner = nextWord(line); group = nextWord(line); // symlink destination #ifndef Q_WS_WIN if (S_ISLNK(mode)) { // ignore the next 3 fields nextWord(line); nextWord(line); nextWord(line); symlinkDest = nextWord(line); } #endif } if (arcType == "gzip") { if (!lineNo) return; //ignore the first line // first field is uncompressed size - ignore it nextWord(line); // size size = nextWord(line).toULong(); // ignore the next field nextWord(line); // full name fullName = nextWord(line); fullName = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1); } if (arcType == "lzma") { fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("lzma"))) { fullName.truncate(fullName.length() - 5); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "xz") { fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("xz"))) { fullName.truncate(fullName.length() - 3); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "bzip2") { // There is no way to list bzip2 files, so we take our information from // the archive itself... fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("bz2"))) { fullName.truncate(fullName.length() - 4); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "lha") { // permissions perm = nextWord(line); if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); // ignore the next field nextWord(line); // size size = nextWord(line).toLong(); // ignore the next field nextWord(line); // date & time int month = (QString("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec").split(',')).indexOf(nextWord(line)) + 1; int day = nextWord(line).toInt(); int year = QDate::currentDate().year(); QString third = nextWord(line); QTime qtime; if (third.contains(":")) qtime = QTime::fromString(third); else year = third.toInt(); QDate qdate(year, month, day); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n'); } if (arcType == "ace") { // date & time QString d = nextWord(line); int year = 1900 + d.mid(6, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // ignore the next field nextWord(line); // size size = nextWord(line).toLong(); // ignore the next field nextWord(line); // full name fullName = nextWord(line, '\n'); if (fullName[ 0 ] == '*') // encrypted archives starts with '*' fullName = fullName.mid(1); } if (arcType == "deb") { // permissions perm = nextWord(line); mode = parsePermString(perm); // Owner & Group owner = nextWord(line, DIR_SEPARATOR_CHAR); group = nextWord(line).mid(1); // size size = nextWord(line).toLong(); // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n').mid(1); //if ( fullName.right( 1 ) == "/" ) return; if (fullName.contains("->")) { symlinkDest = fullName.mid(fullName.indexOf("->") + 2); fullName = fullName.left(fullName.indexOf("->") - 1); } } if (arcType == "7z") { // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), t.mid(6, 2).toInt()); time = QDateTime(qdate, qtime).toTime_t(); // permissions perm = nextWord(line); bool isDir = (perm.at(0).toLower() == 'd'); bool isReadOnly = (perm.at(1).toLower() == 'r'); perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; if (isReadOnly) perm[ 2 ] = '-'; mode = parsePermString(perm); // size size = nextWord(line).toLong(); // ignore the next 15 characters line = line.mid(15); // full name fullName = nextWord(line, '\n'); } if (fullName.right(1) == DIR_SEPARATOR) fullName = fullName.left(fullName.length() - 1); if (!fullName.startsWith(DIR_SEPARATOR)) fullName = DIR_SEPARATOR + fullName; QString path = fullName.left(fullName.lastIndexOf(DIR_SEPARATOR) + 1); // set/create the directory UDSEntryList QHash::iterator itef = dirDict.find(path); if (itef == dirDict.end()) dir = addNewDir(path); else dir = itef.value(); QString name = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1); // file name entry.insert(KIO::UDSEntry::UDS_NAME, name); // file type entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only // file permissions entry.insert(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only // file size entry.insert(KIO::UDSEntry::UDS_SIZE, size); // modification time entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time); // link destination if (!symlinkDest.isEmpty()) { entry.insert(KIO::UDSEntry::UDS_LINK_DEST, symlinkDest); } if (S_ISDIR(mode)) { fullName = fullName + DIR_SEPARATOR; if (dirDict.find(fullName) == dirDict.end()) dirDict.insert(fullName, new UDSEntryList()); else { // try to overwrite an existing entry UDSEntryList::iterator entryIt; for (entryIt = dir->begin(); entryIt != dir->end(); ++entryIt) { if (entryIt->contains(KIO::UDSEntry::UDS_NAME) && entryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) { entryIt->insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time); entryIt->insert(KIO::UDSEntry::UDS_ACCESS, mode); return; } } return; // there is already an entry for this directory } } // multi volume archives can add a file twice, use only one UDSEntryList::iterator dirEntryIt; for (dirEntryIt = dir->begin(); dirEntryIt != dir->end(); ++dirEntryIt) if (dirEntryIt->contains(KIO::UDSEntry::UDS_NAME) && dirEntryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) return; dir->append(entry); } bool kio_krarcProtocol::initArcParameters() { KRFUNC; KRDEBUG("arcType: " << arcType); noencoding = false; cmd.clear(); listCmd = QStringList(); getCmd = QStringList(); copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); renCmd = QStringList(); if (arcType == "zip") { noencoding = true; cmd = fullPathName("unzip"); listCmd << fullPathName("unzip") << "-ZTs-z-t-h"; getCmd << fullPathName("unzip") << "-p"; copyCmd << fullPathName("unzip") << "-jo"; if (QStandardPaths::findExecutable(QStringLiteral("zip")).isEmpty()) { delCmd = QStringList(); putCmd = QStringList(); } else { delCmd << fullPathName("zip") << "-d"; putCmd << fullPathName("zip") << "-ry"; } if (!QStandardPaths::findExecutable(QStringLiteral("7za")).isEmpty()) { renCmd << fullPathName("7za") << "rn"; } if (!getPassword().isEmpty()) { getCmd << "-P" << password; copyCmd << "-P" << password; putCmd << "-P" << password; } } else if (arcType == "rar") { noencoding = true; if (QStandardPaths::findExecutable(QStringLiteral("rar")).isEmpty()) { cmd = fullPathName("unrar"); listCmd << fullPathName("unrar") << "-c-" << "-v" << "v"; getCmd << fullPathName("unrar") << "p" << "-ierr" << "-idp" << "-c-" << "-y"; copyCmd << fullPathName("unrar") << "e" << "-y"; delCmd = QStringList(); putCmd = QStringList(); } else { cmd = fullPathName("rar"); listCmd << fullPathName("rar") << "-c-" << "-v" << "v"; getCmd << fullPathName("rar") << "p" << "-ierr" << "-idp" << "-c-" << "-y"; copyCmd << fullPathName("rar") << "e" << "-y"; delCmd << fullPathName("rar") << "d"; putCmd << fullPathName("rar") << "-r" << "a"; } if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); listCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); if (!putCmd.isEmpty()) { putCmd << QString("-p%1").arg(password); delCmd << QString("-p%1").arg(password); } } } else if (arcType == "rpm") { cmd = fullPathName("rpm"); listCmd << fullPathName("rpm") << "--dump" << "-lpq"; getCmd << fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" << "-iuvdF"; delCmd = QStringList(); putCmd = QStringList(); copyCmd = QStringList(); } else if (arcType == "gzip") { cmd = fullPathName("gzip"); listCmd << fullPathName("gzip") << "-l"; getCmd << fullPathName("gzip") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "bzip2") { cmd = fullPathName("bzip2"); listCmd << fullPathName("bzip2"); getCmd << fullPathName("bzip2") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "lzma") { cmd = fullPathName("lzma"); listCmd << fullPathName("lzma"); getCmd << fullPathName("lzma") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "xz") { cmd = fullPathName("xz"); listCmd << fullPathName("xz"); getCmd << fullPathName("xz") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "arj") { cmd = fullPathName("arj"); listCmd << fullPathName("arj") << "v" << "-y" << "-v"; getCmd << fullPathName("arj") << "-jyov" << "-v" << "e"; copyCmd << fullPathName("arj") << "-jyov" << "-v" << "e"; delCmd << fullPathName("arj") << "d"; putCmd << fullPathName("arj") << "-r" << "a"; if (!getPassword().isEmpty()) { getCmd << QString("-g%1").arg(password); copyCmd << QString("-g%1").arg(password); putCmd << QString("-g%1").arg(password); } } else if (arcType == "lha") { cmd = fullPathName("lha"); listCmd << fullPathName("lha") << "l"; getCmd << fullPathName("lha") << "pq"; copyCmd << fullPathName("lha") << "eif"; delCmd << fullPathName("lha") << "d"; putCmd << fullPathName("lha") << "a"; } else if (arcType == "ace") { cmd = fullPathName("unace"); listCmd << fullPathName("unace") << "v"; getCmd << fullPathName("unace") << "e" << "-o"; copyCmd << fullPathName("unace") << "e" << "-o"; delCmd = QStringList(); putCmd = QStringList(); if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); } } else if (arcType == "deb") { cmd = fullPathName("dpkg"); listCmd << fullPathName("dpkg") << "-c"; getCmd << fullPathName("tar") << "xvf"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "7z") { noencoding = true; cmd = fullPathName("7z"); if (QStandardPaths::findExecutable(cmd).isEmpty()) cmd = fullPathName("7za"); listCmd << cmd << "l" << "-y"; getCmd << cmd << "e" << "-y"; copyCmd << cmd << "e" << "-y"; delCmd << cmd << "d" << "-y"; putCmd << cmd << "a" << "-y"; renCmd << cmd << "rn"; if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); listCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); if (!putCmd.isEmpty()) { putCmd << QString("-p%1").arg(password); delCmd << QString("-p%1").arg(password); } } } // checking if it's an absolute path #ifdef Q_WS_WIN if (cmd.length() > 2 && cmd[ 0 ].isLetter() && cmd[ 1 ] == ':') return true; #else if (cmd.startsWith(DIR_SEPARATOR)) return true; #endif if (QStandardPaths::findExecutable(cmd).isEmpty()) { error(KIO::ERR_CANNOT_LAUNCH_PROCESS, cmd + i18n("\nMake sure that the %1 binary is installed properly on your system.", cmd)); KRDEBUG("Failed to find cmd: " << cmd); return false; } return true; } bool kio_krarcProtocol::checkStatus(int exitCode) { KRFUNC; KRDEBUG(exitCode); return KrArcBaseManager::checkStatus(arcType, exitCode); } void kio_krarcProtocol::checkIf7zIsEncrypted(bool &encrypted, QString fileName) { KRFUNC; if (encryptedArchPath == fileName) encrypted = true; else { // we try to find whether the 7z archive is encrypted // this is hard as the headers are also compressed QString tester = fullPathName("7z"); if (QStandardPaths::findExecutable(tester).isEmpty()) { KRDEBUG("A 7z program was not found"); tester = fullPathName("7za"); if (QStandardPaths::findExecutable(tester).isEmpty()) { KRDEBUG("A 7za program was not found"); return; } } QString testCmd = tester + " t -y "; lastData = encryptedArchPath = ""; KrLinecountingProcess proc; proc << testCmd << fileName; connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::checkOutputForPassword); proc.start(); proc.waitForFinished(); encrypted = this->encrypted; if (encrypted) encryptedArchPath = fileName; } } void kio_krarcProtocol::checkOutputForPassword(KProcess * proc, QByteArray & buf) { KRFUNC; QString data = QString(buf); QString checkable = lastData + data; QStringList lines = checkable.split('\n'); lastData = lines[ lines.count() - 1 ]; for (int i = 0; i != lines.count(); i++) { QString line = lines[ i ].trimmed().toLower(); int ndx = line.indexOf("testing"); if (ndx >= 0) line.truncate(ndx); if (line.isEmpty()) continue; if (line.contains("password") && line.contains("enter")) { KRDEBUG("Encrypted 7z archive found!"); encrypted = true; proc->kill(); return; } } } void kio_krarcProtocol::invalidatePassword() { KRFUNC; KRDEBUG(getPath(arcFile->url(), QUrl::StripTrailingSlash) + DIR_SEPARATOR); if (!encrypted) return; KIO::AuthInfo authInfo; authInfo.caption = i18n("Krarc Password Dialog"); authInfo.username = "archive"; authInfo.readOnly = true; authInfo.keepPassword = true; authInfo.verifyPath = true; QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash); authInfo.url = QUrl::fromLocalFile(ROOT_DIR); authInfo.url.setHost(fileName /*.replace('/','_')*/); authInfo.url.setScheme("krarc"); password.clear(); cacheAuthentication(authInfo); } QString kio_krarcProtocol::getPassword() { KRFUNC; KRDEBUG("Encrypted: " << encrypted); if (!password.isNull()) return password; if (!encrypted) return (password = ""); KIO::AuthInfo authInfo; authInfo.caption = i18n("Krarc Password Dialog"); authInfo.username = "archive"; authInfo.readOnly = true; authInfo.keepPassword = true; authInfo.verifyPath = true; QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash); authInfo.url = QUrl::fromLocalFile(ROOT_DIR); authInfo.url.setHost(fileName /*.replace('/','_')*/); authInfo.url.setScheme("krarc"); if (checkCachedAuthentication(authInfo) && !authInfo.password.isNull()) { KRDEBUG(authInfo.password); return (password = authInfo.password); } authInfo.password.clear(); #if KIO_VERSION_MINOR >= 24 int errCode = openPasswordDialogV2(authInfo, i18n("Accessing the file requires a password.")); if (!errCode && !authInfo.password.isNull()) { #else if (openPasswordDialog(authInfo, i18n("Accessing the file requires a password.")) && !authInfo.password.isNull()) { #endif KRDEBUG(authInfo.password); return (password = authInfo.password); #if KIO_VERSION_MINOR >= 24 } else { error(errCode, QString()); #endif } KRDEBUG(password); return password; } QString kio_krarcProtocol::detectFullPathName(QString name) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information KRDEBUG(name); name = name + EXEC_SUFFIX; QStringList path = QString::fromLocal8Bit(qgetenv("PATH")).split(':'); for (auto & it : path) { if (QDir(it).exists(name)) { QString dir = it; if (!dir.endsWith(DIR_SEPARATOR)) dir += DIR_SEPARATOR; return dir + name; } } return name; } -QString kio_krarcProtocol::fullPathName(QString name) +QString kio_krarcProtocol::fullPathName(const QString& name) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information KRDEBUG(name); QString supposedName = confGrp.readEntry(name, QString()); if (supposedName.isEmpty()) supposedName = detectFullPathName(name); return supposedName; } QString kio_krarcProtocol::localeEncodedString(QString str) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return str; QByteArray array = codec->fromUnicode(str); // encoding the byte array to QString, mapping 0x0000-0x00FF to 0xE000-0xE0FF // see KrArcCodec for more explanation int size = array.size(); QString result; const char *data = array.data(); for (int i = 0; i != size; i++) { unsigned short ch = (((int)data[ i ]) & 0xFF) + 0xE000; // user defined character result.append(QChar(ch)); } return result; } -QByteArray kio_krarcProtocol::encodeString(QString str) +QByteArray kio_krarcProtocol::encodeString(const QString& str) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return QTextCodec::codecForLocale()->fromUnicode(str); return codec->fromUnicode(str); } QString kio_krarcProtocol::decodeString(char * buf) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return QTextCodec::codecForLocale()->toUnicode(buf); return codec->toUnicode(buf); } QString kio_krarcProtocol::getPath(const QUrl &url, QUrl::FormattingOptions options) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information QString path = url.adjusted(options).path(); REPLACE_DIR_SEP2(path); #ifdef Q_WS_WIN if (path.startsWith(DIR_SEPARATOR)) { int p = 1; while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR) p++; /* /C:/Folder */ if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') { path = path.mid(p); } } #endif return path; } #endif // KRARC_ENABLED diff --git a/krArc/krarc.h b/krArc/krarc.h index 45f7a6fa..db5faa96 100644 --- a/krArc/krarc.h +++ b/krArc/krarc.h @@ -1,145 +1,145 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2004-2018 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 KRARC_H #define KRARC_H // QtCore #include #include #include #include #include #include #include #include #include #include "krarcbasemanager.h" #include "krlinecountingprocess.h" #include "../krusader/krdebuglogger.h" class KFileItem; class QByteArray; class QTextCodec; class kio_krarcProtocol : public QObject, public KIO::SlaveBase, public KrArcBaseManager { Q_OBJECT public: kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket); ~kio_krarcProtocol() override; void stat(const QUrl &url) Q_DECL_OVERRIDE; void get(const QUrl &url) Q_DECL_OVERRIDE; void put(const QUrl &url, int permissions, KIO::JobFlags flags) Q_DECL_OVERRIDE; void mkdir(const QUrl &url, int permissions) Q_DECL_OVERRIDE; void listDir(const QUrl &url) Q_DECL_OVERRIDE; void del(QUrl const & url, bool isFile) Q_DECL_OVERRIDE; void copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) Q_DECL_OVERRIDE; void rename(const QUrl &src, const QUrl & dest, KIO::JobFlags flags) Q_DECL_OVERRIDE; public slots: void receivedData(KProcess *, QByteArray &); void checkOutputForPassword(KProcess *, QByteArray &); protected: virtual bool initDirDict(const QUrl &url, bool forced = false); virtual bool initArcParameters(); void checkIf7zIsEncrypted(bool &, QString) Q_DECL_OVERRIDE; virtual void parseLine(int lineNo, QString line); virtual bool setArcFile(const QUrl &url); virtual QString getPassword(); virtual void invalidatePassword(); QString getPath(const QUrl &url, QUrl::FormattingOptions options = nullptr); QString localeEncodedString(QString str); - QByteArray encodeString(QString); + QByteArray encodeString(const QString&); QString decodeString(char *); // archive specific commands QString cmd; ///< the archiver name. QStringList listCmd; ///< list files. QStringList getCmd; ///< unpack files command. QStringList delCmd; ///< delete files command. QStringList putCmd; ///< add file command. QStringList copyCmd; ///< copy to file command. QStringList renCmd; ///< rename file command. private: void get(const QUrl &url, int tries); /** checks if a returned status ("exit code") of an archiving-related process is OK. */ bool checkStatus(int exitCode); /** service function for parseLine. */ QString nextWord(QString &s, char d = ' '); /** translate permission string to mode_t. */ mode_t parsePermString(QString perm); /** return the name of the directory inside the archive. */ QString findArcDirectory(const QUrl &url); /** find the UDSEntry of a file in a directory. */ KIO::UDSEntry* findFileEntry(const QUrl &url); /** add a new directory (file list container). */ - KIO::UDSEntryList* addNewDir(QString path); - QString fullPathName(QString name); + KIO::UDSEntryList* addNewDir(const QString& path); + QString fullPathName(const QString& name); static QString detectFullPathName(QString name); bool checkWriteSupport(); QHash dirDict; //< the directories data structure. bool encrypted; //< tells whether the archive is encrypted bool archiveChanged; //< true if the archive was changed. bool archiveChanging; //< true if the archive is currently changing. bool newArchiveURL; //< true if new archive was entered for the protocol bool noencoding; //< 7z files use UTF-16, so encoding is unnecessary KIO::filesize_t decompressedLen; //< the number of the decompressed bytes KFileItem* arcFile; //< the archive file item. QString arcPath; //< the archive location QString arcTempDir; //< the currently used temp directory. QString arcType; //< the archive type. bool extArcReady; //< Used for RPM & DEB files. QString password; //< Password for the archives KConfig krConf; //< The configuration file for krusader KConfigGroup confGrp; //< the 'Dependencies' config group QString lastData; QString encryptedArchPath; QString currentCharset; QTextCodec * codec; }; #ifdef Q_WS_WIN #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "\\" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '\\' #define REPLACE_DIR_SEP2(x) x = x.replace( DIR_SEPARATOR2, DIR_SEPARATOR ); #define ROOT_DIR "C:\\" #define EXEC_SUFFIX ".exe" #else #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "/" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '/' #define REPLACE_DIR_SEP2(x) #define ROOT_DIR "/" #define EXEC_SUFFIX "" #endif #endif diff --git a/krArc/krarcbasemanager.cpp b/krArc/krarcbasemanager.cpp index 21a27aaa..8bec551f 100644 --- a/krArc/krarcbasemanager.cpp +++ b/krArc/krarcbasemanager.cpp @@ -1,234 +1,234 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2004-2018 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 "krarcbasemanager.h" #include KrArcBaseManager::AutoDetectParams KrArcBaseManager::autoDetectParams[] = {{"zip", 0, "PK\x03\x04"}, {"rar", 0, "Rar!\x1a" }, {"arj", 0, "\x60\xea" }, {"rpm", 0, "\xed\xab\xee\xdb"}, {"ace", 7, "**ACE**" }, {"bzip2", 0, "\x42\x5a\x68\x39\x31" }, {"gzip", 0, "\x1f\x8b"}, {"deb", 0, "!\ndebian-binary " }, {"7z", 0, "7z\xbc\xaf\x27\x1c" }/*, {"xz", 0, "\xfd\x37\x7a\x58\x5a\x00"}*/ }; int KrArcBaseManager::autoDetectElems = sizeof(autoDetectParams) / sizeof(AutoDetectParams); const int KrArcBaseManager::maxLenType = 5; //! Checks if a returned status ("exit code") of an archiving-related process is OK /*! \param arcType A short QString which contains an identifier of the type of the archive. \param exitCode The returned status ("exit code") of an archiving-related process. \return If the exit code means that the archiving-related process ended without errors. */ bool KrArcBaseManager::checkStatus(const QString &arcType, int exitCode) { if (arcType == "zip" || arcType == "rar" || arcType == "7z") return exitCode == 0 || exitCode == 1; else if (arcType == "ace" || arcType == "bzip2" || arcType == "lha" || arcType == "rpm" || arcType == "cpio" || arcType == "tar" || arcType == "tarz" || arcType == "tbz" || arcType == "tgz" || arcType == "arj" || arcType == "deb" || arcType == "tlz" || arcType == "txz") return exitCode == 0; else if (arcType == "gzip" || arcType == "lzma" || arcType == "xz") return exitCode == 0 || exitCode == 2; else return exitCode == 0; } -QString KrArcBaseManager::detectArchive(bool &encrypted, QString fileName, bool checkEncrypted, bool fast) +QString KrArcBaseManager::detectArchive(bool &encrypted, const QString& fileName, bool checkEncrypted, bool fast) { encrypted = false; QFile arcFile(fileName); if (arcFile.open(QIODevice::ReadOnly)) { char buffer[ 1024 ]; long sizeMax = arcFile.read(buffer, sizeof(buffer)); arcFile.close(); for (int i = 0; i < autoDetectElems; i++) { QByteArray detectionString = autoDetectParams[ i ].detectionString; int location = autoDetectParams[ i ].location; int endPtr = detectionString.length() + location; if (endPtr > sizeMax) continue; int j = 0; for (; j != detectionString.length(); j++) { if (detectionString[ j ] == '?') continue; if (buffer[ location + j ] != detectionString[ j ]) break; } if (j == detectionString.length()) { QString type = autoDetectParams[ i ].type; if (type == "bzip2" || type == "gzip") { if (fast) { if (fileName.endsWith(QLatin1String(".tar.gz"))) type = "tgz"; else if (fileName.endsWith(QLatin1String(".tar.bz2"))) type = "tbz"; } else { KTar tapeArchive(fileName); if (tapeArchive.open(QIODevice::ReadOnly)) { tapeArchive.close(); if (type == "gzip") type = "tgz"; else if (type == "bzip2") type = "tbz"; } } } else if (type == "zip") encrypted = (buffer[6] & 1); else if (type == "arj") { if (sizeMax > 4) { long headerSize = ((unsigned char *)buffer)[ 2 ] + 256 * ((unsigned char *)buffer)[ 3 ]; long fileHeader = headerSize + 10; if (fileHeader + 9 < sizeMax && buffer[ fileHeader ] == (char)0x60 && buffer[ fileHeader + 1 ] == (char)0xea) encrypted = (buffer[ fileHeader + 8 ] & 1); } } else if (type == "rar") { if (sizeMax > 13 && buffer[ 9 ] == (char)0x73) { if (buffer[ 10 ] & 0x80) { // the header is encrypted? encrypted = true; } else { long offset = 7; long mainHeaderSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ]; offset += mainHeaderSize; while (offset + 10 < sizeMax) { long headerSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ]; bool isDir = (buffer[ offset+7 ] == '\0') && (buffer[ offset+8 ] == '\0') && (buffer[ offset+9 ] == '\0') && (buffer[ offset+10 ] == '\0'); if (buffer[ offset + 2 ] != (char)0x74) break; if (!isDir) { encrypted = (buffer[ offset + 3 ] & 4) != 0; break; } offset += headerSize; } } } } else if (type == "ace") { long offset = 0; long mainHeaderSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4; offset += mainHeaderSize; while (offset + 10 < sizeMax) { long headerSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4; bool isDir = (buffer[ offset+11 ] == '\0') && (buffer[ offset+12 ] == '\0') && (buffer[ offset+13 ] == '\0') && (buffer[ offset+14 ] == '\0'); if (buffer[ offset + 4 ] != (char)0x01) break; if (!isDir) { encrypted = (buffer[ offset + 6 ] & 64) != 0; break; } offset += headerSize; } } else if (type == "7z") { // encryption check is expensive, check only if it's necessary if (checkEncrypted) checkIf7zIsEncrypted(encrypted, fileName); } return type; } } if (sizeMax >= 512) { /* checking if it's a tar file */ unsigned checksum = 32 * 8; char chksum[ 9 ]; for (int i = 0; i != 512; i++) checksum += ((unsigned char *)buffer)[ i ]; for (int i = 148; i != 156; i++) checksum -= ((unsigned char *)buffer)[ i ]; sprintf(chksum, "0%o", checksum); if (!memcmp(buffer + 148, chksum, strlen(chksum))) { int k = strlen(chksum); for (; k < 8; k++) if (buffer[148+k] != 0 && buffer[148+k] != 32) break; if (k == 8) return "tar"; } } } if (fileName.endsWith(QLatin1String(".tar.lzma")) || fileName.endsWith(QLatin1String(".tlz"))) { return "tlz"; } if (fileName.endsWith(QLatin1String(".lzma"))) { return "lzma"; } if (fileName.endsWith(QLatin1String(".tar.xz")) || fileName.endsWith(QLatin1String(".txz"))) { return "txz"; } if (fileName.endsWith(QLatin1String(".xz"))) { return "xz"; } return QString(); } //! Returns a short identifier of the type of a file, obtained from the mime type of the file /*! \param mime The mime type of the file. \return A short QString which contains an identifier of the type of the file. */ QString KrArcBaseManager::getShortTypeFromMime(const QString &mime) { // 7zip files are a not a normal case because their mimetype does not // follow the norm of other types: zip, tar, lha, ace, arj, etc. if (mime == "application/x-7z-compressed") return "7z"; // If it's a rar file but its mimetype isn't "application/x-rar" if (mime == "application/x-rar-compressed") return "rar"; // The short type that will be returned QString sType = mime; int lastHyphen = sType.lastIndexOf('-'); if (lastHyphen != -1) sType = sType.mid(lastHyphen + 1); else { int lastSlash = sType.lastIndexOf('/'); if (lastSlash != -1) sType = sType.mid(lastSlash + 1); } // The identifier kept short if (sType.length() > maxLenType) sType = sType.right(maxLenType); return sType; } diff --git a/krArc/krarcbasemanager.h b/krArc/krarcbasemanager.h index b5a137f7..833f1cf3 100644 --- a/krArc/krarcbasemanager.h +++ b/krArc/krarcbasemanager.h @@ -1,59 +1,59 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2004-2018 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 KRARCBASEMANAGER_H #define KRARCBASEMANAGER_H // QtCore #include /*! * \brief An abstract base class for managing archives. */ class KrArcBaseManager { private: //! Information about a type of archive and the bytes that are used to detect it. struct AutoDetectParams { QString type; int location; QByteArray detectionString; }; static AutoDetectParams autoDetectParams[]; //! Information used to detect if a file is an archive static int autoDetectElems; //!< The size of autoDetectParams[] protected: //! The maximum length of a short QString that represents the type of a file static const int maxLenType; static bool checkStatus(const QString &, int); public: KrArcBaseManager() {} - QString detectArchive(bool &, QString, bool = true, bool = false); + QString detectArchive(bool &, const QString&, bool = true, bool = false); virtual void checkIf7zIsEncrypted(bool &, QString) = 0; static QString getShortTypeFromMime(const QString &); virtual ~KrArcBaseManager() {} }; #endif // KRARCBASEMANAGER_H diff --git a/krusader/Archive/abstractthreadedjob.cpp b/krusader/Archive/abstractthreadedjob.cpp index fc7c3469..2d863b71 100644 --- a/krusader/Archive/abstractthreadedjob.cpp +++ b/krusader/Archive/abstractthreadedjob.cpp @@ -1,664 +1,664 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * * Copyright (C) 2009-2018 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 "abstractthreadedjob.h" // QtCore #include #include #include #include #include #include // QtWidgets #include #include #include #include #include "krarchandler.h" #include "../krglobal.h" #include "../krservices.h" #include "../FileSystem/filesystemprovider.h" extern KRarcHandler arcHandler; AbstractThreadedJob::AbstractThreadedJob() : KIO::Job(), _locker(), _waiter(), _stack(), _maxProgressValue(0), _currentProgress(0), _exiting(false), _jobThread(nullptr) { } void AbstractThreadedJob::startAbstractJobThread(AbstractJobThread * jobThread) { _jobThread = jobThread; _jobThread->setJob(this); _jobThread->moveToThread(_jobThread); _jobThread->start(); } AbstractThreadedJob::~AbstractThreadedJob() { _exiting = true; if (_jobThread) { _jobThread->abort(); _locker.lock(); _waiter.wakeAll(); _locker.unlock(); _jobThread->wait(); delete _jobThread; } } bool AbstractThreadedJob::event(QEvent *e) { if (e->type() == QEvent::User) { auto *event = (UserEvent*) e; switch (event->command()) { case CMD_SUCCESS: { emitResult(); } break; case CMD_ERROR: { auto error = event->args()[ 0 ].value(); QString errorText = event->args()[ 1 ].value(); setError(error); setErrorText(errorText); emitResult(); } break; case CMD_INFO: { QString info = event->args()[ 0 ].value(); QString arg1 = event->args()[ 1 ].value(); QString arg2 = event->args()[ 2 ].value(); QString arg3 = event->args()[ 3 ].value(); QString arg4 = event->args()[ 4 ].value(); _title = info; emit description(this, info, qMakePair(arg1, arg2), qMakePair(arg3, arg4)); } break; case CMD_RESET: { QString info = event->args()[ 0 ].value(); QString arg1 = event->args()[ 1 ].value(); QString arg2 = event->args()[ 2 ].value(); QString arg3 = event->args()[ 3 ].value(); QString arg4 = event->args()[ 4 ].value(); _title = info; setProcessedAmount(KJob::Bytes, 0); setTotalAmount(KJob::Bytes, 0); emitSpeed(0); emit description(this, info, qMakePair(arg1, arg2), qMakePair(arg3, arg4)); } break; case CMD_UPLOAD_FILES: case CMD_DOWNLOAD_FILES: { QList sources = KrServices::toUrlList(event->args()[ 0 ].value()); QUrl dest = event->args()[ 1 ].value(); KIO::Job *job = KIO::copy(sources, dest, KIO::HideProgressInfo); addSubjob(job); job->setUiDelegate(new KIO::JobUiDelegate()); connect(job, &KIO::Job::result, this, &AbstractThreadedJob::slotDownloadResult); connect(job, SIGNAL(processedAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(slotProcessedAmount(KJob*,KJob::Unit,qulonglong))); connect(job, SIGNAL(totalAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(slotTotalAmount(KJob*,KJob::Unit,qulonglong))); connect(job, SIGNAL(speed(KJob*,ulong)), this, SLOT(slotSpeed(KJob*,ulong))); connect(job, SIGNAL(description(KJob*,QString,QPair,QPair)), this, SLOT(slotDescription(KJob*,QString,QPair,QPair))); } break; case CMD_MAXPROGRESSVALUE: { auto maxValue = event->args()[ 0 ].value(); _maxProgressValue = maxValue; _currentProgress = 0; } break; case CMD_ADD_PROGRESS: { auto progress = event->args()[ 0 ].value(); _currentProgress += progress; if (_maxProgressValue != 0) { setPercent(100 * _currentProgress / _maxProgressValue); int elapsed = _time.isNull() ? 1 : _time.secsTo(QTime::currentTime()); if (elapsed != 0 && event->args().count() > 1) { _time = QTime::currentTime(); QString progressString = (event->args()[ 1 ].value()); emit description(this, _title, qMakePair(progressString, QString("%1/%2").arg(_currentProgress).arg(_maxProgressValue)), qMakePair(QString(), QString()) ); } } } break; case CMD_GET_PASSWORD: { QString path = event->args()[ 0 ].value(); QString password = KRarcHandler::getPassword(path); auto *resultResp = new QList (); (*resultResp) << password; addEventResponse(resultResp); } break; case CMD_MESSAGE: { QString message = event->args()[ 0 ].value(); auto *ui = static_cast(uiDelegate()); KMessageBox::information(ui ? ui->window() : nullptr, message); auto *resultResp = new QList (); addEventResponse(resultResp); } break; } return true; } else { return KIO::Job::event(e); } } void AbstractThreadedJob::addEventResponse(QList * obj) { _locker.lock(); _stack.push(obj); _waiter.wakeOne(); _locker.unlock(); } QList * AbstractThreadedJob::getEventResponse(UserEvent * event) { _locker.lock(); QApplication::postEvent(this, event); _waiter.wait(&_locker); if (_exiting) return nullptr; QList *resp = _stack.pop(); _locker.unlock(); return resp; } void AbstractThreadedJob::sendEvent(UserEvent * event) { QApplication::postEvent(this, event); } void AbstractThreadedJob::slotDownloadResult(KJob* job) { auto *resultResp = new QList (); if (job) { (*resultResp) << QVariant(job->error()); (*resultResp) << QVariant(job->errorText()); } else { (*resultResp) << QVariant(KJob::UserDefinedError); (*resultResp) << QVariant(QString(i18n("Internal error, undefined in result signal"))); } addEventResponse(resultResp); } void AbstractThreadedJob::slotProcessedAmount(KJob *, KJob::Unit unit, qulonglong xu) { setProcessedAmount(unit, xu); } void AbstractThreadedJob::slotTotalAmount(KJob *, KJob::Unit unit, qulonglong xu) { setTotalAmount(unit, xu); } void AbstractThreadedJob::slotSpeed(KJob *, unsigned long spd) { emitSpeed(spd); } void AbstractThreadedJob::slotDescription(KJob *, const QString &title, const QPair &field1, const QPair &field2) { QString mytitle = title; if (!_title.isNull()) mytitle = _title; emit description(this, mytitle, field1, field2); } class AbstractJobObserver : public KRarcObserver { protected: AbstractJobThread * _jobThread; public: explicit AbstractJobObserver(AbstractJobThread * thread): _jobThread(thread) {} ~AbstractJobObserver() override = default; void processEvents() Q_DECL_OVERRIDE { usleep(1000); qApp->processEvents(); } void subJobStarted(const QString & jobTitle, int count) Q_DECL_OVERRIDE { _jobThread->sendReset(jobTitle); _jobThread->sendMaxProgressValue(count); } void subJobStopped() Q_DECL_OVERRIDE { } bool wasCancelled() Q_DECL_OVERRIDE { return _jobThread->_exited; } void error(const QString & error) Q_DECL_OVERRIDE { _jobThread->sendError(KIO::ERR_NO_CONTENT, error); } void detailedError(const QString & error, const QString & details) Q_DECL_OVERRIDE { _jobThread->sendError(KIO::ERR_NO_CONTENT, error + '\n' + details); } void incrementProgress(int c) Q_DECL_OVERRIDE { _jobThread->sendAddProgress(c, _jobThread->_progressTitle); } }; AbstractJobThread::AbstractJobThread() : _job(nullptr), _downloadTempDir(nullptr), _observer(nullptr), _tempFile(nullptr), _tempDir(nullptr), _exited(false) { } AbstractJobThread::~AbstractJobThread() { if (_downloadTempDir) { delete _downloadTempDir; _downloadTempDir = nullptr; } if (_observer) { delete _observer; _observer = nullptr; } if (_tempFile) { delete _tempFile; _tempFile = nullptr; } } void AbstractJobThread::run() { QTimer::singleShot(0, this, &AbstractJobThread::slotStart); QPointer threadLoop = new QEventLoop(this); _loop = threadLoop; threadLoop->exec(); _loop = nullptr; delete threadLoop; } void AbstractJobThread::terminate() { if (_loop && !_exited) { _loop->quit(); _exited = true; } } void AbstractJobThread::abort() { terminate(); } QList AbstractJobThread::remoteUrls(const QUrl &baseUrl, const QStringList & files) { QList urlList; foreach(const QString &name, files) { QUrl url = baseUrl; url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (name)); urlList << url; } return urlList; } QUrl AbstractJobThread::downloadIfRemote(const QUrl &baseUrl, const QStringList & files) { // download remote URL-s if necessary if (!baseUrl.isLocalFile()) { sendInfo(i18n("Downloading remote files")); _downloadTempDir = new QTemporaryDir(); QList urlList = remoteUrls(baseUrl, files); QUrl dest(_downloadTempDir->path()); QList args; args << KrServices::toStringList(urlList); args << dest; auto * downloadEvent = new UserEvent(CMD_DOWNLOAD_FILES, args); QList * result = _job->getEventResponse(downloadEvent); if (result == nullptr) return QUrl(); auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return QUrl(); } else { return dest; } } else { return baseUrl; } } QString AbstractJobThread::tempFileIfRemote(const QUrl &kurl, const QString &type) { if (kurl.isLocalFile()) { return kurl.path(); } _tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.") + type); _tempFile->open(); _tempFileName = _tempFile->fileName(); _tempFile->close(); // necessary to create the filename QFile::remove(_tempFileName); _tempFileTarget = kurl; return _tempFileName; } QString AbstractJobThread::tempDirIfRemote(const QUrl &kurl) { if (kurl.isLocalFile()) { return kurl.adjusted(QUrl::StripTrailingSlash).path(); } _tempDir = new QTemporaryDir(); _tempDirTarget = kurl; return _tempDirName = _tempDir->path(); } void AbstractJobThread::sendSuccess() { terminate(); QList args; auto * errorEvent = new UserEvent(CMD_SUCCESS, args); _job->sendEvent(errorEvent); } -void AbstractJobThread::sendError(int errorCode, QString message) +void AbstractJobThread::sendError(int errorCode, const QString& message) { terminate(); QList args; args << errorCode; args << message; auto * errorEvent = new UserEvent(CMD_ERROR, args); _job->sendEvent(errorEvent); } -void AbstractJobThread::sendInfo(QString message, QString a1, QString a2, QString a3, QString a4) +void AbstractJobThread::sendInfo(const QString& message, const QString& a1, const QString& a2, const QString& a3, const QString& a4) { QList args; args << message; args << a1; args << a2; args << a3; args << a4; auto * infoEvent = new UserEvent(CMD_INFO, args); _job->sendEvent(infoEvent); } -void AbstractJobThread::sendReset(QString message, QString a1, QString a2, QString a3, QString a4) +void AbstractJobThread::sendReset(const QString& message, const QString& a1, const QString& a2, const QString& a3, const QString& a4) { QList args; args << message; args << a1; args << a2; args << a3; args << a4; auto * infoEvent = new UserEvent(CMD_RESET, args); _job->sendEvent(infoEvent); } void AbstractJobThread::sendMaxProgressValue(qulonglong value) { QList args; args << value; auto * infoEvent = new UserEvent(CMD_MAXPROGRESSVALUE, args); _job->sendEvent(infoEvent); } void AbstractJobThread::sendAddProgress(qulonglong value, const QString &progress) { QList args; args << value; if (!progress.isNull()) args << progress; auto * infoEvent = new UserEvent(CMD_ADD_PROGRESS, args); _job->sendEvent(infoEvent); } void countFiles(const QString &path, unsigned long &totalFiles, bool &stop) { const QDir dir(path); if (!dir.exists()) { totalFiles++; // assume it's a file return; } for (const QString name : dir.entryList()) { if (stop) return; if (name == QStringLiteral(".") || name == QStringLiteral("..")) continue; countFiles(dir.absoluteFilePath(name), totalFiles, stop); } } void AbstractJobThread::countLocalFiles(const QUrl &baseUrl, const QStringList &names, unsigned long &totalFiles) { sendReset(i18n("Counting files")); FileSystem *calcSpaceFileSystem = FileSystemProvider::instance().getFilesystem(baseUrl); calcSpaceFileSystem->scanDir(baseUrl); for (const QString name : names) { if (_exited) return; const QString path = calcSpaceFileSystem->getUrl(name).toLocalFile(); if (!QFileInfo(path).exists()) return; countFiles(path, totalFiles, _exited); } delete calcSpaceFileSystem; } KRarcObserver * AbstractJobThread::observer() { if (_observer) return _observer; _observer = new AbstractJobObserver(this); return _observer; } bool AbstractJobThread::uploadTempFiles() { if (_tempFile != nullptr || _tempDir != nullptr) { sendInfo(i18n("Uploading to remote destination")); if (_tempFile) { QList urlList; urlList << QUrl::fromLocalFile(_tempFileName); QList args; args << KrServices::toStringList(urlList); args << _tempFileTarget; auto * uploadEvent = new UserEvent(CMD_UPLOAD_FILES, args); QList * result = _job->getEventResponse(uploadEvent); if (result == nullptr) return false; auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return false; } } if (_tempDir) { QList urlList; QDir tempDir(_tempDirName); QStringList list = tempDir.entryList(); foreach(const QString &name, list) { if (name == "." || name == "..") continue; QUrl url = QUrl::fromLocalFile(_tempDirName).adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (name)); urlList << url; } QList args; args << KrServices::toStringList(urlList); args << _tempDirTarget; auto * uploadEvent = new UserEvent(CMD_UPLOAD_FILES, args); QList * result = _job->getEventResponse(uploadEvent); if (result == nullptr) return false; auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return false; } } } return true; } QString AbstractJobThread::getPassword(const QString &path) { QList args; args << path; auto * getPasswdEvent = new UserEvent(CMD_GET_PASSWORD, args); QList * result = _job->getEventResponse(getPasswdEvent); if (result == nullptr) return QString(); QString password = (*result)[ 0 ].value(); if (password.isNull()) password = QString(""); delete result; return password; } void AbstractJobThread::sendMessage(const QString &message) { QList args; args << message; auto * getPasswdEvent = new UserEvent(CMD_MESSAGE, args); QList * result = _job->getEventResponse(getPasswdEvent); if (result == nullptr) return; delete result; } //! Gets some archive information that is needed in several cases. /*! \param path A path to the archive. \param type The type of the archive. \param password The password of the archive. \param arcName The name of the archive. \param sourceFolder A QUrl, which may be remote, of the folder where the archive is. \return If the archive information has been obtained. */ bool AbstractJobThread::getArchiveInformation(QString &path, QString &type, QString &password, QString &arcName, const QUrl &sourceFolder) { // Safety checks (though the user shouldn't have been able to select something named "" or "..") if (arcName.isEmpty()) return false; if (arcName == "..") return false; QUrl url = sourceFolder.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + arcName); path = url.adjusted(QUrl::StripTrailingSlash).path(); QMimeDatabase db; QMimeType mt = db.mimeTypeForUrl(url); QString mime = mt.isValid() ? mt.name() : QString(); bool encrypted = false; type = arcHandler.getType(encrypted, path, mime); // Check that the archive is supported if (!KRarcHandler::arcSupported(type)) { sendError(KIO::ERR_NO_CONTENT, i18nc("%1=archive filename", "%1, unsupported archive type.", arcName)); return false; } password = encrypted ? getPassword(path) : QString(); return true; } diff --git a/krusader/Archive/abstractthreadedjob.h b/krusader/Archive/abstractthreadedjob.h index 4ea06ad0..85af4e5c 100644 --- a/krusader/Archive/abstractthreadedjob.h +++ b/krusader/Archive/abstractthreadedjob.h @@ -1,188 +1,188 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * * Copyright (C) 2009-2018 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 ABSTRACTTHREADEDJOB_H #define ABSTRACTTHREADEDJOB_H // QtCore #include #include #include #include #include #include #include #include #include #include #include #include class AbstractJobThread; class QTemporaryDir; class UserEvent; class KRarcObserver; class QTemporaryFile; class AbstractThreadedJob : public KIO::Job { friend class AbstractJobThread; Q_OBJECT protected: AbstractThreadedJob(); void addEventResponse(QList * obj); QList * getEventResponse(UserEvent * event); void sendEvent(UserEvent * event); virtual ~AbstractThreadedJob(); virtual bool event(QEvent *) Q_DECL_OVERRIDE; virtual void startAbstractJobThread(AbstractJobThread*); virtual bool doSuspend() Q_DECL_OVERRIDE { return false; } protected slots: void slotDownloadResult(KJob*); void slotProcessedAmount(KJob *, KJob::Unit, qulonglong); void slotTotalAmount(KJob *, KJob::Unit, qulonglong); void slotSpeed(KJob *, unsigned long); void slotDescription(KJob *job, const QString &title, const QPair &field1, const QPair &field2); public: QMutex _locker; QWaitCondition _waiter; QStack *> _stack; QString _title; qulonglong _maxProgressValue; qulonglong _currentProgress; QTime _time; bool _exiting; private: AbstractJobThread * _jobThread; }; class AbstractJobThread : public QThread { friend class AbstractThreadedJob; friend class AbstractJobObserver; Q_OBJECT public: AbstractJobThread(); virtual ~AbstractJobThread(); void abort(); KRarcObserver * observer(); protected slots: virtual void slotStart() = 0; protected: virtual void run() Q_DECL_OVERRIDE; void setJob(AbstractThreadedJob * job) { _job = job; } QList remoteUrls(const QUrl &baseUrl, const QStringList & files); QUrl downloadIfRemote(const QUrl &baseUrl, const QStringList & files); void countLocalFiles(const QUrl &baseUrl, const QStringList &names, unsigned long &totalFiles); - void sendError(int errorCode, QString message); - void sendInfo(QString message, QString a1 = QString(), QString a2 = QString(), QString a3 = QString(), QString a4 = QString()); - void sendReset(QString message, QString a1 = QString(""), QString a2 = QString(""), QString a3 = QString(""), QString a4 = QString("")); + void sendError(int errorCode, const QString& message); + void sendInfo(const QString& message, const QString& a1 = QString(), const QString& a2 = QString(), const QString& a3 = QString(), const QString& a4 = QString()); + void sendReset(const QString& message, const QString& a1 = QString(""), const QString& a2 = QString(""), const QString& a3 = QString(""), const QString& a4 = QString("")); void sendSuccess(); void sendMessage(const QString &message); void sendMaxProgressValue(qulonglong value); void sendAddProgress(qulonglong value, const QString &progress = QString()); void setProgressTitle(const QString &title) { _progressTitle = title; } QString tempFileIfRemote(const QUrl &kurl, const QString &type); QString tempDirIfRemote(const QUrl &kurl); bool uploadTempFiles(); bool isExited() { return _exited; } void terminate(); QString getPassword(const QString &path); bool getArchiveInformation(QString &, QString &, QString &, QString &, const QUrl &); AbstractThreadedJob *_job; QEventLoop *_loop; QTemporaryDir *_downloadTempDir; KRarcObserver *_observer; QTemporaryFile *_tempFile; QString _tempFileName; QUrl _tempFileTarget; QTemporaryDir *_tempDir; QString _tempDirName; QUrl _tempDirTarget; bool _exited; QString _progressTitle; }; enum PossibleCommands { CMD_ERROR = 1, CMD_INFO = 2, CMD_RESET = 3, CMD_DOWNLOAD_FILES = 4, CMD_UPLOAD_FILES = 5, CMD_SUCCESS = 6, CMD_MAXPROGRESSVALUE = 7, CMD_ADD_PROGRESS = 8, CMD_GET_PASSWORD = 9, CMD_MESSAGE = 10 }; class UserEvent : public QEvent { public: UserEvent(int command, const QList &args) : QEvent(QEvent::User), _command(command), _args(args) {} inline int command() { return _command; } inline const QList & args() { return _args; } protected: int _command; QList _args; }; #endif // __ABSTRACTTHREADED_JOB_H__ diff --git a/krusader/Archive/krarchandler.cpp b/krusader/Archive/krarchandler.cpp index e58a7c68..3648e57e 100644 --- a/krusader/Archive/krarchandler.cpp +++ b/krusader/Archive/krarchandler.cpp @@ -1,668 +1,669 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 "krarchandler.h" // QtCore #include #include #include // QtWidgets #include #include #include #include #include #include #include #include +#include #include "kr7zencryptionchecker.h" #include "../krglobal.h" #include "../defaults.h" #include "../krservices.h" #include "../Dialogs/krpleasewait.h" #include "../../krArc/krlinecountingprocess.h" #if 0 class DefaultKRarcObserver : public KRarcObserver { public: DefaultKRarcObserver() {} virtual ~DefaultKRarcObserver() {} virtual void processEvents() Q_DECL_OVERRIDE { usleep(1000); qApp->processEvents(); } virtual void subJobStarted(const QString & jobTitle, int count) Q_DECL_OVERRIDE { krApp->startWaiting(jobTitle, count, true); } virtual void subJobStopped() Q_DECL_OVERRIDE { krApp->stopWait(); } virtual bool wasCancelled() Q_DECL_OVERRIDE { return krApp->wasWaitingCancelled(); } virtual void error(const QString & error) Q_DECL_OVERRIDE { KMessageBox::error(krApp, error, i18n("Error")); } virtual void detailedError(const QString & error, const QString & details) Q_DECL_OVERRIDE { KMessageBox::detailedError(krApp, error, details, i18n("Error")); } virtual void incrementProgress(int c) Q_DECL_OVERRIDE { krApp->plzWait->incProgress(c); } }; #endif static QStringList arcProtocols = QString("tar;bzip;bzip2;lzma;xz;gzip;krarc;zip").split(';'); KWallet::Wallet * KRarcHandler::wallet = nullptr; QStringList KRarcHandler::supportedPackers() { QStringList packers; // we will simply try to find the packers here.. if (KrServices::cmdExist("tar")) packers.append("tar"); if (KrServices::cmdExist("gzip")) packers.append("gzip"); if (KrServices::cmdExist("bzip2")) packers.append("bzip2"); if (KrServices::cmdExist("lzma")) packers.append("lzma"); if (KrServices::cmdExist("xz")) packers.append("xz"); if (KrServices::cmdExist("unzip")) packers.append("unzip"); if (KrServices::cmdExist("zip")) packers.append("zip"); if (KrServices::cmdExist("zip")) packers.append("cbz"); if (KrServices::cmdExist("lha")) packers.append("lha"); if (KrServices::cmdExist("cpio")) packers.append("cpio"); if (KrServices::cmdExist("unrar")) packers.append("unrar"); if (KrServices::cmdExist("rar")) packers.append("rar"); if (KrServices::cmdExist("rar")) packers.append("cbr"); if (KrServices::cmdExist("arj")) packers.append("arj"); if (KrServices::cmdExist("unarj")) packers.append("unarj"); if (KrServices::cmdExist("unace")) packers.append("unace"); if (KrServices::cmdExist("dpkg")) packers.append("dpkg"); if (KrServices::cmdExist("7z") || KrServices::cmdExist("7za")) packers.append("7z"); if (KrServices::cmdExist("rpm") && KrServices::cmdExist("rpm2cpio")) packers.append("rpm"); // qDebug() << "Supported Packers:"; //QStringList::Iterator it; //for( it = packers.begin(); it != packers.end(); ++it ) // qDebug() << *it; return packers; } bool KRarcHandler::arcSupported(QString type) { // lst will contain the supported unpacker list... const KConfigGroup group(krConfig, "Archives"); const QStringList lst = group.readEntry("Supported Packers", QStringList()); // Let's notice that in some cases the QString `type` that arrives here // represents a mimetype, and in some other cases it represents // a short identifier. // If `type` is not a short identifier then it's supposed that `type` is a mime type if (type.length() > maxLenType) { type = getShortTypeFromMime(type); } return (type == "zip" && lst.contains("unzip")) || (type == "tar" && lst.contains("tar")) || (type == "tbz" && lst.contains("tar")) || (type == "tgz" && lst.contains("tar")) || (type == "tlz" && lst.contains("tar")) || (type == "txz" && lst.contains("tar")) || (type == "tarz" && lst.contains("tar")) || (type == "gzip" && lst.contains("gzip")) || (type == "bzip2" && lst.contains("bzip2")) || (type == "lzma" && lst.contains("lzma")) || (type == "xz" && lst.contains("xz")) || (type == "lha" && lst.contains("lha")) || (type == "ace" && lst.contains("unace")) || (type == "rpm" && lst.contains("cpio")) || (type == "cpio" && lst.contains("cpio")) || (type == "rar" && (lst.contains("unrar") || lst.contains("rar"))) || (type == "arj" && (lst.contains("unarj") || lst.contains("arj"))) || (type == "deb" && (lst.contains("dpkg") && lst.contains("tar"))) || (type == "7z" && lst.contains("7z")); } -long KRarcHandler::arcFileCount(QString archive, QString type, QString password, KRarcObserver *observer) +long KRarcHandler::arcFileCount(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer) { int divideWith = 1; // first check if supported if (!arcSupported(type)) return 0; // bzip2, gzip, etc. archives contain only one file if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") return 1L; // set the right lister to do the job QStringList lister; if (type == "zip") lister << KrServices::fullPathName("unzip") << "-ZTs"; else if (type == "tar") lister << KrServices::fullPathName("tar") << "-tvf"; else if (type == "tgz") lister << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tarz") lister << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tbz") lister << KrServices::fullPathName("tar") << "-tjvf"; else if (type == "tlz") lister << KrServices::fullPathName("tar") << "--lzma" << "-tvf"; else if (type == "txz") lister << KrServices::fullPathName("tar") << "--xz" << "-tvf"; else if (type == "lha") lister << KrServices::fullPathName("lha") << "l"; else if (type == "rar") lister << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "l" << "-v"; else if (type == "ace") lister << KrServices::fullPathName("unace") << "l"; else if (type == "arj") { if (KrServices::cmdExist("arj")) lister << KrServices::fullPathName("arj") << "v" << "-y" << "-v", divideWith = 4; else lister << KrServices::fullPathName("unarj") << "l"; } else if (type == "rpm") lister << KrServices::fullPathName("rpm") << "--dump" << "-lpq"; else if (type == "deb") lister << KrServices::fullPathName("dpkg") << "-c"; else if (type == "7z") lister << KrServices::fullPathName("7z") << "-y" << "l"; else return 0L; if (!password.isNull()) { if (type == "arj") lister << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") lister << QString("-p%1").arg(password); } // tell the user to wait observer->subJobStarted(i18n("Counting files in archive"), 0); // count the number of files in the archive long count = 1; KProcess list; list << lister << archive; if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! list.setStandardInputFile("/dev/ptmx"); list.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect list.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (list.waitForStarted()) while (list.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) list.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); if (list.exitStatus() != QProcess::NormalExit || !checkStatus(type, list.exitCode())) { observer->detailedError(i18n("Failed to list the content of the archive (%1).", archive), QString::fromLocal8Bit(list.readAllStandardError())); return 0; } count = list.readAllStandardOutput().count('\n'); //make sure you call stopWait after this function return... // observer->subJobStopped(); return count / divideWith; } -bool KRarcHandler::unpack(QString archive, QString type, QString password, QString dest, KRarcObserver *observer) +bool KRarcHandler::unpack(QString archive, const QString& type, const QString& password, const QString& dest, KRarcObserver *observer) { KConfigGroup group(krConfig, "Archives"); if (group.readEntry("Test Before Unpack", _TestBeforeUnpack)) { // test first - or be sorry later... if (type != "rpm" && type != "deb" && !test(archive, type, password, observer, 0)) { observer->error(i18n("Failed to unpack %1.", archive)); return false; } } // count the files in the archive long count = arcFileCount(archive, type, password, observer); if (count == 0) return false; // not supported if (count == 1) count = 0; // choose the right packer for the job QString cpioName; QStringList packer; // set the right packer to do the job if (type == "zip") packer << KrServices::fullPathName("unzip") << "-o"; else if (type == "tar") packer << KrServices::fullPathName("tar") << "-xvf"; else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-xvzf"; else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-xvzf"; else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-xjvf"; else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-xvf"; else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-xvf"; else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-cd"; else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-cdk"; else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-cdk"; else if (type == "xz") packer << KrServices::fullPathName("xz") << "-cdk"; else if (type == "lha") packer << KrServices::fullPathName("lha") << "xf"; else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "-y" << "x"; else if (type == "ace") packer << KrServices::fullPathName("unace") << "x"; else if (type == "arj") { if (KrServices::cmdExist("arj")) packer << KrServices::fullPathName("arj") << "-y" << "-v" << "x"; else packer << KrServices::fullPathName("unarj") << "x"; } else if (type == "7z") packer << KrServices::fullPathName("7z") << "-y" << "x"; else if (type == "rpm") { // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone) cpioName = QDir::tempPath() + QStringLiteral("/contents.cpio"); KrLinecountingProcess cpio; cpio << KrServices::fullPathName("rpm2cpio") << archive; cpio.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer)) cpio.start(); if (!cpio.waitForFinished() || cpio.exitStatus() != QProcess::NormalExit || !checkStatus("cpio", cpio.exitCode())) { observer->detailedError(i18n("Failed to convert rpm (%1) to cpio.", archive), cpio.getErrorMsg()); return 0; } archive = cpioName; packer << KrServices::fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" << "-iuvdF"; } else if (type == "deb") { // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone) cpioName = QDir::tempPath() + QStringLiteral("/contents.tar"); KrLinecountingProcess dpkg; dpkg << KrServices::fullPathName("dpkg") << "--fsys-tarfile" << archive; dpkg.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer)) dpkg.start(); if (!dpkg.waitForFinished() || dpkg.exitStatus() != QProcess::NormalExit || !checkStatus("deb", dpkg.exitCode())) { observer->detailedError(i18n("Failed to convert deb (%1) to tar.", archive), dpkg.getErrorMsg()); return 0; } archive = cpioName; packer << KrServices::fullPathName("tar") << "xvf"; } else return false; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; if (type == "arj") packer << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") packer << QString("-p%1").arg(password); } // unpack the files KrLinecountingProcess proc; proc << packer << archive; if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") { QString arcname = archive.mid(archive.lastIndexOf("/") + 1); if (arcname.contains(".")) arcname = arcname.left(arcname.lastIndexOf(".")); proc.setStandardOutputFile(dest + '/' + arcname); } if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setWorkingDirectory(dest); // tell the user to wait observer->subJobStarted(i18n("Unpacking File(s)"), count); if (count != 0) { connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); if (type == "rpm") connect(&proc, &KrLinecountingProcess::newErrorLines, observer, &KRarcObserver::incrementProgress); } // start the unpacking process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); if (!cpioName.isEmpty()) QFile(cpioName).remove(); /* remove the cpio file */ // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) { observer->detailedError(i18n("Failed to unpack %1.", archive), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg()); return false; } return true; // SUCCESS } -bool KRarcHandler::test(QString archive, QString type, QString password, KRarcObserver *observer, long count) +bool KRarcHandler::test(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer, long count) { // choose the right packer for the job QStringList packer; // set the right packer to do the job if (type == "zip") packer << KrServices::fullPathName("unzip") << "-t"; else if (type == "tar") packer << KrServices::fullPathName("tar") << "-tvf"; else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-tjvf"; else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-tvf"; else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-tvf"; else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-tv"; else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-tv"; else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-tv"; else if (type == "xz") packer << KrServices::fullPathName("xz") << "-tv"; else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "t"; else if (type == "ace") packer << KrServices::fullPathName("unace") << "t"; else if (type == "lha") packer << KrServices::fullPathName("lha") << "t"; else if (type == "arj") packer << KrServices::fullPathName(KrServices::cmdExist("arj") ? "arj" : "unarj") << "t"; else if (type == "cpio") packer << KrServices::fullPathName("cpio") << "--only-verify-crc" << "-tvF"; else if (type == "7z") packer << KrServices::fullPathName("7z") << "-y" << "t"; else return false; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; if (type == "arj") packer << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") packer << QString("-p%1").arg(password); } // unpack the files KrLinecountingProcess proc; proc << packer << archive; if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); // tell the user to wait observer->subJobStarted(i18n("Testing Archive"), count); if (count != 0) connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); // start the unpacking process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) return false; return true; // SUCCESS } -bool KRarcHandler::pack(QStringList fileNames, QString type, QString dest, long count, QMap extraProps, KRarcObserver *observer) +bool KRarcHandler::pack(QStringList fileNames, QString type, const QString& dest, long count, QMap extraProps, KRarcObserver *observer) { // set the right packer to do the job QStringList packer; if (type == "zip") { packer << KrServices::fullPathName("zip") << "-ry"; } else if (type == "cbz") { packer << KrServices::fullPathName("zip") << "-ry"; type = "zip"; } else if (type == "tar") { packer << KrServices::fullPathName("tar") << "-cvf"; } else if (type == "tar.gz") { packer << KrServices::fullPathName("tar") << "-cvzf"; type = "tgz"; } else if (type == "tar.bz2") { packer << KrServices::fullPathName("tar") << "-cvjf"; type = "tbz"; } else if (type == "tar.lzma") { packer << KrServices::fullPathName("tar") << "--lzma" << "-cvf"; type = "tlz"; } else if (type == "tar.xz") { packer << KrServices::fullPathName("tar") << "--xz" << "-cvf"; type = "txz"; } else if (type == "rar") { packer << KrServices::fullPathName("rar") << "-r" << "a"; } else if (type == "cbr") { packer << KrServices::fullPathName("rar") << "-r" << "a"; type = "rar"; } else if (type == "lha") { packer << KrServices::fullPathName("lha") << "a"; } else if (type == "arj") { packer << KrServices::fullPathName("arj") << "-r" << "-y" << "a"; } else if (type == "7z") { packer << KrServices::fullPathName("7z") << "-y" << "a"; } else return false; QString password; if (extraProps.count("Password") > 0) { password = extraProps[ "Password" ]; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; else if (type == "arj") packer << QString("-g%1").arg(password); else if (type == "ace" || type == "7z") packer << QString("-p%1").arg(password); else if (type == "rar") { if (extraProps.count("EncryptHeaders") > 0) packer << QString("-hp%1").arg(password); else packer << QString("-p%1").arg(password); } else password.clear(); } } if (extraProps.count("VolumeSize") > 0) { QString sizeStr = extraProps[ "VolumeSize" ]; KIO::filesize_t size = sizeStr.toLongLong(); if (size >= 10000) { if (type == "arj" || type == "rar") packer << QString("-v%1b").arg(sizeStr); } } if (extraProps.count("CompressionLevel") > 0) { int level = extraProps[ "CompressionLevel" ].toInt() - 1; if (level < 0) level = 0; if (level > 8) level = 8; if (type == "rar") { static const int rarLevels[] = { 0, 1, 2, 2, 3, 3, 4, 4, 5 }; packer << QString("-m%1").arg(rarLevels[ level ]); } else if (type == "arj") { static const int arjLevels[] = { 0, 4, 4, 3, 3, 2, 2, 1, 1 }; packer << QString("-m%1").arg(arjLevels[ level ]); } else if (type == "zip") { static const int zipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 }; packer << QString("-%1").arg(zipLevels[ level ]); } else if (type == "7z") { static const int sevenZipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 }; packer << QString("-mx%1").arg(sevenZipLevels[ level ]); } } if (extraProps.count("CommandLineSwitches") > 0) packer << QString("%1").arg(extraProps[ "CommandLineSwitches" ]); // prepare to pack KrLinecountingProcess proc; proc << packer << dest; for (auto & fileName : fileNames) { proc << fileName; } // tell the user to wait observer->subJobStarted(i18n("Packing File(s)"), count); if (count != 0) connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); // start the packing process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) { observer->detailedError(i18n("Failed to pack %1.", dest), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg()); return false; } KConfigGroup group(krConfig, "Archives"); if (group.readEntry("Test Archives", _TestArchives) && !test(dest, type, password, observer, count)) { observer->error(i18n("Failed to pack %1.", dest)); return false; } return true; // SUCCESS } bool KRarcHandler::openWallet() { if (!wallet) { // find a suitable parent window QWidget *actWindow = QApplication::activeWindow(); if (!actWindow) actWindow = (QWidget*) QApplication::desktop(); wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), actWindow->effectiveWinId()); } return (wallet != nullptr); } -QString KRarcHandler::getPassword(QString path) +QString KRarcHandler::getPassword(const QString& path) { QString password; QString key = "krarc-" + path; if (!KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), key)) { if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) { delete wallet; wallet = nullptr; } if (openWallet() && wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->setFolder(KWallet::Wallet::PasswordFolder()); QMap map; if (wallet->readMap(key, map) == 0) { QMap::const_iterator it = map.constFind("password"); if (it != map.constEnd()) password = it.value(); } } } bool keep = true; QString user = "archive"; QPointer passDlg = new KPasswordDialog(nullptr, KPasswordDialog::ShowKeepPassword); passDlg->setPrompt(i18n("This archive is encrypted, please supply the password:") ), passDlg->setUsername(user); passDlg->setPassword(password); if (passDlg->exec() == KPasswordDialog::Accepted) { password = passDlg->password(); if (keep) { if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) { delete wallet; wallet = nullptr; } if (openWallet()) { bool ok = true; if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder())) ok = wallet->createFolder(KWallet::Wallet::PasswordFolder()); if (ok) { wallet->setFolder(KWallet::Wallet::PasswordFolder()); QMap map; map.insert("login", "archive"); map.insert("password", password); wallet->writeMap(key, map); } } } delete passDlg; return password; } delete passDlg; return ""; } bool KRarcHandler::isArchive(const QUrl &url) { QString protocol = url.scheme(); if (arcProtocols.indexOf(protocol) != -1) return true; else return false; } -QString KRarcHandler::getType(bool &encrypted, QString fileName, QString mime, bool checkEncrypted, bool fast) +QString KRarcHandler::getType(bool &encrypted, QString fileName, const QString& mime, bool checkEncrypted, bool fast) { - QString result = detectArchive(encrypted, fileName, checkEncrypted, fast); + QString result = detectArchive(encrypted, std::move(fileName), checkEncrypted, fast); if (result.isNull()) { // Then the type is based on the mime type return getShortTypeFromMime(mime); } return result; } -bool KRarcHandler::checkStatus(QString type, int exitCode) +bool KRarcHandler::checkStatus(const QString& type, int exitCode) { return KrArcBaseManager::checkStatus(type, exitCode); } void KRarcHandler::checkIf7zIsEncrypted(bool &encrypted, QString fileName) { Kr7zEncryptionChecker proc; // TODO incorporate all this in Kr7zEncryptionChecker proc << KrServices::fullPathName("7z") << "-y" << "t"; proc << fileName; proc.start(); proc.waitForFinished(); encrypted = proc.isEncrypted(); } diff --git a/krusader/Archive/krarchandler.h b/krusader/Archive/krarchandler.h index d031a236..7e641bd6 100644 --- a/krusader/Archive/krarchandler.h +++ b/krusader/Archive/krarchandler.h @@ -1,87 +1,87 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 KRARCHANDLER_H #define KRARCHANDLER_H // QtCore #include #include #include #include #include "../../krArc/krarcbasemanager.h" namespace KWallet { class Wallet; } class KRarcObserver : public QObject { Q_OBJECT public: ~KRarcObserver() override = default; virtual void processEvents() = 0; virtual void subJobStarted(const QString & jobTitle, int count) = 0; virtual void subJobStopped() = 0; virtual bool wasCancelled() = 0; virtual void error(const QString & error) = 0; virtual void detailedError(const QString & error, const QString & details) = 0; public slots: virtual void incrementProgress(int) = 0; }; class KRarcHandler: public QObject, public KrArcBaseManager { Q_OBJECT public: // return the number of files in the archive - static long arcFileCount(QString archive, QString type, QString password, KRarcObserver *observer); + static long arcFileCount(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer); // unpack an archive to destination directory - static bool unpack(QString archive, QString type, QString password, QString dest, KRarcObserver *observer ); + static bool unpack(QString archive, const QString& type, const QString& password, const QString& dest, KRarcObserver *observer ); // pack an archive to destination directory - static bool pack(QStringList fileNames, QString type, QString dest, long count, QMap extraProps, KRarcObserver *observer ); + static bool pack(QStringList fileNames, QString type, const QString& dest, long count, QMap extraProps, KRarcObserver *observer ); // test an archive - static bool test(QString archive, QString type, QString password, KRarcObserver *observer, long count = 0L ); + static bool test(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer, long count = 0L ); // returns `true` if the right unpacker exist in the system static bool arcSupported(QString type); // return the list of supported packers static QStringList supportedPackers(); // returns `true` if the url is an archive (ie: tar:/home/test/file.tar.bz2) static bool isArchive(const QUrl &url); // used to determine the type of the archive - QString getType(bool &encrypted, QString fileName, QString mime, bool checkEncrypted = true, bool fast = false); + QString getType(bool &encrypted, QString fileName, const QString& mime, bool checkEncrypted = true, bool fast = false); // queries the password from the user - static QString getPassword(QString path); + static QString getPassword(const QString& path); // detects the archive type void checkIf7zIsEncrypted(bool &, QString) Q_DECL_OVERRIDE; private: //! checks if a returned status ("exit code") of an archiving-related process is OK - static bool checkStatus(QString type, int exitCode); + static bool checkStatus(const QString& type, int exitCode); static bool openWallet(); static KWallet::Wallet * wallet; }; #endif diff --git a/krusader/BookMan/kraddbookmarkdlg.cpp b/krusader/BookMan/kraddbookmarkdlg.cpp index 658df617..21b69c60 100644 --- a/krusader/BookMan/kraddbookmarkdlg.cpp +++ b/krusader/BookMan/kraddbookmarkdlg.cpp @@ -1,186 +1,186 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "kraddbookmarkdlg.h" #include "../krglobal.h" #include "../icon.h" #include "krbookmarkhandler.h" // QtWidgets #include #include #include #include #include #include #include -KrAddBookmarkDlg::KrAddBookmarkDlg(QWidget *parent, QUrl url): +KrAddBookmarkDlg::KrAddBookmarkDlg(QWidget *parent, const QUrl& url): QDialog(parent) { setWindowModality(Qt::WindowModal); setWindowTitle(i18n("Add Bookmark")); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *layout = new QGridLayout; // expanding // name and url QLabel *lb1 = new QLabel(i18n("Name:"), this); _name = new KLineEdit(this); _name->setText(url.toDisplayString()); // default name is the url _name->selectAll(); // make the text selected layout->addWidget(lb1, 0, 0); layout->addWidget(_name, 0, 1); QLabel *lb2 = new QLabel(i18n("URL:"), this); _url = new KLineEdit(this); layout->addWidget(lb2, 1, 0); layout->addWidget(_url, 1, 1); _url->setText(url.toDisplayString()); // set the url in the field // create in linedit and button QLabel *lb3 = new QLabel(i18n("Create in:"), this); _folder = new KLineEdit(this); layout->addWidget(lb3, 2, 0); layout->addWidget(_folder, 2, 1); _folder->setReadOnly(true); _createInBtn = new QToolButton(this); _createInBtn->setIcon(Icon("go-down")); _createInBtn->setCheckable(true); connect(_createInBtn, &QToolButton::toggled, this, &KrAddBookmarkDlg::toggleCreateIn); layout->addWidget(_createInBtn, 2, 2); mainLayout->addLayout(layout); detailsWidget = createInWidget(); detailsWidget->setVisible(false); mainLayout->addWidget(detailsWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); newFolderButton = new QPushButton(i18n("New Folder")); buttonBox->addButton(newFolderButton, QDialogButtonBox::ActionRole); newFolderButton->setVisible(false);// hide it until _createIn is shown connect(newFolderButton, &QPushButton::clicked, this, &KrAddBookmarkDlg::newFolder); connect(buttonBox, &QDialogButtonBox::accepted, this, &KrAddBookmarkDlg::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &KrAddBookmarkDlg::reject); _name->setFocus(); resize(sizeHint().width() * 2, sizeHint().height()); } void KrAddBookmarkDlg::toggleCreateIn(bool show) { _createInBtn->setIcon(Icon(show ? "go-up" : "go-down")); newFolderButton->setVisible(show); detailsWidget->setVisible(show); } // creates the widget that lets you decide where to put the new bookmark QWidget *KrAddBookmarkDlg::createInWidget() { _createIn = new KrTreeWidget(this); _createIn->setHeaderLabel(i18n("Folders")); _createIn->header()->hide(); _createIn->setRootIsDecorated(true); _createIn->setAlternatingRowColors(false); // disable alternate coloring auto *item = new QTreeWidgetItem(_createIn); item->setText(0, i18n("Bookmarks")); _createIn->expandItem(item); item->setSelected(true); _xr[item] = krBookMan->_root; populateCreateInWidget(krBookMan->_root, item); _createIn->setCurrentItem(item); slotSelectionChanged(); connect(_createIn, &KrTreeWidget::itemSelectionChanged, this, &KrAddBookmarkDlg::slotSelectionChanged); return _createIn; } void KrAddBookmarkDlg::slotSelectionChanged() { QList items = _createIn->selectedItems(); if (items.count() > 0) { _folder->setText(_xr[ items[ 0 ] ]->text()); } } void KrAddBookmarkDlg::populateCreateInWidget(KrBookmark *root, QTreeWidgetItem *parent) { QListIterator it(root->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) { auto *item = new QTreeWidgetItem(parent); item->setText(0, bm->text()); item->treeWidget()->expandItem(item); _xr[item] = bm; populateCreateInWidget(bm, item); } } } void KrAddBookmarkDlg::newFolder() { // get the name QString newFolder = QInputDialog::getText(this, i18n("New Folder"), i18n("Folder name:")); if (newFolder.isEmpty()) { return; } QList items = _createIn->selectedItems(); if (items.count() == 0) return; // add to the list in bookman KrBookmark *bm = new KrBookmark(newFolder); krBookMan->addBookmark(bm, _xr[ items[ 0 ]]); // fix the gui auto *item = new QTreeWidgetItem(items[ 0 ]); item->setText(0, bm->text()); _xr[item] = bm; _createIn->setCurrentItem(item); item->setSelected(true); } KrBookmark * KrAddBookmarkDlg::folder() const { QList items = _createIn->selectedItems(); if (items.count() == 0) return nullptr; return _xr[ items[ 0 ] ]; } diff --git a/krusader/BookMan/kraddbookmarkdlg.h b/krusader/BookMan/kraddbookmarkdlg.h index 7997cae6..730eebe9 100644 --- a/krusader/BookMan/kraddbookmarkdlg.h +++ b/krusader/BookMan/kraddbookmarkdlg.h @@ -1,70 +1,70 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRADDBOOKMARKDLG_H #define KRADDBOOKMARKDLG_H #include "krbookmark.h" #include "../GUI/krtreewidget.h" // QtCore #include #include // QtWidgets #include #include #include class KrAddBookmarkDlg: public QDialog { Q_OBJECT public: - explicit KrAddBookmarkDlg(QWidget *parent, QUrl url = QUrl()); + explicit KrAddBookmarkDlg(QWidget *parent, const QUrl& url = QUrl()); QUrl url() const { return QUrl::fromUserInput(_url->text(), QString(), QUrl::AssumeLocalFile); } QString name() const { return _name->text(); } KrBookmark *folder() const; protected: QWidget *createInWidget(); void populateCreateInWidget(KrBookmark *root, QTreeWidgetItem *parent); protected slots: void toggleCreateIn(bool show); void slotSelectionChanged(); void newFolder(); private: KLineEdit *_name; KLineEdit *_url; KLineEdit *_folder; KrTreeWidget *_createIn; QMap _xr; QToolButton *_createInBtn; QPushButton *newFolderButton; QWidget *detailsWidget; }; #endif // KRADDBOOKMARKDLG_H diff --git a/krusader/BookMan/krbookmark.cpp b/krusader/BookMan/krbookmark.cpp index 22f5e287..f0afe041 100644 --- a/krusader/BookMan/krbookmark.cpp +++ b/krusader/BookMan/krbookmark.cpp @@ -1,151 +1,152 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krbookmark.h" #include "../krglobal.h" #include "../icon.h" #include "../Archive/krarchandler.h" #include "../FileSystem/krtrashhandler.h" #include "../Panel/listpanelactions.h" #include #include +#include #define BM_NAME(X) (QString("Bookmark:")+X) static const char* NAME_TRASH = I18N_NOOP("Trash bin"); static const char* NAME_VIRTUAL = I18N_NOOP("Virtual Filesystem"); static const char* NAME_LAN = I18N_NOOP("Local Network"); -KrBookmark::KrBookmark(QString name, QUrl url, KActionCollection *parent, QString iconName, QString actionName) : - QAction(parent), _url(url), _iconName(iconName), _folder(false), _separator(false), _autoDelete(true) +KrBookmark::KrBookmark(const QString& name, QUrl url, KActionCollection *parent, const QString& iconName, const QString& actionName) : + QAction(parent), _url(std::move(url)), _iconName(iconName), _folder(false), _separator(false), _autoDelete(true) { QString actName = actionName.isNull() ? BM_NAME(name) : BM_NAME(actionName); setText(name); parent->addAction(actName, this); connect(this, &KrBookmark::triggered, this, &KrBookmark::activatedProxy); setIconName(iconName); } -KrBookmark::KrBookmark(QString name, QString iconName) : +KrBookmark::KrBookmark(const QString& name, const QString& iconName) : QAction(Icon(iconName), name, nullptr), _iconName(iconName), _folder(true), _separator(false), _autoDelete(false) { setIcon(Icon(iconName == "" ? "folder" : iconName)); } KrBookmark::~KrBookmark() { if (_autoDelete) { QListIterator it(_children); while (it.hasNext()) delete it.next(); _children.clear(); } } -void KrBookmark::setIconName(QString iconName) +void KrBookmark::setIconName(const QString& iconName) { if (!iconName.isEmpty()) { setIcon(Icon(iconName)); } else if (_url.isLocalFile()) { setIcon(Icon("folder")); } else if (KRarcHandler::isArchive(_url)) { setIcon(Icon("application-x-tar")); } else { setIcon(Icon("folder-html")); } } -KrBookmark * KrBookmark::getExistingBookmark(QString actionName, KActionCollection *collection) +KrBookmark * KrBookmark::getExistingBookmark(const QString& actionName, KActionCollection *collection) { return static_cast(collection->action(BM_NAME(actionName))); } KrBookmark * KrBookmark::trash(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_TRASH), collection); if (!bm) bm = new KrBookmark(i18n(NAME_TRASH), QUrl("trash:/"), collection); bm->setIcon(Icon(KrTrashHandler::trashIconName())); return bm; } KrBookmark * KrBookmark::virt(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_VIRTUAL), collection); if (!bm) { bm = new KrBookmark(i18n(NAME_VIRTUAL), QUrl("virt:/"), collection); bm->setIcon(Icon("document-open-remote")); } return bm; } KrBookmark * KrBookmark::lan(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_LAN), collection); if (!bm) { bm = new KrBookmark(i18n(NAME_LAN), QUrl("remote:/"), collection); bm->setIcon(Icon("network-workgroup")); } return bm; } QAction * KrBookmark::jumpBackAction(KActionCollection *collection, bool isSetter, ListPanelActions *sourceActions) { auto actionName = isSetter ? QString("setJumpBack") : QString("jumpBack"); auto action = collection->action(actionName); if (action) { return action; } if (!sourceActions) { return nullptr; } // copy essential part of source action auto sourceAction = isSetter ? sourceActions->actSetJumpBack : sourceActions->actJumpBack; action = new QAction(sourceAction->icon(), sourceAction->text(), sourceAction); action->setShortcut(sourceAction->shortcut()); action->setShortcutContext(Qt::WidgetShortcut); connect(action, &QAction::triggered, sourceAction, &QAction::trigger); // ensure there are no accelerator keys coming from another menu action->setText(KLocalizedString::removeAcceleratorMarker(action->text())); collection->addAction(actionName, action); return action; } KrBookmark * KrBookmark::separator() { KrBookmark *bm = new KrBookmark(""); bm->_separator = true; bm->_folder = false; return bm; } void KrBookmark::activatedProxy() { emit activated(url()); } diff --git a/krusader/BookMan/krbookmark.h b/krusader/BookMan/krbookmark.h index 78434241..561c68f2 100644 --- a/krusader/BookMan/krbookmark.h +++ b/krusader/BookMan/krbookmark.h @@ -1,93 +1,93 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRBOOKMARK_H #define KRBOOKMARK_H // QtCore #include #include // QtWidgets #include class KActionCollection; class ListPanelActions; class KrBookmark: public QAction { Q_OBJECT public: - KrBookmark(QString name, QUrl url, KActionCollection *parent, QString iconName = "", QString actionName = QString()); - explicit KrBookmark(QString name, QString iconName = ""); // creates a folder + KrBookmark(const QString& name, QUrl url, KActionCollection *parent, const QString& iconName = "", const QString& actionName = QString()); + explicit KrBookmark(const QString& name, const QString& iconName = ""); // creates a folder ~KrBookmark() override; // text() and setText() to change the name of the bookmark // icon() and setIcon() to change icons - void setIconName(QString iconName); + void setIconName(const QString& iconName); inline const QString& iconName() const { return _iconName; } inline const QUrl &url() const { return _url; } inline void setURL(const QUrl &url) { _url = url; } inline bool isFolder() const { return _folder; } inline bool isSeparator() const { return _separator; } QList& children() { return _children; } - static KrBookmark * getExistingBookmark(QString actionName, KActionCollection *collection); + static KrBookmark * getExistingBookmark(const QString& actionName, KActionCollection *collection); // ----- special bookmarks static KrBookmark * trash(KActionCollection *collection); static KrBookmark * virt(KActionCollection *collection); static KrBookmark * lan(KActionCollection *collection); static QAction * jumpBackAction(KActionCollection *collection, bool isSetter = false, ListPanelActions *sourceActions = nullptr); static KrBookmark * separator(); signals: void activated(const QUrl &url); protected slots: void activatedProxy(); private: QUrl _url; QString _iconName; bool _folder; bool _separator; bool _autoDelete; QList _children; }; #endif // KRBOOKMARK_H diff --git a/krusader/BookMan/krbookmarkhandler.cpp b/krusader/BookMan/krbookmarkhandler.cpp index 174b5e75..3e14e27a 100644 --- a/krusader/BookMan/krbookmarkhandler.cpp +++ b/krusader/BookMan/krbookmarkhandler.cpp @@ -1,881 +1,882 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krbookmarkhandler.h" #include "kraddbookmarkdlg.h" #include "../krglobal.h" #include "../icon.h" #include "../krslots.h" #include "../kractions.h" #include "../krmainwindow.h" #include "../Dialogs/popularurls.h" #include "../FileSystem/filesystem.h" #include "../Panel/krpanel.h" #include "../Panel/listpanelactions.h" // QtCore #include #include #include #include #include #include // QtGui #include #include #include #include #include #include #include +#include #define SPECIAL_BOOKMARKS true // ------------------------ for internal use #define BOOKMARKS_FILE "krusader/krbookmarks.xml" #define CONNECT_BM(X) { disconnect(X, SIGNAL(activated(QUrl)), 0, 0); connect(X, SIGNAL(activated(QUrl)), this, SLOT(slotActivated(QUrl))); } KrBookmarkHandler::KrBookmarkHandler(KrMainWindow *mainWindow) : QObject(mainWindow->widget()), _mainWindow(mainWindow), _middleClick(false), _mainBookmarkPopup(nullptr), _specialBookmarks(), _quickSearchAction(nullptr), _quickSearchBar(nullptr), _quickSearchMenu(nullptr) { // create our own action collection and make the shortcuts apply only to parent _privateCollection = new KActionCollection(this); _collection = _mainWindow->actions(); // create _root: father of all bookmarks. it is a dummy bookmark and never shown _root = new KrBookmark(i18n("Bookmarks")); _root->setParent(this); // load bookmarks importFromFile(); // create bookmark manager QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; manager = KBookmarkManager::managerForFile(filename, QStringLiteral("krusader")); connect(manager, &KBookmarkManager::changed, this, &KrBookmarkHandler::bookmarksChanged); // create the quick search bar and action _quickSearchAction = new QWidgetAction(this); _quickSearchBar = new QLineEdit(); _quickSearchBar->setPlaceholderText(i18n("Type to search...")); _quickSearchAction->setDefaultWidget(_quickSearchBar); // ownership of the bar is transferred to the action _quickSearchAction->setEnabled(false); _setQuickSearchText(""); // fill a dummy menu to properly init actions (allows toolbar bookmark buttons to work properly) auto menu = new QMenu(mainWindow->widget()); populate(menu); menu->deleteLater(); } KrBookmarkHandler::~KrBookmarkHandler() { delete manager; delete _privateCollection; } void KrBookmarkHandler::bookmarkCurrent(QUrl url) { - QPointer dlg = new KrAddBookmarkDlg(_mainWindow->widget(), url); + QPointer dlg = new KrAddBookmarkDlg(_mainWindow->widget(), std::move(url)); if (dlg->exec() == QDialog::Accepted) { KrBookmark *bm = new KrBookmark(dlg->name(), dlg->url(), _collection); addBookmark(bm, dlg->folder()); } delete dlg; } void KrBookmarkHandler::addBookmark(KrBookmark *bm, KrBookmark *folder) { if (folder == nullptr) folder = _root; // add to the list (bottom) folder->children().append(bm); exportToFile(); } void KrBookmarkHandler::deleteBookmark(KrBookmark *bm) { if (bm->isFolder()) clearBookmarks(bm); // remove the child bookmarks removeReferences(_root, bm); foreach(QWidget *w, bm->associatedWidgets()) w->removeAction(bm); delete bm; exportToFile(); } void KrBookmarkHandler::removeReferences(KrBookmark *root, KrBookmark *bmToRemove) { int index = root->children().indexOf(bmToRemove); if (index >= 0) root->children().removeAt(index); QListIterator it(root->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) removeReferences(bm, bmToRemove); } } void KrBookmarkHandler::exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm) { if (bm->isSeparator()) { QDomElement bookmark = doc.createElement("separator"); where.appendChild(bookmark); } else { QDomElement bookmark = doc.createElement("bookmark"); // url bookmark.setAttribute("href", bm->url().toDisplayString()); // icon bookmark.setAttribute("icon", bm->iconName()); // title QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); bookmark.appendChild(title); where.appendChild(bookmark); } } void KrBookmarkHandler::exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder) { QListIterator it(folder->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) { QDomElement newFolder = doc.createElement("folder"); newFolder.setAttribute("icon", bm->iconName()); parent.appendChild(newFolder); QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); newFolder.appendChild(title); exportToFileFolder(doc, newFolder, bm); } else { exportToFileBookmark(doc, parent, bm); } } } // export to file using the xbel standard // // // Developer Web Site // // Title of this folder // KDE Web Site // // My own bookmarks // KOffice Web Site // // KDevelop Web Site // // // void KrBookmarkHandler::exportToFile() { QDomDocument doc("xbel"); QDomElement root = doc.createElement("xbel"); doc.appendChild(root); exportToFileFolder(doc, root, _root); if (!doc.firstChild().isProcessingInstruction()) { // adding: if not already present QDomProcessingInstruction instr = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" "); doc.insertBefore(instr, doc.firstChild()); } QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream.setCodec("UTF-8"); stream << doc.toString(); file.close(); } else { KMessageBox::error(_mainWindow->widget(), i18n("Unable to write to %1", filename), i18n("Error")); } } -bool KrBookmarkHandler::importFromFileBookmark(QDomElement &e, KrBookmark *parent, QString path, QString *errorMsg) +bool KrBookmarkHandler::importFromFileBookmark(QDomElement &e, KrBookmark *parent, const QString& path, QString *errorMsg) { QString url, name, iconName; // verify tag if (e.tagName() != "bookmark") { *errorMsg = i18n("%1 instead of %2", e.tagName(), QLatin1String("bookmark")); return false; } // verify href if (!e.hasAttribute("href")) { *errorMsg = i18n("missing tag %1", QLatin1String("href")); return false; } else url = e.attribute("href"); // verify title QDomElement te = e.firstChild().toElement(); if (te.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = te.text(); // do we have an icon? if (e.hasAttribute("icon")) { iconName = e.attribute("icon"); } // ok: got name and url, let's add a bookmark KrBookmark *bm = KrBookmark::getExistingBookmark(path + name, _collection); if (!bm) { bm = new KrBookmark(name, QUrl(url), _collection, iconName, path + name); } else { bm->setURL(QUrl(url)); bm->setIconName(iconName); } parent->children().append(bm); return true; } -bool KrBookmarkHandler::importFromFileFolder(QDomNode &first, KrBookmark *parent, QString path, QString *errorMsg) +bool KrBookmarkHandler::importFromFileFolder(QDomNode &first, KrBookmark *parent, const QString& path, QString *errorMsg) { QString name; QDomNode n = first; while (!n.isNull()) { QDomElement e = n.toElement(); if (e.tagName() == "bookmark") { if (!importFromFileBookmark(e, parent, path, errorMsg)) return false; } else if (e.tagName() == "folder") { QString iconName = ""; if (e.hasAttribute("icon")) iconName = e.attribute("icon"); // the title is the first child of the folder QDomElement tmp = e.firstChild().toElement(); if (tmp.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = tmp.text(); KrBookmark *folder = new KrBookmark(name, iconName); parent->children().append(folder); QDomNode nextOne = tmp.nextSibling(); if (!importFromFileFolder(nextOne, folder, path + name + '/', errorMsg)) return false; } else if (e.tagName() == "separator") { parent->children().append(KrBookmark::separator()); } n = n.nextSibling(); } return true; } void KrBookmarkHandler::importFromFile() { clearBookmarks(_root, false); QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) return; // no bookmarks file QString errorMsg; QDomNode n; QDomElement e; QDomDocument doc("xbel"); if (!doc.setContent(&file, &errorMsg)) { goto BM_ERROR; } // iterate through the document: first child should be "xbel" (skip all until we find it) n = doc.firstChild(); while (!n.isNull() && n.toElement().tagName() != "xbel") n = n.nextSibling(); if (n.isNull() || n.toElement().tagName() != "xbel") { errorMsg = i18n("%1 does not seem to be a valid bookmarks file", filename); goto BM_ERROR; } else n = n.firstChild(); // skip the xbel part importFromFileFolder(n, _root, "", &errorMsg); goto BM_SUCCESS; BM_ERROR: KMessageBox::error(_mainWindow->widget(), i18n("Error reading bookmarks file: %1", errorMsg), i18n("Error")); BM_SUCCESS: file.close(); } void KrBookmarkHandler::_setQuickSearchText(const QString &text) { bool isEmptyQuickSearchBarVisible = KConfigGroup(krConfig, "Look&Feel").readEntry("Always show search bar", true); _quickSearchBar->setText(text); auto length = text.length(); bool isVisible = isEmptyQuickSearchBarVisible || length > 0; _quickSearchAction->setVisible(isVisible); _quickSearchBar->setVisible(isVisible); if (length == 0) { qDebug() << "Bookmark search: reset"; _resetActionTextAndHighlighting(); } else { qDebug() << "Bookmark search: query =" << text; } } QString KrBookmarkHandler::_quickSearchText() const { return _quickSearchBar->text(); } void KrBookmarkHandler::_highlightAction(QAction *action, bool isMatched) { auto font = action->font(); font.setBold(isMatched); action->setFont(font); } void KrBookmarkHandler::populate(QMenu *menu) { // removing action from previous menu is necessary // otherwise it won't be displayed in the currently populating menu if (_mainBookmarkPopup) { _mainBookmarkPopup->removeAction(_quickSearchAction); } _mainBookmarkPopup = menu; menu->clear(); _specialBookmarks.clear(); buildMenu(_root, menu); } void KrBookmarkHandler::buildMenu(KrBookmark *parent, QMenu *menu, int depth) { // add search bar widget to the top of the menu if (depth == 0) { menu->addAction(_quickSearchAction); } // run the loop twice, in order to put the folders on top. stupid but easy :-) // note: this code drops the separators put there by the user QListIterator it(parent->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (!bm->isFolder()) continue; auto *newMenu = new QMenu(menu); newMenu->setIcon(Icon(bm->iconName())); newMenu->setTitle(bm->text()); QAction *menuAction = menu->addMenu(newMenu); QVariant v; v.setValue(bm); menuAction->setData(v); buildMenu(bm, newMenu, depth + 1); } it.toFront(); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) continue; if (bm->isSeparator()) { menu->addSeparator(); continue; } menu->addAction(bm); CONNECT_BM(bm); } if (depth == 0) { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); if (hasPopularURLs) { menu->addSeparator(); // add the popular links submenu auto *newMenu = new QMenu(menu); newMenu->setTitle(i18n("Popular URLs")); newMenu->setIcon(Icon("folder-bookmark")); QAction *bmfAct = menu->addMenu(newMenu); _specialBookmarks.append(bmfAct); // add the top 15 urls #define MAX 15 QList list = _mainWindow->popularUrls()->getMostPopularUrls(MAX); QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { QString name; if ((*it).isLocalFile()) name = (*it).path(); else name = (*it).toDisplayString(); // note: these bookmark are put into the private collection // as to not spam the general collection KrBookmark *bm = KrBookmark::getExistingBookmark(name, _privateCollection); if (!bm) bm = new KrBookmark(name, *it, _privateCollection); newMenu->addAction(bm); CONNECT_BM(bm); } newMenu->addSeparator(); newMenu->addAction(krPopularUrls); newMenu->installEventFilter(this); } // do we need to add special bookmarks? if (SPECIAL_BOOKMARKS) { if (hasTrash || hasLan || hasVirtualFS) menu->addSeparator(); KrBookmark *bm; // note: special bookmarks are not kept inside the _bookmarks list and added ad-hoc if (hasTrash) { bm = KrBookmark::trash(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasLan) { bm = KrBookmark::lan(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasVirtualFS) { bm = KrBookmark::virt(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasJumpback) { menu->addSeparator(); ListPanelActions *actions = _mainWindow->listPanelActions(); auto slotTriggered = [=] { if (_mainBookmarkPopup && !_mainBookmarkPopup->isHidden()) { _mainBookmarkPopup->close(); } }; auto addJumpBackAction = [=](bool isSetter) { auto action = KrBookmark::jumpBackAction(_privateCollection, isSetter, actions); if (action) { menu->addAction(action); _specialBookmarks.append(action); // disconnecting from this as a receiver is important: // we don't want to break connections established by KrBookmark::jumpBackAction disconnect(action, &QAction::triggered, this, nullptr); connect(action, &QAction::triggered, this, slotTriggered); } }; addJumpBackAction(true); addJumpBackAction(false); } } menu->addSeparator(); menu->addAction(KrActions::actAddBookmark); _specialBookmarks.append(KrActions::actAddBookmark); QAction *bmAct = menu->addAction(Icon("bookmarks"), i18n("Manage Bookmarks"), manager, SLOT(slotEditBookmarks())); _specialBookmarks.append(bmAct); // make sure the menu is connected to us disconnect(menu, SIGNAL(triggered(QAction*)), nullptr, nullptr); } menu->installEventFilter(this); } void KrBookmarkHandler::clearBookmarks(KrBookmark *root, bool removeBookmarks) { for (auto it = root->children().begin(); it != root->children().end(); it = root->children().erase(it)) { KrBookmark *bm = *it; if (bm->isFolder()) { clearBookmarks(bm, removeBookmarks); delete bm; } else if (bm->isSeparator()) { delete bm; } else if (removeBookmarks) { foreach (QWidget *w, bm->associatedWidgets()) { w->removeAction(bm); } delete bm; } } } void KrBookmarkHandler::bookmarksChanged(const QString&, const QString&) { importFromFile(); } bool KrBookmarkHandler::eventFilter(QObject *obj, QEvent *ev) { auto eventType = ev->type(); auto *menu = qobject_cast(obj); if (eventType == QEvent::Show && menu) { _setQuickSearchText(""); _quickSearchMenu = menu; qDebug() << "Bookmark search: menu" << menu << "is shown"; return QObject::eventFilter(obj, ev); } if (eventType == QEvent::Close && menu && _quickSearchMenu) { if (_quickSearchMenu == menu) { qDebug() << "Bookmark search: stopped on menu" << menu; _setQuickSearchText(""); _quickSearchMenu = nullptr; } else { qDebug() << "Bookmark search: active action =" << _quickSearchMenu->activeAction(); // fix automatic deactivation of current action due to spurious close event from submenu auto quickSearchMenu = _quickSearchMenu; auto activeAction = _quickSearchMenu->activeAction(); QTimer::singleShot(0, this, [=]() { qDebug() << "Bookmark search: active action =" << quickSearchMenu->activeAction(); if (!quickSearchMenu->activeAction() && activeAction) { quickSearchMenu->setActiveAction(activeAction); qDebug() << "Bookmark search: restored active action =" << quickSearchMenu->activeAction(); } }); } return QObject::eventFilter(obj, ev); } // Having it occur on keypress is consistent with other shortcuts, // such as Ctrl+W and accelerator keys if (eventType == QEvent::KeyPress && menu) { auto *kev = static_cast(ev); QList acts = menu->actions(); bool quickSearchStarted = false; bool searchInSpecialItems = KConfigGroup(krConfig, "Look&Feel").readEntry("Search in special items", false); if (kev->key() == Qt::Key_Left && kev->modifiers() == Qt::NoModifier) { menu->close(); return true; } if ((kev->modifiers() != Qt::ShiftModifier && kev->modifiers() != Qt::NoModifier) || kev->text().isEmpty() || kev->key() == Qt::Key_Delete || kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Escape) { return QObject::eventFilter(obj, ev); } // update quick search text if (kev->key() == Qt::Key_Backspace) { auto newSearchText = _quickSearchText(); newSearchText.chop(1); _setQuickSearchText(newSearchText); if (_quickSearchText().length() == 0) { return QObject::eventFilter(obj, ev); } } else { quickSearchStarted = _quickSearchText().length() == 0; _setQuickSearchText(_quickSearchText().append(kev->text())); } if (quickSearchStarted) { _quickSearchMenu = menu; qDebug() << "Bookmark search: started on menu" << menu; } // match actions QAction *matchedAction = nullptr; int nMatches = 0; const Qt::CaseSensitivity matchCase = _quickSearchText() == _quickSearchText().toLower() ? Qt::CaseInsensitive : Qt::CaseSensitive; for (auto act : acts) { if (act->isSeparator() || act->text().isEmpty()) { continue; } if (!searchInSpecialItems && _specialBookmarks.contains(act)) { continue; } if (quickSearchStarted) { // if the first key press is an accelerator key, let the accelerator handler process this event if (act->text().contains('&' + kev->text(), Qt::CaseInsensitive)) { qDebug() << "Bookmark search: hit accelerator key of" << act; _setQuickSearchText(""); return QObject::eventFilter(obj, ev); } // strip accelerator keys from actions so they don't interfere with the search key press events auto text = act->text(); _quickSearchOriginalActionTitles.insert(act, text); act->setText(KLocalizedString::removeAcceleratorMarker(text)); } // match prefix of the action text to the query if (act->text().left(_quickSearchText().length()).compare(_quickSearchText(), matchCase) == 0) { _highlightAction(act); if (!matchedAction || matchedAction->menu()) { // Can't highlight menus (see comment below), hopefully pick something we can matchedAction = act; } nMatches++; } else { _highlightAction(act, false); } } if (matchedAction) { qDebug() << "Bookmark search: primary match =" << matchedAction->text() << ", number of matches =" << nMatches; } else { qDebug() << "Bookmark search: no matches"; } // trigger the matched menu item or set an active item accordingly if (nMatches == 1) { _setQuickSearchText(""); if ((bool) matchedAction->menu()) { menu->setActiveAction(matchedAction); } else { matchedAction->activate(QAction::Trigger); } } else if (nMatches > 1) { // Because of a bug submenus cannot be highlighted // https://bugreports.qt.io/browse/QTBUG-939 if (!matchedAction->menu()) { menu->setActiveAction(matchedAction); } else { menu->setActiveAction(nullptr); } } else { menu->setActiveAction(nullptr); } return true; } if (eventType == QEvent::MouseButtonRelease) { switch (static_cast(ev)->button()) { case Qt::RightButton: _middleClick = false; if (obj->inherits("QMenu")) { auto *menu = static_cast(obj); QAction *act = menu->actionAt(static_cast(ev)->pos()); if (obj == _mainBookmarkPopup && _specialBookmarks.contains(act)) { rightClickOnSpecialBookmark(); return true; } auto *bm = qobject_cast(act); if (bm != nullptr) { rightClicked(menu, bm); return true; } else if (act && act->data().canConvert()) { auto *bm = act->data().value(); rightClicked(menu, bm); } } break; case Qt::LeftButton: _middleClick = false; break; case Qt::MidButton: _middleClick = true; break; default: break; } } return QObject::eventFilter(obj, ev); } void KrBookmarkHandler::_resetActionTextAndHighlighting() { for (QHash::const_iterator i = _quickSearchOriginalActionTitles.begin(); i != _quickSearchOriginalActionTitles.end(); ++i) { QAction *action = i.key(); action->setText(i.value()); _highlightAction(action, false); } _quickSearchOriginalActionTitles.clear(); } #define POPULAR_URLS_ID 100100 #define TRASH_ID 100101 #define LAN_ID 100103 #define VIRTUAL_FS_ID 100102 #define JUMP_BACK_ID 100104 void KrBookmarkHandler::rightClickOnSpecialBookmark() { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); QMenu menu(_mainBookmarkPopup); menu.setTitle(i18n("Enable special bookmarks")); QAction *act; act = menu.addAction(i18n("Popular URLs")); act->setData(QVariant(POPULAR_URLS_ID)); act->setCheckable(true); act->setChecked(hasPopularURLs); act = menu.addAction(i18n("Trash bin")); act->setData(QVariant(TRASH_ID)); act->setCheckable(true); act->setChecked(hasTrash); act = menu.addAction(i18n("Local Network")); act->setData(QVariant(LAN_ID)); act->setCheckable(true); act->setChecked(hasLan); act = menu.addAction(i18n("Virtual Filesystem")); act->setData(QVariant(VIRTUAL_FS_ID)); act->setCheckable(true); act->setChecked(hasVirtualFS); act = menu.addAction(i18n("Jump back")); act->setData(QVariant(JUMP_BACK_ID)); act->setCheckable(true); act->setChecked(hasJumpback); connect(_mainBookmarkPopup, SIGNAL(highlighted(int)), &menu, SLOT(close())); connect(_mainBookmarkPopup, SIGNAL(activated(int)), &menu, SLOT(close())); int result = -1; QAction *res = menu.exec(QCursor::pos()); if (res && res->data().canConvert()) result = res->data().toInt(); bool doCloseMain = true; switch (result) { case POPULAR_URLS_ID: group.writeEntry("BM Popular URLs", !hasPopularURLs); break; case TRASH_ID: group.writeEntry("BM Trash", !hasTrash); break; case LAN_ID: group.writeEntry("BM Lan", !hasLan); break; case VIRTUAL_FS_ID: group.writeEntry("BM Virtual FS", !hasVirtualFS); break; case JUMP_BACK_ID: group.writeEntry("BM Jumpback", !hasJumpback); break; default: doCloseMain = false; break; } menu.close(); if (doCloseMain && _mainBookmarkPopup) _mainBookmarkPopup->close(); } #define OPEN_ID 100200 #define OPEN_NEW_TAB_ID 100201 #define DELETE_ID 100202 void KrBookmarkHandler::rightClicked(QMenu *menu, KrBookmark * bm) { QMenu popup(_mainBookmarkPopup); QAction * act; if (!bm->isFolder()) { act = popup.addAction(Icon("document-open"), i18n("Open")); act->setData(QVariant(OPEN_ID)); act = popup.addAction(Icon("tab-new"), i18n("Open in a new tab")); act->setData(QVariant(OPEN_NEW_TAB_ID)); popup.addSeparator(); } act = popup.addAction(Icon("edit-delete"), i18n("Delete")); act->setData(QVariant(DELETE_ID)); connect(menu, SIGNAL(highlighted(int)), &popup, SLOT(close())); connect(menu, SIGNAL(activated(int)), &popup, SLOT(close())); int result = -1; QAction *res = popup.exec(QCursor::pos()); if (res && res->data().canConvert ()) result = res->data().toInt(); popup.close(); if (_mainBookmarkPopup && result >= OPEN_ID && result <= DELETE_ID) { _mainBookmarkPopup->close(); } switch (result) { case OPEN_ID: SLOTS->refresh(bm->url()); break; case OPEN_NEW_TAB_ID: _mainWindow->activeManager()->newTab(bm->url()); break; case DELETE_ID: deleteBookmark(bm); break; } } // used to monitor middle clicks. if mid is found, then the // bookmark is opened in a new tab. ugly, but easier than overloading // KAction and KActionCollection. void KrBookmarkHandler::slotActivated(const QUrl &url) { if (_mainBookmarkPopup && !_mainBookmarkPopup->isHidden()) _mainBookmarkPopup->close(); if (_middleClick) _mainWindow->activeManager()->newTab(url); else SLOTS->refresh(url); } diff --git a/krusader/BookMan/krbookmarkhandler.h b/krusader/BookMan/krbookmarkhandler.h index 414f6eef..657e21d7 100644 --- a/krusader/BookMan/krbookmarkhandler.h +++ b/krusader/BookMan/krbookmarkhandler.h @@ -1,101 +1,101 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRBOOKMARKHANDLER_H #define KRBOOKMARKHANDLER_H // QtCore #include #include #include #include #include // QtXml #include // QtWidgets #include #include #include #include "krbookmark.h" class KActionCollection; class KBookmarkManager; class KrMainWindow; class KrBookmarkHandler: public QObject { Q_OBJECT friend class KrAddBookmarkDlg; enum Actions { BookmarkCurrent = 0, ManageBookmarks }; public: explicit KrBookmarkHandler(KrMainWindow *mainWindow); ~KrBookmarkHandler() override; void populate(QMenu *menu); void addBookmark(KrBookmark *bm, KrBookmark *parent = nullptr); void bookmarkCurrent(QUrl url); protected: void deleteBookmark(KrBookmark *bm); void importFromFile(); - bool importFromFileBookmark(QDomElement &e, KrBookmark *parent, QString path, QString *errorMsg); - bool importFromFileFolder(QDomNode &first, KrBookmark *parent, QString path, QString *errorMsg); + bool importFromFileBookmark(QDomElement &e, KrBookmark *parent, const QString& path, QString *errorMsg); + bool importFromFileFolder(QDomNode &first, KrBookmark *parent, const QString& path, QString *errorMsg); void exportToFile(); void exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder); void exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm); void clearBookmarks(KrBookmark *root, bool removeBookmarks = true); void buildMenu(KrBookmark *parent, QMenu *menu, int depth = 0); bool eventFilter(QObject *obj, QEvent *ev) Q_DECL_OVERRIDE; void rightClicked(QMenu *menu, KrBookmark *bm); void rightClickOnSpecialBookmark(); void removeReferences(KrBookmark *root, KrBookmark *bmToRemove); protected slots: void bookmarksChanged(const QString&, const QString&); void slotActivated(const QUrl &url); private: KrMainWindow *_mainWindow; KActionCollection *_collection, *_privateCollection; KrBookmark *_root; // the whole KBookmarkManager is an ugly hack. use it until we have our own KBookmarkManager *manager; bool _middleClick; // if true, the user clicked the middle button to open the bookmark QPointer _mainBookmarkPopup; // main bookmark popup menu QList _specialBookmarks; // the action list of the special bookmarks QWidgetAction *_quickSearchAction; ///< Search bar container action QLineEdit *_quickSearchBar; ///< Search bar containing current query QMenu *_quickSearchMenu; ///< The menu where the search is performed QHash _quickSearchOriginalActionTitles; ///< Saved original action text values to restore after search void _setQuickSearchText(const QString &text); QString _quickSearchText() const; static void _highlightAction(QAction *action, bool isMatched = true); void _resetActionTextAndHighlighting(); }; Q_DECLARE_METATYPE(KrBookmark *) #endif // KRBOOKMARK_HANDLER_H diff --git a/krusader/Dialogs/checksumdlg.cpp b/krusader/Dialogs/checksumdlg.cpp index 08091452..1033cca2 100644 --- a/krusader/Dialogs/checksumdlg.cpp +++ b/krusader/Dialogs/checksumdlg.cpp @@ -1,576 +1,576 @@ /***************************************************************************** * Copyright (C) 2005 Shie Erlich * * Copyright (C) 2007-2008 Csaba Karai * * Copyright (C) 2008 Jonas Bähr * * Copyright (C) 2005-2018 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 "checksumdlg.h" #include "../krglobal.h" #include "../icon.h" #include "../krservices.h" #include "../krusader.h" #include "../GUI/krlistwidget.h" #include "../GUI/krtreewidget.h" // QtCore #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include // krazy:exclude=includes #include #include #include void Checksum::startCreationWizard(const QString &path, const QStringList &files) { if (files.isEmpty()) return; QDialog *dialog = new CHECKSUM_::CreateWizard(path, files); dialog->show(); } void Checksum::startVerifyWizard(const QString &path, const QString &checksumFile) { QDialog *dialog = new CHECKSUM_::VerifyWizard(path, checksumFile); dialog->show(); } namespace CHECKSUM_ { bool stopListFiles; // async operation invoked by QtConcurrent::run in creation wizard QStringList listFiles(const QString &path, const QStringList &fileNames) { const QDir baseDir(path); QStringList allFiles; for (const QString fileName : fileNames) { if (stopListFiles) return QStringList(); QDir subDir = QDir(baseDir.filePath(fileName)); if (subDir.exists()) { subDir.setFilter(QDir::Files); QDirIterator it(subDir, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); while (it.hasNext()) { if (stopListFiles) return QStringList(); allFiles << baseDir.relativeFilePath(it.next()); } } else { // assume this is a file allFiles << fileName; } } return allFiles; } // ------------- Checksum Process ChecksumProcess::ChecksumProcess(QObject *parent, const QString &path) : KProcess(parent), m_tmpOutFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.stdout")), m_tmpErrFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.stderr")) { m_tmpOutFile.open(); // necessary to create the filename m_tmpErrFile.open(); // necessary to create the filename setOutputChannelMode(KProcess::SeparateChannels); // without this the next 2 lines have no effect! setStandardOutputFile(m_tmpOutFile.fileName()); setStandardErrorFile(m_tmpErrFile.fileName()); setWorkingDirectory(path); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) connect(this, &ChecksumProcess::errorOccurred, this, &ChecksumProcess::slotError); #endif connect(this, static_cast(&QProcess::finished), this, &ChecksumProcess::slotFinished); } ChecksumProcess::~ChecksumProcess() { disconnect(this, nullptr, this, nullptr); // QProcess emits finished() on destruction close(); } void ChecksumProcess::slotError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { KMessageBox::error(nullptr, i18n("Could not start %1.", program().join(" "))); } } void ChecksumProcess::slotFinished(int, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit) { KMessageBox::error(nullptr, i18n("There was an error while running %1.", program().join(" "))); return; } // parse result files if (!KrServices::fileToStringList(&m_tmpOutFile, m_outputLines) || !KrServices::fileToStringList(&m_tmpErrFile, m_errorLines)) { KMessageBox::error(nullptr, i18n("Error reading stdout or stderr")); return; } emit resultReady(); } // ------------- Generic Checksum Wizard ChecksumWizard::ChecksumWizard(const QString &path) : QWizard(krApp), m_path(path), m_process(nullptr) { setAttribute(Qt::WA_DeleteOnClose); // init the dictionary - pity it has to be manually m_checksumTools.insert("md5", "md5sum"); m_checksumTools.insert("sha1", "sha1sum"); m_checksumTools.insert("sha256", "sha256sum"); m_checksumTools.insert("sha224", "sha224sum"); m_checksumTools.insert("sha384", "sha384sum"); m_checksumTools.insert("sha512", "sha512sum"); connect(this, &QWizard::currentIdChanged, this, &ChecksumWizard::slotCurrentIdChanged); } ChecksumWizard::~ChecksumWizard() { if (m_process) { delete m_process; } } void ChecksumWizard::slotCurrentIdChanged(int id) { if (id == m_introId) { onIntroPage(); } else if (id == m_progressId) { if (m_process) { // we are coming from the result page; delete m_process; m_process = nullptr; restart(); } else { button(QWizard::BackButton)->hide(); button(QWizard::NextButton)->hide(); onProgressPage(); } } else if (id == m_resultId) { onResultPage(); } } QWizardPage *ChecksumWizard::createProgressPage(const QString &title) { auto *page = new QWizardPage; page->setTitle(title); page->setPixmap(QWizard::LogoPixmap, Icon("process-working").pixmap(32)); page->setSubTitle(i18n("Please wait...")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // "busy" indicator auto *bar = new QProgressBar(); bar->setRange(0,0); mainLayout->addWidget(bar); return page; } -bool ChecksumWizard::checkExists(const QString type) +bool ChecksumWizard::checkExists(const QString& type) { if (!KrServices::cmdExist(m_checksumTools[type])) { KMessageBox::error( this, i18n("Krusader cannot find a checksum tool that handles %1 on your system. " "Please check the Dependencies page in Krusader's settings.", type)); return false; } return true; } void ChecksumWizard::runProcess(const QString &type, const QStringList &args) { Q_ASSERT(m_process == nullptr); m_process = new ChecksumProcess(this, m_path); m_process->setProgram(KrServices::fullPathName(m_checksumTools[type]), args); // show next page (with results) (only) when process is done connect(m_process, &ChecksumProcess::resultReady, this, &QWizard::next); // run the process m_process->start(); } void ChecksumWizard::addChecksumLine(KrTreeWidget *tree, const QString &line) { auto *item = new QTreeWidgetItem(tree); const int hashLength = line.indexOf(' '); // delimiter is either " " or " *" item->setText(0, line.left(hashLength)); QString fileName = line.mid(hashLength + 2); if (fileName.endsWith('\n')) fileName.chop(1); item->setText(1, fileName); } // ------------- Create Wizard CreateWizard::CreateWizard(const QString &path, const QStringList &_files) : ChecksumWizard(path), m_fileNames(_files), m_listFilesWatcher() { m_introId = addPage(createIntroPage()); m_progressId = addPage(createProgressPage(i18n("Creating Checksums"))); m_resultId = addPage(createResultPage()); setButton(QWizard::FinishButton, QDialogButtonBox(QDialogButtonBox::Save).button(QDialogButtonBox::Save)); connect(&m_listFilesWatcher, &QFutureWatcher::resultReadyAt, this, &CreateWizard::createChecksums); } QWizardPage *CreateWizard::createIntroPage() { auto *page = new QWizardPage; page->setTitle(i18n("Create Checksums")); page->setPixmap(QWizard::LogoPixmap, Icon("document-edit-sign").pixmap(32)); page->setSubTitle(i18n("About to calculate checksum for the following files or directories:")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // file list auto *listWidget = new KrListWidget; listWidget->addItems(m_fileNames); mainLayout->addWidget(listWidget); // checksum method auto *hLayout = new QHBoxLayout; QLabel *methodLabel = new QLabel(i18n("Select the checksum method:")); hLayout->addWidget(methodLabel); m_methodBox = new KComboBox; // -- fill the combo with available methods for (const QString type: m_checksumTools.keys()) m_methodBox->addItem(type); m_methodBox->setFocus(); hLayout->addWidget(m_methodBox); mainLayout->addLayout(hLayout); return page; } QWizardPage *CreateWizard::createResultPage() { auto *page = new QWizardPage; page->setTitle(i18n("Checksum Results")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); m_hashesTreeWidget = new KrTreeWidget(this); m_hashesTreeWidget->setAllColumnsShowFocus(true); m_hashesTreeWidget->setHeaderLabels(QStringList() << i18n("Hash") << i18n("File")); mainLayout->addWidget(m_hashesTreeWidget); m_errorLabel = new QLabel(i18n("Errors received:")); mainLayout->addWidget(m_errorLabel); m_errorListWidget = new KrListWidget; mainLayout->addWidget(m_errorListWidget); m_onePerFileBox = new QCheckBox(i18n("Save one checksum file for each source file")); m_onePerFileBox->setChecked(false); mainLayout->addWidget(m_onePerFileBox); return page; } void CreateWizard::onIntroPage() { button(QWizard::NextButton)->show(); } void CreateWizard::onProgressPage() { // first, get all files (recurse in directories) - async stopListFiles = false; // QFuture cannot cancel QtConcurrent::run connect(this, &CreateWizard::finished, this, [=]() { stopListFiles = true; }); QFuture listFuture = QtConcurrent::run(listFiles, m_path, m_fileNames); m_listFilesWatcher.setFuture(listFuture); } void CreateWizard::createChecksums() { const QString type = m_methodBox->currentText(); if (!checkExists(type)) { button(QWizard::BackButton)->show(); return; } const QStringList &allFiles = m_listFilesWatcher.result(); if (allFiles.isEmpty()) { KMessageBox::error(this, i18n("No files found")); button(QWizard::BackButton)->show(); return; } runProcess(type, allFiles); // set suggested filename m_suggestedFilePath = QDir(m_path).filePath( (m_fileNames.count() > 1 ? "checksum." : (m_fileNames[0] + '.')) + type); } void CreateWizard::onResultPage() { // hash tools display errors into stderr, so we'll use that to determine the result of the job const QStringList outputLines = m_process->stdOutput(); const QStringList errorLines = m_process->errOutput(); bool errors = !errorLines.isEmpty(); bool successes = !outputLines.isEmpty(); QWizardPage *page = currentPage(); page->setPixmap(QWizard::LogoPixmap, Icon(errors || !successes ? "dialog-error" : "dialog-information").pixmap(32)); page->setSubTitle(errors || !successes ? i18n("Errors were detected while creating the checksums") : i18n("Checksums were created successfully")); m_hashesTreeWidget->clear(); m_hashesTreeWidget->setVisible(successes); if (successes) { for (const QString line : outputLines) addChecksumLine(m_hashesTreeWidget, line); //m_hashesTreeWidget->sortItems(1, Qt::AscendingOrder); } m_errorLabel->setVisible(errors); m_errorListWidget->setVisible(errors); m_errorListWidget->clear(); m_errorListWidget->addItems(errorLines); m_onePerFileBox->setEnabled(outputLines.size() > 1); button(QWizard::FinishButton)->setEnabled(successes); } bool CreateWizard::savePerFile() { const QString type = m_suggestedFilePath.mid(m_suggestedFilePath.lastIndexOf('.')); krApp->startWaiting(i18n("Saving checksum files..."), 0); for (const QString line : m_process->stdOutput()) { const QString filename = line.mid(line.indexOf(' ') + 2) + type; if (!saveChecksumFile(QStringList() << line, filename)) { KMessageBox::error(this, i18n("Errors occurred while saving multiple checksums. Stopping")); krApp->stopWait(); return false; } } krApp->stopWait(); return true; } bool CreateWizard::saveChecksumFile(const QStringList &data, const QString &filename) { QString filePath = filename.isEmpty() ? m_suggestedFilePath : filename; if (filename.isEmpty() || QFile::exists(filePath)) { filePath = QFileDialog::getSaveFileName(this, QString(), filePath); if (filePath.isEmpty()) return false; // user pressed cancel } QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); for (const QString line : data) stream << line << "\n"; file.close(); } if (file.error() != QFile::NoError) { KMessageBox::detailedError(this, i18n("Error saving file %1", filePath), file.errorString()); return false; } return true; } void CreateWizard::accept() { const bool saved = m_onePerFileBox->isChecked() ? savePerFile() : saveChecksumFile(m_process->stdOutput()); if (saved) QWizard::accept(); } // ------------- Verify Wizard VerifyWizard::VerifyWizard(const QString &path, const QString &inputFile) : ChecksumWizard(path) { m_checksumFile = isSupported(inputFile) ? inputFile : path; m_introId = addPage(createIntroPage()); // m_checksumFile must already be set m_progressId = addPage(createProgressPage(i18n("Verifying Checksums"))); m_resultId = addPage(createResultPage()); } void VerifyWizard::slotChecksumPathChanged(const QString &path) { m_hashesTreeWidget->clear(); button(QWizard::NextButton)->setEnabled(false); if (!isSupported(path)) return; m_checksumFile = path; // parse and display checksum file content; only for the user, parsed values are not used m_hashesTreeWidget->clear(); QFile file(m_checksumFile); if (file.open(QFile::ReadOnly)) { QTextStream inStream(&file); while (!inStream.atEnd()) { addChecksumLine(m_hashesTreeWidget, file.readLine()); } } file.close(); button(QWizard::NextButton)->setEnabled(true); } QWizardPage *VerifyWizard::createIntroPage() { auto *page = new QWizardPage; page->setTitle(i18n("Verify Checksum File")); page->setPixmap(QWizard::LogoPixmap, Icon("document-edit-verify").pixmap(32)); page->setSubTitle(i18n("About to verify the following checksum file")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // checksum file auto *hLayout = new QHBoxLayout; QLabel *checksumFileLabel = new QLabel(i18n("Checksum file:")); hLayout->addWidget(checksumFileLabel); auto *checksumFileReq = new KUrlRequester; QString typesFilter; for (const QString ext: m_checksumTools.keys()) typesFilter += ("*." + ext + ' '); checksumFileReq->setFilter(typesFilter); checksumFileReq->setText(m_checksumFile); checksumFileReq->setFocus(); connect(checksumFileReq, &KUrlRequester::textChanged, this, &VerifyWizard::slotChecksumPathChanged); hLayout->addWidget(checksumFileReq); mainLayout->addLayout(hLayout); // content of checksum file m_hashesTreeWidget = new KrTreeWidget(page); m_hashesTreeWidget->setAllColumnsShowFocus(true); m_hashesTreeWidget->setHeaderLabels(QStringList() << i18n("Hash") << i18n("File")); mainLayout->addWidget(m_hashesTreeWidget); return page; } QWizardPage *VerifyWizard::createResultPage() { auto *page = new QWizardPage; page->setTitle(i18n("Verify Result")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); m_outputLabel = new QLabel(i18n("Result output:")); mainLayout->addWidget(m_outputLabel); m_outputListWidget = new KrListWidget; mainLayout->addWidget(m_outputListWidget); return page; } void VerifyWizard::onIntroPage() { // cannot do this in constructor: NextButton->hide() is overridden slotChecksumPathChanged(m_checksumFile); } void VerifyWizard::onProgressPage() { // verify checksum file... const QString extension = QFileInfo(m_checksumFile).suffix(); if (!checkExists(extension)) { button(QWizard::BackButton)->show(); return; } runProcess(extension, QStringList() << "--strict" << "-c" << m_checksumFile); } void VerifyWizard::onResultPage() { // better not only trust error output const bool errors = m_process->exitCode() != 0 || !m_process->errOutput().isEmpty(); QWizardPage *page = currentPage(); page->setPixmap(QWizard::LogoPixmap, Icon(errors ? "dialog-error" : "dialog-information").pixmap(32)); page->setSubTitle(errors ? i18n("Errors were detected while verifying the checksums") : i18n("Checksums were verified successfully")); // print everything, errors first m_outputListWidget->clear(); m_outputListWidget->addItems(m_process->errOutput() + m_process->stdOutput()); button(QWizard::FinishButton)->setEnabled(!errors); } bool VerifyWizard::isSupported(const QString &path) { const QFileInfo fileInfo(path); return fileInfo.isFile() && m_checksumTools.keys().contains(fileInfo.suffix()); } } // NAMESPACE CHECKSUM_ diff --git a/krusader/Dialogs/checksumdlg.h b/krusader/Dialogs/checksumdlg.h index 87d0aa25..095c1a0e 100644 --- a/krusader/Dialogs/checksumdlg.h +++ b/krusader/Dialogs/checksumdlg.h @@ -1,180 +1,180 @@ /***************************************************************************** * Copyright (C) 2005 Shie Erlich * * Copyright (C) 2007-2008 Csaba Karai * * Copyright (C) 2008 Jonas Bähr * * Copyright (C) 2005-2018 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 CHECKSUMDLG_H #define CHECKSUMDLG_H // QtCore #include #include #include // QtWidgets #include #include #include #include #include class KrListWidget; class KrTreeWidget; /** * Perform checksum operations: Creation of checksums or verifying files with a checksum file. * * The dialogs are not modal. The used checksum tools only support local files which are expected to * be in one directory (specified by 'path'). */ class Checksum { public: static void startCreationWizard(const QString &path, const QStringList &fileNames); static void startVerifyWizard(const QString &path, const QString &checksumFile = QString()); }; namespace CHECKSUM_ { // private namespace /** Wrapper for KProcess to handle errors and output. */ class ChecksumProcess : public KProcess { Q_OBJECT public: ChecksumProcess(QObject *parent, const QString &path); ~ChecksumProcess() override; QStringList stdOutput() const { return m_outputLines; } QStringList errOutput() const { return m_errorLines; } signals: void resultReady(); private slots: void slotError(QProcess::ProcessError error); void slotFinished(int, QProcess::ExitStatus exitStatus); private: QStringList m_outputLines; QStringList m_errorLines; QTemporaryFile m_tmpOutFile; QTemporaryFile m_tmpErrFile; }; /** Base class for common code in creation and verify wizard. */ class ChecksumWizard : public QWizard { Q_OBJECT public: explicit ChecksumWizard(const QString &path); ~ChecksumWizard() override; private slots: void slotCurrentIdChanged(int id); protected: virtual QWizardPage *createIntroPage() = 0; virtual QWizardPage *createResultPage() = 0; virtual void onIntroPage() = 0; virtual void onProgressPage() = 0; virtual void onResultPage() = 0; QWizardPage *createProgressPage(const QString &title); - bool checkExists(const QString type); + bool checkExists(const QString& type); void runProcess(const QString &type, const QStringList &args); void addChecksumLine(KrTreeWidget *tree, const QString &line); const QString m_path; ChecksumProcess *m_process; QMap m_checksumTools; // extension/typ-name -> binary name int m_introId, m_progressId, m_resultId; }; class CreateWizard : public ChecksumWizard { Q_OBJECT public: CreateWizard(const QString &path, const QStringList &_files); public slots: void accept() Q_DECL_OVERRIDE; private: QWizardPage *createIntroPage() Q_DECL_OVERRIDE; QWizardPage *createResultPage() Q_DECL_OVERRIDE; void onIntroPage() Q_DECL_OVERRIDE; void onProgressPage() Q_DECL_OVERRIDE; void onResultPage() Q_DECL_OVERRIDE; void createChecksums(); bool savePerFile(); bool saveChecksumFile(const QStringList &data, const QString &filename = QString()); const QStringList m_fileNames; QFutureWatcher m_listFilesWatcher; QString m_suggestedFilePath; // intro page KComboBox *m_methodBox; // result page KrTreeWidget *m_hashesTreeWidget; QLabel *m_errorLabel; KrListWidget *m_errorListWidget; QCheckBox *m_onePerFileBox; }; class VerifyWizard : public ChecksumWizard { Q_OBJECT public: VerifyWizard(const QString &path, const QString &inputFile); private slots: void slotChecksumPathChanged(const QString &path); private: QWizardPage *createIntroPage() Q_DECL_OVERRIDE; QWizardPage *createResultPage() Q_DECL_OVERRIDE; void onIntroPage() Q_DECL_OVERRIDE; void onProgressPage() Q_DECL_OVERRIDE; void onResultPage() Q_DECL_OVERRIDE; bool isSupported(const QString &path); QString m_checksumFile; // intro page KrTreeWidget *m_hashesTreeWidget; // result page QLabel *m_outputLabel; KrListWidget *m_outputListWidget; }; } // NAMESPACE CHECKSUM_ #endif // CHECKSUMDLG_H diff --git a/krusader/Dialogs/krpleasewait.cpp b/krusader/Dialogs/krpleasewait.cpp index 864cc6ac..a3e584e8 100644 --- a/krusader/Dialogs/krpleasewait.cpp +++ b/krusader/Dialogs/krpleasewait.cpp @@ -1,156 +1,156 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "krpleasewait.h" // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" -KRPleaseWait::KRPleaseWait(QString msg, QWidget *parent, int count, bool cancel): +KRPleaseWait::KRPleaseWait(const QString& msg, QWidget *parent, int count, bool cancel): QProgressDialog(cancel ? nullptr : parent) , inc(true) { setModal(!cancel); timer = new QTimer(this); setWindowTitle(i18n("Krusader::Wait")); setMinimumDuration(500); setAutoClose(false); setAutoReset(false); connect(timer, &QTimer::timeout, this, &KRPleaseWait::cycleProgress); auto* progress = new QProgressBar(this); progress->setMaximum(count); progress->setMinimum(0); setBar(progress); QLabel* label = new QLabel(this); setLabel(label); QPushButton* btn = new QPushButton(i18n("&Cancel"), this); setCancelButton(btn); btn->setEnabled(canClose = cancel); setLabelText(msg); show(); } void KRPleaseWait::closeEvent(QCloseEvent * e) { if (canClose) { emit canceled(); e->accept(); } else /* if cancel is not allowed, we disable */ e->ignore(); /* the window closing [x] also */ } void KRPleaseWait::incProgress(int howMuch) { setValue(value() + howMuch); } void KRPleaseWait::cycleProgress() { if (inc) setValue(value() + 1); else setValue(value() - 1); if (value() >= 9) inc = false; if (value() <= 0) inc = true; } KRPleaseWaitHandler::KRPleaseWaitHandler(QWidget *parentWindow) : QObject(parentWindow), _parentWindow(parentWindow), job(), dlg(nullptr) { } void KRPleaseWaitHandler::stopWait() { if (dlg != nullptr) delete dlg; dlg = nullptr; cycleMutex = incMutex = false; // return cursor to normal arrow _parentWindow->setCursor(Qt::ArrowCursor); } -void KRPleaseWaitHandler::startWaiting(QString msg, int count , bool cancel) +void KRPleaseWaitHandler::startWaiting(const QString& msg, int count , bool cancel) { if (dlg == nullptr) { dlg = new KRPleaseWait(msg , _parentWindow, count, cancel); connect(dlg, &KRPleaseWait::canceled, this, &KRPleaseWaitHandler::killJob); } incMutex = cycleMutex = _wasCancelled = false; dlg->setValue(0); dlg->setLabelText(msg); if (count == 0) { dlg->setMaximum(10); cycle = true; cycleProgress(); } else { dlg->setMaximum(count); cycle = false; } } void KRPleaseWaitHandler::cycleProgress() { if (cycleMutex) return; cycleMutex = true; if (dlg) dlg->cycleProgress(); if (cycle) QTimer::singleShot(2000, this, &KRPleaseWaitHandler::cycleProgress); cycleMutex = false; } void KRPleaseWaitHandler::killJob() { if (!job.isNull()) job->kill(KJob::EmitResult); stopWait(); _wasCancelled = true; } void KRPleaseWaitHandler::setJob(KIO::Job* j) { job = j; } void KRPleaseWaitHandler::incProgress(int i) { if (incMutex) return; incMutex = true; if (dlg) dlg->incProgress(i); incMutex = false; } diff --git a/krusader/Dialogs/krpleasewait.h b/krusader/Dialogs/krpleasewait.h index c3fe1ebc..febceda4 100644 --- a/krusader/Dialogs/krpleasewait.h +++ b/krusader/Dialogs/krpleasewait.h @@ -1,81 +1,81 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KRPLEASEWAIT_H #define KRPLEASEWAIT_H // QtCore #include #include // QtGui #include // QtWidgets #include #include class KRPleaseWait; class KRPleaseWaitHandler : public QObject { Q_OBJECT public: explicit KRPleaseWaitHandler(QWidget *parentWindow); public slots: - void startWaiting(QString msg, int count = 0, bool cancel = false); + void startWaiting(const QString& msg, int count = 0, bool cancel = false); void stopWait(); void cycleProgress(); void incProgress(int i); void killJob(); void setJob(KIO::Job* j); bool wasCancelled() const { return _wasCancelled; } private: QWidget *_parentWindow; QPointer job; KRPleaseWait * dlg; bool cycle, cycleMutex, incMutex, _wasCancelled; }; class KRPleaseWait : public QProgressDialog { Q_OBJECT public: - KRPleaseWait(QString msg, QWidget *parent, int count = 0 , bool cancel = false); + KRPleaseWait(const QString& msg, QWidget *parent, int count = 0 , bool cancel = false); public slots: void incProgress(int howMuch); void cycleProgress(); protected: bool inc; QTimer* timer; void closeEvent(QCloseEvent * e) Q_DECL_OVERRIDE; bool canClose; }; #endif diff --git a/krusader/Dialogs/krspecialwidgets.cpp b/krusader/Dialogs/krspecialwidgets.cpp index 8ed7936b..a4cd2e04 100644 --- a/krusader/Dialogs/krspecialwidgets.cpp +++ b/krusader/Dialogs/krspecialwidgets.cpp @@ -1,218 +1,219 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "krspecialwidgets.h" #include "krmaskchoice.h" #include "newftpgui.h" #include "../krglobal.h" // QtGui #include #include +#include ///////////////////////////////////////////////////////////////////////////// /////////////////////// Pie related widgets ///////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // The pie-related widgets use hard-coded coordinates to create the look. // This is ok since the whole widget is fitted into an existing view and thus // no re-alignments are needed. #define LEFT 10 #define BOTTOM 150 #define WIDTH 120 #define HEIGHT 40 #define Z_HEIGHT 10 #define STARTANGLE 0 #define DEG(x) (16*(x)) QColor KRPie::colors[ 12 ] = {Qt::red, Qt::blue, Qt::green, Qt::cyan, Qt::magenta, Qt::gray, Qt::black, Qt::white, Qt::darkRed, Qt::darkBlue, Qt::darkMagenta, Qt::darkCyan }; ////////////////////////////////////////////////////////////////////////////// /////////////// KRFSDisplay - Filesystem / Freespace Display ///////////////// ////////////////////////////////////////////////////////////////////////////// // This is the full constructor: use it for a mounted filesystem KRFSDisplay::KRFSDisplay(QWidget *parent, QString _alias, QString _realName, KIO::filesize_t _total, KIO::filesize_t _free) : QWidget(parent), totalSpace(_total), - freeSpace(_free), alias(_alias), realName(_realName), mounted(true), + freeSpace(_free), alias(std::move(_alias)), realName(std::move(_realName)), mounted(true), empty(false), supermount(false) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // Use this one for an unmounted filesystem KRFSDisplay::KRFSDisplay(QWidget *parent, QString _alias, QString _realName, bool sm) : - QWidget(parent), alias(_alias), realName(_realName), mounted(false), + QWidget(parent), alias(std::move(_alias)), realName(std::move(_realName)), mounted(false), empty(false), supermount(sm) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // This is used only when an empty widget needs to be displayed (for example: // when filesystem statistics haven't been calculated yet) KRFSDisplay::KRFSDisplay(QWidget *parent) : QWidget(parent), empty(true) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // The main painter! void KRFSDisplay::paintEvent(QPaintEvent *) { QPainter paint(this); if (!empty) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); // create the text // first, name and location QFont font = paint.font(); font.setWeight(QFont::Bold); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 20, alias); font.setWeight(QFont::Normal); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 37, '(' + realName + ')'); if (mounted) { // incase the filesystem is already mounted // second, the capacity paint.drawText(leftMargin + 10, topMargin + 70, i18n("Capacity: %1", KIO::convertSizeFromKiB(totalSpace))); // third, the 2 boxes (used, free) QPen systemPen = paint.pen(); paint.setPen(Qt::black); paint.drawRect(leftMargin + 10, topMargin + 90, 10, 10); paint.fillRect(leftMargin + 11, topMargin + 91, 8, 8, QBrush(Qt::gray)); paint.drawRect(leftMargin + 10, topMargin + 110, 10, 10); paint.fillRect(leftMargin + 11, topMargin + 111, 8, 8, QBrush(Qt::white)); // now, the text for the boxes paint.setPen(systemPen); paint.drawText(leftMargin + 25, topMargin + 100, i18n("Used: %1", KIO::convertSizeFromKiB(totalSpace - freeSpace))); paint.drawText(leftMargin + 25, topMargin + 120, i18n("Free: %1", KIO::convertSizeFromKiB(freeSpace))); // first, create the empty pie // bottom... paint.setPen(Qt::black); paint.setBrush(Qt::white); paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // body... paint.setPen(Qt::lightGray); for (int i = 1; i < Z_HEIGHT; ++i) paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - i, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // side lines... paint.setPen(Qt::black); paint.drawLine(leftMargin + LEFT, topMargin + BOTTOM + HEIGHT / 2, LEFT, BOTTOM + HEIGHT / 2 - Z_HEIGHT); paint.drawLine(leftMargin + LEFT + WIDTH, topMargin + BOTTOM + HEIGHT / 2, LEFT + WIDTH, BOTTOM + HEIGHT / 2 - Z_HEIGHT); // top of the pie paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - Z_HEIGHT, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // the "used space" slice float i = ((float)(totalSpace - freeSpace) / (totalSpace)) * 360.0; paint.setBrush(Qt::gray); paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - Z_HEIGHT, WIDTH, HEIGHT, STARTANGLE, (int) DEG(i)); // if we need to draw a 3d stripe ... if (i > 180.0) { for (int j = 1; j < Z_HEIGHT; ++j) paint.drawArc(leftMargin + LEFT, topMargin + BOTTOM - j, WIDTH, HEIGHT, STARTANGLE - 16 * 180, (int)(DEG(i - 180.0))); } } else { // if the filesystem is unmounted... font.setWeight(QFont::Bold); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 60, i18n("Not mounted.")); } } else { // if the widget is in empty situation... } } //////////////////////////////////////////////////////////////////////////////// KRPie::KRPie(KIO::filesize_t _totalSize, QWidget *parent) : QWidget(parent), totalSize(_totalSize) { slices.push_back(KRPieSlice(100, Qt::yellow, "DEFAULT")); sizeLeft = totalSize; resize(300, 300); } void KRPie::paintEvent(QPaintEvent *) { QPainter paint(this); // now create the slices float sAngle = STARTANGLE; for (int ndx = 0; ndx != slices.count(); ndx++) { paint.setBrush(slices[ndx].getColor()); paint.setPen(slices[ndx].getColor()); // angles are negative to create a clock-wise drawing of slices float angle = -(slices[ndx].getPerct() / 100 * 360) * 16; for (int i = 1; i < Z_HEIGHT; ++i) paint.drawPie(LEFT, BOTTOM + i, WIDTH, HEIGHT, (int) sAngle, (int) angle); sAngle += angle; } paint.setPen(Qt::yellow); // pen paint.setBrush(Qt::yellow); // fill // for (int i=1; i * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KRSPECIALWIDGETS_H #define KRSPECIALWIDGETS_H // QtCore #include // QtGui #include #include #include #include // QtWidgets #include #include #include +#include class KRPieSlice; class KRPie : public QWidget { Q_OBJECT public: explicit KRPie(KIO::filesize_t _totalSize, QWidget *parent = nullptr); void addSlice(KIO::filesize_t size, QString label); protected: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; private: QList slices; KIO::filesize_t totalSize, sizeLeft; static QColor colors[ 12 ]; }; class KRFSDisplay : public QWidget { Q_OBJECT public: // this constructor is used for a mounted filesystem KRFSDisplay(QWidget *parent, QString _alias, QString _realName, KIO::filesize_t _total, KIO::filesize_t _free); // this one is for an unmounted/supermount file system KRFSDisplay(QWidget *parent, QString _alias, QString _realName, bool sm = false); // the last one is used inside MountMan(R), when no filesystem is selected explicit KRFSDisplay(QWidget *parent); inline void setTotalSpace(KIO::filesize_t t) { totalSpace = t; } inline void setFreeSpace(KIO::filesize_t t) { freeSpace = t; } inline void setAlias(QString a) { - alias = a; + alias = std::move(a); } inline void setRealName(QString r) { - realName = r; + realName = std::move(r); } inline void setMounted(bool m) { mounted = m; } inline void setEmpty(bool e) { empty = e; } inline void setSupermount(bool s) { supermount = s; } protected: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; private: KIO::filesize_t totalSpace, freeSpace; QString alias, realName; bool mounted, empty, supermount; }; class KRPieSlice { public: KRPieSlice(float _perct, QColor _color, QString _label) : - perct(_perct), color(_color), label(_label) {} + perct(_perct), color(std::move(_color)), label(std::move(_label)) {} inline QColor getColor() { return color; } inline float getPerct() { return perct; } inline QString getLabel() { return label; } inline void setPerct(float _perct) { perct = _perct; } inline void setLabel(QString _label) { - label = _label; + label = std::move(_label); } private: float perct; QColor color; QString label; }; #endif diff --git a/krusader/Dialogs/krspwidgets.cpp b/krusader/Dialogs/krspwidgets.cpp index b42563aa..9017ad0a 100644 --- a/krusader/Dialogs/krspwidgets.cpp +++ b/krusader/Dialogs/krspwidgets.cpp @@ -1,258 +1,258 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "krspwidgets.h" #include "../krglobal.h" #include "../icon.h" #include "../Filter/filtertabs.h" #include "../GUI/krlistwidget.h" // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include #include // missing ? #include #include #include #include #include ///////////////////// initiation of the static members //////////////////////// QStringList KRSpWidgets::maskList; /////////////////////////////////////////////////////////////////////////////// KRSpWidgets::KRSpWidgets() = default; -KRQuery KRSpWidgets::getMask(QString caption, bool nameOnly, QWidget * parent) +KRQuery KRSpWidgets::getMask(const QString& caption, bool nameOnly, QWidget * parent) { if (!nameOnly) { return FilterTabs::getQuery(parent); } else { QPointer p = new KRMaskChoiceSub(parent); p->setWindowTitle(caption); p->exec(); QString selection = p->selection->currentText(); delete p; if (selection.isEmpty()) { return KRQuery(); } else { return KRQuery(selection); } } } /////////////////////////// newFTP //////////////////////////////////////// QUrl KRSpWidgets::newFTP() { QPointer p = new newFTPSub(); p->exec(); QString uri = p->url->currentText(); if (uri.isEmpty()) { delete p; return QUrl(); // empty url } QString protocol = p->prefix->currentText(); protocol.truncate(protocol.length() - 3); // remove the trailing :// QString username = p->username->text().simplified(); QString password = p->password->text().simplified(); int uriStart = uri.lastIndexOf('@'); /* lets the user enter user and password in the URI field */ if (uriStart != -1) { QString uriUser = uri.left(uriStart); QString uriPsw; uri = uri.mid(uriStart + 1); int pswStart = uriUser.indexOf(':'); /* getting the password name from the URL */ if (pswStart != -1) { uriPsw = uriUser.mid(pswStart + 1); uriUser = uriUser.left(pswStart); } if (!uriUser.isEmpty()) { /* handling the ftp proxy username and password also */ username = username.isEmpty() ? uriUser : username + '@' + uriUser; } if (!uriPsw.isEmpty()) { /* handling the ftp proxy username and password also */ password = password.isEmpty() ? uriPsw : password + '@' + uriPsw; } } QString host = uri; /* separating the hostname and path from the uri */ QString path; int pathStart = uri.indexOf("/"); if (pathStart != -1) { path = host.mid(pathStart); host = host.left(pathStart); } /* setting the parameters of the URL */ QUrl url; url.setScheme(protocol); url.setHost(host); url.setPath(path); if (protocol == "ftp" || protocol == "fish" || protocol == "sftp") { url.setPort(p->port->cleanText().toInt()); } if (!username.isEmpty()) { url.setUserName(username); } if (!password.isEmpty()) { url.setPassword(password); } delete p; return url; } newFTPSub::newFTPSub() : newFTPGUI(nullptr) { url->setFocus(); setGeometry(krMainWindow->x() + krMainWindow->width() / 2 - width() / 2, krMainWindow->y() + krMainWindow->height() / 2 - height() / 2, width(), height()); } void newFTPSub::accept() { url->addToHistory(url->currentText()); // save the history and completion list when the history combo is // destroyed KConfigGroup group(krConfig, "Private"); QStringList list = url->completionObject()->items(); group.writeEntry("newFTP Completion list", list); list = url->historyItems(); group.writeEntry("newFTP History list", list); QString protocol = prefix->currentText(); group.writeEntry("newFTP Protocol", protocol); newFTPGUI::accept(); } void newFTPSub::reject() { url->lineEdit()->setText(""); newFTPGUI::reject(); } /////////////////////////// KRMaskChoiceSub /////////////////////////////// KRMaskChoiceSub::KRMaskChoiceSub(QWidget * parent) : KRMaskChoice(parent) { PixmapLabel1->setPixmap(Icon("edit-select").pixmap(32)); label->setText(i18n("Enter a selection:")); // the predefined selections list KConfigGroup group(krConfig, "Private"); QStringList lst = group.readEntry("Predefined Selections", QStringList()); if (lst.size() > 0) preSelections->addItems(lst); // the combo-box tweaks selection->setDuplicatesEnabled(false); selection->addItems(KRSpWidgets::maskList); selection->lineEdit()->setText("*"); selection->lineEdit()->selectAll(); selection->setFocus(); } void KRMaskChoiceSub::reject() { selection->clear(); KRMaskChoice::reject(); } void KRMaskChoiceSub::accept() { bool add = true; // make sure we don't have that already for (int i = 0; i != KRSpWidgets::maskList.count(); i++) if (KRSpWidgets::maskList[ i ].simplified() == selection->currentText().simplified()) { // break if we found one such as this add = false; break; } if (add) KRSpWidgets::maskList.insert(0, selection->currentText().toLocal8Bit()); // write down the predefined selections list QStringList list; for (int j = 0; j != preSelections->count(); j++) { QListWidgetItem *i = preSelections->item(j); list.append(i->text()); } KConfigGroup group(krConfig, "Private"); group.writeEntry("Predefined Selections", list); KRMaskChoice::accept(); } void KRMaskChoiceSub::addSelection() { QString temp = selection->currentText(); bool itemExists = false; // check if the selection already exists for (int j = 0; j != preSelections->count(); j++) { QListWidgetItem *i = preSelections->item(j); if (i->text() == temp) { itemExists = true; break; } } if (!temp.isEmpty() && !itemExists) { preSelections->addItem(selection->currentText()); preSelections->update(); } } void KRMaskChoiceSub::deleteSelection() { delete preSelections->currentItem(); preSelections->update(); } void KRMaskChoiceSub::clearSelections() { preSelections->clear(); preSelections->update(); } void KRMaskChoiceSub::acceptFromList(QListWidgetItem *i) { selection->addItem(i->text(), 0); accept(); } void KRMaskChoiceSub::currentItemChanged(QListWidgetItem *i) { if (i) selection->setEditText(i->text()); } diff --git a/krusader/Dialogs/krspwidgets.h b/krusader/Dialogs/krspwidgets.h index bd277e55..3de8cd38 100644 --- a/krusader/Dialogs/krspwidgets.h +++ b/krusader/Dialogs/krspwidgets.h @@ -1,83 +1,83 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KRSPWIDGETS_H #define KRSPWIDGETS_H // QtCore #include // QtGui #include #include #include #include "krmaskchoice.h" #include "newftpgui.h" #include "../FileSystem/krquery.h" class KRMaskChoiceSub; class KRSpWidgets { friend class KRMaskChoiceSub; public: KRSpWidgets(); - static KRQuery getMask(QString caption, bool nameOnly = false, QWidget * parent = 0); // get file-mask for (un)selecting files + static KRQuery getMask(const QString& caption, bool nameOnly = false, QWidget * parent = 0); // get file-mask for (un)selecting files static QUrl newFTP(); private: static QStringList maskList; // used by KRMaskChoiceSub }; /////////////////////////// newFTPSub /////////////////////////////////////// class newFTPSub : public newFTPGUI { public: newFTPSub(); protected: void reject() Q_DECL_OVERRIDE; void accept() Q_DECL_OVERRIDE; }; /////////////////////////// KRMaskChoiceSub ///////////////////////////////// // Inherits KRMaskChoice's generated code to fully implement the functions // ///////////////////////////////////////////////////////////////////////////// class KRMaskChoiceSub : public KRMaskChoice { public: explicit KRMaskChoiceSub(QWidget * parent = 0); public slots: void addSelection() Q_DECL_OVERRIDE; void deleteSelection() Q_DECL_OVERRIDE; void clearSelections() Q_DECL_OVERRIDE; void acceptFromList(QListWidgetItem *i) Q_DECL_OVERRIDE; void currentItemChanged(QListWidgetItem *i) Q_DECL_OVERRIDE; protected: void reject() Q_DECL_OVERRIDE; void accept() Q_DECL_OVERRIDE; }; #endif diff --git a/krusader/Dialogs/packgui.cpp b/krusader/Dialogs/packgui.cpp index 3c63782f..61c2ea51 100644 --- a/krusader/Dialogs/packgui.cpp +++ b/krusader/Dialogs/packgui.cpp @@ -1,127 +1,127 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "packgui.h" #include "../krglobal.h" #include "../defaults.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #define PS(x) lst.contains(x)>0 // clear the statics first QString PackGUI::filename = nullptr; QString PackGUI::destination = nullptr; QString PackGUI::type = nullptr; QMap PackGUI::extraProps; -PackGUI::PackGUI(QString defaultName, QString defaultPath, int noOfFiles, QString filename) : +PackGUI::PackGUI(const QString& defaultName, const QString& defaultPath, int noOfFiles, const QString& filename) : PackGUIBase(nullptr) { // first, fill the WhatToPack textfield with information if (noOfFiles == 1) TextLabel1->setText(i18n("Pack %1", filename)); else TextLabel1->setText(i18np("Pack %1 file", "Pack %1 files", noOfFiles)); // now, according to the Konfigurator, fill the combobox with the information // about what kind of packing we can do KConfigGroup group(krConfig, "Archives"); QStringList lst = group.readEntry("Supported Packers", QStringList()); // now, clear the type combo and begin... typeData->clear(); if (PS("tar")) typeData->addItem("tar"); if (PS("tar") && PS("gzip")) typeData->addItem("tar.gz"); if (PS("tar") && PS("bzip2")) typeData->addItem("tar.bz2"); if (PS("tar") && PS("lzma")) typeData->addItem("tar.lzma"); if (PS("tar") && PS("xz")) typeData->addItem("tar.xz"); if (PS("zip")) typeData->addItem("zip"); if (PS("zip")) typeData->addItem("cbz"); if (PS("rar")) typeData->addItem("rar"); if (PS("rar")) typeData->addItem("cbr"); if (PS("lha")) typeData->addItem("lha"); if (PS("arj")) typeData->addItem("arj"); if (PS("7z")) typeData->addItem("7z"); // set the last used packer as the top one QString tmp = group.readEntry("lastUsedPacker", QString()); if (!tmp.isEmpty()) { for (int i = 0; i < typeData->count(); ++i) if (typeData->itemText(i) == tmp) { typeData->removeItem(i); typeData->insertItem(0, tmp); typeData->setCurrentIndex(0); break; } } checkConsistency(); // and go on with the normal stuff dirData->setText(defaultPath); nameData->setText(defaultName); nameData->setFocus(); if (typeData->count() == 0) // if no packers are available okButton->setEnabled(false); setGeometry(krMainWindow->x() + krMainWindow->width() / 2 - width() / 2, krMainWindow->y() + krMainWindow->height() / 2 - height() / 2, width(), height()); exec(); } void PackGUI::browse() { QString temp = QFileDialog::getExistingDirectory(nullptr, i18n("Please select a folder"), dirData->text()); if (!temp.isEmpty()) { dirData->setText(temp); } } void PackGUI::accept() { if (!extraProperties(extraProps)) return; filename = nameData->text(); destination = dirData->text(); type = typeData->currentText(); // write down the packer chosen, to be lastUsedPacker KConfigGroup group(krConfig, "Archives"); group.writeEntry("lastUsedPacker", type); krConfig->sync(); PackGUIBase::accept(); } void PackGUI::reject() { filename.clear(); destination.clear(); type.clear(); PackGUIBase::reject(); } diff --git a/krusader/Dialogs/packgui.h b/krusader/Dialogs/packgui.h index caea6ec6..ed4ef376 100644 --- a/krusader/Dialogs/packgui.h +++ b/krusader/Dialogs/packgui.h @@ -1,45 +1,45 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 PACKGUI_H #define PACKGUI_H #include "packguibase.h" class PackGUI : public PackGUIBase { Q_OBJECT public: - PackGUI(QString defaultName, QString defaultPath, int noOfFiles, QString filename = ""); + PackGUI(const QString& defaultName, const QString& defaultPath, int noOfFiles, const QString& filename = ""); public slots: void browse() Q_DECL_OVERRIDE; protected slots: void accept() Q_DECL_OVERRIDE; void reject() Q_DECL_OVERRIDE; public: static QString filename, destination, type; static QMap extraProps; }; #endif diff --git a/krusader/DiskUsage/diskusage.cpp b/krusader/DiskUsage/diskusage.cpp index 68b7ea0d..27a972ee 100644 --- a/krusader/DiskUsage/diskusage.cpp +++ b/krusader/DiskUsage/diskusage.cpp @@ -1,1133 +1,1134 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "diskusage.h" // QtCore #include #include #include #include #include #include // QtGui #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "dufilelight.h" #include "dulines.h" #include "dulistview.h" #include "filelightParts/Config.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/filesystemprovider.h" #include "../FileSystem/krpermhandler.h" #include "../Panel/krpanel.h" #include "../Panel/panelfunc.h" #include "../defaults.h" #include "../krglobal.h" #include "../filelisticon.h" // these are the values that will exist in the menu #define DELETE_ID 90 #define EXCLUDE_ID 91 #define PARENT_DIR_ID 92 #define NEW_SEARCH_ID 93 #define REFRESH_ID 94 #define STEP_INTO_ID 95 #define INCLUDE_ALL_ID 96 #define VIEW_POPUP_ID 97 #define LINES_VIEW_ID 98 #define DETAILED_VIEW_ID 99 #define FILELIGHT_VIEW_ID 100 #define NEXT_VIEW_ID 101 #define PREVIOUS_VIEW_ID 102 #define ADDITIONAL_POPUP_ID 103 #define MAX_FILENUM 100 LoaderWidget::LoaderWidget(QWidget *parent) : QScrollArea(parent), cancelled(false) { QPalette palette = viewport()->palette(); palette.setColor(viewport()->backgroundRole(), Qt::white); viewport()->setPalette(palette); widget = new QWidget(parent); auto *loaderLayout = new QGridLayout(widget); loaderLayout->setSpacing(0); loaderLayout->setContentsMargins(0, 0, 0, 0); QFrame *loaderBox = new QFrame(widget); loaderBox->setFrameShape(QFrame::Box); loaderBox->setFrameShadow(QFrame::Sunken); loaderBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); loaderBox->setFrameStyle(QFrame::Panel + QFrame::Raised); loaderBox->setLineWidth(2); auto *synchGrid = new QGridLayout(loaderBox); synchGrid->setSpacing(6); synchGrid->setContentsMargins(11, 11, 11, 11); QLabel *titleLabel = new QLabel(i18n("Loading Usage Information"), loaderBox); titleLabel->setAlignment(Qt::AlignHCenter); synchGrid->addWidget(titleLabel, 0, 0, 1, 2); QLabel *filesLabel = new QLabel(i18n("Files:"), loaderBox); filesLabel->setFrameShape(QLabel::StyledPanel); filesLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(filesLabel, 1, 0); QLabel *directoriesLabel = new QLabel(i18n("Directories:"), loaderBox); directoriesLabel->setFrameShape(QLabel::StyledPanel); directoriesLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(directoriesLabel, 2, 0); QLabel *totalSizeLabel = new QLabel(i18n("Total Size:"), loaderBox); totalSizeLabel->setFrameShape(QLabel::StyledPanel); totalSizeLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(totalSizeLabel, 3, 0); files = new QLabel(loaderBox); files->setFrameShape(QLabel::StyledPanel); files->setFrameShadow(QLabel::Sunken); files->setAlignment(Qt::AlignRight); synchGrid->addWidget(files, 1, 1); directories = new QLabel(loaderBox); directories->setFrameShape(QLabel::StyledPanel); directories->setFrameShadow(QLabel::Sunken); directories->setAlignment(Qt::AlignRight); synchGrid->addWidget(directories, 2, 1); totalSize = new QLabel(loaderBox); totalSize->setFrameShape(QLabel::StyledPanel); totalSize->setFrameShadow(QLabel::Sunken); totalSize->setAlignment(Qt::AlignRight); synchGrid->addWidget(totalSize, 3, 1); int width; searchedDirectory = new KSqueezedTextLabel(loaderBox); searchedDirectory->setFrameShape(QLabel::StyledPanel); searchedDirectory->setFrameShadow(QLabel::Sunken); searchedDirectory->setMinimumWidth(width = QFontMetrics(searchedDirectory->font()).width("W") * 30); searchedDirectory->setMaximumWidth(width); synchGrid->addWidget(searchedDirectory, 4, 0, 1, 2); QFrame *line = new QFrame(loaderBox); line->setFrameStyle(QFrame::HLine | QFrame::Sunken); synchGrid->addWidget(line, 5, 0, 1, 2); QWidget *hboxWidget = new QWidget(loaderBox); auto * hbox = new QHBoxLayout(hboxWidget); auto* spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); hbox->addItem(spacer); auto *cancelButton = new QPushButton(hboxWidget); KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel); hbox->addWidget(cancelButton); synchGrid->addWidget(hboxWidget, 6, 1); loaderLayout->addWidget(loaderBox, 0, 0); setWidget(widget); setAlignment(Qt::AlignCenter); connect(cancelButton, &QPushButton::clicked, this, &LoaderWidget::slotCancelled); } void LoaderWidget::init() { cancelled = false; } void LoaderWidget::setCurrentURL(const QUrl &url) { searchedDirectory->setText(FileSystem::ensureTrailingSlash(url).toDisplayString(QUrl::PreferLocalFile)); } void LoaderWidget::setValues(int fileNum, int dirNum, KIO::filesize_t total) { files->setText(QString("%1").arg(fileNum)); directories->setText(QString("%1").arg(dirNum)); totalSize->setText(QString("%1").arg(KRpermHandler::parseSize(total).trimmed())); } void LoaderWidget::slotCancelled() { cancelled = true; } DiskUsage::DiskUsage(QString confGroup, QWidget *parent) : QStackedWidget(parent), - currentDirectory(nullptr), root(nullptr), configGroup(confGroup), loading(false), + currentDirectory(nullptr), root(nullptr), configGroup(std::move(confGroup)), loading(false), abortLoading(false), clearAfterAbort(false), deleting(false), searchFileSystem(nullptr) { listView = new DUListView(this); lineView = new DULines(this); filelightView = new DUFilelight(this); loaderView = new LoaderWidget(this); addWidget(listView); addWidget(lineView); addWidget(filelightView); addWidget(loaderView); setView(VIEW_LINES); Filelight::Config::read(); connect(&loadingTimer, &QTimer::timeout, this, &DiskUsage::slotLoadDirectory); } DiskUsage::~DiskUsage() { if (listView) // don't remove these lines. The module will crash at exit if removed delete listView; if (lineView) delete lineView; if (filelightView) delete filelightView; if (root) delete root; QHashIterator< File *, Properties * > lit(propertyMap); while (lit.hasNext()) delete lit.next().value(); } void DiskUsage::load(const QUrl &baseDir) { fileNum = dirNum = 0; currentSize = 0; emit status(i18n("Loading the disk usage information...")); clear(); baseURL = baseDir.adjusted(QUrl::StripTrailingSlash); root = new Directory(baseURL.fileName(), baseDir.toDisplayString(QUrl::PreferLocalFile)); directoryStack.clear(); parentStack.clear(); directoryStack.push(""); parentStack.push(root); if (searchFileSystem) { delete searchFileSystem; searchFileSystem = nullptr; } searchFileSystem = FileSystemProvider::instance().getFilesystem(baseDir); if (searchFileSystem == nullptr) { qWarning() << "could not get filesystem for directory=" << baseDir; loading = abortLoading = clearAfterAbort = false; emit loadFinished(false); return; } currentFileItem = nullptr; if (!loading) { viewBeforeLoad = activeView; setView(VIEW_LOADER); } loading = true; loaderView->init(); loaderView->setCurrentURL(baseURL); loaderView->setValues(fileNum, dirNum, currentSize); loadingTimer.setSingleShot(true); loadingTimer.start(0); } void DiskUsage::slotLoadDirectory() { if ((currentFileItem == nullptr && directoryStack.isEmpty()) || loaderView->wasCancelled() || abortLoading) { if (searchFileSystem) delete searchFileSystem; searchFileSystem = nullptr; currentFileItem = nullptr; setView(viewBeforeLoad); if (clearAfterAbort) clear(); else { calculateSizes(); changeDirectory(root); } emit loadFinished(!(loaderView->wasCancelled() || abortLoading)); loading = abortLoading = clearAfterAbort = false; } else if (loading) { for (int counter = 0; counter != MAX_FILENUM; counter ++) { if (currentFileItem == nullptr) { if (directoryStack.isEmpty()) break; dirToCheck = directoryStack.pop(); currentParent = parentStack.pop(); contentMap.insert(dirToCheck, currentParent); QUrl url = baseURL; if (!dirToCheck.isEmpty()) { url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (dirToCheck)); } #ifdef BSD if (url.isLocalFile() && url.path().left(7) == "/procfs") break; #else if (url.isLocalFile() && url.path().left(5) == "/proc") break; #endif loaderView->setCurrentURL(url); if (!searchFileSystem->scanDir(url)) break; fileItems = searchFileSystem->fileItems(); dirNum++; currentFileItem = fileItems.isEmpty() ? 0 : fileItems.takeFirst(); } else { fileNum++; File *newItem = nullptr; QString mime = currentFileItem->getMime(); // fast == not using mimetype magic if (currentFileItem->isDir() && !currentFileItem->isSymLink()) { newItem = new Directory(currentParent, currentFileItem->getName(), dirToCheck, currentFileItem->getSize(), currentFileItem->getMode(), currentFileItem->getOwner(), currentFileItem->getGroup(), currentFileItem->getPerm(), currentFileItem->getTime_t(), currentFileItem->isSymLink(), mime); directoryStack.push((dirToCheck.isEmpty() ? "" : dirToCheck + '/') + currentFileItem->getName()); parentStack.push(dynamic_cast(newItem)); } else { newItem = new File(currentParent, currentFileItem->getName(), dirToCheck, currentFileItem->getSize(), currentFileItem->getMode(), currentFileItem->getOwner(), currentFileItem->getGroup(), currentFileItem->getPerm(), currentFileItem->getTime_t(), currentFileItem->isSymLink(), mime); currentSize += currentFileItem->getSize(); } currentParent->append(newItem); currentFileItem = fileItems.isEmpty() ? 0 : fileItems.takeFirst(); } } loaderView->setValues(fileNum, dirNum, currentSize); loadingTimer.setSingleShot(true); loadingTimer.start(0); } } void DiskUsage::stopLoad() { abortLoading = true; } void DiskUsage::close() { if (loading) { abortLoading = true; clearAfterAbort = true; } } void DiskUsage::dirUp() { if (currentDirectory != nullptr) { if (currentDirectory->parent() != nullptr) changeDirectory((Directory *)(currentDirectory->parent())); else { QUrl up = KIO::upUrl(baseURL); if (KMessageBox::questionYesNo(this, i18n("Stepping into the parent folder requires " "loading the content of the \"%1\" URL. Do you wish " "to continue?", up.toDisplayString(QUrl::PreferLocalFile)), i18n("Krusader::DiskUsage"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "DiskUsageLoadParentDir" ) == KMessageBox::Yes) load(up); } } } Directory * DiskUsage::getDirectory(QString dir) { while (dir.endsWith('/')) dir.truncate(dir.length() - 1); if (dir.isEmpty()) return root; if (contentMap.find(dir) == contentMap.end()) return nullptr; return contentMap[ dir ]; } -File * DiskUsage::getFile(QString path) +File * DiskUsage::getFile(const QString& path) { if (path.isEmpty()) return root; QString dir = path; int ndx = path.lastIndexOf('/'); QString file = path.mid(ndx + 1); if (ndx == -1) dir = ""; else dir.truncate(ndx); Directory *dirEntry = getDirectory(dir); if (dirEntry == nullptr) return nullptr; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) if ((*it)->name() == file) return *it; return nullptr; } void DiskUsage::clear() { baseURL = QUrl(); emit clearing(); QHashIterator< File *, Properties * > lit(propertyMap); while (lit.hasNext()) delete lit.next().value(); propertyMap.clear(); contentMap.clear(); if (root) delete root; root = currentDirectory = nullptr; } int DiskUsage::calculateSizes(Directory *dirEntry, bool emitSig, int depth) { int changeNr = 0; if (dirEntry == nullptr) dirEntry = root; KIO::filesize_t own = 0, total = 0; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File * item = *it; if (!item->isExcluded()) { if (item->isDir()) changeNr += calculateSizes(dynamic_cast(item), emitSig, depth + 1); else own += item->size(); total += item->size(); } } KIO::filesize_t oldOwn = dirEntry->ownSize(), oldTotal = dirEntry->size(); dirEntry->setSizes(total, own); if (dirEntry == currentDirectory) currentSize = total; if (emitSig && (own != oldOwn || total != oldTotal)) { emit changed(dirEntry); changeNr++; } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } int DiskUsage::exclude(File *file, bool calcPercents, int depth) { int changeNr = 0; if (!file->isExcluded()) { file->exclude(true); emit changed(file); changeNr++; if (file->isDir()) { auto *dir = dynamic_cast(file); for (Iterator it = dir->iterator(); it != dir->end(); ++it) changeNr += exclude(*it, false, depth + 1); } } if (calcPercents) { calculateSizes(root, true); calculatePercents(true); createStatus(); } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } int DiskUsage::include(Directory *dir, int depth) { int changeNr = 0; if (dir == nullptr) return 0; for (Iterator it = dir->iterator(); it != dir->end(); ++it) { File *item = *it; if (item->isDir()) changeNr += include(dynamic_cast(item), depth + 1); if (item->isExcluded()) { item->exclude(false); emit changed(item); changeNr++; } } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } void DiskUsage::includeAll() { include(root); calculateSizes(root, true); calculatePercents(true); createStatus(); } int DiskUsage::del(File *file, bool calcPercents, int depth) { int deleteNr = 0; if (file == root) return 0; KConfigGroup gg(krConfig, "General"); bool trash = gg.readEntry("Move To Trash", _MoveToTrash); QUrl url = QUrl::fromLocalFile(file->fullPath()); if (calcPercents) { // now ask the user if he want to delete: KConfigGroup ga(krConfig, "Advanced"); if (ga.readEntry("Confirm Delete", _ConfirmDelete)) { QString s; KGuiItem b; if (trash && url.isLocalFile()) { s = i18nc("singularOnly", "Do you really want to move this item to the trash?"); b = KGuiItem(i18n("&Trash")); } else { s = i18nc("singularOnly", "Do you really want to delete this item?"); b = KStandardGuiItem::del(); } QStringList name; name.append(file->fullPath()); // 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, name, i18n("Warning"), b) != KMessageBox::Continue) return 0; } emit status(i18n("Deleting %1...", file->name())); } if (file == currentDirectory) dirUp(); if (file->isDir()) { auto *dir = dynamic_cast(file); Iterator it; while ((it = dir->iterator()) != dir->end()) deleteNr += del(*it, false, depth + 1); QString path; for (const Directory *d = (Directory*)file; d != root && d && d->parent() != nullptr; d = d->parent()) { if (!path.isEmpty()) path = '/' + path; path = d->name() + path; } contentMap.remove(path); } emit deleted(file); deleteNr++; KIO::Job *job; if (trash) { job = KIO::trash(url); } else { job = KIO::del(QUrl::fromLocalFile(file->fullPath()), KIO::HideProgressInfo); } deleting = true; // during qApp->processEvent strange things can occur grabMouse(); // that's why we disable the mouse and keyboard events grabKeyboard(); job->exec(); delete job; releaseMouse(); releaseKeyboard(); deleting = false; ((Directory *)(file->parent()))->remove(file); delete file; if (depth == 0) createStatus(); if (calcPercents) { calculateSizes(root, true); calculatePercents(true); createStatus(); emit enteringDirectory(currentDirectory); } if (depth == 0 && deleteNr != 0) emit deleteFinished(); return deleteNr; } -void * DiskUsage::getProperty(File *item, QString key) +void * DiskUsage::getProperty(File *item, const QString& key) { QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) return nullptr; QHash::iterator it = (*itr)->find(key); if (it == (*itr)->end()) return nullptr; return it.value(); } -void DiskUsage::addProperty(File *item, QString key, void * prop) +void DiskUsage::addProperty(File *item, const QString& key, void * prop) { Properties *props; QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) { props = new Properties(); propertyMap.insert(item, props); } else props = *itr; props->insert(key, prop); } -void DiskUsage::removeProperty(File *item, QString key) +void DiskUsage::removeProperty(File *item, const QString& key) { QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) return; (*itr)->remove(key); if ((*itr)->count() == 0) propertyMap.remove(item); } void DiskUsage::createStatus() { Directory *dirEntry = currentDirectory; if (dirEntry == nullptr) return; QUrl url = baseURL; if (dirEntry != root) { url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (dirEntry->directory())); } emit status(i18n("Current folder:%1, Total size:%2, Own size:%3", url.toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash), ' ' + KRpermHandler::parseSize(dirEntry->size()), ' ' + KRpermHandler::parseSize(dirEntry->ownSize()))); } void DiskUsage::changeDirectory(Directory *dir) { currentDirectory = dir; currentSize = dir->size(); calculatePercents(true, dir); createStatus(); emit enteringDirectory(dir); } Directory* DiskUsage::getCurrentDir() { return currentDirectory; } -void DiskUsage::rightClickMenu(const QPoint & pos, File *fileItem, QMenu *addPopup, QString addPopupName) +void DiskUsage::rightClickMenu(const QPoint & pos, File *fileItem, QMenu *addPopup, const QString& addPopupName) { QMenu popup(this); popup.setTitle(i18n("Disk Usage")); QHash actionHash; if (fileItem != nullptr) { QAction * actDelete = popup.addAction(i18n("Delete")); actionHash[ actDelete ] = DELETE_ID; actDelete->setShortcut(Qt::Key_Delete); QAction * actExclude = popup.addAction(i18n("Exclude")); actionHash[ actExclude ] = EXCLUDE_ID; actExclude->setShortcut(Qt::CTRL + Qt::Key_E); popup.addSeparator(); } QAction * myAct = popup.addAction(i18n("Up one folder")); actionHash[ myAct ] = PARENT_DIR_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Up); myAct = popup.addAction(i18n("New search")); actionHash[ myAct ] = NEW_SEARCH_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_N); myAct = popup.addAction(i18n("Refresh")); actionHash[ myAct ] = REFRESH_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_R); myAct = popup.addAction(i18n("Include all")); actionHash[ myAct ] = INCLUDE_ALL_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_I); myAct = popup.addAction(i18n("Step into")); actionHash[ myAct ] = STEP_INTO_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Down); popup.addSeparator(); if (addPopup != nullptr) { QAction * menu = popup.addMenu(addPopup); menu->setText(addPopupName); } QMenu viewPopup; myAct = viewPopup.addAction(i18n("Lines")); actionHash[ myAct ] = LINES_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_L); myAct = viewPopup.addAction(i18n("Detailed")); actionHash[ myAct ] = DETAILED_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_D); myAct = viewPopup.addAction(i18n("Filelight")); actionHash[ myAct ] = FILELIGHT_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_F); viewPopup.addSeparator(); myAct = viewPopup.addAction(i18n("Next")); actionHash[ myAct ] = NEXT_VIEW_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Right); myAct = viewPopup.addAction(i18n("Previous")); actionHash[ myAct ] = PREVIOUS_VIEW_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Left); QAction * menu = popup.addMenu(&viewPopup); menu->setText(i18n("View")); QAction * res = popup.exec(pos); if (actionHash.contains(res)) executeAction(actionHash[ res ], fileItem); } void DiskUsage::executeAction(int action, File * fileItem) { // check out the user's option switch (action) { case DELETE_ID: if (fileItem) del(fileItem); break; case EXCLUDE_ID: if (fileItem) exclude(fileItem); break; case PARENT_DIR_ID: dirUp(); break; case NEW_SEARCH_ID: emit newSearch(); break; case REFRESH_ID: load(baseURL); break; case INCLUDE_ALL_ID: includeAll(); break; case STEP_INTO_ID: { QString uri; if (fileItem && fileItem->isDir()) uri = fileItem->fullPath(); else uri = currentDirectory->fullPath(); ACTIVE_FUNC->openUrl(QUrl::fromLocalFile(uri)); } break; case LINES_VIEW_ID: setView(VIEW_LINES); break; case DETAILED_VIEW_ID: setView(VIEW_DETAILED); break; case FILELIGHT_VIEW_ID: setView(VIEW_FILELIGHT); break; case NEXT_VIEW_ID: setView((activeView + 1) % 3); break; case PREVIOUS_VIEW_ID: setView((activeView + 2) % 3); break; } // currentWidget()->setFocus(); } void DiskUsage::keyPressEvent(QKeyEvent *e) { if (activeView != VIEW_LOADER) { switch (e->key()) { case Qt::Key_E: if (e->modifiers() == Qt::ControlModifier) { executeAction(EXCLUDE_ID, getCurrentFile()); return; } break; case Qt::Key_D: if (e->modifiers() == Qt::ControlModifier) { executeAction(DETAILED_VIEW_ID); return; } break; case Qt::Key_F: if (e->modifiers() == Qt::ControlModifier) { executeAction(FILELIGHT_VIEW_ID); return; } break; case Qt::Key_I: if (e->modifiers() == Qt::ControlModifier) { executeAction(INCLUDE_ALL_ID); return; } break; case Qt::Key_L: if (e->modifiers() == Qt::ControlModifier) { executeAction(LINES_VIEW_ID); return; } break; case Qt::Key_N: if (e->modifiers() == Qt::ControlModifier) { executeAction(NEW_SEARCH_ID); return; } break; case Qt::Key_R: if (e->modifiers() == Qt::ControlModifier) { executeAction(REFRESH_ID); return; } break; case Qt::Key_Up: if (e->modifiers() == Qt::ShiftModifier) { executeAction(PARENT_DIR_ID); return; } break; case Qt::Key_Down: if (e->modifiers() == Qt::ShiftModifier) { executeAction(STEP_INTO_ID); return; } break; case Qt::Key_Left: if (e->modifiers() == Qt::ShiftModifier) { executeAction(PREVIOUS_VIEW_ID); return; } break; case Qt::Key_Right: if (e->modifiers() == Qt::ShiftModifier) { executeAction(NEXT_VIEW_ID); return; } break; case Qt::Key_Delete: if (!e->modifiers()) { executeAction(DELETE_ID, getCurrentFile()); return; } break; case Qt::Key_Plus: if (activeView == VIEW_FILELIGHT) { filelightView->zoomIn(); return; } break; case Qt::Key_Minus: if (activeView == VIEW_FILELIGHT) { filelightView->zoomOut(); return; } break; } } QStackedWidget::keyPressEvent(e); } -QPixmap DiskUsage::getIcon(QString mime) +QPixmap DiskUsage::getIcon(const QString& mime) { QPixmap icon; if (!QPixmapCache::find(mime, icon)) { // get the icon. if (mime == "Broken Link !") // FIXME: this doesn't work anymore - the reported mimetype for a broken link is now "unknown" icon = FileListIcon("file-broken").pixmap(); else { QMimeDatabase db; QMimeType mt = db.mimeTypeForName(mime); if (mt.isValid()) icon = FileListIcon(mt.iconName()).pixmap(); else icon = FileListIcon("file-broken").pixmap(); } // insert it into the cache QPixmapCache::insert(mime, icon); } return icon; } int DiskUsage::calculatePercents(bool emitSig, Directory *dirEntry, int depth) { int changeNr = 0; if (dirEntry == nullptr) dirEntry = root; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; if (!item->isExcluded()) { int newPerc; if (dirEntry->size() == 0 && item->size() == 0) newPerc = 0; else if (dirEntry->size() == 0) newPerc = -1; else newPerc = (int)((double)item->size() / (double)currentSize * 10000. + 0.5); int oldPerc = item->intPercent(); item->setPercent(newPerc); if (emitSig && newPerc != oldPerc) { emit changed(item); changeNr++; } if (item->isDir()) changeNr += calculatePercents(emitSig, dynamic_cast(item), depth + 1); } } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } QString DiskUsage::getToolTip(File *item) { QMimeDatabase db; QMimeType mt = db.mimeTypeForName(item->mime()); QString mime; if (mt.isValid()) mime = mt.comment(); time_t tma = item->time(); struct tm* t = localtime((time_t *) & tma); QDateTime tmp(QDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday), QTime(t->tm_hour, t->tm_min)); QString date = QLocale().toString(tmp, QLocale::ShortFormat); QString str = "
" + "" + ""; if (item->isDir()) str += ""; str += "" + "" + "" + "
" + i18n("Name:") + "" + item->name() + "
" + i18n("Type:") + "" + mime + "
" + i18n("Size:") + "" + KRpermHandler::parseSize(item->size()) + "
" + i18n("Own size:") + "" + KRpermHandler::parseSize(item->ownSize()) + "
" + i18n("Last modified:") + "" + date + "
" + i18n("Permissions:") + "" + item->perm() + "
" + i18n("Owner:") + "" + item->owner() + " - " + item->group() + "
"; str.replace(' ', " "); return str; } void DiskUsage::setView(int view) { switch (view) { case VIEW_LINES: setCurrentWidget(lineView); break; case VIEW_DETAILED: setCurrentWidget(listView); break; case VIEW_FILELIGHT: setCurrentWidget(filelightView); break; case VIEW_LOADER: setCurrentWidget(loaderView); break; } // currentWidget()->setFocus(); emit viewChanged(activeView = view); } File * DiskUsage::getCurrentFile() { File * file = nullptr; switch (activeView) { case VIEW_LINES: file = lineView->getCurrentFile(); break; case VIEW_DETAILED: file = listView->getCurrentFile(); break; case VIEW_FILELIGHT: file = filelightView->getCurrentFile(); break; } return file; } bool DiskUsage::event(QEvent * e) { if (deleting) { // if we are deleting, disable the mouse and switch (e->type()) { // keyboard events case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::KeyPress: case QEvent::KeyRelease: return true; default: break; } } if (e->type() == QEvent::ShortcutOverride) { auto* ke = (QKeyEvent*) e; if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::KeypadModifier) { switch (ke->key()) { case Qt::Key_Delete: case Qt::Key_Plus: case Qt::Key_Minus: ke->accept(); break; } } else if (ke->modifiers() == Qt::ShiftModifier) { switch (ke->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: ke->accept(); break; } } else if (ke->modifiers() & Qt::ControlModifier) { switch (ke->key()) { case Qt::Key_D: case Qt::Key_E: case Qt::Key_F: case Qt::Key_I: case Qt::Key_L: case Qt::Key_N: case Qt::Key_R: ke->accept(); break; } } } return QStackedWidget::event(e); } diff --git a/krusader/DiskUsage/diskusage.h b/krusader/DiskUsage/diskusage.h index 14e1b279..3e7696db 100644 --- a/krusader/DiskUsage/diskusage.h +++ b/krusader/DiskUsage/diskusage.h @@ -1,209 +1,209 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 DISKUSAGE_H #define DISKUSAGE_H // QtCore #include #include #include #include #include // QtGui #include #include #include // QtWidgets #include #include #include #include #include #include "filelightParts/fileTree.h" #define VIEW_LINES 0 #define VIEW_DETAILED 1 #define VIEW_FILELIGHT 2 #define VIEW_LOADER 3 typedef QHash Properties; class DUListView; class DULines; class DUFilelight; class QMenu; class LoaderWidget; class FileItem; class FileSystem; class DiskUsage : public QStackedWidget { Q_OBJECT public: explicit DiskUsage(QString confGroup, QWidget *parent = 0); ~DiskUsage(); void load(const QUrl &dirName); void close(); void stopLoad(); bool isLoading() { return loading; } void setView(int view); int getActiveView() { return activeView; } Directory* getDirectory(QString path); - File * getFile(QString path); + File * getFile(const QString& path); QString getConfigGroup() { return configGroup; } - void * getProperty(File *, QString); - void addProperty(File *, QString, void *); - void removeProperty(File *, QString); + void * getProperty(File *, const QString&); + void addProperty(File *, const QString&, void *); + void removeProperty(File *, const QString&); int exclude(File *file, bool calcPercents = true, int depth = 0); void includeAll(); int del(File *file, bool calcPercents = true, int depth = 0); QString getToolTip(File *); - void rightClickMenu(const QPoint &, File *, QMenu * = 0, QString = QString()); + void rightClickMenu(const QPoint &, File *, QMenu * = 0, const QString& = QString()); void changeDirectory(Directory *dir); Directory* getCurrentDir(); File* getCurrentFile(); - QPixmap getIcon(QString mime); + QPixmap getIcon(const QString& mime); QUrl getBaseURL() { return baseURL; } public slots: void dirUp(); void clear(); signals: void enteringDirectory(Directory *); void clearing(); void changed(File *); void changeFinished(); void deleted(File *); void deleteFinished(); void status(QString); void viewChanged(int); void loadFinished(bool); void newSearch(); protected slots: void slotLoadDirectory(); protected: QHash< QString, Directory * > contentMap; QHash< File *, Properties *> propertyMap; Directory* currentDirectory; KIO::filesize_t currentSize; virtual void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; virtual bool event(QEvent *) Q_DECL_OVERRIDE; int calculateSizes(Directory *dir = 0, bool emitSig = false, int depth = 0); int calculatePercents(bool emitSig = false, Directory *dir = 0 , int depth = 0); int include(Directory *dir, int depth = 0); void createStatus(); void executeAction(int, File * = 0); QUrl baseURL; //< the base URL of loading DUListView *listView; DULines *lineView; DUFilelight *filelightView; LoaderWidget *loaderView; Directory *root; int activeView; QString configGroup; bool first; bool loading; bool abortLoading; bool clearAfterAbort; bool deleting; QStack directoryStack; QStack parentStack; FileSystem *searchFileSystem; FileItem *currentFileItem; QList fileItems; Directory *currentParent; QString dirToCheck; int fileNum; int dirNum; int viewBeforeLoad; QTimer loadingTimer; }; class LoaderWidget : public QScrollArea { Q_OBJECT public: explicit LoaderWidget(QWidget *parent = 0); void init(); void setCurrentURL(const QUrl &url); void setValues(int fileNum, int dirNum, KIO::filesize_t total); bool wasCancelled() { return cancelled; } public slots: void slotCancelled(); protected: QLabel *totalSize; QLabel *files; QLabel *directories; KSqueezedTextLabel *searchedDirectory; QWidget *widget; bool cancelled; }; #endif /* __DISK_USAGE_GUI_H__ */ diff --git a/krusader/DiskUsage/diskusagegui.cpp b/krusader/DiskUsage/diskusagegui.cpp index d01ad34b..9a38de44 100644 --- a/krusader/DiskUsage/diskusagegui.cpp +++ b/krusader/DiskUsage/diskusagegui.cpp @@ -1,242 +1,242 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "diskusagegui.h" // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../FileSystem/filesystem.h" #include "../Dialogs/krdialogs.h" DiskUsageGUI::DiskUsageGUI(const QUrl &openDir) : QDialog(nullptr), exitAtFailure(true) { setWindowTitle(i18n("Krusader::Disk Usage")); setAttribute(Qt::WA_DeleteOnClose); baseDirectory = openDir; auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *duGrid = new QGridLayout(); duGrid->setSpacing(6); duGrid->setContentsMargins(11, 11, 11, 11); QWidget *duTools = new QWidget(this); auto *duHBox = new QHBoxLayout(duTools); duHBox->setContentsMargins(0, 0, 0, 0); duTools->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); btnNewSearch = new QToolButton(duTools); btnNewSearch->setIcon(Icon("document-open")); duHBox->addWidget(btnNewSearch); btnNewSearch->setToolTip(i18n("Start new disk usage search")); btnRefresh = new QToolButton(duTools); btnRefresh->setIcon(Icon("view-refresh")); duHBox->addWidget(btnRefresh); btnRefresh->setToolTip(i18n("Refresh")); btnDirUp = new QToolButton(duTools); btnDirUp->setIcon(Icon("go-up")); duHBox->addWidget(btnDirUp); btnDirUp->setToolTip(i18n("Parent folder")); QWidget *separatorWidget = new QWidget(duTools); separatorWidget->setMinimumWidth(10); duHBox->addWidget(separatorWidget); btnLines = new QToolButton(duTools); btnLines->setIcon(Icon("view-list-details")); btnLines->setCheckable(true); duHBox->addWidget(btnLines); btnLines->setToolTip(i18n("Line view")); btnDetailed = new QToolButton(duTools); btnDetailed->setIcon(Icon("view-list-tree")); btnDetailed->setCheckable(true); duHBox->addWidget(btnDetailed); btnDetailed->setToolTip(i18n("Detailed view")); btnFilelight = new QToolButton(duTools); btnFilelight->setIcon(Icon("kr_diskusage")); btnFilelight->setCheckable(true); duHBox->addWidget(btnFilelight); btnFilelight->setToolTip(i18n("Filelight view")); QWidget *spacerWidget = new QWidget(duTools); duHBox->addWidget(spacerWidget); auto *hboxlayout = new QHBoxLayout(spacerWidget); auto* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed); hboxlayout->addItem(spacer); duGrid->addWidget(duTools, 0, 0); diskUsage = new DiskUsage("DiskUsage", this); duGrid->addWidget(diskUsage, 1, 0); status = new KSqueezedTextLabel(this); duGrid->addWidget(status, 2, 0); mainLayout->addLayout(duGrid); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::rejected, this, &DiskUsageGUI::close); connect(diskUsage, &DiskUsage::status, this, &DiskUsageGUI::slotStatus); connect(diskUsage, &DiskUsage::viewChanged, this, &DiskUsageGUI::slotViewChanged); connect(diskUsage, &DiskUsage::newSearch, this, &DiskUsageGUI::askDir); connect(diskUsage, &DiskUsage::loadFinished, this, &DiskUsageGUI::slotLoadFinished); connect(btnNewSearch, &QToolButton::clicked, this, &DiskUsageGUI::askDir); connect(btnRefresh, &QToolButton::clicked, this, &DiskUsageGUI::slotLoadUsageInfo); connect(btnDirUp, &QToolButton::clicked, diskUsage, &DiskUsage::dirUp); connect(btnLines, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectLinesView); connect(btnDetailed, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectListView); connect(btnFilelight, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectFilelightView); KConfigGroup group(krConfig, "DiskUsage"); int view = group.readEntry("View", VIEW_LINES); if (view < VIEW_LINES || view > VIEW_FILELIGHT) view = VIEW_LINES; diskUsage->setView(view); sizeX = group.readEntry("Window Width", QFontMetrics(font()).width("W") * 70); sizeY = group.readEntry("Window Height", QFontMetrics(font()).height() * 25); resize(sizeX, sizeY); if (group.readEntry("Window Maximized", false)) { setWindowState(windowState() | Qt::WindowMaximized); } } void DiskUsageGUI::askDirAndShow() { if (askDir()) { show(); } } void DiskUsageGUI::slotLoadFinished(bool result) { if (exitAtFailure && !result) { close(); } else { exitAtFailure = false; } } void DiskUsageGUI::enableButtons(bool isOn) { btnNewSearch->setEnabled(isOn); btnRefresh->setEnabled(isOn); btnDirUp->setEnabled(isOn); btnLines->setEnabled(isOn); btnDetailed->setEnabled(isOn); btnFilelight->setEnabled(isOn); } void DiskUsageGUI::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } QDialog::resizeEvent(e); } void DiskUsageGUI::closeEvent(QCloseEvent *event) { KConfigGroup group(krConfig, "DiskUsage"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); group.writeEntry("View", diskUsage->getActiveView()); event->accept(); } void DiskUsageGUI::slotLoadUsageInfo() { diskUsage->load(baseDirectory); } -void DiskUsageGUI::slotStatus(QString stat) +void DiskUsageGUI::slotStatus(const QString& stat) { status->setText(stat); } void DiskUsageGUI::slotViewChanged(int view) { if (view == VIEW_LOADER) { enableButtons(false); return; } enableButtons(true); btnLines->setChecked(false); btnDetailed->setChecked(false); btnFilelight->setChecked(false); switch (view) { case VIEW_LINES: btnLines->setChecked(true); break; case VIEW_DETAILED: btnDetailed->setChecked(true); break; case VIEW_FILELIGHT: btnFilelight->setChecked(true); break; case VIEW_LOADER: break; } } bool DiskUsageGUI::askDir() { // ask the user for the copy destX const QUrl newDir = KChooseDir::getDir(i18n("Viewing the usage of folder:"), baseDirectory, baseDirectory); if (newDir.isEmpty()) return false; baseDirectory = newDir; QTimer::singleShot(0, this, &DiskUsageGUI::slotLoadUsageInfo); return true; } diff --git a/krusader/DiskUsage/diskusagegui.h b/krusader/DiskUsage/diskusagegui.h index b5aa2d05..8313bc94 100644 --- a/krusader/DiskUsage/diskusagegui.h +++ b/krusader/DiskUsage/diskusagegui.h @@ -1,87 +1,87 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 DISKUSAGEGUI_H #define DISKUSAGEGUI_H // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include "diskusage.h" class DiskUsageGUI : public QDialog { Q_OBJECT public: explicit DiskUsageGUI(const QUrl &openDir); ~DiskUsageGUI() override = default; void askDirAndShow(); protected slots: void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; protected: void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; private slots: bool askDir(); void slotLoadUsageInfo(); - void slotStatus(QString); + void slotStatus(const QString&); void slotSelectLinesView() { diskUsage->setView(VIEW_LINES); } void slotSelectListView() { diskUsage->setView(VIEW_DETAILED); } void slotSelectFilelightView() { diskUsage->setView(VIEW_FILELIGHT); } void slotViewChanged(int view); void slotLoadFinished(bool); private: void enableButtons(bool); DiskUsage *diskUsage; QUrl baseDirectory; KSqueezedTextLabel *status; QToolButton *btnNewSearch; QToolButton *btnRefresh; QToolButton *btnDirUp; QToolButton *btnLines; QToolButton *btnDetailed; QToolButton *btnFilelight; int sizeX; int sizeY; bool exitAtFailure; }; #endif /* __DISK_USAGE_GUI_H__ */ diff --git a/krusader/DiskUsage/dulines.cpp b/krusader/DiskUsage/dulines.cpp index 52831ad7..9440472c 100644 --- a/krusader/DiskUsage/dulines.cpp +++ b/krusader/DiskUsage/dulines.cpp @@ -1,534 +1,534 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "dulines.h" #include "../icon.h" #include "../krglobal.h" #include "../FileSystem/krpermhandler.h" // QtCore #include // QtGui #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include class DULinesItemDelegate : public QItemDelegate { public: explicit DULinesItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QItemDelegate::paint(painter, option, index); QVariant value = index.data(Qt::UserRole); if (value.isValid()) { QString text = value.toString(); value = index.data(Qt::DisplayRole); QString display; if (value.isValid()) display = value.toString(); QSize iconSize; value = index.data(Qt::DecorationRole); if (value.isValid()) iconSize = qvariant_cast(value).actualSize(option.decorationSize); painter->save(); painter->setClipRect(option.rect); QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) cg = QPalette::Inactive; if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } QFont fnt = option.font; fnt.setItalic(true); painter->setFont(fnt); QFontMetrics fm(fnt); QString renderedText = text; int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin); int pos = 3 * textMargin + option.fontMetrics.width(display) + iconSize.width(); bool truncd = false; QRect rct = option.rect; if (rct.width() > pos) { rct.setX(rct.x() + pos); if (fm.width(renderedText) > rct.width()) { truncd = true; int points = fm.width("..."); while (!renderedText.isEmpty() && (fm.width(renderedText) + points > rct.width())) renderedText.truncate(renderedText.length() - 1); renderedText += "..."; } painter->drawText(rct, Qt::AlignLeft, renderedText); } else truncd = true; if (truncd) ((QAbstractItemModel *)index.model())->setData(index, QVariant(display + " " + text), Qt::ToolTipRole); else ((QAbstractItemModel *)index.model())->setData(index, QVariant(), Qt::ToolTipRole); painter->restore(); } } }; class DULinesItem : public QTreeWidgetItem { public: - DULinesItem(DiskUsage *diskUsageIn, File *fileItem, QTreeWidget * parent, QString label1, - QString label2, QString label3) : QTreeWidgetItem(parent), + DULinesItem(DiskUsage *diskUsageIn, File *fileItem, QTreeWidget * parent, const QString& label1, + const QString& label2, const QString& label3) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileItem) { setText(0, label1); setText(1, label2); setText(2, label3); setTextAlignment(1, Qt::AlignRight); } DULinesItem(DiskUsage *diskUsageIn, File *fileItem, QTreeWidget * parent, QTreeWidgetItem * after, - QString label1, QString label2, QString label3) : QTreeWidgetItem(parent, after), + const QString& label1, const QString& label2, const QString& label3) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileItem) { setText(0, label1); setText(1, label2); setText(2, label3); setTextAlignment(1, Qt::AlignRight); } bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE { int column = treeWidget() ? treeWidget()->sortColumn() : 0; if (text(0) == "..") return true; const auto *compWith = dynamic_cast< const DULinesItem * >(&other); if (compWith == nullptr) return false; switch (column) { case 0: case 1: return file->size() > compWith->file->size(); default: return text(column) < other.text(column); } } inline File * getFile() { return file; } private: DiskUsage *diskUsage; File *file; }; DULines::DULines(DiskUsage *usage) : KrTreeWidget(usage), diskUsage(usage), refreshNeeded(false), started(false) { setItemDelegate(itemDelegate = new DULinesItemDelegate()); setAllColumnsShowFocus(true); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setIndentation(10); int defaultSize = QFontMetrics(font()).width("W"); QStringList labels; labels << i18n("Line View"); labels << i18n("Percent"); labels << i18n("Name"); setHeaderLabels(labels); header()->setSectionResizeMode(QHeaderView::Interactive); KConfigGroup group(krConfig, diskUsage->getConfigGroup()); showFileSize = group.readEntry("L Show File Size", true); if (group.hasKey("L State")) header()->restoreState(group.readEntry("L State", QByteArray())); else { setColumnWidth(0, defaultSize * 20); setColumnWidth(1, defaultSize * 6); setColumnWidth(2, defaultSize * 20); } setStretchingColumn(0); header()->setSortIndicatorShown(true); sortItems(1, Qt::AscendingOrder); // toolTip = new DULinesToolTip( diskUsage, viewport(), this ); connect(diskUsage, &DiskUsage::enteringDirectory, this, &DULines::slotDirChanged); connect(diskUsage, &DiskUsage::clearing, this, &DULines::clear); connect(header(), &QHeaderView::sectionResized, this, &DULines::sectionResized); connect(this, &DULines::itemRightClicked, this, &DULines::slotRightClicked); connect(diskUsage, &DiskUsage::changed, this, &DULines::slotChanged); connect(diskUsage, &DiskUsage::deleted, this, &DULines::slotDeleted); started = true; } DULines::~DULines() { KConfigGroup group(krConfig, diskUsage->getConfigGroup()); group.writeEntry("L State", header()->saveState()); delete itemDelegate; } bool DULines::event(QEvent * event) { switch (event->type()) { case QEvent::ToolTip: { auto *he = static_cast(event); if (viewport()) { QPoint pos = viewport()->mapFromGlobal(he->globalPos()); QTreeWidgetItem * item = itemAt(pos); int column = columnAt(pos.x()); if (item && column == 1) { File *fileItem = ((DULinesItem *)item)->getFile(); QToolTip::showText(he->globalPos(), diskUsage->getToolTip(fileItem), this); return true; } } } break; default: break; } return KrTreeWidget::event(event); } void DULines::slotDirChanged(Directory *dirEntry) { clear(); QTreeWidgetItem * lastItem = nullptr; if (!(dirEntry->parent() == nullptr)) { lastItem = new QTreeWidgetItem(this); lastItem->setText(0, ".."); lastItem->setIcon(0, Icon("go-up")); lastItem->setFlags(lastItem->flags() & (~Qt::ItemIsSelectable)); } int maxPercent = -1; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; if (!item->isExcluded() && item->intPercent() > maxPercent) maxPercent = item->intPercent(); } for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; QString fileName = item->name(); if (lastItem == nullptr) lastItem = new DULinesItem(diskUsage, item, this, "", item->percent() + " ", fileName); else lastItem = new DULinesItem(diskUsage, item, this, lastItem, "", item->percent() + " ", fileName); if (item->isExcluded()) lastItem->setHidden(true); int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; lastItem->setIcon(2, diskUsage->getIcon(item->mime())); lastItem->setData(0, Qt::DecorationRole, createPixmap(item->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); if (showFileSize) lastItem->setData(2, Qt::UserRole, " [" + KIO::convertSize(item->size()) + ']'); QSize size = lastItem->sizeHint(0); size.setWidth(16); lastItem->setSizeHint(0, size); } if (topLevelItemCount() > 0) { setCurrentItem(topLevelItem(0)); } } QPixmap DULines::createPixmap(int percent, int maxPercent, int maxWidth) { if (percent < 0 || percent > maxPercent || maxWidth < 2 || maxPercent == 0) return QPixmap(); maxWidth -= 2; int actualWidth = maxWidth * percent / maxPercent; if (actualWidth == 0) return QPixmap(); QPen pen; pen.setColor(Qt::black); QPainter painter; int size = QFontMetrics(font()).height() - 2; QRect rect(0, 0, actualWidth, size); QRect frameRect(0, 0, actualWidth - 1, size - 1); QPixmap pixmap(rect.width(), rect.height()); painter.begin(&pixmap); painter.setPen(pen); for (int i = 1; i < actualWidth - 1; i++) { int color = (511 * i / (maxWidth - 1)); if (color < 256) pen.setColor(QColor(255 - color, 255, 0)); else pen.setColor(QColor(color - 256, 511 - color, 0)); painter.setPen(pen); painter.drawLine(i, 1, i, size - 1); } pen.setColor(Qt::black); painter.setPen(pen); if (actualWidth != 1) painter.drawRect(frameRect); else painter.drawLine(0, 0, 0, size); painter.end(); pixmap.detach(); return pixmap; } void DULines::resizeEvent(QResizeEvent * re) { KrTreeWidget::resizeEvent(re); if (started && (re->oldSize() != re->size())) sectionResized(0); } void DULines::sectionResized(int column) { if (topLevelItemCount() == 0 || column != 0) return; Directory * currentDir = diskUsage->getCurrentDir(); if (currentDir == nullptr) return; int maxPercent = -1; for (Iterator it = currentDir->iterator(); it != currentDir->end(); ++it) { File *item = *it; if (!item->isExcluded() && item->intPercent() > maxPercent) maxPercent = item->intPercent(); } QTreeWidgetItemIterator it2(this); while (*it2) { QTreeWidgetItem *lvitem = *it2; if (lvitem->text(0) != "..") { auto *duItem = dynamic_cast< DULinesItem *>(lvitem); if (duItem) { int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; duItem->setData(0, Qt::DecorationRole, createPixmap(duItem->getFile()->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); QSize size = duItem->sizeHint(0); size.setWidth(16); duItem->setSizeHint(0, size); } } it2++; } } bool DULines::doubleClicked(QTreeWidgetItem * item) { if (item) { if (item->text(0) != "..") { File *fileItem = ((DULinesItem *)item)->getFile(); if (fileItem->isDir()) diskUsage->changeDirectory(dynamic_cast(fileItem)); return true; } else { auto *upDir = (Directory *)diskUsage->getCurrentDir()->parent(); if (upDir) diskUsage->changeDirectory(upDir); return true; } } return false; } void DULines::mouseDoubleClickEvent(QMouseEvent * e) { if (e || e->button() == Qt::LeftButton) { QPoint vp = viewport()->mapFromGlobal(e->globalPos()); QTreeWidgetItem * item = itemAt(vp); if (doubleClicked(item)) return; } KrTreeWidget::mouseDoubleClickEvent(e); } void DULines::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Return : case Qt::Key_Enter : if (doubleClicked(currentItem())) return; break; case Qt::Key_Left : case Qt::Key_Right : case Qt::Key_Up : case Qt::Key_Down : if (e->modifiers() == Qt::ShiftModifier) { e->ignore(); return; } break; case Qt::Key_Delete : e->ignore(); return; } KrTreeWidget::keyPressEvent(e); } void DULines::slotRightClicked(QTreeWidgetItem *item, const QPoint &pos) { File * file = nullptr; if (item && item->text(0) != "..") file = ((DULinesItem *)item)->getFile(); QMenu linesPopup; QAction *act = linesPopup.addAction(i18n("Show file sizes"), this, SLOT(slotShowFileSizes())); act->setChecked(showFileSize); diskUsage->rightClickMenu(pos, file, &linesPopup, i18n("Lines")); } void DULines::slotShowFileSizes() { showFileSize = !showFileSize; slotDirChanged(diskUsage->getCurrentDir()); } File * DULines::getCurrentFile() { QTreeWidgetItem *item = currentItem(); if (item == nullptr || item->text(0) == "..") return nullptr; return ((DULinesItem *)item)->getFile(); } void DULines::slotChanged(File * item) { QTreeWidgetItemIterator it(this); while (*it) { QTreeWidgetItem *lvitem = *it; it++; if (lvitem->text(0) != "..") { auto *duItem = (DULinesItem *)(lvitem); if (duItem->getFile() == item) { setSortingEnabled(false); duItem->setHidden(item->isExcluded()); duItem->setText(1, item->percent()); if (!refreshNeeded) { refreshNeeded = true; QTimer::singleShot(0, this, &DULines::slotRefresh); } break; } } } } void DULines::slotDeleted(File * item) { QTreeWidgetItemIterator it(this); while (*it) { QTreeWidgetItem *lvitem = *it; it++; if (lvitem->text(0) != "..") { auto *duItem = (DULinesItem *)(lvitem); if (duItem->getFile() == item) { delete duItem; break; } } } } void DULines::slotRefresh() { if (refreshNeeded) { refreshNeeded = false; setSortingEnabled(true); sortItems(1, Qt::AscendingOrder); } } diff --git a/krusader/DiskUsage/dulistview.h b/krusader/DiskUsage/dulistview.h index 2ad97af0..60c6cd2f 100644 --- a/krusader/DiskUsage/dulistview.h +++ b/krusader/DiskUsage/dulistview.h @@ -1,184 +1,184 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 DULISTVIEW_H #define DULISTVIEW_H // QtGui #include #include #include "../GUI/krtreewidget.h" #include "diskusage.h" class DUListViewItem : public QTreeWidgetItem { public: - DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidget * parent, QString label1, - QString label2, QString label3, QString label4, QString label5, QString label6, - QString label7, QString label8, QString label9) + DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidget * parent, const QString& label1, + const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, + const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } - DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidgetItem * parent, QString label1, - QString label2, QString label3, QString label4, QString label5, QString label6, - QString label7, QString label8, QString label9) + DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidgetItem * parent, const QString& label1, + const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, + const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidget * parent, QTreeWidgetItem * after, - QString label1, QString label2, QString label3, QString label4, QString label5, - QString label6, QString label7, QString label8, QString label9) + const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, + const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidgetItem * parent, QTreeWidgetItem * after, - QString label1, QString label2, QString label3, QString label4, QString label5, - QString label6, QString label7, QString label8, QString label9) + const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, + const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } ~DUListViewItem() override { diskUsage->removeProperty(file, "ListView-Ref"); } bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE { int column = treeWidget() ? treeWidget()->sortColumn() : 0; if (text(0) == "..") return true; const auto *compWith = dynamic_cast< const DUListViewItem * >(&other); if (compWith == nullptr) return false; switch (column) { case 1: case 2: return file->size() > compWith->file->size(); case 3: return file->ownSize() > compWith->file->ownSize(); case 5: return file->time() < compWith->file->time(); default: return text(column) < other.text(column); } } inline File * getFile() { return file; } private: DiskUsage *diskUsage; File *file; }; class DUListView : public KrTreeWidget { Q_OBJECT public: explicit DUListView(DiskUsage *usage); ~DUListView() override; File * getCurrentFile(); public slots: void slotDirChanged(Directory *); void slotChanged(File *); void slotDeleted(File *); void slotRightClicked(QTreeWidgetItem *, const QPoint &); void slotExpanded(QTreeWidgetItem *); protected: DiskUsage *diskUsage; void mouseDoubleClickEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; private: void addDirectory(Directory *dirEntry, QTreeWidgetItem *parent); bool doubleClicked(QTreeWidgetItem * item); }; #endif /* __DU_LISTVIEW_H__ */ diff --git a/krusader/FileSystem/filesystem.cpp b/krusader/FileSystem/filesystem.cpp index 2554f88a..eb63a265 100644 --- a/krusader/FileSystem/filesystem.cpp +++ b/krusader/FileSystem/filesystem.cpp @@ -1,334 +1,334 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "filesystem.h" #include // QtCore #include #include #include // QtWidgets #include #include #include #include #include "fileitem.h" #include "krpermhandler.h" #include "../defaults.h" #include "../krglobal.h" #include "../JobMan/jobman.h" #include "../JobMan/krjob.h" FileSystem::FileSystem() : DirListerInterface(nullptr), _isRefreshing(false) {} FileSystem::~FileSystem() { clear(_fileItems); // please don't remove this line. This informs the view about deleting the file items. emit cleared(); } QList FileSystem::getUrls(const QStringList &names) const { QList urls; for (const QString name : names) { urls.append(getUrl(name)); } return urls; } FileItem *FileSystem::getFileItem(const QString &name) const { return _fileItems.contains(name) ? _fileItems.value(name) : 0; } KIO::filesize_t FileSystem::totalSize() const { KIO::filesize_t temp = 0; for (FileItem *item : _fileItems.values()) { if (!item->isDir() && item->getName() != "." && item->getName() != "..") { temp += item->getSize(); } } return temp; } QUrl FileSystem::ensureTrailingSlash(const QUrl &url) { if (url.path().endsWith('/')) { return url; } QUrl adjustedUrl(url); adjustedUrl.setPath(adjustedUrl.path() + '/'); return adjustedUrl; } QUrl FileSystem::preferLocalUrl(const QUrl &url){ if (url.isEmpty() || !url.scheme().isEmpty()) return url; QUrl adjustedUrl = url; adjustedUrl.setScheme("file"); return adjustedUrl; } bool FileSystem::scanOrRefresh(const QUrl &directory, bool onlyScan) { qDebug() << "from current dir=" << _currentDirectory.toDisplayString() << "; to=" << directory.toDisplayString(); if (_isRefreshing) { // NOTE: this does not happen (unless async)"; return false; } // workaround for krarc: find out if transition to local fs is wanted and adjust URL manually QUrl url = directory; if (_currentDirectory.scheme() == "krarc" && url.scheme() == "krarc" && QDir(url.path()).exists()) { url.setScheme("file"); } const bool dirChange = !url.isEmpty() && cleanUrl(url) != _currentDirectory; const QUrl toRefresh = dirChange ? url.adjusted(QUrl::NormalizePathSegments) : _currentDirectory; if (!toRefresh.isValid()) { emit error(i18n("Malformed URL:\n%1", toRefresh.toDisplayString())); return false; } _isRefreshing = true; FileItemDict tempFileItems(_fileItems); // old file items are still used during refresh _fileItems.clear(); if (dirChange) // show an empty directory while loading the new one and clear selection emit cleared(); const bool refreshed = refreshInternal(toRefresh, onlyScan); _isRefreshing = false; if (!refreshed) { // cleanup and abort if (!dirChange) emit cleared(); clear(tempFileItems); return false; } emit scanDone(dirChange); clear(tempFileItems); updateFilesystemInfo(); return true; } void FileSystem::deleteFiles(const QList &urls, bool moveToTrash) { KrJob *krJob = KrJob::createDeleteJob(urls, moveToTrash); connect(krJob, &KrJob::started, this, [=](KIO::Job *job) { connectJobToSources(job, urls); }); if (moveToTrash) { // update destination: the trash bin (in case a panel/tab is showing it) connect(krJob, &KrJob::started, this, [=](KIO::Job *job) { // Note: the "trash" protocol should always have only one "/" after the "scheme:" part connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(QUrl("trash:/"), false); }); }); } krJobMan->manageJob(krJob); } -void FileSystem::connectJobToSources(KJob *job, const QList urls) +void FileSystem::connectJobToSources(KJob *job, const QList& urls) { if (!urls.isEmpty()) { // TODO we assume that all files were in the same directory and only emit one signal for // the directory of the first file URL (all subdirectories of parent are notified) const QUrl url = urls.first().adjusted(QUrl::RemoveFilename); connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(url, true); }); } } void FileSystem::connectJobToDestination(KJob *job, const QUrl &destination) { connect(job, &KIO::Job::result, this, [=]() { emit fileSystemChanged(destination, false); }); // (additional) direct refresh if on local fs because watcher is too slow const bool refresh = cleanUrl(destination) == _currentDirectory && isLocal(); connect(job, &KIO::Job::result, this, [=](KJob* job) { slotJobResult(job, refresh); }); } bool FileSystem::showHiddenFiles() { const KConfigGroup gl(krConfig, "Look&Feel"); return gl.readEntry("Show Hidden", _ShowHidden); } void FileSystem::addFileItem(FileItem *item) { _fileItems.insert(item->getName(), item); } FileItem *FileSystem::createLocalFileItem(const QString &name, const QString &directory, bool virt) { const QDir dir = QDir(directory); const QString path = dir.filePath(name); const QByteArray pathByteArray = path.toLocal8Bit(); const QString fileItemName = virt ? path : name; const QUrl fileItemUrl = QUrl::fromLocalFile(path); // read file status; in case of error create a "broken" file item QT_STATBUF stat_p; memset(&stat_p, 0, sizeof(stat_p)); if (QT_LSTAT(pathByteArray.data(), &stat_p) < 0) return FileItem::createBroken(fileItemName, fileItemUrl); const KIO::filesize_t size = stat_p.st_size; bool isDir = S_ISDIR(stat_p.st_mode); const bool isLink = S_ISLNK(stat_p.st_mode); // for links, read link destination and determine whether it's broken or not QString linkDestination; bool brokenLink = false; if (isLink) { linkDestination = readLinkSafely(pathByteArray.data()); if (linkDestination.isNull()) { brokenLink = true; } else { const QFileInfo linkFile(dir, linkDestination); if (!linkFile.exists()) brokenLink = true; else if (linkFile.isDir()) isDir = true; } } // create normal file item return new FileItem(fileItemName, fileItemUrl, isDir, size, stat_p.st_mode, stat_p.st_mtime, stat_p.st_ctime, stat_p.st_atime, stat_p.st_uid, stat_p.st_gid, QString(), QString(), isLink, linkDestination, brokenLink); } QString FileSystem::readLinkSafely(const char *path) { // inspired by the areadlink_with_size function from gnulib, which is used for coreutils // idea: start with a small buffer and gradually increase it as we discover it wasn't enough QT_OFF_T bufferSize = 1024; // start with 1 KiB QT_OFF_T maxBufferSize = std::numeric_limits::max(); while (true) { // try to read the link std::unique_ptr buffer(new char[bufferSize]); auto nBytesRead = readlink(path, buffer.get(), bufferSize); // should never happen, asserted by the readlink if (nBytesRead > bufferSize) { return QString(); } // read failure if (nBytesRead < 0) { qDebug() << "Failed to read the link " << path; return QString(); } // read success if (nBytesRead < bufferSize || nBytesRead == maxBufferSize) { return QString::fromLocal8Bit(buffer.get(), nBytesRead); } // increase the buffer and retry again // bufferSize < maxBufferSize is implied from previous checks if (bufferSize <= maxBufferSize / 2) { bufferSize *= 2; } else { bufferSize = maxBufferSize; } } } FileItem *FileSystem::createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt) { const KFileItem kfi(entry, directory, true, true); const QString name = kfi.text(); // ignore un-needed entries if (name.isEmpty() || name == "." || name == "..") { return nullptr; } const QString localPath = kfi.localPath(); const QUrl url = !localPath.isEmpty() ? QUrl::fromLocalFile(localPath) : kfi.url(); const QString fname = virt ? url.toDisplayString() : name; // get file statistics... const time_t mtime = kfi.time(KFileItem::ModificationTime).toTime_t(); const time_t ctime = kfi.time(KFileItem::CreationTime).toTime_t(); // "Creation"? its "Changed" const time_t atime = kfi.time(KFileItem::AccessTime).toTime_t(); const mode_t mode = kfi.mode() | kfi.permissions(); // NOTE: we could get the mimetype (and file icon) from the kfileitem here but this is very // slow. Instead, the file item class has it's own (faster) way to determine the file type. // NOTE: "broken link" flag is always false, checking link destination existence is // considered to be too expensive return new FileItem(fname, url, kfi.isDir(), kfi.size(), mode, mtime, ctime, atime, (uid_t) -1, (gid_t) -1, kfi.user(), kfi.group(), kfi.isLink(), kfi.linkDest(), false, kfi.ACL().asString(), kfi.defaultACL().asString()); } void FileSystem::slotJobResult(KJob *job, bool refresh) { if (job->error() && job->uiDelegate()) { // show errors for modifying operations as popup (works always) job->uiDelegate()->showErrorMessage(); } if (refresh) { FileSystem::refresh(); } } void FileSystem::clear(FileItemDict &fileItems) { QHashIterator lit(fileItems); while (lit.hasNext()) { delete lit.next().value(); } fileItems.clear(); } diff --git a/krusader/FileSystem/filesystem.h b/krusader/FileSystem/filesystem.h index 46976611..7fce0ee4 100644 --- a/krusader/FileSystem/filesystem.h +++ b/krusader/FileSystem/filesystem.h @@ -1,221 +1,221 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 FILESYSTEM_H #define FILESYSTEM_H #include "dirlisterinterface.h" // QtCore #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include "../JobMan/jobman.h" class FileItem; /** * An abstract filesystem. Use the implementations of this class for all file operations. * * It represents a directory and gives access to its files. All common file operations * are supported. Methods with absolute URL as argument can be used independently from the current * directory. Otherwise - if the methods argument is a file name - the operation is performed inside * the current directory. * * Notification signals are emitted if the directory content may have been changed. */ class FileSystem : public DirListerInterface { Q_OBJECT public: enum FS_TYPE { /// Virtual filesystem. Krusaders custom virt:/ protocol FS_VIRTUAL, /// Filesystem supporting all KIO protocols (file:/, ftp:/, smb:/, etc.) FS_DEFAULT }; FileSystem(); ~FileSystem() override; // DirListerInterface implementation inline QList fileItems() const Q_DECL_OVERRIDE { return _fileItems.values(); } inline unsigned long numFileItems() const Q_DECL_OVERRIDE { return _fileItems.count(); } inline bool isRoot() const Q_DECL_OVERRIDE { const QString path = _currentDirectory.path(); return path.isEmpty() || path == "/"; } /// Copy (copy, move or link) files in this filesystem. /// Destination is absolute URL. May implemented async. virtual void copyFiles(const QList &urls, const QUrl &destination, KIO::CopyJob::CopyMode mode = KIO::CopyJob::Copy, bool showProgressInfo = true, JobMan::StartMode startMode = JobMan::Default) = 0; /// Handle file dropping in this filesystem. Destination is absolute URL. May implemented async. virtual void dropFiles(const QUrl &destination, QDropEvent *event) = 0; /// Copy (copy, move or link) files to the current filesystem directory or to "dir", the /// directory name relative to the current dir. May implemented async. virtual void addFiles(const QList &fileUrls, KIO::CopyJob::CopyMode mode, const QString &dir = "") = 0; /// Create a new directory in the current directory. May implemented async. virtual void mkDir(const QString &name) = 0; /// Rename file/directory in the current directory. May implemented async. virtual void rename(const QString &fileName, const QString &newName) = 0; /// Return an absolute URL for a single file/directory name in the current directory - with no /// trailing slash. virtual QUrl getUrl(const QString &name) const = 0; /// Return a list of URLs for multiple files/directories in the current directory. QList getUrls(const QStringList &names) const; /// Return true if all files can be moved to trash, else false. virtual bool canMoveToTrash(const QStringList &fileNames) const = 0; /// Return the filesystem mount point of the current directory. Empty string by default. virtual QString mountPoint() const { return QString(); } /// Returns true if this filesystem implementation does not need to be notified about changes in the /// current directory. Else false. virtual bool hasAutoUpdate() const { return false; } /// Notify this filesystem that the filesystem info of the current directory may have changed. virtual void updateFilesystemInfo() {} /** * Scan all files and directories in a directory and create the file items for them. Blocking. * * @param directory if given, the lister tries to change to this directory, else the old * directory is refreshed * @return true if scan was successful, else (not implemented, scan failed or refresh job * was killed) false. */ bool scanDir(const QUrl &directory = QUrl()) { return scanOrRefresh(directory, false); } /// Change or refresh the current directory and scan it. Blocking. /// Returns true if directory was scanned. Returns false if failed or scan job was killed. bool refresh(const QUrl &directory = QUrl()) { return scanOrRefresh(directory, false); } /// Returns the current directory path of this filesystem. inline QUrl currentDirectory() const { return _currentDirectory; } /// Return the file item for a file name in the current directory. Or 0 if not found. FileItem *getFileItem(const QString &name) const; /// The total size of all files in the current directory (only valid after scan). // TODO unused KIO::filesize_t totalSize() const; /// Return the filesystem type. inline FS_TYPE type() const { return _type; } /// Return true if the current directory is local (without recognizing mount points). inline bool isLocal() const { return _currentDirectory.isLocalFile(); } /// Return true if the current directory is a remote (network) location. inline bool isRemote() const { const QString sc = _currentDirectory.scheme(); return (sc == "fish" || sc == "ftp" || sc == "sftp" || sc == "nfs" || sc == "smb" || sc == "webdav"); } /// Returns true if this filesystem is currently refreshing the current directory. inline bool isRefreshing() const { return _isRefreshing; } /// Delete or trash arbitrary files. Implemented async. Universal refresh not fully implemented. void deleteFiles(const QList &urls, bool moveToTrash); /// Return the input URL with a trailing slash if absent. static QUrl ensureTrailingSlash(const QUrl &url); /// Return the input URL without trailing slash. static QUrl cleanUrl(const QUrl &url) { return url.adjusted(QUrl::StripTrailingSlash); } /// Add 'file' scheme to non-empty URL without scheme static QUrl preferLocalUrl(const QUrl &url); /// Return a file item for a local file inside a directory static FileItem *createLocalFileItem(const QString &name, const QString &directory, bool virt = false); /// Return a file item for a KIO result. Returns 0 if entry is not needed static FileItem *createFileItemFromKIO(const KIO::UDSEntry &entry, const QUrl &directory, bool virt = false); /// Read a symlink with an extra precaution static QString readLinkSafely(const char *path); /// Set the parent window to be used for dialogs void setParentWindow(QWidget *widget) { parentWindow = widget; } signals: /// Emitted when this filesystem is currently refreshing the filesystem directory. void refreshJobStarted(KIO::Job *job); /// Emitted when an error occurred in this filesystem during refresh. void error(const QString &msg); /// Emitted when the content of a directory was changed by this filesystem. void fileSystemChanged(const QUrl &directory, bool removed); /// Emitted when the information for the filesystem of the current directory changed. /// Information is either /// * 'metaInfo': a displayable string about the fs, empty by default, OR /// * 'fsType', 'total' and 'free': filesystem type, size and free space, /// empty string or 0 by default void fileSystemInfoChanged(const QString &metaInfo, const QString &fsType, KIO::filesize_t total, KIO::filesize_t free); /// Emitted before a directory path is opened for reading. Used for automounting. void aboutToOpenDir(const QString &path); protected: /// Fill the filesystem dictionary with file items, must be implemented for each filesystem. virtual bool refreshInternal(const QUrl &origin, bool stayInDir) = 0; /// Connect the result signal of a file operation job - source URLs. - void connectJobToSources(KJob *job, const QList urls); + void connectJobToSources(KJob *job, const QList& urls); /// Connect the result signal of a file operation job - destination URL. void connectJobToDestination(KJob *job, const QUrl &destination); /// Returns true if showing hidden files is set in config. bool showHiddenFiles(); /// Add a new file item to the internal dictionary (while refreshing). void addFileItem(FileItem *item); FS_TYPE _type; // the filesystem type. QUrl _currentDirectory; // the path or file the filesystem originates from. bool _isRefreshing; // true if filesystem is busy with refreshing QPointer parentWindow; protected slots: /// Handle result after job (except when refreshing!) finished void slotJobResult(KJob *job, bool refresh); private: typedef QHash FileItemDict; // optional TODO: add an async version of this bool scanOrRefresh(const QUrl &directory, bool onlyScan); /// Delete and clear file items. void clear(FileItemDict &fileItems); FileItemDict _fileItems; // the list of files in the current dictionary }; #endif diff --git a/krusader/FileSystem/krquery.cpp b/krusader/FileSystem/krquery.cpp index f9c81a60..a253c264 100644 --- a/krusader/FileSystem/krquery.cpp +++ b/krusader/FileSystem/krquery.cpp @@ -1,777 +1,778 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 "krquery.h" // QtCore #include #include #include #include #include #include #include #include +#include #include "../Archive/krarchandler.h" #include "fileitem.h" #include "filesystem.h" #include "krpermhandler.h" #define STATUS_SEND_DELAY 250 #define MAX_LINE_LEN 1000 // set the defaults KRQuery::KRQuery() : QObject(), matchesCaseSensitive(true), bNull(true), contain(QString()), containCaseSensetive(true), containWholeWord(false), containRegExp(false), minSize(0), maxSize(0), newerThen(0), olderThen(0), owner(QString()), group(QString()), perm(QString()), type(QString()), inArchive(false), recurse(true), followLinksP(true), receivedBuffer(nullptr), receivedBufferLen(0), processEventsConnected(0), codec(QTextCodec::codecForLocale()) { QChar ch = '\n'; QTextCodec::ConverterState state(QTextCodec::IgnoreHeader); encodedEnterArray = codec->fromUnicode(&ch, 1, &state); encodedEnter = encodedEnterArray.data(); encodedEnterLen = encodedEnterArray.size(); } // set the defaults KRQuery::KRQuery(const QString &name, bool matchCase) : QObject(), bNull(true), contain(QString()), containCaseSensetive(true), containWholeWord(false), containRegExp(false), minSize(0), maxSize(0), newerThen(0), olderThen(0), owner(QString()), group(QString()), perm(QString()), type(QString()), inArchive(false), recurse(true), followLinksP(true), receivedBuffer(nullptr), receivedBufferLen(0), processEventsConnected(0), codec(QTextCodec::codecForLocale()) { QChar ch = '\n'; QTextCodec::ConverterState state(QTextCodec::IgnoreHeader); encodedEnterArray = codec->fromUnicode(&ch, 1, &state); encodedEnter = encodedEnterArray.data(); encodedEnterLen = encodedEnterArray.size(); setNameFilter(name, matchCase); } KRQuery::KRQuery(const KRQuery &that) : QObject(), receivedBuffer(nullptr), receivedBufferLen(0), processEventsConnected(0) { *this = that; } KRQuery::~KRQuery() { if (receivedBuffer) delete[] receivedBuffer; receivedBuffer = nullptr; } KRQuery &KRQuery::operator=(const KRQuery &old) { matches = old.matches; excludes = old.excludes; includedDirs = old.includedDirs; excludedDirs = old.excludedDirs; matchesCaseSensitive = old.matchesCaseSensitive; bNull = old.bNull; contain = old.contain; containCaseSensetive = old.containCaseSensetive; containWholeWord = old.containWholeWord; containRegExp = old.containRegExp; minSize = old.minSize; maxSize = old.maxSize; newerThen = old.newerThen; olderThen = old.olderThen; owner = old.owner; group = old.group; perm = old.perm; type = old.type; customType = old.customType; inArchive = old.inArchive; recurse = old.recurse; followLinksP = old.followLinksP; whereToSearch = old.whereToSearch; excludedFolderNames = old.excludedFolderNames; whereNotToSearch = old.whereNotToSearch; origFilter = old.origFilter; codec = old.codec; encodedEnterArray = old.encodedEnterArray; encodedEnter = encodedEnterArray.data(); encodedEnterLen = encodedEnterArray.size(); return *this; } -void KRQuery::load(KConfigGroup cfg) +void KRQuery::load(const KConfigGroup& cfg) { *this = KRQuery(); // reset parameters first if (cfg.readEntry("IsNull", true)) return; #define LOAD(key, var) (var = cfg.readEntry(key, var)) LOAD("Matches", matches); LOAD("Excludes", excludes); LOAD("IncludedDirs", includedDirs); LOAD("ExcludedDirs", excludedDirs); LOAD("MatchesCaseSensitive", matchesCaseSensitive); LOAD("Contain", contain); LOAD("ContainCaseSensetive", containCaseSensetive); LOAD("ContainWholeWord", containWholeWord); LOAD("ContainRegExp", containRegExp); LOAD("MinSize", minSize); LOAD("MaxSize", maxSize); newerThen = QDateTime::fromString( cfg.readEntry("NewerThan", QDateTime::fromTime_t(newerThen).toString())) .toTime_t(); olderThen = QDateTime::fromString( cfg.readEntry("OlderThan", QDateTime::fromTime_t(olderThen).toString())) .toTime_t(); LOAD("Owner", owner); LOAD("Group", group); LOAD("Perm", perm); LOAD("Type", type); LOAD("CustomType", customType); LOAD("InArchive", inArchive); LOAD("Recurse", recurse); LOAD("FollowLinks", followLinksP); // KF5 TODO? // LOAD("WhereToSearch", whereToSearch); // LOAD("WhereNotToSearch", whereNotToSearch); LOAD("OrigFilter", origFilter); codec = QTextCodec::codecForName(cfg.readEntry("Codec", codec->name())); if (!codec) codec = QTextCodec::codecForLocale(); LOAD("EncodedEnterArray", encodedEnterArray); encodedEnter = encodedEnterArray.data(); encodedEnterLen = encodedEnterArray.size(); #undef LOAD bNull = false; } void KRQuery::save(KConfigGroup cfg) { cfg.writeEntry("IsNull", bNull); if (bNull) return; cfg.writeEntry("Matches", matches); cfg.writeEntry("Excludes", excludes); cfg.writeEntry("IncludedDirs", includedDirs); cfg.writeEntry("ExcludedDirs", excludedDirs); cfg.writeEntry("MatchesCaseSensitive", matchesCaseSensitive); cfg.writeEntry("Contain", contain); cfg.writeEntry("ContainCaseSensetive", containCaseSensetive); cfg.writeEntry("ContainWholeWord", containWholeWord); cfg.writeEntry("ContainRegExp", containRegExp); cfg.writeEntry("MinSize", minSize); cfg.writeEntry("MaxSize", maxSize); cfg.writeEntry("NewerThan", QDateTime::fromTime_t(newerThen).toString()); cfg.writeEntry("OlderThan", QDateTime::fromTime_t(olderThen).toString()); cfg.writeEntry("Owner", owner); cfg.writeEntry("Group", group); cfg.writeEntry("Perm", perm); cfg.writeEntry("Type", type); cfg.writeEntry("CustomType", customType); cfg.writeEntry("InArchive", inArchive); cfg.writeEntry("Recurse", recurse); cfg.writeEntry("FollowLinks", followLinksP); // KF5 TODO? // cfg.writeEntry("WhereToSearch", whereToSearch); // cfg.writeEntry("WhereNotToSearch", whereNotToSearch); cfg.writeEntry("OrigFilter", origFilter); cfg.writeEntry("Codec", codec->name()); cfg.writeEntry("EncodedEnterArray", encodedEnterArray); cfg.writeEntry("EncodedEnter", encodedEnter); cfg.writeEntry("EncodedEnterLen", encodedEnterLen); } void KRQuery::connectNotify(const QMetaMethod &signal) { if (signal == QMetaMethod::fromSignal(&KRQuery::processEvents)) processEventsConnected++; } void KRQuery::disconnectNotify(const QMetaMethod &signal) { if (signal == QMetaMethod::fromSignal(&KRQuery::processEvents)) processEventsConnected--; } bool KRQuery::checkPerm(QString filePerm) const { for (int i = 0; i < 9; ++i) if (perm[i] != '?' && perm[i] != filePerm[i + 1]) return false; return true; } -bool KRQuery::checkType(QString mime) const +bool KRQuery::checkType(const QString& mime) const { if (type == mime) return true; if (type == i18n("Archives")) return KRarcHandler::arcSupported(mime); if (type == i18n("Folders")) return mime.contains("directory"); if (type == i18n("Image Files")) return mime.contains("image/"); if (type == i18n("Text Files")) return mime.contains("text/"); if (type == i18n("Video Files")) return mime.contains("video/"); if (type == i18n("Audio Files")) return mime.contains("audio/"); if (type == i18n("Custom")) return customType.contains(mime); return false; } bool KRQuery::match(const QString &name) const { return matchCommon(name, matches, excludes); } bool KRQuery::matchDirName(const QString &name) const { return matchCommon(name, includedDirs, excludedDirs); } bool KRQuery::matchCommon(const QString &nameIn, const QStringList &matchList, const QStringList &excludeList) const { if (excludeList.count() == 0 && matchList.count() == 0) /* true if there's no match condition */ return true; QString name(nameIn); int ndx = nameIn.lastIndexOf('/'); // virtual filenames may contain '/' if (ndx != -1) // but the end of the filename is OK name = nameIn.mid(ndx + 1); for (int i = 0; i < excludeList.count(); ++i) { if (QRegExp(excludeList[i], matchesCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard) .exactMatch(name)) return false; } if (matchList.count() == 0) return true; for (int i = 0; i < matchList.count(); ++i) { if (QRegExp(matchList[i], matchesCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard) .exactMatch(name)) return true; } return false; } bool KRQuery::match(FileItem *item) const { if (item->isDir() && !matchDirName(item->getName())) return false; // see if the name matches if (!match(item->getName())) return false; // checking the mime if (!type.isEmpty() && !checkType(item->getMime())) return false; // check that the size fit KIO::filesize_t size = item->getSize(); if (minSize && size < minSize) return false; if (maxSize && size > maxSize) return false; // check the time frame time_t mtime = item->getTime_t(); if (olderThen && mtime > olderThen) return false; if (newerThen && mtime < newerThen) return false; // check owner name if (!owner.isEmpty() && item->getOwner() != owner) return false; // check group name if (!group.isEmpty() && item->getGroup() != group) return false; // check permission if (!perm.isEmpty() && !checkPerm(item->getPerm())) return false; if (!contain.isEmpty()) { if ((totalBytes = item->getSize()) == 0) totalBytes++; // sanity receivedBytes = 0; if (receivedBuffer) delete receivedBuffer; receivedBuffer = nullptr; receivedBufferLen = 0; fileName = item->getName(); timer.start(); // search locally if (item->getUrl().isLocalFile()) { return containsContent(item->getUrl().path()); } // search remotely if (processEventsConnected == 0) { return false; } return containsContent(item->getUrl()); } return true; } // takes the string and adds BOLD to it, so that when it is displayed, // the grepped text will be bold void fixFoundTextForDisplay(QString &haystack, int start, int length) { QString before = haystack.left(start); QString text = haystack.mid(start, length); QString after = haystack.mid(start + length); before.replace('&', "&"); before.replace('<', "<"); before.replace('>', ">"); text.replace('&', "&"); text.replace('<', "<"); text.replace('>', ">"); after.replace('&', "&"); after.replace('<', "<"); after.replace('>', ">"); haystack = ("" + before + "" + text + "" + after + ""); } bool KRQuery::checkBuffer(const char *data, int len) const { bool result = false; auto *mergedBuffer = new char[len + receivedBufferLen]; if (receivedBufferLen) memcpy(mergedBuffer, receivedBuffer, receivedBufferLen); if (len) memcpy(mergedBuffer + receivedBufferLen, data, len); int maxLen = len + receivedBufferLen; int maxBuffer = maxLen - encodedEnterLen; int lastLinePosition = 0; for (int enterIndex = 0; enterIndex < maxBuffer; enterIndex++) { if (memcmp(mergedBuffer + enterIndex, encodedEnter, encodedEnterLen) == 0) { QString str = codec->toUnicode(mergedBuffer + lastLinePosition, enterIndex + encodedEnterLen - lastLinePosition); if (str.endsWith('\n')) { str.chop(1); result = result || checkLine(str); lastLinePosition = enterIndex + encodedEnterLen; enterIndex = lastLinePosition; continue; } } } if (maxLen - lastLinePosition > MAX_LINE_LEN || len == 0) { QString str = codec->toUnicode(mergedBuffer + lastLinePosition, maxLen - lastLinePosition); result = result || checkLine(str); lastLinePosition = maxLen; } delete[] receivedBuffer; receivedBuffer = nullptr; receivedBufferLen = maxLen - lastLinePosition; if (receivedBufferLen) { receivedBuffer = new char[receivedBufferLen]; memcpy(receivedBuffer, mergedBuffer + lastLinePosition, receivedBufferLen); } delete[] mergedBuffer; return result; } bool KRQuery::checkLine(const QString &line, bool backwards) const { if (containRegExp) { QRegExp rexp(contain, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp); int ndx = backwards ? rexp.lastIndexIn(line) : rexp.indexIn(line); bool result = ndx >= 0; if (result) fixFoundTextForDisplay(lastSuccessfulGrep = line, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = rexp.matchedLength()); return result; } int ndx = backwards ? -1 : 0; if (line.isNull()) return false; if (containWholeWord) { while ((ndx = (backwards) ? line.lastIndexOf(contain, ndx, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive) : line.indexOf(contain, ndx, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive)) != -1) { QChar before = '\n'; QChar after = '\n'; if (ndx > 0) before = line.at(ndx - 1); if (ndx + contain.length() < line.length()) after = line.at(ndx + contain.length()); if (!before.isLetterOrNumber() && !after.isLetterOrNumber() && after != '_' && before != '_') { lastSuccessfulGrep = line; fixFoundTextForDisplay(lastSuccessfulGrep, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = contain.length()); return true; } if (backwards) ndx -= line.length() + 1; else ndx++; } } else if ((ndx = (backwards) ? line.lastIndexOf(contain, -1, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive) : line.indexOf(contain, 0, containCaseSensetive ? Qt::CaseSensitive : Qt::CaseInsensitive)) != -1) { lastSuccessfulGrep = line; fixFoundTextForDisplay(lastSuccessfulGrep, lastSuccessfulGrepMatchIndex = ndx, lastSuccessfulGrepMatchLength = contain.length()); return true; } return false; } -bool KRQuery::containsContent(QString file) const +bool KRQuery::containsContent(const QString& file) const { QFile qf(file); if (!qf.open(QIODevice::ReadOnly)) return false; char buffer[1440]; // 2k buffer while (!qf.atEnd()) { int bytes = qf.read(buffer, sizeof(buffer)); if (bytes <= 0) break; receivedBytes += bytes; if (checkBuffer(buffer, bytes)) return true; if (checkTimer()) { bool stopped = false; emit((KRQuery *)this)->processEvents(stopped); if (stopped) return false; } } if (checkBuffer(buffer, 0)) return true; lastSuccessfulGrep.clear(); // nothing was found return false; } -bool KRQuery::containsContent(QUrl url) const +bool KRQuery::containsContent(const QUrl& url) const { KIO::TransferJob *contentReader = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); connect(contentReader, &KIO::TransferJob::data, this, &KRQuery::containsContentData); connect(contentReader, &KIO::Job::result, this, &KRQuery::containsContentFinished); busy = true; containsContentResult = false; bool stopped = false; while (busy && !stopped) { checkTimer(); emit((KRQuery *)this)->processEvents(stopped); } if (busy) { contentReader->kill(KJob::EmitResult); busy = false; } return containsContentResult; } void KRQuery::containsContentData(KIO::Job *job, const QByteArray &array) { receivedBytes += array.size(); if (checkBuffer(array.data(), array.size())) { containsContentResult = true; containsContentFinished(job); job->kill(KJob::EmitResult); return; } checkTimer(); } void KRQuery::containsContentFinished(KJob *) { busy = false; } bool KRQuery::checkTimer() const { if (timer.elapsed() >= STATUS_SEND_DELAY) { auto pcnt = (int)(100. * (double)receivedBytes / (double)totalBytes + .5); QString message = i18nc("%1=filename, %2=percentage", "Searching content of '%1' (%2%)", fileName, pcnt); timer.start(); emit((KRQuery *)this)->status(message); return true; } return false; } QStringList KRQuery::split(QString str) { QStringList list; int splitNdx = 0; int startNdx = 0; bool quotation = false; while (splitNdx < str.length()) { if (str[splitNdx] == '"') quotation = !quotation; if (!quotation && str[splitNdx] == ' ') { QString section = str.mid(startNdx, splitNdx - startNdx); startNdx = splitNdx + 1; if (section.startsWith('\"') && section.endsWith('\"') && section.length() >= 2) section = section.mid(1, section.length() - 2); if (!section.isEmpty()) list.append(section); } splitNdx++; } if (startNdx < splitNdx) { QString section = str.mid(startNdx, splitNdx - startNdx); if (section.startsWith('\"') && section.endsWith('\"') && section.length() >= 2) section = section.mid(1, section.length() - 2); if (!section.isEmpty()) list.append(section); } return list; } void KRQuery::setNameFilter(const QString &text, bool cs) { bNull = false; matchesCaseSensitive = cs; origFilter = text; QString matchText = text; QString excludeText; int excludeNdx = 0; bool quotationMark = 0; while (excludeNdx < matchText.length()) { if (matchText[excludeNdx] == '"') quotationMark = !quotationMark; if (!quotationMark) { if (matchText[excludeNdx] == '|') break; } excludeNdx++; } if (excludeNdx < matchText.length()) { excludeText = matchText.mid(excludeNdx + 1).trimmed(); matchText.truncate(excludeNdx); matchText = matchText.trimmed(); if (matchText.isEmpty()) matchText = '*'; } int i; matches = split(matchText); includedDirs.clear(); for (i = 0; i < matches.count();) { if (matches[i].endsWith('/')) { includedDirs.push_back(matches[i].left(matches[i].length() - 1)); matches.removeAll(matches.at(i)); continue; } if (!matches[i].contains("*") && !matches[i].contains("?")) matches[i] = '*' + matches[i] + '*'; i++; } excludes = split(excludeText); excludedDirs.clear(); for (i = 0; i < excludes.count();) { if (excludes[i].endsWith('/')) { excludedDirs.push_back(excludes[i].left(excludes[i].length() - 1)); excludes.removeAll(excludes.at(i)); continue; } if (!excludes[i].contains("*") && !excludes[i].contains("?")) excludes[i] = '*' + excludes[i] + '*'; i++; } } -void KRQuery::setContent(const QString &content, bool cs, bool wholeWord, QString encoding, +void KRQuery::setContent(const QString &content, bool cs, bool wholeWord, const QString& encoding, bool regExp) { bNull = false; contain = content; containCaseSensetive = cs; containWholeWord = wholeWord; containRegExp = regExp; if (encoding.isEmpty()) codec = QTextCodec::codecForLocale(); else { codec = QTextCodec::codecForName(encoding.toLatin1()); if (codec == nullptr) codec = QTextCodec::codecForLocale(); } QChar ch = '\n'; QTextCodec::ConverterState state(QTextCodec::IgnoreHeader); encodedEnterArray = codec->fromUnicode(&ch, 1, &state); encodedEnter = encodedEnterArray.data(); encodedEnterLen = encodedEnterArray.size(); } void KRQuery::setMinimumFileSize(KIO::filesize_t minimumSize) { bNull = false; minSize = minimumSize; } void KRQuery::setMaximumFileSize(KIO::filesize_t maximumSize) { bNull = false; maxSize = maximumSize; } void KRQuery::setNewerThan(time_t time) { bNull = false; newerThen = time; } void KRQuery::setOlderThan(time_t time) { bNull = false; olderThen = time; } void KRQuery::setOwner(const QString &ownerIn) { bNull = false; owner = ownerIn; } void KRQuery::setGroup(const QString &groupIn) { bNull = false; group = groupIn; } void KRQuery::setPermissions(const QString &permIn) { bNull = false; perm = permIn; } void KRQuery::setMimeType(const QString &typeIn, QStringList customList) { bNull = false; type = typeIn; - customType = customList; + customType = std::move(customList); } bool KRQuery::isExcluded(const QUrl &url) { for (QUrl &item : whereNotToSearch) if (item.isParentOf(url) || url.matches(item, QUrl::StripTrailingSlash)) return true; // Exclude folder names that are configured in settings QString filename = url.fileName(); for (QString &item : excludedFolderNames) if (filename == item) return true; if (!matchDirName(filename)) return true; return false; } void KRQuery::setSearchInDirs(const QList &urls) { whereToSearch.clear(); for (int i = 0; i < urls.count(); ++i) { QString url = urls[i].url(); QUrl completed = QUrl::fromUserInput(KUrlCompletion::replacedPath(url, true, true), QString(), QUrl::AssumeLocalFile); whereToSearch.append(completed); } } void KRQuery::setDontSearchInDirs(const QList &urls) { whereNotToSearch.clear(); for (int i = 0; i < urls.count(); ++i) { QString url = urls[i].url(); QUrl completed = QUrl::fromUserInput(KUrlCompletion::replacedPath(url, true, true), QString(), QUrl::AssumeLocalFile); whereNotToSearch.append(completed); } } void KRQuery::setExcludeFolderNames(const QStringList &paths) { excludedFolderNames.clear(); excludedFolderNames.append(paths); } diff --git a/krusader/FileSystem/krquery.h b/krusader/FileSystem/krquery.h index e28759ac..1509547d 100644 --- a/krusader/FileSystem/krquery.h +++ b/krusader/FileSystem/krquery.h @@ -1,228 +1,228 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 KRQUERY_H #define KRQUERY_H // QtCore #include #include #include #include #include class QTextCodec; class FileItem; /** * @brief A search query for files * * Can be used for finding or selecting files and folders by multiple limiting search criteria */ class KRQuery : public QObject { Q_OBJECT public: // null query KRQuery(); // query only with name filter explicit KRQuery(const QString &name, bool matchCase = true); // copy constructor KRQuery(const KRQuery &); // let operator KRQuery &operator=(const KRQuery &); // destructor ~KRQuery() override; // load parameters from config - void load(KConfigGroup cfg); + void load(const KConfigGroup& cfg); // save parameters to config void save(KConfigGroup cfg); // matching a file with the query bool match(FileItem *file) const; // checks if the given fileItem object matches the conditions // matching a name with the query bool match(const QString &name) const; // matching the filename only // matching the name of the directory bool matchDirName(const QString &name) const; // sets the text for name filtering void setNameFilter(const QString &text, bool cs = true); // returns the current filter mask const QString &nameFilter() const { return origFilter; } // returns whether the filter is case sensitive bool isCaseSensitive() { return matchesCaseSensitive; } // returns if the filter is null (was cancelled) bool isNull() { return bNull; } // sets the content part of the query void setContent(const QString &content, bool cs = true, bool wholeWord = false, - QString encoding = QString(), bool regExp = false); + const QString& encoding = QString(), bool regExp = false); const QString content() { return contain; } // sets the minimum file size limit void setMinimumFileSize(KIO::filesize_t); // sets the maximum file size limit void setMaximumFileSize(KIO::filesize_t); // sets the time the file newer than void setNewerThan(time_t time); // sets the time the file older than void setOlderThan(time_t time); // sets the owner void setOwner(const QString &ownerIn); // sets the group void setGroup(const QString &groupIn); // sets the permissions void setPermissions(const QString &permIn); // sets the mimetype for the query // type, must be one of the following: // 1. a valid mime type name // 2. one of: i18n("Archives"), i18n("Folders"), i18n("Image Files") // i18n("Text Files"), i18n("Video Files"), i18n("Audio Files") // 3. i18n("Custom") in which case you must supply a list of valid mime-types // in the member QStringList customType void setMimeType(const QString &typeIn, QStringList customList = QStringList()); // true if setMimeType was called bool hasMimeType() { return type.isEmpty(); } // sets the search in archive flag void setSearchInArchives(bool flag) { inArchive = flag; } // gets the search in archive flag bool searchInArchives() { return inArchive; } // sets the recursive flag void setRecursive(bool flag) { recurse = flag; } // gets the recursive flag bool isRecursive() { return recurse; } // sets whether to follow symbolic links void setFollowLinks(bool flag) { followLinksP = flag; } // gets whether to follow symbolic links bool followLinks() { return followLinksP; } // sets the folder names which the searcher will exclude from traversing void setExcludeFolderNames(const QStringList &urls); // gets the folder names which the searcher excludes const QStringList excludeFolderNames() { return excludedFolderNames; } // sets the folders where the searcher will search void setSearchInDirs(const QList &urls); // gets the folders where the searcher searches const QList &searchInDirs() { return whereToSearch; } // sets the folders where search is not permitted void setDontSearchInDirs(const QList &urls); // gets the folders where search is not permitted const QList &dontSearchInDirs() { return whereNotToSearch; } // checks if a URL is excluded bool isExcluded(const QUrl &url); // gives whether we search for content bool isContentSearched() const { return !contain.isEmpty(); } bool checkLine(const QString &line, bool backwards = false) const; const QString &foundText() const { return lastSuccessfulGrep; } int matchIndex() const { return lastSuccessfulGrepMatchIndex; } int matchLength() const { return lastSuccessfulGrepMatchLength; } protected: // important to know whether the event processor is connected void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE; // important to know whether the event processor is connected void disconnectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE; protected: QStringList matches; // what to search QStringList excludes; // what to exclude QStringList includedDirs; // what dirs to include QStringList excludedDirs; // what dirs to exclude bool matchesCaseSensitive; bool bNull; // flag if the query is null QString contain; // file must contain this string bool containCaseSensetive; bool containWholeWord; bool containRegExp; KIO::filesize_t minSize; KIO::filesize_t maxSize; time_t newerThen; time_t olderThen; QString owner; QString group; QString perm; QString type; QStringList customType; bool inArchive; // if true- search in archive. bool recurse; // if true recurse ob sub-dirs... bool followLinksP; QStringList excludedFolderNames; // substrings of paths where not to search QList whereToSearch; // directories to search QList whereNotToSearch; // directories NOT to search signals: void status(const QString &name); void processEvents(bool &stopped); private: bool matchCommon(const QString &, const QStringList &, const QStringList &) const; bool checkPerm(QString perm) const; - bool checkType(QString mime) const; - bool containsContent(QString file) const; - bool containsContent(QUrl url) const; + bool checkType(const QString& mime) const; + bool containsContent(const QString& file) const; + bool containsContent(const QUrl& url) const; bool checkBuffer(const char *data, int len) const; bool checkTimer() const; QStringList split(QString); private slots: void containsContentData(KIO::Job *, const QByteArray &); void containsContentFinished(KJob *); private: QString origFilter; mutable bool busy; mutable bool containsContentResult; mutable char *receivedBuffer; mutable int receivedBufferLen; mutable QString lastSuccessfulGrep; mutable int lastSuccessfulGrepMatchIndex; mutable int lastSuccessfulGrepMatchLength; mutable QString fileName; mutable KIO::filesize_t receivedBytes; mutable KIO::filesize_t totalBytes; mutable int processEventsConnected; mutable QTime timer; QTextCodec *codec; const char *encodedEnter; int encodedEnterLen; QByteArray encodedEnterArray; }; #endif diff --git a/krusader/Filter/advancedfilter.cpp b/krusader/Filter/advancedfilter.cpp index 3164b7f6..5b5d1761 100644 --- a/krusader/Filter/advancedfilter.cpp +++ b/krusader/Filter/advancedfilter.cpp @@ -1,612 +1,612 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "advancedfilter.h" // QtCore #include #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../Dialogs/krdialogs.h" #define USERSFILE QString("/etc/passwd") #define GROUPSFILE QString("/etc/group") AdvancedFilter::AdvancedFilter(FilterTabs *tabs, QWidget *parent) : QWidget(parent), fltTabs(tabs) { auto *filterLayout = new QGridLayout(this); filterLayout->setSpacing(6); filterLayout->setContentsMargins(11, 11, 11, 11); // Options for size auto *sizeGroup = new QGroupBox(this); QSizePolicy sizeGroupPolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); sizeGroupPolicy.setHeightForWidth(sizeGroup->sizePolicy().hasHeightForWidth()); sizeGroup->setSizePolicy(sizeGroupPolicy); sizeGroup->setTitle(i18n("Size")); auto *sizeLayout = new QGridLayout(sizeGroup); sizeLayout->setAlignment(Qt::AlignTop); sizeLayout->setSpacing(6); sizeLayout->setContentsMargins(11, 11, 11, 11); minSizeEnabled = new QCheckBox(sizeGroup); minSizeEnabled->setText(i18n("At Least")); minSizeEnabled->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); sizeLayout->addWidget(minSizeEnabled, 0, 0); minSizeAmount = new QSpinBox(sizeGroup); minSizeAmount->setRange(0, 999999999); minSizeAmount->setEnabled(false); sizeLayout->addWidget(minSizeAmount, 0, 1); minSizeType = new KComboBox(false, sizeGroup); // i18n: @item:inlistbox next to a text field containing the amount minSizeType->addItem(i18n("Byte")); // i18n: @item:inlistbox next to a text field containing the amount minSizeType->addItem(i18n("KiB")); // i18n: @item:inlistbox next to a text field containing the amount minSizeType->addItem(i18n("MiB")); // i18n: @item:inlistbox next to a text field containing the amount minSizeType->addItem(i18n("GiB")); minSizeType->setEnabled(false); sizeLayout->addWidget(minSizeType, 0, 2); maxSizeEnabled = new QCheckBox(sizeGroup); maxSizeEnabled->setText(i18n("At Most")); maxSizeEnabled->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); sizeLayout->addWidget(maxSizeEnabled, 0, 3); maxSizeAmount = new QSpinBox(sizeGroup); maxSizeAmount->setRange(0, 999999999); maxSizeAmount->setEnabled(false); sizeLayout->addWidget(maxSizeAmount, 0, 4); maxSizeType = new KComboBox(false, sizeGroup); // i18n: @item:inlistbox next to a text field containing the amount maxSizeType->addItem(i18n("Byte")); // i18n: @item:inlistbox next to a text field containing the amount maxSizeType->addItem(i18n("KiB")); // i18n: @item:inlistbox next to a text field containing the amount maxSizeType->addItem(i18n("MiB")); // i18n: @item:inlistbox next to a text field containing the amount maxSizeType->addItem(i18n("GiB")); maxSizeType->setEnabled(false); sizeLayout->addWidget(maxSizeType, 0, 5); filterLayout->addWidget(sizeGroup, 0, 0); // Options for date Icon iconDate("view-calendar"); auto *dateGroup = new QGroupBox(this); auto *btnGroup = new QButtonGroup(dateGroup); dateGroup->setTitle(i18n("Date")); btnGroup->setExclusive(true); auto *dateLayout = new QGridLayout(dateGroup); dateLayout->setAlignment(Qt::AlignTop); dateLayout->setSpacing(6); dateLayout->setContentsMargins(11, 11, 11, 11); anyDateEnabled = new QRadioButton(dateGroup); anyDateEnabled->setText(i18n("Any date")); btnGroup->addButton(anyDateEnabled); anyDateEnabled->setChecked(true); modifiedBetweenEnabled = new QRadioButton(dateGroup); modifiedBetweenEnabled->setText(i18n("&Modified between")); btnGroup->addButton(modifiedBetweenEnabled); modifiedBetweenData1 = new KLineEdit(dateGroup); modifiedBetweenData1->setEnabled(false); modifiedBetweenData1->setText(""); modifiedBetweenBtn1 = new QToolButton(dateGroup); modifiedBetweenBtn1->setEnabled(false); modifiedBetweenBtn1->setText(""); modifiedBetweenBtn1->setIcon(iconDate); QLabel *andLabel = new QLabel(dateGroup); andLabel->setText(i18n("an&d")); modifiedBetweenData2 = new KLineEdit(dateGroup); modifiedBetweenData2->setEnabled(false); modifiedBetweenData2->setText(""); andLabel->setBuddy(modifiedBetweenData2); modifiedBetweenBtn2 = new QToolButton(dateGroup); modifiedBetweenBtn2->setEnabled(false); modifiedBetweenBtn2->setText(""); modifiedBetweenBtn2->setIcon(iconDate); notModifiedAfterEnabled = new QRadioButton(dateGroup); notModifiedAfterEnabled->setText(i18n("&Not modified after")); btnGroup->addButton(notModifiedAfterEnabled); notModifiedAfterData = new KLineEdit(dateGroup); notModifiedAfterData->setEnabled(false); notModifiedAfterData->setText(""); notModifiedAfterBtn = new QToolButton(dateGroup); notModifiedAfterBtn->setEnabled(false); notModifiedAfterBtn->setText(""); notModifiedAfterBtn->setIcon(iconDate); modifiedInTheLastEnabled = new QRadioButton(dateGroup); modifiedInTheLastEnabled->setText(i18n("Mod&ified in the last")); btnGroup->addButton(modifiedInTheLastEnabled); modifiedInTheLastData = new QSpinBox(dateGroup); modifiedInTheLastData->setRange(0, 99999); modifiedInTheLastData->setEnabled(false); modifiedInTheLastType = new KComboBox(dateGroup); modifiedInTheLastType->addItem(i18n("days")); modifiedInTheLastType->addItem(i18n("weeks")); modifiedInTheLastType->addItem(i18n("months")); modifiedInTheLastType->addItem(i18n("years")); modifiedInTheLastType->setEnabled(false); notModifiedInTheLastData = new QSpinBox(dateGroup); notModifiedInTheLastData->setRange(0, 99999); notModifiedInTheLastData->setEnabled(false); QLabel *notModifiedInTheLastLbl = new QLabel(dateGroup); notModifiedInTheLastLbl->setText(i18n("No&t modified in the last")); notModifiedInTheLastLbl->setBuddy(notModifiedInTheLastData); notModifiedInTheLastType = new KComboBox(dateGroup); notModifiedInTheLastType->addItem(i18n("days")); notModifiedInTheLastType->addItem(i18n("weeks")); notModifiedInTheLastType->addItem(i18n("months")); notModifiedInTheLastType->addItem(i18n("years")); notModifiedInTheLastType->setEnabled(false); // Date options layout dateLayout->addWidget(anyDateEnabled, 0, 0); dateLayout->addWidget(modifiedBetweenEnabled, 1, 0); dateLayout->addWidget(modifiedBetweenData1, 1, 1); dateLayout->addWidget(modifiedBetweenBtn1, 1, 2); dateLayout->addWidget(andLabel, 1, 3); dateLayout->addWidget(modifiedBetweenData2, 1, 4); dateLayout->addWidget(modifiedBetweenBtn2, 1, 5); dateLayout->addWidget(notModifiedAfterEnabled, 2, 0); dateLayout->addWidget(notModifiedAfterData, 2, 1); dateLayout->addWidget(notModifiedAfterBtn, 2, 2); dateLayout->addWidget(modifiedInTheLastEnabled, 3, 0); auto *modifiedInTheLastLayout = new QHBoxLayout(); modifiedInTheLastLayout->addWidget(modifiedInTheLastData); modifiedInTheLastLayout->addWidget(modifiedInTheLastType); dateLayout->addLayout(modifiedInTheLastLayout, 3, 1); dateLayout->addWidget(notModifiedInTheLastLbl, 4, 0); modifiedInTheLastLayout = new QHBoxLayout(); modifiedInTheLastLayout->addWidget(notModifiedInTheLastData); modifiedInTheLastLayout->addWidget(notModifiedInTheLastType); dateLayout->addLayout(modifiedInTheLastLayout, 4, 1); filterLayout->addWidget(dateGroup, 1, 0); // Options for ownership auto *ownershipGroup = new QGroupBox(this); ownershipGroup->setTitle(i18n("Ownership")); auto *ownershipLayout = new QGridLayout(ownershipGroup); ownershipLayout->setAlignment(Qt::AlignTop); ownershipLayout->setSpacing(6); ownershipLayout->setContentsMargins(11, 11, 11, 11); auto *hboxLayout = new QHBoxLayout(); hboxLayout->setSpacing(6); hboxLayout->setContentsMargins(0, 0, 0, 0); belongsToUserEnabled = new QCheckBox(ownershipGroup); belongsToUserEnabled->setText(i18n("Belongs to &user")); hboxLayout->addWidget(belongsToUserEnabled); belongsToUserData = new KComboBox(ownershipGroup); belongsToUserData->setEnabled(false); belongsToUserData->setEditable(false); hboxLayout->addWidget(belongsToUserData); belongsToGroupEnabled = new QCheckBox(ownershipGroup); belongsToGroupEnabled->setText(i18n("Belongs to gr&oup")); hboxLayout->addWidget(belongsToGroupEnabled); belongsToGroupData = new KComboBox(ownershipGroup); belongsToGroupData->setEnabled(false); belongsToGroupData->setEditable(false); hboxLayout->addWidget(belongsToGroupData); ownershipLayout->addLayout(hboxLayout, 0, 0, 1, 4); permissionsEnabled = new QCheckBox(ownershipGroup); permissionsEnabled->setText(i18n("P&ermissions")); ownershipLayout->addWidget(permissionsEnabled, 1, 0); auto *ownerGroup = new QGroupBox(ownershipGroup); auto *ownerHBox = new QHBoxLayout(ownerGroup); ownerGroup->setTitle(i18n("O&wner")); ownerR = new KComboBox(ownerGroup); ownerR->addItem(i18n("?")); ownerR->addItem(i18n("r")); ownerR->addItem(i18n("-")); ownerR->setEnabled(false); ownerHBox->addWidget(ownerR); ownerW = new KComboBox(ownerGroup); ownerW->addItem(i18n("?")); ownerW->addItem(i18n("w")); ownerW->addItem(i18n("-")); ownerW->setEnabled(false); ownerHBox->addWidget(ownerW); ownerX = new KComboBox(ownerGroup); ownerX->addItem(i18n("?")); ownerX->addItem(i18n("x")); ownerX->addItem(i18n("-")); ownerX->setEnabled(false); ownerHBox->addWidget(ownerX); ownershipLayout->addWidget(ownerGroup, 1, 1); auto *groupGroup = new QGroupBox(ownershipGroup); auto *groupHBox = new QHBoxLayout(groupGroup); groupGroup->setTitle(i18n("Grou&p")); groupR = new KComboBox(groupGroup); groupR->addItem(i18n("?")); groupR->addItem(i18n("r")); groupR->addItem(i18n("-")); groupR->setEnabled(false); groupHBox->addWidget(groupR); groupW = new KComboBox(groupGroup); groupW->addItem(i18n("?")); groupW->addItem(i18n("w")); groupW->addItem(i18n("-")); groupW->setEnabled(false); groupHBox->addWidget(groupW); groupX = new KComboBox(groupGroup); groupX->addItem(i18n("?")); groupX->addItem(i18n("x")); groupX->addItem(i18n("-")); groupX->setEnabled(false); groupHBox->addWidget(groupX); ownershipLayout->addWidget(groupGroup, 1, 2); auto *allGroup = new QGroupBox(ownershipGroup); auto *allHBox = new QHBoxLayout(allGroup); allGroup->setTitle(i18n("A&ll")); allR = new KComboBox(allGroup); allR->addItem(i18n("?")); allR->addItem(i18n("r")); allR->addItem(i18n("-")); allR->setEnabled(false); allHBox->addWidget(allR); allW = new KComboBox(allGroup); allW->addItem(i18n("?")); allW->addItem(i18n("w")); allW->addItem(i18n("-")); allW->setEnabled(false); allHBox->addWidget(allW); allX = new KComboBox(allGroup); allX->addItem(i18n("?")); allX->addItem(i18n("x")); allX->addItem(i18n("-")); allX->setEnabled(false); allHBox->addWidget(allX); ownershipLayout->addWidget(allGroup, 1, 3); QLabel *infoLabel = new QLabel(ownershipGroup); QFont infoLabel_font(infoLabel->font()); infoLabel_font.setFamily("adobe-helvetica"); infoLabel_font.setItalic(true); infoLabel->setFont(infoLabel_font); infoLabel->setText(i18n("Note: a '?' is a wildcard")); ownershipLayout->addWidget(infoLabel, 2, 0, 1, 4, Qt::AlignRight); filterLayout->addWidget(ownershipGroup, 2, 0); // Connection table connect(minSizeEnabled, &QCheckBox::toggled, minSizeAmount, &QSpinBox::setEnabled); connect(minSizeEnabled, &QCheckBox::toggled, minSizeType, &KComboBox::setEnabled); connect(maxSizeEnabled, &QCheckBox::toggled, maxSizeAmount, &QSpinBox::setEnabled); connect(maxSizeEnabled, &QCheckBox::toggled, maxSizeType, &KComboBox::setEnabled); connect(modifiedBetweenEnabled, &QRadioButton::toggled, modifiedBetweenData1, &KLineEdit::setEnabled); connect(modifiedBetweenEnabled, &QRadioButton::toggled, modifiedBetweenBtn1, &QToolButton::setEnabled); connect(modifiedBetweenEnabled, &QRadioButton::toggled, modifiedBetweenData2, &KLineEdit::setEnabled); connect(modifiedBetweenEnabled, &QRadioButton::toggled, modifiedBetweenBtn2, &QToolButton::setEnabled); connect(notModifiedAfterEnabled, &QRadioButton::toggled, notModifiedAfterData, &KLineEdit::setEnabled); connect(notModifiedAfterEnabled, &QRadioButton::toggled, notModifiedAfterBtn, &QToolButton::setEnabled); connect(modifiedInTheLastEnabled, &QRadioButton::toggled, modifiedInTheLastData, &QSpinBox::setEnabled); connect(modifiedInTheLastEnabled, &QRadioButton::toggled, modifiedInTheLastType, &KComboBox::setEnabled); connect(modifiedInTheLastEnabled, &QRadioButton::toggled, notModifiedInTheLastData, &QSpinBox::setEnabled); connect(modifiedInTheLastEnabled, &QRadioButton::toggled, notModifiedInTheLastType, &KComboBox::setEnabled); connect(belongsToUserEnabled, &QCheckBox::toggled, belongsToUserData, &KComboBox::setEnabled); connect(belongsToGroupEnabled, &QCheckBox::toggled, belongsToGroupData, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, ownerR, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, ownerW, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, ownerX, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, groupR, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, groupW, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, groupX, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, allR, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, allW, &KComboBox::setEnabled); connect(permissionsEnabled, &QCheckBox::toggled, allX, &KComboBox::setEnabled); connect(modifiedBetweenBtn1, &QToolButton::clicked, this, &AdvancedFilter::modifiedBetweenSetDate1); connect(modifiedBetweenBtn2, &QToolButton::clicked, this, &AdvancedFilter::modifiedBetweenSetDate2); connect(notModifiedAfterBtn, &QToolButton::clicked, this, &AdvancedFilter::notModifiedAfterSetDate); // fill the users and groups list fillList(belongsToUserData, USERSFILE); fillList(belongsToGroupData, GROUPSFILE); // tab order setTabOrder(minSizeEnabled, minSizeAmount); setTabOrder(minSizeAmount, maxSizeEnabled); setTabOrder(maxSizeEnabled, maxSizeAmount); setTabOrder(maxSizeAmount, modifiedBetweenEnabled); setTabOrder(modifiedBetweenEnabled, modifiedBetweenData1); setTabOrder(modifiedBetweenData1, modifiedBetweenData2); setTabOrder(modifiedBetweenData2, notModifiedAfterEnabled); setTabOrder(notModifiedAfterEnabled, notModifiedAfterData); setTabOrder(notModifiedAfterData, modifiedInTheLastEnabled); setTabOrder(modifiedInTheLastEnabled, modifiedInTheLastData); setTabOrder(modifiedInTheLastData, notModifiedInTheLastData); setTabOrder(notModifiedInTheLastData, belongsToUserEnabled); setTabOrder(belongsToUserEnabled, belongsToUserData); setTabOrder(belongsToUserData, belongsToGroupEnabled); setTabOrder(belongsToGroupEnabled, belongsToGroupData); setTabOrder(belongsToGroupData, permissionsEnabled); setTabOrder(permissionsEnabled, ownerR); setTabOrder(ownerR, ownerW); setTabOrder(ownerW, ownerX); setTabOrder(ownerX, groupR); setTabOrder(groupR, groupW); setTabOrder(groupW, groupX); setTabOrder(groupX, allR); setTabOrder(allR, allW); setTabOrder(allW, allX); setTabOrder(allX, minSizeType); setTabOrder(minSizeType, maxSizeType); setTabOrder(maxSizeType, modifiedInTheLastType); setTabOrder(modifiedInTheLastType, notModifiedInTheLastType); } void AdvancedFilter::modifiedBetweenSetDate1() { changeDate(modifiedBetweenData1); } void AdvancedFilter::modifiedBetweenSetDate2() { changeDate(modifiedBetweenData2); } void AdvancedFilter::notModifiedAfterSetDate() { changeDate(notModifiedAfterData); } void AdvancedFilter::changeDate(KLineEdit *p) { // check if the current date is valid QDate d = stringToDate(p->text()); if (!d.isValid()) d = QDate::currentDate(); auto *gd = new KRGetDate(d, this); d = gd->getDate(); // if a user pressed ESC or closed the dialog, we'll return an invalid date if (d.isValid()) p->setText(dateToString(d)); delete gd; } -void AdvancedFilter::fillList(KComboBox *list, QString filename) +void AdvancedFilter::fillList(KComboBox *list, const QString& filename) { QFile data(filename); if (!data.open(QIODevice::ReadOnly)) { qWarning() << "Search: Unable to read " << filename << " !!!"; return; } // and read it into the temporary array QTextStream t(&data); while (!t.atEnd()) { QString s = t.readLine(); QString name = s.left(s.indexOf(':')); if (!name.startsWith('#')) list->addItem(name); } } void AdvancedFilter::invalidDateMessage(KLineEdit *p) { // FIXME p->text() is empty sometimes (to reproduce, set date to "13.09.005") KMessageBox::detailedError(this, i18n("Invalid date entered."), i18n("The date %1 is not valid according to your locale. Please re-enter a valid date (use the date button for easy access).", p->text())); p->setFocus(); } bool AdvancedFilter::getSettings(FilterSettings &s) { s.minSizeEnabled = minSizeEnabled->isChecked(); s.minSize.amount = minSizeAmount->value(); s.minSize.unit = static_cast(minSizeType->currentIndex()); s.maxSizeEnabled = maxSizeEnabled->isChecked(); s.maxSize.amount = maxSizeAmount->value(); s.maxSize.unit = static_cast(maxSizeType->currentIndex()); if (s.minSizeEnabled && s.maxSizeEnabled && (s.maxSize.size() < s.minSize.size())) { KMessageBox::detailedError(this, i18n("Specified sizes are inconsistent."), i18n("Please re-enter the values, so that the left side size " "will be smaller than (or equal to) the right side size.")); minSizeAmount->setFocus(); return false; } s.modifiedBetweenEnabled = modifiedBetweenEnabled->isChecked(); s.modifiedBetween1 = stringToDate(modifiedBetweenData1->text()); s.modifiedBetween2 = stringToDate(modifiedBetweenData2->text()); if (s.modifiedBetweenEnabled) { // check if date is valid if (!s.modifiedBetween1.isValid()) { invalidDateMessage(modifiedBetweenData1); return false; } else if (!s.modifiedBetween2.isValid()) { invalidDateMessage(modifiedBetweenData2); return false; } else if (s.modifiedBetween1 > s.modifiedBetween2) { KMessageBox::detailedError(this, i18n("Dates are inconsistent."), i18n("The date on the left is later than the date on the right. " "Please re-enter the dates, so that the left side date " "will be earlier than the right side date.")); modifiedBetweenData1->setFocus(); return false; } } s.notModifiedAfterEnabled = notModifiedAfterEnabled->isChecked(); s.notModifiedAfter = stringToDate(notModifiedAfterData->text()); if(s.notModifiedAfterEnabled && !s.notModifiedAfter.isValid()) { invalidDateMessage(notModifiedAfterData); return false; } s.modifiedInTheLastEnabled = modifiedInTheLastEnabled->isChecked(); s.modifiedInTheLast.amount = modifiedInTheLastData->value(); s.modifiedInTheLast.unit = static_cast(modifiedInTheLastType->currentIndex()); s.notModifiedInTheLast.amount = notModifiedInTheLastData->value(); s.notModifiedInTheLast.unit = static_cast(notModifiedInTheLastType->currentIndex()); if (s.modifiedInTheLastEnabled && s.modifiedInTheLast.amount && s.notModifiedInTheLast.amount) { if (s.modifiedInTheLast.days() < s.notModifiedInTheLast.days()) { KMessageBox::detailedError(this, i18n("Dates are inconsistent."), i18n("The date on top is later than the date on the bottom. " "Please re-enter the dates, so that the top date " "will be earlier than the bottom date.")); modifiedInTheLastData->setFocus(); return false; } } s.ownerEnabled = belongsToUserEnabled->isChecked(); s.owner = belongsToUserData->currentText(); s.groupEnabled = belongsToGroupEnabled->isChecked(); s.group = belongsToGroupData->currentText(); s.permissionsEnabled = permissionsEnabled->isChecked(); s.permissions = ownerR->currentText() + ownerW->currentText() + ownerX->currentText() + groupR->currentText() + groupW->currentText() + groupX->currentText() + allR->currentText() + allW->currentText() + allX->currentText(); return true; } void AdvancedFilter::applySettings(const FilterSettings &s) { minSizeEnabled->setChecked(s.minSizeEnabled); minSizeAmount->setValue(s.minSize.amount); minSizeType->setCurrentIndex(s.minSize.unit); maxSizeEnabled->setChecked(s.maxSizeEnabled); maxSizeAmount->setValue(s.maxSize.amount); maxSizeType->setCurrentIndex(s.maxSize.unit); if (s.modifiedBetweenEnabled) modifiedBetweenEnabled->setChecked(true); else if (s.notModifiedAfterEnabled) notModifiedAfterEnabled->setChecked(true); else if (s.modifiedInTheLastEnabled) modifiedInTheLastEnabled->setChecked(true); else anyDateEnabled->setChecked(true); modifiedBetweenData1->setText(dateToString(s.modifiedBetween1)); modifiedBetweenData2->setText(dateToString(s.modifiedBetween2)); notModifiedAfterData->setText(dateToString(s.notModifiedAfter)); modifiedInTheLastData->setValue(s.modifiedInTheLast.amount); modifiedInTheLastType->setCurrentIndex(s.modifiedInTheLast.unit); notModifiedInTheLastData->setValue(s.notModifiedInTheLast.amount); notModifiedInTheLastType->setCurrentIndex(s.notModifiedInTheLast.unit); belongsToUserEnabled->setChecked(s.ownerEnabled); setComboBoxValue(belongsToUserData, s.owner); belongsToGroupEnabled->setChecked(s.groupEnabled); setComboBoxValue(belongsToGroupData, s.group); permissionsEnabled->setChecked(s.permissionsEnabled); QString perm = s.permissions; if (perm.length() != 9) perm = "?????????"; setComboBoxValue(ownerR, QString(perm[0])); setComboBoxValue(ownerW, QString(perm[1])); setComboBoxValue(ownerX, QString(perm[2])); setComboBoxValue(groupR, QString(perm[3])); setComboBoxValue(groupW, QString(perm[4])); setComboBoxValue(groupX, QString(perm[5])); setComboBoxValue(allR, QString(perm[6])); setComboBoxValue(allW, QString(perm[7])); setComboBoxValue(allX, QString(perm[8])); } diff --git a/krusader/Filter/advancedfilter.h b/krusader/Filter/advancedfilter.h index 6f28b987..5e4f2d65 100644 --- a/krusader/Filter/advancedfilter.h +++ b/krusader/Filter/advancedfilter.h @@ -1,120 +1,120 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 ADVANCEDFILTER_H #define ADVANCEDFILTER_H #include "filterbase.h" // QtWidgets #include #include #include #include #include #include class QSpinBox; class AdvancedFilter : public QWidget, public FilterBase { Q_OBJECT public: explicit AdvancedFilter(FilterTabs *tabs, QWidget *parent = nullptr); void queryAccepted() Q_DECL_OVERRIDE {} QString name() Q_DECL_OVERRIDE { return "AdvancedFilter"; } FilterTabs * filterTabs() Q_DECL_OVERRIDE { return fltTabs; } bool getSettings(FilterSettings&) Q_DECL_OVERRIDE; void applySettings(const FilterSettings&) Q_DECL_OVERRIDE; public slots: void modifiedBetweenSetDate1(); void modifiedBetweenSetDate2(); void notModifiedAfterSetDate(); public: QCheckBox* minSizeEnabled; QSpinBox* minSizeAmount; KComboBox* minSizeType; QCheckBox* maxSizeEnabled; QSpinBox* maxSizeAmount; KComboBox* maxSizeType; QRadioButton* anyDateEnabled; QRadioButton* modifiedBetweenEnabled; QRadioButton* notModifiedAfterEnabled; QRadioButton* modifiedInTheLastEnabled; KLineEdit* modifiedBetweenData1; KLineEdit* modifiedBetweenData2; QToolButton* modifiedBetweenBtn1; QToolButton* modifiedBetweenBtn2; QToolButton* notModifiedAfterBtn; KLineEdit* notModifiedAfterData; QSpinBox* modifiedInTheLastData; QSpinBox* notModifiedInTheLastData; KComboBox* modifiedInTheLastType; KComboBox* notModifiedInTheLastType; QCheckBox* belongsToUserEnabled; KComboBox* belongsToUserData; QCheckBox* belongsToGroupEnabled; KComboBox* belongsToGroupData; QCheckBox* permissionsEnabled; KComboBox* ownerW; KComboBox* ownerR; KComboBox* ownerX; KComboBox* groupW; KComboBox* groupR; KComboBox* groupX; KComboBox* allW; KComboBox* allX; KComboBox* allR; FilterTabs *fltTabs; private: void changeDate(KLineEdit *p); - void fillList(KComboBox *list, QString filename); + void fillList(KComboBox *list, const QString& filename); void invalidDateMessage(KLineEdit *p); static QDate stringToDate(const QString& text) { // 30.12.16 is interpreted as 1916-12-30 return QLocale().toDate(text, QLocale::ShortFormat).addYears(100); } static QString dateToString(const QDate& date) { return QLocale().toString(date, QLocale::ShortFormat); } }; #endif /* ADVANCEDFILTER_H */ diff --git a/krusader/Filter/filterdialog.cpp b/krusader/Filter/filterdialog.cpp index 74c49d79..69ac336d 100644 --- a/krusader/Filter/filterdialog.cpp +++ b/krusader/Filter/filterdialog.cpp @@ -1,106 +1,107 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * * Copyright (C) 2005-2018 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 "filterdialog.h" #include "filtertabs.h" #include "generalfilter.h" // QtWidgets #include #include #include #include +#include -FilterDialog::FilterDialog(QWidget *parent, QString caption, QStringList extraOptions, bool modal) +FilterDialog::FilterDialog(QWidget *parent, const QString& caption, QStringList extraOptions, bool modal) : QDialog(parent) { setWindowTitle(caption.isNull() ? i18n("Krusader::Choose Files") : caption); setModal(modal); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *filterWidget = new QTabWidget; - filterTabs = FilterTabs::addTo(filterWidget, FilterTabs::HasProfileHandler, extraOptions); + filterTabs = FilterTabs::addTo(filterWidget, FilterTabs::HasProfileHandler, std::move(extraOptions)); generalFilter = static_cast (filterTabs->get("GeneralFilter")); mainLayout->addWidget(filterWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Reset); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &FilterDialog::slotOk); connect(buttonBox, &QDialogButtonBox::rejected, this, &FilterDialog::reject); connect(buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, this, &FilterDialog::slotReset); connect(filterTabs, &FilterTabs::closeRequest, this, &FilterDialog::slotCloseRequest); generalFilter->searchFor->setFocus(); if(modal) exec(); } void FilterDialog::applySettings(const FilterSettings &s) { filterTabs->applySettings(s); } KRQuery FilterDialog::getQuery() { return settings.toQuery(); } bool FilterDialog::isExtraOptionChecked(QString name) { - return filterTabs->isExtraOptionChecked(name); + return filterTabs->isExtraOptionChecked(std::move(name)); } void FilterDialog::checkExtraOption(QString name, bool check) { - filterTabs->checkExtraOption(name, check); + filterTabs->checkExtraOption(std::move(name), check); } void FilterDialog::slotCloseRequest(bool doAccept) { if (doAccept) { slotOk(); accept(); } else reject(); } void FilterDialog::slotReset() { filterTabs->reset(); generalFilter->searchFor->setFocus(); } void FilterDialog::slotOk() { settings = filterTabs->getSettings(); if(settings.isValid()) accept(); } diff --git a/krusader/Filter/filterdialog.h b/krusader/Filter/filterdialog.h index 9f1416df..a62f0279 100644 --- a/krusader/Filter/filterdialog.h +++ b/krusader/Filter/filterdialog.h @@ -1,59 +1,59 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * * Copyright (C) 2005-2018 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 FILTERDIALOG_H #define FILTERDIALOG_H #include "filtersettings.h" #include "../FileSystem/krquery.h" // QtWidgets #include class FilterTabs; class GeneralFilter; class FilterDialog : public QDialog { Q_OBJECT public: - explicit FilterDialog(QWidget *parent = nullptr, QString caption = QString(), + explicit FilterDialog(QWidget *parent = nullptr, const QString& caption = QString(), QStringList extraOptions = QStringList(), bool modal = true); KRQuery getQuery(); const FilterSettings& getSettings() { return settings; } void applySettings(const FilterSettings &s); bool isExtraOptionChecked(QString name); void checkExtraOption(QString name, bool check); public slots: void slotCloseRequest(bool doAccept); void slotReset(); void slotOk(); private: FilterTabs * filterTabs; GeneralFilter * generalFilter; FilterSettings settings; }; #endif /* FILTERDIALOG_H */ diff --git a/krusader/Filter/filtersettings.cpp b/krusader/Filter/filtersettings.cpp index e09a8466..d5789902 100644 --- a/krusader/Filter/filtersettings.cpp +++ b/krusader/Filter/filtersettings.cpp @@ -1,321 +1,321 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2011 by Jan Lepper * * Copyright (C) 2004-2018 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 "filtersettings.h" #include "../krservices.h" #include #include FilterSettings::FileSize& FilterSettings::FileSize::operator=(const FileSize &other) = default; KIO::filesize_t FilterSettings::FileSize::size() const { switch (unit) { case Byte: return amount; case KiloByte: return amount * 1024; case MegaByte: return amount * 1024 * 1024; case GigaByte: return amount * 1024 * 1024 * 1024; default: qWarning() << "invalid size unit: " << unit; return amount; } } FilterSettings::TimeSpan& FilterSettings::TimeSpan::operator=(const TimeSpan &other) = default; int FilterSettings::TimeSpan::days() const { switch (unit) { case Day: return amount; case Week: return amount * 7; case Month: return amount * 30; case Year: return amount * 365; default: qWarning() << "invalid time unit: " << unit; return amount; } } FilterSettings::FilterSettings() : valid(false), searchFor("*"), searchForCase(false), searchInArchives(false), recursive(false), followLinks(false), containsTextCase(false), containsWholeWord(false), containsRegExp(false), minSizeEnabled(false), maxSizeEnabled(false), modifiedBetweenEnabled(false), notModifiedAfterEnabled(false), modifiedInTheLastEnabled(false), ownerEnabled(false), groupEnabled(false), permissionsEnabled(false) { } FilterSettings& FilterSettings::operator=(const FilterSettings& other) { #define COPY(var) { var = other.var; } COPY(valid); COPY(searchFor); COPY(searchForCase); COPY(mimeType); COPY(searchInArchives); COPY(recursive); COPY(followLinks); COPY(searchIn); COPY(dontSearchIn); COPY(excludeFolderNames); COPY(contentEncoding); COPY(containsText); COPY(containsTextCase); COPY(containsWholeWord); COPY(containsRegExp); COPY(minSizeEnabled); COPY(minSize); COPY(maxSizeEnabled); COPY(maxSize); COPY(modifiedBetweenEnabled); COPY(modifiedBetween1); COPY(modifiedBetween2); COPY(notModifiedAfterEnabled); COPY(notModifiedAfter); COPY(modifiedInTheLastEnabled); COPY(modifiedInTheLast); COPY(notModifiedInTheLast); COPY(ownerEnabled); COPY(owner); COPY(groupEnabled); COPY(group); COPY(permissionsEnabled); COPY(permissions); #undef COPY return *this; } -void FilterSettings::load(KConfigGroup cfg) { +void FilterSettings::load(const KConfigGroup& cfg) { *this = FilterSettings(); #define LOAD(key, var) { var = cfg.readEntry(key, var); } LOAD("IsValid", valid); if(!isValid()) return; LOAD("SearchFor", searchFor); LOAD("MimeType", mimeType); LOAD("SearchInArchives", searchInArchives); LOAD("Recursive", recursive); LOAD("FollowLinks", followLinks); searchIn = KrServices::toUrlList(cfg.readEntry("SearchIn", QStringList())); dontSearchIn = KrServices::toUrlList(cfg.readEntry("DontSearchIn", QStringList())); excludeFolderNames = QStringList(); LOAD("ContentEncoding", contentEncoding); LOAD("ContainsText", containsText); LOAD("ContainsTextCase", containsTextCase); LOAD("ContainsWholeWord", containsWholeWord); LOAD("ContainsRegExp", containsRegExp); LOAD("MinSizeEnabled", minSizeEnabled); LOAD("MinSizeAmount", minSize.amount); minSize.unit = static_cast(cfg.readEntry("MinSizeUnit", 0)); LOAD("MaxSizeEnabled", maxSizeEnabled); LOAD("MaxSizeAmount", maxSize.amount); maxSize.unit = static_cast(cfg.readEntry("MaxSizeUnit", 0)); LOAD("ModifiedBetweenEnabled", modifiedBetweenEnabled); LOAD("ModifiedBetween1", modifiedBetween1); LOAD("ModifiedBetween2", modifiedBetween2); LOAD("NotModifiedAfterEnabled", notModifiedAfterEnabled); LOAD("NotModifiedAfter", notModifiedAfter); LOAD("ModifiedInTheLastEnabled", modifiedInTheLastEnabled); LOAD("ModifiedInTheLastAmount", modifiedInTheLast.amount); modifiedInTheLast.unit = static_cast(cfg.readEntry("ModifiedInTheLastUnit", 0)); LOAD("NotModifiedInTheLastAmount", notModifiedInTheLast.amount); notModifiedInTheLast.unit = static_cast(cfg.readEntry("NotModifiedInTheLastUnit", 0)); LOAD("OwnerEnabled", ownerEnabled); LOAD("Owner", owner); LOAD("GroupEnabled", groupEnabled); LOAD("Group", group); LOAD("PermissionsEnabled", permissionsEnabled); LOAD("Permissions", permissions); #undef LOAD } -void FilterSettings::saveDate(QString key, const QDate &date, KConfigGroup &cfg) +void FilterSettings::saveDate(const QString& key, const QDate &date, KConfigGroup &cfg) { if(date.isValid()) cfg.writeEntry(key, date); else cfg.deleteEntry(key); } void FilterSettings::save(KConfigGroup cfg) const { cfg.writeEntry("IsValid", valid); if(!isValid()) return; cfg.writeEntry("SearchFor", searchFor); cfg.writeEntry("MimeType", mimeType); cfg.writeEntry("SearchInArchives", searchInArchives); cfg.writeEntry("Recursive", recursive); cfg.writeEntry("FollowLinks", followLinks); cfg.writeEntry("SearchIn", KrServices::toStringList(searchIn)); cfg.writeEntry("DontSearchIn", KrServices::toStringList(dontSearchIn)); cfg.writeEntry("ContentEncoding", contentEncoding); cfg.writeEntry("ContainsText", containsText); cfg.writeEntry("ContainsTextCase", containsTextCase); cfg.writeEntry("ContainsWholeWord", containsWholeWord); cfg.writeEntry("ContainsRegExp", containsRegExp); cfg.writeEntry("MinSizeEnabled", minSizeEnabled); cfg.writeEntry("MinSizeAmount", minSize.amount); cfg.writeEntry("MinSizeUnit", static_cast(minSize.unit)); cfg.writeEntry("MaxSizeEnabled", maxSizeEnabled); cfg.writeEntry("MaxSizeAmount", maxSize.amount); cfg.writeEntry("MaxSizeUnit", static_cast(maxSize.unit)); cfg.writeEntry("ModifiedBetweenEnabled", modifiedBetweenEnabled); saveDate("ModifiedBetween1", modifiedBetween1, cfg); saveDate("ModifiedBetween2", modifiedBetween2, cfg); cfg.writeEntry("NotModifiedAfterEnabled", notModifiedAfterEnabled); saveDate("NotModifiedAfter", notModifiedAfter, cfg); cfg.writeEntry("ModifiedInTheLastEnabled", modifiedInTheLastEnabled); cfg.writeEntry("ModifiedInTheLastAmount", modifiedInTheLast.amount); cfg.writeEntry("ModifiedInTheLastUnit", static_cast(modifiedInTheLast.unit)); cfg.writeEntry("NotModifiedInTheLastAmount", notModifiedInTheLast.amount); cfg.writeEntry("NotModifiedInTheLastUnit", static_cast(notModifiedInTheLast.unit)); cfg.writeEntry("OwnerEnabled", ownerEnabled); cfg.writeEntry("Owner", owner); cfg.writeEntry("GroupEnabled", groupEnabled); cfg.writeEntry("Group", group); cfg.writeEntry("PermissionsEnabled", permissionsEnabled); cfg.writeEntry("Permissions", permissions); } // bool start: set it to true if this date is the beginning of the search, // if it's the end, set it to false time_t FilterSettings::qdate2time_t (QDate d, bool start) { struct tm t; t.tm_sec = (start ? 0 : 59); t.tm_min = (start ? 0 : 59); t.tm_hour = (start ? 0 : 23); t.tm_mday = d.day(); t.tm_mon = d.month() - 1; t.tm_year = d.year() - 1900; t.tm_wday = d.dayOfWeek() - 1; // actually ignored by mktime t.tm_yday = d.dayOfYear() - 1; // actually ignored by mktime t.tm_isdst = -1; // daylight saving time information isn't available return mktime(&t); } KRQuery FilterSettings::toQuery() const { if(!isValid()) return KRQuery(); KRQuery query; ////////////// General Options ////////////// query.setNameFilter(searchFor, searchForCase); query.setMimeType(mimeType); QString charset; if (!contentEncoding.isEmpty()) charset = KCharsets::charsets()->encodingForName(contentEncoding); if (!containsText.isEmpty()) { query.setContent(containsText, containsTextCase, containsWholeWord, charset, containsRegExp); } query.setRecursive(recursive); query.setSearchInArchives(searchInArchives); query.setFollowLinks(followLinks); if (!searchIn.isEmpty()) query.setSearchInDirs(searchIn); if (!dontSearchIn.isEmpty()) query.setDontSearchInDirs(dontSearchIn); query.setExcludeFolderNames(excludeFolderNames); ////////////// Advanced Options ////////////// if (minSizeEnabled) query.setMinimumFileSize(minSize.size()); if (maxSizeEnabled) query.setMaximumFileSize(maxSize.size()); if (modifiedBetweenEnabled) { if(modifiedBetween1.isValid()) query.setNewerThan(qdate2time_t(modifiedBetween1, true)); if(modifiedBetween2.isValid()) query.setOlderThan(qdate2time_t(modifiedBetween2, false)); } else if (notModifiedAfterEnabled && notModifiedAfter.isValid()) { query.setOlderThan(qdate2time_t(notModifiedAfter, false)); } else if (modifiedInTheLastEnabled) { if (modifiedInTheLast.amount) { QDate d = QDate::currentDate().addDays((-1) * modifiedInTheLast.days()); query.setNewerThan(qdate2time_t(d, true)); } if (notModifiedInTheLast.amount) { QDate d = QDate::currentDate().addDays((-1) * notModifiedInTheLast.days()); query.setOlderThan(qdate2time_t(d, true)); } } if (ownerEnabled && !owner.isEmpty()) query.setOwner(owner); if (groupEnabled && !group.isEmpty()) query.setGroup(group); if (permissionsEnabled && !permissions.isEmpty()) query.setPermissions(permissions); return query; } diff --git a/krusader/Filter/filtersettings.h b/krusader/Filter/filtersettings.h index 3732940f..a40d1ca2 100644 --- a/krusader/Filter/filtersettings.h +++ b/krusader/Filter/filtersettings.h @@ -1,117 +1,117 @@ /***************************************************************************** * Copyright (C) 2011 Jan Lepper * * Copyright (C) 2011-2018 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 FILTERSETTINGS_H #define FILTERSETTINGS_H #include "../FileSystem/krquery.h" #include class FilterSettings { friend class FilterTabs; friend class GeneralFilter; friend class AdvancedFilter; public: FilterSettings(); FilterSettings& operator=(const FilterSettings& other); bool isValid() const { return valid; } - void load(KConfigGroup cfg); + void load(const KConfigGroup& cfg); void save(KConfigGroup cfg) const; KRQuery toQuery() const; private: enum SizeUnit { Byte = 0, KiloByte, MegaByte, GigaByte }; enum TimeUnit { Day = 0, Week, Month, Year }; class FileSize { public: FileSize() : amount(0), unit(Byte) {} FileSize& operator=(const FileSize &other); KIO::filesize_t size() const; KIO::filesize_t amount; SizeUnit unit; }; class TimeSpan { public: TimeSpan() : amount(0), unit(Day) {} TimeSpan& operator=(const TimeSpan &other); int days() const; int amount; TimeUnit unit; }; - static void saveDate(QString key, const QDate &date, KConfigGroup &cfg); + static void saveDate(const QString& key, const QDate &date, KConfigGroup &cfg); static time_t qdate2time_t(QDate d, bool start); bool valid; QString searchFor; bool searchForCase; QString mimeType; bool searchInArchives; bool recursive; bool followLinks; QList searchIn; QList dontSearchIn; QStringList excludeFolderNames; QString contentEncoding; QString containsText; bool containsTextCase; bool containsWholeWord; bool containsRegExp; bool minSizeEnabled; FileSize minSize; bool maxSizeEnabled; FileSize maxSize; bool modifiedBetweenEnabled; QDate modifiedBetween1; QDate modifiedBetween2; bool notModifiedAfterEnabled; QDate notModifiedAfter; bool modifiedInTheLastEnabled; TimeSpan modifiedInTheLast; TimeSpan notModifiedInTheLast; bool ownerEnabled; QString owner; bool groupEnabled; QString group; bool permissionsEnabled; QString permissions; }; #endif //FILTERSETTINGS_H diff --git a/krusader/Filter/filtertabs.cpp b/krusader/Filter/filtertabs.cpp index 064e767b..1f81548f 100644 --- a/krusader/Filter/filtertabs.cpp +++ b/krusader/Filter/filtertabs.cpp @@ -1,155 +1,156 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * * Copyright (C) 2005-2018 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 "filtertabs.h" #include "filterdialog.h" #include "generalfilter.h" #include "advancedfilter.h" #include "../krglobal.h" // QtWidgets #include #include #include #include +#include FilterTabs::FilterTabs(int properties, QTabWidget *tabWidget, QObject *parent, QStringList extraOptions) : QObject(parent) { this->tabWidget = tabWidget; - GeneralFilter *generalFilter = new GeneralFilter(this, properties, tabWidget, extraOptions); + GeneralFilter *generalFilter = new GeneralFilter(this, properties, tabWidget, std::move(extraOptions)); tabWidget->addTab(generalFilter, i18n("&General")); filterList.append(generalFilter); pageNumbers.append(tabWidget->indexOf(generalFilter)); auto *advancedFilter = new AdvancedFilter(this, tabWidget); tabWidget->addTab(advancedFilter, i18n("&Advanced")); filterList.append(advancedFilter); pageNumbers.append(tabWidget->indexOf(advancedFilter)); reset(); // apply defaults } bool FilterTabs::isExtraOptionChecked(QString name) { - return static_cast(get("GeneralFilter"))->isExtraOptionChecked(name); + return static_cast(get("GeneralFilter"))->isExtraOptionChecked(std::move(name)); } void FilterTabs::checkExtraOption(QString name, bool check) { - static_cast(get("GeneralFilter"))->checkExtraOption(name, check); + static_cast(get("GeneralFilter"))->checkExtraOption(std::move(name), check); } FilterTabs * FilterTabs::addTo(QTabWidget *tabWidget, int props, QStringList extraOptions) { - return new FilterTabs(props, tabWidget, tabWidget, extraOptions); + return new FilterTabs(props, tabWidget, tabWidget, std::move(extraOptions)); } FilterSettings FilterTabs::getSettings() { FilterSettings s; for (int i = 0; i != filterList.count(); i++) { if(!filterList[i]->getSettings(s)) { tabWidget->setCurrentIndex(pageNumbers[i]); return FilterSettings(); } } s.valid = true; acceptQuery(); return s; } void FilterTabs::applySettings(const FilterSettings &s) { if(s.isValid()) { QListIterator it(filterList); while (it.hasNext()) it.next()->applySettings(s); } } void FilterTabs::reset() { FilterSettings s; // default settings s.valid = true; applySettings(s); } -void FilterTabs::saveToProfile(QString name) +void FilterTabs::saveToProfile(const QString& name) { FilterSettings s(getSettings()); if(s.isValid()) s.save(KConfigGroup(krConfig, name)); krConfig->sync(); } -void FilterTabs::loadFromProfile(QString name) +void FilterTabs::loadFromProfile(const QString& name) { FilterSettings s; s.load(KConfigGroup(krConfig, name)); if(!s.isValid()) KMessageBox::error(tabWidget, i18n("Could not load profile.")); else applySettings(s); } void FilterTabs::acceptQuery() { QListIterator it(filterList); while (it.hasNext()) { FilterBase *filter = it.next(); filter->queryAccepted(); } } bool FilterTabs::fillQuery(KRQuery *query) { *query = getSettings().toQuery(); return !query->isNull(); } -FilterBase * FilterTabs::get(QString name) +FilterBase * FilterTabs::get(const QString& name) { QListIterator it(filterList); while (it.hasNext()) { FilterBase *filter = it.next(); if (filter->name() == name) return filter; } return nullptr; } KRQuery FilterTabs::getQuery(QWidget *parent) { FilterDialog dialog(parent); return dialog.getQuery(); } diff --git a/krusader/Filter/filtertabs.h b/krusader/Filter/filtertabs.h index 9de471f0..7cc9312c 100644 --- a/krusader/Filter/filtertabs.h +++ b/krusader/Filter/filtertabs.h @@ -1,78 +1,78 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * * Copyright (C) 2005-2018 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 FILTERTABS_H #define FILTERTABS_H // QtCore #include #include "filterbase.h" class QTabWidget; class FilterTabs : public QObject { Q_OBJECT public: enum { HasProfileHandler = 0x1000, HasRecurseOptions = 0x2000, HasSearchIn = 0x4000, HasDontSearchIn = 0x8000, Default = 0xe000 }; static FilterTabs * addTo(QTabWidget *tabWidget, int props = FilterTabs::Default, QStringList extraOptions = QStringList()); static KRQuery getQuery(QWidget *parent = nullptr); - FilterBase *get(QString name); + FilterBase *get(const QString& name); bool isExtraOptionChecked(QString name); void checkExtraOption(QString name, bool check); FilterSettings getSettings(); void applySettings(const FilterSettings &s); void reset(); public slots: - void loadFromProfile(QString); - void saveToProfile(QString); + void loadFromProfile(const QString&); + void saveToProfile(const QString&); bool fillQuery(KRQuery *query); void close(bool accept = true) { emit closeRequest(accept); } signals: void closeRequest(bool accept = true); private: FilterTabs(int properties, QTabWidget *tabWidget, QObject *parent, QStringList extraOptions); void acceptQuery(); QList filterList; QList pageNumbers; QTabWidget * tabWidget; }; #endif /* FILTERTABS_H */ diff --git a/krusader/Filter/generalfilter.cpp b/krusader/Filter/generalfilter.cpp index beeb589b..4cd0b0ab 100644 --- a/krusader/Filter/generalfilter.cpp +++ b/krusader/Filter/generalfilter.cpp @@ -1,666 +1,666 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "generalfilter.h" #include "filtertabs.h" #include "../krglobal.h" #include "../krservices.h" #include "../FileSystem/filesystem.h" // QtWidgets #include #include #include #include #include #include #include #include #include #include typedef struct { const char *description; const char *regExp; int cursorAdjustment; } term; static const term items[] = { { I18N_NOOP("Any Character"), ".", 0 }, { I18N_NOOP("Start of Line"), "^", 0 }, { I18N_NOOP("End of Line"), "$", 0 }, { I18N_NOOP("Set of Characters"), "[]", -1 }, { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 }, { I18N_NOOP("Repeats, One or More Times"), "+", 0 }, { I18N_NOOP("Optional"), "?", 0 }, { I18N_NOOP("Escape"), "\\", 0 }, { I18N_NOOP("TAB"), "\\t", 0 }, { I18N_NOOP("Newline"), "\\n", 0 }, { I18N_NOOP("Carriage Return"), "\\r", 0 }, { I18N_NOOP("White Space"), "\\s", 0 }, { I18N_NOOP("Digit"), "\\d", 0 }, }; class RegExpAction : public QAction { public: RegExpAction(QObject *parent, const QString &text, const QString ®Exp, int cursor) : QAction(text, parent), mText(text), mRegExp(regExp), mCursor(cursor) { } QString text() const { return mText; } QString regExp() const { return mRegExp; } int cursor() const { return mCursor; } private: QString mText; QString mRegExp; int mCursor; }; GeneralFilter::GeneralFilter(FilterTabs *tabs, int properties, QWidget *parent, QStringList extraOptions) : QWidget(parent), profileManager(nullptr), fltTabs(tabs) { auto *filterLayout = new QGridLayout(this); filterLayout->setSpacing(6); filterLayout->setContentsMargins(11, 11, 11, 11); this->properties = properties; // Options for name filtering auto *nameGroup = new QGroupBox(this); nameGroup->setTitle(i18n("File Name")); auto *nameGroupLayout = new QGridLayout(nameGroup); nameGroupLayout->setAlignment(Qt::AlignTop); nameGroupLayout->setSpacing(6); nameGroupLayout->setContentsMargins(11, 11, 11, 11); searchForCase = new QCheckBox(nameGroup); searchForCase->setText(i18n("&Case sensitive")); searchForCase->setChecked(false); nameGroupLayout->addWidget(searchForCase, 1, 2); QLabel *searchForLabel = new QLabel(nameGroup); searchForLabel->setText(i18n("Search &for:")); nameGroupLayout->addWidget(searchForLabel, 0, 0); searchFor = new KHistoryComboBox(false, nameGroup/*, "searchFor"*/); QSizePolicy searchForPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); searchForPolicy.setHeightForWidth(searchFor->sizePolicy().hasHeightForWidth()); searchFor->setSizePolicy(searchForPolicy); searchFor->setEditable(true); searchFor->setDuplicatesEnabled(false); searchFor->setMaxCount(25); searchFor->setMinimumContentsLength(10); searchForLabel->setBuddy(searchFor); nameGroupLayout->addWidget(searchFor, 0, 1, 1, 2); const QString s = "

" + i18n("

The filename filtering criteria is defined here.

" "

You can make use of wildcards. Multiple patterns are separated by " "space (means logical OR) and patterns are excluded from the search " "using the pipe symbol.

" "

If the pattern is ended with a slash (*pattern*/), " "that means that pattern relates to recursive search of folders." "

  • pattern - means to search those files/folders " "that name is pattern, recursive search goes through all " "subfolders independently of the value of pattern
  • " "
  • pattern/ - means to search all files/folders, but " "recursive search goes through/excludes the folders that name is " "pattern

" "

It is allowed to use quotation marks for names that contain space." " Filter \"Program Files\" searches out those " "files/folders that name is Program Files.

" "

Examples:

    " "
  • *.o
  • " "
  • *.h *.c\?\?
  • " "
  • *.cpp *.h | *.moc.cpp
  • " "
  • * | .svn/ .git/

" "Note: the search term 'text' is equivalent to " "'*text*'.

"); searchFor->setWhatsThis(s); searchForLabel->setWhatsThis(s); QLabel *searchType = new QLabel(nameGroup); searchType->setText(i18n("&Of type:")); nameGroupLayout->addWidget(searchType, 1, 0); ofType = new KComboBox(false, nameGroup); ofType->setObjectName("ofType"); QSizePolicy ofTypePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); ofTypePolicy.setHeightForWidth(ofType->sizePolicy().hasHeightForWidth()); ofType->setSizePolicy(ofTypePolicy); ofType->setEditable(false); searchType->setBuddy(ofType); ofType->addItem(i18n("All Files")); ofType->addItem(i18n("Archives")); ofType->addItem(i18n("Folders")); ofType->addItem(i18n("Image Files")); ofType->addItem(i18n("Text Files")); ofType->addItem(i18n("Video Files")); ofType->addItem(i18n("Audio Files")); connect(ofType, QOverload::of(&KComboBox::currentIndexChanged), this, &GeneralFilter::slotDisable); nameGroupLayout->addWidget(ofType, 1, 1); filterLayout->addWidget(nameGroup, 0, 0); middleLayout = new QHBoxLayout(); middleLayout->setSpacing(6); middleLayout->setContentsMargins(0, 0, 0, 0); if (properties & FilterTabs::HasProfileHandler) { // The profile handler auto *profileHandler = new QGroupBox(this); profileHandler->setTitle(i18n("&Profile handler")); auto *profileLayout = new QGridLayout(profileHandler); profileLayout->setAlignment(Qt::AlignTop); profileLayout->setSpacing(6); profileLayout->setContentsMargins(11, 11, 11, 11); profileListBox = new KrListWidget(profileHandler); profileLayout->addWidget(profileListBox, 0, 0, 4, 1); profileAddBtn = new QPushButton(profileHandler); KStandardGuiItem::assign(profileAddBtn, KStandardGuiItem::Add); profileLayout->addWidget(profileAddBtn, 0, 1); profileLoadBtn = new QPushButton(i18n("&Load"), profileHandler); profileLoadBtn->setEnabled(false); profileLayout->addWidget(profileLoadBtn, 1, 1); profileOverwriteBtn = new QPushButton(profileHandler); profileOverwriteBtn->setEnabled(false); KStandardGuiItem::assign(profileOverwriteBtn, KStandardGuiItem::Overwrite); profileLayout->addWidget(profileOverwriteBtn, 2, 1); profileRemoveBtn = new QPushButton(profileHandler); profileRemoveBtn->setEnabled(false); KStandardGuiItem::assign(profileRemoveBtn, KStandardGuiItem::Remove); profileLayout->addWidget(profileRemoveBtn, 3, 1); profileManager = new ProfileManager("SelectionProfile", this); profileManager->hide(); middleLayout->addWidget(profileHandler); refreshProfileListBox(); } if (properties & FilterTabs::HasSearchIn) { // Options for search in QGroupBox *searchGroupBox = new QGroupBox(i18n("Searc&h in"), this); auto *searchLayout = new QGridLayout(searchGroupBox); searchLayout->setAlignment(Qt::AlignTop); searchLayout->setSpacing(6); searchLayout->setContentsMargins(11, 11, 11, 11); searchIn = new KURLListRequester(KURLListRequester::RequestDirs, searchGroupBox); searchLayout->addWidget(searchIn, 0, 0); connect(searchIn, &KURLListRequester::changed, this, &GeneralFilter::slotDisable); middleLayout->addWidget(searchGroupBox); } if (properties & FilterTabs::HasDontSearchIn) { // Options for don't search in QGroupBox *searchGroupBox = new QGroupBox(i18n("&Do not search in"), this); auto *searchLayout = new QGridLayout(searchGroupBox); searchLayout->setAlignment(Qt::AlignTop); searchLayout->setSpacing(6); searchLayout->setContentsMargins(11, 11, 11, 11); dontSearchIn = new KURLListRequester(KURLListRequester::RequestDirs, searchGroupBox); searchLayout->addWidget(dontSearchIn, 0, 0, 1, 2); if (properties & FilterTabs::HasRecurseOptions) { KConfigGroup group(krConfig, "Search"); useExcludeFolderNames = createExcludeCheckBox(group); searchLayout->addWidget(useExcludeFolderNames, 1, 0, 1, 1); excludeFolderNames = createExcludeComboBox(group); searchLayout->addWidget(excludeFolderNames, 1, 1, 1, 1); if (!useExcludeFolderNames->isChecked()) { excludeFolderNames->setDisabled(true); } connect(useExcludeFolderNames, &QCheckBox::toggled, excludeFolderNames, &KHistoryComboBox::setEnabled); } middleLayout->addWidget(searchGroupBox); } filterLayout->addLayout(middleLayout, 1, 0); // Options for containing text auto *containsGroup = new QGroupBox(this); containsGroup->setTitle(i18n("Containing text")); auto *containsLayout = new QGridLayout(containsGroup); containsLayout->setAlignment(Qt::AlignTop); containsLayout->setSpacing(6); containsLayout->setContentsMargins(11, 11, 11, 11); auto *containsTextLayout = new QHBoxLayout(); containsTextLayout->setSpacing(6); containsTextLayout->setContentsMargins(0, 0, 0, 0); containsLabel = new QLabel(containsGroup); QSizePolicy containsLabelPolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); containsLabelPolicy.setHeightForWidth(containsLabel->sizePolicy().hasHeightForWidth()); containsLabel->setSizePolicy(containsLabelPolicy); containsLabel->setText(i18n("&Text:")); containsTextLayout->addWidget(containsLabel); containsText = new KHistoryComboBox(false, containsGroup/*, "containsText"*/); QSizePolicy containsTextPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); containsTextPolicy.setHeightForWidth(containsText->sizePolicy().hasHeightForWidth()); containsText->setSizePolicy(containsTextPolicy); containsText->setDuplicatesEnabled(false); containsText->setMaxCount(25); containsText->setMinimumContentsLength(10); containsTextLayout->addWidget(containsText); containsLabel->setBuddy(containsText); containsRegExp = new QToolButton(containsGroup); containsRegExp->setPopupMode(QToolButton::MenuButtonPopup); containsRegExp->setCheckable(true); containsRegExp->setText(i18n("RegExp")); // Populate the popup menu. auto *patterns = new QMenu(containsRegExp); for (int i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++) { patterns->addAction(new RegExpAction(patterns, i18n(items[i].description), items[i].regExp, items[i].cursorAdjustment)); } connect(containsRegExp, &QToolButton::toggled, this, &GeneralFilter::slotDisable); connect(containsRegExp, &QToolButton::triggered, this, &GeneralFilter::slotRegExpTriggered); containsRegExp->setMenu(patterns); patterns->setEnabled(false); containsTextLayout->addWidget(containsRegExp); containsLayout->addLayout(containsTextLayout, 0, 0); auto *containsCbsLayout = new QHBoxLayout(); containsCbsLayout->setSpacing(6); containsCbsLayout->setContentsMargins(0, 0, 0, 0); encLabel = new QLabel(i18n("Encoding:"), containsGroup); containsCbsLayout->addWidget(encLabel); contentEncoding = new KComboBox(containsGroup); contentEncoding->setEditable(false); contentEncoding->addItem(i18nc("Default encoding", "Default")); contentEncoding->addItems(KCharsets::charsets()->descriptiveEncodingNames()); containsCbsLayout->addWidget(contentEncoding); auto* cbSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); containsCbsLayout->addItem(cbSpacer); containsWholeWord = new QCheckBox(containsGroup); QSizePolicy containsWholeWordPolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); containsWholeWordPolicy.setHeightForWidth(containsWholeWord->sizePolicy().hasHeightForWidth()); containsWholeWord->setSizePolicy(containsWholeWordPolicy); containsWholeWord->setText(i18n("&Match whole word only")); containsWholeWord->setChecked(false); containsCbsLayout->addWidget(containsWholeWord); containsTextCase = new QCheckBox(containsGroup); QSizePolicy containsTextCasePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); containsTextCasePolicy.setHeightForWidth(containsTextCase->sizePolicy().hasHeightForWidth()); containsTextCase->setSizePolicy(containsTextCasePolicy); containsTextCase->setText(i18n("Cas&e sensitive")); containsTextCase->setChecked(true); containsCbsLayout->addWidget(containsTextCase); containsLayout->addLayout(containsCbsLayout, 1, 0); filterLayout->addWidget(containsGroup, 2, 0); auto *recurseLayout = new QHBoxLayout(); recurseLayout->setSpacing(6); recurseLayout->setContentsMargins(0, 0, 0, 0); auto* recurseSpacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); recurseLayout->addItem(recurseSpacer); if (properties & FilterTabs::HasRecurseOptions) { // Options for recursive searching searchInDirs = new QCheckBox(this); searchInDirs->setText(i18n("Search in s&ub folders")); searchInDirs->setChecked(true); recurseLayout->addWidget(searchInDirs); searchInArchives = new QCheckBox(this); searchInArchives->setText(i18n("Search in arch&ives")); recurseLayout->addWidget(searchInArchives); followLinks = new QCheckBox(this); followLinks->setText(i18n("Follow &links")); recurseLayout->addWidget(followLinks); } filterLayout->addLayout(recurseLayout, 3, 0); for(int i = 0; i < extraOptions.length(); i++) { auto *option = new QCheckBox(this); option->setText(extraOptions[i]); recurseLayout->addWidget(option); this->extraOptions.insert(extraOptions[i], option); } // Connection table if (properties & FilterTabs::HasProfileHandler) { connect(profileAddBtn, &QPushButton::clicked, this, &GeneralFilter::slotAddBtnClicked); connect(profileLoadBtn, &QPushButton::clicked, this, &GeneralFilter::slotLoadBtnClicked); connect(profileOverwriteBtn, &QPushButton::clicked, this, &GeneralFilter::slotOverwriteBtnClicked); connect(profileRemoveBtn, &QPushButton::clicked, this, &GeneralFilter::slotRemoveBtnClicked); connect(profileListBox, &KrListWidget::itemDoubleClicked, this, &GeneralFilter::slotProfileDoubleClicked); connect(profileManager, &ProfileManager::loadFromProfile, fltTabs, &FilterTabs::loadFromProfile); connect(profileManager, &ProfileManager::saveToProfile, fltTabs, &FilterTabs::saveToProfile); } connect(searchFor, QOverload::of(&KHistoryComboBox::activated), searchFor, &KHistoryComboBox::addToHistory); connect(containsText, QOverload::of(&KHistoryComboBox::activated), containsText, &KHistoryComboBox::addToHistory); // load the completion and history lists // ==> search for KConfigGroup group(krConfig, "Search"); QStringList list = group.readEntry("SearchFor Completion", QStringList()); searchFor->completionObject()->setItems(list); list = group.readEntry("SearchFor History", QStringList()); searchFor->setHistoryItems(list); // ==> grep list = group.readEntry("ContainsText Completion", QStringList()); containsText->completionObject()->setItems(list); list = group.readEntry("ContainsText History", QStringList()); containsText->setHistoryItems(list); setTabOrder(searchFor, containsText); // search for -> content setTabOrder(containsText, searchType); // content -> search type slotDisable(); } GeneralFilter::~GeneralFilter() { // save the history combos // ==> search for QStringList list = searchFor->completionObject()->items(); KConfigGroup group(krConfig, "Search"); group.writeEntry("SearchFor Completion", list); list = searchFor->historyItems(); group.writeEntry("SearchFor History", list); // ==> grep text list = containsText->completionObject()->items(); group.writeEntry("ContainsText Completion", list); list = containsText->historyItems(); group.writeEntry("ContainsText History", list); if ((properties & FilterTabs::HasDontSearchIn) && (properties & FilterTabs::HasRecurseOptions)) { list = excludeFolderNames->historyItems(); group.writeEntry("ExcludeFolderNamesHistory", list); group.writeEntry("ExcludeFolderNames", excludeFolderNames->currentText()); group.writeEntry("ExcludeFolderNamesUse", static_cast(useExcludeFolderNames->checkState())); } krConfig->sync(); } -bool GeneralFilter::isExtraOptionChecked(QString name) +bool GeneralFilter::isExtraOptionChecked(const QString& name) { QCheckBox *option = extraOptions[name]; return option ? option->isChecked() : false; } -void GeneralFilter::checkExtraOption(QString name, bool check) +void GeneralFilter::checkExtraOption(const QString& name, bool check) { QCheckBox *option = extraOptions[name]; if (option) option->setChecked(check); } void GeneralFilter::queryAccepted() { searchFor->addToHistory(searchFor->currentText()); containsText->addToHistory(containsText->currentText()); if ((properties & FilterTabs::HasDontSearchIn) && (properties & FilterTabs::HasRecurseOptions)) { excludeFolderNames->addToHistory(excludeFolderNames->currentText()); } } void GeneralFilter::refreshProfileListBox() { profileListBox->clear(); profileListBox->addItems(ProfileManager::availableProfiles("SelectionProfile")); if (profileListBox->count() != 0) { profileLoadBtn->setEnabled(true); profileOverwriteBtn->setEnabled(true); profileRemoveBtn->setEnabled(true); } else { profileLoadBtn->setEnabled(false); profileOverwriteBtn->setEnabled(false); profileRemoveBtn->setEnabled(false); } } QCheckBox *GeneralFilter::createExcludeCheckBox(const KConfigGroup &group) { auto *excludeCheckBox = new QCheckBox(this); excludeCheckBox->setText(i18n("Exclude Folder Names")); excludeCheckBox->setToolTip(i18n("Filters out specified directory names from the results.")); excludeCheckBox->setChecked(static_cast(group.readEntry("ExcludeFolderNamesUse", 0))); return excludeCheckBox; } KHistoryComboBox *GeneralFilter::createExcludeComboBox(const KConfigGroup &group) { auto *excludeComboBox = new KHistoryComboBox(false, this); QSizePolicy excludeFolderNamesPolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); excludeFolderNamesPolicy.setHeightForWidth(excludeComboBox->sizePolicy().hasHeightForWidth()); excludeComboBox->setSizePolicy(excludeFolderNamesPolicy); excludeComboBox->setEditable(true); excludeComboBox->setDuplicatesEnabled(false); excludeComboBox->setMaxCount(25); excludeComboBox->setMinimumContentsLength(10); excludeComboBox->lineEdit()->setPlaceholderText(i18n("Enter space-separated folder names")); excludeComboBox->lineEdit()->setWhatsThis( i18n("You can insert names with escaped spaces or quoted.\nExample: .git \"target " "build\" build\\ krusader")); excludeComboBox->setHistoryItems(group.readEntry("ExcludeFolderNamesHistory", QStringList())); excludeComboBox->setEditText(group.readEntry("ExcludeFolderNames", "")); return excludeComboBox; } void GeneralFilter::slotAddBtnClicked() { profileManager->newProfile(searchFor->currentText().simplified()); refreshProfileListBox(); } void GeneralFilter::slotOverwriteBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) profileManager->overwriteProfile(item->text()); } void GeneralFilter::slotRemoveBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) { profileManager->deleteProfile(item->text()); refreshProfileListBox(); } } void GeneralFilter::slotProfileDoubleClicked(QListWidgetItem *item) { if (item != nullptr) { QString profileName = item->text(); profileManager->loadProfile(profileName); fltTabs->close(true); } } void GeneralFilter::slotLoadBtnClicked() { QListWidgetItem *item = profileListBox->currentItem(); if (item != nullptr) profileManager->loadProfile(item->text()); } void GeneralFilter::slotDisable() { bool state = containsRegExp->isChecked(); bool global = ofType->currentText() != i18n("Folders"); bool remoteOnly = false; if (properties & FilterTabs::HasSearchIn) { QList urlList = searchIn->urlList(); remoteOnly = urlList.count() != 0; foreach(const QUrl &url, urlList) if (url.scheme() == "file") remoteOnly = false; } containsWholeWord->setEnabled(!state && global); containsRegExp->menu()->setEnabled(state && global); encLabel->setEnabled(global); contentEncoding->setEnabled(global); containsTextCase->setEnabled(global); containsRegExp->setEnabled(global); if (properties & FilterTabs::HasRecurseOptions) searchInArchives->setEnabled(global && !remoteOnly); containsLabel->setEnabled(global); containsText->setEnabled(global); } void GeneralFilter::slotRegExpTriggered(QAction * act) { if (act == nullptr) return; auto *regAct = dynamic_cast(act); if (regAct == nullptr) return; containsText->lineEdit()->insert(regAct->regExp()); containsText->lineEdit()->setCursorPosition(containsText->lineEdit()->cursorPosition() + regAct->cursor()); containsText->lineEdit()->setFocus(); } bool GeneralFilter::getSettings(FilterSettings &s) { // check that we have (at least) what to search, and where to search in if (searchFor->currentText().simplified().isEmpty()) { KMessageBox::error(this , i18n("No search criteria entered.")); searchFor->setFocus(); return false; } s.searchFor = searchFor->currentText().trimmed(); s.searchForCase = searchForCase->isChecked(); if (ofType->currentText() != i18n("All Files")) s.mimeType = ofType->currentText(); if (containsText->isEnabled()) { s.containsText = containsText->currentText(); s.containsTextCase = containsTextCase->isChecked(); s.containsWholeWord = containsWholeWord->isChecked(); s.containsRegExp = containsRegExp->isChecked(); } if (contentEncoding->currentIndex() != 0) s.contentEncoding = KCharsets::charsets()->encodingForName(contentEncoding->currentText()); if (properties & FilterTabs::HasRecurseOptions) { s.recursive = searchInDirs->isChecked(); s.searchInArchives = searchInArchives->isChecked(); s.followLinks = followLinks->isChecked(); } if (properties & FilterTabs::HasSearchIn) { s.searchIn = searchIn->urlList(); if (s.searchIn.isEmpty()) { // we need a place to search in KMessageBox::error(this , i18n("Please specify a location to search in.")); searchIn->lineEdit()->setFocus(); return false; } } if (properties & FilterTabs::HasDontSearchIn) { s.dontSearchIn = dontSearchIn->urlList(); if (properties & FilterTabs::HasRecurseOptions) { if (useExcludeFolderNames->isChecked()) { s.excludeFolderNames = KShell::splitArgs(excludeFolderNames->currentText()); } else { s.excludeFolderNames = QStringList(); } } } return true; } void GeneralFilter::applySettings(const FilterSettings &s) { searchFor->setEditText(s.searchFor); searchForCase->setChecked(s.searchForCase); setComboBoxValue(ofType, s.mimeType); containsText->setEditText(s.containsText); containsTextCase->setChecked(s.containsTextCase); containsWholeWord->setChecked(s.containsWholeWord); containsRegExp->setChecked(s.containsRegExp); setComboBoxValue(contentEncoding, KCharsets::charsets()->descriptionForEncoding(s.contentEncoding)); if (properties & FilterTabs::HasRecurseOptions) { searchInDirs->setChecked(s.recursive); searchInArchives->setChecked(s.searchInArchives); followLinks->setChecked(s.followLinks); } if (properties & FilterTabs::HasSearchIn) { searchIn->lineEdit()->clear(); searchIn->listBox()->clear(); searchIn->listBox()->addItems(KrServices::toStringList(s.searchIn)); } if (properties & FilterTabs::HasDontSearchIn) { dontSearchIn->lineEdit()->clear(); dontSearchIn->listBox()->clear(); dontSearchIn->listBox()->addItems(KrServices::toStringList(s.dontSearchIn)); } } diff --git a/krusader/Filter/generalfilter.h b/krusader/Filter/generalfilter.h index aa9feb00..b6117062 100644 --- a/krusader/Filter/generalfilter.h +++ b/krusader/Filter/generalfilter.h @@ -1,118 +1,118 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 GENERALFILTER_H #define GENERALFILTER_H // QtWidgets #include #include #include #include #include #include #include #include #include #include "filterbase.h" #include "../Dialogs/kurllistrequester.h" #include "../GUI/profilemanager.h" #include "../GUI/krlistwidget.h" class GeneralFilter : public QWidget, public FilterBase { Q_OBJECT public: GeneralFilter(FilterTabs *tabs, int properties, QWidget *parent = nullptr, QStringList extraOptions = QStringList()); ~GeneralFilter() override; void queryAccepted() Q_DECL_OVERRIDE; QString name() Q_DECL_OVERRIDE { return "GeneralFilter"; } FilterTabs * filterTabs() Q_DECL_OVERRIDE { return fltTabs; } bool getSettings(FilterSettings&) Q_DECL_OVERRIDE; void applySettings(const FilterSettings&) Q_DECL_OVERRIDE; - bool isExtraOptionChecked(QString name); - void checkExtraOption(QString name, bool check); + bool isExtraOptionChecked(const QString& name); + void checkExtraOption(const QString& name, bool check); public slots: void slotAddBtnClicked(); void slotLoadBtnClicked(); void slotOverwriteBtnClicked(); void slotRemoveBtnClicked(); void slotDisable(); void slotRegExpTriggered(QAction * act); void slotProfileDoubleClicked(QListWidgetItem *); void refreshProfileListBox(); public: KComboBox* contentEncoding; QCheckBox* searchForCase; QCheckBox* containsTextCase; QCheckBox* containsWholeWord; QCheckBox* useExcludeFolderNames; QCheckBox* searchInDirs; QCheckBox* searchInArchives; QCheckBox* followLinks; QHash extraOptions; KURLListRequester *searchIn; KURLListRequester *dontSearchIn; QLayout *middleLayout; KHistoryComboBox* searchFor; KHistoryComboBox* containsText; KHistoryComboBox* excludeFolderNames; QToolButton* containsRegExp; KComboBox* ofType; QLabel *encLabel; QLabel *containsLabel; KShellCompletion completion; KrListWidget *profileListBox; QPushButton *profileAddBtn; QPushButton *profileLoadBtn; QPushButton *profileOverwriteBtn; QPushButton *profileRemoveBtn; ProfileManager *profileManager; int properties; FilterTabs *fltTabs; private: QCheckBox *createExcludeCheckBox(const KConfigGroup &group); KHistoryComboBox *createExcludeComboBox(const KConfigGroup &group); }; #endif /* GENERALFILTER_H */ diff --git a/krusader/GUI/kcmdline.cpp b/krusader/GUI/kcmdline.cpp index 625f2891..4ad4f6f1 100644 --- a/krusader/GUI/kcmdline.cpp +++ b/krusader/GUI/kcmdline.cpp @@ -1,315 +1,316 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "kcmdline.h" // QtCore #include #include #include // QtGui #include #include #include #include #include // QtWidgets #include #include #include #include #include #include +#include #include "../krglobal.h" #include "../icon.h" #include "../krslots.h" #include "../defaults.h" #include "../krusaderview.h" #include "../krservices.h" #include "../ActionMan/addplaceholderpopup.h" #include "kcmdmodebutton.h" CmdLineCombo::CmdLineCombo(QWidget *parent) : KHistoryComboBox(parent), _handlingLineEditResize(false) { lineEdit()->installEventFilter(this); _pathLabel = new QLabel(this); _pathLabel->setWhatsThis(i18n("Name of folder where command will be processed.")); _pathLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); } bool CmdLineCombo::eventFilter(QObject *watched, QEvent *e) { if(watched == lineEdit() && (e->type() == QEvent::Move || e->type() == QEvent::Resize)) { if(!_handlingLineEditResize) { // avoid infinite recursion _handlingLineEditResize = true; updateLineEditGeometry(); _handlingLineEditResize = false; } } return false; } void CmdLineCombo::setPath(QString path) { - _path = path; + _path = std::move(path); doLayout(); } void CmdLineCombo::updateLineEditGeometry() { QRect r = lineEdit()->geometry(); r.setLeft(_pathLabel->geometry().right()); lineEdit()->setGeometry(r); } void CmdLineCombo::doLayout() { QString pathNameLabel = _path; QFontMetrics fm(_pathLabel->fontMetrics()); int textWidth = fm.width(_path); int maxWidth = (width() + _pathLabel->width()) * 2 / 5; int letters = _path.length() / 2; while (letters && textWidth > maxWidth) { pathNameLabel = _path.left(letters) + "..." + _path.right(letters); letters--; textWidth = fm.width(pathNameLabel); } _pathLabel->setText(pathNameLabel + "> "); _pathLabel->adjustSize(); QStyleOptionComboBox opt; initStyleOption(&opt); QRect labelRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this); labelRect.adjust(2, 0, 0, 0); labelRect.setWidth(_pathLabel->width()); _pathLabel->setGeometry(labelRect); updateLineEditGeometry(); } void CmdLineCombo::resizeEvent(QResizeEvent *e) { KHistoryComboBox::resizeEvent(e); doLayout(); } void CmdLineCombo::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: if (e->modifiers() & Qt::ControlModifier) { SLOTS->insertFileName((e->modifiers()&Qt::ShiftModifier)!=0); break; } KHistoryComboBox::keyPressEvent(e); break; case Qt::Key_Down: if (e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { MAIN_VIEW->focusTerminalEmulator(); return; } else KHistoryComboBox::keyPressEvent(e); break; case Qt::Key_Up: if (e->modifiers() == Qt::ControlModifier || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { emit returnToPanel(); return; } else KHistoryComboBox::keyPressEvent(e); break; case Qt::Key_Escape: if (e->modifiers() == 0) { emit returnToPanel(); return; } else KHistoryComboBox::keyPressEvent(e); break; default: KHistoryComboBox::keyPressEvent(e); } } KCMDLine::KCMDLine(QWidget *parent) : QWidget(parent) { auto * layout = new QGridLayout(this); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); int height = QFontMetrics(QFontDatabase::systemFont(QFontDatabase::GeneralFont)).height(); height = height + 5 * (height > 14) + 6; // and editable command line completion.setMode(KUrlCompletion::FileCompletion); cmdLine = new CmdLineCombo(this); cmdLine->setMaxCount(100); // remember 100 commands cmdLine->setMinimumContentsLength(10); cmdLine->setDuplicatesEnabled(false); cmdLine->setMaximumHeight(height); cmdLine->setCompletionObject(&completion); cmdLine->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); // load the history KConfigGroup grpSvr(krConfig, "Private"); QStringList list = grpSvr.readEntry("cmdline history", QStringList()); cmdLine->setHistoryItems(list); connect(cmdLine, QOverload::of(&CmdLineCombo::returnPressed), this, &KCMDLine::slotRun); connect(cmdLine, QOverload::of(&CmdLineCombo::returnPressed), cmdLine->lineEdit(), &QLineEdit::clear); connect(cmdLine, &CmdLineCombo::returnToPanel, this, &KCMDLine::slotReturnFocus); cmdLine->setWhatsThis(i18n("

Well, it is actually quite simple: you type your command here and Krusader obeys.

Tip: move within command line history with <Up> and <Down> arrows.

")); layout->addWidget(cmdLine, 0, 1); buttonAddPlaceholder = new QToolButton(this); buttonAddPlaceholder->setAutoRaise(true); buttonAddPlaceholder->setIcon(Icon("list-add")); connect(buttonAddPlaceholder, &QToolButton::clicked, this, &KCMDLine::addPlaceholder); buttonAddPlaceholder->setWhatsThis(i18n("Add Placeholders for the selected files in the panel.")); layout->addWidget(buttonAddPlaceholder, 0, 2); // a run in terminal button terminal = new KCMDModeButton(this); terminal->setAutoRaise(true); layout->addWidget(terminal, 0, 3); layout->activate(); } KCMDLine::~KCMDLine() { KConfigGroup grpSvr(krConfig, "Private"); QStringList list = cmdLine->historyItems(); //qWarning() << list[0]; grpSvr.writeEntry("cmdline history", list); krConfig->sync(); } void KCMDLine::addPlaceholder() { AddPlaceholderPopup popup(this); QString exp = popup.getPlaceholder( buttonAddPlaceholder->mapToGlobal(QPoint(0, 0)) ); this->addText(exp); } void KCMDLine::setCurrent(const QString &path) { cmdLine->setPath(path); completion.setDir(QUrl::fromLocalFile(path)); // make sure our command is executed in the right directory // This line is important for Krusader overall functions -> do not remove ! QDir::setCurrent(path); } void KCMDLine::slotRun() { const QString command1(cmdLine->currentText()); if (command1.isEmpty()) return; cmdLine->addToHistory(command1); // bugfix by aardvark: current editline is destroyed by addToHistory() in some cases cmdLine->setEditText(command1); if (command1.simplified().left(3) == "cd ") { // cd command effect the active panel QString dir = command1.right(command1.length() - command1.indexOf(" ")).trimmed(); if (dir == "~") dir = QDir::homePath(); else if (dir.left(1) != "/" && !dir.contains(":/")) dir = cmdLine->path() + (cmdLine->path() == "/" ? "" : "/") + dir; SLOTS->refresh(QUrl::fromUserInput(dir,QDir::currentPath(),QUrl::AssumeLocalFile)); } else { exec(); cmdLine->clearEditText(); } } void KCMDLine::slotReturnFocus() { MAIN_VIEW->cmdLineUnFocus(); } static const KrActionBase::ExecType execModesMenu[] = { KrActionBase::Normal, KrActionBase::CollectOutputSeparateStderr, KrActionBase::CollectOutput, KrActionBase::Terminal, KrActionBase::RunInTE, }; QString KCMDLine::command() const { return cmdLine->currentText(); } KrActionBase::ExecType KCMDLine::execType() const { KConfigGroup grp(krConfig, "Private"); int i = grp.readEntry("Command Execution Mode", (int)0); return execModesMenu[i]; } QString KCMDLine::startpath() const { return cmdLine->path(); // return path->text().left(path->text().length() - 1); } QString KCMDLine::user() const { return QString(); } QString KCMDLine::text() const { return cmdLine->currentText(); } bool KCMDLine::acceptURLs() const { return false; } bool KCMDLine::confirmExecution() const { return false; } bool KCMDLine::doSubstitution() const { return true; } -void KCMDLine::setText(QString text) +void KCMDLine::setText(const QString& text) { cmdLine->lineEdit()->setText(text); } diff --git a/krusader/GUI/kcmdline.h b/krusader/GUI/kcmdline.h index 87c41b59..05c68f3c 100644 --- a/krusader/GUI/kcmdline.h +++ b/krusader/GUI/kcmdline.h @@ -1,119 +1,119 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KCMDLINE_H #define KCMDLINE_H // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include "../UserAction/kractionbase.h" class KCMDModeButton; class CmdLineCombo : public KHistoryComboBox { Q_OBJECT public: explicit CmdLineCombo(QWidget *parent); bool eventFilter(QObject *watched, QEvent *e) Q_DECL_OVERRIDE; QString path() { return _path; } void setPath(QString path); signals: void returnToPanel(); protected slots: void doLayout(); protected: void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; void updateLineEditGeometry(); QLabel *_pathLabel; QString _path; bool _handlingLineEditResize; }; class KCMDLine : public QWidget, KrActionBase { Q_OBJECT public: explicit KCMDLine(QWidget *parent = nullptr); ~KCMDLine() override; void setCurrent(const QString &path); //virtual methods from KrActionBase - void setText(QString text); + void setText(const QString& text); QString command() const Q_DECL_OVERRIDE; ExecType execType() const Q_DECL_OVERRIDE; QString startpath() const Q_DECL_OVERRIDE; QString user() const Q_DECL_OVERRIDE; QString text() const Q_DECL_OVERRIDE; bool acceptURLs() const Q_DECL_OVERRIDE; bool confirmExecution() const Q_DECL_OVERRIDE; bool doSubstitution() const Q_DECL_OVERRIDE; signals: void signalRun(); public slots: void slotReturnFocus(); // returns keyboard focus to panel void slotRun(); void addPlaceholder(); - void addText(QString text) { + void addText(const QString& text) { cmdLine->lineEdit()->setText(cmdLine->lineEdit()->text() + text); } void popup() { cmdLine->showPopup(); } protected: void focusInEvent(QFocusEvent*) Q_DECL_OVERRIDE { cmdLine->setFocus(); } void calcLabelSize(); private: CmdLineCombo *cmdLine; KCMDModeButton *terminal; QToolButton *buttonAddPlaceholder; KShellCompletion completion; }; #endif diff --git a/krusader/GUI/mediabutton.cpp b/krusader/GUI/mediabutton.cpp index 04f1f965..fa77f5ac 100644 --- a/krusader/GUI/mediabutton.cpp +++ b/krusader/GUI/mediabutton.cpp @@ -1,629 +1,630 @@ /***************************************************************************** * Copyright (C) 2006 Csaba Karai * * Copyright (C) 2006-2018 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 "mediabutton.h" #include "../krglobal.h" #include "../icon.h" #include "../MountMan/kmountman.h" // QtCore #include // QtGui #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include QString MediaButton::remotePrefix = QLatin1String("remote:"); MediaButton::MediaButton(QWidget *parent) : QToolButton(parent), popupMenu(nullptr), rightMenu(nullptr), openInNewTab(false) { setAutoRaise(true); setIcon(Icon("system-file-manager")); setText(i18n("Open the available media list")); setToolTip(i18n("Open the available media list")); setPopupMode(QToolButton::InstantPopup); setAcceptDrops(false); popupMenu = new QMenu(this); popupMenu->installEventFilter(this); Q_CHECK_PTR(popupMenu); setMenu(popupMenu); connect(popupMenu, &QMenu::aboutToShow, this, &MediaButton::slotAboutToShow); connect(popupMenu, &QMenu::aboutToHide, this, &MediaButton::slotAboutToHide); connect(popupMenu, &QMenu::triggered, this, &MediaButton::slotPopupActivated); Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); connect(notifier, &Solid::DeviceNotifier::deviceAdded, this, &MediaButton::slotDeviceAdded); connect(notifier, &Solid::DeviceNotifier::deviceRemoved, this, &MediaButton::slotDeviceRemoved); connect(&mountCheckerTimer, &QTimer::timeout, this, &MediaButton::slotCheckMounts); } MediaButton::~MediaButton() = default; void MediaButton::updateIcon(const QString &mountPoint) { if(!mountPoint.isEmpty() && mountPoint == currentMountPoint) return; currentMountPoint = mountPoint; QString iconName("system-file-manager"); QStringList overlays; if(!mountPoint.isEmpty()) { Solid::Device device(krMtMan.findUdiForPath(mountPoint, Solid::DeviceInterface::StorageAccess));; auto *vol = device.as (); if(device.isValid()) iconName = device.icon(); if (vol && vol->usage() == Solid::StorageVolume::Encrypted) overlays << "security-high"; } setIcon(Icon(iconName, overlays)); } void MediaButton::slotAboutToShow() { emit aboutToShow(); popupMenu->clear(); udiNameMap.clear(); createMediaList(); } void MediaButton::slotAboutToHide() { if (rightMenu) rightMenu->close(); mountCheckerTimer.stop(); } void MediaButton::createMediaList() { // devices detected by solid storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); for (int p = storageDevices.count() - 1 ; p >= 0; p--) { Solid::Device device = storageDevices[ p ]; QString udi = device.udi(); QString name; QIcon kdeIcon; if (!getNameAndIcon(device, name, kdeIcon)) continue; QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); udiNameMap[ udi ] = name; connect(device.as(), &Solid::StorageAccess::accessibilityChanged, this, &MediaButton::slotAccessibilityChanged); } KMountPoint::List possibleMountList = KMountPoint::possibleMountPoints(); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); for (auto & it : possibleMountList) { if (krMtMan.networkFilesystem(it->mountType())) { QString path = it->mountPoint(); bool mounted = false; for (auto & it2 : currentMountList) { if (krMtMan.networkFilesystem(it2->mountType()) && it->mountPoint() == it2->mountPoint()) { mounted = true; break; } } QString name = i18nc("%1 is the mount point of the remote share", "Remote Share [%1]", it->mountPoint()); QStringList overlays; if (mounted) overlays << "emblem-mounted"; QAction * act = popupMenu->addAction(Icon("network-wired", overlays), name); QString udi = remotePrefix + it->mountPoint(); act->setData(QVariant(udi)); } } mountCheckerTimer.setSingleShot(true); mountCheckerTimer.start(1000); } bool MediaButton::getNameAndIcon(Solid::Device & device, QString &name, QIcon &iconOut) { auto *access = device.as(); if (access == nullptr) return false; QString udi = device.udi(); QString label = i18nc("Unknown label", "Unknown"); bool mounted = access->isAccessible(); QString path = access->filePath(); QString type = i18nc("Unknown media type", "Unknown"); QString iconName = device.icon(); QString fstype; QString size; auto * vol = device.as (); if (vol) { label = vol->label(); fstype = vol->fsType(); size = KIO::convertSize(vol->size()); } bool printSize = false; if (iconName == "media-floppy") type = i18n("Floppy"); else if (iconName == "drive-optical") type = i18n("CD/DVD-ROM"); else if (iconName == "drive-removable-media-usb-pendrive") type = i18n("USB pen drive"), printSize = true; else if (iconName == "drive-removable-media-usb") type = i18n("USB device"), printSize = true; else if (iconName == "drive-removable-media") type = i18n("Removable media"), printSize = true; else if (iconName == "drive-harddisk") type = i18n("Hard Disk"), printSize = true; else if (iconName == "camera-photo") type = i18n("Camera"); else if (iconName == "media-optical-video") type = i18n("Video CD/DVD-ROM"); else if (iconName == "media-optical-audio") type = i18n("Audio CD/DVD-ROM"); else if (iconName == "media-optical") type = i18n("Recordable CD/DVD-ROM"); KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("MediaMenu")); if (printSize) { QString showSizeSetting = cfg.readEntry("ShowSize", "Always"); if (showSizeSetting == "WhenNoLabel") printSize = label.isEmpty(); else if (showSizeSetting == "Never") printSize = false; } if (printSize && !size.isEmpty()) name += size + ' '; if (!label.isEmpty()) name += label + ' '; else name += type + ' '; if (!fstype.isEmpty() && cfg.readEntry("ShowFSType", true)) name += '(' + fstype + ") "; if (!path.isEmpty() && cfg.readEntry("ShowPath", true)) name += '[' + path + "] "; name = name.trimmed(); QStringList overlays; if (mounted) { overlays << "emblem-mounted"; } else { overlays << QString(); // We have to guarantee the placement of the next emblem } if (vol && vol->usage() == Solid::StorageVolume::Encrypted) { overlays << "security-high"; } iconOut = Icon(iconName, overlays); return true; } void MediaButton::slotPopupActivated(QAction *action) { if (action && action->data().canConvert()) { QString udi = action->data().toString(); if (!udi.isEmpty()) { bool mounted = false; QString mountPoint; getStatus(udi, mounted, &mountPoint); if (mounted) emit openUrl(QUrl::fromLocalFile(mountPoint)); else mount(udi, true); } } } void MediaButton::showMenu() { QMenu * pP = menu(); if (pP) { menu() ->exec(mapToGlobal(QPoint(0, height()))); } } bool MediaButton::eventFilter(QObject *o, QEvent *e) { if (o == popupMenu) { if (e->type() == QEvent::ContextMenu) { QAction *act = popupMenu->activeAction(); if (act && act->data().canConvert()) { QString id = act->data().toString(); if (!id.isEmpty()) { QPoint globalPos = popupMenu->mapToGlobal(popupMenu->actionGeometry(act).topRight()); rightClickMenu(id, globalPos); return true; } } } else if (e->type() == QEvent::KeyPress) { auto *ke = static_cast(e); if (ke->key() == Qt::Key_Return && ke->modifiers() == Qt::ControlModifier) { if (QAction *act = popupMenu->activeAction()) { QString id = act->data().toString(); if (!id.isEmpty()) { toggleMount(id); return true; } } } } else if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { auto *m = static_cast(e); if (m->button() == Qt::RightButton) { if (e->type() == QEvent::MouseButtonPress) { QAction * act = popupMenu->actionAt(m->pos()); if (act && act->data().canConvert()) { QString id = act->data().toString(); if (!id.isEmpty()) rightClickMenu(id, m->globalPos()); } } m->accept(); return true; } } } return false; } -void MediaButton::rightClickMenu(QString udi, QPoint pos) +void MediaButton::rightClickMenu(const QString& udi, QPoint pos) { if (rightMenu) rightMenu->close(); bool ejectable = false; bool mounted = false; QString mountPoint; getStatus(udi, mounted, &mountPoint, &ejectable); QUrl openURL = QUrl::fromLocalFile(mountPoint); QMenu * myMenu = rightMenu = new QMenu(popupMenu); QAction * actOpen = myMenu->addAction(i18n("Open")); actOpen->setData(QVariant(1)); QAction * actOpenNewTab = myMenu->addAction(i18n("Open in a new tab")); actOpenNewTab->setData(QVariant(2)); myMenu->addSeparator(); if (!mounted) { QAction * actMount = myMenu->addAction(i18n("Mount")); actMount->setData(QVariant(3)); } else { QAction * actUnmount = myMenu->addAction(i18n("Unmount")); actUnmount->setData(QVariant(4)); } if (ejectable) { QAction * actEject = myMenu->addAction(i18n("Eject")); actEject->setData(QVariant(5)); } QAction *act = myMenu->exec(pos); int result = -1; if (act != nullptr && act->data().canConvert()) result = act->data().toInt(); delete myMenu; if (rightMenu == myMenu) rightMenu = nullptr; else return; switch (result) { case 1: case 2: popupMenu->close(); if (mounted) { if (result == 1) emit openUrl(openURL); else emit newTab(openURL); } else { mount(udi, true, result == 2); // mount first, when mounted open the tab } break; case 3: mount(udi); break; case 4: umount(udi); break; case 5: eject(udi); break; default: break; } } -void MediaButton::toggleMount(QString udi) +void MediaButton::toggleMount(const QString& udi) { bool mounted = false; getStatus(udi, mounted); if (mounted) umount(udi); else mount(udi); } -void MediaButton::getStatus(QString udi, bool &mounted, QString *mountPointOut, bool *ejectableOut) +void MediaButton::getStatus(const QString& udi, bool &mounted, QString *mountPointOut, bool *ejectableOut) { mounted = false; bool network = udi.startsWith(remotePrefix); bool ejectable = false; QString mountPoint; if (network) { mountPoint = udi.mid(remotePrefix.length()); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); for (auto & it : currentMountList) { if (krMtMan.networkFilesystem(it->mountType()) && it->mountPoint() == mountPoint) { mounted = true; break; } } } else { Solid::Device device(udi); auto *access = device.as(); auto *optdisc = device.as(); if (access) mountPoint = access->filePath(); if (access && access->isAccessible()) mounted = true; if (optdisc) ejectable = true; } if (mountPointOut) *mountPointOut = mountPoint; if (ejectableOut) *ejectableOut = ejectable; } -void MediaButton::mount(QString udi, bool open, bool newtab) +void MediaButton::mount(const QString& udi, bool open, bool newtab) { if (udi.startsWith(remotePrefix)) { QString mp = udi.mid(remotePrefix.length()); krMtMan.mount(mp, true); if (newtab) emit newTab(QUrl::fromLocalFile(mp)); else emit openUrl(QUrl::fromLocalFile(mp)); return; } Solid::Device device(udi); auto *access = device.as(); if (access && !access->isAccessible()) { if (open) udiToOpen = device.udi(), openInNewTab = newtab; connect(access, &Solid::StorageAccess::setupDone, this, &MediaButton::slotSetupDone); access->setup(); } } -void MediaButton::slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) +void MediaButton::slotSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString &udi) { if (error == Solid::NoError) { if (udi == udiToOpen) { auto *access = Solid::Device(udi).as(); if (access && access->isAccessible()) { if (openInNewTab) emit newTab(QUrl::fromLocalFile(access->filePath())); else emit openUrl(QUrl::fromLocalFile(access->filePath())); } udiToOpen = QString(), openInNewTab = false; } } else { if (udi == udiToOpen) udiToOpen = QString(), openInNewTab = false; QString name; if (udiNameMap.contains(udi)) name = udiNameMap[ udi ]; if (errorData.isValid()) { KMessageBox::sorry(this, i18n("An error occurred while accessing '%1', the system responded: %2", name, errorData.toString())); } else { KMessageBox::sorry(this, i18n("An error occurred while accessing '%1'", name)); } } } -void MediaButton::umount(QString udi) +void MediaButton::umount(const QString& udi) { if (udi.startsWith(remotePrefix)) { krMtMan.unmount(udi.mid(remotePrefix.length()), false); return; } krMtMan.unmount(krMtMan.pathForUdi(udi), false); } void MediaButton::eject(QString udi) { - krMtMan.eject(krMtMan.pathForUdi(udi)); + krMtMan.eject(krMtMan.pathForUdi(std::move(udi))); } void MediaButton::slotAccessibilityChanged(bool /*accessible*/, const QString & udi) { QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { Solid::Device device(udi); QString name; QIcon kdeIcon; if (getNameAndIcon(device, name, kdeIcon)) { act->setText(name); act->setIcon(kdeIcon); } break; } } } void MediaButton::slotDeviceAdded(const QString& udi) { if (popupMenu->isHidden()) return; Solid::Device device(udi); auto *access = device.as(); if (access == nullptr) return; QString name; QIcon kdeIcon; if (!getNameAndIcon(device, name, kdeIcon)) return; QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); udiNameMap[ udi ] = name; connect(device.as(), &Solid::StorageAccess::accessibilityChanged, this, &MediaButton::slotAccessibilityChanged); } void MediaButton::slotDeviceRemoved(const QString& udi) { if (popupMenu->isHidden()) return; QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { popupMenu->removeAction(act); delete act; break; } } } void MediaButton::slotCheckMounts() { if (isHidden()) return; KMountPoint::List possibleMountList = KMountPoint::possibleMountPoints(); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString().startsWith(remotePrefix)) { QString mountPoint = act->data().toString().mid(remotePrefix.length()); bool available = false; for (auto & it : possibleMountList) { if (krMtMan.networkFilesystem(it->mountType()) && it->mountPoint() == mountPoint) { available = true; break; } } if (!available) { popupMenu->removeAction(act); delete act; } } } for (auto & it : possibleMountList) { if (krMtMan.networkFilesystem(it->mountType())) { QString path = it->mountPoint(); bool mounted = false; QString udi = remotePrefix + path; QAction * correspondingAct = nullptr; foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { correspondingAct = act; break; } } for (auto & it2 : currentMountList) { if (krMtMan.networkFilesystem(it2->mountType()) && path == it2->mountPoint()) { mounted = true; break; } } QString name = i18nc("%1 is the mount point of the remote share", "Remote Share [%1]", it->mountPoint()); QStringList overlays; if (mounted) overlays << "emblem-mounted"; QIcon kdeIcon = Icon("network-wired", overlays); if (!correspondingAct) { QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); } else { correspondingAct->setText(name); correspondingAct->setIcon(kdeIcon); } } } mountCheckerTimer.setSingleShot(true); mountCheckerTimer.start(1000); } diff --git a/krusader/GUI/mediabutton.h b/krusader/GUI/mediabutton.h index 2e8ee897..a37daa18 100644 --- a/krusader/GUI/mediabutton.h +++ b/krusader/GUI/mediabutton.h @@ -1,93 +1,93 @@ /***************************************************************************** * Copyright (C) 2006 Csaba Karai * * Copyright (C) 2006-2018 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 MEDIABUTTON_H #define MEDIABUTTON_H // QtCore #include #include #include #include #include // QtWidgets #include #include #include #include #include class QMenu; class MediaButton : public QToolButton { Q_OBJECT public: explicit MediaButton(QWidget *parent = nullptr); ~MediaButton() override; public slots: void slotAboutToShow(); void slotAboutToHide(); void slotPopupActivated(QAction *); void slotAccessibilityChanged(bool, const QString &); void slotDeviceAdded(const QString&); void slotDeviceRemoved(const QString&); void showMenu(); void slotCheckMounts(); void updateIcon(const QString &mountPoint); signals: void openUrl(const QUrl&); void newTab(const QUrl&); void aboutToShow(); protected: bool eventFilter(QObject *o, QEvent *e) Q_DECL_OVERRIDE; bool getNameAndIcon(Solid::Device &, QString &, QIcon &); private: void createMediaList(); - void toggleMount(QString udi); - void getStatus(QString udi, bool &mounted, QString *mountPointOut = nullptr, bool *ejectableOut = nullptr); - void mount(QString, bool open = false, bool newtab = false); - void umount(QString); + void toggleMount(const QString& udi); + void getStatus(const QString& udi, bool &mounted, QString *mountPointOut = nullptr, bool *ejectableOut = nullptr); + void mount(const QString&, bool open = false, bool newtab = false); + void umount(const QString&); void eject(QString); - void rightClickMenu(QString udi, QPoint pos); + void rightClickMenu(const QString& udi, QPoint pos); QList storageDevices; private slots: - void slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi); + void slotSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString &udi); private: static QString remotePrefix; QMenu *popupMenu; QMenu *rightMenu; QString udiToOpen; bool openInNewTab; QMap udiNameMap; QTimer mountCheckerTimer; QString currentMountPoint; // for performance optimization }; #endif /* MEDIABUTTON_H */ diff --git a/krusader/GUI/profilemanager.cpp b/krusader/GUI/profilemanager.cpp index 8c6f58ba..c563e401 100644 --- a/krusader/GUI/profilemanager.cpp +++ b/krusader/GUI/profilemanager.cpp @@ -1,184 +1,184 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "profilemanager.h" // QtGui #include // QtWidgets #include #include #include #include #include "../krglobal.h" #include "../icon.h" -ProfileManager::ProfileManager(QString profileType, QWidget * parent) +ProfileManager::ProfileManager(const QString& profileType, QWidget * parent) : QPushButton(parent) { setText(""); setIcon(Icon("user-identity")); setFixedWidth(16 + 15); setToolTip(i18n("Profiles")); this->profileType = profileType; connect(this, &ProfileManager::clicked, this, &ProfileManager::profilePopup); KConfigGroup group(krConfig, "Private"); profileList = group.readEntry(profileType, QStringList()); } void ProfileManager::profilePopup() { // profile menu identifiers #define ADD_NEW_ENTRY_ID 1000 #define LOAD_ENTRY_ID 2000 #define REMOVE_ENTRY_ID 3000 #define OVERWRITE_ENTRY_ID 4000 // create the menu QMenu popup, removePopup, overwritePopup; popup.setTitle(i18n("Profiles")); for (int i = 0; i != profileList.count() ; i++) { KConfigGroup group(krConfig, profileType + " - " + profileList[i]); QString name = group.readEntry("Name"); popup.addAction(name)->setData(QVariant((int)(LOAD_ENTRY_ID + i))); removePopup.addAction(name)->setData(QVariant((int)(REMOVE_ENTRY_ID + i))); overwritePopup.addAction(name)->setData(QVariant((int)(OVERWRITE_ENTRY_ID + i))); } popup.addSeparator(); if (profileList.count()) { popup.addMenu(&removePopup)->setText(i18n("Remove entry")); popup.addMenu(&overwritePopup)->setText(i18n("Overwrite entry")); } popup.addAction(i18n("Add new entry"))->setData(QVariant((int)(ADD_NEW_ENTRY_ID))); int result = 0; QAction * res = popup.exec(QCursor::pos()); if (res && res->data().canConvert()) result = res->data().toInt(); // check out the user's selection if (result == ADD_NEW_ENTRY_ID) newProfile(); else if (result >= LOAD_ENTRY_ID && result < LOAD_ENTRY_ID + profileList.count()) { emit loadFromProfile(profileType + " - " + profileList[ result - LOAD_ENTRY_ID ]); } else if (result >= REMOVE_ENTRY_ID && result < REMOVE_ENTRY_ID + profileList.count()) { krConfig->deleteGroup(profileType + " - " + profileList[ result - REMOVE_ENTRY_ID ]); profileList.removeAll(profileList[ result - REMOVE_ENTRY_ID ]); KConfigGroup group(krConfig, "Private"); group.writeEntry(profileType, profileList); krConfig->sync(); } else if (result >= OVERWRITE_ENTRY_ID && result < OVERWRITE_ENTRY_ID + profileList.count()) { emit saveToProfile(profileType + " - " + profileList[ result - OVERWRITE_ENTRY_ID ]); } } -void ProfileManager::newProfile(QString defaultName) +void ProfileManager::newProfile(const QString& defaultName) { QString profile = QInputDialog::getText(this, i18n("Krusader::ProfileManager"), i18n("Enter the profile name:"), QLineEdit::Normal, defaultName); if (!profile.isEmpty()) { int profileNum = 1; while (profileList.contains(QString("%1").arg(profileNum))) profileNum++; QString profileString = QString("%1").arg(profileNum); QString profileName = profileType + " - " + profileString; profileList.append(QString("%1").arg(profileString)); KConfigGroup group(krConfig, "Private"); group.writeEntry(profileType, profileList); KConfigGroup pg(krConfig, profileName); pg.writeEntry("Name", profile); emit saveToProfile(profileName); krConfig->sync(); } } -void ProfileManager::deleteProfile(QString name) +void ProfileManager::deleteProfile(const QString& name) { for (int i = 0; i != profileList.count() ; i++) { KConfigGroup group(krConfig, profileType + " - " + profileList[ i ]); QString currentName = group.readEntry("Name"); if (name == currentName) { krConfig->deleteGroup(profileType + " - " + profileList[ i ]); profileList.removeAll(profileList[ i ]); KConfigGroup pg(krConfig, "Private"); pg.writeEntry(profileType, profileList); krConfig->sync(); return; } } } -void ProfileManager::overwriteProfile(QString name) +void ProfileManager::overwriteProfile(const QString& name) { for (int i = 0; i != profileList.count() ; i++) { KConfigGroup group(krConfig, profileType + " - " + profileList[ i ]); QString currentName = group.readEntry("Name"); if (name == currentName) { emit saveToProfile(profileType + " - " + profileList[ i ]); return; } } } -bool ProfileManager::loadProfile(QString name) +bool ProfileManager::loadProfile(const QString& name) { for (int i = 0; i != profileList.count() ; i++) { KConfigGroup group(krConfig, profileType + " - " + profileList[i]); QString currentName = group.readEntry("Name"); if (name == currentName) { emit loadFromProfile(profileType + " - " + profileList[ i ]); return true; } } return false; } -QStringList ProfileManager::availableProfiles(QString profileType) +QStringList ProfileManager::availableProfiles(const QString& profileType) { KConfigGroup group(krConfig, "Private"); QStringList profiles = group.readEntry(profileType, QStringList()); QStringList profileNames; for (int i = 0; i != profiles.count() ; i++) { KConfigGroup pg(krConfig, profileType + " - " + profiles[ i ]); profileNames.append(pg.readEntry("Name")); } return profileNames; } diff --git a/krusader/GUI/profilemanager.h b/krusader/GUI/profilemanager.h index 444e063f..aef239e3 100644 --- a/krusader/GUI/profilemanager.h +++ b/krusader/GUI/profilemanager.h @@ -1,77 +1,77 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 PROFILEMANAGER_H #define PROFILEMANAGER_H // QtCore #include // QtWidgets #include /** * A generic profile manager: Profiles are arbitrary configurations groups and the manager handles * saving/loading multiple groups to/from the configuration. * * A manager instance is responsible for a profile type specified by a type name: * "Panel", "SelectionProfile", "SearcherProfile", "SynchronizerProfile", ... * * Profiles are numbered in the configuration group name and have an additional name property. E.g. * * [Panel - 2] * Name=Media Profile * ... * * This class is view and model at the same time :/ * The GUI button opens a popup menu for selecting, creating, overwriting and removing profiles. */ class ProfileManager : public QPushButton { Q_OBJECT public: - explicit ProfileManager(QString profileType, QWidget * parent = nullptr); + explicit ProfileManager(const QString& profileType, QWidget * parent = nullptr); /** * @param profileType Type of the profile (sync, search, ...) * @return A list of all available profile-names */ - static QStringList availableProfiles(QString profileType); + static QStringList availableProfiles(const QString& profileType); QStringList getNames(); public slots: void profilePopup(); - void newProfile(QString defaultName = QString()); - void deleteProfile(QString name); - void overwriteProfile(QString name); - bool loadProfile(QString name); + void newProfile(const QString& defaultName = QString()); + void deleteProfile(const QString& name); + void overwriteProfile(const QString& name); + bool loadProfile(const QString& name); signals: void saveToProfile(QString profileName); void loadFromProfile(QString profileName); private: QString profileType; QStringList profileList; }; #endif /* PROFILEMANAGER_H */ diff --git a/krusader/KViewer/diskusageviewer.cpp b/krusader/KViewer/diskusageviewer.cpp index 0f5ea129..981b6fb7 100644 --- a/krusader/KViewer/diskusageviewer.cpp +++ b/krusader/KViewer/diskusageviewer.cpp @@ -1,121 +1,122 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * * Copyright (C) 2005-2018 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 "diskusageviewer.h" // QtWidgets #include #include #include +#include #include "../FileSystem/filesystem.h" #include "../Panel/krpanel.h" #include "../Panel/panelfunc.h" #include "../krglobal.h" DiskUsageViewer::DiskUsageViewer(QWidget *parent) : QWidget(parent), diskUsage(nullptr), statusLabel(nullptr) { layout = new QGridLayout(this); layout->setContentsMargins(0, 0, 0, 0); } DiskUsageViewer::~ DiskUsageViewer() { if (diskUsage) { KConfigGroup group(krConfig, "DiskUsageViewer"); group.writeEntry("View", diskUsage->getActiveView()); delete diskUsage; } } void DiskUsageViewer::openUrl(QUrl url) { if (diskUsage == nullptr) { diskUsage = new DiskUsage("DiskUsageViewer", this); connect(diskUsage, &DiskUsage::enteringDirectory, this, [=]() { slotUpdateStatus(); }); connect(diskUsage, &DiskUsage::status, this, &DiskUsageViewer::slotUpdateStatus); connect(diskUsage, &DiskUsage::newSearch, this, &DiskUsageViewer::slotNewSearch); layout->addWidget(diskUsage, 0, 0); this->show(); diskUsage->show(); KConfigGroup group(krConfig, "DiskUsageViewer"); int view = group.readEntry("View", VIEW_FILELIGHT); if (view < VIEW_LINES || view > VIEW_FILELIGHT) view = VIEW_FILELIGHT; diskUsage->setView(view); } url.setPath(url.adjusted(QUrl::StripTrailingSlash).path()); QUrl baseURL = diskUsage->getBaseURL(); if (!diskUsage->isLoading() && !baseURL.isEmpty()) { if (url.scheme() == baseURL.scheme() && (url.host().isEmpty() || url.host() == baseURL.host())) { QString baseStr = FileSystem::ensureTrailingSlash(baseURL).path(); QString urlStr = FileSystem::ensureTrailingSlash(url).path(); if (urlStr.startsWith(baseStr)) { QString relURL = urlStr.mid(baseStr.length()); if (relURL.endsWith('/')) relURL.truncate(relURL.length() - 1); Directory *dir = diskUsage->getDirectory(relURL); if (dir) { diskUsage->changeDirectory(dir); return; } } } } diskUsage->load(url); } void DiskUsageViewer::closeUrl() { if (diskUsage) diskUsage->close(); } void DiskUsageViewer::setStatusLabel(QLabel *statLabel, QString pref) { statusLabel = statLabel; - prefix = pref; + prefix = std::move(pref); } void DiskUsageViewer::slotUpdateStatus(QString status) { if (statusLabel) { if (status.isEmpty()) { Directory * dir = diskUsage->getCurrentDir(); if (dir) status = prefix + dir->name() + " [" + KIO::convertSize(dir->size()) + ']'; } statusLabel->setText(status); } } void DiskUsageViewer::slotNewSearch() { diskUsage->load(ACTIVE_PANEL->virtualPath()); } diff --git a/krusader/KViewer/krviewer.cpp b/krusader/KViewer/krviewer.cpp index 3912e319..1db05b1b 100644 --- a/krusader/KViewer/krviewer.cpp +++ b/krusader/KViewer/krviewer.cpp @@ -1,711 +1,712 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krviewer.h" // QtCore #include #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "../defaults.h" #include "../icon.h" #include "panelviewer.h" #define VIEW_ICON "document-preview" #define EDIT_ICON "document-edit" #define MODIFIED_ICON "document-save-as" #define CHECK_MODFIED_INTERVAL 500 /* NOTE: Currently the code expects PanelViewer::openUrl() to be called only once in the panel viewer's life time - otherwise unexpected things might happen. */ QList KrViewer::viewers; KrViewer::KrViewer(QWidget *parent) : KParts::MainWindow(parent, (Qt::WindowFlags)KDE_DEFAULT_WINDOWFLAGS), manager(this, this), tabBar(this), reservedKeys(), reservedKeyActions(), sizeX(-1), sizeY(-1) { //setWFlags(Qt::WType_TopLevel | WDestructiveClose); setXMLFile("krviewer.rc"); // kpart-related xml file setHelpMenuEnabled(false); connect(&manager, &KParts::PartManager::activePartChanged, this, &KrViewer::createGUI); connect(&tabBar, &QTabWidget::currentChanged, this, &KrViewer::tabChanged); connect(&tabBar, &QTabWidget::tabCloseRequested, this, [=](int index) { tabCloseRequest(index, false); }); tabBar.setDocumentMode(true); tabBar.setMovable(true); setCentralWidget(&tabBar); printAction = KStandardAction::print(this, SLOT(print()), nullptr); copyAction = KStandardAction::copy(this, SLOT(copy()), nullptr); viewerMenu = new QMenu(this); QAction *tempAction; KActionCollection *ac = actionCollection(); #define addCustomMenuAction(name, text, slot, shortcut)\ tempAction = ac->addAction(name, this, slot);\ tempAction->setText(text);\ ac->setDefaultShortcut(tempAction, shortcut);\ viewerMenu->addAction(tempAction); addCustomMenuAction("genericViewer", i18n("&Generic Viewer"), SLOT(viewGeneric()), Qt::CTRL + Qt::SHIFT + Qt::Key_G); addCustomMenuAction("textViewer", i18n("&Text Viewer"), SLOT(viewText()), Qt::CTRL + Qt::SHIFT + Qt::Key_T); addCustomMenuAction("hexViewer", i18n("&Hex Viewer"), SLOT(viewHex()), Qt::CTRL + Qt::SHIFT + Qt::Key_H); addCustomMenuAction("lister", i18n("&Lister"), SLOT(viewLister()), Qt::CTRL + Qt::SHIFT + Qt::Key_L); viewerMenu->addSeparator(); addCustomMenuAction("textEditor", i18n("Text &Editor"), SLOT(editText()), Qt::CTRL + Qt::SHIFT + Qt::Key_E); viewerMenu->addSeparator(); QList actList = menuBar()->actions(); bool hasPrint = false, hasCopy = false; foreach(QAction *a, actList) { if (a->shortcut().matches(printAction->shortcut()) != QKeySequence::NoMatch) hasPrint = true; if (a->shortcut().matches(copyAction->shortcut()) != QKeySequence::NoMatch) hasCopy = true; } QAction *printAct = viewerMenu->addAction(printAction->icon(), printAction->text(), this, SLOT(print())); if (hasPrint) printAct->setShortcut(printAction->shortcut()); QAction *copyAct = viewerMenu->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy())); if (hasCopy) copyAct->setShortcut(copyAction->shortcut()); viewerMenu->addSeparator(); configKeysAction = ac->addAction(KStandardAction::KeyBindings, this, SLOT(configureShortcuts())); viewerMenu->addAction(configKeysAction); viewerMenu->addSeparator(); detachAction = ac->addAction("detachTab", this, SLOT(detachTab())); detachAction->setText(i18n("&Detach Tab")); //no point in detaching only one tab.. detachAction->setEnabled(false); ac->setDefaultShortcut(detachAction, Qt::META + Qt::Key_D); viewerMenu->addAction(detachAction); quitAction = ac->addAction(KStandardAction::Quit, this, SLOT(close())); viewerMenu->addAction(quitAction); QList shortcuts; tabCloseAction = ac->addAction("closeTab", this, SLOT(tabCloseRequest())); tabCloseAction->setText(i18n("&Close Current Tab")); shortcuts = KStandardShortcut::close(); shortcuts.append(Qt::Key_Escape); ac->setDefaultShortcuts(tabCloseAction, shortcuts); tabNextAction = ac->addAction("nextTab", this, SLOT(nextTab())); tabNextAction->setText(i18n("&Next Tab")); shortcuts = KStandardShortcut::tabNext(); shortcuts.append(Qt::CTRL + Qt::Key_Tab); // reenforce QTabWidget shortcut ac->setDefaultShortcuts(tabNextAction, shortcuts); tabPrevAction = ac->addAction("prevTab", this, SLOT(prevTab())); tabPrevAction->setText(i18n("&Previous Tab")); shortcuts = KStandardShortcut::tabPrev(); shortcuts.append(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab); // reenforce QTabWidget shortcut ac->setDefaultShortcuts(tabPrevAction, shortcuts); tabBar.setTabsClosable(true); checkModified(); KConfigGroup group(krConfig, "KrViewerWindow"); int sx = group.readEntry("Window Width", -1); int sy = group.readEntry("Window Height", -1); if (sx != -1 && sy != -1) resize(sx, sy); else resize(900, 700); if (group.readEntry("Window Maximized", false)) { setWindowState(windowState() | Qt::WindowMaximized); } // filtering out the key events menuBar() ->installEventFilter(this); } KrViewer::~KrViewer() { disconnect(&manager, SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(createGUI(KParts::Part*))); viewers.removeAll(this); // close tabs before deleting tab bar - this avoids Qt bug 26115 // https://bugreports.qt-project.org/browse/QTBUG-26115 while(tabBar.count()) tabCloseRequest(tabBar.currentIndex(), true); delete printAction; delete copyAction; } void KrViewer::configureDeps() { PanelEditor::configureDeps(); } void KrViewer::createGUI(KParts::Part* part) { KParts::MainWindow::createGUI(part); updateActions(); toolBar() ->show(); statusBar() ->show(); // the KParts part may override the viewer shortcuts. We prevent it // by installing an event filter on the menuBar() and the part reservedKeys.clear(); reservedKeyActions.clear(); QList list = viewerMenu->actions(); // also add the actions that are not in the menu... list << tabCloseAction << tabNextAction << tabPrevAction; // getting the key sequences of the viewer menu for (int w = 0; w != list.count(); w++) { QAction *act = list[ w ]; QList sequences = act->shortcuts(); foreach(QKeySequence keySeq, sequences) { for(int i = 0; i < keySeq.count(); ++i) { reservedKeys.push_back(keySeq[i]); reservedKeyActions.push_back(act); //the same action many times in case of multiple shortcuts } } } // and "fix" the menubar QList actList = menuBar()->actions(); viewerMenu->setTitle(i18n("&KrViewer")); QAction * act = menuBar() ->addMenu(viewerMenu); act->setData(QVariant(70)); menuBar() ->show(); } void KrViewer::configureShortcuts() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this); } bool KrViewer::eventFilter(QObject * /* watched */, QEvent * e) { // TODO: after porting to Qt5/KF5 we never catch *ANY* KeyPress or ShortcutOverride events here anymore. // Should look into if there is any way to fix it. Currently if a KPart has same shortcut as KrViewer then // it causes a conflict, messagebox shown to user and no action triggered. if (e->type() == QEvent::ShortcutOverride) { auto* ke = (QKeyEvent*) e; if (reservedKeys.contains(ke->key())) { ke->accept(); QAction *act = reservedKeyActions[ reservedKeys.indexOf(ke->key())]; if (act != nullptr) { // don't activate the close functions immediately! // it can cause crash if (act == tabCloseAction || act == quitAction) { QTimer::singleShot(0, act, &QAction::trigger); } else { act->activate(QAction::Trigger); } } return true; } } else if (e->type() == QEvent::KeyPress) { auto* ke = (QKeyEvent*) e; if (reservedKeys.contains(ke->key())) { ke->accept(); return true; } } return false; } KrViewer* KrViewer::getViewer(bool new_window) { if (!new_window) { if (viewers.isEmpty()) { viewers.prepend(new KrViewer()); // add to first (active) } else { if (viewers.first()->isMinimized()) { // minimized? -> show it again viewers.first()->setWindowState((viewers.first()->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); viewers.first()->show(); } viewers.first()->raise(); viewers.first()->activateWindow(); } return viewers.first(); } else { auto *newViewer = new KrViewer(); viewers.prepend(newViewer); return newViewer; } } void KrViewer::view(QUrl url, QWidget * parent) { KConfigGroup group(krConfig, "General"); bool defaultWindow = group.readEntry("View In Separate Window", _ViewInSeparateWindow); - view(url, Default, defaultWindow, parent); + view(std::move(url), Default, defaultWindow, parent); } void KrViewer::view(QUrl url, Mode mode, bool new_window, QWidget * parent) { KrViewer* viewer = getViewer(new_window); - viewer->viewInternal(url, mode, parent); + viewer->viewInternal(std::move(url), mode, parent); viewer->show(); } void KrViewer::edit(QUrl url, QWidget * parent) { - edit(url, Text, -1, parent); + edit(std::move(url), Text, -1, parent); } -void KrViewer::edit(QUrl url, Mode mode, int new_window, QWidget * parent) +void KrViewer::edit(const QUrl& url, Mode mode, int new_window, QWidget * parent) { KConfigGroup group(krConfig, "General"); QString editor = group.readEntry("Editor", _Editor); if (new_window == -1) new_window = group.readEntry("View In Separate Window", _ViewInSeparateWindow); if (editor != "internal editor" && !editor.isEmpty()) { KProcess proc; QStringList cmdArgs = KShell::splitArgs(editor, KShell::TildeExpand); if (cmdArgs.isEmpty()) { KMessageBox::error(krMainWindow, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in editor command:\n%1", editor)); return; } // if the file is local, pass a normal path and not a url. this solves // the problem for editors that aren't url-aware proc << cmdArgs << url.toDisplayString(QUrl::PreferLocalFile); if (!proc.startDetached()) KMessageBox::sorry(krMainWindow, i18n("Can not open \"%1\"", editor)); return; } KrViewer* viewer = getViewer(new_window); viewer->editInternal(url, mode, parent); viewer->show(); } void KrViewer::addTab(PanelViewerBase *pvb) { int tabIndex = tabBar.addTab(pvb, makeTabIcon(pvb), makeTabText(pvb)); tabBar.setCurrentIndex(tabIndex); tabBar.setTabToolTip(tabIndex, makeTabToolTip(pvb)); updateActions(); // now we can offer the option to detach tabs (we have more than one) if (tabBar.count() > 1) { detachAction->setEnabled(true); } tabBar.show(); connect(pvb, &PanelViewerBase::openUrlFinished, this, &KrViewer::openUrlFinished); connect(pvb, &PanelViewerBase::urlChanged, this, &KrViewer::tabURLChanged); } void KrViewer::tabURLChanged(PanelViewerBase *pvb, const QUrl &url) { Q_UNUSED(url) refreshTab(pvb); } void KrViewer::openUrlFinished(PanelViewerBase *pvb, bool success) { if (success) { KParts::ReadOnlyPart *part = pvb->part(); if (part) { if (!isPartAdded(part)) addPart(part); if (tabBar.currentWidget() == pvb) { manager.setActivePart(part); if (part->widget()) part->widget()->setFocus(); } } } else { tabCloseRequest(tabBar.currentIndex(), false); } } void KrViewer::tabChanged(int index) { QWidget *w = tabBar.widget(index); if(!w) return; KParts::ReadOnlyPart *part = static_cast(w)->part(); if (part && isPartAdded(part)) { manager.setActivePart(part); if (part->widget()) part->widget()->setFocus(); } else manager.setActivePart(nullptr); // set this viewer to be the main viewer if (viewers.removeAll(this)) viewers.prepend(this); // move to first } void KrViewer::tabCloseRequest(int index, bool force) { // important to save as returnFocusTo will be cleared at removePart QWidget *returnFocusToThisWidget = returnFocusTo; auto *pvb = static_cast(tabBar.widget(index)); if (!pvb) return; if (!force && !pvb->queryClose()) return; if (pvb->part() && isPartAdded(pvb->part())) removePart(pvb->part()); disconnect(pvb, nullptr, this, nullptr); pvb->closeUrl(); tabBar.removeTab(index); delete pvb; pvb = nullptr; if (tabBar.count() <= 0) { if (returnFocusToThisWidget) { returnFocusToThisWidget->raise(); returnFocusToThisWidget->activateWindow(); } else { krMainWindow->raise(); krMainWindow->activateWindow(); } QTimer::singleShot(0, this, &KrViewer::close); } else if (tabBar.count() == 1) { // no point in detaching only one tab.. detachAction->setEnabled(false); } } void KrViewer::tabCloseRequest() { tabCloseRequest(tabBar.currentIndex()); } bool KrViewer::queryClose() { KConfigGroup group(krConfig, "KrViewerWindow"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); for (int i = 0; i != tabBar.count(); i++) { auto* pvb = static_cast(tabBar.widget(i)); if (!pvb) continue; tabBar.setCurrentIndex(i); if (!pvb->queryClose()) return false; } return true; } void KrViewer::viewGeneric() { auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) viewInternal(pvb->url(), Generic); } void KrViewer::viewText() { auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) viewInternal(pvb->url(), Text); } void KrViewer::viewLister() { auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) viewInternal(pvb->url(), Lister); } void KrViewer::viewHex() { auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) viewInternal(pvb->url(), Hex); } void KrViewer::editText() { auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) editInternal(pvb->url(), Text); } void KrViewer::checkModified() { QTimer::singleShot(CHECK_MODFIED_INTERVAL, this, &KrViewer::checkModified); auto* pvb = static_cast(tabBar.currentWidget()); if (pvb) refreshTab(pvb); } void KrViewer::refreshTab(PanelViewerBase* pvb) { int ndx = tabBar.indexOf(pvb); tabBar.setTabText(ndx, makeTabText(pvb)); tabBar.setTabIcon(ndx, makeTabIcon(pvb)); tabBar.setTabToolTip(ndx, makeTabToolTip(pvb)); } void KrViewer::nextTab() { int index = (tabBar.currentIndex() + 1) % tabBar.count(); tabBar.setCurrentIndex(index); } void KrViewer::prevTab() { int index = (tabBar.currentIndex() - 1) % tabBar.count(); while (index < 0) index += tabBar.count(); tabBar.setCurrentIndex(index); } void KrViewer::detachTab() { auto* pvb = static_cast(tabBar.currentWidget()); if (!pvb) return; KrViewer* viewer = getViewer(true); bool wasPartAdded = false; KParts::ReadOnlyPart *part = pvb->part(); if (part && isPartAdded(part)) { wasPartAdded = true; removePart(part); } disconnect(pvb, nullptr, this, nullptr); tabBar.removeTab(tabBar.indexOf(pvb)); if (tabBar.count() == 1) { //no point in detaching only one tab.. detachAction->setEnabled(false); } pvb->setParent(&viewer->tabBar); pvb->move(QPoint(0, 0)); viewer->addTab(pvb); if (wasPartAdded) { viewer->addPart(part); if (part->widget()) part->widget()->setFocus(); } viewer->show(); } void KrViewer::changeEvent(QEvent *e) { if (e->type() == QEvent::ActivationChange && isActiveWindow()) if (viewers.removeAll(this)) viewers.prepend(this); // move to first } void KrViewer::print() { auto* pvb = static_cast(tabBar.currentWidget()); if (!pvb || !pvb->part() || !isPartAdded(pvb->part())) return; KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(pvb->part()); if (ext && ext->isActionEnabled("print")) Invoker(ext, SLOT(print())).invoke(); } void KrViewer::copy() { auto* pvb = static_cast(tabBar.currentWidget()); if (!pvb || !pvb->part() || !isPartAdded(pvb->part())) return; KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(pvb->part()); if (ext && ext->isActionEnabled("copy")) Invoker(ext, SLOT(copy())).invoke(); } void KrViewer::updateActions() { QList actList = toolBar()->actions(); bool hasPrint = false, hasCopy = false; foreach(QAction *a, actList) { if (a->text() == printAction->text()) hasPrint = true; if (a->text() == copyAction->text()) hasCopy = true; } if (!hasPrint) toolBar()->addAction(printAction->icon(), printAction->text(), this, SLOT(print())); if (!hasCopy) toolBar()->addAction(copyAction->icon(), copyAction->text(), this, SLOT(copy())); } bool KrViewer::isPartAdded(KParts::Part* part) { return manager.parts().contains(part); } void KrViewer::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } KParts::MainWindow::resizeEvent(e); } QString KrViewer::makeTabText(PanelViewerBase *pvb) { QString fileName = pvb->url().fileName(); if (pvb->isModified()) fileName.prepend("*"); return pvb->isEditor() ? i18nc("filename (filestate)", "%1 (Editing)", fileName) : i18nc("filename (filestate)", "%1 (Viewing)", fileName); } QString KrViewer::makeTabToolTip(PanelViewerBase *pvb) { QString url = pvb->url().toDisplayString(QUrl::PreferLocalFile); return pvb->isEditor() ? i18nc("filestate: filename", "Editing: %1", url) : i18nc("filestate: filename", "Viewing: %1", url); } QIcon KrViewer::makeTabIcon(PanelViewerBase *pvb) { QString iconName; if (pvb->isModified()) iconName = MODIFIED_ICON; else if (pvb->isEditor()) iconName = EDIT_ICON; else iconName = VIEW_ICON; return Icon(iconName); } void KrViewer::addPart(KParts::ReadOnlyPart *part) { Q_ASSERT(part); Q_ASSERT(!isPartAdded(part)); if (isPartAdded(part)) { qDebug()<<"part already added:"<installEventFilter(this); manager.addPart(part, false); // don't automatically set active part } void KrViewer::removePart(KParts::ReadOnlyPart *part) { Q_ASSERT(part); Q_ASSERT(isPartAdded(part)); if (isPartAdded(part)) { disconnect(part, nullptr, this, nullptr); part->removeEventFilter(this); manager.removePart(part); } else qDebug()<<"part hasn't been added:"<openUrl(url); + viewWidget->openUrl(std::move(url)); } void KrViewer::editInternal(QUrl url, Mode mode, QWidget * parent) { returnFocusTo = parent; PanelViewerBase* editWidget = new PanelEditor(&tabBar, mode); addTab(editWidget); - editWidget->openUrl(url); + editWidget->openUrl(std::move(url)); } diff --git a/krusader/KViewer/krviewer.h b/krusader/KViewer/krviewer.h index c1256a1d..52e42bf9 100644 --- a/krusader/KViewer/krviewer.h +++ b/krusader/KViewer/krviewer.h @@ -1,154 +1,154 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRVIEWER_H #define KRVIEWER_H // QtCore #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" class PanelViewerBase; class KrViewer : public KParts::MainWindow { Q_OBJECT public: virtual ~KrViewer(); enum Mode {Generic, Text, Hex, Lister, Default}; static void view(QUrl url, QWidget * parent = krMainWindow); static void view(QUrl url, Mode mode, bool new_window, QWidget * parent = krMainWindow); static void edit(QUrl url, QWidget * parent); - static void edit(QUrl url, Mode mode = Text, int new_window = -1, QWidget * parent = krMainWindow); + static void edit(const QUrl& url, Mode mode = Text, int new_window = -1, QWidget * parent = krMainWindow); static void configureDeps(); virtual bool eventFilter(QObject * watched, QEvent * e) Q_DECL_OVERRIDE; public slots: void createGUI(KParts::Part*); void configureShortcuts(); void viewGeneric(); void viewText(); void viewHex(); void viewLister(); void editText(); void print(); void copy(); void tabChanged(int index); void tabURLChanged(PanelViewerBase * pvb, const QUrl &url); void tabCloseRequest(int index, bool force = false); void tabCloseRequest(); void nextTab(); void prevTab(); void detachTab(); void checkModified(); protected: virtual bool queryClose() Q_DECL_OVERRIDE; virtual void changeEvent(QEvent *e) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; virtual void focusInEvent(QFocusEvent *) Q_DECL_OVERRIDE { if (viewers.removeAll(this)) viewers.prepend(this); } // move to first private slots: void openUrlFinished(PanelViewerBase *pvb, bool success); private: explicit KrViewer(QWidget *parent = 0); void addTab(PanelViewerBase* pvb); void updateActions(); void refreshTab(PanelViewerBase* pvb); void viewInternal(QUrl url, Mode mode, QWidget * parent = krMainWindow); void editInternal(QUrl url, Mode mode, QWidget * parent = krMainWindow); void addPart(KParts::ReadOnlyPart *part); void removePart(KParts::ReadOnlyPart *part); bool isPartAdded(KParts::Part* part); static KrViewer* getViewer(bool new_window); static QString makeTabText(PanelViewerBase *pvb); static QString makeTabToolTip(PanelViewerBase *pvb); static QIcon makeTabIcon(PanelViewerBase *pvb); KParts::PartManager manager; QMenu* viewerMenu; QTabWidget tabBar; QPointer returnFocusTo; QAction *detachAction; QAction *printAction; QAction *copyAction; QAction *quitAction; QAction *configKeysAction; QAction *tabCloseAction; QAction *tabNextAction; QAction *tabPrevAction; static QList viewers; // the first viewer is the active one QList reservedKeys; // the reserved key sequences QList reservedKeyActions; // the IDs of the reserved keys int sizeX; int sizeY; }; class Invoker : public QObject { Q_OBJECT public: Invoker(QObject *recv, const char * slot) { connect(this, SIGNAL(invokeSignal()), recv, slot); } void invoke() { emit invokeSignal(); } signals: void invokeSignal(); }; #endif diff --git a/krusader/KViewer/lister.cpp b/krusader/KViewer/lister.cpp index a9070d24..9553f23a 100644 --- a/krusader/KViewer/lister.cpp +++ b/krusader/KViewer/lister.cpp @@ -1,2276 +1,2276 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * * Copyright (C) 2009-2018 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 "lister.h" // QtCore #include #include #include #include #include // QtGui #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include // QtPrintSupport #include #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../kractions.h" #include "../GUI/krremoteencodingmenu.h" #define SEARCH_CACHE_CHARS 100000 #define SEARCH_MAX_ROW_LEN 4000 #define CONTROL_CHAR 752 #define CACHE_SIZE 1048576 // cache size set to 1MiB ListerTextArea::ListerTextArea(Lister *lister, QWidget *parent) : KTextEdit(parent), _lister(lister) { connect(this, &QTextEdit::cursorPositionChanged, this, &ListerTextArea::slotCursorPositionChanged); _tabWidth = 4; setWordWrapMode(QTextOption::NoWrap); setLineWrapMode(QTextEdit::NoWrap); // zoom shortcuts connect(new QShortcut(QKeySequence("Ctrl++"), this), &QShortcut::activated, this, [=]() { zoomIn(); }); connect(new QShortcut(QKeySequence("Ctrl+-"), this), &QShortcut::activated, this, [=]() { zoomOut(); }); // start cursor blinking connect(&_blinkTimer, &QTimer::timeout, this, [=] { if (!_cursorBlinkMutex.tryLock()) { return; } setCursorWidth(cursorWidth() == 0 ? 2 : 0); _cursorBlinkMutex.unlock(); }); _blinkTimer.start(500); } void ListerTextArea::reset() { _screenStartPos = 0; _cursorPos = 0; _cursorAnchorPos = -1; _cursorAtFirstColumn = true; calculateText(); } void ListerTextArea::sizeChanged() { if (_cursorAnchorPos > _lister->fileSize()) _cursorAnchorPos = -1; if (_cursorPos > _lister->fileSize()) _cursorPos = _lister->fileSize(); redrawTextArea(true); } void ListerTextArea::resizeEvent(QResizeEvent * event) { KTextEdit::resizeEvent(event); redrawTextArea(); } void ListerTextArea::calculateText(const bool forcedUpdate) { const QRect contentRect = viewport()->contentsRect(); const QFontMetrics fm(font()); const int fontHeight = std::max(fm.height(), 1); // This is quite accurate (although not perfect) way of getting // a single character width along with its surrounding space. const float fontWidth = (fm.width("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW") - fm.width("W")) / 99.0; const int sizeY = contentRect.height() / fontHeight; _pageSize = sizeY; const int textViewportWidth = std::max(contentRect.width() - (int) fontWidth, 0); setTabStopWidth(fontWidth * _tabWidth); const int sizeX = textViewportWidth / fontWidth; _sizeChanged = (_sizeY != sizeY) || (_sizeX != sizeX) || forcedUpdate; _sizeY = sizeY; _sizeX = sizeX; QList rowStarts; QStringList list = readLines(_screenStartPos, _screenEndPos, _sizeY, &rowStarts); if (_sizeChanged) { _averagePageSize = _screenEndPos - _screenStartPos; setUpScrollBar(); } const QStringList listRemn = readLines(_screenEndPos, _screenEndPos, 1); list << listRemn; if (list != _rowContent) { _cursorBlinkMutex.lock(); _blinkTimer.stop(); setCursorWidth(0); setPlainText(list.join("\n")); if (_cursorAnchorPos == -1 || _cursorAnchorPos == _cursorPos) { clearSelection(); _blinkTimer.start(500); } _cursorBlinkMutex.unlock(); _rowContent = list; _rowStarts = rowStarts; if (_rowStarts.size() < _sizeY) { _rowStarts << _screenEndPos; } } } qint64 ListerTextArea::textToFilePositionOnScreen(const int x, const int y, bool &isfirst) { isfirst = (x == 0); if (y >= _rowStarts.count()) { return 0; } const qint64 rowStart = _rowStarts[ y ]; if (x == 0) { return rowStart; } if (_hexMode) { const qint64 pos = rowStart + _lister->hexPositionToIndex(_sizeX, x); if (pos > _lister->fileSize()) { return _lister->fileSize(); } return pos; } // we can't use fromUnicode because of the invalid encoded chars const int maxBytes = 2 * _sizeX * MAX_CHAR_LENGTH; QByteArray chunk = _lister->cacheChunk(rowStart, maxBytes); QTextStream stream(&chunk); stream.setCodec(_lister->codec()); stream.read(x); return rowStart + stream.pos(); } void ListerTextArea::fileToTextPositionOnScreen(const qint64 p, const bool isfirst, int &x, int &y) { // check if cursor is outside of visible area if (p < _screenStartPos || p > _screenEndPos || _rowStarts.count() < 1) { x = -1; y = (p > _screenEndPos) ? -2 : -1; return; } // find row y = 0; while (y < _rowStarts.count() && _rowStarts[ y ] <= p) { y++; } y--; if (y < 0) { x = y = -1; return; } const qint64 rowStart = _rowStarts[ y ]; if (_hexMode) { x = _lister->hexIndexToPosition(_sizeX, (int)(p - rowStart)); return; } // find column const int maxBytes = 2 * _sizeX * MAX_CHAR_LENGTH; x = 0; if (rowStart >= p) { if ((rowStart == p) && !isfirst && y > 0) { const qint64 previousRow = _rowStarts[ y - 1 ]; const QByteArray chunk = _lister->cacheChunk(previousRow, maxBytes); QByteArray cachedBuffer = chunk.left(p - previousRow); QTextStream stream(&cachedBuffer); stream.setCodec(_lister->codec()); stream.read(_rowContent[ y - 1].length()); if (previousRow + stream.pos() == p) { y--; x = _rowContent[ y ].length(); } } return; } const QByteArray chunk = _lister->cacheChunk(rowStart, maxBytes); const QByteArray cachedBuffer = chunk.left(p - rowStart); x = _lister->codec()->toUnicode(cachedBuffer).length(); } void ListerTextArea::getCursorPosition(int &x, int &y) { getScreenPosition(textCursor().position(), x, y); } void ListerTextArea::getScreenPosition(const int position, int &x, int &y) { x = position; y = 0; foreach (const QString &row, _rowContent) { const int rowLen = row.length() + 1; if (x < rowLen) { return; } x -= rowLen; y++; } } void ListerTextArea::setCursorPositionOnScreen(const int x, const int y, const int anchorX, const int anchorY) { setCursorWidth(0); int finalX = x; int finalY = y; if (finalX == -1 || finalY < 0) { if (anchorY == -1) { return; } if (finalY == -2) { finalY = _sizeY; finalX = (_rowContent.count() > _sizeY) ? _rowContent[ _sizeY ].length() : 0; } else finalX = finalY = 0; } const int realSizeY = std::min(_sizeY + 1, _rowContent.count()); const auto setUpCursor = [&] (const int cursorX, const int cursorY, const QTextCursor::MoveMode mode) -> bool { if (cursorY > realSizeY) { return false; } _skipCursorChangedListener = true; moveCursor(QTextCursor::Start, mode); for (int i = 0; i < cursorY; i++) { moveCursor(QTextCursor::Down, mode); } int finalCursorX = cursorX; if (_rowContent.count() > cursorY && finalCursorX > _rowContent[ cursorY ].length()) { finalCursorX = _rowContent[ cursorY ].length(); } for (int i = 0; i < finalCursorX; i++) { moveCursor(QTextCursor::Right, mode); } _skipCursorChangedListener = false; return true; }; QTextCursor::MoveMode mode = QTextCursor::MoveAnchor; // set cursor anchor if (anchorX != -1 && anchorY != -1) { const bool canContinue = setUpCursor(anchorX, anchorY, mode); if (!canContinue) { return; } mode = QTextCursor::KeepAnchor; } // set cursor position setUpCursor(finalX, finalY, mode); } qint64 ListerTextArea::getCursorPosition(bool &isfirst) { if (cursorWidth() == 0) { isfirst = _cursorAtFirstColumn; return _cursorPos; } int x, y; getCursorPosition(x, y); return textToFilePositionOnScreen(x, y, isfirst); } void ListerTextArea::setCursorPositionInDocument(const qint64 p, const bool isfirst) { _cursorPos = p; int x, y; fileToTextPositionOnScreen(p, isfirst, x, y); bool startBlinkTimer = _screenStartPos <= _cursorPos && _cursorPos <= _screenEndPos; int anchorX = -1, anchorY = -1; if (_cursorAnchorPos != -1 && _cursorAnchorPos != p) { int anchPos = _cursorAnchorPos; bool anchorBelow = false, anchorAbove = false; if (anchPos < _screenStartPos) { anchPos = _screenStartPos; anchorY = -2; anchorAbove = true; } if (anchPos > _screenEndPos) { anchPos = _screenEndPos; anchorY = -3; anchorBelow = true; } fileToTextPositionOnScreen(anchPos, isfirst, anchorX, anchorY); if (_hexMode) { if (anchorAbove) { anchorX = 0; } if (anchorBelow && _rowContent.count() > 0) { anchorX = _rowContent[ 0 ].length(); } } startBlinkTimer = startBlinkTimer && !anchorAbove && !anchorBelow; } if (startBlinkTimer) { _blinkTimer.start(500); } setCursorPositionOnScreen(x, y, anchorX, anchorY); _lister->slotUpdate(); } void ListerTextArea::slotCursorPositionChanged() { if (_skipCursorChangedListener) { return; } int cursorX, cursorY; getCursorPosition(cursorX, cursorY); _cursorAtFirstColumn = (cursorX == 0); _cursorPos = textToFilePositionOnScreen(cursorX, cursorY, _cursorAtFirstColumn); _lister->slotUpdate(); } QString ListerTextArea::readSection(const qint64 p1, const qint64 p2) { if (p1 == p2) return QString(); qint64 sel1 = p1; qint64 sel2 = p2; if (sel1 > sel2) { std::swap(sel1, sel2); } QString section; if (_hexMode) { while (sel1 != sel2) { const QStringList list = _lister->readHexLines(sel1, sel2, _sizeX, 1); if (list.isEmpty()) { break; } if (!section.isEmpty()) { section += QChar('\n'); } section += list.at(0); } return section; } qint64 pos = sel1; QScopedPointer decoder(_lister->codec()->makeDecoder()); do { const int maxBytes = std::min(_sizeX * _sizeY * MAX_CHAR_LENGTH, (int) (sel2 - pos)); const QByteArray chunk = _lister->cacheChunk(pos, maxBytes); if (chunk.isEmpty()) break; section += decoder->toUnicode(chunk); pos += chunk.size(); } while (pos < sel2); return section; } QStringList ListerTextArea::readLines(qint64 filePos, qint64 &endPos, const int lines, QList * locs) { QStringList list; if (_hexMode) { endPos = _lister->fileSize(); if (filePos >= endPos) { return list; } const int bytes = _lister->hexBytesPerLine(_sizeX); qint64 startPos = (filePos / bytes) * bytes; qint64 shiftPos = startPos; list = _lister->readHexLines(shiftPos, endPos, _sizeX, lines); endPos = shiftPos; if (locs) { for (int i = 0; i < list.count(); i++) { (*locs) << startPos; startPos += bytes; } } return list; } endPos = filePos; const int maxBytes = _sizeX * _sizeY * MAX_CHAR_LENGTH; const QByteArray chunk = _lister->cacheChunk(filePos, maxBytes); if (chunk.isEmpty()) return list; int byteCounter = 0; QString row = ""; int effLength = 0; if (locs) (*locs) << filePos; bool skipImmediateNewline = false; const auto performNewline = [&] (qint64 nextRowStartOffset) { list << row; effLength = 0; row = ""; if (locs) { (*locs) << (filePos + nextRowStartOffset); } }; QScopedPointer decoder(_lister->codec()->makeDecoder()); while (byteCounter < chunk.size() && list.size() < lines) { const int lastCnt = byteCounter; QString chr = decoder->toUnicode(chunk.mid(byteCounter++, 1)); if (chr.isEmpty()) { continue; } if ((chr[ 0 ] < 32) && (chr[ 0 ] != '\n') && (chr[ 0 ] != '\t')) { chr = QChar(CONTROL_CHAR); } if (chr == "\n") { if (!skipImmediateNewline) { performNewline(byteCounter); } skipImmediateNewline = false; continue; } skipImmediateNewline = false; if (chr == "\t") { effLength += _tabWidth - (effLength % _tabWidth) - 1; if (effLength > _sizeX) { performNewline(lastCnt); } } row += chr; effLength++; if (effLength >= _sizeX) { performNewline(byteCounter); skipImmediateNewline = true; } } if (list.size() < lines) list << row; endPos = filePos + byteCounter; return list; } void ListerTextArea::setUpScrollBar() { if (_averagePageSize == _lister->fileSize()) { _lister->scrollBar()->setPageStep(0); _lister->scrollBar()->setMaximum(0); _lister->scrollBar()->hide(); _lastPageStartPos = 0; } else { const int maxPage = MAX_CHAR_LENGTH * _sizeX * _sizeY; qint64 pageStartPos = _lister->fileSize() - maxPage; qint64 endPos; if (pageStartPos < 0) pageStartPos = 0; QStringList list = readLines(pageStartPos, endPos, maxPage); if (list.count() <= _sizeY) { _lastPageStartPos = 0; } else { readLines(pageStartPos, _lastPageStartPos, list.count() - _sizeY); } const int maximum = (_lastPageStartPos > SLIDER_MAX) ? SLIDER_MAX : _lastPageStartPos; int pageSize = (_lastPageStartPos > SLIDER_MAX) ? SLIDER_MAX * _averagePageSize / _lastPageStartPos : _averagePageSize; if (pageSize == 0) pageSize++; _lister->scrollBar()->setPageStep(pageSize); _lister->scrollBar()->setMaximum(maximum); _lister->scrollBar()->show(); } } void ListerTextArea::keyPressEvent(QKeyEvent * ke) { if (KrGlobal::copyShortcut == QKeySequence(ke->key() | ke->modifiers())) { copySelectedToClipboard(); ke->accept(); return; } if (ke->modifiers() == Qt::NoModifier || ke->modifiers() & Qt::ShiftModifier) { qint64 newAnchor = -1; if (ke->modifiers() & Qt::ShiftModifier) { newAnchor = _cursorAnchorPos; if (_cursorAnchorPos == -1) newAnchor = _cursorPos; } switch (ke->key()) { case Qt::Key_F3: ke->accept(); if (ke->modifiers() == Qt::ShiftModifier) _lister->searchPrev(); else _lister->searchNext(); return; case Qt::Key_Home: case Qt::Key_End: _cursorAnchorPos = newAnchor; break; case Qt::Key_Left: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); int x, y; getCursorPosition(x, y); if (y == 0 && x == 0) slotActionTriggered(QAbstractSlider::SliderSingleStepSub); } break; case Qt::Key_Right: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); if (textCursor().position() == toPlainText().length()) slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); } break; case Qt::Key_Up: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); int x, y; getCursorPosition(x, y); if (y == 0) slotActionTriggered(QAbstractSlider::SliderSingleStepSub); } break; case Qt::Key_Down: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); int x, y; getCursorPosition(x, y); if (y >= _sizeY-1) slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); } break; case Qt::Key_PageDown: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); ke->accept(); int x, y; getCursorPosition(x, y); slotActionTriggered(QAbstractSlider::SliderPageStepAdd); y += _sizeY - _skippedLines; if (y > _rowContent.count()) { y = _rowContent.count() - 1; if (y > 0) x = _rowContent[ y - 1 ].length(); else x = 0; } _cursorPos = textToFilePositionOnScreen(x, y, _cursorAtFirstColumn); setCursorPositionInDocument(_cursorPos, _cursorAtFirstColumn); } return; case Qt::Key_PageUp: { _cursorAnchorPos = newAnchor; ensureVisibleCursor(); ke->accept(); int x, y; getCursorPosition(x, y); slotActionTriggered(QAbstractSlider::SliderPageStepSub); y -= _sizeY - _skippedLines; if (y < 0) { y = 0; x = 0; } _cursorPos = textToFilePositionOnScreen(x, y, _cursorAtFirstColumn); setCursorPositionInDocument(_cursorPos, _cursorAtFirstColumn); } return; } } if (ke->modifiers() == Qt::ControlModifier) { switch (ke->key()) { case Qt::Key_G: ke->accept(); _lister->jumpToPosition(); return; case Qt::Key_F: ke->accept(); _lister->enableSearch(true); return; case Qt::Key_Home: _cursorAnchorPos = -1; ke->accept(); slotActionTriggered(QAbstractSlider::SliderToMinimum); setCursorPositionInDocument((qint64)0, true); return; case Qt::Key_A: case Qt::Key_End: { _cursorAnchorPos = (ke->key() == Qt::Key_A) ? 0 : -1; ke->accept(); slotActionTriggered(QAbstractSlider::SliderToMaximum); const qint64 endPos = _lister->fileSize(); setCursorPositionInDocument(endPos, false); return; } case Qt::Key_Down: ke->accept(); slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); return; case Qt::Key_Up: ke->accept(); slotActionTriggered(QAbstractSlider::SliderSingleStepSub); return; case Qt::Key_PageDown: ke->accept(); slotActionTriggered(QAbstractSlider::SliderPageStepAdd); return; case Qt::Key_PageUp: ke->accept(); slotActionTriggered(QAbstractSlider::SliderPageStepSub); return; } } const int oldAnchor = textCursor().anchor(); KTextEdit::keyPressEvent(ke); handleAnchorChange(oldAnchor); } void ListerTextArea::mousePressEvent(QMouseEvent * e) { KTextEdit::mousePressEvent(e); // do change anchor only when shift is not pressed if (!(QGuiApplication::keyboardModifiers() & Qt::ShiftModifier)) { performAnchorChange(textCursor().anchor()); } } void ListerTextArea::mouseDoubleClickEvent(QMouseEvent * e) { _cursorAnchorPos = -1; const int oldAnchor = textCursor().anchor(); KTextEdit::mouseDoubleClickEvent(e); handleAnchorChange(oldAnchor); } void ListerTextArea::mouseMoveEvent(QMouseEvent * e) { if (e->pos().y() < 0) { slotActionTriggered(QAbstractSlider::SliderSingleStepSub); } else if (e->pos().y() > height()) { slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); } KTextEdit::mouseMoveEvent(e); } void ListerTextArea::wheelEvent(QWheelEvent * e) { int delta = e->delta(); if (delta) { // zooming if (e->modifiers() & Qt::ControlModifier) { e->accept(); if (delta > 0) { zoomIn(); } else { zoomOut(); } return; } if (delta > 0) { e->accept(); while (delta > 0) { slotActionTriggered(QAbstractSlider::SliderSingleStepSub); slotActionTriggered(QAbstractSlider::SliderSingleStepSub); slotActionTriggered(QAbstractSlider::SliderSingleStepSub); delta -= 120; } } else { e->accept(); while (delta < 0) { slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); slotActionTriggered(QAbstractSlider::SliderSingleStepAdd); delta += 120; } } setCursorPositionInDocument(_cursorPos, false); } } void ListerTextArea::slotActionTriggered(int action) { switch (action) { case QAbstractSlider::SliderSingleStepAdd: { qint64 endPos; readLines(_screenStartPos, endPos, 1); if (endPos <= _lastPageStartPos) { _screenStartPos = endPos; } } break; case QAbstractSlider::SliderSingleStepSub: { if (_screenStartPos == 0) { break; } if (_hexMode) { int bytesPerRow = _lister->hexBytesPerLine(_sizeX); _screenStartPos = (_screenStartPos / bytesPerRow) * bytesPerRow; _screenStartPos -= bytesPerRow; if (_screenStartPos < 0) { _screenStartPos = 0; } break; } int maxSize = _sizeX * _sizeY * MAX_CHAR_LENGTH; const QByteArray encodedEnter = _lister->codec()->fromUnicode(QString("\n")); qint64 readPos = _screenStartPos - maxSize; if (readPos < 0) { readPos = 0; } maxSize = _screenStartPos - readPos; const QByteArray chunk = _lister->cacheChunk(readPos, maxSize); int from = chunk.size(); while (from > 0) { from--; from = chunk.lastIndexOf(encodedEnter, from); if (from == -1) { from = 0; break; } const int backRef = std::max(from - 20, 0); const int size = from - backRef + encodedEnter.size(); const QString decoded = _lister->codec()->toUnicode(chunk.mid(backRef, size)); if (decoded.endsWith(QLatin1String("\n"))) { if (from < (chunk.size() - encodedEnter.size())) { from += encodedEnter.size(); break; } } } readPos += from; qint64 previousPos = readPos; while (readPos < _screenStartPos) { previousPos = readPos; readLines(readPos, readPos, 1); } _screenStartPos = previousPos; } break; case QAbstractSlider::SliderPageStepAdd: { _skippedLines = 0; qint64 endPos; for (int i = 0; i < _sizeY; i++) { readLines(_screenStartPos, endPos, 1); if (endPos <= _lastPageStartPos) { _screenStartPos = endPos; _skippedLines++; } else { break; } } } break; case QAbstractSlider::SliderPageStepSub: { _skippedLines = 0; if (_screenStartPos == 0) { break; } if (_hexMode) { const int bytesPerRow = _lister->hexBytesPerLine(_sizeX); _screenStartPos = (_screenStartPos / bytesPerRow) * bytesPerRow; _screenStartPos -= _sizeY * bytesPerRow; if (_screenStartPos < 0) { _screenStartPos = 0; } break; } // text lister mode int maxSize = 2 * _sizeX * _sizeY * MAX_CHAR_LENGTH; const QByteArray encodedEnter = _lister->codec()->fromUnicode(QString("\n")); qint64 readPos = _screenStartPos - maxSize; if (readPos < 0) readPos = 0; maxSize = _screenStartPos - readPos; const QByteArray chunk = _lister->cacheChunk(readPos, maxSize); maxSize = chunk.size(); int sizeY = _sizeY + 1; int origSizeY = sizeY; int from = maxSize; int lastEnter = maxSize; bool readNext = true; while (readNext) { readNext = false; while (from > 0) { from--; from = chunk.lastIndexOf(encodedEnter, from); if (from == -1) { from = 0; break; } const int backRef = std::max(from - 20, 0); const int size = from - backRef + encodedEnter.size(); QString decoded = _lister->codec()->toUnicode(chunk.mid(backRef, size)); if (decoded.endsWith(QLatin1String("\n"))) { if (from < (maxSize - encodedEnter.size())) { int arrayStart = from + encodedEnter.size(); decoded = _lister->codec()->toUnicode(chunk.mid(arrayStart, lastEnter - arrayStart)); sizeY -= ((decoded.length() / (_sizeX + 1)) + 1); if (sizeY < 0) { from = arrayStart; break; } } lastEnter = from; } } qint64 searchPos = readPos + from; QList locs; while (searchPos < _screenStartPos) { locs << searchPos; readLines(searchPos, searchPos, 1); } if (locs.count() >= _sizeY) { _screenStartPos = locs[ locs.count() - _sizeY ]; } else if (from != 0) { origSizeY += locs.count() + 1; sizeY = origSizeY; readNext = true; } else if (readPos == 0) { _screenStartPos = 0; } } } break; case QAbstractSlider::SliderToMinimum: _screenStartPos = 0; break; case QAbstractSlider::SliderToMaximum: _screenStartPos = _lastPageStartPos; break; case QAbstractSlider::SliderMove: { if (_inSliderOp) // self created call? return; qint64 pos = _lister->scrollBar()->sliderPosition(); if (pos == SLIDER_MAX) { _screenStartPos = _lastPageStartPos; break; } else if (pos == 0) { _screenStartPos = 0; break; } if (_lastPageStartPos > SLIDER_MAX) pos = _lastPageStartPos * pos / SLIDER_MAX; if (pos != 0) { if (_hexMode) { const int bytesPerRow = _lister->hexBytesPerLine(_sizeX); pos = (pos / bytesPerRow) * bytesPerRow; } else { const int maxSize = _sizeX * _sizeY * MAX_CHAR_LENGTH; qint64 readPos = pos - maxSize; if (readPos < 0) readPos = 0; qint64 previousPos = readPos; while (readPos <= pos) { previousPos = readPos; readLines(readPos, readPos, 1); } pos = previousPos; } } _screenStartPos = pos; } break; case QAbstractSlider::SliderNoAction: break; }; _inSliderOp = true; const int value = (_lastPageStartPos > SLIDER_MAX) ? SLIDER_MAX * _screenStartPos / _lastPageStartPos : _screenStartPos; _lister->scrollBar()->setSliderPosition(value); _inSliderOp = false; redrawTextArea(); } void ListerTextArea::redrawTextArea(bool forcedUpdate) { if (_redrawing) { return; } _redrawing = true; bool isfirst; const qint64 pos = getCursorPosition(isfirst); calculateText(forcedUpdate); setCursorPositionInDocument(pos, isfirst); _redrawing = false; } void ListerTextArea::ensureVisibleCursor() { if (_screenStartPos <= _cursorPos && _cursorPos <= _screenEndPos) { return; } int delta = _sizeY / 2; if (delta == 0) delta++; qint64 newScreenStart = _cursorPos; while (delta) { const int maxSize = _sizeX * MAX_CHAR_LENGTH; qint64 readPos = newScreenStart - maxSize; if (readPos < 0) readPos = 0; qint64 previousPos = readPos; while (readPos < newScreenStart) { previousPos = readPos; readLines(readPos, readPos, 1); if (readPos == previousPos) break; } newScreenStart = previousPos; delta--; } if (newScreenStart > _lastPageStartPos) { newScreenStart = _lastPageStartPos; } _screenStartPos = newScreenStart; slotActionTriggered(QAbstractSlider::SliderNoAction); } void ListerTextArea::setAnchorAndCursor(qint64 anchor, qint64 cursor) { _cursorPos = cursor; _cursorAnchorPos = anchor; ensureVisibleCursor(); setCursorPositionInDocument(cursor, false); } QString ListerTextArea::getSelectedText() { if (_cursorAnchorPos != -1 && _cursorAnchorPos != _cursorPos) { return readSection(_cursorAnchorPos, _cursorPos); } return QString(); } void ListerTextArea::copySelectedToClipboard() { const QString selection = getSelectedText(); if (!selection.isEmpty()) { QApplication::clipboard()->setText(selection); } } void ListerTextArea::clearSelection() { QTextCursor cursor = textCursor(); cursor.clearSelection(); setTextCursor(cursor); _cursorAnchorPos = -1; } void ListerTextArea::performAnchorChange(int anchor) { int x, y; bool isfirst; getScreenPosition(anchor, x, y); _cursorAnchorPos = textToFilePositionOnScreen(x, y, isfirst); } void ListerTextArea::handleAnchorChange(int oldAnchor) { const int anchor = textCursor().anchor(); if (oldAnchor != anchor) { performAnchorChange(anchor); } } void ListerTextArea::setHexMode(bool hexMode) { bool isfirst; const qint64 pos = getCursorPosition(isfirst); _hexMode = hexMode; _screenStartPos = 0; calculateText(true); setCursorPositionInDocument(pos, isfirst); ensureVisibleCursor(); } void ListerTextArea::zoomIn(int range) { KTextEdit::zoomIn(range); redrawTextArea(); } void ListerTextArea::zoomOut(int range) { KTextEdit::zoomOut(range); redrawTextArea(); } ListerPane::ListerPane(Lister *lister, QWidget *parent) : QWidget(parent), _lister(lister) { } bool ListerPane::event(QEvent *e) { const bool handled = ListerPane::handleCloseEvent(e); if (!handled) { return QWidget::event(e); } return true; } bool ListerPane::handleCloseEvent(QEvent *e) { if (e->type() == QEvent::ShortcutOverride) { auto *ke = static_cast(e); if (ke->key() == Qt::Key_Escape) { if (_lister->isSearchEnabled()) { _lister->searchDelete(); _lister->enableSearch(false); ke->accept(); return true; } if (!_lister->textArea()->getSelectedText().isEmpty()) { _lister->textArea()->clearSelection(); ke->accept(); return true; } } } return false; } ListerBrowserExtension::ListerBrowserExtension(Lister * lister) : KParts::BrowserExtension(lister) { _lister = lister; emit enableAction("copy", true); emit enableAction("print", true); } void ListerBrowserExtension::copy() { _lister->textArea()->copySelectedToClipboard(); } void ListerBrowserExtension::print() { _lister->print(); } class ListerEncodingMenu : public KrRemoteEncodingMenu { public: ListerEncodingMenu(Lister *lister, const QString &text, const QString &icon, KActionCollection *parent) : KrRemoteEncodingMenu(text, icon, parent), _lister(lister) { } protected: QString currentCharacterSet() Q_DECL_OVERRIDE { return _lister->characterSet(); } void chooseDefault() Q_DECL_OVERRIDE { _lister->setCharacterSet(QString()); } void chooseEncoding(QString encodingName) Q_DECL_OVERRIDE { QString charset = KCharsets::charsets()->encodingForName(encodingName); _lister->setCharacterSet(charset); } Lister * _lister; }; Lister::Lister(QWidget *parent) : KParts::ReadOnlyPart(parent) { setXMLFile("krusaderlisterui.rc"); _actionSaveSelected = new QAction(Icon("document-save"), i18n("Save selection..."), this); connect(_actionSaveSelected, &QAction::triggered, this, &Lister::saveSelected); actionCollection()->addAction("save_selected", _actionSaveSelected); _actionSaveAs = new QAction(Icon("document-save-as"), i18n("Save as..."), this); connect(_actionSaveAs, &QAction::triggered, this, &Lister::saveAs); actionCollection()->addAction("save_as", _actionSaveAs); _actionPrint = new QAction(Icon("document-print"), i18n("Print..."), this); connect(_actionPrint, &QAction::triggered, this, &Lister::print); actionCollection()->addAction("print", _actionPrint); actionCollection()->setDefaultShortcut(_actionPrint, Qt::CTRL + Qt::Key_P); _actionSearch = new QAction(Icon("system-search"), i18n("Search"), this); connect(_actionSearch, &QAction::triggered, this, &Lister::searchAction); actionCollection()->addAction("search", _actionSearch); actionCollection()->setDefaultShortcut(_actionSearch, Qt::CTRL + Qt::Key_F); _actionSearchNext = new QAction(Icon("go-down"), i18n("Search next"), this); connect(_actionSearchNext, &QAction::triggered, this, &Lister::searchNext); actionCollection()->addAction("search_next", _actionSearchNext); actionCollection()->setDefaultShortcut(_actionSearchNext, Qt::Key_F3); _actionSearchPrev = new QAction(Icon("go-up"), i18n("Search previous"), this); connect(_actionSearchPrev, &QAction::triggered, this, &Lister::searchPrev); actionCollection()->addAction("search_prev", _actionSearchPrev); actionCollection()->setDefaultShortcut(_actionSearchPrev, Qt::SHIFT + Qt::Key_F3); _actionJumpToPosition = new QAction(Icon("go-jump"), i18n("Jump to position"), this); connect(_actionJumpToPosition, &QAction::triggered, this, &Lister::jumpToPosition); actionCollection()->addAction("jump_to_position", _actionJumpToPosition); actionCollection()->setDefaultShortcut(_actionJumpToPosition, Qt::CTRL + Qt::Key_G); _actionHexMode = new QAction(Icon("document-preview"), i18n("Hex mode"), this); connect(_actionHexMode, &QAction::triggered, this, &Lister::toggleHexMode); actionCollection()->addAction("hex_mode", _actionHexMode); actionCollection()->setDefaultShortcut(_actionHexMode, Qt::CTRL + Qt::Key_H); new ListerEncodingMenu(this, i18n("Select charset"), "character-set", actionCollection()); QWidget * widget = new ListerPane(this, parent); widget->setFocusPolicy(Qt::StrongFocus); auto *grid = new QGridLayout(widget); _textArea = new ListerTextArea(this, widget); _textArea->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); _textArea->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); _textArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); _textArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); widget->setFocusProxy(_textArea); grid->addWidget(_textArea, 0, 0); _scrollBar = new QScrollBar(Qt::Vertical, widget); grid->addWidget(_scrollBar, 0, 1); _scrollBar->hide(); QWidget * statusWidget = new QWidget(widget); auto *hbox = new QHBoxLayout(statusWidget); _listerLabel = new QLabel(i18n("Lister:"), statusWidget); hbox->addWidget(_listerLabel); _searchProgressBar = new QProgressBar(statusWidget); _searchProgressBar->setMinimum(0); _searchProgressBar->setMaximum(1000); _searchProgressBar->setValue(0); _searchProgressBar->hide(); hbox->addWidget(_searchProgressBar); _searchStopButton = new QToolButton(statusWidget); _searchStopButton->setIcon(Icon("process-stop")); _searchStopButton->setToolTip(i18n("Stop search")); _searchStopButton->hide(); connect(_searchStopButton, &QToolButton::clicked, this, &Lister::searchDelete); hbox->addWidget(_searchStopButton); _searchLineEdit = new KLineEdit(statusWidget); _searchLineEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); _originalBackground = _searchLineEdit->palette().color(QPalette::Base); _originalForeground = _searchLineEdit->palette().color(QPalette::Text); connect(_searchLineEdit, &KLineEdit::returnPressed, this, &Lister::searchNext); connect(_searchLineEdit, &KLineEdit::textChanged, this, &Lister::searchTextChanged); hbox->addWidget(_searchLineEdit); _searchNextButton = new QPushButton(Icon("go-down"), i18n("Next"), statusWidget); _searchNextButton->setToolTip(i18n("Jump to next match")); connect(_searchNextButton, &QPushButton::clicked, this, &Lister::searchNext); hbox->addWidget(_searchNextButton); _searchPrevButton = new QPushButton(Icon("go-up"), i18n("Previous"), statusWidget); _searchPrevButton->setToolTip(i18n("Jump to previous match")); connect(_searchPrevButton, &QPushButton::clicked, this, &Lister::searchPrev); hbox->addWidget(_searchPrevButton); _searchOptions = new QPushButton(i18n("Options"), statusWidget); _searchOptions->setToolTip(i18n("Modify search behavior")); auto * menu = new QMenu(); _fromCursorAction = menu->addAction(i18n("From cursor")); _fromCursorAction->setCheckable(true); _fromCursorAction->setChecked(true); _caseSensitiveAction = menu->addAction(i18n("Case sensitive")); _caseSensitiveAction->setCheckable(true); _matchWholeWordsOnlyAction = menu->addAction(i18n("Match whole words only")); _matchWholeWordsOnlyAction->setCheckable(true); _regExpAction = menu->addAction(i18n("RegExp")); _regExpAction->setCheckable(true); _hexAction = menu->addAction(i18n("Hexadecimal")); _hexAction->setCheckable(true); _searchOptions->setMenu(menu); hbox->addWidget(_searchOptions); hbox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); _statusLabel = new QLabel(statusWidget); hbox->addWidget(_statusLabel); grid->addWidget(statusWidget, 1, 0, 1, 2); setWidget(widget); connect(_scrollBar, &QScrollBar::actionTriggered, _textArea, &ListerTextArea::slotActionTriggered); connect(&_searchUpdateTimer, &QTimer::timeout, this, &Lister::slotUpdate); new ListerBrowserExtension(this); enableSearch(false); _tempFile = new QTemporaryFile(this); _tempFile->setFileTemplate(QDir::tempPath() + QLatin1String("/krusader_lister.XXXXXX")); } bool Lister::openUrl(const QUrl &listerUrl) { _downloading = false; setUrl(listerUrl); _fileSize = 0; if (listerUrl.isLocalFile()) { _filePath = listerUrl.path(); if (!QFile::exists(_filePath)) return false; _fileSize = getFileSize(); } else { if (_tempFile->isOpen()) { _tempFile->close(); } _tempFile->open(); _filePath = _tempFile->fileName(); KIO::TransferJob *downloadJob = KIO::get(listerUrl, KIO::NoReload, KIO::HideProgressInfo); connect(downloadJob, &KIO::TransferJob::data, this, [=](KIO::Job*, QByteArray array) { if (array.size() != 0) { _tempFile->write(array); } }); connect(downloadJob, &KIO::TransferJob::result, this, [=](KJob *job) { _tempFile->flush(); if (job->error()) { /* any error occurred? */ auto *kioJob = (KIO::TransferJob *)job; KMessageBox::error(_textArea, i18n("Error reading file %1.", kioJob->url().toDisplayString(QUrl::PreferLocalFile))); } _downloading = false; _downloadUpdateTimer.stop(); slotUpdate(); }); connect(&_downloadUpdateTimer, &QTimer::timeout, this, [&]() { slotUpdate(); }); _downloadUpdateTimer.start(500); _downloading = true; } // invalidate cache _cache.clear(); _textArea->reset(); emit started(nullptr); emit setWindowCaption(listerUrl.toDisplayString()); emit completed(); return true; } QByteArray Lister::cacheChunk(const qint64 filePos, const int maxSize) { if (filePos >= _fileSize) { return QByteArray(); } int size = maxSize; if (_fileSize - filePos < size) { size = _fileSize - filePos; } if (!_cache.isEmpty() && (filePos >= _cachePos) && (filePos + size <= _cachePos + _cache.size())) { return _cache.mid(filePos - _cachePos, size); } const int negativeOffset = CACHE_SIZE * 2 / 5; qint64 cachePos = filePos - negativeOffset; if (cachePos < 0) cachePos = 0; QFile sourceFile(_filePath); if (!sourceFile.open(QIODevice::ReadOnly)) { return QByteArray(); } if (!sourceFile.seek(cachePos)) { return QByteArray(); } const QByteArray bytes = sourceFile.read(CACHE_SIZE); if (bytes.isEmpty()) { return bytes; } _cache = bytes; _cachePos = cachePos; const qint64 cacheRefIndex = filePos - _cachePos; int newSize = bytes.size() - cacheRefIndex; if (newSize < size) size = newSize; return _cache.mid(cacheRefIndex, size); } qint64 Lister::getFileSize() { return QFile(_filePath).size(); } void Lister::guiActivateEvent(KParts::GUIActivateEvent * event) { if (event->activated()) { slotUpdate(); _textArea->redrawTextArea(true); } else { enableSearch(false); } KParts::ReadOnlyPart::guiActivateEvent(event); } void Lister::slotUpdate() { const qint64 oldSize = _fileSize; _fileSize = getFileSize(); if (oldSize != _fileSize) _textArea->sizeChanged(); int cursorX = 0, cursorY = 0; _textArea->getCursorPosition(cursorX, cursorY); bool isfirst = false; const qint64 cursor = _textArea->getCursorPosition(isfirst); const int percent = (_fileSize == 0) ? 0 : (int)((201 * cursor) / _fileSize / 2); const QString status = i18n("Column: %1, Position: %2 (%3, %4%)", cursorX, cursor, _fileSize, percent); _statusLabel->setText(status); if (_searchProgressCounter) _searchProgressCounter--; } bool Lister::isSearchEnabled() { return !_searchLineEdit->isHidden() || !_searchProgressBar->isHidden(); } void Lister::enableSearch(const bool enable) { if (enable) { _listerLabel->setText(i18n("Search:")); _searchLineEdit->show(); _searchNextButton->show(); _searchPrevButton->show(); _searchOptions->show(); if (!_searchLineEdit->hasFocus()) { _searchLineEdit->setFocus(); const QString selection = _textArea->getSelectedText(); if (!selection.isEmpty()) { _searchLineEdit->setText(selection); } _searchLineEdit->selectAll(); } } else { _listerLabel->setText(i18n("Lister:")); _searchLineEdit->hide(); _searchNextButton->hide(); _searchPrevButton->hide(); _searchOptions->hide(); _textArea->setFocus(); } } void Lister::searchNext() { search(true); } void Lister::searchPrev() { search(false); } void Lister::search(const bool forward, const bool restart) { _restartFromBeginning = restart; if (_searchInProgress || _searchLineEdit->text().isEmpty()) return; if (_searchLineEdit->isHidden()) enableSearch(true); _searchPosition = forward ? 0 : _fileSize; if (_fromCursorAction->isChecked()) { bool isfirst; qint64 cursor = _textArea->getCursorPosition(isfirst); if (cursor != 0 && !forward) cursor--; if (_searchLastFailedPosition == -1 || _searchLastFailedPosition != cursor) _searchPosition = cursor; } const bool caseSensitive = _caseSensitiveAction->isChecked(); const bool matchWholeWord = _matchWholeWordsOnlyAction->isChecked(); const bool regExp = _regExpAction->isChecked(); const bool hex = _hexAction->isChecked(); if (hex) { QString hexcontent = _searchLineEdit->text(); hexcontent.remove(QLatin1String("0x")); hexcontent.remove(' '); hexcontent.remove('\t'); hexcontent.remove('\n'); hexcontent.remove('\r'); _searchHexQuery = QByteArray(); if (hexcontent.length() & 1) { setColor(false, false); return; } while (!hexcontent.isEmpty()) { const QString hexData = hexcontent.left(2); hexcontent = hexcontent.mid(2); bool ok = true; const int c = hexData.toUInt(&ok, 16); if (!ok) { setColor(false, false); return; } _searchHexQuery.push_back((char) c); } } else { _searchQuery.setContent(_searchLineEdit->text(), caseSensitive, matchWholeWord, codec()->name(), regExp); } _searchIsForward = forward; _searchHexadecimal = hex; QTimer::singleShot(0, this, &Lister::slotSearchMore); _searchInProgress = true; _searchProgressCounter = 3; enableActions(false); } void Lister::enableActions(const bool state) { _actionSearch->setEnabled(state); _actionSearchNext->setEnabled(state); _actionSearchPrev->setEnabled(state); _actionJumpToPosition->setEnabled(state); if (state) { _searchUpdateTimer.stop(); } else { slotUpdate(); } } void Lister::slotSearchMore() { if (!_searchInProgress) return; if (!_searchUpdateTimer.isActive()) { _searchUpdateTimer.start(200); } updateProgressBar(); if (!_searchIsForward) _searchPosition--; if (_searchPosition < 0 || _searchPosition >= _fileSize) { if (_restartFromBeginning) resetSearchPosition(); else { searchFailed(); return; } } int maxCacheSize = SEARCH_CACHE_CHARS; qint64 searchPos = _searchPosition; bool setPosition = true; if (!_searchIsForward) { qint64 origSearchPos = _searchPosition; searchPos -= maxCacheSize; if (searchPos <= 0) { searchPos = 0; _searchPosition = 0; setPosition = false; } qint64 diff = origSearchPos - searchPos; if (diff < maxCacheSize) maxCacheSize = diff; } const QByteArray chunk = cacheChunk(searchPos, maxCacheSize); if (chunk.isEmpty()) { searchFailed(); return; } const int chunkSize = chunk.size(); qint64 foundAnchor = -1; qint64 foundCursor = -1; int byteCounter = 0; if (_searchHexadecimal) { const int ndx = _searchIsForward ? chunk.indexOf(_searchHexQuery) : chunk.lastIndexOf(_searchHexQuery); if (chunkSize > _searchHexQuery.length()) { if (_searchIsForward) { _searchPosition = searchPos + chunkSize; if ((_searchPosition < _fileSize) && (chunkSize > _searchHexQuery.length())) _searchPosition -= _searchHexQuery.length(); byteCounter = _searchPosition - searchPos; } else { if (_searchPosition > 0) _searchPosition += _searchHexQuery.length(); } } if (ndx != -1) { foundAnchor = searchPos + ndx; foundCursor = foundAnchor + _searchHexQuery.length(); } } else { int rowStart = 0; QString row = ""; QScopedPointer decoder(_codec->makeDecoder()); while (byteCounter < chunkSize) { const QString chr = decoder->toUnicode(chunk.mid(byteCounter++, 1)); if (chr.isEmpty() && byteCounter < chunkSize) { continue; } if (chr != "\n") row += chr; if (chr == "\n" || row.length() >= SEARCH_MAX_ROW_LEN || byteCounter >= chunkSize) { if (setPosition) { _searchPosition = searchPos + byteCounter; if (!_searchIsForward) { _searchPosition++; setPosition = false; } } if (_searchQuery.checkLine(row, !_searchIsForward)) { QByteArray cachedBuffer = chunk.mid(rowStart, chunkSize - rowStart); QTextStream stream(&cachedBuffer); stream.setCodec(_codec); stream.read(_searchQuery.matchIndex()); foundAnchor = searchPos + rowStart + stream.pos(); stream.read(_searchQuery.matchLength()); foundCursor = searchPos + rowStart + stream.pos(); if (_searchIsForward) break; } row = ""; rowStart = byteCounter; } } } if (foundAnchor != -1 && foundCursor != -1) { _textArea->setAnchorAndCursor(foundAnchor, foundCursor); searchSucceeded(); return; } if (_searchIsForward && searchPos + byteCounter >= _fileSize) { if (_restartFromBeginning) resetSearchPosition(); else { searchFailed(); return; } } else if (_searchPosition <= 0 || _searchPosition >= _fileSize) { if (_restartFromBeginning) resetSearchPosition(); else { searchFailed(); return; } } QTimer::singleShot(0, this, &Lister::slotSearchMore); } void Lister::resetSearchPosition() { _restartFromBeginning = false; _searchPosition = _searchIsForward ? 0 : _fileSize - 1; } void Lister::searchSucceeded() { _searchInProgress = false; setColor(true, false); hideProgressBar(); _searchLastFailedPosition = -1; enableActions(true); } void Lister::searchFailed() { _searchInProgress = false; setColor(false, false); hideProgressBar(); bool isfirst; _searchLastFailedPosition = _textArea->getCursorPosition(isfirst); if (!_searchIsForward) _searchLastFailedPosition--; enableActions(true); } void Lister::searchDelete() { _searchInProgress = false; setColor(false, true); hideProgressBar(); _searchLastFailedPosition = -1; enableActions(true); } void Lister::searchTextChanged() { searchDelete(); if (_fileSize < 0x10000) { // autosearch files less than 64k if (!_searchLineEdit->text().isEmpty()) { bool isfirst; const qint64 anchor = _textArea->getCursorAnchor(); const qint64 cursor = _textArea->getCursorPosition(isfirst); if (cursor > anchor && anchor != -1) { _textArea->setCursorPositionInDocument(anchor, true); } search(true, true); } } } void Lister::setColor(const bool match, const bool restore) { QColor fore, back; if (!restore) { const KConfigGroup gc(krConfig, "Colors"); QString foreground, background; const QPalette p = QGuiApplication::palette(); if (match) { foreground = "Quicksearch Match Foreground"; background = "Quicksearch Match Background"; fore = Qt::black; back = QColor(192, 255, 192); } else { foreground = "Quicksearch Non-match Foreground"; background = "Quicksearch Non-match Background"; fore = Qt::black; back = QColor(255, 192, 192); } if (gc.readEntry(foreground, QString()) == "KDE default") fore = p.color(QPalette::Active, QPalette::Text); else if (!gc.readEntry(foreground, QString()).isEmpty()) fore = gc.readEntry(foreground, fore); if (gc.readEntry(background, QString()) == "KDE default") back = p.color(QPalette::Active, QPalette::Base); else if (!gc.readEntry(background, QString()).isEmpty()) back = gc.readEntry(background, back); } else { back = _originalBackground; fore = _originalForeground; } QPalette pal = _searchLineEdit->palette(); pal.setColor(QPalette::Base, back); pal.setColor(QPalette::Text, fore); _searchLineEdit->setPalette(pal); } void Lister::hideProgressBar() { if (!_searchProgressBar->isHidden()) { _searchProgressBar->hide(); _searchStopButton->hide(); _searchLineEdit->show(); _searchNextButton->show(); _searchPrevButton->show(); _searchOptions->show(); _listerLabel->setText(i18n("Search:")); } } void Lister::updateProgressBar() { if (_searchProgressCounter) return; if (_searchProgressBar->isHidden()) { _searchProgressBar->show(); _searchStopButton->show(); _searchOptions->hide(); _searchLineEdit->hide(); _searchNextButton->hide(); _searchPrevButton->hide(); _listerLabel->setText(i18n("Search position:")); // otherwise focus is set to document tab _textArea->setFocus(); } const qint64 pcnt = (_fileSize == 0) ? 1000 : (2001 * _searchPosition) / _fileSize / 2; const auto pctInt = (int) pcnt; if (_searchProgressBar->value() != pctInt) _searchProgressBar->setValue(pctInt); } void Lister::jumpToPosition() { bool ok = true; QString res = QInputDialog::getText(_textArea, i18n("Jump to position"), i18n("Text position:"), QLineEdit::Normal, "0", &ok); if (!ok) return; res = res.trimmed(); qint64 pos = -1; if (res.startsWith(QLatin1String("0x"))) { res = res.mid(2); bool ok; const qulonglong upos = res.toULongLong(&ok, 16); if (!ok) { KMessageBox::error(_textArea, i18n("Invalid number."), i18n("Jump to position")); return; } pos = (qint64)upos; } else { bool ok; const qulonglong upos = res.toULongLong(&ok); if (!ok) { KMessageBox::error(_textArea, i18n("Invalid number."), i18n("Jump to position")); return; } pos = (qint64)upos; } if (pos < 0 || pos > _fileSize) { KMessageBox::error(_textArea, i18n("Number out of range."), i18n("Jump to position")); return; } _textArea->deleteAnchor(); _textArea->setCursorPositionInDocument(pos, true); _textArea->ensureVisibleCursor(); } void Lister::saveAs() { const QUrl url = QFileDialog::getSaveFileUrl(_textArea, i18n("Lister")); if (url.isEmpty()) return; QUrl sourceUrl; if (!_downloading) sourceUrl = QUrl::fromLocalFile(_filePath); else sourceUrl = this->url(); QList urlList; urlList << sourceUrl; KIO::Job *job = KIO::copy(urlList, url); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } void Lister::saveSelected() { bool isfirst; const qint64 start = _textArea->getCursorAnchor(); const qint64 end = _textArea->getCursorPosition(isfirst); if (start == -1 || start == end) { KMessageBox::error(_textArea, i18n("Nothing is selected."), i18n("Save selection...")); return; } if (start > end) { _savePosition = end; _saveEnd = start; } else { _savePosition = start; _saveEnd = end; } const QUrl url = QFileDialog::getSaveFileUrl(_textArea, i18n("Lister")); if (url.isEmpty()) return; KIO::TransferJob *saveJob = KIO::put(url, -1, KIO::Overwrite); connect(saveJob, &KIO::TransferJob::dataReq, this, &Lister::slotDataSend); connect(saveJob, &KIO::TransferJob::result, this, &Lister::slotSendFinished); saveJob->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(saveJob); saveJob->uiDelegate()->setAutoErrorHandlingEnabled(true); _actionSaveSelected->setEnabled(false); } void Lister::slotDataSend(KIO::Job *, QByteArray &array) { if (_savePosition >= _saveEnd) { array = QByteArray(); return; } qint64 max = _saveEnd - _savePosition; if (max > 1000) max = 1000; array = cacheChunk(_savePosition, (int) max); _savePosition += array.size(); } void Lister::slotSendFinished(KJob *) { _actionSaveSelected->setEnabled(true); } -void Lister::setCharacterSet(const QString set) +void Lister::setCharacterSet(const QString& set) { _characterSet = set; if (_characterSet.isEmpty()) { _codec = QTextCodec::codecForLocale(); } else { _codec = KCharsets::charsets()->codecForName(_characterSet); } _textArea->redrawTextArea(true); } void Lister::print() { bool isfirst; const qint64 anchor = _textArea->getCursorAnchor(); const qint64 cursor = _textArea->getCursorPosition(isfirst); const bool hasSelection = (anchor != -1 && anchor != cursor); const QString docName = url().fileName(); QPrinter printer; printer.setDocName(docName); QScopedPointer printDialog(new QPrintDialog(&printer, _textArea)); if (hasSelection) { printDialog->addEnabledOption(QAbstractPrintDialog::PrintSelection); } if (!printDialog->exec()) { return; } if (printer.pageOrder() == QPrinter::LastPageFirst) { switch (KMessageBox::warningContinueCancel(_textArea, i18n("Reverse printing is not supported. Continue with normal printing?"))) { case KMessageBox::Continue : break; default: return; } } QPainter painter; painter.begin(&printer); const QString dateString = QDate::currentDate().toString(Qt::SystemLocaleShortDate); const QRect pageRect = printer.pageRect(); const QRect drawingRect(0, 0, pageRect.width(), pageRect.height()); const QFont normalFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); const QFontMetrics fmNormal(normalFont); const int normalFontHeight = fmNormal.height(); const QFontMetrics fmFixed(fixedFont); const int fixedFontHeight = std::max(fmFixed.height(), 1); const int fixedFontWidth = std::max(fmFixed.width("W"), 1); const int effPageSize = drawingRect.height() - normalFontHeight - 1; const int rowsPerPage = std::max(effPageSize / fixedFontHeight, 1); const int columnsPerPage = std::max(drawingRect.width() / fixedFontWidth, 1); bool firstPage = true; qint64 startPos = 0; qint64 endPos = _fileSize; if (printer.printRange() == QPrinter::Selection) { if (anchor > cursor) startPos = cursor, endPos = anchor; else startPos = anchor, endPos = cursor; } int page = 0; while (startPos < endPos) { page++; QStringList rows = readLines(startPos, endPos, columnsPerPage, rowsPerPage); // print since set-up fromPage number if (printer.fromPage() && page < printer.fromPage()) { continue; } // print until set-up toPage number if (printer.toPage() && printer.toPage() >= printer.fromPage() && page > printer.toPage()) break; if (!firstPage) { printer.newPage(); } firstPage = false; // Use the painter to draw on the page. painter.setFont(normalFont); painter.drawText(drawingRect, Qt::AlignLeft, dateString); painter.drawText(drawingRect, Qt::AlignHCenter, docName); painter.drawText(drawingRect, Qt::AlignRight, QString("%1").arg(page)); painter.drawLine(0, normalFontHeight, drawingRect.width(), normalFontHeight); painter.setFont(fixedFont); int yOffset = normalFontHeight + 1; foreach (const QString &row, rows) { painter.drawText(0, yOffset + fixedFontHeight, row); yOffset += fixedFontHeight; } } } QStringList Lister::readLines(qint64 &filePos, const qint64 endPos, const int columns, const int lines) { if (_textArea->hexMode()) { return readHexLines(filePos, endPos, columns, lines); } QStringList list; const int maxBytes = std::min(columns * lines * MAX_CHAR_LENGTH, (int) (endPos - filePos)); if (maxBytes <= 0) { return list; } const QByteArray chunk = cacheChunk(filePos, maxBytes); if (chunk.isEmpty()) { return list; } QScopedPointer decoder(_codec->makeDecoder()); int byteCounter = 0; QString row = ""; bool skipImmediateNewline = false; while (byteCounter < chunk.size() && list.size() < lines) { QString chr = decoder->toUnicode(chunk.mid(byteCounter++, 1)); if (chr.isEmpty()) { continue; } // replace unreadable characters if ((chr[ 0 ] < 32) && (chr[ 0 ] != '\n') && (chr[ 0 ] != '\t')) { chr = QChar(' '); } // handle newline if (chr == "\n") { if (!skipImmediateNewline) { list << row; row = ""; } skipImmediateNewline = false; continue; } skipImmediateNewline = false; // handle tab if (chr == "\t") { const int tabLength = _textArea->tabWidth() - (row.length() % _textArea->tabWidth()); if (row.length() + tabLength > columns) { list << row; row = ""; } row += QString(tabLength, QChar(' ')); } else { // normal printable character row += chr; } if (row.length() >= columns) { list << row; row = ""; skipImmediateNewline = true; } } if (list.size() < lines) { list << row; } filePos += byteCounter; return list; } int Lister::hexPositionDigits() { int positionDigits = 0; qint64 checker = _fileSize; while (checker) { positionDigits++; checker /= 16; } if (positionDigits < 8) { return 8; } return positionDigits; } int Lister::hexBytesPerLine(const int columns) { const int positionDigits = hexPositionDigits(); if (columns >= positionDigits + 5 + 128) { return 32; } if (columns >= positionDigits + 5 + 64) { return 16; } return 8; } QStringList Lister::readHexLines(qint64 &filePos, const qint64 endPos, const int columns, const int lines) { const int positionDigits = hexPositionDigits(); const int bytesPerRow = hexBytesPerLine(columns); QStringList list; const qint64 choppedPos = (filePos / bytesPerRow) * bytesPerRow; const int maxBytes = std::min(bytesPerRow * lines, (int) (endPos - choppedPos)); if (maxBytes <= 0) return list; const QByteArray chunk = cacheChunk(choppedPos, maxBytes); if (chunk.isEmpty()) return list; int cnt = 0; for (int l = 0; l < lines; l++) { if (filePos >= endPos) { break; } const qint64 printPos = (filePos / bytesPerRow) * bytesPerRow; QString pos; pos.setNum(printPos, 16); while (pos.length() < positionDigits) pos = QString("0") + pos; pos = QString("0x") + pos; pos += QString(": "); QString charData; for (int i = 0; i != bytesPerRow; ++i, ++cnt) { const qint64 currentPos = printPos + i; if (currentPos < filePos || currentPos >= endPos) { pos += QString(" "); charData += QString(" "); } else { char c = chunk.at(cnt); auto charCode = (int)c; if (charCode < 0) charCode += 256; QString hex; hex.setNum(charCode, 16); if (hex.length() < 2) hex = QString("0") + hex; pos += hex + QString(" "); if (c < 32) c = '.'; charData += QChar(c); } } pos += QString(" ") + charData; list << pos; filePos = printPos + bytesPerRow; } if (filePos > endPos) { filePos = endPos; } return list; } int Lister::hexIndexToPosition(const int columns, const int index) { const int positionDigits = hexPositionDigits(); const int bytesPerRow = hexBytesPerLine(columns); const int finalIndex = std::min(index, bytesPerRow); return positionDigits + 4 + (3*finalIndex); } int Lister::hexPositionToIndex(const int columns, const int position) { const int positionDigits = hexPositionDigits(); const int bytesPerRow = hexBytesPerLine(columns); int finalPosition = position; finalPosition -= 4 + positionDigits; if (finalPosition <= 0) return 0; finalPosition /= 3; if (finalPosition >= bytesPerRow) return bytesPerRow; return finalPosition; } void Lister::toggleHexMode() { setHexMode(!_textArea->hexMode()); } void Lister::setHexMode(const bool mode) { if (mode) { _textArea->setHexMode(true); _actionHexMode->setText(i18n("Text mode")); } else { _textArea->setHexMode(false); _actionHexMode->setText(i18n("Hex mode")); } } diff --git a/krusader/KViewer/lister.h b/krusader/KViewer/lister.h index 3f17d53c..1586abd9 100644 --- a/krusader/KViewer/lister.h +++ b/krusader/KViewer/lister.h @@ -1,320 +1,320 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * * Copyright (C) 2009-2018 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 LISTER_H #define LISTER_H // QtCore #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include "../FileSystem/krquery.h" #define SLIDER_MAX 10000 #define MAX_CHAR_LENGTH 4 class Lister; class QLabel; class QProgressBar; class QPushButton; class QToolButton; class QAction; class QTemporaryFile; class ListerEncodingMenu; class ListerTextArea : public KTextEdit { Q_OBJECT public: ListerTextArea(Lister *lister, QWidget *parent); void reset(); void calculateText(const bool forcedUpdate = false); void redrawTextArea(const bool forcedUpdate = false); qint64 textToFilePositionOnScreen(const int x, const int y, bool &isfirst); void fileToTextPositionOnScreen(const qint64 p, const bool isfirst, int &x, int &y); int tabWidth() { return _tabWidth; } bool hexMode() { return _hexMode; } void setHexMode(const bool hexMode); void copySelectedToClipboard(); QString getSelectedText(); void clearSelection(); void getCursorPosition(int &x, int &y); qint64 getCursorPosition(bool &isfirst); qint64 getCursorAnchor() { return _cursorAnchorPos; } void setCursorPositionInDocument(const qint64 p, const bool isfirst); void ensureVisibleCursor(); void deleteAnchor() { _cursorAnchorPos = -1; } void setAnchorAndCursor(const qint64 anchor, const qint64 cursor); void sizeChanged(); protected: void resizeEvent(QResizeEvent * event) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent * e) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent * event) Q_DECL_OVERRIDE; QStringList readLines(const qint64 filePos, qint64 &endPos, const int lines, QList * locs = nullptr); QString readSection(qint64 p1, qint64 p2); void setUpScrollBar(); void setCursorPositionOnScreen(const int x, const int y, const int anchorX = -1, const int anchorY = -1); void handleAnchorChange(const int oldAnchor); void performAnchorChange(const int anchor); void getScreenPosition(const int position, int &x, int &y); public slots: void slotActionTriggered(const int action); protected slots: void slotCursorPositionChanged(); void zoomIn(const int range = 1); void zoomOut(const int range = 1); protected: Lister *_lister; qint64 _screenStartPos = 0; qint64 _screenEndPos = 0; qint64 _averagePageSize = 0; qint64 _lastPageStartPos = 0; int _sizeX = -1; int _sizeY = -1; int _pageSize = 0; int _tabWidth = 4; bool _sizeChanged = false; QStringList _rowContent; QList _rowStarts; qint64 _cursorPos = -1; bool _cursorAtFirstColumn = true; bool _skipCursorChangedListener = false; qint64 _cursorAnchorPos = -1; int _skippedLines = 0; bool _inSliderOp = false; bool _hexMode = false; bool _redrawing = false; QMutex _cursorBlinkMutex; QTimer _blinkTimer; }; class ListerBrowserExtension : public KParts::BrowserExtension { Q_OBJECT public: explicit ListerBrowserExtension(Lister * lister); public slots: void copy(); void print(); protected: Lister *_lister; }; class ListerPane : public QWidget { Q_OBJECT public: ListerPane(Lister *lister, QWidget *parent); protected: bool event(QEvent *event) Q_DECL_OVERRIDE; protected: bool handleCloseEvent(QEvent *e); Lister *_lister; }; class Lister : public KParts::ReadOnlyPart { Q_OBJECT public: explicit Lister(QWidget *parent); QScrollBar *scrollBar() { return _scrollBar; } ListerTextArea *textArea() { return _textArea; } inline qint64 fileSize() { return _fileSize; } QByteArray cacheChunk(const qint64 filePos, const int maxSize); bool isSearchEnabled(); void enableSearch(const bool); void enableActions(const bool); QString characterSet() { return _characterSet; } QTextCodec *codec() { return _codec; } - void setCharacterSet(const QString set); + void setCharacterSet(const QString& set); void setHexMode(const bool); QStringList readHexLines(qint64 &filePos, const qint64 endPos, const int columns, const int lines); int hexBytesPerLine(const int columns); int hexPositionDigits(); int hexIndexToPosition(const int columns, const int index); int hexPositionToIndex(const int columns, const int position); public slots: void searchAction() { enableSearch(true); } void searchNext(); void searchPrev(); void searchDelete(); void jumpToPosition(); void saveAs(); void saveSelected(); void print(); void toggleHexMode(); void slotUpdate(); protected slots: void slotSearchMore(); void searchSucceeded(); void searchFailed(); void searchTextChanged(); void slotDataSend(KIO::Job *, QByteArray &); void slotSendFinished(KJob *); protected: bool openUrl(const QUrl &url) Q_DECL_OVERRIDE; bool closeUrl() Q_DECL_OVERRIDE { return true; } bool openFile() Q_DECL_OVERRIDE { return true; } void guiActivateEvent(KParts::GUIActivateEvent * event) Q_DECL_OVERRIDE; void setColor(const bool match, const bool restore); void hideProgressBar(); void updateProgressBar(); void resetSearchPosition(); qint64 getFileSize(); void search(const bool forward, const bool restart = false); QStringList readLines(qint64 &filePos, const qint64 endPos, const int columns, const int lines); QTimer _searchUpdateTimer; ListerTextArea *_textArea; QScrollBar *_scrollBar; QLabel *_listerLabel; KLineEdit *_searchLineEdit; QProgressBar *_searchProgressBar; QToolButton *_searchStopButton; QPushButton *_searchNextButton; QPushButton *_searchPrevButton; bool _searchInProgress = false; bool _searchHexadecimal = false; QPushButton *_searchOptions; QLabel *_statusLabel; QAction *_fromCursorAction; QAction *_caseSensitiveAction; QAction *_matchWholeWordsOnlyAction; QAction *_regExpAction; QAction *_hexAction; QAction *_actionSaveSelected; QAction *_actionSaveAs; QAction *_actionPrint; QAction *_actionSearch; QAction *_actionSearchNext; QAction *_actionSearchPrev; QAction *_actionJumpToPosition; QAction *_actionHexMode; QString _filePath; qint64 _fileSize = 0; QByteArray _cache; qint64 _cachePos = 0; KRQuery _searchQuery; QByteArray _searchHexQuery; qint64 _searchPosition = 0; bool _searchIsForward = true; qint64 _searchLastFailedPosition = -1; int _searchProgressCounter = 0; QColor _originalBackground; QColor _originalForeground; QString _characterSet; QTextCodec *_codec = QTextCodec::codecForLocale(); QTemporaryFile *_tempFile = nullptr; bool _downloading = false; bool _restartFromBeginning = false; QTimer _downloadUpdateTimer; qint64 _savePosition = 0; qint64 _saveEnd = 0; }; #endif // __LISTER_H__ diff --git a/krusader/KViewer/panelviewer.cpp b/krusader/KViewer/panelviewer.cpp index cb1286ea..4505c333 100644 --- a/krusader/KViewer/panelviewer.cpp +++ b/krusader/KViewer/panelviewer.cpp @@ -1,430 +1,430 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "panelviewer.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include "lister.h" #include "../defaults.h" #define DICTSIZE 211 PanelViewerBase::PanelViewerBase(QWidget *parent, KrViewer::Mode mode) : QStackedWidget(parent), mimes(nullptr), cpart(nullptr), mode(mode) { setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored)); mimes = new QHash >(); cpart = nullptr; // NOTE: the fallback should be never visible. The viewer is not opened without a file and the // tab is closed if a file cannot be opened. fallback = new QLabel(i18n("No file selected or selected file cannot be displayed."), this); fallback->setAlignment(Qt::Alignment(QFlag(Qt::AlignCenter | Qt::TextExpandTabs))); fallback->setWordWrap(true); addWidget(fallback); setCurrentWidget(fallback); } PanelViewerBase::~PanelViewerBase() { // cpart->queryClose(); closeUrl(); QHashIterator< QString, QPointer > lit(*mimes); while (lit.hasNext()) { QPointer p = lit.next().value(); if (p) delete p; } mimes->clear(); delete mimes; delete fallback; } void PanelViewerBase::slotStatResult(KJob* job) { if (job->error()) { KMessageBox::error(this, job->errorString()); emit openUrlFinished(this, false); } else { KIO::UDSEntry entry = static_cast(job)->statResult(); openFile(KFileItem(entry, curl)); } } -KParts::ReadOnlyPart* PanelViewerBase::getPart(QString mimetype) +KParts::ReadOnlyPart* PanelViewerBase::getPart(const QString& mimetype) { KParts::ReadOnlyPart* part = nullptr; if (mimes->find(mimetype) == mimes->end()) { part = createPart(mimetype); if(part) mimes->insert(mimetype, part); } else part = (*mimes)[mimetype]; return part; } -void PanelViewerBase::openUrl(QUrl url) +void PanelViewerBase::openUrl(const QUrl& url) { closeUrl(); curl = url; emit urlChanged(this, url); if (url.isLocalFile()) openFile(KFileItem(url)); else { KIO::StatJob* statJob = KIO::stat(url, KIO::HideProgressInfo); connect(statJob, &KIO::StatJob::result, this, &PanelViewerBase::slotStatResult); } } /* ----==={ PanelViewer }===---- */ PanelViewer::PanelViewer(QWidget *parent, KrViewer::Mode mode) : PanelViewerBase(parent, mode) { } PanelViewer::~PanelViewer() = default; KParts::ReadOnlyPart* PanelViewer::getListerPart(bool hexMode) { KParts::ReadOnlyPart* part = nullptr; if (mimes->find(QLatin1String("krusader_lister")) == mimes->end()) { part = new Lister(this); mimes->insert(QLatin1String("krusader_lister"), part); } else part = (*mimes)[ QLatin1String("krusader_lister")]; if (part) { auto *lister = qobject_cast((KParts::ReadOnlyPart *)part); if (lister) lister->setHexMode(hexMode); } return part; } KParts::ReadOnlyPart* PanelViewer::getHexPart() { KParts::ReadOnlyPart* part = nullptr; if (KConfigGroup(krConfig, "General").readEntry("UseOktetaViewer", _UseOktetaViewer)) { if (mimes->find("oktetapart") == mimes->end()) { KPluginFactory* factory = nullptr; // Okteta >= 0.26 provides a desktop file, prefer that as the binary changes name KService::Ptr service = KService::serviceByDesktopName("oktetapart"); if (service) { factory = KPluginLoader(*service.data()).factory(); } else { // fallback to search for desktopfile-less old variant factory = KPluginLoader("oktetapart").factory(); } if (factory) { if ((part = factory->create(this, this))) mimes->insert("oktetapart", part); } } else part = (*mimes)["oktetapart"]; } return part ? part : getListerPart(true); } KParts::ReadOnlyPart* PanelViewer::getTextPart() { KParts::ReadOnlyPart* part = getPart("text/plain"); if(!part) part = getPart("all/allfiles"); return part ? part : getListerPart(); } -KParts::ReadOnlyPart* PanelViewer::getDefaultPart(KFileItem fi) +KParts::ReadOnlyPart* PanelViewer::getDefaultPart(const KFileItem& fi) { KConfigGroup group(krConfig, "General"); QString modeString = group.readEntry("Default Viewer Mode", QString("generic")); KrViewer::Mode mode = KrViewer::Generic; if (modeString == "generic") mode = KrViewer::Generic; else if (modeString == "text") mode = KrViewer::Text; else if (modeString == "hex") mode = KrViewer::Hex; else if (modeString == "lister") mode = KrViewer::Lister; QMimeType mimeType = fi.determineMimeType(); bool isBinary = false; if (mode == KrViewer::Generic || mode == KrViewer::Lister) { isBinary = !mimeType.inherits(QStringLiteral("text/plain")); } KIO::filesize_t fileSize = fi.size(); KIO::filesize_t limit = (KIO::filesize_t)group.readEntry("Lister Limit", _ListerLimit) * 0x100000; QString mimetype = fi.mimetype(); KParts::ReadOnlyPart* part = nullptr; switch(mode) { case KrViewer::Generic: if ((mimetype.startsWith(QLatin1String("text/")) || mimetype.startsWith(QLatin1String("all/"))) && fileSize > limit) { part = getListerPart(isBinary); break; } else if ((part = getPart(mimetype))) { break; } #if __GNUC__ >= 7 [[gnu::fallthrough]]; #endif case KrViewer::Text: part = fileSize > limit ? getListerPart(false) : getTextPart(); break; case KrViewer::Lister: part = getListerPart(isBinary); break; case KrViewer::Hex: part = fileSize > limit ? getListerPart(true) : getHexPart(); break; default: abort(); } return part; } void PanelViewer::openFile(KFileItem fi) { switch(mode) { case KrViewer::Generic: cpart = getPart(fi.mimetype()); break; case KrViewer::Text: cpart = getTextPart(); break; case KrViewer::Lister: cpart = getListerPart(); break; case KrViewer::Hex: cpart = getHexPart(); break; case KrViewer::Default: cpart = getDefaultPart(fi); break; default: abort(); } if(cpart) { addWidget(cpart->widget()); setCurrentWidget(cpart->widget()); if (cpart->inherits("KParts::ReadWritePart")) static_cast(cpart.data())->setReadWrite(false); KParts::OpenUrlArguments args; args.setReload(true); cpart->setArguments(args); if (cpart->openUrl(curl)) { connect(cpart.data(), &KParts::ReadOnlyPart::destroyed, this, &PanelViewer::slotCPartDestroyed); emit openUrlFinished(this, true); return; } } setCurrentWidget(fallback); emit openUrlFinished(this, false); } bool PanelViewer::closeUrl() { setCurrentWidget(fallback); if (cpart && cpart->closeUrl()) { cpart = nullptr; return true; } return false; } KParts::ReadOnlyPart* PanelViewer::createPart(QString mimetype) { KParts::ReadOnlyPart * part = nullptr; KService::Ptr ptr = KMimeTypeTrader::self()->preferredService(mimetype, "KParts/ReadOnlyPart"); if (ptr) { QVariantList args; QVariant argsProp = ptr->property("X-KDE-BrowserView-Args"); if (argsProp.isValid()) args << argsProp; QVariant prop = ptr->property("X-KDE-BrowserView-AllowAsDefault"); if (!prop.isValid() || prop.toBool()) // defaults to true part = ptr->createInstance(this, this, args); } if (part) { KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(part); if (ext) { connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelViewer::openUrl); connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelViewer::openUrlRequest); } } return part; } /* ----==={ PanelEditor }===---- */ PanelEditor::PanelEditor(QWidget *parent, KrViewer::Mode mode) : PanelViewerBase(parent, mode) { } PanelEditor::~PanelEditor() = default; void PanelEditor::configureDeps() { KService::Ptr ptr = KMimeTypeTrader::self()->preferredService("text/plain", "KParts/ReadWritePart"); if (!ptr) ptr = KMimeTypeTrader::self()->preferredService("all/allfiles", "KParts/ReadWritePart"); if (!ptr) KMessageBox::sorry(nullptr, missingKPartMsg(), i18n("Missing Plugin"), KMessageBox::AllowLink); } QString PanelEditor::missingKPartMsg() { return i18nc("missing kpart - arg1 is a URL", "No text editor plugin available.
" "Internal editor will not work without this.
" "You can fix this by installing Kate:
%1", QString("%1").arg( "http://www.kde.org/applications/utilities/kate")); } void PanelEditor::openFile(KFileItem fi) { KIO::filesize_t fileSize = fi.size(); KConfigGroup group(krConfig, "General"); KIO::filesize_t limitMB = (KIO::filesize_t)group.readEntry("Lister Limit", _ListerLimit); QString mimetype = fi.mimetype(); if (mode == KrViewer::Generic) cpart = getPart(mimetype); if(fileSize > limitMB * 0x100000) { if(!cpart || mimetype.startsWith(QLatin1String("text/")) || mimetype.startsWith(QLatin1String("all/"))) { if(KMessageBox::Cancel == KMessageBox::warningContinueCancel(this, i18n("%1 is bigger than %2 MB" , curl.toDisplayString(QUrl::PreferLocalFile), limitMB))) { setCurrentWidget(fallback); emit openUrlFinished(this, false); return; } } } if (!cpart) cpart = getPart("text/plain"); if (!cpart) cpart = getPart("all/allfiles"); if (cpart) { addWidget(cpart->widget()); setCurrentWidget(cpart->widget()); KParts::OpenUrlArguments args; args.setReload(true); cpart->setArguments(args); if (cpart->openUrl(curl)) { connect(cpart.data(), &KParts::ReadOnlyPart::destroyed, this, &PanelEditor::slotCPartDestroyed); emit openUrlFinished(this, true); return; } // else: don't show error message - assume this has been done by the editor part } else KMessageBox::sorry(this, missingKPartMsg(), i18n("Cannot edit %1", curl.toDisplayString(QUrl::PreferLocalFile)), KMessageBox::AllowLink); setCurrentWidget(fallback); emit openUrlFinished(this, false); } bool PanelEditor::queryClose() { if (!cpart) return true; return static_cast((KParts::ReadOnlyPart *)cpart)->queryClose(); } bool PanelEditor::closeUrl() { if (!cpart) return false; static_cast((KParts::ReadOnlyPart *)cpart)->closeUrl(false); setCurrentWidget(fallback); cpart = nullptr; return true; } KParts::ReadOnlyPart* PanelEditor::createPart(QString mimetype) { KParts::ReadWritePart * part = nullptr; KService::Ptr ptr = KMimeTypeTrader::self()->preferredService(mimetype, "KParts/ReadWritePart"); if (ptr) { QVariantList args; QVariant argsProp = ptr->property("X-KDE-BrowserView-Args"); if (argsProp.isValid()) args << argsProp; QVariant prop = ptr->property("X-KDE-BrowserView-AllowAsDefault"); if (!prop.isValid() || prop.toBool()) // defaults to true part = ptr->createInstance(this, this, args); } if (part) { KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject(part); if (ext) { connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelEditor::openUrl); connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelEditor::openUrlRequest); } } return part; } bool PanelEditor::isModified() { if (cpart) return static_cast((KParts::ReadOnlyPart *)cpart)->isModified(); else return false; } diff --git a/krusader/KViewer/panelviewer.h b/krusader/KViewer/panelviewer.h index 0b24b40b..f10204ae 100644 --- a/krusader/KViewer/panelviewer.h +++ b/krusader/KViewer/panelviewer.h @@ -1,140 +1,140 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 PANELVIEWER_H #define PANELVIEWER_H // QtCore #include #include #include // QtWidgets #include #include # include #include "krviewer.h" class PanelViewerBase: public QStackedWidget { Q_OBJECT public: explicit PanelViewerBase(QWidget *parent, KrViewer::Mode mode = KrViewer::Default); ~PanelViewerBase() override; inline QUrl url() const { return curl; } inline KParts::ReadOnlyPart* part() const { return cpart; } virtual bool isModified() { return false; } virtual bool isEditor() = 0; public slots: virtual bool closeUrl() { return false; } virtual bool queryClose() { return true; } - void openUrl(QUrl url); + void openUrl(const QUrl& url); signals: void openUrlRequest(const QUrl &url); void urlChanged(PanelViewerBase *, const QUrl &); void partDestroyed(PanelViewerBase *); void openUrlFinished(PanelViewerBase *viewWidget, bool success); protected slots: void slotCPartDestroyed() { emit partDestroyed(this); } void slotStatResult(KJob* job); protected: virtual void openFile(KFileItem fi) = 0; virtual KParts::ReadOnlyPart* createPart(QString mimetype) = 0; - KParts::ReadOnlyPart* getPart(QString mimetype); + KParts::ReadOnlyPart* getPart(const QString& mimetype); QHash > *mimes; QPointer cpart; QUrl curl; QLabel *fallback; KrViewer::Mode mode; }; class PanelViewer: public PanelViewerBase { Q_OBJECT public slots: bool closeUrl() Q_DECL_OVERRIDE; public: explicit PanelViewer(QWidget *parent, KrViewer::Mode mode = KrViewer::Default); ~PanelViewer() override; bool isEditor() Q_DECL_OVERRIDE { return false; } protected: void openFile(KFileItem fi) Q_DECL_OVERRIDE; KParts::ReadOnlyPart* createPart(QString mimetype) Q_DECL_OVERRIDE; - KParts::ReadOnlyPart* getDefaultPart(KFileItem fi); + KParts::ReadOnlyPart* getDefaultPart(const KFileItem& fi); KParts::ReadOnlyPart* getHexPart(); KParts::ReadOnlyPart* getListerPart(bool hexMode = false); KParts::ReadOnlyPart* getTextPart(); }; class PanelEditor: public PanelViewerBase { Q_OBJECT public: bool isModified() Q_DECL_OVERRIDE; bool isEditor() Q_DECL_OVERRIDE { return true; } static void configureDeps(); public slots: bool closeUrl() Q_DECL_OVERRIDE; bool queryClose() Q_DECL_OVERRIDE; public: explicit PanelEditor(QWidget *parent, KrViewer::Mode mode = KrViewer::Default); ~PanelEditor() override; protected: void openFile(KFileItem fi) Q_DECL_OVERRIDE; KParts::ReadOnlyPart* createPart(QString mimetype) Q_DECL_OVERRIDE; static QString missingKPartMsg(); }; #endif diff --git a/krusader/Konfigurator/kgcolors.cpp b/krusader/Konfigurator/kgcolors.cpp index 23fefb78..92c7a33b 100644 --- a/krusader/Konfigurator/kgcolors.cpp +++ b/krusader/Konfigurator/kgcolors.cpp @@ -1,705 +1,706 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "kgcolors.h" #include "../defaults.h" #include "../Panel/krcolorcache.h" #include "../Synchronizer/synchronizercolors.h" // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include +#include KgColors::KgColors(bool first, QWidget* parent) : KonfiguratorPage(first, parent), offset(0), activeTabIdx(-1), inactiveTabIdx(-1), #ifdef SYNCHRONIZER_ENABLED synchronizerTabIdx(-1), #endif otherTabIdx(-1) { QWidget *innerWidget = new QFrame(this); setWidget(innerWidget); setWidgetResizable(true); auto *kgColorsLayout = new QGridLayout(innerWidget); kgColorsLayout->setSpacing(6); // -------------------------- GENERAL GROUPBOX ---------------------------------- QGroupBox *generalGrp = createFrame(i18n("General"), innerWidget); QGridLayout *generalGrid = createGridLayout(generalGrp); generalGrid->setSpacing(0); generalGrid->setContentsMargins(5, 5, 5, 5); KONFIGURATOR_CHECKBOX_PARAM generalSettings[] = // cfg_class cfg_name default text restart tooltip {{"Colors", "KDE Default", _KDEDefaultColors, i18n("Use the default KDE colors"), false, "

" + i18n("

Use KDE's global color configuration.

KDE System Settings -> Application Appearance -> Colors

") }, {"Colors", "Enable Alternate Background", _AlternateBackground, i18n("Use alternate background color"), false, i18n("

The background color and the alternate background color alternates line by line.

When you don't use the KDE default colors, you can configure the alternate colors in the colors box.

") }, {"Colors", "Show Current Item Always", _ShowCurrentItemAlways, i18n("Show current item even if not focused"), false, i18n("

Shows the last cursor position in the non active list panel.

This option is only available when you don't use the KDE default colors.

") }, {"Colors", "Dim Inactive Colors", _DimInactiveColors, i18n("Dim the colors of the inactive panel"), false, i18n("

The colors of the inactive panel are calculated by a dim color and a dim factor.

") } }; generals = createCheckBoxGroup(0, 2, generalSettings, sizeof(generalSettings) / sizeof(generalSettings[0]), generalGrp); generalGrid->addWidget(generals, 1, 0); generals->layout()->setSpacing(5); connect(generals->find("KDE Default"), &KonfiguratorCheckBox::stateChanged, this, &KgColors::slotDisable); connect(generals->find("Show Current Item Always"), &KonfiguratorCheckBox::stateChanged, this, &KgColors::slotDisable); connect(generals->find("Dim Inactive Colors"), &KonfiguratorCheckBox::stateChanged, this, &KgColors::slotDisable); kgColorsLayout->addWidget(generalGrp, 0 , 0, 1, 3); QWidget *hboxWidget = new QWidget(innerWidget); auto *hbox = new QHBoxLayout(hboxWidget); // -------------------------- COLORS GROUPBOX ---------------------------------- QGroupBox *colorsFrameGrp = createFrame(i18n("Colors"), hboxWidget); QGridLayout *colorsFrameGrid = createGridLayout(colorsFrameGrp); colorsFrameGrid->setSpacing(0); colorsFrameGrid->setContentsMargins(3, 3, 3, 3); colorTabWidget = new QTabWidget(colorsFrameGrp); colorsGrp = new QWidget(colorTabWidget); activeTabIdx = colorTabWidget->addTab(colorsGrp, i18n("Active")); colorsGrid = new QGridLayout(colorsGrp); colorsGrid->setSpacing(0); colorsGrid->setContentsMargins(2, 2, 2, 2); ADDITIONAL_COLOR transparent = { i18n("Transparent"), Qt::white, "transparent" }; QPalette p = QGuiApplication::palette(); addColorSelector("Foreground", i18n("Foreground:"), p.color(QPalette::Active, QPalette::Text)); addColorSelector("Directory Foreground", i18n("Folder foreground:"), getColorSelector("Foreground")->getColor(), i18n("Same as foreground")); addColorSelector("Executable Foreground", i18n("Executable foreground:"), getColorSelector("Foreground")->getColor(), i18n("Same as foreground")); addColorSelector("Symlink Foreground", i18n("Symbolic link foreground:"), getColorSelector("Foreground")->getColor(), i18n("Same as foreground")); addColorSelector("Invalid Symlink Foreground", i18n("Invalid symlink foreground:"), getColorSelector("Foreground")->getColor(), i18n("Same as foreground")); addColorSelector("Background", i18n("Background:"), p.color(QPalette::Active, QPalette::Base)); ADDITIONAL_COLOR sameAsBckgnd = { i18n("Same as background"), getColorSelector("Background")->getColor(), "Background" }; addColorSelector("Alternate Background", i18n("Alternate background:"), p.color(QPalette::Active, QPalette::AlternateBase), "", &sameAsBckgnd, 1); addColorSelector("Marked Foreground", i18n("Selected foreground:"), p.color(QPalette::Active, QPalette::HighlightedText), "", &transparent, 1); addColorSelector("Marked Background", i18n("Selected background:"), p.color(QPalette::Active, QPalette::Highlight), "", &sameAsBckgnd, 1); ADDITIONAL_COLOR sameAsAltern = { i18n("Same as alt. background"), getColorSelector("Alternate Background")->getColor(), "Alternate Background" }; addColorSelector("Alternate Marked Background", i18n("Alternate selected background:"), getColorSelector("Marked Background")->getColor(), i18n("Same as selected background"), &sameAsAltern, 1); addColorSelector("Current Foreground", i18n("Current foreground:"), Qt::white, i18n("Not used")); ADDITIONAL_COLOR sameAsMarkedForegnd = { i18n("Same as selected foreground"), getColorSelector("Marked Foreground")->getColor(), "Marked Foreground" }; addColorSelector("Marked Current Foreground", i18n("Selected current foreground:"), Qt::white, i18n("Not used"), &sameAsMarkedForegnd, 1); addColorSelector("Current Background", i18n("Current background:"), Qt::white, i18n("Not used"), &sameAsBckgnd, 1); colorsGrid->addWidget(createSpacer(colorsGrp), itemList.count() - offset, 1); connect(getColorSelector("Foreground"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotForegroundChanged); connect(getColorSelector("Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotBackgroundChanged); connect(getColorSelector("Alternate Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotAltBackgroundChanged); connect(getColorSelector("Marked Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotMarkedBackgroundChanged); inactiveColorStack = new QStackedWidget(colorTabWidget); inactiveTabIdx = colorTabWidget->addTab(inactiveColorStack, i18n("Inactive")); colorsGrp = normalInactiveWidget = new QWidget(inactiveColorStack); colorsGrid = new QGridLayout(normalInactiveWidget); colorsGrid->setSpacing(0); colorsGrid->setContentsMargins(2, 2, 2, 2); offset = endOfActiveColors = itemList.count(); addColorSelector("Inactive Foreground", i18n("Foreground:"), getColorSelector("Foreground")->getColor(), i18n("Same as active")); ADDITIONAL_COLOR sameAsInactForegnd = { i18n("Same as foreground"), getColorSelector("Inactive Foreground")->getColor(), "Inactive Foreground" }; addColorSelector("Inactive Directory Foreground", i18n("Folder foreground:"), getColorSelector("Directory Foreground")->getColor(), i18n("Same as active"), &sameAsInactForegnd, 1); addColorSelector("Inactive Executable Foreground", i18n("Executable foreground:"), getColorSelector("Executable Foreground")->getColor(), i18n("Same as active"), &sameAsInactForegnd, 1); addColorSelector("Inactive Symlink Foreground", i18n("Symbolic link foreground:"), getColorSelector("Symlink Foreground")->getColor(), i18n("Same as active"), &sameAsInactForegnd, 1); addColorSelector("Inactive Invalid Symlink Foreground", i18n("Invalid symlink foreground:"), getColorSelector("Invalid Symlink Foreground")->getColor(), i18n("Same as active"), &sameAsInactForegnd, 1); addColorSelector("Inactive Background", i18n("Background:"), getColorSelector("Background")->getColor(), i18n("Same as active")); ADDITIONAL_COLOR sameAsInactBckgnd = { i18n("Same as background"), getColorSelector("Inactive Background")->getColor(), "Inactive Background" }; addColorSelector("Inactive Alternate Background", i18n("Alternate background:"), getColorSelector("Alternate Background")->getColor(), i18n("Same as active"), &sameAsInactBckgnd, 1); addColorSelector("Inactive Marked Foreground", i18n("Selected foreground:"), getColorSelector("Marked Foreground")->getColor(), i18n("Same as active"), &transparent, 1); addColorSelector("Inactive Marked Background", i18n("Selected background:"), getColorSelector("Marked Background")->getColor(), i18n("Same as active"), &sameAsInactBckgnd, 1); ADDITIONAL_COLOR sameAsInactAltern[] = {{ i18n("Same as alt. background"), getColorSelector("Inactive Alternate Background")->getColor(), "Inactive Alternate Background" }, { i18n("Same as selected background"), getColorSelector("Inactive Marked Background")->getColor(), "Inactive Marked Background" } }; addColorSelector("Inactive Alternate Marked Background", i18n("Alternate selected background:"), getColorSelector("Alternate Marked Background")->getColor(), i18n("Same as active"), sameAsInactAltern, 2); addColorSelector("Inactive Current Foreground", i18n("Current foreground:"), getColorSelector("Current Foreground")->getColor(), i18n("Same as active")); ADDITIONAL_COLOR sameAsInactMarkedForegnd = { i18n("Same as selected foreground"), getColorSelector("Inactive Marked Foreground")->getColor(), "Inactive Marked Foreground" }; addColorSelector("Inactive Marked Current Foreground", i18n("Selected current foreground:"), getColorSelector("Marked Current Foreground")->getColor(), i18n("Same as active"), &sameAsInactMarkedForegnd, 1); addColorSelector("Inactive Current Background", i18n("Current background:"), getColorSelector("Current Background")->getColor(), i18n("Same as active"), &sameAsInactBckgnd, 1); colorsGrid->addWidget(createSpacer(normalInactiveWidget), itemList.count() - offset, 1); connect(getColorSelector("Inactive Foreground"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotInactiveForegroundChanged); connect(getColorSelector("Inactive Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotInactiveBackgroundChanged); connect(getColorSelector("Inactive Alternate Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotInactiveAltBackgroundChanged); connect(getColorSelector("Inactive Marked Background"), &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotInactiveMarkedBackgroundChanged); offset = endOfPanelColors = itemList.count(); inactiveColorStack->addWidget(normalInactiveWidget); colorsGrp = dimmedInactiveWidget = new QWidget(inactiveColorStack); colorsGrid = new QGridLayout(dimmedInactiveWidget); colorsGrid->setSpacing(0); colorsGrid->setContentsMargins(2, 2, 2, 2); addColorSelector("Dim Target Color", i18n("Dim target color:"), Qt::black); int index = itemList.count() - offset; labelList.append(addLabel(colorsGrid, index, 0, i18n("Dim factor:"), colorsGrp)); dimFactor = createSpinBox("Colors", "Dim Factor", 80, 0, 100, colorsGrp); dimFactor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); colorsGrid->addWidget(dimFactor, index++, 1); colorsGrid->addWidget(createSpacer(dimmedInactiveWidget), itemList.count() + 1 - offset, 1); inactiveColorStack->addWidget(dimmedInactiveWidget); inactiveColorStack->setCurrentWidget(normalInactiveWidget); ADDITIONAL_COLOR KDEDefaultBase = { i18n("KDE default"), p.color(QPalette::Active, QPalette::Base), "KDE default" }; ADDITIONAL_COLOR KDEDefaultFore = { i18n("KDE default"), p.color(QPalette::Active, QPalette::Text), "KDE default" }; #ifdef SYNCHRONIZER_ENABLED colorsGrp = new QWidget(colorTabWidget); synchronizerTabIdx = colorTabWidget->addTab(colorsGrp, i18n("Synchronizer")); colorsGrid = new QGridLayout(colorsGrp); colorsGrid->setSpacing(0); colorsGrid->setContentsMargins(2, 2, 2, 2); offset = endOfPanelColors = itemList.count(); DECLARE_SYNCHRONIZER_BACKGROUND_DEFAULTS; DECLARE_SYNCHRONIZER_FOREGROUND_DEFAULTS; addColorSelector("Synchronizer Equals Foreground", i18n("Equals foreground:"), SYNCHRONIZER_FOREGROUND_DEFAULTS[0], QString(), &KDEDefaultFore, 1); addColorSelector("Synchronizer Equals Background", i18n("Equals background:"), SYNCHRONIZER_BACKGROUND_DEFAULTS[0], QString(), &KDEDefaultBase, 1); addColorSelector("Synchronizer Differs Foreground", i18n("Differing foreground:"), SYNCHRONIZER_FOREGROUND_DEFAULTS[1], QString(), &KDEDefaultFore, 1); addColorSelector("Synchronizer Differs Background", i18n("Differing background:"), SYNCHRONIZER_BACKGROUND_DEFAULTS[1], QString(), &KDEDefaultBase, 1); addColorSelector("Synchronizer LeftCopy Foreground", i18n("Copy to left foreground:"), SYNCHRONIZER_FOREGROUND_DEFAULTS[2], QString(), &KDEDefaultFore, 1); addColorSelector("Synchronizer LeftCopy Background", i18n("Copy to left background:"), SYNCHRONIZER_BACKGROUND_DEFAULTS[2], QString(), &KDEDefaultBase, 1); addColorSelector("Synchronizer RightCopy Foreground", i18n("Copy to right foreground:"), SYNCHRONIZER_FOREGROUND_DEFAULTS[3], QString(), &KDEDefaultFore, 1); addColorSelector("Synchronizer RightCopy Background", i18n("Copy to right background:"), SYNCHRONIZER_BACKGROUND_DEFAULTS[3], QString(), &KDEDefaultBase, 1); addColorSelector("Synchronizer Delete Foreground", i18n("Delete foreground:"), SYNCHRONIZER_FOREGROUND_DEFAULTS[4], QString(), &KDEDefaultFore, 1); addColorSelector("Synchronizer Delete Background", i18n("Delete background:"), SYNCHRONIZER_BACKGROUND_DEFAULTS[4], QString(), &KDEDefaultBase, 1); colorsGrid->addWidget(createSpacer(colorsGrp), itemList.count() - offset, 1); #endif colorsGrp = new QWidget(colorTabWidget); otherTabIdx = colorTabWidget->addTab(colorsGrp, i18n("Other")); colorsGrid = new QGridLayout(colorsGrp); colorsGrid->setSpacing(0); colorsGrid->setContentsMargins(2, 2, 2, 2); offset = endOfPanelColors = itemList.count(); addColorSelector("Quicksearch Match Foreground", i18n("Quicksearch, match foreground:"), Qt::black, QString(), &KDEDefaultFore, 1); addColorSelector("Quicksearch Match Background", i18n("Quicksearch, match background:"), QColor(192, 255, 192), QString(), &KDEDefaultBase, 1); addColorSelector("Quicksearch Non-match Foreground", i18n("Quicksearch, non-match foreground:"), Qt::black, QString(), &KDEDefaultFore, 1); addColorSelector("Quicksearch Non-match Background", i18n("Quicksearch, non-match background:"), QColor(255, 192, 192), QString(), &KDEDefaultBase, 1); ADDITIONAL_COLOR KDEDefaultWindowFore = { i18n("KDE default"), p.color(QPalette::Active, QPalette::WindowText), "KDE default" }; ADDITIONAL_COLOR KDEDefaultWindowBack = { i18n("KDE default"), p.color(QPalette::Active, QPalette::Window), "KDE default" }; addColorSelector("Statusbar Foreground Active", i18n("Statusbar, active foreground:"), p.color(QPalette::Active, QPalette::HighlightedText), QString(), &KDEDefaultWindowFore, 1); addColorSelector("Statusbar Background Active", i18n("Statusbar, active background:"), p.color(QPalette::Active, QPalette::Highlight), QString(), &KDEDefaultWindowBack, 1); addColorSelector("Statusbar Foreground Inactive", i18n("Statusbar, inactive foreground:"), p.color(QPalette::Inactive, QPalette::Text), QString(), &KDEDefaultWindowFore, 1); addColorSelector("Statusbar Background Inactive", i18n("Statusbar, inactive background:"), p.color(QPalette::Inactive, QPalette::Base), QString(), &KDEDefaultWindowBack, 1); colorsGrid->addWidget(createSpacer(colorsGrp), itemList.count() - offset, 1); colorsFrameGrid->addWidget(colorTabWidget, 0, 0); hbox->addWidget(colorsFrameGrp); // -------------------------- PREVIEW GROUPBOX ---------------------------------- previewGrp = createFrame(i18n("Preview"), hboxWidget); previewGrid = createGridLayout(previewGrp); preview = new KrTreeWidget(previewGrp); preview->setBackgroundRole(QPalette::Window); preview->setAutoFillBackground(true); QStringList labels; labels << i18n("Colors"); preview->setHeaderLabels(labels); preview->header()->setSortIndicatorShown(false); preview->setSortingEnabled(false); preview->setEnabled(false); previewGrid->addWidget(preview, 0 , 0); hbox->addWidget(previewGrp); connect(generals->find("Enable Alternate Background"), &KonfiguratorCheckBox::stateChanged, this, &KgColors::generatePreview); connect(colorTabWidget, &QTabWidget::currentChanged, this, &KgColors::generatePreview); connect(dimFactor, QOverload::of(&KonfiguratorSpinBox::valueChanged), this, &KgColors::generatePreview); kgColorsLayout->addWidget(hboxWidget, 1 , 0, 1, 3); importBtn = new QPushButton(i18n("Import color-scheme"), innerWidget); kgColorsLayout->addWidget(importBtn, 2, 0); exportBtn = new QPushButton(i18n("Export color-scheme"), innerWidget); kgColorsLayout->addWidget(exportBtn, 2, 1); kgColorsLayout->addWidget(createSpacer(innerWidget), 2, 2); connect(importBtn, &QPushButton::clicked, this, &KgColors::slotImportColors); connect(exportBtn, &QPushButton::clicked, this, &KgColors::slotExportColors); slotDisable(); } -int KgColors::addColorSelector(QString cfgName, QString name, QColor defaultValue, QString dfltName, +int KgColors::addColorSelector(const QString& cfgName, QString name, QColor defaultValue, const QString& dfltName, ADDITIONAL_COLOR *addColor, int addColNum) { int index = itemList.count() - offset; - labelList.append(addLabel(colorsGrid, index, 0, name, colorsGrp)); - KonfiguratorColorChooser *chooser = createColorChooser("Colors", cfgName, defaultValue, colorsGrp, false, addColor, addColNum); + labelList.append(addLabel(colorsGrid, index, 0, std::move(name), colorsGrp)); + KonfiguratorColorChooser *chooser = createColorChooser("Colors", cfgName, std::move(defaultValue), colorsGrp, false, addColor, addColNum); chooser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); if (!dfltName.isEmpty()) chooser->setDefaultText(dfltName); colorsGrid->addWidget(chooser, index, 1); connect(chooser, &KonfiguratorColorChooser::colorChanged, this, &KgColors::generatePreview); if (!offset) connect(chooser, &KonfiguratorColorChooser::colorChanged, this, &KgColors::slotActiveChanged); itemList.append(chooser); itemNames.append(cfgName); return index; } -KonfiguratorColorChooser *KgColors::getColorSelector(QString name) +KonfiguratorColorChooser *KgColors::getColorSelector(const QString& name) { QList::iterator it; int position = 0; for (it = itemNames.begin(); it != itemNames.end(); it++, position++) if (*it == name) return itemList.at(position); return nullptr; } -QLabel *KgColors::getSelectorLabel(QString name) +QLabel *KgColors::getSelectorLabel(const QString& name) { QList::iterator it; int position = 0; for (it = itemNames.begin(); it != itemNames.end(); it++, position++) if (*it == name) return labelList.at(position); return nullptr; } void KgColors::slotDisable() { bool enabled = generals->find("KDE Default")->isChecked(); importBtn->setEnabled(!enabled); exportBtn->setEnabled(!enabled); for (int i = 0; i < labelList.count() && i < endOfPanelColors ; i++) labelList.at(i)->setEnabled(!enabled); for (int j = 0; j < itemList.count() && j < endOfPanelColors ; j++) itemList.at(j)->setEnabled(!enabled); generals->find("Enable Alternate Background")->setEnabled(enabled); generals->find("Show Current Item Always")->setEnabled(!enabled); generals->find("Dim Inactive Colors")->setEnabled(!enabled); bool dimmed = !enabled && generals->find("Dim Inactive Colors")->isChecked(); if (dimmed) inactiveColorStack->setCurrentWidget(dimmedInactiveWidget); else inactiveColorStack->setCurrentWidget(normalInactiveWidget); enabled = enabled || !generals->find("Show Current Item Always")->isChecked(); getColorSelector("Inactive Current Foreground")->setEnabled(!enabled); getColorSelector("Inactive Current Background")->setEnabled(!enabled); generatePreview(); } void KgColors::slotActiveChanged() { for (int i = 0; i != endOfActiveColors; i++) itemList.at(endOfActiveColors + i)->setDefaultColor(itemList.at(i)->getColor()); } void KgColors::slotForegroundChanged() { QColor color = getColorSelector("Foreground")->getColor(); getColorSelector("Directory Foreground")->setDefaultColor(color); getColorSelector("Executable Foreground")->setDefaultColor(color); getColorSelector("Symlink Foreground")->setDefaultColor(color); getColorSelector("Invalid Symlink Foreground")->setDefaultColor(color); } void KgColors::slotBackgroundChanged() { QColor color = getColorSelector("Background")->getColor(); getColorSelector("Alternate Background")->changeAdditionalColor(0, color); getColorSelector("Marked Background")->changeAdditionalColor(0, color); getColorSelector("Current Background")->changeAdditionalColor(0, color); } void KgColors::slotAltBackgroundChanged() { QColor color = getColorSelector("Alternate Background")->getColor(); getColorSelector("Alternate Marked Background")->changeAdditionalColor(0, color); } void KgColors::slotMarkedBackgroundChanged() { QColor color = getColorSelector("Marked Background")->getColor(); getColorSelector("Alternate Marked Background")->setDefaultColor(color); } void KgColors::slotInactiveForegroundChanged() { QColor color = getColorSelector("Inactive Foreground")->getColor(); getColorSelector("Inactive Directory Foreground")->changeAdditionalColor(0, color); getColorSelector("Inactive Executable Foreground")->changeAdditionalColor(0, color); getColorSelector("Inactive Symlink Foreground")->changeAdditionalColor(0, color); getColorSelector("Inactive Invalid Symlink Foreground")->changeAdditionalColor(0, color); } void KgColors::slotInactiveBackgroundChanged() { QColor color = getColorSelector("Inactive Background")->getColor(); getColorSelector("Inactive Alternate Background")->changeAdditionalColor(0, color); getColorSelector("Inactive Marked Background")->changeAdditionalColor(0, color); getColorSelector("Inactive Current Background")->changeAdditionalColor(0, color); } void KgColors::slotInactiveAltBackgroundChanged() { QColor color = getColorSelector("Inactive Alternate Background")->getColor(); getColorSelector("Inactive Alternate Marked Background")->changeAdditionalColor(0, color); } void KgColors::slotInactiveMarkedBackgroundChanged() { QColor color = getColorSelector("Inactive Marked Background")->getColor(); getColorSelector("Inactive Alternate Marked Background")->changeAdditionalColor(1, color); } void KgColors::setColorWithDimming(PreviewItem * item, QColor foreground, QColor background, bool dimmed) { if (dimmed && dimFactor->value() < 100) { int dim = dimFactor->value(); QColor dimColor = getColorSelector("Dim Target Color")->getColor(); foreground = QColor((dimColor.red() * (100 - dim) + foreground.red() * dim) / 100, (dimColor.green() * (100 - dim) + foreground.green() * dim) / 100, (dimColor.blue() * (100 - dim) + foreground.blue() * dim) / 100); background = QColor((dimColor.red() * (100 - dim) + background.red() * dim) / 100, (dimColor.green() * (100 - dim) + background.green() * dim) / 100, (dimColor.blue() * (100 - dim) + background.blue() * dim) / 100); } item->setColor(foreground, background); } void KgColors::generatePreview() { const int currentPage = colorTabWidget->currentIndex(); preview->clear(); if (currentPage == activeTabIdx || currentPage == inactiveTabIdx) { PreviewItem *pwMarkCur = new PreviewItem(preview, i18n("Selected + Current")); PreviewItem *pwMark2 = new PreviewItem(preview, i18n("Selected 2")); PreviewItem *pwMark1 = new PreviewItem(preview, i18n("Selected 1")); PreviewItem *pwCurrent = new PreviewItem(preview, i18n("Current")); PreviewItem *pwInvLink = new PreviewItem(preview, i18n("Invalid symlink")); PreviewItem *pwSymLink = new PreviewItem(preview, i18n("Symbolic link")); PreviewItem *pwApp = new PreviewItem(preview, i18n("Application")); PreviewItem *pwFile = new PreviewItem(preview, i18n("File")); PreviewItem *pwDir = new PreviewItem(preview, i18n("Folder")); bool isActive = currentPage == 0; // create local color cache instance, which does NOT affect the color cache instance using to paint the panels KrColorCache colCache; // create local color settings instance, which initially contains the settings from krConfig KrColorSettings colorSettings; // copy over local settings to color settings instance, which does not affect the persisted krConfig settings QList names = KrColorSettings::getColorNames(); for (auto & name : names) { KonfiguratorColorChooser * chooser = getColorSelector(name); if (!chooser) continue; colorSettings.setColorTextValue(name, chooser->getValue()); if (chooser->isValueRGB()) colorSettings.setColorValue(name, chooser->getColor()); else colorSettings.setColorValue(name, QColor()); } colorSettings.setBoolValue("KDE Default", generals->find("KDE Default")->isChecked()); colorSettings.setBoolValue("Enable Alternate Background", generals->find("Enable Alternate Background")->isChecked()); colorSettings.setBoolValue("Show Current Item Always", generals->find("Show Current Item Always")->isChecked()); colorSettings.setBoolValue("Dim Inactive Colors", generals->find("Dim Inactive Colors")->isChecked()); colorSettings.setNumValue("Dim Factor", dimFactor->value()); // let the color cache use the local color settings colCache.setColors(colorSettings); // ask the local color cache for certain color groups and use them to color the preview KrColorGroup cg; // setting the background color colCache.getColors(cg, KrColorItemType(KrColorItemType::File, false, isActive, false, false)); QPalette pal = preview->palette(); pal.setColor(QPalette::Base, cg.background()); preview->setPalette(pal); colCache.getColors(cg, KrColorItemType(KrColorItemType::Directory, false, isActive, false, false)); pwDir->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::File, true, isActive, false, false)); pwFile->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::Executable, false, isActive, false, false)); pwApp->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::Symlink, true, isActive, false, false)); pwSymLink->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::InvalidSymlink, false, isActive, false, false)); pwInvLink->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::File, true, isActive, true, false)); pwCurrent->setColor(cg.text(), cg.background()); colCache.getColors(cg, KrColorItemType(KrColorItemType::File, false, isActive, false, true)); pwMark1->setColor(cg.highlightedText(), cg.highlight()); colCache.getColors(cg, KrColorItemType(KrColorItemType::File, true, isActive, false, true)); pwMark2->setColor(cg.highlightedText(), cg.highlight()); colCache.getColors(cg, KrColorItemType(KrColorItemType::File, false, isActive, true, true)); pwMarkCur->setColor(cg.highlightedText(), cg.highlight()); } #ifdef SYNCHRONIZER_ENABLED else if (currentPage == synchronizerTabIdx) { PreviewItem *pwDelete = new PreviewItem(preview, i18n("Delete")); PreviewItem *pwRightCopy = new PreviewItem(preview, i18n("Copy to right")); PreviewItem *pwLeftCopy = new PreviewItem(preview, i18n("Copy to left")); PreviewItem *pwDiffers = new PreviewItem(preview, i18n("Differing")); PreviewItem *pwEquals = new PreviewItem(preview, i18n("Equals")); pwEquals->setColor(getColorSelector("Synchronizer Equals Foreground")->getColor(), getColorSelector("Synchronizer Equals Background")->getColor()); pwDiffers->setColor(getColorSelector("Synchronizer Differs Foreground")->getColor(), getColorSelector("Synchronizer Differs Background")->getColor()); pwLeftCopy->setColor(getColorSelector("Synchronizer LeftCopy Foreground")->getColor(), getColorSelector("Synchronizer LeftCopy Background")->getColor()); pwRightCopy->setColor(getColorSelector("Synchronizer RightCopy Foreground")->getColor(), getColorSelector("Synchronizer RightCopy Background")->getColor()); pwDelete->setColor(getColorSelector("Synchronizer Delete Foreground")->getColor(), getColorSelector("Synchronizer Delete Background")->getColor()); } #endif else if (currentPage == otherTabIdx) { PreviewItem *pwNonMatch = new PreviewItem(preview, i18n("Quicksearch non-match")); PreviewItem *pwMatch = new PreviewItem(preview, i18n("Quicksearch match")); pwMatch->setColor(getColorSelector("Quicksearch Match Foreground")->getColor(), getColorSelector("Quicksearch Match Background")->getColor()); pwNonMatch->setColor(getColorSelector("Quicksearch Non-match Foreground")->getColor(), getColorSelector("Quicksearch Non-match Background")->getColor()); PreviewItem *pwStatusActive = new PreviewItem(preview, i18n("Statusbar active")); PreviewItem *pwStatusInactive = new PreviewItem(preview, i18n("Statusbar inactive")); pwStatusActive->setColor(getColorSelector("Statusbar Foreground Active")->getColor(), getColorSelector("Statusbar Background Active")->getColor()); pwStatusInactive->setColor(getColorSelector("Statusbar Foreground Inactive")->getColor(), getColorSelector("Statusbar Background Inactive")->getColor()); } } bool KgColors::apply() { bool result = KonfiguratorPage::apply(); KrColorCache::getColorCache().refreshColors(); return result; } void KgColors::slotImportColors() { // find $KDEDIR/share/apps/krusader QString basedir= QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("total_commander.keymap")); basedir = QFileInfo(basedir).absolutePath(); // let the user select a file to load QString file = QFileDialog::getOpenFileName(nullptr, i18n("Select a color-scheme file"), basedir, QStringLiteral("*.color")); if (file.isEmpty()) { return; } QFile f(file); if (!f.open(QIODevice::ReadOnly)) { KMessageBox::error(this, i18n("Error: unable to read from file"), i18n("Error")); return; } QDataStream stream(&f); // ok, import away deserialize(stream); generatePreview(); } void KgColors::slotExportColors() { QString file = QFileDialog::getSaveFileName(nullptr, i18n("Select a color scheme file"), QString(), QStringLiteral("*")); if (file.isEmpty()) { return; } QFile f(file); if (f.exists() && KMessageBox::warningContinueCancel(this, i18n("File %1 already exists. Are you sure you want to overwrite it?", file), i18n("Warning"), KStandardGuiItem::overwrite()) != KMessageBox::Continue) return; if (!f.open(QIODevice::WriteOnly)) { KMessageBox::error(this, i18n("Error: unable to write to file"), i18n("Error")); return; } QDataStream stream(&f); serialize(stream); } void KgColors::serialize(QDataStream & stream) { serializeItem(stream, "Alternate Background"); serializeItem(stream, "Alternate Marked Background"); serializeItem(stream, "Background"); serializeItem(stream, "Current Background"); serializeItem(stream, "Current Foreground"); serializeItem(stream, "Enable Alternate Background"); serializeItem(stream, "Foreground"); serializeItem(stream, "Directory Foreground"); serializeItem(stream, "Executable Foreground"); serializeItem(stream, "Symlink Foreground"); serializeItem(stream, "Invalid Symlink Foreground"); serializeItem(stream, "Inactive Alternate Background"); serializeItem(stream, "Inactive Alternate Marked Background"); serializeItem(stream, "Inactive Background"); serializeItem(stream, "Inactive Current Foreground"); serializeItem(stream, "Inactive Current Background"); serializeItem(stream, "Inactive Marked Background"); serializeItem(stream, "Inactive Marked Current Foreground"); serializeItem(stream, "Inactive Marked Foreground"); serializeItem(stream, "Inactive Foreground"); serializeItem(stream, "Inactive Directory Foreground"); serializeItem(stream, "Inactive Executable Foreground"); serializeItem(stream, "Inactive Symlink Foreground"); serializeItem(stream, "Inactive Invalid Symlink Foreground"); serializeItem(stream, "Dim Inactive Colors"); serializeItem(stream, "Dim Target Color"); serializeItem(stream, "Dim Factor"); serializeItem(stream, "KDE Default"); serializeItem(stream, "Marked Background"); serializeItem(stream, "Marked Current Foreground"); serializeItem(stream, "Marked Foreground"); serializeItem(stream, "Show Current Item Always"); #ifdef SYNCHRONIZER_ENABLED serializeItem(stream, "Synchronizer Equals Foreground"); serializeItem(stream, "Synchronizer Equals Background"); serializeItem(stream, "Synchronizer Differs Foreground"); serializeItem(stream, "Synchronizer Differs Background"); serializeItem(stream, "Synchronizer LeftCopy Foreground"); serializeItem(stream, "Synchronizer LeftCopy Background"); serializeItem(stream, "Synchronizer RightCopy Foreground"); serializeItem(stream, "Synchronizer RightCopy Background"); serializeItem(stream, "Synchronizer Delete Foreground"); serializeItem(stream, "Synchronizer Delete Background"); #endif serializeItem(stream, "Quicksearch Match Foreground"); serializeItem(stream, "Quicksearch Match Background"); serializeItem(stream, "Quicksearch Non-match Foreground"); serializeItem(stream, "Quicksearch Non-match Background"); serializeItem(stream, "Statusbar Foreground Active"); serializeItem(stream, "Statusbar Background Active"); serializeItem(stream, "Statusbar Foreground Inactive"); serializeItem(stream, "Statusbar Background Inactive"); stream << QString("") << QString(""); } void KgColors::deserialize(QDataStream & stream) { for (;;) { QString name; QString value; stream >> name >> value; if (name.isEmpty()) break; if (name == "KDE Default" || name == "Enable Alternate Background" || name == "Show Current Item Always" || name == "Dim Inactive Colors") { bool bValue = false; value = value.toLower(); if (value == "true" || value == "yes" || value == "on" || value == "1") bValue = true; generals->find(name)->setChecked(bValue); continue; } if (name == "Dim Factor") { dimFactor->setValue(value.toInt()); continue; } KonfiguratorColorChooser *selector = getColorSelector(name); if (selector == nullptr) break; selector->setValue(value); } } void KgColors::serializeItem(class QDataStream & stream, const char * name) { stream << QString(name); if (strcmp(name, "KDE Default") == 0 || strcmp(name, "Enable Alternate Background") == 0 || strcmp(name, "Show Current Item Always") == 0 || strcmp(name, "Dim Inactive Colors") == 0) { bool bValue = generals->find(name)->isChecked(); stream << QString(bValue ? "true" : "false"); } else if (strcmp(name, "Dim Factor") == 0) stream << QString::number(dimFactor->value()); else { KonfiguratorColorChooser *selector = getColorSelector(name); stream << selector->getValue(); } } diff --git a/krusader/Konfigurator/kgcolors.h b/krusader/Konfigurator/kgcolors.h index 7af2bc1b..651c4905 100644 --- a/krusader/Konfigurator/kgcolors.h +++ b/krusader/Konfigurator/kgcolors.h @@ -1,140 +1,140 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 KGCOLORS_H #define KGCOLORS_H // QtCore #include // QtWidgets #include #include #include #include #include "konfiguratorpage.h" #include "../GUI/krtreewidget.h" class KgColors : public KonfiguratorPage { Q_OBJECT public: explicit KgColors(bool first, QWidget* parent = nullptr); bool apply() Q_DECL_OVERRIDE; public slots: void slotDisable(); void slotForegroundChanged(); void slotBackgroundChanged(); void slotAltBackgroundChanged(); void slotActiveChanged(); void slotMarkedBackgroundChanged(); void slotInactiveForegroundChanged(); void slotInactiveBackgroundChanged(); void slotInactiveAltBackgroundChanged(); void slotInactiveMarkedBackgroundChanged(); void generatePreview(); protected slots: void slotImportColors(); void slotExportColors(); private: class PreviewItem; - int addColorSelector(QString cfgName, QString name, QColor defaultValue, QString dfltName = QString(), + int addColorSelector(const QString& cfgName, QString name, QColor defaultValue, const QString& dfltName = QString(), ADDITIONAL_COLOR *addColor = nullptr, int addColNum = 0); - KonfiguratorColorChooser *getColorSelector(QString name); - QLabel *getSelectorLabel(QString name); + KonfiguratorColorChooser *getColorSelector(const QString& name); + QLabel *getSelectorLabel(const QString& name); void serialize(class QDataStream &); void deserialize(class QDataStream &); void serializeItem(class QDataStream &, const char * name); void setColorWithDimming(PreviewItem * item, QColor foreground, QColor background, bool dimmed); private: QWidget *colorsGrp; QGridLayout *colorsGrid; int offset; int endOfActiveColors; int endOfPanelColors; int activeTabIdx, inactiveTabIdx; #ifdef SYNCHRONIZER_ENABLED int synchronizerTabIdx; #endif int otherTabIdx; QGroupBox *previewGrp; QGridLayout *previewGrid; QTabWidget *colorTabWidget; QStackedWidget *inactiveColorStack; QWidget *normalInactiveWidget; QWidget *dimmedInactiveWidget; KonfiguratorSpinBox *dimFactor; KonfiguratorCheckBoxGroup *generals; QList labelList; QList itemList; QList itemNames; KrTreeWidget *preview; QPushButton *importBtn, *exportBtn; class PreviewItem : public QTreeWidgetItem { private: QColor defaultBackground; QColor defaultForeground; QString label; public: - PreviewItem(QTreeWidget * parent, QString name) : QTreeWidgetItem() { + PreviewItem(QTreeWidget * parent, const QString& name) : QTreeWidgetItem() { setText(0, name); defaultBackground = QColor(255, 255, 255); defaultForeground = QColor(0, 0, 0); label = name; parent->insertTopLevelItem(0, this); } - void setColor(QColor foregnd, QColor backgnd) { + void setColor(const QColor& foregnd, const QColor& backgnd) { defaultForeground = foregnd; defaultBackground = backgnd; QBrush textColor(foregnd); QBrush baseColor(backgnd); for (int i = 0; i != columnCount(); i++) { if (backgnd.isValid()) setBackground(i, baseColor); if (foregnd.isValid()) setForeground(i, textColor); } } QString text() { return label; } }; }; #endif /* __KGCOLORS_H__ */ diff --git a/krusader/Konfigurator/kgdependencies.cpp b/krusader/Konfigurator/kgdependencies.cpp index 0f05ce8d..55ef4b1a 100644 --- a/krusader/Konfigurator/kgdependencies.cpp +++ b/krusader/Konfigurator/kgdependencies.cpp @@ -1,172 +1,172 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "kgdependencies.h" #include "../krservices.h" #include "../krglobal.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #define PAGE_GENERAL 0 #define PAGE_PACKERS 1 #define PAGE_CHECKSUM 2 KgDependencies::KgDependencies(bool first, QWidget* parent) : KonfiguratorPage(first, parent) { auto *kgDependenciesLayout = new QGridLayout(this); kgDependenciesLayout->setSpacing(6); // ---------------------------- GENERAL TAB ------------------------------------- tabWidget = new QTabWidget(this); QWidget *general_tab = new QWidget(tabWidget); auto* general_scroll = new QScrollArea(tabWidget); general_scroll->setFrameStyle(QFrame::NoFrame); general_scroll->setWidget(general_tab); // this also sets scrollacrea as the new parent for widget general_scroll->setWidgetResizable(true); // let the widget use every space available tabWidget->addTab(general_scroll, i18n("General")); auto *pathsGrid = new QGridLayout(general_tab); pathsGrid->setSpacing(6); pathsGrid->setContentsMargins(11, 11, 11, 11); pathsGrid->setAlignment(Qt::AlignTop); addApplication("kget", pathsGrid, 0, general_tab, PAGE_GENERAL); addApplication("mailer", pathsGrid, 1, general_tab, PAGE_GENERAL); addApplication("diff utility", pathsGrid, 2, general_tab, PAGE_GENERAL); addApplication("krename", pathsGrid, 3, general_tab, PAGE_GENERAL); addApplication("locate", pathsGrid, 4, general_tab, PAGE_GENERAL); addApplication("mount", pathsGrid, 5, general_tab, PAGE_GENERAL); addApplication("umount", pathsGrid, 6, general_tab, PAGE_GENERAL); addApplication("updatedb", pathsGrid, 7, general_tab, PAGE_GENERAL); // ---------------------------- PACKERS TAB ------------------------------------- QWidget *packers_tab = new QWidget(tabWidget); auto* packers_scroll = new QScrollArea(tabWidget); packers_scroll->setFrameStyle(QFrame::NoFrame); packers_scroll->setWidget(packers_tab); // this also sets scrollacrea as the new parent for widget packers_scroll->setWidgetResizable(true); // let the widget use every space available tabWidget->addTab(packers_scroll, i18n("Packers")); auto *archGrid1 = new QGridLayout(packers_tab); archGrid1->setSpacing(6); archGrid1->setContentsMargins(11, 11, 11, 11); archGrid1->setAlignment(Qt::AlignTop); addApplication("7z", archGrid1, 0, packers_tab, PAGE_PACKERS, "7za"); addApplication("arj", archGrid1, 1, packers_tab, PAGE_PACKERS); addApplication("bzip2", archGrid1, 2, packers_tab, PAGE_PACKERS); addApplication("cpio", archGrid1, 3, packers_tab, PAGE_PACKERS); addApplication("dpkg", archGrid1, 4, packers_tab, PAGE_PACKERS); addApplication("gzip", archGrid1, 5, packers_tab, PAGE_PACKERS); addApplication("lha", archGrid1, 6, packers_tab, PAGE_PACKERS); addApplication("lzma", archGrid1, 7, packers_tab, PAGE_PACKERS); addApplication("rar", archGrid1, 8, packers_tab, PAGE_PACKERS); addApplication("tar", archGrid1, 9, packers_tab, PAGE_PACKERS); addApplication("unace", archGrid1, 10, packers_tab, PAGE_PACKERS); addApplication("unarj", archGrid1, 11, packers_tab, PAGE_PACKERS); addApplication("unrar", archGrid1, 12, packers_tab, PAGE_PACKERS); addApplication("unzip", archGrid1, 13, packers_tab, PAGE_PACKERS); addApplication("zip", archGrid1, 14, packers_tab, PAGE_PACKERS); addApplication("xz", archGrid1, 15, packers_tab, PAGE_PACKERS); // ---------------------------- CHECKSUM TAB ------------------------------------- QWidget *checksum_tab = new QWidget(tabWidget); auto* checksum_scroll = new QScrollArea(tabWidget); checksum_scroll->setFrameStyle(QFrame::NoFrame); checksum_scroll->setWidget(checksum_tab); // this also sets scrollacrea as the new parent for widget checksum_scroll->setWidgetResizable(true); // let the widget use every space available tabWidget->addTab(checksum_scroll, i18n("Checksum Utilities")); auto *archGrid2 = new QGridLayout(checksum_tab); archGrid2->setSpacing(6); archGrid2->setContentsMargins(11, 11, 11, 11); archGrid2->setAlignment(Qt::AlignTop); addApplication("md5sum", archGrid2, 0, checksum_tab, PAGE_CHECKSUM); addApplication("sha1sum", archGrid2, 1, checksum_tab, PAGE_CHECKSUM); addApplication("sha224sum", archGrid2, 2, checksum_tab, PAGE_CHECKSUM); addApplication("sha256sum", archGrid2, 3, checksum_tab, PAGE_CHECKSUM); addApplication("sha384sum", archGrid2, 4, checksum_tab, PAGE_CHECKSUM); addApplication("sha512sum", archGrid2, 5, checksum_tab, PAGE_CHECKSUM); kgDependenciesLayout->addWidget(tabWidget, 0, 0); } -void KgDependencies::addApplication(QString name, QGridLayout *grid, int row, QWidget *parent, - int page, QString additionalList) +void KgDependencies::addApplication(const QString& name, QGridLayout *grid, int row, QWidget *parent, + int page, const QString& additionalList) { // try to autodetect the full path name QString defaultValue = KrServices::fullPathName(name); if (defaultValue.isEmpty()) { QStringList list = additionalList.split(',', QString::SkipEmptyParts); for (int i = 0; i != list.count(); i++) if (!KrServices::fullPathName(list[ i ]).isEmpty()) { defaultValue = KrServices::fullPathName(list[ i ]); break; } } addLabel(grid, row, 0, name, parent); KonfiguratorURLRequester *fullPath = createURLRequester("Dependencies", name, defaultValue, parent, false, page); connect(fullPath->extension(), &KonfiguratorExtension::applyManually, this, &KgDependencies::slotApply); grid->addWidget(fullPath, row, 1); } -void KgDependencies::slotApply(QObject *obj, QString configGroup, QString name) +void KgDependencies::slotApply(QObject *obj, const QString& configGroup, const QString& name) { auto *urlRequester = (KonfiguratorURLRequester *) obj; KConfigGroup group(krConfig, configGroup); group.writeEntry(name, urlRequester->url().toDisplayString(QUrl::PreferLocalFile)); QString usedPath = KrServices::fullPathName(name); if (urlRequester->url().toDisplayString(QUrl::PreferLocalFile) != usedPath) { group.writeEntry(name, usedPath); if (usedPath.isEmpty()) KMessageBox::error(this, i18n("The %1 path is incorrect, no valid path found.", urlRequester->url().toDisplayString(QUrl::PreferLocalFile))); else KMessageBox::error( this, i18n("The %1 path is incorrect, %2 used instead.", urlRequester->url().toDisplayString(QUrl::PreferLocalFile), usedPath)); urlRequester->setUrl(QUrl::fromLocalFile(usedPath)); } } int KgDependencies::activeSubPage() { return tabWidget->currentIndex(); } diff --git a/krusader/Konfigurator/kgdependencies.h b/krusader/Konfigurator/kgdependencies.h index 482749a0..412d3463 100644 --- a/krusader/Konfigurator/kgdependencies.h +++ b/krusader/Konfigurator/kgdependencies.h @@ -1,51 +1,51 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 KGDEPENDENCIES_H #define KGDEPENDENCIES_H // QtWidgets #include #include "konfiguratorpage.h" class QTabWidget; class KgDependencies : public KonfiguratorPage { Q_OBJECT public: explicit KgDependencies(bool first, QWidget* parent = nullptr); int activeSubPage() Q_DECL_OVERRIDE; private: - void addApplication(QString name, QGridLayout *grid, int row, QWidget *parent, int page, QString additionalList = QString()); + void addApplication(const QString& name, QGridLayout *grid, int row, QWidget *parent, int page, const QString& additionalList = QString()); public slots: - void slotApply(QObject *obj, QString configGroup, QString name); + void slotApply(QObject *obj, const QString& configGroup, const QString& name); private: QTabWidget *tabWidget; }; #endif /* __KGDEPENDENCIES_H__ */ diff --git a/krusader/Konfigurator/kggeneral.cpp b/krusader/Konfigurator/kggeneral.cpp index f17ba1b6..ce221309 100644 --- a/krusader/Konfigurator/kggeneral.cpp +++ b/krusader/Konfigurator/kggeneral.cpp @@ -1,329 +1,329 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "kggeneral.h" // QtCore #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include "krresulttabledialog.h" #include "../defaults.h" #include "../icon.h" #include "../krglobal.h" #define PAGE_GENERAL 0 #define PAGE_VIEWER 1 #define PAGE_EXTENSIONS 2 KgGeneral::KgGeneral(bool first, QWidget* parent) : KonfiguratorPage(first, parent) { if (first) slotFindTools(); tabWidget = new QTabWidget(this); setWidget(tabWidget); setWidgetResizable(true); createGeneralTab(); createViewerTab(); createExtensionsTab(); } -QWidget* KgGeneral::createTab(QString name) +QWidget* KgGeneral::createTab(const QString& name) { auto *scrollArea = new QScrollArea(tabWidget); tabWidget->addTab(scrollArea, name); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setWidgetResizable(true); QWidget *tab = new QWidget(scrollArea); scrollArea->setWidget(tab); return tab; } void KgGeneral::createViewerTab() { QWidget *tab = createTab(i18n("Viewer/Editor")); auto *tabLayout = new QGridLayout(tab); tabLayout->setSpacing(6); tabLayout->setContentsMargins(11, 11, 11, 11); tabLayout->addWidget(createCheckBox("General", "View In Separate Window", _ViewInSeparateWindow, i18n("Internal editor and viewer opens each file in a separate window"), tab, false, i18n("If checked, each file will open in a separate window, otherwise, the viewer will work in a single, tabbed mode"), PAGE_VIEWER)); // ------------------------- viewer ---------------------------------- QGroupBox *viewerGrp = createFrame(i18n("Viewer"), tab); tabLayout->addWidget(viewerGrp, 1, 0); QGridLayout *viewerGrid = createGridLayout(viewerGrp); QWidget * hboxWidget2 = new QWidget(viewerGrp); auto * hbox2 = new QHBoxLayout(hboxWidget2); QWidget * vboxWidget = new QWidget(hboxWidget2); auto * vbox = new QVBoxLayout(vboxWidget); vbox->addWidget(new QLabel(i18n("Default viewer mode:"), vboxWidget)); KONFIGURATOR_NAME_VALUE_TIP viewMode[] = // name value tooltip {{ i18n("Generic mode"), "generic", i18n("Use the system's default viewer") }, { i18n("Text mode"), "text", i18n("View the file in text-only mode") }, { i18n("Hex mode"), "hex", i18n("View the file in hex-mode (better for binary files)") }, { i18n("Lister mode"), "lister", i18n("View the file with lister (for huge text files)") } }; vbox->addWidget(createRadioButtonGroup("General", "Default Viewer Mode", "generic", 0, 4, viewMode, 4, vboxWidget, false, PAGE_VIEWER)); vbox->addWidget( createCheckBox("General", "UseOktetaViewer", _UseOktetaViewer, i18n("Use Okteta as Hex viewer"), vboxWidget, false, i18n("If available, use Okteta as Hex viewer instead of the internal viewer"), PAGE_VIEWER) ); QWidget * hboxWidget4 = new QWidget(vboxWidget); auto * hbox4 = new QHBoxLayout(hboxWidget4); QLabel *label5 = new QLabel(i18n("Use lister if the text file is bigger than:"), hboxWidget4); hbox4->addWidget(label5); KonfiguratorSpinBox *spinBox = createSpinBox("General", "Lister Limit", _ListerLimit, 0, 0x7FFFFFFF, hboxWidget4, false, PAGE_VIEWER); hbox4->addWidget(spinBox); QLabel *label6 = new QLabel(i18n("MB"), hboxWidget4); hbox4->addWidget(label6); vbox->addWidget(hboxWidget4); hbox2->addWidget(vboxWidget); viewerGrid->addWidget(hboxWidget2, 0, 0); // ------------------------- editor ---------------------------------- QGroupBox *editorGrp = createFrame(i18n("Editor"), tab); tabLayout->addWidget(editorGrp, 2, 0); QGridLayout *editorGrid = createGridLayout(editorGrp); QLabel *label1 = new QLabel(i18n("Editor:"), editorGrp); editorGrid->addWidget(label1, 0, 0); KonfiguratorURLRequester *urlReq = createURLRequester("General", "Editor", "internal editor", editorGrp, false, PAGE_VIEWER, false); editorGrid->addWidget(urlReq, 0, 1); QLabel *label2 = new QLabel(i18n("Hint: use 'internal editor' if you want to use Krusader's fast built-in editor"), editorGrp); editorGrid->addWidget(label2, 1, 0, 1, 2); } void KgGeneral::createExtensionsTab() { // ------------------------- atomic extensions ---------------------------------- QWidget *tab = createTab(i18n("Atomic extensions")); auto *tabLayout = new QGridLayout(tab); tabLayout->setSpacing(6); tabLayout->setContentsMargins(11, 11, 11, 11); QWidget * vboxWidget2 = new QWidget(tab); tabLayout->addWidget(vboxWidget2); auto * vbox2 = new QVBoxLayout(vboxWidget2); QWidget * hboxWidget3 = new QWidget(vboxWidget2); vbox2->addWidget(hboxWidget3); auto * hbox3 = new QHBoxLayout(hboxWidget3); QLabel * atomLabel = new QLabel(i18n("Atomic extensions:"), hboxWidget3); hbox3->addWidget(atomLabel); int size = QFontMetrics(atomLabel->font()).height(); auto *addButton = new QToolButton(hboxWidget3); hbox3->addWidget(addButton); QPixmap iconPixmap = Icon("list-add").pixmap(size); addButton->setFixedSize(iconPixmap.width() + 4, iconPixmap.height() + 4); addButton->setIcon(QIcon(iconPixmap)); connect(addButton, &QToolButton::clicked, this, &KgGeneral::slotAddExtension); auto *removeButton = new QToolButton(hboxWidget3); hbox3->addWidget(removeButton); iconPixmap = Icon("list-remove").pixmap(size); removeButton->setFixedSize(iconPixmap.width() + 4, iconPixmap.height() + 4); removeButton->setIcon(QIcon(iconPixmap)); connect(removeButton, &QToolButton::clicked, this, &KgGeneral::slotRemoveExtension); QStringList defaultAtomicExtensions; defaultAtomicExtensions += ".tar.gz"; defaultAtomicExtensions += ".tar.bz2"; defaultAtomicExtensions += ".tar.lzma"; defaultAtomicExtensions += ".tar.xz"; defaultAtomicExtensions += ".moc.cpp"; listBox = createListBox("Look&Feel", "Atomic Extensions", defaultAtomicExtensions, vboxWidget2, true, PAGE_EXTENSIONS); vbox2->addWidget(listBox); } void KgGeneral::createGeneralTab() { QWidget *tab = createTab(i18n("General")); auto *kgGeneralLayout = new QGridLayout(tab); kgGeneralLayout->setSpacing(6); kgGeneralLayout->setContentsMargins(11, 11, 11, 11); // -------------------------- GENERAL GROUPBOX ---------------------------------- QGroupBox *generalGrp = createFrame(i18n("General"), tab); QGridLayout *generalGrid = createGridLayout(generalGrp); KONFIGURATOR_CHECKBOX_PARAM settings[] = { // cfg_class cfg_name default text restart tooltip {"Look&Feel", "Warn On Exit", _WarnOnExit, i18n("Warn on exit"), false, i18n("Display a warning when trying to close the main window.") }, // KDE4: move warn on exit to the other confirmations {"Look&Feel", "Minimize To Tray", _ShowTrayIcon, i18n("Show and close to tray"), false, i18n("Show an icon in the system tray and keep running in the background when the window is closed.") }, }; KonfiguratorCheckBoxGroup *cbs = createCheckBoxGroup(2, 0, settings, 2 /*count*/, generalGrp, PAGE_GENERAL); generalGrid->addWidget(cbs, 0, 0); // temp dir auto *hbox = new QHBoxLayout(); hbox->addWidget(new QLabel(i18n("Temp Folder:"), generalGrp)); KonfiguratorURLRequester *urlReq3 = createURLRequester("General", "Temp Directory", _TempDirectory, generalGrp, false, PAGE_GENERAL); urlReq3->setMode(KFile::Directory); connect(urlReq3->extension(), &KonfiguratorExtension::applyManually, this, &KgGeneral::applyTempDir); hbox->addWidget(urlReq3); generalGrid->addLayout(hbox, 13, 0, 1, 1); QLabel *label4 = new QLabel(i18n("Note: you must have full permissions for the temporary folder."), generalGrp); generalGrid->addWidget(label4, 14, 0, 1, 1); kgGeneralLayout->addWidget(generalGrp, 0 , 0); // ----------------------- delete mode -------------------------------------- QGroupBox *delGrp = createFrame(i18n("Delete mode"), tab); QGridLayout *delGrid = createGridLayout(delGrp); KONFIGURATOR_NAME_VALUE_TIP deleteMode[] = // name value tooltip {{i18n("Move to trash"), "true", i18n("Files will be moved to trash when deleted.")}, {i18n("Delete files"), "false", i18n("Files will be permanently deleted.")} }; KonfiguratorRadioButtons *trashRadio = createRadioButtonGroup("General", "Move To Trash", _MoveToTrash ? "true" : "false", 2, 0, deleteMode, 2, delGrp, false, PAGE_GENERAL); delGrid->addWidget(trashRadio); kgGeneralLayout->addWidget(delGrp, 1 , 0); // ----------------------- terminal ----------------------------------------- QGroupBox *terminalGrp = createFrame(i18n("Terminal"), tab); QGridLayout *terminalGrid = createGridLayout(terminalGrp); QLabel *label3 = new QLabel(i18n("External Terminal:"), generalGrp); terminalGrid->addWidget(label3, 0, 0); KonfiguratorURLRequester *urlReq2 = createURLRequester("General", "Terminal", _Terminal, generalGrp, false, PAGE_GENERAL, false); terminalGrid->addWidget(urlReq2, 0, 1); QLabel *terminalLabel = new QLabel(i18n("%d will be replaced by the workdir."), terminalGrp); terminalGrid->addWidget(terminalLabel, 1, 1); KONFIGURATOR_CHECKBOX_PARAM terminal_settings[] = { // cfg_class cfg_name default text restart tooltip {"General", "Send CDs", _SendCDs, i18n("Embedded Terminal sends Chdir on panel change"), false, i18n("When checked, whenever the panel is changed (for example, by pressing Tab), Krusader changes the current folder in the embedded terminal.") }, }; cbs = createCheckBoxGroup(1, 0, terminal_settings, 1 /*count*/, terminalGrp, PAGE_GENERAL); terminalGrid->addWidget(cbs, 2, 0, 1, 2); kgGeneralLayout->addWidget(terminalGrp, 2 , 0); } -void KgGeneral::applyTempDir(QObject *obj, QString configGroup, QString name) +void KgGeneral::applyTempDir(QObject *obj, const QString& configGroup, const QString& name) { auto *urlReq = (KonfiguratorURLRequester *)obj; QString value = urlReq->url().toDisplayString(QUrl::PreferLocalFile); KConfigGroup(krConfig, configGroup).writeEntry(name, value); } void KgGeneral::slotFindTools() { QPointer dlg = new KrResultTableDialog(this, KrResultTableDialog::Tool, i18n("Search results"), i18n("Searching for tools..."), "tools-wizard", i18n("Make sure to install new tools in your $PATH (e.g. /usr/bin)")); dlg->exec(); delete dlg; } void KgGeneral::slotAddExtension() { bool ok; QString atomExt = QInputDialog::getText(this, i18n("Add new atomic extension"), i18n("Extension:"), QLineEdit::Normal, QString(), &ok); if (ok) { if (!atomExt.startsWith('.') || atomExt.indexOf('.', 1) == -1) KMessageBox::error(krMainWindow, i18n("Atomic extensions must start with '.' and must contain at least one more '.' character."), i18n("Error")); else listBox->addItem(atomExt); } } void KgGeneral::slotRemoveExtension() { QList list = listBox->selectedItems(); for (int i = 0; i != list.count(); i++) listBox->removeItem(list[ i ]->text()); } diff --git a/krusader/Konfigurator/kggeneral.h b/krusader/Konfigurator/kggeneral.h index 6d4c1235..2a366f19 100644 --- a/krusader/Konfigurator/kggeneral.h +++ b/krusader/Konfigurator/kggeneral.h @@ -1,50 +1,50 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 KGGENERAL_H #define KGGENERAL_H #include "konfiguratorpage.h" class KgGeneral : public KonfiguratorPage { Q_OBJECT public: explicit KgGeneral(bool first, QWidget* parent = nullptr); public slots: - void applyTempDir(QObject *, QString, QString); + void applyTempDir(QObject *, const QString&, const QString&); void slotFindTools(); void slotAddExtension(); void slotRemoveExtension(); private: void createGeneralTab(); void createViewerTab(); void createExtensionsTab(); - QWidget* createTab(QString name); + QWidget* createTab(const QString& name); QTabWidget *tabWidget; KonfiguratorListBox *listBox; }; #endif /* __KGGENERAL_H__ */ diff --git a/krusader/Konfigurator/kgprotocols.cpp b/krusader/Konfigurator/kgprotocols.cpp index 985d468d..41adaf17 100644 --- a/krusader/Konfigurator/kgprotocols.cpp +++ b/krusader/Konfigurator/kgprotocols.cpp @@ -1,389 +1,389 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 "kgprotocols.h" #include "../krglobal.h" #include "../icon.h" #include "../krservices.h" // QtCore #include #include // QtWidgets #include #include #include #include #include #include KgProtocols::KgProtocols(bool first, QWidget* parent) : KonfiguratorPage(first, parent) { auto *KgProtocolsLayout = new QGridLayout(this); KgProtocolsLayout->setSpacing(6); // -------------------------- LINK VIEW ---------------------------------- QGroupBox *linkGrp = createFrame(i18n("Links"), this); QGridLayout *linkGrid = createGridLayout(linkGrp); QStringList labels; labels << i18n("Defined Links"); linkList = new KrTreeWidget(linkGrp); linkList->setHeaderLabels(labels); linkList->setRootIsDecorated(true); linkGrid->addWidget(linkList, 0, 0); KgProtocolsLayout->addWidget(linkGrp, 0, 0, 2, 1); // -------------------------- BUTTONS ---------------------------------- QWidget *vbox1Widget = new QWidget(this); auto *vbox1 = new QVBoxLayout(vbox1Widget); addSpacer(vbox1); btnAddProtocol = new QPushButton(vbox1Widget); btnAddProtocol->setIcon(Icon("arrow-left")); btnAddProtocol->setWhatsThis(i18n("Add protocol to the link list.")); vbox1->addWidget(btnAddProtocol); btnRemoveProtocol = new QPushButton(vbox1Widget); btnRemoveProtocol->setIcon(Icon("arrow-right")); btnRemoveProtocol->setWhatsThis(i18n("Remove protocol from the link list.")); vbox1->addWidget(btnRemoveProtocol); addSpacer(vbox1); KgProtocolsLayout->addWidget(vbox1Widget, 0 , 1); QWidget *vbox2Widget = new QWidget(this); auto *vbox2 = new QVBoxLayout(vbox2Widget); addSpacer(vbox2); btnAddMime = new QPushButton(vbox2Widget); btnAddMime->setIcon(Icon("arrow-left")); btnAddMime->setWhatsThis(i18n("Add MIME to the selected protocol on the link list.")); vbox2->addWidget(btnAddMime); btnRemoveMime = new QPushButton(vbox2Widget); btnRemoveMime->setIcon(Icon("arrow-right")); btnRemoveMime->setWhatsThis(i18n("Remove MIME from the link list.")); vbox2->addWidget(btnRemoveMime); addSpacer(vbox2); KgProtocolsLayout->addWidget(vbox2Widget, 1 , 1); // -------------------------- PROTOCOLS LISTBOX ---------------------------------- QGroupBox *protocolGrp = createFrame(i18n("Protocols"), this); QGridLayout *protocolGrid = createGridLayout(protocolGrp); protocolList = new KrListWidget(protocolGrp); loadProtocols(); protocolGrid->addWidget(protocolList, 0, 0); KgProtocolsLayout->addWidget(protocolGrp, 0 , 2); // -------------------------- MIMES LISTBOX ---------------------------------- QGroupBox *mimeGrp = createFrame(i18n("MIMEs"), this); QGridLayout *mimeGrid = createGridLayout(mimeGrp); mimeList = new KrListWidget(mimeGrp); loadMimes(); mimeGrid->addWidget(mimeList, 0, 0); KgProtocolsLayout->addWidget(mimeGrp, 1 , 2); // -------------------------- CONNECT TABLE ---------------------------------- connect(protocolList, &KrListWidget::itemSelectionChanged, this, &KgProtocols::slotDisableButtons); connect(linkList, &KrTreeWidget::itemSelectionChanged, this, &KgProtocols::slotDisableButtons); connect(mimeList, &KrListWidget::itemSelectionChanged, this, &KgProtocols::slotDisableButtons); connect(linkList, &KrTreeWidget::currentItemChanged , this, &KgProtocols::slotDisableButtons); connect(btnAddProtocol, &QPushButton::clicked , this, &KgProtocols::slotAddProtocol); connect(btnRemoveProtocol, &QPushButton::clicked , this, &KgProtocols::slotRemoveProtocol); connect(btnAddMime, &QPushButton::clicked , this, &KgProtocols::slotAddMime); connect(btnRemoveMime, &QPushButton::clicked , this, &KgProtocols::slotRemoveMime); loadInitialValues(); slotDisableButtons(); } void KgProtocols::addSpacer(QBoxLayout *layout) { layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); } void KgProtocols::loadProtocols() { QStringList protocols = KProtocolInfo::protocols(); protocols.sort(); foreach(const QString &protocol, protocols) { QUrl u; u.setScheme(protocol); if(KProtocolManager::inputType(u) == KProtocolInfo::T_FILESYSTEM) { protocolList->addItem(protocol); } } } void KgProtocols::loadMimes() { QMimeDatabase db; QList mimes = db.allMimeTypes(); for (QList::const_iterator it = mimes.constBegin(); it != mimes.constEnd(); ++it) mimeList->addItem((*it).name()); mimeList->sortItems(); } void KgProtocols::slotDisableButtons() { btnAddProtocol->setEnabled(protocolList->selectedItems().count() != 0); QTreeWidgetItem *listViewItem = linkList->currentItem(); bool isProtocolSelected = (listViewItem == nullptr ? false : listViewItem->parent() == nullptr); btnRemoveProtocol->setEnabled(isProtocolSelected); btnAddMime->setEnabled(listViewItem != nullptr && mimeList->selectedItems().count() != 0); btnRemoveMime->setEnabled(listViewItem == nullptr ? false : listViewItem->parent() != nullptr); if (linkList->currentItem() == nullptr && linkList->topLevelItemCount() != 0) linkList->setCurrentItem(linkList->topLevelItem(0)); QList list = linkList->selectedItems(); if (list.count() == 0 && linkList->currentItem() != nullptr) linkList->currentItem()->setSelected(true); } void KgProtocols::slotAddProtocol() { QList list = protocolList->selectedItems(); if (list.count() > 0) { addProtocol(list[ 0 ]->text(), true); slotDisableButtons(); emit sigChanged(); } } -void KgProtocols::addProtocol(QString name, bool changeCurrent) +void KgProtocols::addProtocol(const QString& name, bool changeCurrent) { QList list = protocolList->findItems(name, Qt::MatchExactly); if (list.count() > 0) { delete list[ 0 ]; auto *listViewItem = new QTreeWidgetItem(linkList); listViewItem->setText(0, name); QString iconName = KProtocolInfo::icon(name); if (iconName.isEmpty()) iconName = "go-next-view"; listViewItem->setIcon(0, Icon(iconName)); if (changeCurrent) linkList->setCurrentItem(listViewItem); } } void KgProtocols::slotRemoveProtocol() { QTreeWidgetItem *item = linkList->currentItem(); if (item) { removeProtocol(item->text(0)); slotDisableButtons(); emit sigChanged(); } } -void KgProtocols::removeProtocol(QString name) +void KgProtocols::removeProtocol(const QString& name) { QList itemList = linkList->findItems(name, Qt::MatchExactly, 0); if (itemList.count()) { QTreeWidgetItem *item = itemList[ 0 ]; while (item->childCount() != 0) removeMime(item->child(0)->text(0)); linkList->takeTopLevelItem(linkList->indexOfTopLevelItem(item)); protocolList->addItem(name); protocolList->sortItems(); } } void KgProtocols::slotAddMime() { QList list = mimeList->selectedItems(); if (list.count() > 0 && linkList->currentItem() != nullptr) { QTreeWidgetItem *itemToAdd = linkList->currentItem(); if (itemToAdd->parent()) itemToAdd = itemToAdd->parent(); addMime(list[ 0 ]->text(), itemToAdd->text(0)); slotDisableButtons(); emit sigChanged(); } } -void KgProtocols::addMime(QString name, QString protocol) +void KgProtocols::addMime(QString name, const QString& protocol) { QList list = mimeList->findItems(name, Qt::MatchExactly); QList itemList = linkList->findItems(protocol, Qt::MatchExactly | Qt::MatchRecursive, 0); QTreeWidgetItem *currentListItem = nullptr; if (itemList.count() != 0) currentListItem = itemList[ 0 ]; if (list.count() > 0 && currentListItem && currentListItem->parent() == nullptr) { delete list[ 0 ]; auto *listViewItem = new QTreeWidgetItem(currentListItem); listViewItem->setText(0, name); listViewItem->setIcon(0, Icon(name.replace(QLatin1Char('/'), QLatin1Char('-')), Icon("unknown"))); linkList->expandItem( currentListItem ); } } void KgProtocols::slotRemoveMime() { QTreeWidgetItem *item = linkList->currentItem(); if (item) { removeMime(item->text(0)); slotDisableButtons(); emit sigChanged(); } } -void KgProtocols::removeMime(QString name) +void KgProtocols::removeMime(const QString& name) { QList itemList = linkList->findItems(name, Qt::MatchExactly | Qt::MatchRecursive, 0); QTreeWidgetItem *currentMimeItem = nullptr; if (itemList.count() != 0) currentMimeItem = itemList[ 0 ]; if (currentMimeItem && currentMimeItem->parent() != nullptr) { mimeList->addItem(currentMimeItem->text(0)); mimeList->sortItems(); currentMimeItem->parent()->removeChild(currentMimeItem); } } void KgProtocols::loadInitialValues() { if (linkList->model()->rowCount() > 0) while (linkList->topLevelItemCount() != 0) removeProtocol(linkList->topLevelItem(0)->text(0)); KConfigGroup group(krConfig, "Protocols"); QStringList protList = group.readEntry("Handled Protocols", QStringList()); for (auto & it : protList) { addProtocol(it); QStringList mimes = group.readEntry(QString("Mimes For %1").arg(it), QStringList()); for (auto & mime : mimes) addMime(mime, it); } if (linkList->topLevelItemCount() != 0) linkList->setCurrentItem(linkList->topLevelItem(0)); slotDisableButtons(); linkList->expandAll(); emit sigChanged(); } void KgProtocols::setDefaults() { while (linkList->topLevelItemCount() != 0) removeProtocol(linkList->topLevelItem(0)->text(0)); slotDisableButtons(); if (isChanged()) emit sigChanged(); } bool KgProtocols::isChanged() { KConfigGroup group(krConfig, "Protocols"); QStringList protList = group.readEntry("Handled Protocols", QStringList()); if ((int)protList.count() != linkList->topLevelItemCount()) return true; for (int i = 0; i != linkList->topLevelItemCount(); i++) { QTreeWidgetItem *item = linkList->topLevelItem(i); if (!protList.contains(item->text(0))) return true; QStringList mimes = group.readEntry(QString("Mimes For %1").arg(item->text(0)), QStringList()); if ((int)mimes.count() != item->childCount()) return true; for (int j = 0; j != item->childCount(); j++) { QTreeWidgetItem *children = item->child(j); if (!mimes.contains(children->text(0))) return true; } } return false; } bool KgProtocols::apply() { KConfigGroup group(krConfig, "Protocols"); QStringList protocolList; for (int i = 0; i != linkList->topLevelItemCount(); i++) { QTreeWidgetItem *item = linkList->topLevelItem(i); protocolList.append(item->text(0)); QStringList mimes; for (int j = 0; j != item->childCount(); j++) { QTreeWidgetItem *children = item->child(j); mimes.append(children->text(0)); } group.writeEntry(QString("Mimes For %1").arg(item->text(0)), mimes); } group.writeEntry("Handled Protocols", protocolList); krConfig->sync(); KrServices::clearProtocolCache(); emit sigChanged(); return false; } void KgProtocols::init() { } diff --git a/krusader/Konfigurator/kgprotocols.h b/krusader/Konfigurator/kgprotocols.h index b9cebf14..5237bcd5 100644 --- a/krusader/Konfigurator/kgprotocols.h +++ b/krusader/Konfigurator/kgprotocols.h @@ -1,73 +1,73 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * * Copyright (C) 2004-2018 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 KGPROTOCOLS_H #define KGPROTOCOLS_H // QtWidgets #include #include "konfiguratorpage.h" #include "../GUI/krtreewidget.h" #include "../GUI/krlistwidget.h" class KgProtocols : public KonfiguratorPage { Q_OBJECT public: explicit KgProtocols(bool first, QWidget* parent = nullptr); void loadInitialValues() Q_DECL_OVERRIDE; void setDefaults() Q_DECL_OVERRIDE; bool apply() Q_DECL_OVERRIDE; bool isChanged() Q_DECL_OVERRIDE; static void init(); public slots: void slotDisableButtons(); void slotAddProtocol(); void slotRemoveProtocol(); void slotAddMime(); void slotRemoveMime(); protected: void loadProtocols(); void loadMimes(); void addSpacer(QBoxLayout *parent); - void addProtocol(QString name, bool changeCurrent = false); - void removeProtocol(QString name); - void addMime(QString name, QString protocol); - void removeMime(QString name); + void addProtocol(const QString& name, bool changeCurrent = false); + void removeProtocol(const QString& name); + void addMime(QString name, const QString& protocol); + void removeMime(const QString& name); KrTreeWidget *linkList; KrListWidget *protocolList; KrListWidget *mimeList; QPushButton *btnAddProtocol; QPushButton *btnRemoveProtocol; QPushButton *btnAddMime; QPushButton *btnRemoveMime; }; #endif /* __KgProtocols_H__ */ diff --git a/krusader/Konfigurator/konfiguratoritems.cpp b/krusader/Konfigurator/konfiguratoritems.cpp index be13e5ac..98639521 100644 --- a/krusader/Konfigurator/konfiguratoritems.cpp +++ b/krusader/Konfigurator/konfiguratoritems.cpp @@ -1,836 +1,837 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "konfiguratoritems.h" #include "../krglobal.h" #include "../icon.h" // QtCore #include // QtGui #include #include #include // QtWidgets #include #include #include #include #include #include +#include KonfiguratorExtension::KonfiguratorExtension(QObject *obj, QString cfgGroup, QString cfgName, bool restartNeeded, int page) : QObject(), objectPtr(obj), applyConnected(false), setDefaultsConnected(false), changed(false), - restartNeeded(restartNeeded), subpage(page), configGroup(cfgGroup), configName(cfgName) + restartNeeded(restartNeeded), subpage(page), configGroup(std::move(cfgGroup)), configName(std::move(cfgName)) { } void KonfiguratorExtension::connectNotify(const QMetaMethod &signal) { if (signal == QMetaMethod::fromSignal(&KonfiguratorExtension::applyManually)) applyConnected = true; else if (signal == QMetaMethod::fromSignal(&KonfiguratorExtension::setDefaultsManually)) setDefaultsConnected = true; QObject::connectNotify(signal); } bool KonfiguratorExtension::apply() { if (!changed) return false; if (applyConnected) emit applyManually(objectPtr, configGroup, configName); else emit applyAuto(objectPtr, configGroup, configName); setChanged(false); return restartNeeded; } void KonfiguratorExtension::setDefaults() { if (setDefaultsConnected) emit setDefaultsManually(objectPtr); else emit setDefaultsAuto(objectPtr); } void KonfiguratorExtension::loadInitialValue() { emit setInitialValue(objectPtr); } bool KonfiguratorExtension::isChanged() { return changed; } // KonfiguratorCheckBox class /////////////////////////////// -KonfiguratorCheckBox::KonfiguratorCheckBox(QString configGroup, QString name, bool defaultValue, QString text, +KonfiguratorCheckBox::KonfiguratorCheckBox(QString configGroup, QString name, bool defaultValue, const QString& text, QWidget *parent, bool restart, int page) : QCheckBox(text, parent), defaultValue(defaultValue) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorCheckBox::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorCheckBox::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorCheckBox::loadInitialValue); connect(this, &KonfiguratorCheckBox::stateChanged, ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); loadInitialValue(); } KonfiguratorCheckBox::~KonfiguratorCheckBox() { delete ext; } void KonfiguratorCheckBox::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); setChecked(group.readEntry(ext->getConfigName(), defaultValue)); ext->setChanged(false); } void KonfiguratorCheckBox::checkStateSet() { QCheckBox::checkStateSet(); updateDeps(); } void KonfiguratorCheckBox::nextCheckState() { QCheckBox::nextCheckState(); updateDeps(); } void KonfiguratorCheckBox::addDep(KonfiguratorCheckBox *dep) { deps << dep; dep->setEnabled(isChecked()); } void KonfiguratorCheckBox::updateDeps() { foreach(KonfiguratorCheckBox *dep, deps) dep->setEnabled(isChecked()); } -void KonfiguratorCheckBox::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorCheckBox::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, isChecked()); } void KonfiguratorCheckBox::slotSetDefaults(QObject *) { if (isChecked() != defaultValue) setChecked(defaultValue); } // KonfiguratorSpinBox class /////////////////////////////// KonfiguratorSpinBox::KonfiguratorSpinBox(QString configGroup, QString configName, int defaultValue, int min, int max, QWidget *parent, bool restartNeeded, int page) : QSpinBox(parent), defaultValue(defaultValue) { - ext = new KonfiguratorExtension(this, configGroup, configName, restartNeeded, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(configName), restartNeeded, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorSpinBox::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorSpinBox::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorSpinBox::loadInitialValue); connect(this, QOverload::of(&KonfiguratorSpinBox::valueChanged), ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); setMinimum(min); setMaximum(max); loadInitialValue(); } KonfiguratorSpinBox::~KonfiguratorSpinBox() { delete ext; } void KonfiguratorSpinBox::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); setValue(group.readEntry(ext->getConfigName(), defaultValue)); ext->setChanged(false); } -void KonfiguratorSpinBox::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorSpinBox::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, value()); } void KonfiguratorSpinBox::slotSetDefaults(QObject *) { if (value() != defaultValue) setValue(defaultValue); } // KonfiguratorCheckBoxGroup class /////////////////////////////// void KonfiguratorCheckBoxGroup::add(KonfiguratorCheckBox *checkBox) { checkBoxList.append(checkBox); } KonfiguratorCheckBox * KonfiguratorCheckBoxGroup::find(int index) { if (index < 0 || index >= checkBoxList.count()) return nullptr; return checkBoxList.at(index); } -KonfiguratorCheckBox * KonfiguratorCheckBoxGroup::find(QString name) +KonfiguratorCheckBox * KonfiguratorCheckBoxGroup::find(const QString& name) { QListIterator it(checkBoxList); while (it.hasNext()) { KonfiguratorCheckBox * checkBox = it.next(); if (checkBox->extension()->getConfigName() == name) return checkBox; } return nullptr; } // KonfiguratorRadioButtons class /////////////////////////////// KonfiguratorRadioButtons::KonfiguratorRadioButtons(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page) : - QWidget(parent), defaultValue(defaultValue) + QWidget(parent), defaultValue(std::move(defaultValue)) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorRadioButtons::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorRadioButtons::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorRadioButtons::loadInitialValue); } KonfiguratorRadioButtons::~KonfiguratorRadioButtons() { delete ext; } -void KonfiguratorRadioButtons::addRadioButton(QRadioButton *radioWidget, QString name, QString value) +void KonfiguratorRadioButtons::addRadioButton(QRadioButton *radioWidget, const QString& name, const QString& value) { radioButtons.append(radioWidget); radioNames.push_back(name); radioValues.push_back(value); connect(radioWidget, &QRadioButton::toggled, ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); } QRadioButton * KonfiguratorRadioButtons::find(int index) { if (index < 0 || index >= radioButtons.count()) return nullptr; return radioButtons.at(index); } -QRadioButton * KonfiguratorRadioButtons::find(QString name) +QRadioButton * KonfiguratorRadioButtons::find(const QString& name) { int index = radioNames.indexOf(name); if (index == -1) return nullptr; return radioButtons.at(index); } -void KonfiguratorRadioButtons::selectButton(QString value) +void KonfiguratorRadioButtons::selectButton(const QString& value) { int cnt = 0; QListIterator it(radioButtons); while (it.hasNext()) { QRadioButton * btn = it.next(); if (value == radioValues[ cnt ]) { btn->setChecked(true); return; } cnt++; } if (!radioButtons.isEmpty()) radioButtons.first()->setChecked(true); } void KonfiguratorRadioButtons::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); QString initValue = group.readEntry(ext->getConfigName(), defaultValue); selectButton(initValue); ext->setChanged(false); } QString KonfiguratorRadioButtons::selectedValue() { int cnt = 0; QListIterator it(radioButtons); while (it.hasNext()) { QRadioButton * btn = it.next(); if (btn->isChecked()) { return radioValues[ cnt ]; } cnt++; } return QString(); } -void KonfiguratorRadioButtons::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorRadioButtons::slotApply(QObject *, const QString& configGroup, const QString& name) { QString value = selectedValue(); if (!value.isEmpty()) KConfigGroup(krConfig, configGroup).writeEntry(name, value); } void KonfiguratorRadioButtons::slotSetDefaults(QObject *) { selectButton(defaultValue); } // KonfiguratorEditBox class /////////////////////////////// KonfiguratorEditBox::KonfiguratorEditBox(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page) : QLineEdit(parent), - defaultValue(defaultValue) + defaultValue(std::move(defaultValue)) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorEditBox::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorEditBox::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorEditBox::loadInitialValue); connect(this, &KonfiguratorEditBox::textChanged, ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); loadInitialValue(); } KonfiguratorEditBox::~KonfiguratorEditBox() { delete ext; } void KonfiguratorEditBox::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); setText(group.readEntry(ext->getConfigName(), defaultValue)); ext->setChanged(false); } -void KonfiguratorEditBox::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorEditBox::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, text()); } void KonfiguratorEditBox::slotSetDefaults(QObject *) { if (text() != defaultValue) setText(defaultValue); } // KonfiguratorURLRequester class /////////////////////////////// KonfiguratorURLRequester::KonfiguratorURLRequester(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page, bool expansion) - : KUrlRequester(parent), defaultValue(defaultValue), expansion(expansion) + : KUrlRequester(parent), defaultValue(std::move(defaultValue)), expansion(expansion) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorURLRequester::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorURLRequester::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorURLRequester::loadInitialValue); connect(this, &KonfiguratorURLRequester::textChanged, ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); loadInitialValue(); } KonfiguratorURLRequester::~KonfiguratorURLRequester() { delete ext; } void KonfiguratorURLRequester::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); lineEdit()->setText(group.readEntry(ext->getConfigName(), defaultValue)); ext->setChanged(false); } -void KonfiguratorURLRequester::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorURLRequester::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup) .writeEntry(name, expansion ? url().toDisplayString(QUrl::PreferLocalFile) : text()); } void KonfiguratorURLRequester::slotSetDefaults(QObject *) { if (url().toDisplayString(QUrl::PreferLocalFile) != defaultValue) lineEdit()->setText(defaultValue); } // KonfiguratorFontChooser class /////////////////////////////// -KonfiguratorFontChooser::KonfiguratorFontChooser(QString configGroup, QString name, QFont defaultValue, +KonfiguratorFontChooser::KonfiguratorFontChooser(QString configGroup, QString name, const QFont& defaultValue, QWidget *parent, bool restart, int page) : QWidget(parent), defaultValue(defaultValue) { auto *layout = new QHBoxLayout(this); - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorFontChooser::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorFontChooser::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorFontChooser::loadInitialValue); pLabel = new QLabel(this); pLabel->setMinimumWidth(150); layout->addWidget(pLabel); pToolButton = new QToolButton(this); connect(pToolButton, &QToolButton::clicked, this, &KonfiguratorFontChooser::slotBrowseFont); pToolButton->setIcon(Icon("document-open")); layout->addWidget(pToolButton); loadInitialValue(); } KonfiguratorFontChooser::~KonfiguratorFontChooser() { delete ext; } void KonfiguratorFontChooser::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); font = group.readEntry(ext->getConfigName(), defaultValue); ext->setChanged(false); setFont(); } void KonfiguratorFontChooser::setFont() { pLabel->setFont(font); pLabel->setText(font.family() + QString(", %1").arg(font.pointSize())); } -void KonfiguratorFontChooser::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorFontChooser::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, font); } void KonfiguratorFontChooser::slotSetDefaults(QObject *) { font = defaultValue; ext->setChanged(); setFont(); } void KonfiguratorFontChooser::slotBrowseFont() { bool ok; font = QFontDialog::getFont(&ok, font, this); if (!ok) return; // cancelled by the user, and font is actually not changed (getFont returns the font we gave it) ext->setChanged(); setFont(); } // KonfiguratorComboBox class /////////////////////////////// KonfiguratorComboBox::KonfiguratorComboBox(QString configGroup, QString name, QString defaultValue, KONFIGURATOR_NAME_VALUE_PAIR *listIn, int listInLen, QWidget *parent, bool restart, bool editable, int page) : QComboBox(parent), - defaultValue(defaultValue), listLen(listInLen) + defaultValue(std::move(defaultValue)), listLen(listInLen) { list = new KONFIGURATOR_NAME_VALUE_PAIR[ listInLen ]; for (int i = 0; i != listLen; i++) { list[i] = listIn[i]; addItem(list[i].text); } - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorComboBox::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorComboBox::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorComboBox::loadInitialValue); //connect(this, &KonfiguratorComboBox::highlighted, ext, &KonfiguratorExtension::setChanged); /* Removed because of startup combo failure */ connect(this, QOverload::of(&KonfiguratorComboBox::activated), ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); connect(this, &KonfiguratorComboBox::currentTextChanged, ext, QOverload<>::of(&KonfiguratorExtension::setChanged)); setEditable(editable); loadInitialValue(); } KonfiguratorComboBox::~KonfiguratorComboBox() { delete []list; delete ext; } void KonfiguratorComboBox::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); QString select = group.readEntry(ext->getConfigName(), defaultValue); selectEntry(select); ext->setChanged(false); } -void KonfiguratorComboBox::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorComboBox::slotApply(QObject *, const QString& configGroup, const QString& name) { QString text = isEditable() ? lineEdit()->text() : currentText(); QString value = text; for (int i = 0; i != listLen; i++) if (list[i].text == text) { value = list[i].value; break; } KConfigGroup(krConfig, configGroup).writeEntry(name, value); } -void KonfiguratorComboBox::selectEntry(QString entry) +void KonfiguratorComboBox::selectEntry(const QString& entry) { for (int i = 0; i != listLen; i++) if (list[i].value == entry) { setCurrentIndex(i); return; } if (isEditable()) lineEdit()->setText(entry); else setCurrentIndex(0); } void KonfiguratorComboBox::slotSetDefaults(QObject *) { selectEntry(defaultValue); } // KonfiguratorColorChooser class /////////////////////////////// KonfiguratorColorChooser::KonfiguratorColorChooser(QString configGroup, QString name, - QColor defaultValue, QWidget *parent, + const QColor& defaultValue, QWidget *parent, bool restart, ADDITIONAL_COLOR *addColPtr, int addColNum, int page) : QComboBox(parent), defaultValue(defaultValue), disableColorChooser(true) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorColorChooser::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorColorChooser::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorColorChooser::loadInitialValue); addColor(i18n("Custom color"), QColor(255, 255, 255)); addColor(i18nc("Default color", "Default"), defaultValue); for (int i = 0; i != addColNum; i++) { additionalColors.push_back(addColPtr[i]); addColor(addColPtr[i].name, addColPtr[i].color); } addColor(i18n("Red"), Qt::red); addColor(i18n("Green"), Qt::green); addColor(i18n("Blue"), Qt::blue); addColor(i18n("Cyan"), Qt::cyan); addColor(i18n("Magenta"), Qt::magenta); addColor(i18n("Yellow"), Qt::yellow); addColor(i18n("Dark Red"), Qt::darkRed); addColor(i18n("Dark Green"), Qt::darkGreen); addColor(i18n("Dark Blue"), Qt::darkBlue); addColor(i18n("Dark Cyan"), Qt::darkCyan); addColor(i18n("Dark Magenta"), Qt::darkMagenta); addColor(i18n("Dark Yellow"), Qt::darkYellow); addColor(i18n("White"), Qt::white); addColor(i18n("Light Gray"), Qt::lightGray); addColor(i18n("Gray"), Qt::gray); addColor(i18n("Dark Gray"), Qt::darkGray); addColor(i18n("Black"), Qt::black); connect(this, QOverload::of(&KonfiguratorColorChooser::activated), this, &KonfiguratorColorChooser::slotCurrentChanged); loadInitialValue(); } KonfiguratorColorChooser::~KonfiguratorColorChooser() { delete ext; } -QPixmap KonfiguratorColorChooser::createPixmap(QColor color) +QPixmap KonfiguratorColorChooser::createPixmap(const QColor& color) { QPainter painter; QPen pen; int size = QFontMetrics(font()).height() * 3 / 4; QRect rect(0, 0, size, size); QPixmap pixmap(rect.width(), rect.height()); pen.setColor(Qt::black); painter.begin(&pixmap); QBrush brush(color); painter.fillRect(rect, brush); painter.setPen(pen); painter.drawRect(rect); painter.end(); pixmap.detach(); return pixmap; } -void KonfiguratorColorChooser::addColor(QString text, QColor color) +void KonfiguratorColorChooser::addColor(const QString& text, const QColor& color) { addItem(createPixmap(color), text); palette.push_back(color); } void KonfiguratorColorChooser::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); QString selected = group.readEntry(ext->getConfigName(), QString("")); setValue(selected); ext->setChanged(false); } void KonfiguratorColorChooser::setDefaultColor(QColor dflt) { - defaultValue = dflt; + defaultValue = std::move(dflt); palette[1] = defaultValue; setItemIcon(1, createPixmap(defaultValue)); if (currentIndex() == 1) emit colorChanged(); } -void KonfiguratorColorChooser::changeAdditionalColor(int num, QColor color) +void KonfiguratorColorChooser::changeAdditionalColor(int num, const QColor& color) { if (num < additionalColors.size()) { palette[2+num] = color; additionalColors[num].color = color; setItemIcon(2 + num, createPixmap(color)); if (currentIndex() == 2 + num) emit colorChanged(); } } -void KonfiguratorColorChooser::setDefaultText(QString text) +void KonfiguratorColorChooser::setDefaultText(const QString& text) { setItemIcon(1, createPixmap(defaultValue)); setItemText(1, text); } -void KonfiguratorColorChooser::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorColorChooser::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, getValue()); } -void KonfiguratorColorChooser::setValue(QString value) +void KonfiguratorColorChooser::setValue(const QString& value) { disableColorChooser = true; if (value.isEmpty()) { setCurrentIndex(1); customValue = defaultValue; } else { bool found = false; for (int j = 0; j != additionalColors.size(); j++) if (additionalColors[j].value == value) { setCurrentIndex(2 + j); found = true; break; } if (! found) { KConfigGroup colGroup(krConfig, ext->getConfigGroup()); colGroup.writeEntry("TmpColor", value); QColor color = colGroup.readEntry("TmpColor", defaultValue); customValue = color; colGroup.deleteEntry("TmpColor"); setCurrentIndex(0); for (int i = 2 + additionalColors.size(); i != palette.size(); i++) if (palette[i] == color) { setCurrentIndex(i); break; } } } palette[0] = customValue; setItemIcon(0, createPixmap(customValue)); ext->setChanged(); emit colorChanged(); disableColorChooser = false; } QString KonfiguratorColorChooser::getValue() { QColor color = palette[ currentIndex()]; if (currentIndex() == 1) /* it's the default value? */ return ""; else if (currentIndex() >= 2 && currentIndex() < 2 + additionalColors.size()) return additionalColors[ currentIndex() - 2 ].value; else return QString("%1,%2,%3").arg(color.red()).arg(color.green()).arg(color.blue()); } bool KonfiguratorColorChooser::isValueRGB() { return !(currentIndex() >= 1 && currentIndex() < 2 + additionalColors.size()); } void KonfiguratorColorChooser::slotSetDefaults(QObject *) { ext->setChanged(); setCurrentIndex(1); emit colorChanged(); } void KonfiguratorColorChooser::slotCurrentChanged(int number) { ext->setChanged(); if (number == 0 && !disableColorChooser) { QColor color = QColorDialog::getColor(customValue, this); if (color.isValid()) { disableColorChooser = true; customValue = color; palette[0] = customValue; setItemIcon(0, createPixmap(customValue)); disableColorChooser = false; } } emit colorChanged(); } QColor KonfiguratorColorChooser::getColor() { return palette[ currentIndex()]; } // KonfiguratorListBox class /////////////////////////////// KonfiguratorListBox::KonfiguratorListBox(QString configGroup, QString name, QStringList defaultValue, QWidget *parent, bool restart, int page) : KrListWidget(parent), - defaultValue(defaultValue) + defaultValue(std::move(defaultValue)) { - ext = new KonfiguratorExtension(this, configGroup, name, restart, page); + ext = new KonfiguratorExtension(this, std::move(configGroup), std::move(name), restart, page); connect(ext, &KonfiguratorExtension::applyAuto, this, &KonfiguratorListBox::slotApply); connect(ext, &KonfiguratorExtension::setDefaultsAuto, this, &KonfiguratorListBox::slotSetDefaults); connect(ext, &KonfiguratorExtension::setInitialValue, this, &KonfiguratorListBox::loadInitialValue); loadInitialValue(); } KonfiguratorListBox::~KonfiguratorListBox() { delete ext; } void KonfiguratorListBox::loadInitialValue() { KConfigGroup group(krConfig, ext->getConfigGroup()); setList(group.readEntry(ext->getConfigName(), defaultValue)); ext->setChanged(false); } -void KonfiguratorListBox::slotApply(QObject *, QString configGroup, QString name) +void KonfiguratorListBox::slotApply(QObject *, const QString& configGroup, const QString& name) { KConfigGroup(krConfig, configGroup).writeEntry(name, list()); } void KonfiguratorListBox::slotSetDefaults(QObject *) { if (list() != defaultValue) { ext->setChanged(); setList(defaultValue); } } -void KonfiguratorListBox::setList(QStringList list) +void KonfiguratorListBox::setList(const QStringList& list) { clear(); addItems(list); } QStringList KonfiguratorListBox::list() { QStringList lst; for (int i = 0; i != count(); i++) lst += item(i)->text(); return lst; } void KonfiguratorListBox::addItem(const QString & item) { if (!list().contains(item)) { KrListWidget::addItem(item); ext->setChanged(); } } void KonfiguratorListBox::removeItem(const QString & item) { QList list = findItems(item, Qt::MatchExactly); for (int i = 0; i != list.count(); i++) delete list[ i ]; if (list.count()) ext->setChanged(); } diff --git a/krusader/Konfigurator/konfiguratoritems.h b/krusader/Konfigurator/konfiguratoritems.h index 28c446fb..a1aa3574 100644 --- a/krusader/Konfigurator/konfiguratoritems.h +++ b/krusader/Konfigurator/konfiguratoritems.h @@ -1,427 +1,427 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 KONFIGURATORITEMS_H #define KONFIGURATORITEMS_H // QtCore #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include "../GUI/krlistwidget.h" #define FIRST_PAGE 0 class KonfiguratorExtension : public QObject { Q_OBJECT public: KonfiguratorExtension(QObject *obj, QString cfgGroup, QString cfgName, bool restartNeeded = false, int page = FIRST_PAGE); virtual void loadInitialValue(); virtual bool apply(); virtual void setDefaults(); virtual bool isChanged(); virtual void setSubPage(int page) { subpage = page; } virtual int subPage() { return subpage; } inline QObject *object() { return objectPtr; } inline QString getConfigGroup() { return configGroup; } inline QString getConfigName() { return configName; } public slots: void setChanged() { emit sigChanged(changed = true); } void setChanged(bool chg) { emit sigChanged(changed = chg); } signals: void applyManually(QObject *, QString, QString); void applyAuto(QObject *, QString, QString); void setDefaultsManually(QObject *); void setDefaultsAuto(QObject *); void setInitialValue(QObject *); void sigChanged(bool); protected: QObject *objectPtr; bool applyConnected; bool setDefaultsConnected; bool changed; bool restartNeeded; int subpage; QString configGroup; QString configName; void connectNotify(const QMetaMethod &signal) Q_DECL_OVERRIDE; }; // KonfiguratorCheckBox class /////////////////////////////// class KonfiguratorCheckBox : public QCheckBox { Q_OBJECT public: - KonfiguratorCheckBox(QString configGroup, QString name, bool defaultValue, QString text, + KonfiguratorCheckBox(QString configGroup, QString name, bool defaultValue, const QString& text, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); ~KonfiguratorCheckBox() override; inline KonfiguratorExtension *extension() { return ext; } // indicate that a checkobox is dependent of this, // meaning that dep is only available if this box is checked void addDep(KonfiguratorCheckBox *dep); public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: void checkStateSet() Q_DECL_OVERRIDE; void nextCheckState() Q_DECL_OVERRIDE; void updateDeps(); bool defaultValue; KonfiguratorExtension *ext; QList deps; }; // KonfiguratorSpinBox class /////////////////////////////// class KonfiguratorSpinBox : public QSpinBox { Q_OBJECT public: KonfiguratorSpinBox(QString configGroup, QString configName, int defaultValue, int min, int max, QWidget *parent = nullptr, bool restartNeeded = false, int page = FIRST_PAGE); ~KonfiguratorSpinBox() override; inline KonfiguratorExtension *extension() { return ext; } public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: int defaultValue; KonfiguratorExtension *ext; }; // KonfiguratorCheckBoxGroup class /////////////////////////////// class KonfiguratorCheckBoxGroup : public QWidget { public: explicit KonfiguratorCheckBoxGroup(QWidget *parent = nullptr) : QWidget(parent){} void add(KonfiguratorCheckBox *); int count() { return checkBoxList.count(); }; KonfiguratorCheckBox * find(int index); - KonfiguratorCheckBox * find(QString name); + KonfiguratorCheckBox * find(const QString& name); private: QList checkBoxList; }; // KonfiguratorRadioButtons class /////////////////////////////// class KonfiguratorRadioButtons : public QWidget { Q_OBJECT public: KonfiguratorRadioButtons(QString configGroup, QString name, QString defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); ~KonfiguratorRadioButtons() override; inline KonfiguratorExtension *extension() { return ext; } - void addRadioButton(QRadioButton *radioWidget, QString name, QString value); + void addRadioButton(QRadioButton *radioWidget, const QString& name, const QString& value); - void selectButton(QString value); + void selectButton(const QString& value); int count() { return radioButtons.count(); } QString selectedValue(); QRadioButton *find(int index); - QRadioButton *find(QString name); + QRadioButton *find(const QString& name); public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: QList radioButtons; QList radioValues; QList radioNames; QString defaultValue; KonfiguratorExtension *ext; }; // KonfiguratorEditBox class /////////////////////////////// class KonfiguratorEditBox : public QLineEdit { Q_OBJECT public: KonfiguratorEditBox(QString configGroup, QString name, QString defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); ~KonfiguratorEditBox() override; inline KonfiguratorExtension *extension() { return ext; } public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: QString defaultValue; KonfiguratorExtension *ext; }; // KonfiguratorURLRequester class /////////////////////////////// class KonfiguratorURLRequester : public KUrlRequester { Q_OBJECT public: KonfiguratorURLRequester(QString configGroup, QString name, QString defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE, bool expansion = true); ~KonfiguratorURLRequester() override; inline KonfiguratorExtension *extension() { return ext; } public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: QString defaultValue; KonfiguratorExtension *ext; bool expansion; }; // KonfiguratorFontChooser class /////////////////////////////// class KonfiguratorFontChooser : public QWidget { Q_OBJECT public: - KonfiguratorFontChooser(QString configGroup, QString name, QFont defaultValue, + KonfiguratorFontChooser(QString configGroup, QString name, const QFont& defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); ~KonfiguratorFontChooser() override; inline KonfiguratorExtension *extension() { return ext; } public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); void slotBrowseFont(); protected: QFont defaultValue; QFont font; KonfiguratorExtension *ext; QLabel * pLabel; QToolButton * pToolButton; void setFont(); }; // KONFIGURATOR_NAME_VALUE_PAIR structure /////////////////////////////// struct KONFIGURATOR_NAME_VALUE_PAIR { QString text; QString value; }; // KONFIGURATOR_NAME_VALUE_TIP structure /////////////////////////////// struct KONFIGURATOR_NAME_VALUE_TIP { QString text; QString value; QString tooltip; }; // KonfiguratorComboBox class /////////////////////////////// class KonfiguratorComboBox : public QComboBox { Q_OBJECT public: KonfiguratorComboBox(QString configGroup, QString name, QString defaultValue, KONFIGURATOR_NAME_VALUE_PAIR *listIn, int listInLen, QWidget *parent = nullptr, bool restart = false, bool editable = false, int page = FIRST_PAGE); ~KonfiguratorComboBox() override; inline KonfiguratorExtension *extension() { return ext; } public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: QString defaultValue; KONFIGURATOR_NAME_VALUE_PAIR *list; int listLen; KonfiguratorExtension *ext; - void selectEntry(QString entry); + void selectEntry(const QString& entry); }; // KonfiguratorColorChooser class /////////////////////////////// typedef struct { QString name; QColor color; QString value; } ADDITIONAL_COLOR; class KonfiguratorColorChooser : public QComboBox { Q_OBJECT public: - KonfiguratorColorChooser(QString configGroup, QString name, QColor defaultValue, + KonfiguratorColorChooser(QString configGroup, QString name, const QColor& defaultValue, QWidget *parent = nullptr, bool restart = false, ADDITIONAL_COLOR *addColPtr = nullptr, int addColNum = 0, int page = FIRST_PAGE); ~KonfiguratorColorChooser() override; inline KonfiguratorExtension *extension() { return ext; } void setDefaultColor(QColor dflt); - void setDefaultText(QString text); + void setDefaultText(const QString& text); QColor getColor(); - void changeAdditionalColor(int num, QColor color); + void changeAdditionalColor(int num, const QColor& color); QString getValue(); bool isValueRGB(); - void setValue(QString value); + void setValue(const QString& value); public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); void slotCurrentChanged(int number); signals: void colorChanged(); private: - void addColor(QString text, QColor color); - QPixmap createPixmap(QColor color); + void addColor(const QString& text, const QColor& color); + QPixmap createPixmap(const QColor& color); protected: QColor defaultValue; QColor customValue; QList palette; QList additionalColors; KonfiguratorExtension *ext; bool disableColorChooser; }; // KonfiguratorListBox class /////////////////////////////// class KonfiguratorListBox : public KrListWidget { Q_OBJECT public: KonfiguratorListBox(QString configGroup, QString name, QStringList defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); ~KonfiguratorListBox() override; inline KonfiguratorExtension *extension() { return ext; } void addItem(const QString &); void removeItem(const QString &); public slots: virtual void loadInitialValue(); - void slotApply(QObject *, QString, QString); + void slotApply(QObject *, const QString&, const QString&); void slotSetDefaults(QObject *); protected: QStringList list(); - void setList(QStringList); + void setList(const QStringList&); QStringList defaultValue; KonfiguratorExtension *ext; }; #endif /* __KONFIGURATOR_ITEMS_H__ */ diff --git a/krusader/Konfigurator/konfiguratorpage.cpp b/krusader/Konfigurator/konfiguratorpage.cpp index 5c191270..740233dd 100644 --- a/krusader/Konfigurator/konfiguratorpage.cpp +++ b/krusader/Konfigurator/konfiguratorpage.cpp @@ -1,284 +1,285 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "konfiguratorpage.h" // QtWidgets #include #include #include #include #include #include +#include #include "../krglobal.h" KonfiguratorPage::KonfiguratorPage(bool firstTime, QWidget* parent) : QScrollArea(parent), firstCall(firstTime) { setFrameStyle(QFrame::NoFrame); } bool KonfiguratorPage::apply() { bool restartNeeded = false; for (auto & item : itemList) restartNeeded = item->apply() || restartNeeded; krConfig->sync(); return restartNeeded; } void KonfiguratorPage::setDefaults() { int activePage = activeSubPage(); for (auto & item : itemList) { if (item->subPage() == activePage) item->setDefaults(); } } void KonfiguratorPage::loadInitialValues() { for (auto & item : itemList) item->loadInitialValue(); } bool KonfiguratorPage::isChanged() { bool isChanged = false; for (auto & item : itemList) isChanged = isChanged || item->isChanged(); return isChanged; } KonfiguratorCheckBox* KonfiguratorPage::createCheckBox(QString configGroup, QString name, - bool defaultValue, QString text, QWidget *parent, bool restart, QString toolTip, int page) + bool defaultValue, QString text, QWidget *parent, bool restart, const QString& toolTip, int page) { - KonfiguratorCheckBox *checkBox = new KonfiguratorCheckBox(configGroup, name, defaultValue, text, + KonfiguratorCheckBox *checkBox = new KonfiguratorCheckBox(std::move(configGroup), std::move(name), defaultValue, std::move(text), parent, restart, page); if (!toolTip.isEmpty()) checkBox->setWhatsThis(toolTip); registerObject(checkBox->extension()); return checkBox; } KonfiguratorSpinBox* KonfiguratorPage::createSpinBox(QString configGroup, QString configName, int defaultValue, int min, int max, QWidget *parent, bool restart, int page) { - KonfiguratorSpinBox *spinBox = new KonfiguratorSpinBox(configGroup, configName, defaultValue, + KonfiguratorSpinBox *spinBox = new KonfiguratorSpinBox(std::move(configGroup), std::move(configName), defaultValue, min, max, parent, restart, page); registerObject(spinBox->extension()); return spinBox; } KonfiguratorEditBox* KonfiguratorPage::createEditBox(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page) { - KonfiguratorEditBox *editBox = new KonfiguratorEditBox(configGroup, name, defaultValue, parent, + KonfiguratorEditBox *editBox = new KonfiguratorEditBox(std::move(configGroup), std::move(name), std::move(defaultValue), parent, restart, page); registerObject(editBox->extension()); return editBox; } KonfiguratorListBox* KonfiguratorPage::createListBox(QString configGroup, QString name, QStringList defaultValue, QWidget *parent, bool restart, int page) { - KonfiguratorListBox *listBox = new KonfiguratorListBox(configGroup, name, defaultValue, parent, + KonfiguratorListBox *listBox = new KonfiguratorListBox(std::move(configGroup), std::move(name), std::move(defaultValue), parent, restart, page); registerObject(listBox->extension()); return listBox; } KonfiguratorURLRequester* KonfiguratorPage::createURLRequester(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page, bool expansion) { - KonfiguratorURLRequester *urlRequester = new KonfiguratorURLRequester(configGroup, name, defaultValue, + KonfiguratorURLRequester *urlRequester = new KonfiguratorURLRequester(std::move(configGroup), std::move(name), std::move(defaultValue), parent, restart, page, expansion); registerObject(urlRequester->extension()); return urlRequester; } -QGroupBox* KonfiguratorPage::createFrame(QString text, QWidget *parent) +QGroupBox* KonfiguratorPage::createFrame(const QString& text, QWidget *parent) { auto *groupBox = new QGroupBox(parent); if (!text.isNull()) groupBox->setTitle(text); return groupBox; } QGridLayout* KonfiguratorPage::createGridLayout(QWidget *parent) { auto *gridLayout = new QGridLayout(parent); gridLayout->setAlignment(Qt::AlignTop); gridLayout->setSpacing(6); gridLayout->setContentsMargins(11, 11, 11, 11); return gridLayout; } -QLabel* KonfiguratorPage::addLabel(QGridLayout *layout, int x, int y, QString label, +QLabel* KonfiguratorPage::addLabel(QGridLayout *layout, int x, int y, const QString& label, QWidget *parent) { QLabel *lbl = new QLabel(label, parent); layout->addWidget(lbl, x, y); return lbl; } QWidget* KonfiguratorPage::createSpacer(QWidget *parent) { QWidget *widget = new QWidget(parent); auto *hboxlayout = new QHBoxLayout(widget); auto* spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); hboxlayout->addItem(spacer); return widget; } KonfiguratorCheckBoxGroup* KonfiguratorPage::createCheckBoxGroup(int sizex, int sizey, KONFIGURATOR_CHECKBOX_PARAM *params, int paramNum, QWidget *parent, int page) { auto *groupWidget = new KonfiguratorCheckBoxGroup(parent); auto *layout = new QGridLayout(groupWidget); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); int x = 0, y = 0; for (int i = 0; i != paramNum; i++) { KonfiguratorCheckBox *checkBox = createCheckBox(params[i].configClass, params[i].configName, params[i].defaultValue, params[i].text, groupWidget, params[i].restart, params[i].toolTip, page); groupWidget->add(checkBox); layout->addWidget(checkBox, y, x); if (sizex) { if (++x == sizex) x = 0, y++; } else { if (++y == sizey) y = 0, x++; } } return groupWidget; } KonfiguratorRadioButtons* KonfiguratorPage::createRadioButtonGroup(QString configGroup, QString name, QString defaultValue, int sizex, int sizey, KONFIGURATOR_NAME_VALUE_TIP *params, int paramNum, QWidget *parent, bool restart, int page) { - KonfiguratorRadioButtons *radioWidget = new KonfiguratorRadioButtons(configGroup, name, defaultValue, parent, restart, page); + KonfiguratorRadioButtons *radioWidget = new KonfiguratorRadioButtons(std::move(configGroup), std::move(name), std::move(defaultValue), parent, restart, page); auto *layout = new QGridLayout(radioWidget); layout->setAlignment(Qt::AlignTop); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); int x = 0, y = 0; for (int i = 0; i != paramNum; i++) { auto *radBtn = new QRadioButton(params[i].text, radioWidget); if (!params[i].tooltip.isEmpty()) radBtn->setWhatsThis(params[i].tooltip); layout->addWidget(radBtn, y, x); radioWidget->addRadioButton(radBtn, params[i].text, params[i].value); if (sizex) { if (++x == sizex) x = 0, y++; } else { if (++y == sizey) y = 0, x++; } } radioWidget->loadInitialValue(); registerObject(radioWidget->extension()); return radioWidget; } KonfiguratorFontChooser *KonfiguratorPage::createFontChooser(QString configGroup, QString name, - QFont defaultValue, QWidget *parent, bool restart, int page) + const QFont& defaultValue, QWidget *parent, bool restart, int page) { - KonfiguratorFontChooser *fontChooser = new KonfiguratorFontChooser(configGroup, name, defaultValue, parent, + KonfiguratorFontChooser *fontChooser = new KonfiguratorFontChooser(std::move(configGroup), std::move(name), defaultValue, parent, restart, page); registerObject(fontChooser->extension()); return fontChooser; } KonfiguratorComboBox *KonfiguratorPage::createComboBox(QString configGroup, QString name, QString defaultValue, KONFIGURATOR_NAME_VALUE_PAIR *params, int paramNum, QWidget *parent, bool restart, bool editable, int page) { - KonfiguratorComboBox *comboBox = new KonfiguratorComboBox(configGroup, name, defaultValue, params, + KonfiguratorComboBox *comboBox = new KonfiguratorComboBox(std::move(configGroup), std::move(name), std::move(defaultValue), params, paramNum, parent, restart, editable, page); registerObject(comboBox->extension()); return comboBox; } QFrame* KonfiguratorPage::createLine(QWidget *parent, bool vertical) { QFrame *line = new QFrame(parent); line->setFrameStyle((vertical ? QFrame::VLine : QFrame::HLine) | QFrame::Sunken); return line; } void KonfiguratorPage::registerObject(KonfiguratorExtension *item) { itemList.push_back(item); connect(item, SIGNAL(sigChanged(bool)), this, SIGNAL(sigChanged())); } void KonfiguratorPage::removeObject(KonfiguratorExtension *item) { int ndx = itemList.indexOf(item); if (ndx != -1) { QList::iterator it = itemList.begin() + ndx; delete *it; itemList.erase(it); } } KonfiguratorColorChooser *KonfiguratorPage::createColorChooser(QString configGroup, QString name, QColor defaultValue, QWidget *parent, bool restart, ADDITIONAL_COLOR *addColPtr, int addColNum, int page) { - KonfiguratorColorChooser *colorChooser = new KonfiguratorColorChooser(configGroup, name, defaultValue, parent, + KonfiguratorColorChooser *colorChooser = new KonfiguratorColorChooser(std::move(configGroup), std::move(name), std::move(defaultValue), parent, restart, addColPtr, addColNum, page); registerObject(colorChooser->extension()); return colorChooser; } diff --git a/krusader/Konfigurator/konfiguratorpage.h b/krusader/Konfigurator/konfiguratorpage.h index 1a27497e..3ec2889e 100644 --- a/krusader/Konfigurator/konfiguratorpage.h +++ b/krusader/Konfigurator/konfiguratorpage.h @@ -1,513 +1,513 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 KONFIGURATORPAGE_H #define KONFIGURATORPAGE_H // QtWidgets #include #include #include #include #include #include "konfiguratoritems.h" struct KONFIGURATOR_CHECKBOX_PARAM; /** * KonfiguratorPage is responsible for handling pages in Konfigurator. * It provides simple methods for create and manage Konfigurator pages. * * @short The base class of a page in Konfigurator */ class KonfiguratorPage : public QScrollArea { Q_OBJECT public: /** * The constructor of the KonfiguratorPage class. * * @param firstTime this parameter is true if it is the first call of Konfigurator * @param parent reference to the parent widget */ KonfiguratorPage(bool firstTime, QWidget *parent); /** * Applies the changes in the Konfigurator page. * * Writes out all relevant information to the configuration object and synchronizes * it with the file storage (hard disk, krusaderrc file). This function calls the apply() * method of each konfigurator item and finally performs the synchronization. * * @return a boolean value indicates that Krusader restart is needed for the correct change */ virtual bool apply(); /** * Sets every konfigurator item to its default value on the page. * * This method calls the setDefaults() method of each konfigurator item. This function * doesn't modify the current configuration, only the values of the GUI items. The * apply() method must be called for finalizing the changes. */ virtual void setDefaults(); /** * Reloads the original value of each konfigurator item from the configuration object. * * This function calls the loadInitialValue() method of each konfigurator item. * Used to rollback the changes on the konfigurator page. Called if the user * responds 'No' to the "Apply changes" question. */ virtual void loadInitialValues(); /** * Checks whether the page was changed. * * This function calls the isChanged() method of each konfigurator item and * performs logical OR operation on them. Actually, this function returns true * if any of the konfigurator items was changed. * * @return true if at least one of the konfigurator items was changed */ virtual bool isChanged(); /** * Flag, indicates the first call of Konfigurator * @return true if konfigurator was started at the first time */ inline bool isFirst() { return firstCall; } /** * This method is used to query the active subpage from the Konfigurator * @return the active page (by default the first page) */ virtual int activeSubPage() { return FIRST_PAGE; } /** * Adds a new checkbox item to the page. *
The checkbox widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorCheckBox *myCheckBox = createCheckBox( "class", "name", false, parentWidget);
* myLayout->addWidget( myCheckBox, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the checkbox * @param text The text field of the checkbox * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param toolTip Tooltip used for this checkbox * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created checkbox */ KonfiguratorCheckBox *createCheckBox(QString configGroup, QString name, bool defaultValue, QString text, QWidget *parent = nullptr, bool restart = false, - QString toolTip = QString(), int page = FIRST_PAGE); + const QString& toolTip = QString(), int page = FIRST_PAGE); /** * Adds a new spinbox item to the page. *
The spinbox widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorSpinBox *mySpinBox = createSpinBox( "class", "name", 10, 1, 100, parentWidget);
* myLayout->addWidget( mySpinBox, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param configName The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the spinbox * @param min The minimum value of the spinbox * @param max The maximum value of the spinbox * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created spinbox */ KonfiguratorSpinBox *createSpinBox(QString configGroup, QString configName, int defaultValue, int min, int max, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); /** * Adds a new editbox item to the page. *
The editbox widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorEditBox *myEditBox = createEditBox( "class", "name", "default", parentWidget);
* myLayout->addWidget( myEditBox, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The itemname used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the editbox * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created editbox */ KonfiguratorEditBox *createEditBox(QString configGroup, QString name, QString defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); /** * Adds a new listbox item to the page. *
The listbox widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* QStringList valueList;
* valueList += "item";
* KonfiguratorListBox *myListBox = createListBox( "class", "name", valueList, parentWidget);
* myLayout->addWidget( myListBox, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The itemname used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the listbox * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created editbox */ KonfiguratorListBox *createListBox(QString configGroup, QString name, QStringList defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); /** * Adds a new URL requester item to the page. *
The URL requester widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorURLRequester *myURLRequester = createURLRequester( "class", "name", "default", parentWidget );
* myLayout->addWidget( myURLRequester, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The itemname used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the URL requester * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * @param expansion Whether to perform url expansion * * @return reference to the newly created URL requester */ KonfiguratorURLRequester *createURLRequester(QString configGroup, QString name, QString defaultValue, QWidget *parent, bool restart, int page = FIRST_PAGE, bool expansion = true); /** * Adds a new font chooser item to the page. *
The font chooser widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorFontChooser *myFontChooser = createFontChooser( "class", "name", QFont(), parentWidget );
* myLayout->addWidget( myFontChooser, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the font chooser * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created font chooser */ KonfiguratorFontChooser *createFontChooser(QString configGroup, QString name, - QFont defaultValue, QWidget *parent = nullptr, + const QFont& defaultValue, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); /** * Adds a new combobox item to the page. *
The combobox widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KONFIGURATOR_NAME_VALUE_PAIR comboInfo[] =
*  {{ i18n( "combo text1" ), "value1" },
*   { i18n( "combo text2" ), "value2" },
*   { i18n( "combo text3" ), "value3" }};

* KonfiguratorComboBox *myComboBox = createComboBox( "class", "name", "value2", comboInfo, 3, parentWidget );
* myLayout->addWidget( myComboBox, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the combobox * @param params Pointer to the name-value pair array (combo elements) * @param paramNum Number of the combobox elements * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param editable Flag indicates that the combo can be edited * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created combobox */ KonfiguratorComboBox *createComboBox(QString configGroup, QString name, QString defaultValue, KONFIGURATOR_NAME_VALUE_PAIR *params, int paramNum, QWidget *parent = nullptr, bool restart = false, bool editable = false, int page = FIRST_PAGE); /** * Creates a frame on the page. * * Sample:

* QGroupBox *myGroup = createFrame( i18n( "MyFrameName" ), parentWidget, "frameName" );
* myLayout->addWidget( myGroup, 0, 0 ); * * @param text The text written out onto the frame * @param parent Reference to the parent widget * * @return reference to the newly created frame */ - QGroupBox *createFrame(QString text = QString(), QWidget *parent = nullptr); + QGroupBox *createFrame(const QString& text = QString(), QWidget *parent = nullptr); /** * Creates a new QGridLayout element and sets its margins. * * Sample:

* QGroupBox *myGroup = createFrame( i18n( "MyFrameName" ), parentWidget, "frameName" );
* QGridLayout *myLayout = createGridLayout( myGroup ) );
* myLayout->addWidget( myGroup, 0, 0 ); * * @param parent Reference to the parent layout * * @return reference to the newly created QGridLayout */ QGridLayout *createGridLayout(QWidget *parent); /** * Adds a new label to a grid layout. * * Sample:

* QGroupBox *myGroup = createFrame( i18n( "MyFrameName" ), parentWidget, "frameName" );
* QGridLayout *myLayout = createGridLayout( myGroup->layout() );
* addLabel( myLayout, 0, 0, i18n( "Hello world!" ), myGroup, "myLabel" );
* mainLayout->addWidget( myGroup, 0, 0 ); * * @param layout The grid layout on which the item will be placed * @param x the column to which the label will be placed * @param y the row to which the label will be placed * @param label the text of the label * @param parent Reference to the parent widget * * @return reference to the newly created label */ - QLabel *addLabel(QGridLayout *layout, int x, int y, QString label, QWidget *parent = nullptr); + QLabel *addLabel(QGridLayout *layout, int x, int y, const QString& label, QWidget *parent = nullptr); /** * Creates a spacer object (for justifying in QHBox). * * Sample:

* QHBox *hbox = new QHBox( myParent, "hbox" );
* createSpinBox( "class", "spin", 5, 1, 10, hbox );
* createSpacer( hbox );
* myLayout->addWidget( hbox, 0, 0 ); * * @param parent Reference to the parent widget * * @return reference to the newly created spacer widget */ QWidget *createSpacer(QWidget *parent = nullptr); /** * Creates a separator line. * * Sample:

* QFrame *myLine = createLine( myParent, "myLine" );
* myLayout->addWidget( myLine, 1, 0 );
* * @param parent Reference to the parent widget * @param vertical Means vertical line * * @return reference to the newly created spacer widget */ QFrame *createLine(QWidget *parent = nullptr, bool vertical = false); /** * Creates a checkbox group. A checkbox group contains a lot of checkboxes. * The grouped checkboxes are embedded into one widget, which can be placed anywhere * on the GUI. The placing of the elements can be horizontal or vertical in the group. * At horizontal placing the sizex integer defines the maximum element number in * one row, sizey is 0. At vertical placing sizex is 0, and sizey defines the * maximum row number in one column.
* * One specific element can be reached by its name or index with the find methods. * The first element is checkBoxGroup->find( 0 ), "myCb" element is checkBoxGroup->find( "myCb") ... * * Sample:

* KONFIGURATOR_CHECKBOX_PARAM myCBArray[] =
*  {{"CbClass","CbName1", false, i18n( "name1" ), false, "tooltip1"},
*   {"CbClass","CbName2", true, i18n( "name2" ), false, "tooltip2"},
*   {"CbClass","CbName3", true, i18n( "name3" ), false, "tooltip3"}};

* KonfiguratorCheckBoxGroup *myCheckBoxGroup = createCheckBoxGroup( 1, 0, myCBArray, 3, myParent, "myCheckboxGroup" );
* myCheckBoxGroup->find( 0 )->setEnabled( false );

* myLayout->addWidget( myCheckBoxGroup, 0, 0 );
* * @param sizex the maximum column number at horizontal placing * @param sizey the maximum row number at vertical placing * @param params pointer to the checkbox array * @param paramNum number of the checkbox elements * @param parent Reference to the parent widget * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created checkbox group widget */ KonfiguratorCheckBoxGroup *createCheckBoxGroup(int sizex, int sizey, KONFIGURATOR_CHECKBOX_PARAM *params, int paramNum, QWidget *parent = nullptr, int page = FIRST_PAGE); /** * Creates a radio button group. A radio button group contains a lot of radio buttons. * The grouped buttons are embedded into one widget, which can be placed anywhere * on the GUI. The placing of the elements can be horizontal or vertical in the group. * At horizontal placing the sizex integer defines the maximum element number in * one row, sizey is 0. At vertical placing sizex is 0, and sizey defines the * maximum row number in one column.
* * The references of the buttons can be accessed by the find methods of KonfiguratorRadioButtons. * The first element is myRadioGrp->find( 0 ), "myRadio" element is myRadioGrp->find( "myRadio") ... * * Sample:

* KONFIGURATOR_NAME_VALUE_TIP radioInfo[] =
*  {{ i18n( "radio text1" ), "value1", i18n( "tooltip1" ) },
*   { i18n( "radio text2" ), "value2", i18n( "tooltip2" ) },
*   { i18n( "radio text3" ), "value3", i18n( "tooltip3" ) }};

* KonfiguratorRadioButtons *myRadioGroup = createRadioButtonGroup( "class", "name", "value1", * 1, 0, radioInfo, 3, myParent, "myRadioGroup" );
* myRadioGroup->find( i18n( "radio text1" ) )->setEnabled( false );
* myLayout->addWidget( myRadioGroup, 0, 0 );
* * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the radio buttons * @param sizex the maximum column number at horizontal placing * @param sizey the maximum row number at vertical placing * @param params pointer to the checkbox array * @param paramNum number of the checkbox elements * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created radio button group widget */ KonfiguratorRadioButtons *createRadioButtonGroup(QString configGroup, QString name, QString defaultValue, int sizex, int sizey, KONFIGURATOR_NAME_VALUE_TIP *params, int paramNum, QWidget *parent = nullptr, bool restart = false, int page = FIRST_PAGE); /** * This function is used to insert new, unknown items into KonfiguratorPage. The * item must be derived from KonfiguratorExtension class, which have * isChanged(), apply(), setDefaults, loadInitialValue() methods. After that, the * object is properly handled by Konfigurator page. * * * @param item The item to be added to KonfiguratorPage */ void registerObject(KonfiguratorExtension *item); /** * This function is used to remove elements from KonfiguratorPage. * * Sample:

* KonfiguratorEditBox *myEditBox = createEditBox( "class", "name", "default", parentWidget);
* myLayout->addWidget( myEditBox, 0, 0 );
* removeObject( myEditBox->extension() ); * * After the removeObject myEditBox will be untouched at apply(), setDefaults(), isChanged(), * loadInitialValues() methods of the KonfiguratorPage. * * @param item The item to be removed from KonfiguratorPage */ void removeObject(KonfiguratorExtension *item); /** * Adds a new color chooser combobox item to the page. *
The chooser's widget's name is QString(configGroup + "/" + name).ascii()
* * Sample:

* KonfiguratorColorChooser *myColorChooser = createColorChooser( "class", "name", QColor( 255, 0, 255 ), parentWidget );
* myLayout->addWidget( myColorChooser, 0, 0 ); * * @param configGroup The class name used in KConfig (ex. "Archives") * @param name The item name used in KConfig (ex. "Do Tar") * @param defaultValue The default value of the color chooser * @param parent Reference to the parent widget * @param restart The change of this parameter requires Krusader restart * @param addColPtr The additional color values * @param addColNum Number of additional colors * @param page The subpage of a Konfigurator page (because of setDefaults) * * @return reference to the newly created combobox */ KonfiguratorColorChooser *createColorChooser(QString configGroup, QString name, QColor defaultValue, QWidget *parent = nullptr, bool restart = false, ADDITIONAL_COLOR *addColPtr = nullptr, int addColNum = 0, int page = FIRST_PAGE); signals: /** * The signal is emitted if the changed flag was modified in any konfigurator item. * Used for enabling/disabling the apply button. */ void sigChanged(); protected: QList itemList; private: bool firstCall; }; /** * KONFIGURATOR_CHECKBOX_PARAM is the basic item of checkbox arrays. It contains * every information related to a checkbox. */ struct KONFIGURATOR_CHECKBOX_PARAM { /** * The class used in KConfig (ex. "Archives") */ QString configClass; /** * The item name used in KConfig (ex. "Do Tar") */ QString configName; /** * The default value of the checkbox */ bool defaultValue; /** * The text field of the checkbox */ QString text; /** * The change of this parameter requires Krusader restart */ bool restart; /** * The checkbox's tooltip */ QString toolTip; }; #endif /* __KONFIGURATOR_PAGE_H__ */ diff --git a/krusader/MountMan/kmountman.cpp b/krusader/MountMan/kmountman.cpp index f9cbadc0..888d3722 100644 --- a/krusader/MountMan/kmountman.cpp +++ b/krusader/MountMan/kmountman.cpp @@ -1,464 +1,465 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "kmountman.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "../krglobal.h" #include "../icon.h" #include "../kractions.h" #include "../defaults.h" #include "../Dialogs/krdialogs.h" #include "../krservices.h" #include "kmountmangui.h" #include "../FileSystem/krpermhandler.h" #ifdef _OS_SOLARIS_ #define FSTAB "/etc/filesystemtab" #else #define FSTAB "/etc/fstab" #endif KMountMan::KMountMan(QWidget *parent) : QObject(), _operational(false), waiting(false), mountManGui(nullptr), parentWindow(parent) { _action = new KToolBarPopupAction(Icon("kr_mountman"), i18n("&MountMan..."), this); connect(_action, &QAction::triggered, this, &KMountMan::mainWindow); connect(_action->menu(), &QMenu::aboutToShow, this, &KMountMan::quickList); _manageAction = _action->menu()->addAction(i18n("Open &MountMan")); connect(_manageAction, &QAction::triggered, this, &KMountMan::mainWindow); _action->menu()->addSeparator(); // added as a precaution, although we use kde services now _operational = KrServices::cmdExist("mount"); network_fs << "nfs" << "smbfs" << "fuse.fusesmb" << "fuse.sshfs"; //TODO: is this list complete ? // list of FS that we don't manage at all invalid_fs << "swap" << "/dev/pts" << "tmpfs" << "devpts" << "sysfs" << "rpc_pipefs" << "usbfs" << "binfmt_misc"; #ifdef BSD invalid_fs << "procfs"; #else invalid_fs << "proc"; #endif // list of FS that we don't allow to mount/unmount nonmount_fs << "supermount"; { KConfigGroup group(krConfig, "Advanced"); QStringList nonmount = group.readEntry("Nonmount Points", _NonMountPoints).split(','); nonmount_fs_mntpoint += nonmount; // simplify the white space for (auto & it : nonmount_fs_mntpoint) { it = it.simplified(); } } } KMountMan::~KMountMan() = default; -bool KMountMan::invalidFilesystem(QString type) +bool KMountMan::invalidFilesystem(const QString& type) { return (invalid_fs.contains(type) > 0); } // this is an ugly hack, but type can actually be a mountpoint. oh well... -bool KMountMan::nonmountFilesystem(QString type, QString mntPoint) +bool KMountMan::nonmountFilesystem(const QString& type, const QString& mntPoint) { return((nonmount_fs.contains(type) > 0) || (nonmount_fs_mntpoint.contains(mntPoint) > 0)); } -bool KMountMan::networkFilesystem(QString type) +bool KMountMan::networkFilesystem(const QString& type) { return (network_fs.contains(type) > 0); } void KMountMan::mainWindow() { // left as a precaution, although we use kde's services now if (!KrServices::cmdExist("mount")) { KMessageBox::error(nullptr, i18n("Cannot start 'mount'. Check the 'Dependencies' page in konfigurator.")); return; } mountManGui = new KMountManGUI(this); delete mountManGui; /* as KMountManGUI is modal, we can now delete it */ mountManGui = nullptr; /* for sanity */ } QExplicitlySharedDataPointer KMountMan::findInListByMntPoint(KMountPoint::List &lst, QString value) { if (value.length() > 1 && value.endsWith('/')) value = value.left(value.length() - 1); QExplicitlySharedDataPointer m; for (auto & it : lst) { m = it.data(); QString mntPnt = m->mountPoint(); if (mntPnt.length() > 1 && mntPnt.endsWith('/')) mntPnt = mntPnt.left(mntPnt.length() - 1); if (mntPnt == value) return m; } return QExplicitlySharedDataPointer(); } void KMountMan::jobResult(KJob *job) { waiting = false; if (job->error()) job->uiDelegate()->showErrorMessage(); } -void KMountMan::mount(QString mntPoint, bool blocking) +void KMountMan::mount(const QString& mntPoint, bool blocking) { QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::StorageAccess); if (!udi.isNull()) { Solid::Device device(udi); auto *access = device.as(); if (access && !access->isAccessible()) { connect(access, &Solid::StorageAccess::setupDone, this, &KMountMan::slotSetupDone, Qt::UniqueConnection); if (blocking) waiting = true; // prepare to block access->setup(); } } else { KMountPoint::List possible = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions); QExplicitlySharedDataPointer m = findInListByMntPoint(possible, mntPoint); if (!((bool)m)) return; if (blocking) waiting = true; // prepare to block // KDE4 doesn't allow mounting devices as user, because they think it's the right behaviour. // I add this patch, as I don't think so. if (geteuid()) { // tries to mount as an user? KProcess proc; proc << KrServices::fullPathName("mount") << mntPoint; proc.start(); if (!blocking) return; proc.waitForFinished(-1); // -1 msec blocks without timeout if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0) return; } KIO::SimpleJob *job = KIO::mount(false, m->mountType().toLocal8Bit(), m->mountedFrom(), m->mountPoint(), KIO::DefaultFlags); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); connect(job, &KIO::SimpleJob::result, this, &KMountMan::jobResult); } while (blocking && waiting) { qApp->processEvents(); usleep(1000); } } -void KMountMan::unmount(QString mntPoint, bool blocking) +void KMountMan::unmount(const QString& mntPoint, bool blocking) { //if working dir is below mountpoint cd to ~ first if(QUrl::fromLocalFile(QDir(mntPoint).canonicalPath()).isParentOf(QUrl::fromLocalFile(QDir::current().canonicalPath()))) QDir::setCurrent(QDir::homePath()); QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::StorageAccess); if (!udi.isNull()) { Solid::Device device(udi); auto *access = device.as(); if (access && access->isAccessible()) { connect(access, &Solid::StorageAccess::teardownDone, this, &KMountMan::slotTeardownDone, Qt::UniqueConnection); access->teardown(); } } else { if (blocking) waiting = true; // prepare to block // KDE4 doesn't allow unmounting devices as user, because they think it's the right behaviour. // I add this patch, as I don't think so. if (geteuid()) { // tries to mount as an user? KProcess proc; proc << KrServices::fullPathName("umount") << mntPoint; proc.start(); if (!blocking) return; proc.waitForFinished(-1); // -1 msec blocks without timeout if (proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0) return; } KIO::SimpleJob *job = KIO::unmount(mntPoint, KIO::DefaultFlags); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); connect(job, &KIO::SimpleJob::result, this, &KMountMan::jobResult); } while (blocking && waiting) { qApp->processEvents(); usleep(1000); } } -KMountMan::mntStatus KMountMan::getStatus(QString mntPoint) +KMountMan::mntStatus KMountMan::getStatus(const QString& mntPoint) { QExplicitlySharedDataPointer mountPoint; // 1: is it already mounted KMountPoint::List current = KMountPoint::currentMountPoints(); mountPoint = findInListByMntPoint(current, mntPoint); if ((bool) mountPoint) return MOUNTED; // 2: is it a mount point but not mounted? KMountPoint::List possible = KMountPoint::possibleMountPoints(); mountPoint = findInListByMntPoint(possible, mntPoint); if ((bool) mountPoint) return NOT_MOUNTED; // 3: unknown return DOESNT_EXIST; } -void KMountMan::toggleMount(QString mntPoint) +void KMountMan::toggleMount(const QString& mntPoint) { mntStatus status = getStatus(mntPoint); switch (status) { case MOUNTED: unmount(mntPoint); break; case NOT_MOUNTED: mount(mntPoint); break; case DOESNT_EXIST: // do nothing: no-op to make the compiler quiet ;-) break; } } -void KMountMan::autoMount(QString path) +void KMountMan::autoMount(const QString& path) { KConfigGroup group(krConfig, "Advanced"); if (!group.readEntry("AutoMount", _AutoMount)) return; // auto mount disabled if (getStatus(path) == NOT_MOUNTED) mount(path); } -void KMountMan::eject(QString mntPoint) +void KMountMan::eject(const QString& mntPoint) { QString udi = findUdiForPath(mntPoint, Solid::DeviceInterface::OpticalDrive); if (udi.isNull()) return; Solid::Device dev(udi); auto *drive = dev.as(); if (drive == nullptr) return; //if working dir is below mountpoint cd to ~ first if(QUrl::fromLocalFile(QDir(mntPoint).canonicalPath()).isParentOf(QUrl::fromLocalFile(QDir::current().canonicalPath()))) QDir::setCurrent(QDir::homePath()); connect(drive, &Solid::OpticalDrive::ejectDone, this, &KMountMan::slotTeardownDone); drive->eject(); } // returns true if the path is an ejectable mount point (at the moment CDROM and DVD) bool KMountMan::ejectable(QString path) { - QString udi = findUdiForPath(path, Solid::DeviceInterface::OpticalDisc); + QString udi = findUdiForPath(std::move(path), Solid::DeviceInterface::OpticalDisc); if (udi.isNull()) return false; Solid::Device dev(udi); return dev.as() != nullptr; } bool KMountMan::removable(QString path) { - QString udi = findUdiForPath(path, Solid::DeviceInterface::StorageAccess); + QString udi = findUdiForPath(std::move(path), Solid::DeviceInterface::StorageAccess); if (udi.isNull()) return false; return removable(Solid::Device(udi)); } bool KMountMan::removable(Solid::Device d) { if(!d.isValid()) return false; auto *drive = d.as(); if(drive) return drive->isRemovable(); else return(removable(d.parent())); } // populate the pop-up menu of the mountman tool-button with actions void KMountMan::quickList() { if (!_operational) { KMessageBox::error(nullptr, i18n("MountMan is not operational. Sorry")); return; } // clear mount / unmount actions for (QAction *action : _action->menu()->actions()) { if (action == _manageAction || action->isSeparator()) { continue; } _action->menu()->removeAction(action); } // create lists of current and possible mount points const KMountPoint::List currentMountPoints = KMountPoint::currentMountPoints(); // create a menu, displaying mountpoints with possible actions for (QExplicitlySharedDataPointer possibleMountPoint : KMountPoint::possibleMountPoints()) { // skip nonmountable file systems if (nonmountFilesystem(possibleMountPoint->mountType(), possibleMountPoint->mountPoint()) || invalidFilesystem(possibleMountPoint->mountType())) { continue; } // does the mountpoint exist in current list? // if so, it can only be umounted, otherwise, it can be mounted bool needUmount = false; for (QExplicitlySharedDataPointer currentMountPoint : currentMountPoints) { if (currentMountPoint->mountPoint() == possibleMountPoint->mountPoint()) { // found -> needs umount needUmount = true; break; } } // add the item to the menu const QString text = QString("%1 %2 (%3)").arg(needUmount ? i18n("Unmount") : i18n("Mount"), possibleMountPoint->mountPoint(), possibleMountPoint->mountedFrom()); QAction *act = _action->menu()->addAction(text); act->setData(QList({ QVariant(needUmount ? KMountMan::ActionType::Unmount : KMountMan::ActionType::Mount), QVariant(possibleMountPoint->mountPoint()) })); } connect(_action->menu(), &QMenu::triggered, this, &KMountMan::delayedPerformAction); } void KMountMan::delayedPerformAction(const QAction *action) { if (!action || !action->data().canConvert>()) { return; } disconnect(_action->menu(), &QMenu::triggered, nullptr, nullptr); const QList actData = action->data().toList(); const int actionType = actData[0].toInt(); const QString mountPoint = actData[1].toString(); QTimer::singleShot(0, this, [=] { if (actionType == KMountMan::ActionType::Mount) { mount(mountPoint); } else { unmount(mountPoint); } }); } -QString KMountMan::findUdiForPath(QString path, const Solid::DeviceInterface::Type &expType) +QString KMountMan::findUdiForPath(const QString& path, const Solid::DeviceInterface::Type &expType) { KMountPoint::List current = KMountPoint::currentMountPoints(); KMountPoint::List possible = KMountPoint::possibleMountPoints(); QExplicitlySharedDataPointer mp = findInListByMntPoint(current, path); if (!(bool)mp) { mp = findInListByMntPoint(possible, path); if (!(bool)mp) return QString(); } QString dev = QDir(mp->mountedFrom()).canonicalPath(); QList storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::Block); for (int p = storageDevices.count() - 1 ; p >= 0; p--) { Solid::Device device = storageDevices[ p ]; QString udi = device.udi(); auto * sb = device.as(); if (sb) { QString devb = QDir(sb->device()).canonicalPath(); if (expType != Solid::DeviceInterface::Unknown && !device.isDeviceInterface(expType)) continue; if (devb == dev) return udi; } } return QString(); } -QString KMountMan::pathForUdi(QString udi) +QString KMountMan::pathForUdi(const QString& udi) { Solid::Device device(udi); auto *access = device.as(); if(access) return access->filePath(); else return QString(); } -void KMountMan::slotTeardownDone(Solid::ErrorType error, QVariant errorData, const QString& /*udi*/) +void KMountMan::slotTeardownDone(Solid::ErrorType error, const QVariant& errorData, const QString& /*udi*/) { waiting = false; if (error != Solid::NoError && errorData.isValid()) { KMessageBox::sorry(parentWindow, errorData.toString()); } } -void KMountMan::slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString& /*udi*/) +void KMountMan::slotSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString& /*udi*/) { waiting = false; if (error != Solid::NoError && errorData.isValid()) { KMessageBox::sorry(parentWindow, errorData.toString()); } } diff --git a/krusader/MountMan/kmountman.h b/krusader/MountMan/kmountman.h index 63744100..72d02c62 100644 --- a/krusader/MountMan/kmountman.h +++ b/krusader/MountMan/kmountman.h @@ -1,117 +1,117 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KMOUNTMAN_H #define KMOUNTMAN_H // QtCore #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include class KMountManGUI; class KToolBarPopupAction; class KMountMan : public QObject { Q_OBJECT friend class KMountManGUI; public: enum mntStatus {DOESNT_EXIST, NOT_MOUNTED, MOUNTED}; inline bool operational() { return _operational; } // check this 1st - void mount(QString mntPoint, bool blocking = true); // this is probably what you need for mount - void unmount(QString mntPoint, bool blocking = true); // this is probably what you need for unmount - mntStatus getStatus(QString mntPoint); // return the status of a mntPoint (if any) - void eject(QString mntPoint); + void mount(const QString& mntPoint, bool blocking = true); // this is probably what you need for mount + void unmount(const QString& mntPoint, bool blocking = true); // this is probably what you need for unmount + mntStatus getStatus(const QString& mntPoint); // return the status of a mntPoint (if any) + void eject(const QString& mntPoint); bool ejectable(QString path); bool removable(QString path); bool removable(Solid::Device d); - bool invalidFilesystem(QString type); - bool networkFilesystem(QString type); - bool nonmountFilesystem(QString type, QString mntPoint); + bool invalidFilesystem(const QString& type); + bool networkFilesystem(const QString& type); + bool nonmountFilesystem(const QString& type, const QString& mntPoint); QAction *action() { return (QAction *) _action; } explicit KMountMan(QWidget *parent); ~KMountMan(); // NOTE: this function needs some time (~50msec) - QString findUdiForPath(QString path, const Solid::DeviceInterface::Type &expType = Solid::DeviceInterface::Unknown); - QString pathForUdi(QString udi); + QString findUdiForPath(const QString& path, const Solid::DeviceInterface::Type &expType = Solid::DeviceInterface::Unknown); + QString pathForUdi(const QString& udi); public slots: void mainWindow(); // opens up the GUI - void autoMount(QString path); // just call it before refreshing into a dir + void autoMount(const QString& path); // just call it before refreshing into a dir void delayedPerformAction(const QAction *action); void quickList(); protected slots: void jobResult(KJob *job); - void slotTeardownDone(Solid::ErrorType error, QVariant errorData, const QString &udi); - void slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi); + void slotTeardownDone(Solid::ErrorType error, const QVariant& errorData, const QString &udi); + void slotSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString &udi); protected: // used internally static QExplicitlySharedDataPointer findInListByMntPoint(KMountPoint::List &lst, QString value); - void toggleMount(QString mntPoint); + void toggleMount(const QString& mntPoint); void emitRefreshPanel(const QUrl &url) { emit refreshPanel(url); } signals: void refreshPanel(const QUrl &); private: enum ActionType { Mount, Unmount }; KToolBarPopupAction *_action; QAction *_manageAction; bool _operational; // if false, something went terribly wrong on startup bool waiting; // used to block krusader while waiting for (un)mount operation KMountManGUI *mountManGui; // the following is the FS type QStringList invalid_fs; QStringList nonmount_fs; QStringList network_fs; // the following is the FS name QStringList nonmount_fs_mntpoint; QPointer parentWindow; }; #endif diff --git a/krusader/MountMan/kmountmangui.h b/krusader/MountMan/kmountmangui.h index 061d6a87..e48e26b8 100644 --- a/krusader/MountMan/kmountmangui.h +++ b/krusader/MountMan/kmountmangui.h @@ -1,193 +1,194 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KMOUNTMANGUI_H #define KMOUNTMANGUI_H // QtCore #include #include #include // QtWidgets #include #include #include +#include #include "../GUI/krtreewidget.h" #include "kmountman.h" #define WATCHER_DELAY 500 class QCheckBox; class KRFSDisplay; // forward definitions class fsData; class KMountManGUI : public QDialog { Q_OBJECT enum Pages { Filesystems = 0 }; public: explicit KMountManGUI(KMountMan *mntMan); ~KMountManGUI() override; protected: void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; protected slots: void doubleClicked(QTreeWidgetItem *); void clicked(QTreeWidgetItem *, const QPoint &); void slotToggleMount(); void slotEject(); void changeActive(); void changeActive(QTreeWidgetItem *); void checkMountChange(); // check whether the mount table was changed void updateList(); // fill-up the filesystems list void getSpaceData(); protected: QLayout *createMainPage(); // creator of the main page - filesystems void addItemToMountList(KrTreeWidget *lst, fsData &fs); fsData* getFsData(QTreeWidgetItem *item); QString getMntPoint(QTreeWidgetItem *item); void addNonMounted(); private: KMountMan *mountMan; KRFSDisplay *info; KrTreeWidget *mountList; QCheckBox *cbShowOnlyRemovable; QPushButton *mountButton; QPushButton *ejectButton; QTimer *watcher; QDateTime lastMtab; // used for the getSpace - gotSpace functions KMountPoint::List possible, mounted; QList fileSystems; int sizeX; int sizeY; }; // Data container for a single-filesystem data // maximum size supported is 2GB of 1kb blocks == 2048GB, enough. // not really needed, but kept for backward compatibility class fsData { public: fsData() : Name(), Type(), MntPoint(), TotalBlks(0), FreeBlks(0), Mounted(false) {} // get information inline QString name() { return Name; } inline QString shortName() { return Name.right(Name.length() - Name.indexOf("/", 1) - 1); } inline QString type() { return Type; } inline QString mntPoint() { return MntPoint; } inline long totalBlks() { return TotalBlks; } inline long freeBlks() { return FreeBlks; } inline KIO::filesize_t totalBytes() { return TotalBlks * 1024; } inline KIO::filesize_t freeBytes() { return FreeBlks * 1024; } //////////////////// insert a good round function here ///////////////// int usedPerct() { if (TotalBlks == 0) return 0; float res = ((float)(TotalBlks - FreeBlks)) / ((float) TotalBlks) * 100; if ((res - (int) res) > 0.5) return (int) res + 1; else return (int) res; } inline bool mounted() { return Mounted; } // set information inline void setName(QString n_) { - Name = n_; + Name = std::move(n_); } inline void setType(QString t_) { - Type = t_; + Type = std::move(t_); } inline void setMntPoint(QString m_) { - MntPoint = m_; + MntPoint = std::move(m_); } inline void setTotalBlks(long t_) { TotalBlks = t_; } inline void setFreeBlks(long f_) { FreeBlks = f_; } inline void setMounted(bool m_) { Mounted = m_; } private: QString Name; // i.e: /dev/cdrom QString Type; // i.e: iso9600 QString MntPoint; // i.e: /mnt/cdrom long TotalBlks; // measured in 1024bytes per block long FreeBlks; bool Mounted; // true if filesystem is mounted // additional attributes of a filesystem, parsed from fstab public: QString options; // additional fstab options }; class KrMountDetector { QString checksum; #ifndef BSD QDateTime lastMtab; #endif public: KrMountDetector(); static KrMountDetector * getInstance(); bool hasMountsChanged(); }; #endif diff --git a/krusader/Panel/PanelView/krsort.cpp b/krusader/Panel/PanelView/krsort.cpp index 209234ba..a2734a49 100644 --- a/krusader/Panel/PanelView/krsort.cpp +++ b/krusader/Panel/PanelView/krsort.cpp @@ -1,333 +1,335 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krsort.h" +#include + #include "krview.h" #include "../FileSystem/fileitem.h" namespace KrSort { void SortProps::init(FileItem *fileitem, int col, const KrViewProperties * props, bool isDummy, bool asc, int origNdx, QVariant customData) { _col = col; _prop = props; _isdummy = isDummy; _ascending = asc; _fileItem = fileitem; _index = origNdx; _name = fileitem->getName(); - _customData = customData; + _customData = std::move(customData); if(_prop->sortOptions & KrViewProperties::IgnoreCase) _name = _name.toLower(); switch (_col) { case KrViewProperties::Ext: { if (fileitem->isDir()) { _ext = ""; } else { // check if the file has an extension const QString& fileitemName = fileitem->getName(); int loc = fileitemName.lastIndexOf('.'); if (loc > 0) { // avoid mishandling of .bashrc and friend // check if it has one of the predefined 'atomic extensions' for (const auto & atomicExtension : props->atomicExtensions) { if (fileitemName.endsWith(atomicExtension) && fileitemName != atomicExtension) { loc = fileitemName.length() - atomicExtension.length(); break; } } _ext = _name.mid(loc); } else _ext = ""; } break; } case KrViewProperties::Type: _data = isDummy ? "" : KrView::mimeTypeText(fileitem); break; case KrViewProperties::Permissions: _data = isDummy ? "" : KrView::permissionsText(properties(), fileitem); break; case KrViewProperties::KrPermissions: _data = isDummy ? "" : KrView::krPermissionText(fileitem); break; case KrViewProperties::Owner: _data = isDummy ? "" : fileitem->getOwner(); break; case KrViewProperties::Group: _data = isDummy ? "" : fileitem->getGroup(); break; default: break; } } // compares numbers within two strings int compareNumbers(QString& aS1, int& aPos1, QString& aS2, int& aPos2) { int res = 0; int start1 = aPos1; int start2 = aPos2; while (aPos1 < aS1.length() && aS1.at(aPos1).isDigit()) aPos1++; while (aPos2 < aS2.length() && aS2.at(aPos2).isDigit()) aPos2++; // the left-most difference determines what's bigger int i1 = aPos1 - 1; int i2 = aPos2 - 1; for (; i1 >= start1 || i2 >= start2; i1--, i2--) { int c1 = 0; int c2 = 0; if (i1 >= start1) c1 = aS1.at(i1).digitValue(); if (i2 >= start2) c2 = aS2.at(i2).digitValue(); if (c1 < c2) res = -1; else if (c1 > c2) res = 1; } return res; } bool compareTextsAlphabetical(QString& aS1, QString& aS2, const KrViewProperties * _viewProperties, bool aNumbers) { int lPositionS1 = 0; int lPositionS2 = 0; // sometimes, localeAwareCompare is not case sensitive. in that case, we need to fallback to a simple string compare (KDE bug #40131) bool lUseLocaleAware = ((_viewProperties->sortOptions & KrViewProperties::IgnoreCase) || _viewProperties->localeAwareCompareIsCaseSensitive) && (_viewProperties->sortOptions & KrViewProperties::LocaleAwareSort); int j = 0; QChar lchar1; QChar lchar2; while (true) { lchar1 = aS1[lPositionS1]; lchar2 = aS2[lPositionS2]; // detect numbers if (aNumbers && lchar1.isDigit() && lchar2.isDigit()) { int j = compareNumbers(aS1, lPositionS1, aS2, lPositionS2); if (j != 0) return j < 0; } else if (lUseLocaleAware && ((lchar1 >= 128 && ((lchar2 >= 'A' && lchar2 <= 'Z') || (lchar2 >= 'a' && lchar2 <= 'z') || lchar2 >= 128)) || (lchar2 >= 128 && ((lchar1 >= 'A' && lchar1 <= 'Z') || (lchar1 >= 'a' && lchar1 <= 'z') || lchar1 >= 128)) ) ) { // use localeAwareCompare when a unicode character is encountered j = QString::localeAwareCompare(lchar1, lchar2); if (j != 0) return j < 0; lPositionS1++; lPositionS2++; } else { // if characters are latin or localeAwareCompare is not case sensitive then use simple characters compare is enough if (lchar1 < lchar2) return true; if (lchar1 > lchar2) return false; lPositionS1++; lPositionS2++; } // at this point strings are equal, check if ends of strings are reached if (lPositionS1 == aS1.length() && lPositionS2 == aS2.length()) return false; if (lPositionS1 == aS1.length() && lPositionS2 < aS2.length()) return true; if (lPositionS1 < aS1.length() && lPositionS2 == aS2.length()) return false; } } bool compareTextsCharacterCode(QString& aS1, QString& aS2, const KrViewProperties * _viewProperties, bool aNumbers) { Q_UNUSED(_viewProperties); int lPositionS1 = 0; int lPositionS2 = 0; while (true) { // detect numbers if (aNumbers && aS1[lPositionS1].isDigit() && aS2[lPositionS2].isDigit()) { int j = compareNumbers(aS1, lPositionS1, aS2, lPositionS2); if (j != 0) return j < 0; } else { if (aS1[lPositionS1] < aS2[lPositionS2]) return true; if (aS1[lPositionS1] > aS2[lPositionS2]) return false; lPositionS1++; lPositionS2++; } // at this point strings are equal, check if ends of strings are reached if (lPositionS1 == aS1.length() && lPositionS2 == aS2.length()) return false; if (lPositionS1 == aS1.length() && lPositionS2 < aS2.length()) return true; if (lPositionS1 < aS1.length() && lPositionS2 == aS2.length()) return false; } } bool compareTextsKrusader(const QString &aS1, const QString &aS2, const KrViewProperties *_viewProperties) { // sometimes, localeAwareCompare is not case sensitive. in that case, we need to fallback to a simple string compare (KDE bug #40131) if (((_viewProperties->sortOptions & KrViewProperties::IgnoreCase) || _viewProperties->localeAwareCompareIsCaseSensitive) && (_viewProperties->sortOptions & KrViewProperties::LocaleAwareSort)) return QString::localeAwareCompare(aS1, aS2) < 0; else // if localeAwareCompare is not case sensitive then use simple compare is enough return QString::compare(aS1, aS2) < 0; } bool compareTexts(QString aS1, QString aS2, const KrViewProperties * _viewProperties, bool asc, bool isName) { //check empty strings if (aS1.length() == 0 && aS2.length() == 0) { return false; } else if (aS1.length() == 0) { return true; } else if (aS2.length() == 0) { return false; } if (isName) { if (aS1 == "..") { return !asc; } else { if (aS2 == "..") return asc; } } switch (_viewProperties->sortMethod) { case KrViewProperties::Alphabetical: return compareTextsAlphabetical(aS1, aS2, _viewProperties, false); case KrViewProperties::AlphabeticalNumbers: return compareTextsAlphabetical(aS1, aS2, _viewProperties, true); case KrViewProperties::CharacterCode: return compareTextsCharacterCode(aS1, aS2, _viewProperties, false); case KrViewProperties::CharacterCodeNumbers: return compareTextsCharacterCode(aS1, aS2, _viewProperties, true); case KrViewProperties::Krusader: default: return compareTextsKrusader(aS1, aS2, _viewProperties); } } bool itemLessThan(SortProps *sp, SortProps *sp2) { FileItem * file1 = sp->fileitem(); FileItem * file2 = sp2->fileitem(); bool isdir1 = file1->isDir(); bool isdir2 = file2->isDir(); bool dirsFirst = sp->properties()->sortOptions & KrViewProperties::DirsFirst; bool alwaysSortDirsByName = sp->properties()->sortOptions & KrViewProperties::AlwaysSortDirsByName && dirsFirst && isdir1 && isdir2; if(dirsFirst) { if (isdir1 && !isdir2) return sp->isAscending(); if (isdir2 && !isdir1) return !sp->isAscending(); } if (sp->isDummy()) return sp->isAscending(); if (sp2->isDummy()) return !sp->isAscending(); int column = sp->column(); if (dirsFirst && isdir1 && isdir2 && alwaysSortDirsByName) { alwaysSortDirsByName = !sp->isAscending(); column = KrViewProperties::Name; } switch (column) { case KrViewProperties::Name: return compareTexts(sp->name(), sp2->name(), sp->properties(), sp->isAscending(), true) ^ alwaysSortDirsByName; case KrViewProperties::Ext: if (sp->extension() == sp2->extension()) return compareTexts(sp->name(), sp2->name(), sp->properties(), sp->isAscending(), true); return compareTexts(sp->extension(), sp2->extension(), sp->properties(), sp->isAscending(), true); case KrViewProperties::Size: if (file1->getSize() == file2->getSize()) return compareTexts(sp->name(), sp2->name(), sp->properties(), sp->isAscending(), true); return file1->getSize() < file2->getSize(); case KrViewProperties::Modified: return compareTime(file1->getTime_t(), file2->getTime_t(), sp, sp2); case KrViewProperties::Changed: return compareTime(file1->getChangedTime(), file2->getChangedTime(), sp, sp2); case KrViewProperties::Accessed: return compareTime(file1->getAccessTime(), file2->getAccessTime(), sp, sp2); case KrViewProperties::Type: case KrViewProperties::Permissions: case KrViewProperties::KrPermissions: case KrViewProperties::Owner: case KrViewProperties::Group: if (sp->data() == sp2->data()) return compareTexts(sp->name(), sp2->name(), sp->properties(), sp->isAscending(), true); return compareTexts(sp->data(), sp2->data(), sp->properties(), sp->isAscending(), true); } return sp->name() < sp2->name(); } bool compareTime(time_t time1, time_t time2, SortProps *sp, SortProps *sp2) { return time1 != time2 ? time1 < time2 : compareTexts(sp->name(), sp2->name(), sp->properties(), sp->isAscending(), true); } bool itemGreaterThan(SortProps *sp, SortProps *sp2) { return !itemLessThan(sp, sp2); } Sorter::Sorter(int reserveItems, const KrViewProperties *viewProperties, LessThanFunc lessThanFunc, LessThanFunc greaterThanFunc) : _viewProperties(viewProperties), _lessThanFunc(lessThanFunc), _greaterThanFunc(greaterThanFunc) { _items.reserve(reserveItems); _itemStore.reserve(reserveItems); } void Sorter::addItem(FileItem *fileitem, bool isDummy, int idx, QVariant customData) { - _itemStore << SortProps(fileitem, _viewProperties->sortColumn, _viewProperties, isDummy, !descending(), idx, customData); + _itemStore << SortProps(fileitem, _viewProperties->sortColumn, _viewProperties, isDummy, !descending(), idx, std::move(customData)); _items << &_itemStore.last(); } void Sorter::sort() { qStableSort(_items.begin(), _items.end(), descending() ? _greaterThanFunc : _lessThanFunc); } int Sorter::insertIndex(FileItem *fileitem, bool isDummy, QVariant customData) { - SortProps props(fileitem, _viewProperties->sortColumn, _viewProperties, isDummy, !descending(), -1, customData); + SortProps props(fileitem, _viewProperties->sortColumn, _viewProperties, isDummy, !descending(), -1, std::move(customData)); const QVector::iterator it = qLowerBound(_items.begin(), _items.end(), &props, descending() ? _greaterThanFunc : _lessThanFunc); if(it != _items.end()) return _items.indexOf((*it)); else return _items.count(); } bool Sorter::descending() const { return _viewProperties->sortOptions & KrViewProperties::Descending; } } // namespace KrSort diff --git a/krusader/Panel/PanelView/krview.cpp b/krusader/Panel/PanelView/krview.cpp index eac7492b..67a1519f 100644 --- a/krusader/Panel/PanelView/krview.cpp +++ b/krusader/Panel/PanelView/krview.cpp @@ -1,1222 +1,1222 @@ /***************************************************************************** * Copyright (C) 2000-2002 Shie Erlich * * Copyright (C) 2000-2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krview.h" #include "krselectionmode.h" #include "krviewfactory.h" #include "krviewitem.h" #include "../FileSystem/dirlisterinterface.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/krpermhandler.h" #include "../Filter/filterdialog.h" #include "../defaults.h" #include "../filelisticon.h" #include "../krcolorcache.h" #include "../krglobal.h" #include "../krpreviews.h" #include "../viewactions.h" // QtCore #include #include // QtGui #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #define FILEITEM getFileItem() KrView *KrViewOperator::_changedView = nullptr; KrViewProperties::PropertyType KrViewOperator::_changedProperties = KrViewProperties::NoProperty; // ----------------------------- operator KrViewOperator::KrViewOperator(KrView *view, QWidget *widget) : _view(view), _widget(widget), _massSelectionUpdate(false) { _saveDefaultSettingsTimer.setSingleShot(true); connect(&_saveDefaultSettingsTimer, &QTimer::timeout, this, &KrViewOperator::saveDefaultSettings); } KrViewOperator::~KrViewOperator() { if(_changedView == _view) saveDefaultSettings(); } void KrViewOperator::startUpdate() { _view->refresh(); } void KrViewOperator::cleared() { _view->clear(); } void KrViewOperator::fileAdded(FileItem *fileitem) { _view->addItem(fileitem); } void KrViewOperator::fileUpdated(FileItem *newFileitem) { _view->updateItem(newFileitem); } void KrViewOperator::startDrag() { QStringList items; _view->getSelectedItems(&items); if (items.empty()) return ; // don't drag an empty thing QPixmap px; if (items.count() > 1 || _view->getCurrentKrViewItem() == nullptr) px = FileListIcon("document-multiple").pixmap(); // we are dragging multiple items else px = _view->getCurrentKrViewItem()->icon(); emit letsDrag(items, px); } bool KrViewOperator::searchItem(const QString &text, bool caseSensitive, int direction) { KrViewItem * item = _view->getCurrentKrViewItem(); if (!item) { return false; } const QRegExp regeEx(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); if (!direction) { if (regeEx.indexIn(item->name()) == 0) { return true; } direction = 1; } KrViewItem * startItem = item; while (true) { item = (direction > 0) ? _view->getNext(item) : _view->getPrev(item); if (!item) item = (direction > 0) ? _view->getFirst() : _view->getLast(); if (regeEx.indexIn(item->name()) == 0) { _view->setCurrentKrViewItem(item); _view->makeItemVisible(item); return true; } if (item == startItem) { return false; } } } bool KrViewOperator::filterSearch(const QString &text, bool caseSensitive) { _view->_quickFilterMask = QRegExp(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); _view->refresh(); return _view->_count || !_view->_files->numFileItems(); } void KrViewOperator::setMassSelectionUpdate(bool upd) { _massSelectionUpdate = upd; if (!upd) { emit selectionChanged(); _view->redraw(); } } void KrViewOperator::settingsChanged(KrViewProperties::PropertyType properties) { if(!_view->_updateDefaultSettings || _view->_ignoreSettingsChange) return; if(_changedView != _view) saveDefaultSettings(); _changedView = _view; _changedProperties = static_cast(_changedProperties | properties); _saveDefaultSettingsTimer.start(100); } void KrViewOperator::saveDefaultSettings() { _saveDefaultSettingsTimer.stop(); if(_changedView) _changedView->saveDefaultSettings(_changedProperties); _changedProperties = KrViewProperties::NoProperty; _changedView = nullptr; } // ----------------------------- krview const KrView::IconSizes KrView::iconSizes; KrView::KrView(KrViewInstance &instance, KConfig *cfg) : _config(cfg), _properties(nullptr), _focused(false), _fileIconSize(0), _instance(instance), _files(nullptr), _mainWindow(nullptr), _widget(nullptr), _nameToMakeCurrent(QString()), _previews(nullptr), _updateDefaultSettings(false), _ignoreSettingsChange(false), _count(0), _numDirs(0), _dummyFileItem(nullptr) { } KrView::~KrView() { _instance.m_objects.removeOne(this); delete _previews; _previews = nullptr; delete _dummyFileItem; _dummyFileItem = nullptr; if (_properties) qFatal("A class inheriting KrView didn't delete _properties!"); if (_operator) qFatal("A class inheriting KrView didn't delete _operator!"); } void KrView::init(bool enableUpdateDefaultSettings) { // sanity checks: if (!_widget) qFatal("_widget must be set during construction of KrView inheritors"); // ok, continue initProperties(); _operator = createOperator(); setup(); restoreDefaultSettings(); _updateDefaultSettings = enableUpdateDefaultSettings && KConfigGroup(_config, "Startup").readEntry("Update Default Panel Settings", _RememberPos); _instance.m_objects.append(this); } void KrView::initProperties() { const KConfigGroup grpInstance(_config, _instance.name()); const bool displayIcons = grpInstance.readEntry("With Icons", _WithIcons); const KConfigGroup grpSvr(_config, "Look&Feel"); const bool numericPermissions = grpSvr.readEntry("Numeric permissions", _NumericPermissions); int sortOps = 0; if (grpSvr.readEntry("Show Directories First", true)) sortOps |= KrViewProperties::DirsFirst; if(grpSvr.readEntry("Always sort dirs by name", false)) sortOps |= KrViewProperties::AlwaysSortDirsByName; if (!grpSvr.readEntry("Case Sensative Sort", _CaseSensativeSort)) sortOps |= KrViewProperties::IgnoreCase; if (grpSvr.readEntry("Locale Aware Sort", true)) sortOps |= KrViewProperties::LocaleAwareSort; auto sortOptions = static_cast(sortOps); KrViewProperties::SortMethod sortMethod = static_cast( grpSvr.readEntry("Sort method", (int)_DefaultSortMethod)); const bool humanReadableSize = grpSvr.readEntry("Human Readable Size", _HumanReadableSize); // see KDE bug #40131 const bool localeAwareCompareIsCaseSensitive = QString("a").localeAwareCompare("B") > 0; QStringList defaultAtomicExtensions; defaultAtomicExtensions += ".tar.gz"; defaultAtomicExtensions += ".tar.bz2"; defaultAtomicExtensions += ".tar.lzma"; defaultAtomicExtensions += ".tar.xz"; defaultAtomicExtensions += ".moc.cpp"; QStringList atomicExtensions = grpSvr.readEntry("Atomic Extensions", defaultAtomicExtensions); for (QStringList::iterator i = atomicExtensions.begin(); i != atomicExtensions.end();) { QString & ext = *i; ext = ext.trimmed(); if (!ext.length()) { i = atomicExtensions.erase(i); continue; } if (!ext.startsWith('.')) ext.insert(0, '.'); ++i; } _properties = new KrViewProperties(displayIcons, numericPermissions, sortOptions, sortMethod, humanReadableSize, localeAwareCompareIsCaseSensitive, atomicExtensions); } void KrView::showPreviews(bool show) { if(show) { if(!_previews) { _previews = new KrPreviews(this); _previews->update(); } } else { delete _previews; _previews = nullptr; } redraw(); // op()->settingsChanged(KrViewProperties::PropShowPreviews); op()->emitRefreshActions(); } void KrView::updatePreviews() { if(_previews) _previews->update(); } QPixmap KrView::processIcon(const QPixmap &icon, bool dim, const QColor & dimColor, int dimFactor, bool symlink) { QPixmap pixmap = icon; if (symlink) { const QStringList overlays = QStringList() << QString() << "emblem-symbolic-link"; Icon::applyOverlays(&pixmap, overlays); } if(!dim) return pixmap; QImage dimmed = pixmap.toImage(); QPainter p(&dimmed); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(0, 0, icon.width(), icon.height(), dimColor); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.setOpacity((qreal)dimFactor / (qreal)100); p.drawPixmap(0, 0, icon.width(), icon.height(), pixmap); return QPixmap::fromImage(dimmed, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection ); } QPixmap KrView::getIcon(FileItem *fileitem, bool active, int size/*, KRListItem::cmpColor color*/) { // KConfigGroup ag( krConfig, "Advanced"); ////////////////////////////// QPixmap icon; QString iconName = fileitem->getIcon(); QString cacheName; if(!size) size = _FilelistIconSize.toInt(); QColor dimColor; int dimFactor; bool dim = !active && KrColorCache::getColorCache().getDimSettings(dimColor, dimFactor); if (iconName.isNull()) iconName = ""; cacheName.append(QString::number(size)); if(fileitem->isSymLink()) cacheName.append("LINK_"); if(dim) cacheName.append("DIM_"); cacheName.append(iconName); //QPixmapCache::setCacheLimit( ag.readEntry("Icon Cache Size",_IconCacheSize) ); // first try the cache if (!QPixmapCache::find(cacheName, icon)) { icon = processIcon(Icon(iconName, Icon("unknown")).pixmap(size), dim, dimColor, dimFactor, fileitem->isSymLink()); // insert it into the cache QPixmapCache::insert(cacheName, icon); } return icon; } QPixmap KrView::getIcon(FileItem *fileitem) { if(_previews) { QPixmap icon; if(_previews->getPreview(fileitem, icon, _focused)) return icon; } return getIcon(fileitem, _focused, _fileIconSize); } /** * this function ADDs a list of selected item names into 'names'. * it assumes the list is ready and doesn't initialize it, or clears it */ -void KrView::getItemsByMask(QString mask, QStringList* names, bool dirs, bool files) +void KrView::getItemsByMask(const QString& mask, QStringList* names, bool dirs, bool files) { for (KrViewItem * it = getFirst(); it != nullptr; it = getNext(it)) { if ((it->name() == "..") || !QDir::match(mask, it->name())) continue; // if we got here, than the item fits the mask if (it->getFileItem()->isDir() && !dirs) continue; // do we need to skip folders? if (!it->getFileItem()->isDir() && !files) continue; // do we need to skip files names->append(it->name()); } } /** * this function ADDs a list of selected item names into 'names'. * it assumes the list is ready and doesn't initialize it, or clears it */ void KrView::getSelectedItems(QStringList *names, bool fallbackToFocused) { for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) if (it->isSelected() && (it->name() != "..")) names->append(it->name()); if (fallbackToFocused) { // if all else fails, take the current item const QString item = getCurrentItem(); if (names->empty() && !item.isEmpty() && item != "..") { names->append(item); } } } void KrView::getSelectedKrViewItems(KrViewItemList *items) { for (KrViewItem * it = getFirst(); it != nullptr; it = getNext(it)) if (it->isSelected() && (it->name() != "..")) items->append(it); // if all else fails, take the current item QString item = getCurrentItem(); if (items->empty() && !item.isEmpty() && item != ".." && getCurrentKrViewItem() != nullptr) { items->append(getCurrentKrViewItem()); } } QString KrView::statistics() { KIO::filesize_t size = calcSize(); KIO::filesize_t selectedSize = calcSelectedSize(); QString tmp; KConfigGroup grp(_config, "Look&Feel"); if(grp.readEntry("Show Size In Bytes", false)) { tmp = i18nc("%1=number of selected items,%2=total number of items, \ %3=filesize of selected items,%4=filesize in Bytes, \ %5=filesize of all items in folder,%6=filesize in Bytes", "%1 out of %2, %3 (%4) out of %5 (%6)", numSelected(), _count, KIO::convertSize(selectedSize), KRpermHandler::parseSize(selectedSize), KIO::convertSize(size), KRpermHandler::parseSize(size)); } else { tmp = i18nc("%1=number of selected items,%2=total number of items, \ %3=filesize of selected items,%4=filesize of all items in folder", "%1 out of %2, %3 out of %4", numSelected(), _count, KIO::convertSize(selectedSize), KIO::convertSize(size)); } // notify if we're running a filtered view if (filter() != KrViewProperties::All) tmp = ">> [ " + filterMask().nameFilter() + " ] " + tmp; return tmp; } bool KrView::changeSelection(const KRQuery& filter, bool select) { KConfigGroup grpSvr(_config, "Look&Feel"); return changeSelection(filter, select, grpSvr.readEntry("Mark Dirs", _MarkDirs), true); } bool KrView::changeSelection(const KRQuery& filter, bool select, bool includeDirs, bool makeVisible) { if (op()) op()->setMassSelectionUpdate(true); KrViewItem *temp = getCurrentKrViewItem(); KrViewItem *firstMatch = nullptr; for (KrViewItem * it = getFirst(); it != nullptr; it = getNext(it)) { if (it->name() == "..") continue; if (it->getFileItem()->isDir() && !includeDirs) continue; FileItem * file = it->getMutableFileItem(); // filter::match calls getMimetype which isn't const if (file == nullptr) continue; if (filter.match(file)) { it->setSelected(select); if (!firstMatch) firstMatch = it; } } if (op()) op()->setMassSelectionUpdate(false); updateView(); if (ensureVisibilityAfterSelect() && temp != nullptr) { makeItemVisible(temp); } else if (makeVisible && firstMatch != nullptr) { // if no selected item is visible... KrViewItemList selectedItems; getSelectedKrViewItems(&selectedItems); bool anyVisible = false; for (KrViewItem *item : selectedItems) { if (isItemVisible(item)) { anyVisible = true; break; } } if (!anyVisible) { // ...scroll to fist selected item makeItemVisible(firstMatch); } } redraw(); return firstMatch != nullptr; // return if any file was selected } void KrView::invertSelection() { if (op()) op()->setMassSelectionUpdate(true); KConfigGroup grpSvr(_config, "Look&Feel"); bool markDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); KrViewItem *temp = getCurrentKrViewItem(); for (KrViewItem * it = getFirst(); it != nullptr; it = getNext(it)) { if (it->name() == "..") continue; if (it->getFileItem()->isDir() && !markDirs && !it->isSelected()) continue; it->setSelected(!it->isSelected()); } if (op()) op()->setMassSelectionUpdate(false); updateView(); if (ensureVisibilityAfterSelect() && temp != nullptr) makeItemVisible(temp); } QString KrView::firstUnmarkedBelowCurrent(const bool skipCurrent) { if (getCurrentKrViewItem() == nullptr) return QString(); KrViewItem *iterator = getCurrentKrViewItem(); if (skipCurrent) iterator = getNext(iterator); while (iterator && iterator->isSelected()) iterator = getNext(iterator); if (!iterator) { iterator = getPrev(getCurrentKrViewItem()); while (iterator && iterator->isSelected()) iterator = getPrev(iterator); } if (!iterator) return QString(); return iterator->name(); } void KrView::deleteItem(const QString &name, bool onUpdate) { KrViewItem *viewItem = findItemByName(name); if (!viewItem) return; if (_previews && !onUpdate) _previews->deletePreview(viewItem); preDeleteItem(viewItem); if (viewItem->FILEITEM->isDir()) { --_numDirs; } --_count; delete viewItem; if (!onUpdate) op()->emitSelectionChanged(); } void KrView::addItem(FileItem *fileItem, bool onUpdate) { if (isFiltered(fileItem)) return; KrViewItem *viewItem = preAddItem(fileItem); if (!viewItem) return; // not added if (_previews) _previews->updatePreview(viewItem); if (fileItem->isDir()) ++_numDirs; ++_count; if (!onUpdate) { op()->emitSelectionChanged(); } } void KrView::updateItem(FileItem *newFileItem) { // file name did not change const QString name = newFileItem->getName(); // preserve 'current' and 'selection' const bool isCurrent = getCurrentItem() == name; QStringList selectedNames; getSelectedItems(&selectedNames, false); const bool isSelected = selectedNames.contains(name); // delete old file item deleteItem(name, true); if (!isFiltered(newFileItem)) { addItem(newFileItem, true); } if (isCurrent) setCurrentItem(name, false); if (isSelected) setSelected(newFileItem, true); op()->emitSelectionChanged(); } void KrView::clear() { if(_previews) _previews->clear(); _count = _numDirs = 0; delete _dummyFileItem; _dummyFileItem = nullptr; redraw(); } bool KrView::handleKeyEvent(QKeyEvent *e) { qDebug() << "key event=" << e; switch (e->key()) { case Qt::Key_Enter : case Qt::Key_Return : { if (e->modifiers() & Qt::ControlModifier) // let the panel handle it e->ignore(); else { KrViewItem * i = getCurrentKrViewItem(); if (i == nullptr) return true; QString tmp = i->name(); op()->emitExecuted(tmp); } return true; } case Qt::Key_QuoteLeft : // Terminal Emulator bugfix if (e->modifiers() == Qt::ControlModifier) { // let the panel handle it e->ignore(); } else { // a normal click - do a lynx-like moving thing // ask krusader to move to the home directory op()->emitGoHome(); } return true; case Qt::Key_Delete : // delete/trash the file (delete with alternative mode is a panel action) // allow only no modifier or KeypadModifier (i.e. Del on a Numeric Keypad) if ((e->modifiers() & ~Qt::KeypadModifier) == Qt::NoModifier) { op()->emitDefaultDeleteFiles(); } return true; case Qt::Key_Insert: { KrViewItem * i = getCurrentKrViewItem(); if (!i) return true; i->setSelected(!i->isSelected()); if (KrSelectionMode::getSelectionHandler()->insertMovesDown()) { KrViewItem * next = getNext(i); if (next) { setCurrentKrViewItem(next); makeItemVisible(next); } } op()->emitSelectionChanged(); return true; } case Qt::Key_Space: { KrViewItem * viewItem = getCurrentKrViewItem(); if (viewItem != nullptr) { viewItem->setSelected(!viewItem->isSelected()); if (viewItem->getFileItem()->isDir() && KrSelectionMode::getSelectionHandler()->spaceCalculatesDiskSpace()) { op()->emitQuickCalcSpace(viewItem); } if (KrSelectionMode::getSelectionHandler()->spaceMovesDown()) { KrViewItem * next = getNext(viewItem); if (next) { setCurrentKrViewItem(next); makeItemVisible(next); } } op()->emitSelectionChanged(); } return true; } case Qt::Key_Backspace : // Terminal Emulator bugfix case Qt::Key_Left : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { // let the panel handle it e->ignore(); } else { // a normal click - do a lynx-like moving thing // ask krusader to move up a directory op()->emitDirUp(); } return true; // safety case Qt::Key_Right : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { // let the panel handle it e->ignore(); } else { // just a normal click - do a lynx-like moving thing KrViewItem *i = getCurrentKrViewItem(); if (i) op()->emitGoInside(i->name()); } return true; case Qt::Key_Up : if (e->modifiers() == Qt::ControlModifier) { // let the panel handle it - jump to the Location Bar e->ignore(); } else { KrViewItem *item = getCurrentKrViewItem(); if (item) { if (e->modifiers() == Qt::ShiftModifier) { item->setSelected(!item->isSelected()); op()->emitSelectionChanged(); } item = getPrev(item); if (item) { setCurrentKrViewItem(item); makeItemVisible(item); } } } return true; case Qt::Key_Down : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { // let the panel handle it - jump to command line e->ignore(); } else { KrViewItem *item = getCurrentKrViewItem(); if (item) { if (e->modifiers() == Qt::ShiftModifier) { item->setSelected(!item->isSelected()); op()->emitSelectionChanged(); } item = getNext(item); if (item) { setCurrentKrViewItem(item); makeItemVisible(item); } } } return true; case Qt::Key_Home: { if (e->modifiers() & Qt::ShiftModifier) { bool select = true; KrViewItem *pos = getCurrentKrViewItem(); if (pos == nullptr) pos = getLast(); KrViewItem *item = getFirst(); op()->setMassSelectionUpdate(true); while (item) { item->setSelected(select); if (item == pos) select = false; item = getNext(item); } op()->setMassSelectionUpdate(false); } KrViewItem * first = getFirst(); if (first) { setCurrentKrViewItem(first); makeItemVisible(first); } } return true; case Qt::Key_End: if (e->modifiers() & Qt::ShiftModifier) { bool select = false; KrViewItem *pos = getCurrentKrViewItem(); if (pos == nullptr) pos = getFirst(); op()->setMassSelectionUpdate(true); KrViewItem *item = getFirst(); while (item) { if (item == pos) select = true; item->setSelected(select); item = getNext(item); } op()->setMassSelectionUpdate(false); } else { KrViewItem *last = getLast(); if (last) { setCurrentKrViewItem(last); makeItemVisible(last); } } return true; case Qt::Key_PageDown: { KrViewItem * current = getCurrentKrViewItem(); int downStep = itemsPerPage(); while (downStep != 0 && current) { KrViewItem * newCurrent = getNext(current); if (newCurrent == nullptr) break; current = newCurrent; downStep--; } if (current) { setCurrentKrViewItem(current); makeItemVisible(current); } return true; } case Qt::Key_PageUp: { KrViewItem * current = getCurrentKrViewItem(); int upStep = itemsPerPage(); while (upStep != 0 && current) { KrViewItem * newCurrent = getPrev(current); if (newCurrent == nullptr) break; current = newCurrent; upStep--; } if (current) { setCurrentKrViewItem(current); makeItemVisible(current); } return true; } case Qt::Key_Escape: e->ignore(); return true; // otherwise the selection gets lost??!?? // also it is needed by the panel case Qt::Key_A : // mark all if (e->modifiers() == Qt::ControlModifier) { //FIXME: shouldn't there also be a shortcut for unselecting everything ? selectAllIncludingDirs(); return true; } #if __GNUC__ >= 7 [[gnu::fallthrough]]; #endif default: return false; } return false; } void KrView::zoomIn() { int idx = iconSizes.indexOf(_fileIconSize); if(idx >= 0 && (idx+1) < iconSizes.count()) setFileIconSize(iconSizes[idx+1]); } void KrView::zoomOut() { int idx = iconSizes.indexOf(_fileIconSize); if(idx > 0) setFileIconSize(iconSizes[idx-1]); } void KrView::setFileIconSize(int size) { if(iconSizes.indexOf(size) < 0) return; _fileIconSize = size; if(_previews) { _previews->clear(); _previews->update(); } redraw(); op()->emitRefreshActions(); } int KrView::defaultFileIconSize() { KConfigGroup grpSvr(_config, _instance.name()); return grpSvr.readEntry("IconSize", _FilelistIconSize).toInt(); } void KrView::saveDefaultSettings(KrViewProperties::PropertyType properties) { saveSettings(KConfigGroup(_config, _instance.name()), properties); op()->emitRefreshActions(); } void KrView::restoreDefaultSettings() { restoreSettings(KConfigGroup(_config, _instance.name())); } void KrView::saveSettings(KConfigGroup group, KrViewProperties::PropertyType properties) { if(properties & KrViewProperties::PropIconSize) group.writeEntry("IconSize", fileIconSize()); if(properties & KrViewProperties::PropShowPreviews) group.writeEntry("ShowPreviews", previewsShown()); if(properties & KrViewProperties::PropSortMode) saveSortMode(group); if(properties & KrViewProperties::PropFilter) { group.writeEntry("Filter", static_cast(_properties->filter)); group.writeEntry("FilterApplysToDirs", _properties->filterApplysToDirs); if(_properties->filterSettings.isValid()) _properties->filterSettings.save(KConfigGroup(&group, "FilterSettings")); } } -void KrView::restoreSettings(KConfigGroup group) +void KrView::restoreSettings(const KConfigGroup& group) { _ignoreSettingsChange = true; doRestoreSettings(group); _ignoreSettingsChange = false; refresh(); } void KrView::doRestoreSettings(KConfigGroup group) { restoreSortMode(group); setFileIconSize(group.readEntry("IconSize", defaultFileIconSize())); showPreviews(group.readEntry("ShowPreviews", false)); _properties->filter = static_cast(group.readEntry("Filter", static_cast(KrViewProperties::All))); _properties->filterApplysToDirs = group.readEntry("FilterApplysToDirs", false); _properties->filterSettings.load(KConfigGroup(&group, "FilterSettings")); _properties->filterMask = _properties->filterSettings.toQuery(); } void KrView::applySettingsToOthers() { for(auto view : _instance.m_objects) { if(this != view) { view->_ignoreSettingsChange = true; view->copySettingsFrom(this); view->_ignoreSettingsChange = false; } } } void KrView::sortModeUpdated(KrViewProperties::ColumnType sortColumn, bool descending) { if(sortColumn == _properties->sortColumn && descending == (bool) (_properties->sortOptions & KrViewProperties::Descending)) return; int options = _properties->sortOptions; if(descending) options |= KrViewProperties::Descending; else options &= ~KrViewProperties::Descending; _properties->sortColumn = sortColumn; _properties->sortOptions = static_cast(options); // op()->settingsChanged(KrViewProperties::PropSortMode); } bool KrView::drawCurrent() const { return isFocused() || KConfigGroup(_config, "Look&Feel") .readEntry("Always Show Current Item", _AlwaysShowCurrentItem); } void KrView::saveSortMode(KConfigGroup &group) { group.writeEntry("Sort Column", static_cast(_properties->sortColumn)); group.writeEntry("Descending Sort Order", _properties->sortOptions & KrViewProperties::Descending); } void KrView::restoreSortMode(KConfigGroup &group) { int column = group.readEntry("Sort Column", static_cast(KrViewProperties::Name)); bool isDescending = group.readEntry("Descending Sort Order", false); setSortMode(static_cast(column), isDescending); } QString KrView::krPermissionText(const FileItem * fileitem) { QString tmp; switch (fileitem->isReadable()) { case ALLOWED_PERM: tmp+='r'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } switch (fileitem->isWriteable()) { case ALLOWED_PERM: tmp+='w'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } switch (fileitem->isExecutable()) { case ALLOWED_PERM: tmp+='x'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } return tmp; } QString KrView::permissionsText(const KrViewProperties *properties, const FileItem *fileItem) { return properties->numericPermissions ? QString().asprintf("%.4o", fileItem->getMode() & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) : fileItem->getPerm(); } QString KrView::sizeText(const KrViewProperties *properties, KIO::filesize_t size) { return properties->humanReadableSize ? KIO::convertSize(size) : KRpermHandler::parseSize(size); } QString KrView::mimeTypeText(FileItem *fileItem) { QMimeType mt = QMimeDatabase().mimeTypeForName(fileItem->getMime()); return mt.isValid() ? mt.comment() : QString(); } bool KrView::isFiltered(FileItem *fileitem) { if (_quickFilterMask.isValid() && _quickFilterMask.indexIn(fileitem->getName()) == -1) return true; bool filteredOut = false; bool isDir = fileitem->isDir(); if (!isDir || (isDir && properties()->filterApplysToDirs)) { switch (properties()->filter) { case KrViewProperties::All : break; case KrViewProperties::Custom : if (!properties()->filterMask.match(fileitem)) filteredOut = true; break; case KrViewProperties::Dirs: if (!isDir) filteredOut = true; break; case KrViewProperties::Files: if (isDir) filteredOut = true; break; default: break; } } return filteredOut; } void KrView::setFiles(DirListerInterface *files) { if(files != _files) { clear(); if(_files) QObject::disconnect(_files, nullptr, op(), nullptr); _files = files; } if(!_files) return; QObject::disconnect(_files, nullptr, op(), nullptr); QObject::connect(_files, &DirListerInterface::scanDone, op(), &KrViewOperator::startUpdate); QObject::connect(_files, &DirListerInterface::cleared, op(), &KrViewOperator::cleared); QObject::connect(_files, &DirListerInterface::addedFileItem, op(), &KrViewOperator::fileAdded); QObject::connect(_files, &DirListerInterface::updatedFileItem, op(), &KrViewOperator::fileUpdated); } -void KrView::setFilter(KrViewProperties::FilterSpec filter, FilterSettings customFilter, bool applyToDirs) +void KrView::setFilter(KrViewProperties::FilterSpec filter, const FilterSettings& customFilter, bool applyToDirs) { _properties->filter = filter; _properties->filterSettings = customFilter; _properties->filterMask = customFilter.toQuery(); _properties->filterApplysToDirs = applyToDirs; refresh(); } void KrView::setFilter(KrViewProperties::FilterSpec filter) { KConfigGroup cfg(_config, "Look&Feel"); bool rememberSettings = cfg.readEntry("FilterDialogRemembersSettings", _FilterDialogRemembersSettings); bool applyToDirs = rememberSettings ? _properties->filterApplysToDirs : false; switch (filter) { case KrViewProperties::All : break; case KrViewProperties::Custom : { FilterDialog dialog(_widget, i18n("Filter Files"), QStringList(i18n("Apply filter to folders")), false); dialog.checkExtraOption(i18n("Apply filter to folders"), applyToDirs); if(rememberSettings) dialog.applySettings(_properties->filterSettings); dialog.exec(); FilterSettings s(dialog.getSettings()); if(!s.isValid()) // if the user canceled - quit return; _properties->filterSettings = s; _properties->filterMask = s.toQuery(); applyToDirs = dialog.isExtraOptionChecked(i18n("Apply filter to folders")); } break; default: return; } _properties->filterApplysToDirs = applyToDirs; _properties->filter = filter; refresh(); } void KrView::customSelection(bool select) { KConfigGroup grpSvr(_config, "Look&Feel"); bool includeDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); FilterDialog dialog(nullptr, i18n("Select Files"), QStringList(i18n("Apply selection to folders")), false); dialog.checkExtraOption(i18n("Apply selection to folders"), includeDirs); dialog.exec(); KRQuery query = dialog.getQuery(); // if the user canceled - quit if (query.isNull()) return; includeDirs = dialog.isExtraOptionChecked(i18n("Apply selection to folders")); changeSelection(query, select, includeDirs); } void KrView::refresh() { const QString currentItem = !nameToMakeCurrent().isEmpty() ? // nameToMakeCurrent() : getCurrentItem(); bool scrollToCurrent = !nameToMakeCurrent().isEmpty() || isItemVisible(getCurrentKrViewItem()); setNameToMakeCurrent(QString()); const QModelIndex currentIndex = getCurrentIndex(); const QList selection = selectedUrls(); clear(); if(!_files) return; QList fileItems; // if we are not at the root add the ".." entry if(!_files->isRoot()) { _dummyFileItem = FileItem::createDummy(); fileItems << _dummyFileItem; } foreach(FileItem *fileitem, _files->fileItems()) { if(!fileitem || isFiltered(fileitem)) continue; if(fileitem->isDir()) _numDirs++; _count++; fileItems << fileitem; } populate(fileItems, _dummyFileItem); if(!selection.isEmpty()) setSelectionUrls(selection); if (!currentItem.isEmpty()) { if (currentItem == ".." && _count > 0 && // !_quickFilterMask.isEmpty() && _quickFilterMask.isValid()) { // In a filtered view we should never select the dummy entry if // there are real matches. setCurrentKrViewItem(getNext(getFirst())); } else { setCurrentItem(currentItem, scrollToCurrent, currentIndex); } } else { setCurrentKrViewItem(getFirst()); } updatePreviews(); redraw(); op()->emitSelectionChanged(); } void KrView::setSelected(const FileItem* fileitem, bool select) { if (fileitem == _dummyFileItem) return; if (select) clearSavedSelection(); intSetSelected(fileitem, select); } void KrView::saveSelection() { _savedSelection = selectedUrls(); op()->emitRefreshActions(); } void KrView::restoreSelection() { if(canRestoreSelection()) setSelectionUrls(_savedSelection); } void KrView::clearSavedSelection() { _savedSelection.clear(); op()->emitRefreshActions(); } void KrView::markSameBaseName() { KrViewItem* item = getCurrentKrViewItem(); if (!item) return; KRQuery query(QString("%1.*").arg(item->name(false))); changeSelection(query, true, false); } void KrView::markSameExtension() { KrViewItem* item = getCurrentKrViewItem(); if (!item) return; KRQuery query(QString("*.%1").arg(item->extension())); changeSelection(query, true, false); } diff --git a/krusader/Panel/PanelView/krview.h b/krusader/Panel/PanelView/krview.h index aa044463..1ea863f7 100644 --- a/krusader/Panel/PanelView/krview.h +++ b/krusader/Panel/PanelView/krview.h @@ -1,384 +1,385 @@ /***************************************************************************** * Copyright (C) 2000-2002 Shie Erlich * * Copyright (C) 2000-2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRVIEW_H #define KRVIEW_H // QtCore #include #include #include #include #include #include // QtGui #include #include +#include #include "krviewproperties.h" class KrView; class KrViewItem; class KrPreviews; class KrViewInstance; class DirListerInterface; typedef QList KrViewItemList; // operator can handle two ways of doing things: // 1. if the view is a widget (inherits krview and klistview for example) // 2. if the view HAS A widget (a krview-son has a member of klistview) // this is done by specifying the view and the widget in the constructor, // even if they are actually the same object (specify it twice in that case) class KrViewOperator : public QObject { Q_OBJECT public: KrViewOperator(KrView *view, QWidget *widget); ~KrViewOperator() override; KrView *view() const { return _view; } QWidget *widget() const { return _widget; } void startDrag(); void emitGotDrop(QDropEvent *e) { emit gotDrop(e); } - void emitLetsDrag(QStringList items, QPixmap icon) { emit letsDrag(items, icon); } + void emitLetsDrag(QStringList items, const QPixmap& icon) { emit letsDrag(std::move(items), icon); } void emitItemDescription(const QString &desc) { emit itemDescription(desc); } void emitContextMenu(const QPoint &point) { emit contextMenu(point); } void emitEmptyContextMenu(const QPoint &point) { emit emptyContextMenu(point); } void emitRenameItem(const QString &oldName, const QString &newName) { emit renameItem(oldName, newName); } void emitExecuted(const QString &name) { emit executed(name); } void emitGoInside(const QString &name) { emit goInside(name); } void emitNeedFocus() { emit needFocus(); } void emitMiddleButtonClicked(KrViewItem *item) { emit middleButtonClicked(item); } void emitCurrentChanged(KrViewItem *item) { emit currentChanged(item); } void emitPreviewJobStarted(KJob *job) { emit previewJobStarted(job); } void emitGoHome() { emit goHome(); } void emitDirUp() { emit dirUp(); } void emitQuickCalcSpace(KrViewItem *item) { emit quickCalcSpace(item); } void emitDefaultDeleteFiles() { emit defaultDeleteFiles(); } void emitRefreshActions() { emit refreshActions(); } void emitGoBack() { emit goBack(); } void emitGoForward() { emit goForward(); } /** * Search for an item by file name beginning at the current cursor position and set the * cursor to it. * * @param text file name to search for, can be regex * @param caseSensitive whether the search is case sensitive * @param direction @c 0 is for forward, @c 1 is for backward * @return true if there is a next/previous item matching the text, else false */ bool searchItem(const QString &text, bool caseSensitive, int direction = 0); /** * Filter view items. */ bool filterSearch(const QString &, bool); void setMassSelectionUpdate(bool upd); bool isMassSelectionUpdate() { return _massSelectionUpdate; } void settingsChanged(KrViewProperties::PropertyType properties); public slots: void emitSelectionChanged() { if (!_massSelectionUpdate) emit selectionChanged(); } void startUpdate(); void cleared(); void fileAdded(FileItem *fileitem); void fileUpdated(FileItem *newFileitem); signals: void selectionChanged(); void gotDrop(QDropEvent *e); void letsDrag(QStringList items, QPixmap icon); void itemDescription(const QString &desc); void contextMenu(const QPoint &point); void emptyContextMenu(const QPoint &point); void renameItem(const QString &oldName, const QString &newName); void executed(const QString &name); void goInside(const QString &name); void needFocus(); void middleButtonClicked(KrViewItem *item); void currentChanged(KrViewItem *item); void previewJobStarted(KJob *job); void goHome(); void defaultDeleteFiles(); void dirUp(); void quickCalcSpace(KrViewItem *item); void refreshActions(); void goBack(); void goForward(); protected slots: void saveDefaultSettings(); protected: // never delete those KrView *_view; QWidget *_widget; private: bool _massSelectionUpdate; QTimer _saveDefaultSettingsTimer; static KrViewProperties::PropertyType _changedProperties; static KrView *_changedView; }; /**************************************************************************** * READ THIS FIRST: Using the view * * You always hold a pointer to KrView, thus you can only use functions declared * in this class. If you need something else, either this class is missing something * or you are ;-) * * The functions you'd usually want: * 1) getSelectedItems - returns all selected items, or (if none) the current item. * it never returns anything which includes the "..", and thus can return an empty list! * 2) getSelectedKrViewItems - the same as (1), but returns a QValueList with KrViewItems * 3) getCurrentItem, setCurrentItem - work with QString * 4) getFirst, getNext, getPrev, getCurrentKrViewItem - all work with KrViewItems, and * used to iterate through a list of items. note that getNext and getPrev accept a pointer * to the current item (used in detailedview for safe iterating), thus your loop should be: * for (KrViewItem *it = view->getFirst(); it!=0; it = view->getNext(it)) { blah; } * 5) nameToMakeCurrent(), setNameToMakeCurrent() - work with QString * * IMPORTANT NOTE: every one who subclasses this must call initProperties() in the constructor !!! */ class KrView { friend class KrViewItem; friend class KrViewOperator; public: class IconSizes : public QVector { public: IconSizes() : QVector() { *this << 12 << 16 << 22 << 32 << 48 << 64 << 128 << 256; } }; // instantiating a new view // 1. new KrView // 2. view->init() // notes: constructor does as little as possible, setup() does the rest. esp, note that // if you need something from operator or properties, move it into setup() void init(bool enableUpdateDefaultSettings = true); KrViewInstance *instance() { return &_instance; } static const IconSizes iconSizes; protected: void initProperties(); KrViewOperator *createOperator() { return new KrViewOperator(this, _widget); } virtual void setup() = 0; /////////////////////////////////////////////////////// // Every view must implement the following functions // /////////////////////////////////////////////////////// public: // interview related functions virtual QModelIndex getCurrentIndex() = 0; virtual bool isSelected(const QModelIndex &) = 0; virtual bool ensureVisibilityAfterSelect() = 0; virtual void selectRegion(KrViewItem *, KrViewItem *, bool) = 0; virtual uint numSelected() const = 0; virtual QList selectedUrls() = 0; virtual void setSelectionUrls(const QList urls) = 0; virtual KrViewItem *getFirst() = 0; virtual KrViewItem *getLast() = 0; virtual KrViewItem *getNext(KrViewItem *current) = 0; virtual KrViewItem *getPrev(KrViewItem *current) = 0; virtual KrViewItem *getCurrentKrViewItem() = 0; virtual KrViewItem *getKrViewItemAt(const QPoint &vp) = 0; virtual KrViewItem *findItemByName(const QString &name) = 0; virtual KrViewItem *findItemByUrl(const QUrl &url) = 0; virtual QString getCurrentItem() const = 0; virtual void setCurrentItem(const QString &name, bool scrollToCurrent = true, const QModelIndex &fallbackToIndex = QModelIndex()) = 0; virtual void setCurrentKrViewItem(KrViewItem *item, bool scrollToCurrent = true) = 0; virtual void makeItemVisible(const KrViewItem *item) = 0; virtual bool isItemVisible(const KrViewItem *item) = 0; virtual void updateView() = 0; virtual void sort() = 0; virtual void refreshColors() = 0; virtual void redraw() = 0; virtual bool handleKeyEvent(QKeyEvent *e); virtual void prepareForActive() = 0; virtual void prepareForPassive() = 0; virtual void renameCurrentItem() = 0; // Rename current item. returns immediately virtual int itemsPerPage() = 0; virtual void showContextMenu(const QPoint &point = QPoint(0, 0)) = 0; protected: virtual KrViewItem *preAddItem(FileItem *fileitem) = 0; virtual void preDeleteItem(KrViewItem *item) = 0; virtual void copySettingsFrom(KrView *other) = 0; virtual void populate(const QList &fileItems, FileItem *dummy) = 0; virtual void intSetSelected(const FileItem *fileitem, bool select) = 0; virtual void clear(); void addItem(FileItem *fileItem, bool onUpdate = false); void deleteItem(const QString &name, bool onUpdate = false); void updateItem(FileItem *newFileItem); public: ////////////////////////////////////////////////////// // the following functions are already implemented, // // and normally - should NOT be re-implemented. // ////////////////////////////////////////////////////// uint numFiles() const { return _count - _numDirs; } uint numDirs() const { return _numDirs; } uint count() const { return _count; } void getSelectedItems(QStringList *names, bool fallbackToFocused = true); - void getItemsByMask(QString mask, QStringList *names, bool dirs = true, bool files = true); + void getItemsByMask(const QString& mask, QStringList *names, bool dirs = true, bool files = true); void getSelectedKrViewItems(KrViewItemList *items); void selectAllIncludingDirs() { changeSelection(KRQuery("*"), true, true); } void select(const KRQuery &filter = KRQuery("*")) { changeSelection(filter, true); } void unselect(const KRQuery &filter = KRQuery("*")) { changeSelection(filter, false); } void unselectAll() { changeSelection(KRQuery("*"), false, true); } void invertSelection(); QString nameToMakeCurrent() const { return _nameToMakeCurrent; } - void setNameToMakeCurrent(const QString name) { _nameToMakeCurrent = name; } + void setNameToMakeCurrent(const QString& name) { _nameToMakeCurrent = name; } QString firstUnmarkedBelowCurrent(const bool skipCurrent); QString statistics(); const KrViewProperties *properties() const { return _properties; } KrViewOperator *op() const { return _operator; } void showPreviews(bool show); bool previewsShown() { return _previews != nullptr; } void applySettingsToOthers(); void setFiles(DirListerInterface *files); /** * Refresh the file view items after the underlying file model changed. * * Tries to preserve current file and file selection if applicable. */ void refresh(); bool changeSelection(const KRQuery &filter, bool select); bool changeSelection(const KRQuery &filter, bool select, bool includeDirs, bool makeVisible = false); bool isFiltered(FileItem *fileitem); void setSelected(const FileItem *fileitem, bool select); ///////////////////////////////////////////////////////////// // the following functions have a default and minimalistic // // implementation, and may be re-implemented if needed // ///////////////////////////////////////////////////////////// virtual void setSortMode(KrViewProperties::ColumnType sortColumn, bool descending) { sortModeUpdated(sortColumn, descending); } const KRQuery &filterMask() const { return _properties->filterMask; } KrViewProperties::FilterSpec filter() const { return _properties->filter; } void setFilter(KrViewProperties::FilterSpec filter); - void setFilter(KrViewProperties::FilterSpec filter, FilterSettings customFilter, + void setFilter(KrViewProperties::FilterSpec filter, const FilterSettings& customFilter, bool applyToDirs); void customSelection(bool select); int defaultFileIconSize(); virtual void setFileIconSize(int size); void setDefaultFileIconSize() { setFileIconSize(defaultFileIconSize()); } void zoomIn(); void zoomOut(); // save this view's settings to be restored after restart virtual void saveSettings(KConfigGroup grp, KrViewProperties::PropertyType properties = KrViewProperties::AllProperties); inline QWidget *widget() { return _widget; } inline int fileIconSize() const { return _fileIconSize; } inline bool isFocused() const { return _focused; } QPixmap getIcon(FileItem *fileitem); void setMainWindow(QWidget *mainWindow) { _mainWindow = mainWindow; } // save this view's settings as default for new views of this type void saveDefaultSettings( KrViewProperties::PropertyType properties = KrViewProperties::AllProperties); // restore the default settings for this view type void restoreDefaultSettings(); // call this to restore this view's settings after restart - void restoreSettings(KConfigGroup grp); + void restoreSettings(const KConfigGroup& grp); void saveSelection(); void restoreSelection(); bool canRestoreSelection() { return !_savedSelection.isEmpty(); } void clearSavedSelection(); void markSameBaseName(); void markSameExtension(); // todo: what about selection modes ??? virtual ~KrView(); static QPixmap getIcon(FileItem *fileitem, bool active, int size = 0); static QPixmap processIcon(const QPixmap &icon, bool dim, const QColor &dimColor, int dimFactor, bool symlink); // Get GUI strings for file item properties static QString krPermissionText(const FileItem *fileitem); static QString permissionsText(const KrViewProperties *properties, const FileItem *fileItem); static QString sizeText(const KrViewProperties *properties, KIO::filesize_t size); static QString mimeTypeText(FileItem *fileItem); protected: KrView(KrViewInstance &instance, KConfig *cfg); virtual void doRestoreSettings(KConfigGroup grp); virtual KIO::filesize_t calcSize() = 0; virtual KIO::filesize_t calcSelectedSize() = 0; void sortModeUpdated(KrViewProperties::ColumnType sortColumn, bool descending); inline void setWidget(QWidget *w) { _widget = w; } bool drawCurrent() const; KConfig *_config; KrViewProperties *_properties; KrViewOperator *_operator; bool _focused; int _fileIconSize; private: void updatePreviews(); void saveSortMode(KConfigGroup &group); void restoreSortMode(KConfigGroup &group); KrViewInstance &_instance; DirListerInterface *_files; QWidget *_mainWindow; QWidget *_widget; QList _savedSelection; QString _nameToMakeCurrent; KrPreviews *_previews; bool _updateDefaultSettings; bool _ignoreSettingsChange; QRegExp _quickFilterMask; uint _count, _numDirs; FileItem *_dummyFileItem; }; #endif /* KRVIEW_H */ diff --git a/krusader/Panel/PanelView/krviewproperties.cpp b/krusader/Panel/PanelView/krviewproperties.cpp index cadb6149..e6322594 100644 --- a/krusader/Panel/PanelView/krviewproperties.cpp +++ b/krusader/Panel/PanelView/krviewproperties.cpp @@ -1,33 +1,35 @@ /***************************************************************************** * Copyright (C) 2017-2018 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 "krviewproperties.h" +#include + KrViewProperties::KrViewProperties(bool displayIcons, bool numericPermissions, KrViewProperties::SortOptions sortOptions, KrViewProperties::SortMethod sortMethod, bool humanReadableSize, bool localeAwareCompareIsCaseSensitive, QStringList atomicExtensions) : numericPermissions(numericPermissions), displayIcons(displayIcons), sortColumn(Name), sortOptions(sortOptions), sortMethod(sortMethod), filter(KrViewProperties::All), filterMask(KRQuery("*")), filterApplysToDirs(false), localeAwareCompareIsCaseSensitive(localeAwareCompareIsCaseSensitive), - humanReadableSize(humanReadableSize), atomicExtensions(atomicExtensions), numberOfColumns(1) + humanReadableSize(humanReadableSize), atomicExtensions(std::move(atomicExtensions)), numberOfColumns(1) { } diff --git a/krusader/Panel/dirhistoryqueue.cpp b/krusader/Panel/dirhistoryqueue.cpp index 00265ebf..f80e484f 100644 --- a/krusader/Panel/dirhistoryqueue.cpp +++ b/krusader/Panel/dirhistoryqueue.cpp @@ -1,162 +1,162 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2004-2018 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 "dirhistoryqueue.h" #include "krpanel.h" #include "PanelView/krview.h" #include "../defaults.h" #include "../krservices.h" // QtCore #include DirHistoryQueue::DirHistoryQueue(KrPanel *panel) : _panel(panel), _currentPos(0) { } DirHistoryQueue::~DirHistoryQueue() = default; void DirHistoryQueue::clear() { _urlQueue.clear(); _currentItems.clear(); _currentPos = 0; } QUrl DirHistoryQueue::currentUrl() { if(_urlQueue.count()) return _urlQueue[_currentPos]; else return QUrl(); } void DirHistoryQueue::setCurrentUrl(const QUrl &url) { if(_urlQueue.count()) _urlQueue[_currentPos] = url; } QString DirHistoryQueue::currentItem() { if(count()) return _currentItems[_currentPos]; else return QString(); } void DirHistoryQueue::saveCurrentItem() { // if the filesystem-url hasn't been refreshed yet, // avoid saving current item for the wrong url if(count() && _panel->virtualPath().matches(_urlQueue[_currentPos], QUrl::StripTrailingSlash)) _currentItems[_currentPos] = _panel->view->getCurrentItem(); } -void DirHistoryQueue::add(QUrl url, QString currentItem) +void DirHistoryQueue::add(QUrl url, const QString& currentItem) { url.setPath(QDir::cleanPath(url.path())); if(_urlQueue.isEmpty()) { _urlQueue.push_front(url); _currentItems.push_front(currentItem); return; } if(_urlQueue[_currentPos].matches(url, QUrl::StripTrailingSlash)) { _currentItems[_currentPos] = currentItem; return; } for (int i = 0; i < _currentPos; i++) { _urlQueue.pop_front(); _currentItems.pop_front(); } _currentPos = 0; // do we have room for another ? if (_urlQueue.count() > 12) { // FIXME: use user-defined size // no room - remove the oldest entry _urlQueue.pop_back(); _currentItems.pop_back(); } saveCurrentItem(); _urlQueue.push_front(url); _currentItems.push_front(currentItem); } bool DirHistoryQueue::gotoPos(int pos) { if(pos >= 0 && pos < _urlQueue.count()) { saveCurrentItem(); _currentPos = pos; return true; } return false; } bool DirHistoryQueue::goBack() { return gotoPos(_currentPos + 1); } bool DirHistoryQueue::goForward() { return gotoPos(_currentPos - 1); } void DirHistoryQueue::save(KConfigGroup cfg) { saveCurrentItem(); QList urls; foreach(const QUrl &url, _urlQueue) { // make sure no passwords are permanently stored QUrl safeUrl(url); safeUrl.setPassword(QString()); urls << safeUrl; } cfg.writeEntry("Entrys", KrServices::toStringList(urls)); // krazy:exclude=spelling cfg.writeEntry("CurrentItems", _currentItems); cfg.writeEntry("CurrentIndex", _currentPos); } -bool DirHistoryQueue::restore(KConfigGroup cfg) +bool DirHistoryQueue::restore(const KConfigGroup& cfg) { clear(); _urlQueue = KrServices::toUrlList(cfg.readEntry("Entrys", QStringList())); // krazy:exclude=spelling _currentItems = cfg.readEntry("CurrentItems", QStringList()); if(!_urlQueue.count() || _urlQueue.count() != _currentItems.count()) { clear(); return false; } _currentPos = cfg.readEntry("CurrentIndex", 0); if(_currentPos >= _urlQueue.count() || _currentPos < 0) _currentPos = 0; return true; } diff --git a/krusader/Panel/dirhistoryqueue.h b/krusader/Panel/dirhistoryqueue.h index f01a2177..19905488 100644 --- a/krusader/Panel/dirhistoryqueue.h +++ b/krusader/Panel/dirhistoryqueue.h @@ -1,79 +1,79 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2004-2018 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 DIRHISTORYQUEUE_H #define DIRHISTORYQUEUE_H // QtCore #include #include #include # include class KrPanel; class DirHistoryQueue : public QObject { Q_OBJECT public: explicit DirHistoryQueue(KrPanel *panel); ~DirHistoryQueue() override; void clear(); int currentPos() { return _currentPos; } int count() { return _urlQueue.count(); } QUrl currentUrl(); void setCurrentUrl(const QUrl &url); const QUrl &get(int pos) { return _urlQueue[pos]; } - void add(QUrl url, QString currentItem); + void add(QUrl url, const QString& currentItem); bool gotoPos(int pos); bool goBack(); bool goForward(); bool canGoBack() { return _currentPos < count() - 1; } bool canGoForward() { return _currentPos > 0; } QString currentItem(); // current item of the view void save(KConfigGroup cfg); - bool restore(KConfigGroup cfg); + bool restore(const KConfigGroup& cfg); public slots: void saveCurrentItem(); private: KrPanel* _panel; int _currentPos; QList _urlQueue; QStringList _currentItems; }; #endif diff --git a/krusader/Panel/krerrordisplay.cpp b/krusader/Panel/krerrordisplay.cpp index bdb6de9f..cda39959 100644 --- a/krusader/Panel/krerrordisplay.cpp +++ b/krusader/Panel/krerrordisplay.cpp @@ -1,67 +1,67 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 "krerrordisplay.h" #include #include "krcolorcache.h" KrErrorDisplay::KrErrorDisplay(QWidget *parent) : QLabel(parent), _currentDim(100) { setAutoFillBackground(true); _startColor = QColor(240,150,150); QPalette p(palette()); _targetColor = p.color(QPalette::Window); p.setColor(QPalette::Window, _startColor); setPalette(p); _dimTimer.setSingleShot(true); connect(&_dimTimer, &QTimer::timeout, this, &KrErrorDisplay::slotTimeout); } -void KrErrorDisplay::setText(QString text) +void KrErrorDisplay::setText(const QString& text) { QLabel::setText(text); _currentDim = 100; QPalette p(palette()); p.setColor(QPalette::Window, _startColor); setPalette(p); _dimTimer.start(5000); } void KrErrorDisplay::slotTimeout() { _currentDim -= 2; dim(); if( _currentDim > 0) _dimTimer.start(50); } void KrErrorDisplay::dim() { QPalette p(palette()); p.setColor(QPalette::Window, KrColorCache::dimColor(_startColor, _currentDim, _targetColor)); setPalette(p); } diff --git a/krusader/Panel/krerrordisplay.h b/krusader/Panel/krerrordisplay.h index c30ea6a5..4a9c4560 100644 --- a/krusader/Panel/krerrordisplay.h +++ b/krusader/Panel/krerrordisplay.h @@ -1,52 +1,52 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 KRERRORDISPLAY_H #define KRERRORDISPLAY_H // QtCore #include // QtGui #include // QtWidgets #include #include class KrErrorDisplay: public QLabel { Q_OBJECT public: explicit KrErrorDisplay(QWidget *parent); - void setText(QString text); + void setText(const QString& text); private slots: void slotTimeout(); private: void dim(); QTimer _dimTimer; QColor _startColor; QColor _targetColor; int _currentDim; }; #endif diff --git a/krusader/Panel/krlayoutfactory.cpp b/krusader/Panel/krlayoutfactory.cpp index 15e6c960..ce149e4c 100644 --- a/krusader/Panel/krlayoutfactory.cpp +++ b/krusader/Panel/krlayoutfactory.cpp @@ -1,308 +1,308 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 "krlayoutfactory.h" #include "listpanelframe.h" #include "listpanel.h" #include "../krglobal.h" // QtCore #include #include #include #include #include // QtWidgets #include #include #include #include // QtXml #include #include #include #define XMLFILE_VERSION "1.0" #define MAIN_FILE "layout.xml" #define MAIN_FILE_RC_PATH ":/" MAIN_FILE #define EXTRA_FILE_MASK "layouts/*.xml" #define DEFAULT_LAYOUT "krusader:default" bool KrLayoutFactory::_parsed = false; QDomDocument KrLayoutFactory::_mainDoc; QList KrLayoutFactory::_extraDocs; -QString KrLayoutFactory::layoutDescription(QString layoutName) +QString KrLayoutFactory::layoutDescription(const QString& layoutName) { if(layoutName == DEFAULT_LAYOUT) return i18nc("Default layout", "Default"); else if(layoutName == "krusader:compact") return i18n("Compact"); else if(layoutName == "krusader:classic") return i18n("Classic"); else return i18n("Custom layout: \"%1\"", layoutName); } bool KrLayoutFactory::parseFiles() { if (_parsed) return true; _parsed = parseResource(MAIN_FILE_RC_PATH, _mainDoc); if (!_parsed) { return false; } QStringList extraFilePaths = QStandardPaths::locateAll(QStandardPaths::DataLocation, EXTRA_FILE_MASK); foreach(const QString &path, extraFilePaths) { qWarning() << "extra file: " << path; QDomDocument doc; if (parseFile(path, doc)) _extraDocs << doc; } return true; } -bool KrLayoutFactory::parseFile(QString path, QDomDocument &doc) +bool KrLayoutFactory::parseFile(const QString& path, QDomDocument &doc) { bool success = false; QFile file(path); if (file.open(QIODevice::ReadOnly)) return parseContent(file.readAll(), path, doc); else qWarning() << "can't open" << path; return success; } -bool KrLayoutFactory::parseResource(QString path, QDomDocument &doc) +bool KrLayoutFactory::parseResource(const QString& path, QDomDocument &doc) { QResource res(path); if (res.isValid()) { QByteArray data; if (res.isCompressed()) data = qUncompress(res.data(), res.size()); else data = QByteArray(reinterpret_cast(res.data()), res.size()); return parseContent(data, path, doc); } else { qWarning() << "resource does not exist:" << path; return false; } } -bool KrLayoutFactory::parseContent(QByteArray content, QString fileName, QDomDocument &doc) +bool KrLayoutFactory::parseContent(const QByteArray& content, const QString& fileName, QDomDocument &doc) { bool success = false; QString errorMsg; if (doc.setContent(content, &errorMsg)) { QDomElement root = doc.documentElement(); if (root.tagName() == "KrusaderLayout") { QString version = root.attribute("version"); if(version == XMLFILE_VERSION) success = true; else qWarning() << fileName << "has wrong version" << version << "- required is" << XMLFILE_VERSION; } else qWarning() << "root.tagName() != \"KrusaderLayout\""; } else qWarning() << "error parsing" << fileName << ":" << errorMsg; return success; } -void KrLayoutFactory::getLayoutNames(QDomDocument doc, QStringList &names) +void KrLayoutFactory::getLayoutNames(const QDomDocument& doc, QStringList &names) { QDomElement root = doc.documentElement(); for(QDomElement e = root.firstChildElement(); ! e.isNull(); e = e.nextSiblingElement()) { if (e.tagName() == "layout") { QString name(e.attribute("name")); if (!name.isEmpty() && (name != DEFAULT_LAYOUT)) names << name; } } } QStringList KrLayoutFactory::layoutNames() { QStringList names; names << DEFAULT_LAYOUT; if (parseFiles()) { getLayoutNames(_mainDoc, names); foreach(const QDomDocument &doc, _extraDocs) getLayoutNames(doc, names); } return names; } -QDomElement KrLayoutFactory::findLayout(QDomDocument doc, QString layoutName) +QDomElement KrLayoutFactory::findLayout(const QDomDocument& doc, const QString& layoutName) { QDomElement root = doc.documentElement(); for(QDomElement e = root.firstChildElement(); ! e.isNull(); e = e.nextSiblingElement()) { if (e.tagName() == "layout" && e.attribute("name") == layoutName) return e; } return QDomElement(); } QLayout *KrLayoutFactory::createLayout(QString layoutName) { if(layoutName.isEmpty()) { KConfigGroup cg(krConfig, "PanelLayout"); layoutName = cg.readEntry("Layout", DEFAULT_LAYOUT); } QLayout *layout = nullptr; if (parseFiles()) { QDomElement layoutRoot; layoutRoot = findLayout(_mainDoc, layoutName); if (layoutRoot.isNull()) { foreach(const QDomDocument &doc, _extraDocs) { layoutRoot = findLayout(doc, layoutName); if(!layoutRoot.isNull()) break; } } if (layoutRoot.isNull()) { qWarning() << "no layout with name" << layoutName << "found"; if(layoutName != DEFAULT_LAYOUT) return createLayout(DEFAULT_LAYOUT); } else { layout = createLayout(layoutRoot, panel); } } if(layout) { foreach(const QString &name, widgets.keys()) { qWarning() << "widget" << name << "was not added to the layout"; widgets[name]->hide(); } } else qWarning() << "couldn't load layout" << layoutName; return layout; } -QBoxLayout *KrLayoutFactory::createLayout(QDomElement e, QWidget *parent) +QBoxLayout *KrLayoutFactory::createLayout(const QDomElement& e, QWidget *parent) { QBoxLayout *l = nullptr; bool horizontal = false; if(e.attribute("type") == "horizontal") { horizontal = true; l = new QHBoxLayout(); } else if(e.attribute("type") == "vertical") l = new QVBoxLayout(); else { qWarning() << "unknown layout type:" << e.attribute("type"); return nullptr; } l->setSpacing(0); l->setContentsMargins(0, 0, 0, 0); for(QDomElement child = e.firstChildElement(); ! child.isNull(); child = child.nextSiblingElement()) { if (child.tagName() == "layout") { if(QLayout *childLayout = createLayout(child, parent)) l->addLayout(childLayout); } else if(child.tagName() == "frame") { QWidget *frame = createFrame(child, parent); l->addWidget(frame); } else if(child.tagName() == "widget") { if(QWidget *w = widgets.take(child.attribute("name"))) l->addWidget(w); else qWarning() << "layout: no such widget:" << child.attribute("name"); } else if(child.tagName() == "hide_widget") { if(QWidget *w = widgets.take(child.attribute("name"))) w->hide(); else qWarning() << "layout: no such widget:" << child.attribute("name"); } else if(child.tagName() == "spacer") { if(horizontal) l->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); else l->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); } } return l; } -QWidget *KrLayoutFactory::createFrame(QDomElement e, QWidget *parent) +QWidget *KrLayoutFactory::createFrame(const QDomElement& e, QWidget *parent) { KConfigGroup cg(krConfig, "PanelLayout"); QString color = cg.readEntry("FrameColor", "default"); if(color == "default") color = e.attribute("color"); else if(color == "none") color.clear(); int shadow = -1, shape = -1; QMetaEnum shadowEnum = QFrame::staticMetaObject.enumerator(QFrame::staticMetaObject.indexOfEnumerator("Shadow")); QString cfgShadow = cg.readEntry("FrameShadow", "default"); if(cfgShadow != "default") shadow = shadowEnum.keyToValue(cfgShadow.toLatin1().data()); if(shadow < 0) shadow = shadowEnum.keyToValue(e.attribute("shadow").toLatin1().data()); QMetaEnum shapeEnum = QFrame::staticMetaObject.enumerator(QFrame::staticMetaObject.indexOfEnumerator("Shape")); QString cfgShape = cg.readEntry("FrameShape", "default"); if(cfgShape!= "default") shape = shapeEnum.keyToValue(cfgShape.toLatin1().data()); if(shape < 0) shape = shapeEnum.keyToValue(e.attribute("shape").toLatin1().data()); ListPanelFrame *frame = new ListPanelFrame(parent, color); frame->setFrameStyle(shape | shadow); frame->setAcceptDrops(true); if(QLayout *l = createLayout(e, frame)) { l->setContentsMargins(frame->frameWidth(), frame->frameWidth(), frame->frameWidth(), frame->frameWidth()); frame->setLayout(l); } QObject::connect(frame, &ListPanelFrame::dropped, panel, [=](QDropEvent *event) { qobject_cast(panel)->handleDrop(event); }); if(!color.isEmpty()) QObject::connect(qobject_cast(panel), QOverload::of(&ListPanel::refreshColors), qobject_cast(frame), &ListPanelFrame::refreshColors); return frame; } diff --git a/krusader/Panel/krlayoutfactory.h b/krusader/Panel/krlayoutfactory.h index 1fe4455a..f8f9f97c 100644 --- a/krusader/Panel/krlayoutfactory.h +++ b/krusader/Panel/krlayoutfactory.h @@ -1,63 +1,63 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 KRLAYOUTFACTORY_H #define KRLAYOUTFACTORY_H // QtCore #include #include // QtXml #include class QWidget; class QLayout; class QBoxLayout; class KrLayoutFactory { public: KrLayoutFactory(QWidget *panel, QHash &widgets) : panel(panel), widgets(widgets) {} // creates the layout and adds the widgets to it QLayout *createLayout(QString layoutName = QString()); static QStringList layoutNames(); - static QString layoutDescription(QString layoutName); + static QString layoutDescription(const QString& layoutName); private: - QBoxLayout *createLayout(QDomElement e, QWidget *parent); - QWidget *createFrame(QDomElement e, QWidget *parent); + QBoxLayout *createLayout(const QDomElement& e, QWidget *parent); + QWidget *createFrame(const QDomElement& e, QWidget *parent); static bool parseFiles(); - static bool parseFile(QString path, QDomDocument &doc); - static bool parseResource(QString path, QDomDocument &doc); - static bool parseContent(QByteArray content, QString fileName, QDomDocument &doc); - static void getLayoutNames(QDomDocument doc, QStringList &names); - static QDomElement findLayout(QDomDocument doc, QString layoutName); + static bool parseFile(const QString& path, QDomDocument &doc); + static bool parseResource(const QString& path, QDomDocument &doc); + static bool parseContent(const QByteArray& content, const QString& fileName, QDomDocument &doc); + static void getLayoutNames(const QDomDocument& doc, QStringList &names); + static QDomElement findLayout(const QDomDocument& doc, const QString& layoutName); QWidget *panel; QHash &widgets; static bool _parsed; static QDomDocument _mainDoc; static QList _extraDocs; }; #endif diff --git a/krusader/Panel/listpanel.cpp b/krusader/Panel/listpanel.cpp index 24d7e65b..94938f6b 100644 --- a/krusader/Panel/listpanel.cpp +++ b/krusader/Panel/listpanel.cpp @@ -1,1378 +1,1379 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "listpanel.h" // QtCore #include #include #include #include #include #include #include // QtGui #include #include #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "dirhistoryqueue.h" #include "krcolorcache.h" #include "krerrordisplay.h" #include "krlayoutfactory.h" #include "krpreviewpopup.h" #include "krsearchbar.h" #include "listpanelactions.h" #include "panelcontextmenu.h" #include "panelfunc.h" #include "sidebar.h" #include "viewactions.h" #include "PanelView/krview.h" #include "PanelView/krviewfactory.h" #include "PanelView/krviewitem.h" #include "../defaults.h" #include "../icon.h" #include "../krservices.h" #include "../krslots.h" #include "../krusader.h" #include "../krusaderview.h" #include "../Archive/krarchandler.h" #include "../BookMan/krbookmarkbutton.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/filesystem.h" #include "../FileSystem/krpermhandler.h" #include "../FileSystem/sizecalculator.h" #include "../Dialogs/krdialogs.h" #include "../Dialogs/krspwidgets.h" #include "../Dialogs/krsqueezedtextlabel.h" #include "../Dialogs/percentalsplitter.h" #include "../Dialogs/popularurls.h" #include "../GUI/dirhistorybutton.h" #include "../GUI/kcmdline.h" #include "../GUI/mediabutton.h" #include "../MountMan/kmountman.h" #include "../UserAction/useractionpopupmenu.h" class ActionButton : public QToolButton { public: - ActionButton(QWidget *parent, ListPanel *panel, QAction *action, QString text = QString()) : + ActionButton(QWidget *parent, ListPanel *panel, QAction *action, const QString& text = QString()) : QToolButton(parent), panel(panel), action(action) { setText(text); setAutoRaise(true); if(KConfigGroup(krConfig, "ListPanelButtons").readEntry("Icons", false) || text.isEmpty()) setIcon(action->icon()); setToolTip(action->toolTip()); } protected: void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE { panel->slotFocusOnMe(); action->trigger(); } ListPanel *panel; QAction *action; }; ///////////////////////////////////////////////////// // The list panel constructor // ///////////////////////////////////////////////////// -ListPanel::ListPanel(QWidget *parent, AbstractPanelManager *manager, KConfigGroup cfg) : +ListPanel::ListPanel(QWidget *parent, AbstractPanelManager *manager, const KConfigGroup& cfg) : QWidget(parent), KrPanel(manager, this, new ListPanelFunc(this)), panelType(-1), colorMask(255), compareMode(false), previewJob(nullptr), inlineRefreshJob(nullptr), searchBar(nullptr), cdRootButton(nullptr), cdUpButton(nullptr), sidebarButton(nullptr), sidebar(nullptr), fileSystemError(nullptr), _navigatorUrl(), _tabState(TabState::DEFAULT) { if(cfg.isValid()) panelType = cfg.readEntry("Type", -1); if (panelType == -1) panelType = defaultPanelType(); _actions = krApp->listPanelActions(); setAcceptDrops(true); QHash widgets; #define ADD_WIDGET(widget) widgets.insert(#widget, widget); // media button mediaButton = new MediaButton(this); connect(mediaButton, &MediaButton::aboutToShow, this, [=]() { slotFocusOnMe(); }); connect(mediaButton, &MediaButton::openUrl, [=](const QUrl & _t1) { func->openUrl(_t1); }); connect(mediaButton, &MediaButton::newTab, this, [=](const QUrl &url) { newTab(url); }); ADD_WIDGET(mediaButton); // status bar status = new KrSqueezedTextLabel(this); KConfigGroup group(krConfig, "Look&Feel"); status->setFont(group.readEntry("Filelist Font", _FilelistFont)); status->setAutoFillBackground(false); status->setText(""); // needed for initialization code! status->setWhatsThis(i18n("The statusbar displays information about the filesystem " "which holds your current folder: total size, free space, " "type of filesystem, etc.")); ADD_WIDGET(status); // back button backButton = new ActionButton(this, this, _actions->actHistoryBackward); ADD_WIDGET(backButton); // forward button forwardButton = new ActionButton(this, this, _actions->actHistoryForward); ADD_WIDGET(forwardButton); // ... create the history button historyButton = new DirHistoryButton(func->history, this); connect(historyButton, &DirHistoryButton::aboutToShow, this, [=]() { slotFocusOnMe(); }); connect(historyButton, &DirHistoryButton::gotoPos, func, &ListPanelFunc::historyGotoPos); ADD_WIDGET(historyButton); // bookmarks button bookmarksButton = new KrBookmarkButton(this); connect(bookmarksButton, &KrBookmarkButton::aboutToShow, this, [=]() { slotFocusOnMe(); }); connect(bookmarksButton, &KrBookmarkButton::openUrl, [=](const QUrl & _t1) { func->openUrl(_t1); }); bookmarksButton->setWhatsThis(i18n("Open menu with bookmarks. You can also add " "current location to the list, edit bookmarks " "or add subfolder to the list.")); ADD_WIDGET(bookmarksButton); // url input field urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), QUrl(), this); urlNavigator->setWhatsThis(i18n("Name of folder where you are. You can also " "enter name of desired location to move there. " "Use of Net protocols like ftp or fish is possible.")); // handle certain key events here in event filter urlNavigator->editor()->installEventFilter(this); urlNavigator->setUrlEditable(isNavigatorEditModeSet()); urlNavigator->setShowFullPath(group.readEntry("Navigator Full Path", false)); connect(urlNavigator, &KUrlNavigator::returnPressed, this, [=]() { slotFocusOnMe(); }); connect(urlNavigator, &KUrlNavigator::urlChanged, this, &ListPanel::slotNavigatorUrlChanged); connect(urlNavigator->editor()->lineEdit(), &QLineEdit::editingFinished, this, &ListPanel::resetNavigatorMode); connect(urlNavigator, &KUrlNavigator::tabRequested, this, [=](const QUrl &url) { ListPanel::newTab(url); }); connect(urlNavigator, &KUrlNavigator::urlsDropped, this, QOverload::of(&ListPanel::handleDrop)); ADD_WIDGET(urlNavigator); // toolbar QWidget * toolbar = new QWidget(this); auto * toolbarLayout = new QHBoxLayout(toolbar); toolbarLayout->setContentsMargins(0, 0, 0, 0); toolbarLayout->setSpacing(0); ADD_WIDGET(toolbar); fileSystemError = new KrErrorDisplay(this); fileSystemError->setWordWrap(true); fileSystemError->hide(); ADD_WIDGET(fileSystemError); // client area clientArea = new QWidget(this); auto *clientLayout = new QVBoxLayout(clientArea); clientLayout->setSpacing(0); clientLayout->setContentsMargins(0, 0, 0, 0); ADD_WIDGET(clientArea); // totals label totals = new KrSqueezedTextLabel(this); totals->setFont(group.readEntry("Filelist Font", _FilelistFont)); totals->setAutoFillBackground(false); totals->setWhatsThis(i18n("The totals bar shows how many files exist, " "how many selected and the bytes math")); ADD_WIDGET(totals); // free space label freeSpace = new KrSqueezedTextLabel(this); freeSpace->setFont(group.readEntry("Filelist Font", _FilelistFont)); freeSpace->setAutoFillBackground(false); freeSpace->setText(""); freeSpace->setAlignment(Qt::AlignRight | Qt::AlignVCenter); ADD_WIDGET(freeSpace); // progress indicator and cancel button for the quick calc size quickSizeCalcProgress = new QProgressBar(this); quickSizeCalcProgress->hide(); ADD_WIDGET(quickSizeCalcProgress); cancelQuickSizeCalcButton = new QToolButton(this); cancelQuickSizeCalcButton->hide(); cancelQuickSizeCalcButton->setIcon(Icon("dialog-cancel")); cancelQuickSizeCalcButton->setToolTip(i18n("Cancel directory space calculation")); ADD_WIDGET(cancelQuickSizeCalcButton); // progress indicator for the preview job previewProgress = new QProgressBar(this); previewProgress->hide(); ADD_WIDGET(previewProgress); // a cancel button for the filesystem refresh and preview job cancelProgressButton = new QToolButton(this); cancelProgressButton->hide(); cancelProgressButton->setIcon(Icon("dialog-cancel")); connect(cancelProgressButton, &QToolButton::clicked, this, &ListPanel::cancelProgress); ADD_WIDGET(cancelProgressButton); // button for changing the panel sidebar position in the panel sidebarPositionButton = new QToolButton(this); sidebarPositionButton->hide(); sidebarPositionButton->setAutoRaise(true); sidebarPositionButton->setIcon(Icon("exchange-positions")); sidebarPositionButton->setToolTip(i18n("Move Sidebar clockwise")); connect(sidebarPositionButton, &QToolButton::clicked, [this]() { // moving position clockwise setSidebarPosition((sidebarPosition() + 1) % 4); }); ADD_WIDGET(sidebarPositionButton); // a quick button to open the sidebar sidebarButton = new QToolButton(this); sidebarButton->setAutoRaise(true); sidebarButton->setIcon(Icon("arrow-up")); connect(sidebarButton, &QToolButton::clicked, this, &ListPanel::toggleSidebar); sidebarButton->setToolTip(i18n("Open the Sidebar")); ADD_WIDGET(sidebarButton); #undef ADD_WIDGET // toolbar buttons cdOtherButton = new ActionButton(toolbar, this, _actions->actCdToOther, "="); toolbarLayout->addWidget(cdOtherButton); cdUpButton = new ActionButton(toolbar, this, _actions->actDirUp, ".."); toolbarLayout->addWidget(cdUpButton); cdHomeButton = new ActionButton(toolbar, this, _actions->actHome, "~"); toolbarLayout->addWidget(cdHomeButton); cdRootButton = new ActionButton(toolbar, this, _actions->actRoot, "/"); toolbarLayout->addWidget(cdRootButton); // create the button for sync-browsing syncBrowseButton = new QToolButton(toolbar); syncBrowseButton->setIcon(Icon("kr_syncbrowse_off")); syncBrowseButton->setCheckable(true); const QString syncBrowseText = i18n("This button toggles the sync-browse mode.\n" "When active, each folder change is performed in the\n" "active and inactive panel - if possible."); syncBrowseButton->setText(syncBrowseText); syncBrowseButton->setToolTip(syncBrowseText); connect(syncBrowseButton, &QToolButton::toggled, [=](bool checked) { syncBrowseButton->setIcon( Icon(checked ? "kr_syncbrowse_on" : "kr_syncbrowse_off")); }); syncBrowseButton->setAutoRaise(true); toolbarLayout->addWidget(syncBrowseButton); setButtons(); // create a splitter to hold the view and the sidebar sidebarSplitter = new PercentalSplitter(clientArea); sidebarSplitter->setChildrenCollapsible(true); sidebarSplitter->setOrientation(Qt::Horizontal); // expand vertical if splitter orientation is horizontal QSizePolicy sizePolicy = sidebarSplitter->sizePolicy(); sizePolicy.setVerticalPolicy(QSizePolicy::Expanding); sidebarSplitter->setSizePolicy(sizePolicy); clientLayout->addWidget(sidebarSplitter); // view createView(); // search (in folder) bar searchBar = new KrSearchBar(view, clientArea); searchBar->hide(); bool top = group.readEntry("Quicksearch Position", "bottom") == "top"; clientLayout->insertWidget(top ? 0 : -1, searchBar); // create the layout KrLayoutFactory fact(this, widgets); QLayout *layout = fact.createLayout(); if(!layout) { // fallback: create a layout by ourself auto *v = new QVBoxLayout; v->setContentsMargins(0, 0, 0, 0); v->setSpacing(0); auto *h = new QHBoxLayout; h->setContentsMargins(0, 0, 0, 0); h->setSpacing(0); h->addWidget(urlNavigator); h->addWidget(toolbar); h->addStretch(); v->addLayout(h); h = new QHBoxLayout; h->setContentsMargins(0, 0, 0, 0); h->setSpacing(0); h->addWidget(mediaButton); h->addWidget(status); h->addWidget(backButton); h->addWidget(forwardButton); h->addWidget(historyButton); h->addWidget(bookmarksButton); v->addLayout(h); v->addWidget(fileSystemError); v->addWidget(clientArea); h = new QHBoxLayout; h->setContentsMargins(0, 0, 0, 0); h->setSpacing(0); h->addWidget(totals); h->addWidget(freeSpace); h->addWidget(quickSizeCalcProgress); h->addWidget(cancelQuickSizeCalcButton); h->addWidget(previewProgress); h->addWidget(cancelProgressButton); h->addWidget(sidebarButton); v->addLayout(h); layout = v; } setLayout(layout); connect(&KrColorCache::getColorCache(), &KrColorCache::colorsRefreshed, this, QOverload<>::of(&ListPanel::refreshColors)); connect(krApp, &Krusader::shutdown, this, &ListPanel::cancelProgress); } ListPanel::~ListPanel() { cancelProgress(); delete view; view = nullptr; delete func; delete status; delete bookmarksButton; delete totals; delete urlNavigator; delete cdRootButton; delete cdHomeButton; delete cdUpButton; delete cdOtherButton; delete syncBrowseButton; // delete layout; } void ListPanel::reparent(QWidget *parent, AbstractPanelManager *manager) { setParent(parent); _manager = manager; } int ListPanel::defaultPanelType() { KConfigGroup group(krConfig, "Look&Feel"); return group.readEntry("Default Panel Type", KrViewFactory::defaultViewId()); } bool ListPanel::isNavigatorEditModeSet() { KConfigGroup group(krConfig, "Look&Feel"); return group.readEntry("Navigator Edit Mode", false); } void ListPanel::createView() { view = KrViewFactory::createView(panelType, sidebarSplitter, krConfig); view->init(); view->setMainWindow(krApp); // KrViewFactory may create a different view type than requested panelType = view->instance()->id(); if(this == ACTIVE_PANEL) view->prepareForActive(); else view->prepareForPassive(); view->refreshColors(); sidebarSplitter->insertWidget(sidebarPosition() < 2 ? 1 : 0, view->widget()); view->widget()->installEventFilter(this); connect(view->op(), &KrViewOperator::quickCalcSpace, func, &ListPanelFunc::quickCalcSpace); connect(view->op(), &KrViewOperator::goHome, func, &ListPanelFunc::home); connect(view->op(), &KrViewOperator::dirUp, func, &ListPanelFunc::dirUp); connect(view->op(), &KrViewOperator::defaultDeleteFiles, func, &ListPanelFunc::defaultDeleteFiles); connect(view->op(), &KrViewOperator::middleButtonClicked, this, QOverload::of(&ListPanel::newTab)); connect(view->op(), &KrViewOperator::currentChanged, this, &ListPanel::slotCurrentChanged); connect(view->op(), &KrViewOperator::renameItem, func, QOverload::of(&ListPanelFunc::rename)); connect(view->op(), &KrViewOperator::executed, func, &ListPanelFunc::execute); connect(view->op(), &KrViewOperator::goInside, func, &ListPanelFunc::goInside); connect(view->op(), &KrViewOperator::needFocus, this, [=]() { slotFocusOnMe(); }); connect(view->op(), &KrViewOperator::selectionChanged, this, &ListPanel::slotUpdateTotals); connect(view->op(), &KrViewOperator::itemDescription, krApp, &Krusader::statusBarUpdate); connect(view->op(), &KrViewOperator::contextMenu, this, &ListPanel::popRightClickMenu); connect(view->op(), &KrViewOperator::emptyContextMenu, this, &ListPanel::popEmptyRightClickMenu); connect(view->op(), &KrViewOperator::letsDrag, this, &ListPanel::startDragging); connect(view->op(), &KrViewOperator::gotDrop, this, [this](QDropEvent *event) {handleDrop(event, true); }); connect(view->op(), &KrViewOperator::previewJobStarted, this, &ListPanel::slotPreviewJobStarted); connect(view->op(), &KrViewOperator::refreshActions, krApp->viewActions(), &ViewActions::refreshActions); connect(view->op(), &KrViewOperator::currentChanged, func->history, &DirHistoryQueue::saveCurrentItem); connect(view->op(), &KrViewOperator::goBack, func, &ListPanelFunc::historyBackward); connect(view->op(), &KrViewOperator::goForward, func, &ListPanelFunc::historyForward); view->setFiles(func->files()); func->refreshActions(); } void ListPanel::changeType(int type) { if (panelType != type) { QString current = view->getCurrentItem(); QList selection = view->selectedUrls(); bool filterApplysToDirs = view->properties()->filterApplysToDirs; KrViewProperties::FilterSpec filter = view->filter(); FilterSettings filterSettings = view->properties()->filterSettings; panelType = type; KrView *oldView = view; createView(); searchBar->setView(view); delete oldView; view->setFilter(filter, filterSettings, filterApplysToDirs); view->setSelectionUrls(selection); view->setCurrentItem(current); view->makeItemVisible(view->getCurrentKrViewItem()); } } int ListPanel::getProperties() { int props = 0; if (syncBrowseButton->isChecked()) { props |= PROP_SYNC_BUTTON_ON; } if (isLocked()) { props |= PROP_LOCKED; } else if (isPinned()) { props |= PROP_PINNED; } return props; } void ListPanel::setProperties(int prop) { syncBrowseButton->setChecked(prop & PROP_SYNC_BUTTON_ON); if (prop & PROP_LOCKED) { _tabState = TabState::LOCKED; } else if (prop & PROP_PINNED) { _tabState = TabState::PINNED; } else { _tabState = TabState::DEFAULT; } } bool ListPanel::eventFilter(QObject * watched, QEvent * e) { if(view && watched == view->widget()) { if(e->type() == QEvent::FocusIn && this != ACTIVE_PANEL && !isHidden()) slotFocusOnMe(); else if(e->type() == QEvent::ShortcutOverride) { auto *ke = static_cast(e); if(ke->key() == Qt::Key_Escape && ke->modifiers() == Qt::NoModifier) { // if the cancel refresh action has no shortcut assigned, // we need this event ourselves to cancel refresh if(_actions->actCancelRefresh->shortcut().isEmpty()) { e->accept(); return true; } } } } // handle URL navigator key events else if(watched == urlNavigator->editor()) { // override default shortcut for panel focus if(e->type() == QEvent::ShortcutOverride) { auto *ke = static_cast(e); if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) { e->accept(); // we will get the key press event now return true; } } else if(e->type() == QEvent::KeyPress) { auto *ke = static_cast(e); if ((ke->key() == Qt::Key_Down) && (ke->modifiers() == Qt::ControlModifier)) { slotFocusOnMe(); return true; } else if ((ke->key() == Qt::Key_Escape) && (ke->modifiers() == Qt::NoModifier)) { // reset navigator urlNavigator->editor()->setUrl(urlNavigator->locationUrl()); slotFocusOnMe(); return true; } } } return false; } void ListPanel::toggleSidebar() { if(!sidebar) { sidebar = new Sidebar(sidebarSplitter); // fix vertical grow of splitter (and entire window) if its content // demands more space QSizePolicy sizePolicy = sidebar->sizePolicy(); sizePolicy.setVerticalPolicy(QSizePolicy::Ignored); sidebar->setSizePolicy(sizePolicy); connect(this, &ListPanel::pathChanged, sidebar, &Sidebar::onPanelPathChange); connect(sidebar, &Sidebar::urlActivated, SLOTS, &KRslots::refresh); sidebarSplitter->insertWidget(0, sidebar); } if (sidebar->isHidden()) { if (sidebarSplitterSizes.count() > 0) { sidebarSplitter->setSizes(sidebarSplitterSizes); } else { // on the first time, resize to 50% QList lst; lst << height() / 2 << height() / 2; sidebarSplitter->setSizes(lst); } sidebar->show(); sidebarButton->setIcon(Icon("arrow-down")); sidebarButton->setToolTip(i18n("Close the Sidebar")); sidebarPositionButton->show(); } else { sidebarSplitterSizes.clear(); sidebarSplitterSizes = sidebarSplitter->sizes(); sidebar->hide(); sidebarButton->setIcon(Icon("arrow-up")); sidebarButton->setToolTip(i18n("Open the Sidebar")); sidebarPositionButton->hide(); QList lst; lst << height() << 0; sidebarSplitter->setSizes(lst); if (ACTIVE_PANEL) ACTIVE_PANEL->gui->slotFocusOnMe(); } } QString ListPanel::lastLocalPath() const { return _lastLocalPath; } void ListPanel::setButtons() { KConfigGroup group(krConfig, "Look&Feel"); mediaButton->setVisible(group.readEntry("Media Button Visible", true)); backButton->setVisible(group.readEntry("Back Button Visible", false)); forwardButton->setVisible(group.readEntry("Forward Button Visible", false)); historyButton->setVisible(group.readEntry("History Button Visible", true)); bookmarksButton->setVisible(group.readEntry("Bookmarks Button Visible", true)); if (group.readEntry("Panel Toolbar visible", _PanelToolBar)) { cdRootButton->setVisible(group.readEntry("Root Button Visible", _cdRoot)); cdHomeButton->setVisible(group.readEntry("Home Button Visible", _cdHome)); cdUpButton->setVisible(group.readEntry("Up Button Visible", _cdUp)); cdOtherButton->setVisible(group.readEntry("Equal Button Visible", _cdOther)); syncBrowseButton->setVisible(group.readEntry("SyncBrowse Button Visible", _syncBrowseButton)); } else { cdRootButton->hide(); cdHomeButton->hide(); cdUpButton->hide(); cdOtherButton->hide(); syncBrowseButton->hide(); } } void ListPanel::slotUpdateTotals() { totals->setText(view->statistics()); } void ListPanel::compareDirs(bool otherPanelToo) { // Performs a check in order to avoid that the next code is executed twice if (otherPanelToo == true) { // If both panels are showing the same directory if (_manager->currentPanel()->virtualPath() == otherPanel()->virtualPath()) { if (KMessageBox::warningContinueCancel(this, i18n("Warning: The left and the right side are showing the same folder.")) != KMessageBox::Continue) { return; } } } KConfigGroup pg(krConfig, "Private"); int compareMode = pg.readEntry("Compare Mode", 0); KConfigGroup group(krConfig, "Look&Feel"); bool selectDirs = group.readEntry("Mark Dirs", false); KrViewItem *item, *otherItem; for (item = view->getFirst(); item != nullptr; item = view->getNext(item)) { if (item->name() == "..") continue; for (otherItem = otherPanel()->view->getFirst(); otherItem != nullptr && otherItem->name() != item->name(); otherItem = otherPanel()->view->getNext(otherItem)); bool isSingle = (otherItem == nullptr), isDifferent = false, isNewer = false; if (func->getFileItem(item)->isDir() && !selectDirs) { item->setSelected(false); continue; } if (otherItem) { if (!func->getFileItem(item)->isDir()) isDifferent = otherPanel()->func->getFileItem(otherItem)->getSize() != func->getFileItem(item)->getSize(); isNewer = func->getFileItem(item)->getTime_t() > otherPanel()->func->getFileItem(otherItem)->getTime_t(); } switch (compareMode) { case 0: item->setSelected(isNewer || isSingle); break; case 1: item->setSelected(isNewer); break; case 2: item->setSelected(isSingle); break; case 3: item->setSelected(isDifferent || isSingle); break; case 4: item->setSelected(isDifferent); break; } } view->updateView(); if (otherPanelToo) otherPanel()->gui->compareDirs(false); } void ListPanel::refreshColors() { view->refreshColors(); emit refreshColors(this == ACTIVE_PANEL); } void ListPanel::slotFocusOnMe(bool focus) { if (focus && _manager->currentPanel() != this) { // ignore focus request if this panel is not shown return; } krApp->setUpdatesEnabled(false); if(focus) { emit activate(); _actions->activePanelChanged(); func->refreshActions(); slotCurrentChanged(view->getCurrentKrViewItem()); view->prepareForActive(); otherPanel()->gui->slotFocusOnMe(false); } else { // in case a new url was entered but not refreshed to, // reset url navigator to the current url setNavigatorUrl(virtualPath()); view->prepareForPassive(); } urlNavigator->setActive(focus); refreshColors(); krApp->setUpdatesEnabled(true); } // this is used to start the panel ////////////////////////////////////////////////////////////////// void ListPanel::start(const QUrl &url) { QUrl startUrl(url); if (!startUrl.isValid()) startUrl = QUrl::fromLocalFile(ROOT_DIR); _lastLocalPath = startUrl.isLocalFile() ? startUrl.path() : ROOT_DIR; func->openUrl(startUrl); setJumpBack(startUrl); } void ListPanel::slotStartUpdate(bool directoryChange) { if (inlineRefreshJob) inlineRefreshListResult(nullptr); setCursor(Qt::BusyCursor); const QUrl currentUrl = virtualPath(); if (directoryChange) { if (this == ACTIVE_PANEL) { slotFocusOnMe(); } if (currentUrl.isLocalFile()) _lastLocalPath = currentUrl.path(); setNavigatorUrl(currentUrl); emit pathChanged(currentUrl); krApp->popularUrls()->addUrl(currentUrl); searchBar->hideBar(); } if (compareMode) otherPanel()->view->refresh(); // return cursor to normal arrow setCursor(Qt::ArrowCursor); slotUpdateTotals(); } void ListPanel::updateFilesystemStats(const QString &metaInfo, const QString &fsType, KIO::filesize_t total, KIO::filesize_t free) { QString statusText, mountPoint, freeSpaceText; if (!metaInfo.isEmpty()) { statusText = metaInfo; mountPoint = freeSpaceText = ""; } else { const int perc = total == 0 ? 0 : (int)(((float)free / (float)total) * 100.0); mountPoint = func->files()->mountPoint(); statusText = i18nc("%1=free space,%2=total space,%3=percentage of usage, " "%4=mountpoint,%5=filesystem type", "%1 free out of %2 (%3%) on %4 [(%5)]", KIO::convertSize(free), KIO::convertSize(total), perc, mountPoint, fsType); freeSpaceText = " " + i18n("%1 free", KIO::convertSize(free)); } status->setText(statusText); freeSpace->setText(freeSpaceText); mediaButton->updateIcon(mountPoint); } void ListPanel::handleDrop(QDropEvent *event, bool onView) { // check what was dropped const QList urls = KUrlMimeData::urlsFromMimeData(event->mimeData()); if (urls.isEmpty()) { event->ignore(); // not for us to handle! return; } // find dropping destination QString destinationDir = ""; const bool dragFromThisPanel = event->source() == this; const KrViewItem *item = onView ? view->getKrViewItemAt(event->pos()) : nullptr; if (item) { const FileItem *file = item->getFileItem(); if (file && !file->isDir() && dragFromThisPanel) { event->ignore(); // dragging on files in same panel, ignore return; } else if (!file || file->isDir()) { // item is ".." dummy or a directory destinationDir = item->name(); } } else if (dragFromThisPanel) { event->ignore(); // dragged from this panel onto an empty spot in this panel, ignore return; } QUrl destination = QUrl(virtualPath()); destination.setPath(destination.path() + '/' + destinationDir); func->files()->dropFiles(destination, event); if(KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) { KrPanel *p = dragFromThisPanel ? this : otherPanel(); p->view->saveSelection(); p->view->unselectAll(); } } void ListPanel::handleDrop(const QUrl &destination, QDropEvent *event) { func->files()->dropFiles(destination, event); } -void ListPanel::startDragging(QStringList names, QPixmap px) +void ListPanel::startDragging(const QStringList& names, const QPixmap& px) { if (names.isEmpty()) { // avoid dragging empty urls return; } QList urls = func->files()->getUrls(names); auto *drag = new QDrag(this); auto *mimeData = new QMimeData; drag->setPixmap(px); mimeData->setUrls(urls); drag->setMimeData(mimeData); drag->start(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction); } // pops a right-click menu for items void ListPanel::popRightClickMenu(const QPoint &loc) { // run it, on the mouse location int j = QFontMetrics(font()).height() * 2; PanelContextMenu::run(QPoint(loc.x() + 5, loc.y() + j), this); } void ListPanel::popEmptyRightClickMenu(const QPoint &loc) { PanelContextMenu::run(loc, this); } QString ListPanel::getCurrentName() { QString name = view->getCurrentItem(); if (name != "..") return name; else return QString(); } QStringList ListPanel::getSelectedNames() { QStringList fileNames; view->getSelectedItems(&fileNames); return fileNames; } void ListPanel::prepareToDelete() { const bool skipCurrent = (view->numSelected() == 0); view->setNameToMakeCurrent(view->firstUnmarkedBelowCurrent(skipCurrent)); } void ListPanel::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Enter : case Qt::Key_Return : if (e->modifiers() & Qt::ControlModifier) { if (e->modifiers() & Qt::AltModifier) { FileItem *fileitem = func->files()->getFileItem(view->getCurrentKrViewItem()->name()); if (fileitem && fileitem->isDir()) newTab(fileitem->getUrl(), true); } else { SLOTS->insertFileName((e->modifiers()&Qt::ShiftModifier)!=0); } } else { e->ignore(); } break; case Qt::Key_Right : case Qt::Key_Left : if (e->modifiers() == Qt::ControlModifier) { // user pressed CTRL+Right/Left - refresh other panel to the selected path if it's a // directory otherwise as this one if ((isLeft() && e->key() == Qt::Key_Right) || (!isLeft() && e->key() == Qt::Key_Left)) { QUrl newPath; KrViewItem *it = view->getCurrentKrViewItem(); if (it->name() == "..") { newPath = KIO::upUrl(virtualPath()); } else { FileItem *v = func->getFileItem(it); // If it's a directory different from ".." if (v && v->isDir() && v->getName() != "..") { newPath = v->getUrl(); } else { // If it's a supported compressed file if (v && KRarcHandler::arcSupported(v->getMime())) { newPath = func->browsableArchivePath(v->getUrl().fileName()); } else { newPath = virtualPath(); } } } otherPanel()->func->openUrl(newPath); } else { func->openUrl(otherPanel()->virtualPath()); } return; } else e->ignore(); break; case Qt::Key_Down : if (e->modifiers() == Qt::ControlModifier) { // give the keyboard focus to the command line if (MAIN_VIEW->cmdLine()->isVisible()) MAIN_VIEW->cmdLineFocus(); else MAIN_VIEW->focusTerminalEmulator(); return; } else if (e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { // give the keyboard focus to TE MAIN_VIEW->focusTerminalEmulator(); } else e->ignore(); break; case Qt::Key_Up : if (e->modifiers() == Qt::ControlModifier) { // give the keyboard focus to the url navigator editLocation(); return; } else e->ignore(); break; case Qt::Key_Escape: cancelProgress(); break; default: // if we got this, it means that the view is not doing // the quick search thing, so send the characters to the commandline, if normal key if (e->modifiers() == Qt::NoModifier) MAIN_VIEW->cmdLine()->addText(e->text()); //e->ignore(); } } void ListPanel::showEvent(QShowEvent *e) { panelVisible(); QWidget::showEvent(e); } void ListPanel::hideEvent(QHideEvent *e) { panelHidden(); QWidget::hideEvent(e); } void ListPanel::panelVisible() { func->setPaused(false); } void ListPanel::panelHidden() { func->setPaused(true); } void ListPanel::slotPreviewJobStarted(KJob *job) { previewJob = job; connect(job, SIGNAL(percent(KJob*,ulong)), SLOT(slotPreviewJobPercent(KJob*,ulong))); connect(job, &KJob::result, this, &ListPanel::slotPreviewJobResult); cancelProgressButton->setMaximumHeight(sidebarButton->height()); cancelProgressButton->show(); previewProgress->setValue(0); previewProgress->setFormat(i18n("loading previews: %p%")); previewProgress->setMaximumHeight(cancelProgressButton->height()); previewProgress->show(); } void ListPanel::slotPreviewJobPercent(KJob* /*job*/, unsigned long percent) { previewProgress->setValue(percent); } void ListPanel::slotPreviewJobResult(KJob* /*job*/) { previewJob = nullptr; previewProgress->hide(); if(!inlineRefreshJob) cancelProgressButton->hide(); } void ListPanel::slotRefreshJobStarted(KIO::Job* job) { // disable the parts of the panel we don't want touched status->setEnabled(false); urlNavigator->setEnabled(false); cdRootButton->setEnabled(false); cdHomeButton->setEnabled(false); cdUpButton->setEnabled(false); cdOtherButton->setEnabled(false); sidebarButton->setEnabled(false); if(sidebar) sidebar->setEnabled(false); bookmarksButton->setEnabled(false); historyButton->setEnabled(false); syncBrowseButton->setEnabled(false); // connect to the job interface to provide in-panel refresh notification connect(job, &KIO::Job::infoMessage, this, &ListPanel::inlineRefreshInfoMessage); connect(job, SIGNAL(percent(KJob*,ulong)), SLOT(inlineRefreshPercent(KJob*,ulong))); connect(job, &KIO::Job::result, this, &ListPanel::inlineRefreshListResult); inlineRefreshJob = job; totals->setText(i18n(">> Reading...")); cancelProgressButton->show(); } void ListPanel::cancelProgress() { if (inlineRefreshJob) { disconnect(inlineRefreshJob, nullptr, this, nullptr); inlineRefreshJob->kill(KJob::EmitResult); inlineRefreshListResult(nullptr); } if(previewJob) { disconnect(previewJob, nullptr, this, nullptr); previewJob->kill(KJob::EmitResult); slotPreviewJobResult(nullptr); } } void ListPanel::setNavigatorUrl(const QUrl &url) { _navigatorUrl = url; urlNavigator->setLocationUrl(url); } void ListPanel::inlineRefreshPercent(KJob*, unsigned long perc) { QString msg = i18n(">> Reading: %1 % complete...", perc); totals->setText(msg); } void ListPanel::inlineRefreshInfoMessage(KJob*, const QString &msg) { totals->setText(i18n(">> Reading: %1", msg)); } void ListPanel::inlineRefreshListResult(KJob*) { if(inlineRefreshJob) disconnect(inlineRefreshJob, nullptr, this, nullptr); inlineRefreshJob = nullptr; // reenable everything status->setEnabled(true); urlNavigator->setEnabled(true); cdRootButton->setEnabled(true); cdHomeButton->setEnabled(true); cdUpButton->setEnabled(true); cdOtherButton->setEnabled(true); sidebarButton->setEnabled(true); if(sidebar) sidebar->setEnabled(true); bookmarksButton->setEnabled(true); historyButton->setEnabled(true); syncBrowseButton->setEnabled(true); if(!previewJob) cancelProgressButton->hide(); } void ListPanel::jumpBack() { func->openUrl(_jumpBackURL); } void ListPanel::setJumpBack(QUrl url) { - _jumpBackURL = url; + _jumpBackURL = std::move(url); } -void ListPanel::slotFilesystemError(QString msg) +void ListPanel::slotFilesystemError(const QString& msg) { refreshColors(); fileSystemError->setText(i18n("Error: %1", msg)); fileSystemError->show(); } void ListPanel::showButtonMenu(QToolButton *b) { if(this != ACTIVE_PANEL) slotFocusOnMe(); if(b->isHidden()) b->menu()->exec(mapToGlobal(clientArea->pos())); else b->click(); } void ListPanel::openBookmarks() { showButtonMenu(bookmarksButton); } void ListPanel::openHistory() { showButtonMenu(historyButton); } void ListPanel::openMedia() { showButtonMenu(mediaButton); } void ListPanel::rightclickMenu() { if (view->getCurrentKrViewItem()) popRightClickMenu(mapToGlobal(view->getCurrentKrViewItem()->itemRect().topLeft())); } void ListPanel::toggleSyncBrowse() { syncBrowseButton->toggle(); } void ListPanel::editLocation() { urlNavigator->setUrlEditable(true); urlNavigator->setFocus(); urlNavigator->editor()->lineEdit()->selectAll(); } void ListPanel::showSearchBar() { searchBar->showBar(KrSearchBar::MODE_SEARCH); } void ListPanel::showSearchBarSelection() { searchBar->showBar(KrSearchBar::MODE_SELECT); } void ListPanel::showSearchBarFilter() { searchBar->showBar(KrSearchBar::MODE_FILTER); } void ListPanel::saveSettings(KConfigGroup cfg, bool saveHistory) { QUrl url = virtualPath(); url.setPassword(QString()); // make sure no password is saved cfg.writeEntry("Url", url.toString()); cfg.writeEntry("Type", getType()); cfg.writeEntry("Properties", getProperties()); cfg.writeEntry("PinnedUrl", pinnedUrl().toString()); if(saveHistory) func->history->save(KConfigGroup(&cfg, "History")); view->saveSettings(KConfigGroup(&cfg, "View")); // splitter/sidebar state if (sidebar && !sidebar->isHidden()) { sidebar->saveSettings(KConfigGroup(&cfg, "PanelPopup")); cfg.writeEntry("PopupPosition", sidebarPosition()); cfg.writeEntry("SplitterSizes", sidebarSplitter->saveState()); cfg.writeEntry("PopupPage", sidebar->currentPage()); } else { cfg.deleteEntry("PopupPosition"); cfg.deleteEntry("SplitterSizes"); cfg.deleteEntry("PopupPage"); } } void ListPanel::restoreSettings(KConfigGroup cfg) { changeType(cfg.readEntry("Type", defaultPanelType())); view->restoreSettings(KConfigGroup(&cfg, "View")); // "locked" property must be set after URL path is restored! // This panel can be reused when loading a profile, // so we reset its properties before calling openUrl(). setProperties(0); _lastLocalPath = ROOT_DIR; if(func->history->restore(KConfigGroup(&cfg, "History"))) { func->refresh(); } else { QUrl url(cfg.readEntry("Url", "invalid")); if (!url.isValid()) url = QUrl::fromLocalFile(ROOT_DIR); func->openUrl(url); } setJumpBack(func->history->currentUrl()); setProperties(cfg.readEntry("Properties", 0)); if (isPinned()) { QUrl pinnedUrl(cfg.readEntry("PinnedUrl", "invalid")); if (!pinnedUrl.isValid()) { pinnedUrl = func->history->currentUrl(); } func->openUrl(pinnedUrl); setPinnedUrl(pinnedUrl); } if (cfg.hasKey("PopupPosition")) { // sidebar was visible, restore toggleSidebar(); // create and show sidebar->restoreSettings(KConfigGroup(&cfg, "PanelPopup")); setSidebarPosition(cfg.readEntry("PopupPosition", 42 /* dummy */)); sidebarSplitter->restoreState(cfg.readEntry("SplitterSizes", QByteArray())); sidebar->setCurrentPage(cfg.readEntry("PopupPage", 0)); } } void ListPanel::slotCurrentChanged(KrViewItem *item) { // update status bar if (item) krApp->statusBarUpdate(item->description()); // update sidebar; which panel to display on? Sidebar *p; if (sidebar && !sidebar->isHidden()) { p = sidebar; } else if(otherPanel()->gui->sidebar && !otherPanel()->gui->sidebar->isHidden()) { p = otherPanel()->gui->sidebar; } else { return; } p->update(item ? func->files()->getFileItem(item->name()) : nullptr); } void ListPanel::otherPanelChanged() { func->syncURL = QUrl(); } void ListPanel::getFocusCandidates(QVector &widgets) { if(urlNavigator->editor()->isVisible()) widgets << urlNavigator->editor(); if(view->widget()->isVisible()) widgets << view->widget(); if(sidebar && sidebar->isVisible()) widgets << sidebar; } void ListPanel::updateButtons() { backButton->setEnabled(func->history->canGoBack()); forwardButton->setEnabled(func->history->canGoForward()); historyButton->setEnabled(func->history->count() > 1); cdRootButton->setEnabled(!virtualPath().matches(QUrl::fromLocalFile(ROOT_DIR), QUrl::StripTrailingSlash)); cdUpButton->setEnabled(!func->files()->isRoot()); cdHomeButton->setEnabled(!func->atHome()); } void ListPanel::newTab(KrViewItem *it) { if (!it) return; else if (it->name() == "..") { newTab(KIO::upUrl(virtualPath()), true); } else if (func->getFileItem(it)->isDir()) { QUrl url = virtualPath(); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (it->name())); newTab(url, true); } } void ListPanel::newTab(const QUrl &url, bool nextToThis) { _manager->newTab(url, nextToThis ? this : nullptr); } void ListPanel::slotNavigatorUrlChanged(const QUrl &url) { if (url == _navigatorUrl) return; // this is the URL we just set ourself if (!isNavigatorEditModeSet()) { urlNavigator->setUrlEditable(false); } func->openUrl(KrServices::escapeFileUrl(url), QString(), true); } void ListPanel::resetNavigatorMode() { if (isNavigatorEditModeSet()) return; // set to "navigate" mode if url wasn't changed if (urlNavigator->uncommittedUrl().matches(virtualPath(), QUrl::StripTrailingSlash)) { // NOTE: this also sets focus to the navigator urlNavigator->setUrlEditable(false); slotFocusOnMe(); } } int ListPanel::sidebarPosition() const { int pos = sidebarSplitter->orientation() == Qt::Vertical ? 1 : 0; return pos + (qobject_cast(sidebarSplitter->widget(0)) == NULL ? 2 : 0); } void ListPanel::setSidebarPosition(int pos) { sidebarSplitter->setOrientation(pos % 2 == 0 ? Qt::Horizontal : Qt::Vertical); if ((pos < 2) != (qobject_cast(sidebarSplitter->widget(0)) != NULL)) { sidebarSplitter->insertWidget(0, sidebarSplitter->widget(1)); // swapping widgets in splitter } } void ListPanel::connectQuickSizeCalculator(SizeCalculator *sizeCalculator) { connect(sizeCalculator, &SizeCalculator::started, this, [=]() { quickSizeCalcProgress->reset(); quickSizeCalcProgress->show(); cancelQuickSizeCalcButton->show(); }); connect(cancelQuickSizeCalcButton, &QToolButton::clicked, sizeCalculator, &SizeCalculator::cancel); connect(sizeCalculator, &SizeCalculator::progressChanged, quickSizeCalcProgress, &QProgressBar::setValue); connect(sizeCalculator, &SizeCalculator::finished, this, [=]() { cancelQuickSizeCalcButton->hide(); quickSizeCalcProgress->hide(); }); } diff --git a/krusader/Panel/listpanel.h b/krusader/Panel/listpanel.h index 0a014bfc..721f3524 100644 --- a/krusader/Panel/listpanel.h +++ b/krusader/Panel/listpanel.h @@ -1,258 +1,258 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 LISTPANEL_H #define LISTPANEL_H // QtCore #include #include #include #include #include #include // QtGui #include #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include "krpanel.h" #define PROP_SYNC_BUTTON_ON 1 #define PROP_LOCKED 2 #define PROP_PINNED 4 class DirHistoryButton; class KrBookmarkButton; class KrErrorDisplay; class KrSqueezedTextLabel; class KrSearchBar; class KrView; class KrViewItem; class ListPanelActions; class ListPanelFunc; class MediaButton; class Sidebar; class SizeCalculator; class ListPanel : public QWidget, public KrPanel { friend class ListPanelFunc; Q_OBJECT public: enum TabState { DEFAULT, LOCKED, // locked tab with changeable address PINNED }; // constructor create the panel, but DOESN'T fill it with data, use start() - ListPanel(QWidget *parent, AbstractPanelManager *manager, KConfigGroup cfg = KConfigGroup()); + ListPanel(QWidget *parent, AbstractPanelManager *manager, const KConfigGroup& cfg = KConfigGroup()); ~ListPanel() override; void otherPanelChanged() Q_DECL_OVERRIDE; void start(const QUrl &url = QUrl()); void reparent(QWidget *parent, AbstractPanelManager *manager); int getType() { return panelType; } void changeType(int); bool isLocked() { return _tabState == TabState::LOCKED; } bool isPinned() { return _tabState == TabState::PINNED; } void setTabState(TabState tabState) { _tabState = tabState; } ListPanelActions *actions() { return _actions; } QString lastLocalPath() const; QString getCurrentName(); QStringList getSelectedNames(); void setButtons(); void setJumpBack(QUrl url); int getProperties(); void setProperties(int); void getFocusCandidates(QVector &widgets); void saveSettings(KConfigGroup cfg, bool saveHistory); void restoreSettings(KConfigGroup cfg); void setPinnedUrl(QUrl &pinnedUrl) { _pinnedUrl = pinnedUrl; }; QUrl pinnedUrl() const { return _pinnedUrl; }; public slots: void handleDrop(QDropEvent *event, bool onView = false); // handle drops on frame or view void handleDrop(const QUrl &destination, QDropEvent *event); // handle drops with destination void popRightClickMenu(const QPoint&); void popEmptyRightClickMenu(const QPoint &); void compareDirs(bool otherPanelToo = true); void slotFocusOnMe(bool focus = true); void slotUpdateTotals(); // react to file changes in filesystem (path change or refresh) void slotStartUpdate(bool directoryChange); void toggleSidebar(); void panelVisible(); // called when the panel becomes active void panelHidden(); // called when panel becomes inactive void refreshColors(); void cancelProgress(); // cancel filesystem refresh and/or preview (if running) void setNavigatorUrl(const QUrl &url); void openMedia(); void openHistory(); void openBookmarks(); void rightclickMenu(); void toggleSyncBrowse(); void editLocation(); void showSearchBar(); void showSearchBarSelection(); void showSearchBarFilter(); void jumpBack(); void setJumpBack() { setJumpBack(virtualPath()); } ///////////////////////// service functions - called internally //////////////////////// void prepareToDelete(); // internal use only protected: void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent*) Q_DECL_OVERRIDE { slotFocusOnMe(); } void showEvent(QShowEvent *) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *) Q_DECL_OVERRIDE; bool eventFilter(QObject * watched, QEvent * e) Q_DECL_OVERRIDE; void showButtonMenu(QToolButton *b); void createView(); void updateButtons(); static int defaultPanelType(); static bool isNavigatorEditModeSet(); // return the navigator edit mode setting protected slots: void slotCurrentChanged(KrViewItem *item); - void startDragging(QStringList, QPixmap); + void startDragging(const QStringList&, const QPixmap&); void slotPreviewJobStarted(KJob *job); void slotPreviewJobPercent(KJob *job, unsigned long percent); void slotPreviewJobResult(KJob *job); // those handle the in-panel refresh notifications void slotRefreshJobStarted(KIO::Job* job); void inlineRefreshInfoMessage(KJob* job, const QString &msg); void inlineRefreshListResult(KJob* job); void inlineRefreshPercent(KJob*, unsigned long); - void slotFilesystemError(QString msg); + void slotFilesystemError(const QString& msg); void newTab(KrViewItem *item); void newTab(const QUrl &url, bool nextToThis = false); void slotNavigatorUrlChanged(const QUrl &url); void resetNavigatorMode(); // set navigator mode after focus was lost // update filesystem meta info, disk-free and mount status void updateFilesystemStats(const QString &metaInfo, const QString &fsType, KIO::filesize_t total, KIO::filesize_t free); signals: void signalStatus(QString msg); // emitted when we need to update the status bar void pathChanged(const QUrl &url); // directory changed or refreshed void activate(); // emitted when the user changes panels void finishedDragging(); // NOTE: currently not used void refreshColors(bool active); protected: int panelType; QString _lastLocalPath; QUrl _jumpBackURL; int colorMask; bool compareMode; //FilterSpec filter; KJob *previewJob; KIO::Job *inlineRefreshJob; ListPanelActions *_actions; QPixmap currDragPix; QWidget *clientArea; QSplitter *sidebarSplitter; KUrlNavigator* urlNavigator; KrSearchBar* searchBar; QToolButton *backButton, *forwardButton; QToolButton *cdRootButton; QToolButton *cdHomeButton; QToolButton *cdUpButton; QToolButton *cdOtherButton; QToolButton *sidebarPositionButton; QToolButton *sidebarButton; Sidebar *sidebar; // lazy initialized KrBookmarkButton *bookmarksButton; KrSqueezedTextLabel *status, *totals, *freeSpace; QProgressBar *quickSizeCalcProgress; QToolButton *cancelQuickSizeCalcButton; QProgressBar *previewProgress; DirHistoryButton* historyButton; MediaButton *mediaButton; QToolButton *syncBrowseButton; QToolButton *cancelProgressButton; // for thumbnail previews and filesystem refresh KrErrorDisplay *fileSystemError; private: bool handleDropInternal(QDropEvent *event, const QString &dir); int sidebarPosition() const; // 0: West, 1: North, 2: East, 3: South void setSidebarPosition(int); void connectQuickSizeCalculator(SizeCalculator *sizeCalculator); private: QUrl _navigatorUrl; // distinguish between new user set URL and new custom set URL QUrl _pinnedUrl; // only for TabState::PINNED TabState _tabState; QList sidebarSplitterSizes; }; #endif diff --git a/krusader/Panel/listpanelframe.cpp b/krusader/Panel/listpanelframe.cpp index 5ab08240..70c61f91 100644 --- a/krusader/Panel/listpanelframe.cpp +++ b/krusader/Panel/listpanelframe.cpp @@ -1,107 +1,107 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 "listpanelframe.h" #include "krcolorcache.h" #include "../krglobal.h" // QtCore #include // QtGui #include #include #include #include -ListPanelFrame::ListPanelFrame(QWidget *parent, QString color) : QFrame(parent), color(color) +ListPanelFrame::ListPanelFrame(QWidget *parent, const QString& color) : QFrame(parent), color(color) { if(!color.isEmpty()) { colorsChanged(); refreshColors(false); setAutoFillBackground(true); connect(&KrColorCache::getColorCache(), &KrColorCache::colorsRefreshed, this, &ListPanelFrame::colorsChanged); } } void ListPanelFrame::dragEnterEvent(QDragEnterEvent *e) { if (acceptDrops()) { QList URLs = KUrlMimeData::urlsFromMimeData(e->mimeData()); e->setAccepted(!URLs.isEmpty()); } else QFrame::dragEnterEvent(e); } void ListPanelFrame::colorsChanged() { QPalette p = QGuiApplication::palette(); QColor windowForeground = p.color(QPalette::Active, QPalette::WindowText); QColor windowBackground = p.color(QPalette::Active, QPalette::Window); KConfigGroup gc(krConfig, "Colors"); QColor fgAct = getColor(gc, color + " Foreground Active", p.color(QPalette::Active, QPalette::HighlightedText), windowForeground); QColor bgAct = getColor(gc, color + " Background Active", p.color(QPalette::Active, QPalette::Highlight), windowBackground); QColor fgInact = getColor(gc, color + " Foreground Inactive", p.color(QPalette::Active, QPalette::Text), windowForeground); QColor bgInact = getColor(gc, color + " Background Inactive", p.color(QPalette::Active, QPalette::Base), windowBackground); palActive = palInactive = palette(); // --- active --- // foreground palActive.setColor(QPalette::WindowText, fgAct); palActive.setColor(QPalette::ButtonText, fgAct); // background palActive.setColor(QPalette::Window, bgAct); palActive.setColor(QPalette::Button, bgAct); // --- inactive --- // foreground palInactive.setColor(QPalette::WindowText, fgInact); palInactive.setColor(QPalette::ButtonText, fgInact); // background palInactive.setColor(QPalette::Window, bgInact); palInactive.setColor(QPalette::Button, bgInact); } void ListPanelFrame::refreshColors(bool active) { if(color.isEmpty()) return; setPalette(active ? palActive : palInactive); } -QColor ListPanelFrame::getColor(KConfigGroup &cg, QString name, const QColor &def, const QColor &kdedef) +QColor ListPanelFrame::getColor(KConfigGroup &cg, const QString& name, const QColor &def, const QColor &kdedef) { if (cg.readEntry(name, QString()) == "KDE default") return kdedef; else if(!cg.readEntry(name, QString()).isEmpty()) return cg.readEntry(name, def); return def; } diff --git a/krusader/Panel/listpanelframe.h b/krusader/Panel/listpanelframe.h index 47a9a0fb..582d65b6 100644 --- a/krusader/Panel/listpanelframe.h +++ b/krusader/Panel/listpanelframe.h @@ -1,60 +1,60 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 LISTPANELFRAME_H #define LISTPANELFRAME_H // QtGui #include // QtWidgets #include # include class QDragEnterEvent; class ListPanelFrame : public QFrame { Q_OBJECT public: - ListPanelFrame(QWidget *parent, QString color); + ListPanelFrame(QWidget *parent, const QString& color); signals: void dropped(QDropEvent*, QWidget*); /**< emitted when someone drops URL onto the frame */ protected slots: void colorsChanged(); public slots: void refreshColors(bool active); protected: - QColor getColor(KConfigGroup &cg, QString name, const QColor &def, const QColor &kdedef); + QColor getColor(KConfigGroup &cg, const QString& name, const QColor &def, const QColor &kdedef); void dropEvent(QDropEvent *e) Q_DECL_OVERRIDE { emit dropped(e, this); } void dragEnterEvent(QDragEnterEvent*) Q_DECL_OVERRIDE; QString color; QPalette palActive, palInactive; }; #endif // LISTPANELFRAME_H diff --git a/krusader/Panel/panelfunc.cpp b/krusader/Panel/panelfunc.cpp index d72acf1d..f59b64c5 100644 --- a/krusader/Panel/panelfunc.cpp +++ b/krusader/Panel/panelfunc.cpp @@ -1,1291 +1,1291 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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; 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::edit() { panel->searchBar->hideBarIfSearching(); KFileItem tmp; if (fileToCreate.isEmpty()) { QString name = panel->getCurrentName(); if (name.isNull()) return; fileToCreate = files()->getUrl(name); } tmp = KFileItem(fileToCreate); if (tmp.isDir()) { KMessageBox::sorry(krMainWindow, i18n("You cannot edit a folder")); fileToCreate = QUrl(); return; } if (!tmp.isReadable()) { KMessageBox::sorry(nullptr, i18n("No permissions to edit this file.")); fileToCreate = QUrl(); return; } KrViewer::edit(fileToCreate); fileToCreate = QUrl(); } void ListPanelFunc::editNew() { if(!fileToCreate.isEmpty()) return; // ask the user for the filename to edit fileToCreate = KChooseDir::getFile(i18n("Enter the filename to edit:"), QUrl(panel->getCurrentName()), panel->virtualPath()); if(fileToCreate.isEmpty()) return ; // the user canceled // if the file exists, edit it instead of creating a new one QFile f(fileToCreate.toLocalFile()); if(f.exists()) { edit(); } else { auto *tempFile = new QTemporaryFile; tempFile->open(); KIO::CopyJob *job = KIO::copy(QUrl::fromLocalFile(tempFile->fileName()), fileToCreate); job->setUiDelegate(nullptr); job->setDefaultPermissions(true); connect(job, &KIO::CopyJob::result, this, &ListPanelFunc::slotFileCreated); connect(job, &KIO::CopyJob::result, tempFile, &QTemporaryFile::deleteLater); } } void ListPanelFunc::slotFileCreated(KJob *job) { if(!job->error() || job->error() == KIO::ERR_FILE_ALREADY_EXIST) { KrViewer::edit(fileToCreate); if(KIO::upUrl(fileToCreate).matches(panel->virtualPath(), QUrl::StripTrailingSlash)) refresh(); else if(KIO::upUrl(fileToCreate).matches(panel->otherPanel()->virtualPath(), QUrl::StripTrailingSlash)) otherFunc()->refresh(); } else KMessageBox::sorry(krMainWindow, job->errorString()); fileToCreate = QUrl(); } 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 (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 (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 = static_cast(files()); fileSystem->remove(fileNames); } void ListPanelFunc::goInside(const QString& name) { openFileNameInternal(name, false); } -void ListPanelFunc::runCommand(QString cmd) +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, QList urls) +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(QList urls) +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 KrViewItemList items; panel->view->getSelectedKrViewItems(&items); 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/Panel/panelfunc.h b/krusader/Panel/panelfunc.h index a558c9b1..d346fa61 100644 --- a/krusader/Panel/panelfunc.h +++ b/krusader/Panel/panelfunc.h @@ -1,168 +1,168 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 PANELFUNC_H #define PANELFUNC_H // QtCore #include #include #include // QtGui #include #include #include #include class DirHistoryQueue; class FileItem; class FileSystem; class KrViewItem; class ListPanel; class SizeCalculator; class ListPanelFunc : public QObject { friend class ListPanel; Q_OBJECT public slots: void execute(const QString&); void goInside(const QString&); void openUrl(const QUrl &path, const QString& nameToMakeCurrent = QString(), bool manuallyEntered = false); void rename(const QString &oldname, const QString &newname); // actions void historyBackward(); void historyForward(); void dirUp(); void refresh(); void home(); void root(); void cdToOtherPanel(); void FTPDisconnect(); void newFTPconnection(); void terminal(); void view(); void viewDlg(); void edit(); void editNew(); // create a new textfile and edit it void moveFilesDelayed() { moveFiles(true); } void copyFilesDelayed() { copyFiles(true); } void moveFiles(bool enqueue = false) { copyFiles(enqueue, true); } void copyFiles(bool enqueue = false, bool move = false); /*! * asks the user the new directory name */ void mkdir(); /** Delete or move selected files to trash - depending on user setting. */ void defaultDeleteFiles() { defaultOrAlternativeDeleteFiles(false); } /** Delete or move selected files to trash - inverting the user setting. */ void alternativeDeleteFiles() { defaultOrAlternativeDeleteFiles(true); } /** Delete virtual files or directories in virtual filesystem. */ void removeVirtualFiles(); void rename(); void krlink(bool sym = true); void createChecksum(); void matchChecksum(); void pack(); void unpack(); void testArchive(); /** Calculate the occupied space of the currently selected items and show a dialog. */ void calcSpace(); /** Calculate the occupied space of the current item without dialog. */ void quickCalcSpace(); void properties(); void cut() { copyToClipboard(true); } void copyToClipboard(bool move = false); void pasteFromClipboard(); void syncOtherPanel(); /** Disable refresh if panel is not visible. */ void setPaused(bool paused); public: explicit ListPanelFunc(ListPanel *parent); ~ListPanelFunc() override; FileSystem* files(); // return a pointer to the filesystem QUrl virtualDirectory(); // return the current URL (simulated when panel is paused) FileItem* getFileItem(KrViewItem *item); FileItem* getFileItem(const QString& name); void refreshActions(); void redirectLink(); - void runService(const KService &service, QList urls); - void displayOpenWithDialog(QList urls); + void runService(const KService &service, const QList& urls); + void displayOpenWithDialog(const QList& urls); QUrl browsableArchivePath(const QString &); void deleteFiles(bool moveToTrash); ListPanelFunc* otherFunc(); bool atHome(); /** Ask user for confirmation before deleting files. Returns only confirmed files.*/ static QList confirmDeletion(const QList &urls, bool moveToTrash, bool virtualFS, bool showPath); protected slots: // Load the current url from history and refresh filesystem and panel to it void doRefresh(); void slotFileCreated(KJob *job); // a file has been created by editNewFile() void historyGotoPos(int pos); void clipboardChanged(QClipboard::Mode mode); // Update the directory size in view void slotSizeCalculated(const QUrl &url, KIO::filesize_t size); protected: QUrl cleanPath(const QUrl &url); bool isSyncing(const QUrl &url); // when externallyExecutable == true, the file can be executed or opened with other software void openFileNameInternal(const QString &name, bool externallyExecutable); void immediateOpenUrl(const QUrl &url); void openUrlInternal(const QUrl &url, const QString& makeCurrent, bool immediately, bool manuallyEntered); - void runCommand(QString cmd); + void runCommand(const QString& cmd); ListPanel* panel; // our ListPanel DirHistoryQueue* history; FileSystem* fileSystemP; // pointer to fileSystem. QTimer delayTimer; QUrl syncURL; QUrl fileToCreate; // file that's to be created by editNewFile() bool urlManuallyEntered; static QPointer copyToClipboardOrigin; private: void defaultOrAlternativeDeleteFiles(bool invert); bool getSelectedFiles(QStringList& args); SizeCalculator *createAndConnectSizeCalculator(const QList &urls); bool _isPaused; // do not refresh while panel is not visible bool _refreshAfterPaused; // refresh after not paused anymore QPointer _quickSizeCalculator; }; #endif diff --git a/krusader/Panel/sidebar.cpp b/krusader/Panel/sidebar.cpp index a9ad12a4..cf24251a 100644 --- a/krusader/Panel/sidebar.cpp +++ b/krusader/Panel/sidebar.cpp @@ -1,289 +1,289 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2004-2018 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 "sidebar.h" #include "krfiletreeview.h" #include "krpanel.h" #include "panelfunc.h" #include "viewactions.h" #include "../defaults.h" #include "../icon.h" #include "../Dialogs/krsqueezedtextlabel.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/filesystem.h" #include "../KViewer/diskusageviewer.h" #include "../KViewer/panelviewer.h" #include "PanelView/krview.h" #include "PanelView/krviewitem.h" // QtCore #include #include // QtWidgets #include #include #include #include Sidebar::Sidebar(QWidget *parent) : QWidget(parent), stack(nullptr), imageFilePreview(nullptr), pjob(nullptr) { auto * layout = new QGridLayout(this); layout->setContentsMargins(0, 0, 0, 0); // create the label+buttons setup dataLine = new KrSqueezedTextLabel(this); KConfigGroup lg(krConfig, "Look&Feel"); dataLine->setFont(lg.readEntry("Filelist Font", _FilelistFont)); // --- hack: setup colors to be the same as an inactive panel dataLine->setBackgroundRole(QPalette::Window); int sheight = QFontMetrics(dataLine->font()).height() + 4; dataLine->setMaximumHeight(sheight); btns = new QButtonGroup(this); btns->setExclusive(true); connect(btns, QOverload::of(&QButtonGroup::buttonClicked), this, &Sidebar::tabSelected); treeBtn = new QToolButton(this); treeBtn->setToolTip(i18n("Tree Panel: a tree view of the local file system")); treeBtn->setIcon(Icon("view-list-tree")); treeBtn->setFixedSize(20, 20); treeBtn->setCheckable(true); btns->addButton(treeBtn, Tree); previewBtn = new QToolButton(this); previewBtn->setToolTip(i18n("Preview Panel: display a preview of the current file")); previewBtn->setIcon(Icon("view-preview")); previewBtn->setFixedSize(20, 20); previewBtn->setCheckable(true); btns->addButton(previewBtn, Preview); viewerBtn = new QToolButton(this); viewerBtn->setToolTip(i18n("View Panel: view the current file")); viewerBtn->setIcon(Icon("zoom-original")); viewerBtn->setFixedSize(20, 20); viewerBtn->setCheckable(true); btns->addButton(viewerBtn, View); duBtn = new QToolButton(this); duBtn->setToolTip(i18n("Disk Usage Panel: view the usage of a folder")); duBtn->setIcon(Icon("kr_diskusage")); duBtn->setFixedSize(20, 20); duBtn->setCheckable(true); btns->addButton(duBtn, DskUsage); layout->addWidget(dataLine, 0, 0); layout->addWidget(treeBtn, 0, 1); layout->addWidget(previewBtn, 0, 2); layout->addWidget(viewerBtn, 0, 3); layout->addWidget(duBtn, 0, 4); // create a widget stack on which to put the parts stack = new QStackedWidget(this); // create the tree part ---------- tree = new KrFileTreeView(stack); tree->setProperty("KrusaderWidgetId", QVariant(Tree)); stack->addWidget(tree); // NOTE: the F2 key press event is caught before it gets to the tree tree->setEditTriggers(QAbstractItemView::EditKeyPressed); // connecting signal to signal connect(tree, &KrFileTreeView::urlActivated, this, &Sidebar::urlActivated); // create the quickview part ------ imageFilePreview = new KImageFilePreview(stack); imageFilePreview->setProperty("KrusaderWidgetId", QVariant(Preview)); stack->addWidget(imageFilePreview); // create the panelview fileViewer = new PanelViewer(stack); fileViewer->setProperty("KrusaderWidgetId", QVariant(View)); // kparts demand too much width QSizePolicy sizePolicy = fileViewer->sizePolicy(); sizePolicy.setHorizontalPolicy(QSizePolicy::Ignored); fileViewer->setSizePolicy(sizePolicy); stack->addWidget(fileViewer); connect(fileViewer, &PanelViewer::openUrlRequest, this, &Sidebar::handleOpenUrlRequest); // create the disk usage view diskusage = new DiskUsageViewer(stack); diskusage->setStatusLabel(dataLine, i18n("Disk Usage:")); diskusage->setProperty("KrusaderWidgetId", QVariant(DskUsage)); stack->addWidget(diskusage); connect(diskusage, &DiskUsageViewer::openUrlRequest, this, &Sidebar::handleOpenUrlRequest); // -------- finish the layout (General one) layout->addWidget(stack, 1, 0, 1, 5); hide(); // for not to open the 3rd hand tool at start (selecting the last used tab) setCurrentPage(0); } Sidebar::~Sidebar() = default; -void Sidebar::saveSettings(KConfigGroup cfg) const +void Sidebar::saveSettings(const KConfigGroup& cfg) const { tree->saveSettings(cfg); } void Sidebar::restoreSettings(const KConfigGroup &cfg) { tree->restoreSettings(cfg); } void Sidebar::setCurrentPage(int id) { QAbstractButton * curr = btns->button(id); if (curr) { curr->click(); } } void Sidebar::show() { QWidget::show(); tabSelected(currentPage()); } void Sidebar::hide() { QWidget::hide(); if (currentPage() == View) fileViewer->closeUrl(); if (currentPage() == DskUsage) diskusage->closeUrl(); } void Sidebar::focusInEvent(QFocusEvent*) { switch (currentPage()) { case Preview: if (!isHidden()) imageFilePreview->setFocus(); break; case View: if (!isHidden() && fileViewer->part() && fileViewer->part()->widget()) fileViewer->part()->widget()->setFocus(); break; case DskUsage: if (!isHidden() && diskusage->getWidget() && diskusage->getWidget()->currentWidget()) diskusage->getWidget()->currentWidget()->setFocus(); break; case Tree: if (!isHidden()) tree->setFocus(); break; } } void Sidebar::handleOpenUrlRequest(const QUrl &url) { QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(url); if (mime.isValid() && mime.name() == "inode/directory") ACTIVE_PANEL->func->openUrl(url); } void Sidebar::tabSelected(int id) { QUrl url; const FileItem *fileitem = nullptr; if (ACTIVE_PANEL && ACTIVE_PANEL->view) fileitem = ACTIVE_PANEL->func->files()->getFileItem(ACTIVE_PANEL->view->getCurrentItem()); if(fileitem) url = fileitem->getUrl(); // if tab is tree, set something logical in the data line switch (id) { case Tree: stack->setCurrentWidget(tree); dataLine->setText(i18n("Tree:")); if (!isHidden()) tree->setFocus(); if (ACTIVE_PANEL) tree->setCurrentUrl(ACTIVE_PANEL->virtualPath()); break; case Preview: stack->setCurrentWidget(imageFilePreview); dataLine->setText(i18n("Preview:")); update(fileitem); break; case View: stack->setCurrentWidget(fileViewer); dataLine->setText(i18n("View:")); update(fileitem); if (!isHidden() && fileViewer->part() && fileViewer->part()->widget()) fileViewer->part()->widget()->setFocus(); break; case DskUsage: stack->setCurrentWidget(diskusage); dataLine->setText(i18n("Disk Usage:")); update(fileitem); if (!isHidden() && diskusage->getWidget() && diskusage->getWidget()->currentWidget()) diskusage->getWidget()->currentWidget()->setFocus(); break; } if (id != View) fileViewer->closeUrl(); } // decide which part to update, if at all void Sidebar::update(const FileItem *fileitem) { if (isHidden()) return; QUrl url; if(fileitem) url = fileitem->getUrl(); switch (currentPage()) { case Preview: imageFilePreview->showPreview(url); dataLine->setText(i18n("Preview: %1", url.fileName())); break; case View: if(fileitem && !fileitem->isDir() && fileitem->isReadable()) fileViewer->openUrl(fileitem->getUrl()); else fileViewer->closeUrl(); dataLine->setText(i18n("View: %1", url.fileName())); break; case DskUsage: { if(fileitem && !fileitem->isDir()) url = KIO::upUrl(url); dataLine->setText(i18n("Disk Usage: %1", url.fileName())); diskusage->openUrl(url); } break; case Tree: // nothing to do break; } } void Sidebar::onPanelPathChange(const QUrl &url) { switch (currentPage()) { case Tree: if (url.isLocalFile()) { tree->setCurrentUrl(url); // synchronize panel path with tree path } break; } } diff --git a/krusader/Panel/sidebar.h b/krusader/Panel/sidebar.h index cc5610a0..f94e2556 100644 --- a/krusader/Panel/sidebar.h +++ b/krusader/Panel/sidebar.h @@ -1,102 +1,102 @@ /***************************************************************************** * Copyright (C) 2003 Shie Erlich * * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2004-2018 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 SIDEBAR_H #define SIDEBAR_H // QtCore #include // QtWidgets #include #include #include #include #include #include #include class KrSqueezedTextLabel; class PanelViewer; class DiskUsageViewer; class KrFileTreeView; class FileItem; /** * Additional side widget showing various meta information for the current file/directories. */ class Sidebar: public QWidget { Q_OBJECT enum Parts { /** Folder tree view */ Tree, /** Preview image for current file/directory */ Preview, /** File view: show file in most appropriate, read-only editor */ View, /** Disk usage for current directory structure */ DskUsage, /** Dummy */ Last = 0xFF }; public: explicit Sidebar(QWidget *parent); ~Sidebar() override; inline int currentPage() const { return stack->currentWidget()->property("KrusaderWidgetId").toInt(); } - void saveSettings(KConfigGroup cfg) const; + void saveSettings(const KConfigGroup& cfg) const; void restoreSettings(const KConfigGroup &cfg); void setCurrentPage(int); public slots: void update(const FileItem *fileitem); void onPanelPathChange(const QUrl &url); void show(); void hide(); signals: void urlActivated(const QUrl &url); void hideMe(); protected slots: void tabSelected(int id); void handleOpenUrlRequest(const QUrl &url); protected: void focusInEvent(QFocusEvent*) Q_DECL_OVERRIDE; bool _hidden; QStackedWidget *stack; KImageFilePreview *imageFilePreview; KrSqueezedTextLabel *dataLine; QPointer pjob; KrFileTreeView *tree; QToolButton *treeBtn, *previewBtn, *viewerBtn, *duBtn; QButtonGroup *btns; PanelViewer *fileViewer; DiskUsageViewer *diskusage; }; #endif diff --git a/krusader/Search/krsearchdialog.cpp b/krusader/Search/krsearchdialog.cpp index 7b159d00..77390f36 100644 --- a/krusader/Search/krsearchdialog.cpp +++ b/krusader/Search/krsearchdialog.cpp @@ -1,661 +1,661 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 "krsearchdialog.h" // QtCore #include #include // QtGui #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include "krsearchmod.h" #include "../Dialogs/krdialogs.h" #include "../Dialogs/krspecialwidgets.h" #include "../Dialogs/krsqueezedtextlabel.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/krquery.h" #include "../FileSystem/virtualfilesystem.h" #include "../Filter/filtertabs.h" #include "../Filter/generalfilter.h" #include "../KViewer/krviewer.h" #include "../Panel/PanelView/krview.h" #include "../Panel/PanelView/krviewfactory.h" #include "../Panel/PanelView/krviewitem.h" #include "../Panel/krpanel.h" #include "../Panel/krsearchbar.h" #include "../Panel/panelfunc.h" #include "../defaults.h" #include "../kractions.h" #include "../krglobal.h" #include "../filelisticon.h" #include "../krservices.h" #include "../krslots.h" #include "../krusaderview.h" #include "../panelmanager.h" #define RESULTVIEW_TYPE 0 class SearchResultContainer : public DirListerInterface { public: explicit SearchResultContainer(QObject *parent) : DirListerInterface(parent) {} ~SearchResultContainer() override { clear(); } QList fileItems() const Q_DECL_OVERRIDE { return _fileItems; } unsigned long numFileItems() const Q_DECL_OVERRIDE { return _fileItems.count(); } bool isRoot() const Q_DECL_OVERRIDE { return true; } void clear() { emit cleared(); foreach(FileItem *fileitem, _fileItems) delete fileitem; _fileItems.clear(); _foundText.clear(); } void addItem(const FileItem &file, const QString &foundText) { const QString path = file.getUrl().toDisplayString(QUrl::PreferLocalFile); FileItem *fileitem = FileItem::createCopy(file, path); _fileItems << fileitem; if(!foundText.isEmpty()) _foundText[fileitem] = foundText; emit addedFileItem(fileitem); } QString foundText(const FileItem *fileitem) { return _foundText[fileitem]; } private: QList _fileItems; QHash _foundText; }; KrSearchDialog *KrSearchDialog::SearchDialog = nullptr; QString KrSearchDialog::lastSearchText = QString('*'); int KrSearchDialog::lastSearchType = 0; bool KrSearchDialog::lastSearchForCase = false; bool KrSearchDialog::lastContainsWholeWord = false; bool KrSearchDialog::lastContainsWithCase = false; bool KrSearchDialog::lastSearchInSubDirs = true; bool KrSearchDialog::lastSearchInArchives = false; bool KrSearchDialog::lastFollowSymLinks = false; bool KrSearchDialog::lastContainsRegExp = false; // class starts here ///////////////////////////////////////// -KrSearchDialog::KrSearchDialog(QString profile, QWidget* parent) +KrSearchDialog::KrSearchDialog(const QString& profile, QWidget* parent) : QDialog(parent), query(nullptr), searcher(nullptr), isBusy(false), closed(false) { KConfigGroup group(krConfig, "Search"); setWindowTitle(i18n("Krusader::Search")); setWindowIcon(Icon("system-search")); auto* searchBaseLayout = new QGridLayout(this); searchBaseLayout->setSpacing(6); searchBaseLayout->setContentsMargins(11, 11, 11, 11); // creating the dialog buttons ( Search, Stop, Close ) auto* buttonsLayout = new QHBoxLayout(); buttonsLayout->setSpacing(6); buttonsLayout->setContentsMargins(0, 0, 0, 0); profileManager = new ProfileManager("SearcherProfile", this); buttonsLayout->addWidget(profileManager); searchTextToClipboard = new QCheckBox(this); searchTextToClipboard->setText(i18n("Query to clipboard")); searchTextToClipboard->setToolTip(i18n("Place search text to clipboard when a found file is opened.")); searchTextToClipboard->setCheckState(static_cast(group.readEntry("QueryToClipboard", 0))); connect(searchTextToClipboard, &QCheckBox::stateChanged, this, [=](int state) { KConfigGroup group(krConfig, "Search"); group.writeEntry("QueryToClipboard", state); }); buttonsLayout->addWidget(searchTextToClipboard); auto* spacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); buttonsLayout->addItem(spacer); mainSearchBtn = new QPushButton(this); mainSearchBtn->setText(i18n("Search")); mainSearchBtn->setIcon(Icon("edit-find")); mainSearchBtn->setDefault(true); buttonsLayout->addWidget(mainSearchBtn); mainStopBtn = new QPushButton(this); mainStopBtn->setEnabled(false); mainStopBtn->setText(i18n("Stop")); mainStopBtn->setIcon(Icon("process-stop")); buttonsLayout->addWidget(mainStopBtn); mainCloseBtn = new QPushButton(this); mainCloseBtn->setText(i18n("Close")); mainCloseBtn->setIcon(Icon("dialog-close")); buttonsLayout->addWidget(mainCloseBtn); searchBaseLayout->addLayout(buttonsLayout, 1, 0); // creating the searcher tabs searcherTabs = new QTabWidget(this); filterTabs = FilterTabs::addTo(searcherTabs, FilterTabs::Default); generalFilter = (GeneralFilter *)filterTabs->get("GeneralFilter"); QWidget* resultTab = new QWidget(searcherTabs); auto* resultLayout = new QGridLayout(resultTab); resultLayout->setSpacing(6); resultLayout->setContentsMargins(6, 6, 6, 6); // creating the result tab // creating the result list view result = new SearchResultContainer(this); // the view resultView = KrViewFactory::createView(RESULTVIEW_TYPE, resultTab, krConfig); resultView->init(false); resultView->restoreSettings(KConfigGroup(&group, "ResultView")); resultView->setMainWindow(this); resultView->prepareForActive(); resultView->refreshColors(); resultView->setFiles(result); resultView->refresh(); resultLayout->addWidget(resultView->widget(), 0, 0); // search bar - hidden by default searchBar = new KrSearchBar(resultView, this); searchBar->hide(); resultLayout->addWidget(searchBar, 1, 0); // text found row foundTextFrame = new QFrame(resultTab); foundTextFrame->setFrameShape(QLabel::StyledPanel); foundTextFrame->setFrameShadow(QLabel::Sunken); auto* foundTextLayout = new QHBoxLayout(); foundTextLayout->setSpacing(6); QLabel *textFoundLabel = new QLabel(i18n("Text found:"), resultTab); foundTextLayout->addWidget(textFoundLabel); foundTextLabel = new KrSqueezedTextLabel(resultTab); foundTextLayout->addWidget(foundTextLabel); foundTextFrame->setLayout(foundTextLayout); foundTextFrame->hide(); resultLayout->addWidget(foundTextFrame, 2, 0); // result info row auto* resultLabelLayout = new QHBoxLayout(); resultLabelLayout->setSpacing(6); resultLabelLayout->setContentsMargins(0, 0, 0, 0); searchingLabel = new KSqueezedTextLabel(i18n("Idle"), resultTab); resultLabelLayout->addWidget(searchingLabel); foundLabel = new QLabel("", resultTab); resultLabelLayout->addWidget(foundLabel); mainFeedToListBoxBtn = new QPushButton(QIcon::fromTheme("list-add"), i18n("Feed to listbox"), this); mainFeedToListBoxBtn->setEnabled(false); resultLabelLayout->addWidget(mainFeedToListBoxBtn); resultLayout->addLayout(resultLabelLayout, 3, 0); searcherTabs->addTab(resultTab, i18n("&Results")); searchBaseLayout->addWidget(searcherTabs, 0, 0); // signals and slots connections connect(mainSearchBtn, &QPushButton::clicked, this, &KrSearchDialog::startSearch); connect(mainStopBtn, &QPushButton::clicked, this, &KrSearchDialog::stopSearch); connect(mainCloseBtn, &QPushButton::clicked, this, &KrSearchDialog::closeDialog); connect(mainFeedToListBoxBtn, &QPushButton::clicked, this, &KrSearchDialog::feedToListBox); connect(profileManager, &ProfileManager::loadFromProfile, filterTabs, &FilterTabs::loadFromProfile); connect(profileManager, &ProfileManager::saveToProfile, filterTabs, &FilterTabs::saveToProfile); connect(resultView->op(), &KrViewOperator::currentChanged, this, &KrSearchDialog::currentChanged); connect(resultView->op(), &KrViewOperator::executed, this, &KrSearchDialog::executed); connect(resultView->op(), &KrViewOperator::contextMenu, this, &KrSearchDialog::contextMenu); // tab order setTabOrder(mainSearchBtn, mainCloseBtn); setTabOrder(mainCloseBtn, mainStopBtn); setTabOrder(mainStopBtn, searcherTabs); setTabOrder(searcherTabs, resultView->widget()); sizeX = group.readEntry("Window Width", -1); sizeY = group.readEntry("Window Height", -1); if (sizeX != -1 && sizeY != -1) resize(sizeX, sizeY); if (group.readEntry("Window Maximized", false)) showMaximized(); else show(); generalFilter->searchFor->setFocus(); // finally, load a profile of apply defaults: if (profile.isEmpty()) { // load the last used values generalFilter->searchFor->setEditText(lastSearchText); generalFilter->searchFor->lineEdit()->selectAll(); generalFilter->ofType->setCurrentIndex(lastSearchType); generalFilter->searchForCase->setChecked(lastSearchForCase); generalFilter->containsWholeWord->setChecked(lastContainsWholeWord); generalFilter->containsTextCase->setChecked(lastContainsWithCase); generalFilter->containsRegExp->setChecked(lastContainsRegExp); generalFilter->searchInDirs->setChecked(lastSearchInSubDirs); generalFilter->searchInArchives->setChecked(lastSearchInArchives); generalFilter->followLinks->setChecked(lastFollowSymLinks); // the path in the active panel should be the default search location generalFilter->searchIn->lineEdit()->setText(ACTIVE_PANEL->virtualPath().toDisplayString(QUrl::PreferLocalFile)); } else profileManager->loadProfile(profile); // important: call this _after_ you've connected profileManager ot the loadFromProfile!! } KrSearchDialog::~KrSearchDialog() { delete query; query = nullptr; delete resultView; resultView = nullptr; } void KrSearchDialog::closeDialog(bool isAccept) { if(isBusy) { closed = true; return; } // stop the search if it's on-going if (searcher != nullptr) { delete searcher; searcher = nullptr; } // saving the searcher state KConfigGroup group(krConfig, "Search"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); resultView->saveSettings(KConfigGroup(&group, "ResultView")); lastSearchText = generalFilter->searchFor->currentText(); lastSearchType = generalFilter->ofType->currentIndex(); lastSearchForCase = generalFilter->searchForCase->isChecked(); lastContainsWholeWord = generalFilter->containsWholeWord->isChecked(); lastContainsWithCase = generalFilter->containsTextCase->isChecked(); lastContainsRegExp = generalFilter->containsRegExp->isChecked(); lastSearchInSubDirs = generalFilter->searchInDirs->isChecked(); lastSearchInArchives = generalFilter->searchInArchives->isChecked(); lastFollowSymLinks = generalFilter->followLinks->isChecked(); hide(); SearchDialog = nullptr; if (isAccept) QDialog::accept(); else QDialog::reject(); this->deleteLater(); } void KrSearchDialog::reject() { closeDialog(false); } void KrSearchDialog::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } } void KrSearchDialog::slotFound(const FileItem &file, const QString &foundText) { result->addItem(file, foundText); foundLabel->setText(i18np("Found %1 match.", "Found %1 matches.", result->numFileItems())); } bool KrSearchDialog::gui2query() { // prepare the query ... /////////////////// names, locations and greps if (query != nullptr) { delete query; query = nullptr; } query = new KRQuery(); return filterTabs->fillQuery(query); } void KrSearchDialog::startSearch() { if(isBusy) return; // prepare the query ///////////////////////////////////////////// if (!gui2query()) return; // first, informative messages if (query->searchInArchives()) { KMessageBox::information(this, i18n("Since you chose to also search in archives, " "note the following limitations:\n" "You cannot search for text (grep) while doing" " a search that includes archives."), nullptr, "searchInArchives"); } // prepare the gui /////////////////////////////////////////////// result->clear(); resultView->setSortMode(KrViewProperties::NoColumn, 0); foundTextFrame->setVisible(!query->content().isEmpty()); foundTextLabel->setText(""); searchingLabel->setText(""); foundLabel->setText(i18n("Found 0 matches.")); mainFeedToListBoxBtn->setEnabled(false); mainSearchBtn->setEnabled(false); mainStopBtn->setEnabled(true); mainCloseBtn->setEnabled(false); searcherTabs->setCurrentIndex(2); // show the results page isBusy = true; qApp->processEvents(); // start the search. if (searcher != nullptr) abort(); searcher = new KRSearchMod(query); connect(searcher, &KRSearchMod::searching, searchingLabel, &KSqueezedTextLabel::setText); connect(searcher, &KRSearchMod::found, this, &KrSearchDialog::slotFound); connect(searcher, &KRSearchMod::finished, this, &KrSearchDialog::stopSearch); searcher->start(); isBusy = false; delete searcher; searcher = nullptr; // gui stuff mainSearchBtn->setEnabled(true); mainCloseBtn->setEnabled(true); mainStopBtn->setEnabled(false); if (result->numFileItems()) mainFeedToListBoxBtn->setEnabled(true); searchingLabel->setText(i18n("Finished searching.")); if (closed) closeDialog(); } void KrSearchDialog::stopSearch() { if (searcher != nullptr) { searcher->stop(); disconnect(searcher, nullptr, nullptr, nullptr); } } void KrSearchDialog::executed(const QString &name) { // 'name' is (local) file path or complete URL QString path = name; QString fileName; if(!name.endsWith('/')) { // not a directory, split filename and path int idx = name.lastIndexOf("/"); fileName = name.mid(idx+1); path = name.left(idx); } QUrl url(path); if (url.scheme().isEmpty()) { url = QUrl::fromLocalFile(path); } ACTIVE_FUNC->openUrl(url, fileName); showMinimized(); } void KrSearchDialog::currentChanged(KrViewItem *item) { if(!item) return; QString text = result->foundText(item->getFileItem()); if(!text.isEmpty()) { // ugly hack: find the and in the text, then // use it to set the are which we don't want squeezed int idx = text.indexOf("") - 4; // take "" into account int length = text.indexOf("") - idx + 4; foundTextLabel->setText(text, idx, length); } } void KrSearchDialog::closeEvent(QCloseEvent *e) { /* if searching is in progress we must not close the window */ if (isBusy) /* because qApp->processEvents() is called by the searcher and */ { /* at window destruction, the searcher object will be deleted */ stopSearch(); /* instead we stop searching */ closed = true; /* and after stopping: startSearch can close the window */ e->ignore(); /* ignoring the close event */ } else QDialog::closeEvent(e); /* if no searching, let QDialog handle the event */ } void KrSearchDialog::keyPressEvent(QKeyEvent *e) { // TODO: don't use hardcoded shortcuts if (isBusy && e->key() == Qt::Key_Escape) { /* at searching we must not close the window */ stopSearch(); /* so we simply stop searching */ return; } if (resultView->widget()->hasFocus()) { if ((e->key() | e->modifiers()) == (Qt::CTRL | Qt::Key_I)) { searchBar->showBar(KrSearchBar::MODE_FILTER); } else if (e->key() == Qt::Key_F4) { tryPlaceSearchQueryToClipboard(); editCurrent(); return; } else if (e->key() == Qt::Key_F3) { tryPlaceSearchQueryToClipboard(); viewCurrent(); return; } else if (e->key() == Qt::Key_F10) { compareByContent(); return; } else if (KrGlobal::copyShortcut == QKeySequence(e->key() | e->modifiers())) { copyToClipBoard(); return; } } QDialog::keyPressEvent(e); } void KrSearchDialog::editCurrent() { KrViewItem *current = resultView->getCurrentKrViewItem(); if (current) KrViewer::edit(current->getFileItem()->getUrl(), this); } void KrSearchDialog::viewCurrent() { KrViewItem *current = resultView->getCurrentKrViewItem(); if (current) KrViewer::view(current->getFileItem()->getUrl(), this); } void KrSearchDialog::compareByContent() { KrViewItemList list; resultView->getSelectedKrViewItems(&list); if (list.count() != 2) return; SLOTS->compareContent(list[0]->getFileItem()->getUrl(),list[1]->getFileItem()->getUrl()); } void KrSearchDialog::contextMenu(const QPoint &pos) { // create the menu QMenu popup; popup.setTitle(i18n("Krusader Search")); QAction *actView = popup.addAction(i18n("View File (F3)")); QAction *actEdit = popup.addAction(i18n("Edit File (F4)")); QAction *actComp = popup.addAction(i18n("Compare by content (F10)")); if(resultView->numSelected() != 2) actComp->setEnabled(false); QAction *actClip = popup.addAction(i18n("Copy selected to clipboard")); QAction *result = popup.exec(pos); // check out the user's option if (result == actView) viewCurrent(); else if (result == actEdit) editCurrent(); else if (result == actClip) copyToClipBoard(); else if (result == actComp) compareByContent(); } void KrSearchDialog::feedToListBox() { VirtualFileSystem virtFilesystem; virtFilesystem.scanDir(QUrl::fromLocalFile("/")); KConfigGroup group(krConfig, "Search"); int listBoxNum = group.readEntry("Feed To Listbox Counter", 1); QString queryName; if(query) { const QString where = KrServices::toStringList(query->searchInDirs()).join(", "); queryName = query->content().isEmpty() ? i18n("Search results for \"%1\" in %2", query->nameFilter(), where) : i18n("Search results for \"%1\" containing \"%2\" in %3", query->nameFilter(), query->content(), where); } QString fileSystemName; do { fileSystemName = i18n("Search results") + QString(" %1").arg(listBoxNum++); } while (virtFilesystem.getFileItem(fileSystemName) != nullptr); group.writeEntry("Feed To Listbox Counter", listBoxNum); KConfigGroup ga(krConfig, "Advanced"); if (ga.readEntry("Confirm Feed to Listbox", _ConfirmFeedToListbox)) { bool ok; fileSystemName = QInputDialog::getText(this, i18n("Query name"), i18n("Here you can name the file collection"), QLineEdit::Normal, fileSystemName, &ok); if (! ok) return; } QList urlList; foreach(FileItem *fileitem, result->fileItems()) urlList.push_back(fileitem->getUrl()); mainSearchBtn->setEnabled(false); mainCloseBtn->setEnabled(false); mainFeedToListBoxBtn->setEnabled(false); isBusy = true; const QUrl url = QUrl(QString("virt:/") + fileSystemName); virtFilesystem.scanDir(url); virtFilesystem.addFiles(urlList); virtFilesystem.setMetaInformation(queryName); //ACTIVE_FUNC->openUrl(url); ACTIVE_MNG->slotNewTab(url); isBusy = false; closeDialog(); } void KrSearchDialog::copyToClipBoard() { QList urls; foreach(FileItem *fileitem, result->fileItems()) urls.push_back(fileitem->getUrl()); if (urls.count() == 0) return; auto *mimeData = new QMimeData; mimeData->setImageData(FileListIcon("file").pixmap()); mimeData->setUrls(urls); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } void KrSearchDialog::tryPlaceSearchQueryToClipboard() { if (searchTextToClipboard->isChecked() && !generalFilter->containsText->currentText().isEmpty() && QApplication::clipboard()->text() != generalFilter->containsText->currentText()) { QApplication::clipboard()->setText(generalFilter->containsText->currentText()); } } diff --git a/krusader/Search/krsearchdialog.h b/krusader/Search/krsearchdialog.h index 0d3d3b4d..0e85e853 100644 --- a/krusader/Search/krsearchdialog.h +++ b/krusader/Search/krsearchdialog.h @@ -1,130 +1,130 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 KRSEARCHDIALOG_H #define KRSEARCHDIALOG_H // QtCore #include #include // QtWidgets #include #include #include #include class FileItem; class FilterTabs; class GeneralFilter; class KrSearchBar; class KrSqueezedTextLabel; class KrView; class KrViewItem; class KRQuery; class KRSearchMod; class KSqueezedTextLabel; class ProfileManager; class SearchResultContainer; class KrSearchDialog : public QDialog { Q_OBJECT public: - explicit KrSearchDialog(QString profile = QString(), QWidget* parent = nullptr); + explicit KrSearchDialog(const QString& profile = QString(), QWidget* parent = nullptr); ~KrSearchDialog() override; void prepareGUI(); static KrSearchDialog *SearchDialog; public slots: void startSearch(); void stopSearch(); void feedToListBox(); void copyToClipBoard(); void slotFound(const FileItem &file, const QString &foundText); void closeDialog(bool isAccept = true); void executed(const QString &name); void currentChanged(KrViewItem *item); void contextMenu(const QPoint &); void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; void closeEvent(QCloseEvent *e) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; protected slots: void reject() Q_DECL_OVERRIDE; private: bool gui2query(); void editCurrent(); void viewCurrent(); void compareByContent(); /** * Placing search query to clipboard is optional (opt-in). * So user has clipboard untact by default when opening found documents, * but can enable it persistently by checking "Query to clipboard" checkbox. */ void tryPlaceSearchQueryToClipboard(); private: ProfileManager *profileManager; QCheckBox *searchTextToClipboard; FilterTabs * filterTabs; GeneralFilter * generalFilter; QPushButton* mainSearchBtn; QPushButton* mainStopBtn; QPushButton* mainCloseBtn; QPushButton* mainFeedToListBoxBtn; QTabWidget* searcherTabs; QLabel* foundLabel; KrSqueezedTextLabel *foundTextLabel; KSqueezedTextLabel *searchingLabel; SearchResultContainer *result; KrView *resultView; KrSearchBar *searchBar; QFrame *foundTextFrame; KRQuery *query; KRSearchMod *searcher; bool isBusy; bool closed; static QString lastSearchText; static int lastSearchType; static bool lastSearchForCase; static bool lastContainsWholeWord; static bool lastContainsWithCase; static bool lastSearchInSubDirs; static bool lastSearchInArchives; static bool lastFollowSymLinks; static bool lastContainsRegExp; int sizeX; int sizeY; }; #endif diff --git a/krusader/Splitter/combiner.cpp b/krusader/Splitter/combiner.cpp index bbeb5b8a..607ade68 100644 --- a/krusader/Splitter/combiner.cpp +++ b/krusader/Splitter/combiner.cpp @@ -1,342 +1,343 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "combiner.h" #include "../FileSystem/filesystem.h" // QtCore #include #include #include #include #include #include +#include //TODO: delete destination file on error //TODO: cache more than one byte array of data Combiner::Combiner(QWidget* parent, QUrl baseURLIn, QUrl destinationURLIn, bool unixNamingIn) : - QProgressDialog(parent, nullptr), baseURL(baseURLIn), destinationURL(destinationURLIn), + QProgressDialog(parent, nullptr), baseURL(std::move(baseURLIn)), destinationURL(std::move(destinationURLIn)), hasValidSplitFile(false), fileCounter(0), permissions(-1), receivedSize(0), statJob(nullptr), combineReadJob(nullptr), combineWriteJob(nullptr), unixNaming(unixNamingIn) { crcContext = new CRC32(); splitFile = ""; setMaximum(100); setAutoClose(false); /* don't close or reset the dialog automatically */ setAutoReset(false); setLabelText("Krusader::Combiner"); setWindowModality(Qt::WindowModal); firstFileIs000 = true; //start with this assumption, will set it to false as soon as .000 isn't found } Combiner::~Combiner() { combineAbortJobs(); delete crcContext; } void Combiner::combine() { setWindowTitle(i18n("Krusader::Combining...")); setLabelText(i18n("Combining the file %1...", baseURL.toDisplayString(QUrl::PreferLocalFile))); /* check whether the .crc file exists */ splURL = baseURL.adjusted(QUrl::RemoveFilename); splURL.setPath(splURL.path() + baseURL.fileName() + ".crc"); KFileItem file(splURL); //FIXME: works only for local files - use KIO::stat() instead file.refresh(); if (!file.isReadable()) { int ret = KMessageBox::questionYesNo(nullptr, i18n("The CRC information file (%1) is missing.\n" "Validity checking is impossible without it. Continue combining?", splURL.toDisplayString(QUrl::PreferLocalFile))); if (ret == KMessageBox::No) { emit reject(); return; } statDest(); } else { permissions = file.permissions() | QFile::WriteUser; combineReadJob = KIO::get(splURL, KIO::NoReload, KIO::HideProgressInfo); connect(combineReadJob, &KIO::TransferJob::data, this, &Combiner::combineSplitFileDataReceived); connect(combineReadJob, &KIO::TransferJob::result, this, &Combiner::combineSplitFileFinished); } exec(); } void Combiner::combineSplitFileDataReceived(KIO::Job *, const QByteArray &byteArray) { splitFile += QString(byteArray); } void Combiner::combineSplitFileFinished(KJob *job) { combineReadJob = nullptr; QString error; if (job->error()) error = i18n("Error at reading the CRC file (%1).", splURL.toDisplayString(QUrl::PreferLocalFile)); else { splitFile.remove('\r'); // Windows compatibility QStringList splitFileContent = splitFile.split('\n'); bool hasFileName = false, hasSize = false, hasCrc = false; for (int i = 0; i != splitFileContent.count(); i++) { int ndx = splitFileContent[i].indexOf('='); if (ndx == -1) continue; QString token = splitFileContent[i].left(ndx).trimmed(); QString value = splitFileContent[i].mid(ndx + 1); if (token == "filename") { expectedFileName = value; hasFileName = true; } else if (token == "size") { //FIXME - don't use c functions !!! sscanf(value.trimmed().toLocal8Bit(), "%llu", &expectedSize); hasSize = true; } if (token == "crc32") { expectedCrcSum = value.trimmed().rightJustified(8, '0'); hasCrc = true; } } if (!hasFileName || !hasSize || !hasCrc) error = i18n("Not a valid CRC file."); else hasValidSplitFile = true; } if (!error.isEmpty()) { int ret = KMessageBox::questionYesNo(nullptr, error + i18n("\nValidity checking is impossible without a good CRC file. Continue combining?")); if (ret == KMessageBox::No) { emit reject(); return; } } statDest(); } void Combiner::statDest() { if (writeURL.isEmpty()) { writeURL = FileSystem::ensureTrailingSlash(destinationURL); if (hasValidSplitFile) writeURL.setPath(writeURL.path() + expectedFileName); else if (unixNaming) writeURL.setPath(writeURL.path() + baseURL.fileName() + ".out"); else writeURL.setPath(writeURL.path() + baseURL.fileName()); } statJob = KIO::stat(writeURL, KIO::StatJob::DestinationSide, 0, KIO::HideProgressInfo); connect(statJob, &KIO::StatJob::result, this, &Combiner::statDestResult); } void Combiner::statDestResult(KJob* job) { statJob = nullptr; if (job->error()) { if (job->error() == KIO::ERR_DOES_NOT_EXIST) { openNextFile(); } else { static_cast(job)->uiDelegate()->showErrorMessage(); emit reject(); } } else { // destination already exists KIO::RenameDialog_Options mode = static_cast(job)->statResult().isDir() ? KIO::RenameDialog_IsDirectory : KIO::RenameDialog_Overwrite; KIO::RenameDialog dlg(this, i18n("File Already Exists"), QUrl(), writeURL, mode); switch (dlg.exec()) { case KIO::R_OVERWRITE: openNextFile(); break; case KIO::R_RENAME: { writeURL = dlg.newDestUrl(); statDest(); break; } default: emit reject(); } } } void Combiner::openNextFile() { if (unixNaming) { if (readURL.isEmpty()) readURL = baseURL; else { QString name = readURL.fileName(); int pos = name.length() - 1; QChar ch; do { ch = name.at(pos).toLatin1() + 1; if (ch == QChar('Z' + 1)) ch = 'A'; if (ch == QChar('z' + 1)) ch = 'a'; name[ pos ] = ch; pos--; } while (pos >= 0 && ch.toUpper() == QChar('A')); readURL = readURL.adjusted(QUrl::RemoveFilename); readURL.setPath(readURL.path() + name); } } else { QString index("%1"); /* determining the filename */ index = index.arg(fileCounter++).rightJustified(3, '0'); readURL = baseURL.adjusted(QUrl::RemoveFilename); readURL.setPath(readURL.path() + baseURL.fileName() + '.' + index); } /* creating a read job */ combineReadJob = KIO::get(readURL, KIO::NoReload, KIO::HideProgressInfo); connect(combineReadJob, &KIO::TransferJob::data, this, &Combiner::combineDataReceived); connect(combineReadJob, &KIO::TransferJob::result, this, &Combiner::combineReceiveFinished); if (hasValidSplitFile) connect(combineReadJob, SIGNAL(percent(KJob*,ulong)), this, SLOT(combineWritePercent(KJob*,ulong))); } void Combiner::combineDataReceived(KIO::Job *, const QByteArray &byteArray) { if (byteArray.size() == 0) return; crcContext->update((unsigned char *)byteArray.data(), byteArray.size()); transferArray = QByteArray(byteArray.data(), byteArray.length()); receivedSize += byteArray.size(); if (combineWriteJob == nullptr) { combineWriteJob = KIO::put(writeURL, permissions, KIO::HideProgressInfo | KIO::Overwrite); connect(combineWriteJob, &KIO::TransferJob::dataReq, this, &Combiner::combineDataSend); connect(combineWriteJob, &KIO::TransferJob::result, this, &Combiner::combineSendFinished); } // continue writing and suspend read job until received data is handed over to the write job combineReadJob->suspend(); combineWriteJob->resume(); } void Combiner::combineReceiveFinished(KJob *job) { combineReadJob = nullptr; /* KIO automatically deletes the object after Finished signal */ if (job->error()) { if (job->error() == KIO::ERR_DOES_NOT_EXIST) { if (fileCounter == 1) { // .000 file doesn't exist but .001 is still a valid first file firstFileIs000 = false; openNextFile(); } else if (!firstFileIs000 && fileCounter == 2) { // neither .000 nor .001 exist combineAbortJobs(); KMessageBox::error(nullptr, i18n("Cannot open the first split file of %1.", baseURL.toDisplayString(QUrl::PreferLocalFile))); emit reject(); } else { // we've received the last file // write out the remaining part of the file combineWriteJob->resume(); if (hasValidSplitFile) { QString crcResult = QString("%1").arg(crcContext->result(), 0, 16).toUpper().trimmed() .rightJustified(8, '0'); if (receivedSize != expectedSize) error = i18n("Incorrect filesize, the file might have been corrupted."); else if (crcResult != expectedCrcSum.toUpper().trimmed()) error = i18n("Incorrect CRC checksum, the file might have been corrupted."); } } } else { combineAbortJobs(); static_cast(job)->uiDelegate()->showErrorMessage(); emit reject(); } } else openNextFile(); } void Combiner::combineDataSend(KIO::Job *, QByteArray &byteArray) { byteArray = transferArray; transferArray = QByteArray(); if (combineReadJob) { // continue reading and suspend write job until data is available combineReadJob->resume(); combineWriteJob->suspend(); } } void Combiner::combineSendFinished(KJob *job) { combineWriteJob = nullptr; /* KIO automatically deletes the object after Finished signal */ if (job->error()) { /* any error occurred? */ combineAbortJobs(); static_cast(job)->uiDelegate()->showErrorMessage(); emit reject(); } else if (!error.isEmpty()) { /* was any error message at reading ? */ combineAbortJobs(); /* we cannot write out it in combineReceiveFinished */ KMessageBox::error(nullptr, error); /* because emit accept closes it in this function */ emit reject(); } else emit accept(); } void Combiner::combineAbortJobs() { if (statJob) statJob->kill(KJob::Quietly); if (combineReadJob) combineReadJob->kill(KJob::Quietly); if (combineWriteJob) combineWriteJob->kill(KJob::Quietly); statJob = combineReadJob = combineWriteJob = nullptr; } void Combiner::combineWritePercent(KJob *, unsigned long) { auto percent = (int)((((double)receivedSize / expectedSize) * 100.) + 0.5); setValue(percent); } diff --git a/krusader/Splitter/splitter.cpp b/krusader/Splitter/splitter.cpp index 90dce5d9..1970ce08 100644 --- a/krusader/Splitter/splitter.cpp +++ b/krusader/Splitter/splitter.cpp @@ -1,295 +1,296 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "splitter.h" #include "../FileSystem/filesystem.h" // QtCore #include // QtWidgets #include #include #include #include #include #include +#include Splitter::Splitter(QWidget* parent, QUrl fileNameIn, QUrl destinationDirIn, bool overWriteIn) : QProgressDialog(parent, nullptr), - fileName(fileNameIn), - destinationDir(destinationDirIn), + fileName(std::move(fileNameIn)), + destinationDir(std::move(destinationDirIn)), splitSize(0), permissions(0), overwrite(overWriteIn), fileNumber(0), outputFileRemaining(0), receivedSize(0), crcContext(new CRC32()), statJob(nullptr), splitReadJob(nullptr), splitWriteJob(nullptr) { setMaximum(100); setAutoClose(false); /* don't close or reset the dialog automatically */ setAutoReset(false); setLabelText("Krusader::Splitter"); setWindowModality(Qt::WindowModal); } Splitter::~Splitter() { splitAbortJobs(); delete crcContext; } void Splitter::split(KIO::filesize_t splitSizeIn) { Q_ASSERT(!splitReadJob); Q_ASSERT(!splitWriteJob); Q_ASSERT(!fileNumber); Q_ASSERT(!receivedSize); Q_ASSERT(!outputFileRemaining); splitReadJob = splitWriteJob = nullptr; fileNumber = receivedSize = outputFileRemaining = 0; splitSize = splitSizeIn; KFileItem file(fileName); file.refresh(); //FIXME: works only for local files - use KIO::stat() instead permissions = file.permissions() | QFile::WriteUser; setWindowTitle(i18n("Krusader::Splitting...")); setLabelText(i18n("Splitting the file %1...", fileName.toDisplayString(QUrl::PreferLocalFile))); if (file.isDir()) { KMessageBox::error(nullptr, i18n("Cannot split a folder.")); return; } splitReadJob = KIO::get(fileName, KIO::NoReload, KIO::HideProgressInfo); connect(splitReadJob, &KIO::TransferJob::data, this, &Splitter::splitDataReceived); connect(splitReadJob, &KIO::TransferJob::result, this, &Splitter::splitReceiveFinished); connect(splitReadJob, SIGNAL(percent(KJob*,ulong)), this, SLOT(splitReceivePercent(KJob*,ulong))); exec(); } void Splitter::splitDataReceived(KIO::Job *, const QByteArray &byteArray) { Q_ASSERT(!transferArray.length()); // transfer buffer must be empty if (byteArray.size() == 0) return; crcContext->update((unsigned char *)byteArray.data(), byteArray.size()); receivedSize += byteArray.size(); if (!splitWriteJob) nextOutputFile(); transferArray = QByteArray(byteArray.data(), byteArray.length()); // suspend read job until transfer buffer is handed to the write job splitReadJob->suspend(); if (splitWriteJob) splitWriteJob->resume(); } void Splitter::splitReceiveFinished(KJob *job) { splitReadJob = nullptr; /* KIO automatically deletes the object after Finished signal */ if (splitWriteJob) splitWriteJob->resume(); // finish writing the output if (job->error()) { /* any error occurred? */ splitAbortJobs(); KMessageBox::error(nullptr, i18n("Error reading file %1: %2", fileName.toDisplayString(QUrl::PreferLocalFile), job->errorString())); emit reject(); return; } QString crcResult = QString("%1").arg(crcContext->result(), 0, 16).toUpper().trimmed() .rightJustified(8, '0'); splitInfoFileContent = QString("filename=%1\n").arg(fileName.fileName()) + QString("size=%1\n") .arg(KIO::number(receivedSize)) + QString("crc32=%1\n") .arg(crcResult); } void Splitter::splitReceivePercent(KJob *, unsigned long percent) { setValue(percent); } void Splitter::nextOutputFile() { Q_ASSERT(!outputFileRemaining); fileNumber++; outputFileRemaining = splitSize; QString index("%1"); /* making the split filename */ index = index.arg(fileNumber).rightJustified(3, '0'); QString outFileName = fileName.fileName() + '.' + index; writeURL = destinationDir; writeURL = writeURL.adjusted(QUrl::StripTrailingSlash); writeURL.setPath(writeURL.path() + '/' + (outFileName)); if (overwrite) openOutputFile(); else { statJob = KIO::stat(writeURL, KIO::StatJob::DestinationSide, 0, KIO::HideProgressInfo); connect(statJob, &KIO::Job::result, this, &Splitter::statOutputFileResult); } } void Splitter::statOutputFileResult(KJob* job) { statJob = nullptr; if (job->error()) { if (job->error() == KIO::ERR_DOES_NOT_EXIST) openOutputFile(); else { static_cast(job)->uiDelegate()->showErrorMessage(); emit reject(); } } else { // destination already exists KIO::RenameDialog dlg(this, i18n("File Already Exists"), QUrl(), writeURL, static_cast(KIO::M_MULTI | KIO::M_OVERWRITE | KIO::M_NORENAME)); switch (dlg.exec()) { case KIO::R_OVERWRITE: openOutputFile(); break; case KIO::R_OVERWRITE_ALL: overwrite = true; openOutputFile(); break; default: emit reject(); } } } void Splitter::openOutputFile() { // create write job splitWriteJob = KIO::put(writeURL, permissions, KIO::HideProgressInfo | KIO::Overwrite); connect(splitWriteJob, &KIO::TransferJob::dataReq, this, &Splitter::splitDataSend); connect(splitWriteJob, &KIO::TransferJob::result, this, &Splitter::splitSendFinished); } void Splitter::splitDataSend(KIO::Job *, QByteArray &byteArray) { KIO::filesize_t bufferLen = transferArray.size(); if (!outputFileRemaining) { // current output file needs to be closed ? byteArray = QByteArray(); // giving empty buffer which indicates closing } else if (bufferLen > outputFileRemaining) { // maximum length reached ? byteArray = QByteArray(transferArray.data(), outputFileRemaining); transferArray = QByteArray(transferArray.data() + outputFileRemaining, bufferLen - outputFileRemaining); outputFileRemaining = 0; } else { outputFileRemaining -= bufferLen; // write the whole buffer to the output file byteArray = transferArray; transferArray = QByteArray(); if (splitReadJob) { // suspend write job until transfer buffer is filled or the read job is finished splitWriteJob->suspend(); splitReadJob->resume(); } // else: write job continues until transfer buffer is empty } } void Splitter::splitSendFinished(KJob *job) { splitWriteJob = nullptr; /* KIO automatically deletes the object after Finished signal */ if (job->error()) { /* any error occurred? */ splitAbortJobs(); KMessageBox::error(nullptr, i18n("Error writing file %1: %2", writeURL.toDisplayString(QUrl::PreferLocalFile), job->errorString())); emit reject(); return; } if (transferArray.size()) /* any data remained in the transfer buffer? */ nextOutputFile(); else if (splitReadJob) splitReadJob->resume(); else { // read job is finished and transfer buffer is empty -> splitting is finished /* writing the split information file out */ writeURL = destinationDir; writeURL = writeURL.adjusted(QUrl::StripTrailingSlash); writeURL.setPath(writeURL.path() + '/' + (fileName.fileName() + ".crc")); splitWriteJob = KIO::put(writeURL, permissions, KIO::HideProgressInfo | KIO::Overwrite); connect(splitWriteJob, &KIO::TransferJob::dataReq, this, &Splitter::splitFileSend); connect(splitWriteJob, &KIO::TransferJob::result, this, &Splitter::splitFileFinished); } } void Splitter::splitAbortJobs() { if (statJob) statJob->kill(KJob::Quietly); if (splitReadJob) splitReadJob->kill(KJob::Quietly); if (splitWriteJob) splitWriteJob->kill(KJob::Quietly); splitReadJob = splitWriteJob = nullptr; } void Splitter::splitFileSend(KIO::Job *, QByteArray &byteArray) { byteArray = splitInfoFileContent.toLocal8Bit(); splitInfoFileContent = ""; } void Splitter::splitFileFinished(KJob *job) { splitWriteJob = nullptr; /* KIO automatically deletes the object after Finished signal */ if (job->error()) { /* any error occurred? */ KMessageBox::error(nullptr, i18n("Error writing file %1: %2", writeURL.toDisplayString(QUrl::PreferLocalFile), job->errorString())); emit reject(); return; } emit accept(); } diff --git a/krusader/Splitter/splittergui.cpp b/krusader/Splitter/splittergui.cpp index 4c1cc6a4..252ee2e8 100644 --- a/krusader/Splitter/splittergui.cpp +++ b/krusader/Splitter/splittergui.cpp @@ -1,265 +1,266 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "splittergui.h" #include "../FileSystem/filesystem.h" // QtCore #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include struct SplitterGUI::PredefinedDevice { PredefinedDevice(QString name, KIO::filesize_t capacity) : - name(name), capacity(capacity) {} + name(std::move(name)), capacity(capacity) {} PredefinedDevice(const PredefinedDevice &other) : name(other.name), capacity(other.capacity) {} PredefinedDevice &operator=(const PredefinedDevice &other); QString name; KIO::filesize_t capacity; }; const QList &SplitterGUI::predefinedDevices() { static QList list; if(!list.count()) { list << PredefinedDevice(i18n("1.44 MB (3.5\")"), 1457664); list << PredefinedDevice(i18n("1.2 MB (5.25\")"), 1213952); list << PredefinedDevice(i18n("720 kB (3.5\")"), 730112); list << PredefinedDevice(i18n("360 kB (5.25\")"), 362496); list << PredefinedDevice(i18n("100 MB (ZIP)"), 100431872); list << PredefinedDevice(i18n("250 MB (ZIP)"), 250331136); list << PredefinedDevice(i18n("650 MB (CD-R)"), 650*0x100000); list << PredefinedDevice(i18n("700 MB (CD-R)"), 700*0x100000); } return list; }; -SplitterGUI::SplitterGUI(QWidget* parent, QUrl fileURL, QUrl defaultDir) : +SplitterGUI::SplitterGUI(QWidget* parent, const QUrl& fileURL, const QUrl& defaultDir) : QDialog(parent), userDefinedSize(0x100000), lastSelectedDevice(-1), division(1) { setModal(true); auto *grid = new QGridLayout(this); grid->setSpacing(6); grid->setContentsMargins(11, 11, 11, 11); QLabel *splitterLabel = new QLabel(this); splitterLabel->setText(i18n("Split the file %1 to folder:", fileURL.toDisplayString(QUrl::PreferLocalFile))); splitterLabel->setMinimumWidth(400); grid->addWidget(splitterLabel, 0 , 0); urlReq = new KUrlRequester(this); urlReq->setUrl(defaultDir); urlReq->setMode(KFile::Directory); grid->addWidget(urlReq, 1 , 0); QWidget *splitSizeLine = new QWidget(this); auto * splitSizeLineLayout = new QHBoxLayout; splitSizeLineLayout->setContentsMargins(0, 0, 0, 0); splitSizeLine->setLayout(splitSizeLineLayout); deviceCombo = new QComboBox(splitSizeLine); for (int i = 0; i != predefinedDevices().count(); i++) deviceCombo->addItem(predefinedDevices()[i].name); deviceCombo->addItem(i18n("User Defined")); splitSizeLineLayout->addWidget(deviceCombo); QLabel *spacer = new QLabel(splitSizeLine); spacer->setText(" "); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); splitSizeLineLayout->addWidget(spacer); QLabel *bytesPerFile = new QLabel(splitSizeLine); bytesPerFile->setText(i18n("Max file size:")); splitSizeLineLayout->addWidget(bytesPerFile); spinBox = new QDoubleSpinBox(splitSizeLine); spinBox->setMaximum(9999999999.0); spinBox->setMinimumWidth(85); spinBox->setEnabled(false); splitSizeLineLayout->addWidget(spinBox); sizeCombo = new QComboBox(splitSizeLine); sizeCombo->addItem(i18n("Byte")); sizeCombo->addItem(i18n("kByte")); sizeCombo->addItem(i18n("MByte")); sizeCombo->addItem(i18n("GByte")); splitSizeLineLayout->addWidget(sizeCombo); grid->addWidget(splitSizeLine, 2 , 0); overwriteCb = new QCheckBox(i18n("Overwrite files without confirmation"), this); grid->addWidget(overwriteCb, 3, 0); QFrame *separator = new QFrame(this); separator->setFrameStyle(QFrame::HLine | QFrame::Sunken); separator->setFixedHeight(separator->sizeHint().height()); grid->addWidget(separator, 4 , 0); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("&Split")); grid->addWidget(buttonBox, 5 , 0); setWindowTitle(i18n("Krusader::Splitter")); KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Splitter")); overwriteCb->setChecked(cfg.readEntry("OverWriteFiles", false)); connect(sizeCombo, QOverload::of(&QComboBox::activated), this, &SplitterGUI::sizeComboActivated); connect(deviceCombo, QOverload::of(&QComboBox::activated), this, &SplitterGUI::predefinedComboActivated); connect(buttonBox, &QDialogButtonBox::rejected, this, &SplitterGUI::reject); connect(buttonBox , &QDialogButtonBox::accepted, this, &SplitterGUI::splitPressed); predefinedComboActivated(0); } SplitterGUI::~SplitterGUI() { KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Splitter")); cfg.writeEntry("OverWriteFiles", overwriteCb->isChecked()); } KIO::filesize_t SplitterGUI::getSplitSize() { if(deviceCombo->currentIndex() < predefinedDevices().count()) // predefined size selected return predefinedDevices()[deviceCombo->currentIndex()].capacity; // user defined size selected return spinBox->value() * division; } bool SplitterGUI::overWriteFiles() { return overwriteCb->isChecked(); } void SplitterGUI::sizeComboActivated(int item) { KIO::filesize_t prevDivision = division; switch (item) { case 0: division = 1; /* byte */ break; case 1: division = 0x400; /* kbyte */ break; case 2: division = 0x100000; /* Mbyte */ break; case 3: division = 0x40000000; /* Gbyte */ break; } double value; if(deviceCombo->currentIndex() < predefinedDevices().count()) // predefined size selected value = (double)predefinedDevices()[deviceCombo->currentIndex()].capacity / division; else { // use defined size selected value = (double)(spinBox->value() * prevDivision) / division; if(value < 1) value = 1; } spinBox->setValue(value); } void SplitterGUI::predefinedComboActivated(int item) { if(item == lastSelectedDevice) return; KIO::filesize_t capacity = userDefinedSize; if (lastSelectedDevice == predefinedDevices().count()) // user defined was selected previously userDefinedSize = spinBox->value() * division; // remember user defined size if(item < predefinedDevices().count()) { // predefined size selected capacity = predefinedDevices()[item].capacity; spinBox->setEnabled(false); } else // user defined size selected spinBox->setEnabled(true); //qDebug() << capacity; if (capacity >= 0x40000000) { /* Gbyte */ sizeCombo->setCurrentIndex(3); division = 0x40000000; } else if (capacity >= 0x100000) { /* Mbyte */ sizeCombo->setCurrentIndex(2); division = 0x100000; } else if (capacity >= 0x400) { /* kbyte */ sizeCombo->setCurrentIndex(1); division = 0x400; } else { sizeCombo->setCurrentIndex(0); /* byte */ division = 1; } spinBox->setValue((double)capacity / division); lastSelectedDevice = item; } void SplitterGUI::splitPressed() { if (!urlReq->url().isValid()) { KMessageBox::error(this, i18n("The folder path URL is malformed.")); return; } if(getSplitSize() > 0) emit accept(); } void SplitterGUI::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Enter : case Qt::Key_Return : emit splitPressed(); return; default: QDialog::keyPressEvent(e); } } diff --git a/krusader/Splitter/splittergui.h b/krusader/Splitter/splittergui.h index 2d87597a..2bd213d5 100644 --- a/krusader/Splitter/splittergui.h +++ b/krusader/Splitter/splittergui.h @@ -1,74 +1,74 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 SPLITTERGUI_H #define SPLITTERGUI_H // QtCore #include // QtWidgets #include #include #include class QComboBox; class QCheckBox; class QDoubleSpinBox; class SplitterGUI : public QDialog { Q_OBJECT private: struct PredefinedDevice; static const QList &predefinedDevices(); KIO::filesize_t userDefinedSize; int lastSelectedDevice; KIO::filesize_t division; QDoubleSpinBox *spinBox; QComboBox *deviceCombo; QComboBox *sizeCombo; QCheckBox *overwriteCb; KUrlRequester *urlReq; public: - SplitterGUI(QWidget* parent, QUrl fileURL, QUrl defaultDir); + SplitterGUI(QWidget* parent, const QUrl& fileURL, const QUrl& defaultDir); ~SplitterGUI() override; QUrl getDestinationDir() { return urlReq->url(); } KIO::filesize_t getSplitSize(); bool overWriteFiles(); public slots: virtual void sizeComboActivated(int item); virtual void predefinedComboActivated(int item); virtual void splitPressed(); protected: void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; }; #endif /* __SPLITTERGUI_H__ */ diff --git a/krusader/Synchronizer/synchronizer.cpp b/krusader/Synchronizer/synchronizer.cpp index 26d2fb61..f95b17ad 100644 --- a/krusader/Synchronizer/synchronizer.cpp +++ b/krusader/Synchronizer/synchronizer.cpp @@ -1,1443 +1,1443 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "synchronizer.h" #include "synchronizerdirlist.h" #include "../krglobal.h" #include "../krservices.h" #include "../FileSystem/filesystem.h" #include "../FileSystem/krquery.h" #include // QtCore #include #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POSIX_ACL #include #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS #include #endif #endif #define DISPLAY_UPDATE_PERIOD 2 Synchronizer::Synchronizer() : displayUpdateCount(0), markEquals(true), markDiffers(true), markCopyToLeft(true), markCopyToRight(true), markDeletable(true), stack(), jobMap(), receivedMap(), parentWidget(nullptr), resultListIt(resultList) { } Synchronizer::~Synchronizer() { clearLists(); } -QUrl Synchronizer::fsUrl(QString strUrl) +QUrl Synchronizer::fsUrl(const QString& strUrl) { QUrl result = QUrl::fromUserInput(strUrl, QString(), QUrl::AssumeLocalFile); return KrServices::escapeFileUrl(result); } void Synchronizer::clearLists() { QListIterator i1(resultList); while (i1.hasNext()) delete i1.next(); resultList.clear(); QListIterator i2(stack); while (i2.hasNext()) delete i2.next(); stack.clear(); temporaryList.clear(); } void Synchronizer::reset() { displayUpdateCount = 0; markEquals = markDiffers = markCopyToLeft = markCopyToRight = markDeletable = true; stopped = false; recurseSubDirs = followSymLinks = ignoreDate = asymmetric = cmpByContent = ignoreCase = autoScroll = false; markEquals = markDiffers = markCopyToLeft = markCopyToRight = markDeletable = markDuplicates = markSingles = false; leftCopyEnabled = rightCopyEnabled = deleteEnabled = overWrite = autoSkip = paused = false; leftCopyNr = rightCopyNr = deleteNr = 0; leftCopySize = rightCopySize = deleteSize = 0; comparedDirs = fileCount = 0; leftBaseDir.clear(); rightBaseDir.clear(); clearLists(); } int Synchronizer::compare(QString leftURL, QString rightURL, KRQuery *query, bool subDirs, bool symLinks, bool igDate, bool asymm, bool cmpByCnt, bool igCase, bool autoSc, QStringList &selFiles, int equThres, int timeOffs, int parThreads, bool hiddenFiles) { clearLists(); recurseSubDirs = subDirs; followSymLinks = symLinks; ignoreDate = igDate; asymmetric = asymm; cmpByContent = cmpByCnt; autoScroll = autoSc; ignoreCase = igCase; selectedFiles = selFiles; equalsThreshold = equThres; timeOffset = timeOffs; parallelThreads = parThreads; ignoreHidden = hiddenFiles; stopped = false; this->query = query; leftURL = KUrlCompletion::replacedPath(leftURL, true, true); rightURL = KUrlCompletion::replacedPath(rightURL, true, true); if (!leftURL.endsWith('/')) leftURL += '/'; if (!rightURL.endsWith('/')) rightURL += '/'; excludedPaths = KrServices::toStringList(query->dontSearchInDirs()); for (int i = 0; i != excludedPaths.count(); i++) if (excludedPaths[ i ].endsWith('/')) excludedPaths[ i ].truncate(excludedPaths[ i ].length() - 1); comparedDirs = fileCount = 0; stack.append(new CompareTask(nullptr, leftBaseDir = leftURL, rightBaseDir = rightURL, "", "", ignoreHidden)); compareLoop(); QListIterator it(temporaryList); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); if (item->isTemporary()) delete item; } temporaryList.clear(); if (!autoScroll) refresh(true); emit statusInfo(i18n("Number of files: %1", fileCount)); return fileCount; } void Synchronizer::compareLoop() { while (!stopped && !stack.isEmpty()) { for (int thread = 0; thread < (int)stack.count() && thread < parallelThreads; thread++) { SynchronizerTask * entry = stack.at(thread); if (entry->state() == ST_STATE_NEW) entry->start(parentWidget); if (entry->inherits("CompareTask")) { if (entry->state() == ST_STATE_READY) { auto *ctentry = (CompareTask *) entry; if (ctentry->isDuplicate()) compareDirectory(ctentry->parent(), ctentry->leftDirList(), ctentry->rightDirList(), ctentry->leftDir(), ctentry->rightDir()); else addSingleDirectory(ctentry->parent(), ctentry->dirList(), ctentry->dir(), ctentry->isLeft()); } if (entry->state() == ST_STATE_READY || entry->state() == ST_STATE_ERROR) comparedDirs++; } switch (entry->state()) { case ST_STATE_STATUS: emit statusInfo(entry->status()); break; case ST_STATE_READY: case ST_STATE_ERROR: emit statusInfo(i18n("Number of compared folders: %1", comparedDirs)); stack.removeAll(entry); delete entry; continue; default: break; } } if (!stack.isEmpty()) qApp->processEvents(); } QListIterator it(stack); while (it.hasNext()) delete it.next(); stack.clear(); } void Synchronizer::compareDirectory(SynchronizerFileItem *parent, SynchronizerDirList * left_directory, SynchronizerDirList * right_directory, const QString &leftDir, const QString &rightDir) { const QString &leftURL = left_directory->url(); const QString &rightURL = right_directory->url(); FileItem *left_file; FileItem *right_file; QString file_name; bool checkIfSelected = false; if (leftDir.isEmpty() && rightDir.isEmpty() && selectedFiles.count()) checkIfSelected = true; /* walking through in the left directory */ for (left_file = left_directory->first(); left_file != nullptr && !stopped; left_file = left_directory->next()) { if (isDir(left_file)) continue; file_name = left_file->getName(); if (checkIfSelected && !selectedFiles.contains(file_name)) continue; if (!query->match(left_file)) continue; if ((right_file = right_directory->search(file_name, ignoreCase)) == nullptr) addLeftOnlyItem(parent, file_name, leftDir, left_file->getSize(), left_file->getTime_t(), readLink(left_file), left_file->getOwner(), left_file->getGroup(), left_file->getMode(), left_file->getACL()); else { if (isDir(right_file)) continue; addDuplicateItem(parent, file_name, right_file->getName(), leftDir, rightDir, left_file->getSize(), right_file->getSize(), left_file->getTime_t(), right_file->getTime_t(), readLink(left_file), readLink(right_file), left_file->getOwner(), right_file->getOwner(), left_file->getGroup(), right_file->getGroup(), left_file->getMode(), right_file->getMode(), left_file->getACL(), right_file->getACL()); } } /* walking through in the right directory */ for (right_file = right_directory->first(); right_file != nullptr && !stopped; right_file = right_directory->next()) { if (isDir(right_file)) continue; file_name = right_file->getName(); if (checkIfSelected && !selectedFiles.contains(file_name)) continue; if (!query->match(right_file)) continue; if (left_directory->search(file_name, ignoreCase) == nullptr) addRightOnlyItem(parent, file_name, rightDir, right_file->getSize(), right_file->getTime_t(), readLink(right_file), right_file->getOwner(), right_file->getGroup(), right_file->getMode(), right_file->getACL()); } /* walking through the subdirectories */ if (recurseSubDirs) { for (left_file = left_directory->first(); left_file != nullptr && !stopped; left_file = left_directory->next()) { if (left_file->isDir() && (followSymLinks || !left_file->isSymLink())) { QString left_file_name = left_file->getName(); if (checkIfSelected && !selectedFiles.contains(left_file_name)) continue; if (excludedPaths.contains(leftDir.isEmpty() ? left_file_name : leftDir + '/' + left_file_name)) continue; if (!query->matchDirName(left_file_name)) continue; if ((right_file = right_directory->search(left_file_name, ignoreCase)) == nullptr) { SynchronizerFileItem *me = addLeftOnlyItem(parent, left_file_name, leftDir, 0, left_file->getTime_t(), readLink(left_file), left_file->getOwner(), left_file->getGroup(), left_file->getMode(), left_file->getACL(), true, !query->match(left_file)); stack.append(new CompareTask(me, leftURL + left_file_name + '/', leftDir.isEmpty() ? left_file_name : leftDir + '/' + left_file_name, true, ignoreHidden)); } else { QString right_file_name = right_file->getName(); SynchronizerFileItem *me = addDuplicateItem(parent, left_file_name, right_file_name, leftDir, rightDir, 0, 0, left_file->getTime_t(), right_file->getTime_t(), readLink(left_file), readLink(right_file), left_file->getOwner(), right_file->getOwner(), left_file->getGroup(), right_file->getGroup(), left_file->getMode(), right_file->getMode(), left_file->getACL(), right_file->getACL(), true, !query->match(left_file)); stack.append(new CompareTask(me, leftURL + left_file_name + '/', rightURL + right_file_name + '/', leftDir.isEmpty() ? left_file_name : leftDir + '/' + left_file_name, rightDir.isEmpty() ? right_file_name : rightDir + '/' + right_file_name, ignoreHidden)); } } } /* walking through the right side subdirectories */ for (right_file = right_directory->first(); right_file != nullptr && !stopped; right_file = right_directory->next()) { if (right_file->isDir() && (followSymLinks || !right_file->isSymLink())) { file_name = right_file->getName(); if (checkIfSelected && !selectedFiles.contains(file_name)) continue; if (excludedPaths.contains(rightDir.isEmpty() ? file_name : rightDir + '/' + file_name)) continue; if (!query->matchDirName(file_name)) continue; if (left_directory->search(file_name, ignoreCase) == nullptr) { SynchronizerFileItem *me = addRightOnlyItem(parent, file_name, rightDir, 0, right_file->getTime_t(), readLink(right_file), right_file->getOwner(), right_file->getGroup(), right_file->getMode(), right_file->getACL(), true, !query->match(right_file)); stack.append(new CompareTask(me, rightURL + file_name + '/', rightDir.isEmpty() ? file_name : rightDir + '/' + file_name, false, ignoreHidden)); } } } } } QString Synchronizer::getTaskTypeName(TaskType taskType) { static QString names[] = {"=", "!=", "<-", "->", "DEL", "?", "?", "?", "?", "?"}; return names[taskType]; } SynchronizerFileItem * Synchronizer::addItem(SynchronizerFileItem *parent, const QString &leftFile, const QString &rightFile, const QString &leftDir, const QString &rightDir, bool existsLeft, bool existsRight, KIO::filesize_t leftSize, KIO::filesize_t rightSize, time_t leftDate, time_t rightDate, const QString &leftLink, const QString &rightLink, const QString &leftOwner, const QString &rightOwner, const QString &leftGroup, const QString &rightGroup, mode_t leftMode, mode_t rightMode, const QString &leftACL, const QString &rightACL, TaskType tsk, bool isDir, bool isTemp) { bool marked = autoScroll ? !isTemp && isMarked(tsk, existsLeft && existsRight) : false; auto *item = new SynchronizerFileItem(leftFile, rightFile, leftDir, rightDir, marked, existsLeft, existsRight, leftSize, rightSize, leftDate, rightDate, leftLink, rightLink, leftOwner, rightOwner, leftGroup, rightGroup, leftMode, rightMode, leftACL, rightACL, tsk, isDir, isTemp, parent); if (!isTemp) { while (parent && parent->isTemporary()) setPermanent(parent); bool doRefresh = false; if (marked) { fileCount++; if (autoScroll && markParentDirectories(item)) doRefresh = true; } resultList.append(item); emit comparedFileData(item); if (doRefresh) refresh(true); if (marked && (displayUpdateCount++ % DISPLAY_UPDATE_PERIOD == (DISPLAY_UPDATE_PERIOD - 1))) qApp->processEvents(); } else temporaryList.append(item); return item; } void Synchronizer::compareContentResult(SynchronizerFileItem * item, bool res) { item->compareContentResult(res); bool marked = autoScroll ? isMarked(item->task(), item->existsInLeft() && item->existsInRight()) : false; item->setMarked(marked); if (marked) { markParentDirectories(item); fileCount++; emit markChanged(item, true); } } void Synchronizer::setPermanent(SynchronizerFileItem *item) { if (item->parent() && item->parent()->isTemporary()) setPermanent(item->parent()); item->setPermanent(); resultList.append(item); emit comparedFileData(item); } SynchronizerFileItem * Synchronizer::addLeftOnlyItem(SynchronizerFileItem *parent, const QString &file_name, const QString &dir, KIO::filesize_t size, time_t date, const QString &link, const QString &owner, const QString &group, mode_t mode, const QString &acl, bool isDir, bool isTemp) { return addItem(parent, file_name, file_name, dir, dir, true, false, size, 0, date, 0, link, QString(), owner, QString(), group, QString(), mode, (mode_t) - 1, acl, QString(), asymmetric ? TT_DELETE : TT_COPY_TO_RIGHT, isDir, isTemp); } SynchronizerFileItem * Synchronizer::addRightOnlyItem(SynchronizerFileItem *parent, const QString &file_name, const QString &dir, KIO::filesize_t size, time_t date, const QString &link, const QString &owner, const QString &group, mode_t mode, const QString &acl, bool isDir, bool isTemp) { return addItem(parent, file_name, file_name, dir, dir, false, true, 0, size, 0, date, QString(), link, QString(), owner, QString(), group, (mode_t) - 1, mode, QString(), acl, TT_COPY_TO_LEFT, isDir, isTemp); } SynchronizerFileItem * Synchronizer::addDuplicateItem(SynchronizerFileItem *parent, const QString &leftName, const QString &rightName, const QString &leftDir, const QString &rightDir, KIO::filesize_t leftSize, KIO::filesize_t rightSize, time_t leftDate, time_t rightDate, const QString &leftLink, const QString &rightLink, const QString &leftOwner, const QString &rightOwner, const QString &leftGroup, const QString &rightGroup, mode_t leftMode, mode_t rightMode, const QString &leftACL, const QString &rightACL, bool isDir, bool isTemp) { TaskType task; int checkedRightDate = rightDate - timeOffset; int uncertain = 0; do { if (isDir) { task = TT_EQUALS; break; } if (leftSize == rightSize) { if (!leftLink.isNull() || !rightLink.isNull()) { if (leftLink == rightLink) { task = TT_EQUALS; break; } } else if (cmpByContent) uncertain = TT_UNKNOWN; else { if (ignoreDate || leftDate == checkedRightDate) { task = TT_EQUALS; break; } time_t diff = (leftDate > checkedRightDate) ? leftDate - checkedRightDate : checkedRightDate - leftDate; if (diff <= equalsThreshold) { task = TT_EQUALS; break; } } } if (asymmetric) task = TT_COPY_TO_LEFT; else if (ignoreDate) task = TT_DIFFERS; else if (leftDate > checkedRightDate) task = TT_COPY_TO_RIGHT; else if (leftDate < checkedRightDate) task = TT_COPY_TO_LEFT; else task = TT_DIFFERS; } while (false); SynchronizerFileItem * item = addItem(parent, leftName, rightName, leftDir, rightDir, true, true, leftSize, rightSize, leftDate, rightDate, leftLink, rightLink, leftOwner, rightOwner, leftGroup, rightGroup, leftMode, rightMode, leftACL, rightACL, (TaskType)(task + uncertain), isDir, isTemp); if (uncertain == TT_UNKNOWN) { QUrl leftURL = Synchronizer::fsUrl(leftDir.isEmpty() ? leftBaseDir + leftName : leftBaseDir + leftDir + '/' + leftName); QUrl rightURL = Synchronizer::fsUrl(rightDir.isEmpty() ? rightBaseDir + rightName : rightBaseDir + rightDir + '/' + rightName); stack.append(new CompareContentTask(this, item, leftURL, rightURL, leftSize)); } return item; } void Synchronizer::addSingleDirectory(SynchronizerFileItem *parent, SynchronizerDirList *directory, const QString &dirName, bool isLeft) { const QString &url = directory->url(); FileItem *file; QString file_name; /* walking through the directory files */ for (file = directory->first(); file != nullptr && !stopped; file = directory->next()) { if (isDir(file)) continue; file_name = file->getName(); if (!query->match(file)) continue; if (isLeft) addLeftOnlyItem(parent, file_name, dirName, file->getSize(), file->getTime_t(), readLink(file), file->getOwner(), file->getGroup(), file->getMode(), file->getACL()); else addRightOnlyItem(parent, file_name, dirName, file->getSize(), file->getTime_t(), readLink(file), file->getOwner(), file->getGroup(), file->getMode(), file->getACL()); } /* walking through the subdirectories */ for (file = directory->first(); file != nullptr && !stopped; file = directory->next()) { if (file->isDir() && (followSymLinks || !file->isSymLink())) { file_name = file->getName(); if (excludedPaths.contains(dirName.isEmpty() ? file_name : dirName + '/' + file_name)) continue; if (!query->matchDirName(file_name)) continue; SynchronizerFileItem *me; if (isLeft) me = addLeftOnlyItem(parent, file_name, dirName, 0, file->getTime_t(), readLink(file), file->getOwner(), file->getGroup(), file->getMode(), file->getACL(), true, !query->match(file)); else me = addRightOnlyItem(parent, file_name, dirName, 0, file->getTime_t(), readLink(file), file->getOwner(), file->getGroup(), file->getMode(), file->getACL(), true, !query->match(file)); stack.append(new CompareTask(me, url + file_name + '/', dirName.isEmpty() ? file_name : dirName + '/' + file_name, isLeft, ignoreHidden)); } } } void Synchronizer::setMarkFlags(bool left, bool equal, bool differs, bool right, bool dup, bool sing, bool del) { markEquals = equal; markDiffers = differs; markCopyToLeft = left; markCopyToRight = right; markDeletable = del; markDuplicates = dup; markSingles = sing; } bool Synchronizer::isMarked(TaskType task, bool isDuplicate) { if ((isDuplicate && !markDuplicates) || (!isDuplicate && !markSingles)) return false; switch (task) { case TT_EQUALS: return markEquals; case TT_DIFFERS: return markDiffers; case TT_COPY_TO_LEFT: return markCopyToLeft; case TT_COPY_TO_RIGHT: return markCopyToRight; case TT_DELETE: return markDeletable; default: return false; } } bool Synchronizer::markParentDirectories(SynchronizerFileItem *item) { if (item->parent() == nullptr || item->parent()->isMarked()) return false; markParentDirectories(item->parent()); item->parent()->setMarked(true); fileCount++; emit markChanged(item->parent(), false); return true; } int Synchronizer::refresh(bool nostatus) { fileCount = 0; QListIterator it(resultList); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); bool marked = isMarked(item->task(), item->existsInLeft() && item->existsInRight()); item->setMarked(marked); if (marked) { markParentDirectories(item); fileCount++; } } it.toFront(); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); emit markChanged(item, false); } if (!nostatus) emit statusInfo(i18n("Number of files: %1", fileCount)); return fileCount; } void Synchronizer::operate(SynchronizerFileItem *item, void (*executeOperation)(SynchronizerFileItem *)) { executeOperation(item); if (item->isDir()) { QString leftDirName = (item->leftDirectory().isEmpty()) ? item->leftName() : item->leftDirectory() + '/' + item->leftName(); QString rightDirName = (item->rightDirectory().isEmpty()) ? item->rightName() : item->rightDirectory() + '/' + item->rightName(); QListIterator it(resultList); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); if (item->leftDirectory() == leftDirName || item->leftDirectory().startsWith(leftDirName + '/') || item->rightDirectory() == rightDirName || item->rightDirectory().startsWith(rightDirName + '/')) executeOperation(item); } } } void Synchronizer::excludeOperation(SynchronizerFileItem *item) { item->setTask(TT_DIFFERS); } void Synchronizer::exclude(SynchronizerFileItem *item) { if (!item->parent() || item->parent()->task() != TT_DELETE) operate(item, excludeOperation); /* exclude only if the parent task is not DEL */ } void Synchronizer::restoreOperation(SynchronizerFileItem *item) { item->restoreOriginalTask(); } void Synchronizer::restore(SynchronizerFileItem *item) { operate(item, restoreOperation); while ((item = item->parent()) != nullptr) /* in case of restore, the parent directories */ { /* must be changed for being consistent */ if (item->task() != TT_DIFFERS) break; if (item->originalTask() == TT_DELETE) /* if the parent original task is delete */ break; /* don't touch it */ item->restoreOriginalTask(); /* restore */ } } void Synchronizer::reverseDirectionOperation(SynchronizerFileItem *item) { if (item->existsInRight() && item->existsInLeft()) { if (item->task() == TT_COPY_TO_LEFT) item->setTask(TT_COPY_TO_RIGHT); else if (item->task() == TT_COPY_TO_RIGHT) item->setTask(TT_COPY_TO_LEFT); } } void Synchronizer::reverseDirection(SynchronizerFileItem *item) { operate(item, reverseDirectionOperation); } void Synchronizer::deleteLeftOperation(SynchronizerFileItem *item) { if (!item->existsInRight() && item->existsInLeft()) item->setTask(TT_DELETE); } void Synchronizer::deleteLeft(SynchronizerFileItem *item) { operate(item, deleteLeftOperation); } void Synchronizer::copyToLeftOperation(SynchronizerFileItem *item) { if (item->existsInRight()) { if (!item->isDir()) item->setTask(TT_COPY_TO_LEFT); else { if (item->existsInLeft() && item->existsInRight()) item->setTask(TT_EQUALS); else if (!item->existsInLeft() && item->existsInRight()) item->setTask(TT_COPY_TO_LEFT); } } } void Synchronizer::copyToLeft(SynchronizerFileItem *item) { operate(item, copyToLeftOperation); while ((item = item->parent()) != nullptr) { if (item->task() != TT_DIFFERS) break; if (item->existsInLeft() && item->existsInRight()) item->setTask(TT_EQUALS); else if (!item->existsInLeft() && item->existsInRight()) item->setTask(TT_COPY_TO_LEFT); } } void Synchronizer::copyToRightOperation(SynchronizerFileItem *item) { if (item->existsInLeft()) { if (!item->isDir()) item->setTask(TT_COPY_TO_RIGHT); else { if (item->existsInLeft() && item->existsInRight()) item->setTask(TT_EQUALS); else if (item->existsInLeft() && !item->existsInRight()) item->setTask(TT_COPY_TO_RIGHT); } } } void Synchronizer::copyToRight(SynchronizerFileItem *item) { operate(item, copyToRightOperation); while ((item = item->parent()) != nullptr) { if (item->task() != TT_DIFFERS && item->task() != TT_DELETE) break; if (item->existsInLeft() && item->existsInRight()) item->setTask(TT_EQUALS); else if (item->existsInLeft() && !item->existsInRight()) item->setTask(TT_COPY_TO_RIGHT); } } bool Synchronizer::totalSizes(int * leftCopyNr, KIO::filesize_t *leftCopySize, int * rightCopyNr, KIO::filesize_t *rightCopySize, int *deleteNr, KIO::filesize_t *deletableSize) { bool hasAnythingToDo = false; *leftCopySize = *rightCopySize = *deletableSize = 0; *leftCopyNr = *rightCopyNr = *deleteNr = 0; QListIterator it(resultList); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); if (item->isMarked()) { switch (item->task()) { case TT_COPY_TO_LEFT: *leftCopySize += item->rightSize(); (*leftCopyNr)++; hasAnythingToDo = true; break; case TT_COPY_TO_RIGHT: *rightCopySize += item->leftSize(); (*rightCopyNr)++; hasAnythingToDo = true; break; case TT_DELETE: *deletableSize += item->leftSize(); (*deleteNr)++; hasAnythingToDo = true; break; default: break; } } } return hasAnythingToDo; } void Synchronizer::swapSides() { QString leftTmp = leftBaseDir; leftBaseDir = rightBaseDir; rightBaseDir = leftTmp; QListIterator it(resultList); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); item->swap(asymmetric); } } void Synchronizer::setScrolling(bool scroll) { autoScroll = scroll; if (autoScroll) { int oldFileCount = fileCount; refresh(true); fileCount = oldFileCount; } } void Synchronizer::synchronize(QWidget *syncWdg, bool leftCopyEnabled, bool rightCopyEnabled, bool deleteEnabled, bool overWrite, int parThreads) { this->leftCopyEnabled = leftCopyEnabled; this->rightCopyEnabled = rightCopyEnabled; this->deleteEnabled = deleteEnabled; this->overWrite = overWrite; this->parallelThreads = parThreads; this->syncDlgWidget = syncWdg; autoSkip = paused = disableNewTasks = false; leftCopyNr = rightCopyNr = deleteNr = 0; leftCopySize = rightCopySize = deleteSize = 0; inTaskFinished = 0; lastTask = nullptr; jobMap.clear(); receivedMap.clear(); resultListIt = resultList; synchronizeLoop(); } void Synchronizer::synchronizeLoop() { if (disableNewTasks) { if (!resultListIt.hasNext() && jobMap.count() == 0) emit synchronizationFinished(); return; } while ((int)jobMap.count() < parallelThreads) { SynchronizerFileItem *task = getNextTask(); if (task == nullptr) { if (jobMap.count() == 0) emit synchronizationFinished(); return; } executeTask(task); if (disableNewTasks) break; } } SynchronizerFileItem * Synchronizer::getNextTask() { TaskType task; SynchronizerFileItem * currentTask; do { if (!resultListIt.hasNext()) return nullptr; currentTask = resultListIt.next(); if (currentTask->isMarked()) { task = currentTask->task(); if (leftCopyEnabled && task == TT_COPY_TO_LEFT) break; else if (rightCopyEnabled && task == TT_COPY_TO_RIGHT) break; else if (deleteEnabled && task == TT_DELETE) break; } } while (true); return lastTask = currentTask; } void Synchronizer::executeTask(SynchronizerFileItem * task) { QString leftDirName = task->leftDirectory(); if (!leftDirName.isEmpty()) leftDirName += '/'; QString rightDirName = task->rightDirectory(); if (!rightDirName.isEmpty()) rightDirName += '/'; QUrl leftURL = Synchronizer::fsUrl(leftBaseDir + leftDirName + task->leftName()); QUrl rightURL = Synchronizer::fsUrl(rightBaseDir + rightDirName + task->rightName()); switch (task->task()) { case TT_COPY_TO_LEFT: if (task->isDir()) { KIO::SimpleJob *job = KIO::mkdir(leftURL); connect(job, &KIO::MkdirJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; disableNewTasks = true; } else { QUrl destURL(leftURL); if (!task->destination().isNull()) destURL = Synchronizer::fsUrl(task->destination()); if (task->rightLink().isNull()) { KIO::FileCopyJob *job = KIO::file_copy(rightURL, destURL, -1, ((overWrite || task->overWrite()) ? KIO::Overwrite : KIO::DefaultFlags) | KIO::HideProgressInfo); connect(job, SIGNAL(processedSize(KJob*,qulonglong)), this, SLOT(slotProcessedSize(KJob*,qulonglong))); connect(job, &KIO::FileCopyJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; } else { KIO::SimpleJob *job = KIO::symlink(task->rightLink(), destURL, ((overWrite || task->overWrite()) ? KIO::Overwrite : KIO::DefaultFlags) | KIO::HideProgressInfo); connect(job, &KIO::SimpleJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; } } break; case TT_COPY_TO_RIGHT: if (task->isDir()) { KIO::SimpleJob *job = KIO::mkdir(rightURL); connect(job, &KIO::SimpleJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; disableNewTasks = true; } else { QUrl destURL(rightURL); if (!task->destination().isNull()) destURL = Synchronizer::fsUrl(task->destination()); if (task->leftLink().isNull()) { KIO::FileCopyJob *job = KIO::file_copy(leftURL, destURL, -1, ((overWrite || task->overWrite()) ? KIO::Overwrite : KIO::DefaultFlags) | KIO::HideProgressInfo); connect(job, SIGNAL(processedSize(KJob*,qulonglong)), this, SLOT(slotProcessedSize(KJob*,qulonglong))); connect(job, &KIO::FileCopyJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; } else { KIO::SimpleJob *job = KIO::symlink(task->leftLink(), destURL, ((overWrite || task->overWrite()) ? KIO::Overwrite : KIO::DefaultFlags) | KIO::HideProgressInfo); connect(job, &KIO::SimpleJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; } } break; case TT_DELETE: { KIO::DeleteJob *job = KIO::del(leftURL, KIO::DefaultFlags); connect(job, &KIO::DeleteJob::result, this, &Synchronizer::slotTaskFinished); jobMap[ job ] = task; } break; default: break; } } void Synchronizer::slotTaskFinished(KJob *job) { inTaskFinished++; SynchronizerFileItem * item = jobMap[ job ]; jobMap.remove(job); KIO::filesize_t receivedSize = 0; if (receivedMap.contains(job)) { receivedSize = receivedMap[ job ]; receivedMap.remove(job); } if (disableNewTasks && item == lastTask) disableNewTasks = false; // the blocker task finished QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; QUrl leftURL = Synchronizer::fsUrl(leftBaseDir + leftDirName + item->leftName()); QUrl rightURL = Synchronizer::fsUrl(rightBaseDir + rightDirName + item->rightName()); do { if (!job->error()) { switch (item->task()) { case TT_COPY_TO_LEFT: if (leftURL.isLocalFile()) { struct utimbuf timestamp; timestamp.actime = time(nullptr); timestamp.modtime = item->rightDate() - timeOffset; utime((const char *)(leftURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), ×tamp); auto newOwnerID = (uid_t) - 1; // chown(2) : -1 means no change if (!item->rightOwner().isEmpty()) { struct passwd* pw = getpwnam(QFile::encodeName(item->rightOwner())); if (pw != nullptr) newOwnerID = pw->pw_uid; } auto newGroupID = (gid_t) - 1; // chown(2) : -1 means no change if (!item->rightGroup().isEmpty()) { struct group* g = getgrnam(QFile::encodeName(item->rightGroup())); if (g != nullptr) newGroupID = g->gr_gid; } int status1 = chown((const char *)(leftURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), newOwnerID, (gid_t) - 1); int status2 = chown((const char *)(leftURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), (uid_t) - 1, newGroupID); if (status1 < 0 || status2 < 0) { // synchronizer currently ignores chown errors } chmod((const char *)(leftURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), item->rightMode() & 07777); #ifdef HAVE_POSIX_ACL if (!item->rightACL().isNull()) { acl_t acl = acl_from_text(item->rightACL().toLatin1()); if (acl && !acl_valid(acl)) acl_set_file(leftURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit(), ACL_TYPE_ACCESS, acl); if (acl) acl_free(acl); } #endif } break; case TT_COPY_TO_RIGHT: if (rightURL.isLocalFile()) { struct utimbuf timestamp; timestamp.actime = time(nullptr); timestamp.modtime = item->leftDate() + timeOffset; utime((const char *)(rightURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), ×tamp); auto newOwnerID = (uid_t) - 1; // chown(2) : -1 means no change if (!item->leftOwner().isEmpty()) { struct passwd* pw = getpwnam(QFile::encodeName(item->leftOwner())); if (pw != nullptr) newOwnerID = pw->pw_uid; } auto newGroupID = (gid_t) - 1; // chown(2) : -1 means no change if (!item->leftGroup().isEmpty()) { struct group* g = getgrnam(QFile::encodeName(item->leftGroup())); if (g != nullptr) newGroupID = g->gr_gid; } int status1 = chown((const char *)(rightURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), newOwnerID, (uid_t) - 1); int status2 = chown((const char *)(rightURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), (uid_t) - 1, newGroupID); if (status1 < 0 || status2 < 0) { // synchronizer currently ignores chown errors } chmod((const char *)(rightURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit()), item->leftMode() & 07777); #ifdef HAVE_POSIX_ACL if (!item->leftACL().isNull()) { acl_t acl = acl_from_text(item->leftACL().toLatin1()); if (acl && !acl_valid(acl)) acl_set_file(rightURL.adjusted(QUrl::StripTrailingSlash).path().toLocal8Bit(), ACL_TYPE_ACCESS, acl); if (acl) acl_free(acl); } #endif } break; default: break; } } else { if (job->error() == KIO::ERR_FILE_ALREADY_EXIST && item->task() != TT_DELETE) { KIO::RenameDialog_Result result; QString newDest; if (autoSkip) break; auto *ui = static_cast(job->uiDelegate()); ui->setWindow(syncDlgWidget); if (item->task() == TT_COPY_TO_LEFT) { result = ui->askFileRename(job, i18n("File Already Exists"), rightURL, leftURL, KIO::RenameDialog_Overwrite | KIO::RenameDialog_Skip | KIO::RenameDialog_MultipleItems, newDest, item->rightSize(), item->leftSize(), QDateTime(), QDateTime(), QDateTime::fromTime_t(item->rightDate()), QDateTime::fromTime_t(item->leftDate())); } else { result = ui->askFileRename(job, i18n("File Already Exists"), leftURL, rightURL, KIO::RenameDialog_Overwrite | KIO::RenameDialog_Skip | KIO::RenameDialog_MultipleItems, newDest, item->leftSize(), item->rightSize(), QDateTime(), QDateTime(), QDateTime::fromTime_t(item->leftDate()), QDateTime::fromTime_t(item->rightDate())); } switch (result) { case KIO::R_RENAME: item->setDestination(newDest); executeTask(item); inTaskFinished--; return; case KIO::R_OVERWRITE: item->setOverWrite(); executeTask(item); inTaskFinished--; return; case KIO::R_OVERWRITE_ALL: overWrite = true; executeTask(item); inTaskFinished--; return; case KIO::R_AUTO_SKIP: autoSkip = true; case KIO::R_SKIP: default: break; } break; } if (job->error() != KIO::ERR_DOES_NOT_EXIST || item->task() != TT_DELETE) { if (autoSkip) break; QString error; switch (item->task()) { case TT_COPY_TO_LEFT: error = i18n("Error at copying file %1 to %2.", rightURL.toDisplayString(QUrl::PreferLocalFile), leftURL.toDisplayString(QUrl::PreferLocalFile)); break; case TT_COPY_TO_RIGHT: error = i18n("Error at copying file %1 to %2.", leftURL.toDisplayString(QUrl::PreferLocalFile), rightURL.toDisplayString(QUrl::PreferLocalFile)); break; case TT_DELETE: error = i18n("Error at deleting file %1.", leftURL.toDisplayString(QUrl::PreferLocalFile)); break; default: break; } auto *ui = static_cast(job->uiDelegate()); ui->setWindow(syncDlgWidget); KIO::SkipDialog_Result result = ui->askSkip(job, KIO::SkipDialog_MultipleItems, error); switch (result) { case KIO::S_CANCEL: executeTask(item); /* simply retry */ inTaskFinished--; return; case KIO::S_AUTO_SKIP: autoSkip = true; default: break; } } } } while (false); switch (item->task()) { case TT_COPY_TO_LEFT: leftCopyNr++; leftCopySize += item->rightSize() - receivedSize; break; case TT_COPY_TO_RIGHT: rightCopyNr++; rightCopySize += item->leftSize() - receivedSize; break; case TT_DELETE: deleteNr++; deleteSize += item->leftSize() - receivedSize; break; default: break; } emit processedSizes(leftCopyNr, leftCopySize, rightCopyNr, rightCopySize, deleteNr, deleteSize); if (--inTaskFinished == 0) { if (paused) emit pauseAccepted(); else synchronizeLoop(); } } void Synchronizer::slotProcessedSize(KJob * job , qulonglong size) { KIO::filesize_t dl = 0, dr = 0, dd = 0; SynchronizerFileItem * item = jobMap[ job ]; KIO::filesize_t lastProcessedSize = 0; if (receivedMap.contains(job)) lastProcessedSize = receivedMap[ job ]; receivedMap[ job ] = size; switch (item->task()) { case TT_COPY_TO_LEFT: dl = size - lastProcessedSize; break; case TT_COPY_TO_RIGHT: dr = size - lastProcessedSize; break; case TT_DELETE: dd = size - lastProcessedSize; break; default: break; } emit processedSizes(leftCopyNr, leftCopySize += dl, rightCopyNr, rightCopySize += dr, deleteNr, deleteSize += dd); } void Synchronizer::pause() { paused = true; } void Synchronizer::resume() { paused = false; synchronizeLoop(); } QString Synchronizer::leftBaseDirectory() { return leftBaseDir; } QString Synchronizer::rightBaseDirectory() { return rightBaseDir; } KgetProgressDialog::KgetProgressDialog(QWidget *parent, const QString &caption, const QString &text, bool modal) : QDialog(parent) { if (caption.isEmpty()) setWindowTitle(caption); setModal(modal); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(new QLabel(text)); mProgressBar = new QProgressBar; mainLayout->addWidget(mProgressBar); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); mPauseButton = new QPushButton(i18n("Pause")); buttonBox->addButton(mPauseButton, QDialogButtonBox::ActionRole); buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); connect(mPauseButton, &QPushButton::clicked, this, &KgetProgressDialog::slotPause); connect(buttonBox, &QDialogButtonBox::rejected, this, &KgetProgressDialog::slotCancel); mCancelled = mPaused = false; } void KgetProgressDialog::slotPause() { if ((mPaused = !mPaused) == false) mPauseButton->setText(i18n("Pause")); else mPauseButton->setText(i18n("Resume")); } void KgetProgressDialog::slotCancel() { mCancelled = true; reject(); } void Synchronizer::synchronizeWithKGet() { bool isLeftLocal = QUrl::fromUserInput(leftBaseDirectory(), QString(), QUrl::AssumeLocalFile).isLocalFile(); KgetProgressDialog *progDlg = nullptr; int processedCount = 0, totalCount = 0; QListIterator it(resultList); while (it.hasNext()) if (it.next()->isMarked()) totalCount++; it.toFront(); while (it.hasNext()) { SynchronizerFileItem * item = it.next(); if (item->isMarked()) { QUrl downloadURL; QUrl destURL; QString destDir; QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; if (progDlg == nullptr) { progDlg = new KgetProgressDialog(krMainWindow, i18n("Krusader::Synchronizer"), i18n("Feeding the URLs to KGet"), true); progDlg->progressBar()->setMaximum(totalCount); progDlg->show(); qApp->processEvents(); } if (item->task() == TT_COPY_TO_RIGHT && !isLeftLocal) { downloadURL = Synchronizer::fsUrl(leftBaseDirectory() + leftDirName + item->leftName()); destDir = rightBaseDirectory() + rightDirName; destURL = Synchronizer::fsUrl(destDir + item->rightName()); if (item->isDir()) destDir += item->leftName(); } if (item->task() == TT_COPY_TO_LEFT && isLeftLocal) { downloadURL = Synchronizer::fsUrl(rightBaseDirectory() + rightDirName + item->rightName()); destDir = leftBaseDirectory() + leftDirName; destURL = Synchronizer::fsUrl(destDir + item->leftName()); if (item->isDir()) destDir += item->rightName(); } // creating the directory system for (int i = 0; i >= 0 ; i = destDir.indexOf('/', i + 1)) if (!QDir(destDir.left(i)).exists()) QDir().mkdir(destDir.left(i)); if (!item->isDir() && !downloadURL.isEmpty()) { if (QFile(destURL.path()).exists()) QFile(destURL.path()).remove(); QString source = downloadURL.toDisplayString(); if (source.indexOf('@') >= 2) { /* is this an ftp proxy URL? */ int lastAt = source.lastIndexOf('@'); QString startString = source.left(lastAt); QString endString = source.mid(lastAt); startString.replace('@', "%40"); source = startString + endString; } KProcess p; p << KrServices::fullPathName("kget") << source << destURL.path(); if (!p.startDetached()) KMessageBox::error(parentWidget, i18n("Error executing %1.", KrServices::fullPathName("kget"))); } progDlg->progressBar()->setValue(++processedCount); QTime t; t.start(); bool canExit = false; do { qApp->processEvents(); if (progDlg->wasCancelled()) break; canExit = (t.elapsed() > 100); if (progDlg->isPaused() || !canExit) usleep(10000); } while (progDlg->isPaused() || !canExit); if (progDlg->wasCancelled()) break; } } if (progDlg) delete progDlg; } bool Synchronizer::isDir(const FileItem *file) { if (followSymLinks) { return file->isDir(); } else { return file->isDir() && !file->isSymLink(); } } QString Synchronizer::readLink(const FileItem *file) { if (file->isSymLink()) return file->getSymDest(); else return QString(); } SynchronizerFileItem *Synchronizer::getItemAt(unsigned ndx) { if (ndx < (unsigned)resultList.count()) return resultList.at(ndx); else return nullptr; } diff --git a/krusader/Synchronizer/synchronizer.h b/krusader/Synchronizer/synchronizer.h index 150d5809..4a2264d2 100644 --- a/krusader/Synchronizer/synchronizer.h +++ b/krusader/Synchronizer/synchronizer.h @@ -1,233 +1,233 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 SYNCHRONIZER_H #define SYNCHRONIZER_H // QtCore #include #include #include // QtGui #include // QtWidgets #include #include # include "synchronizertask.h" #include "synchronizerfileitem.h" class KRQuery; class FileItem; class Synchronizer : public QObject { Q_OBJECT private: int displayUpdateCount; // the display is refreshed after every x-th change public: Synchronizer(); ~Synchronizer() override; int compare(QString leftURL, QString rightURL, KRQuery *query, bool subDirs, bool symLinks, bool igDate, bool asymm, bool cmpByCnt, bool igCase, bool autoSc, QStringList &selFiles, int equThres, int timeOffs, int parThreads, bool hiddenFiles); void stop() { stopped = true; } void setMarkFlags(bool left, bool equal, bool differs, bool right, bool dup, bool sing, bool del); int refresh(bool nostatus = false); bool totalSizes(int *, KIO::filesize_t *, int *, KIO::filesize_t *, int *, KIO::filesize_t *); void synchronize(QWidget *, bool leftCopyEnabled, bool rightCopyEnabled, bool deleteEnabled, bool overWrite, int parThreads); void synchronizeWithKGet(); void setScrolling(bool scroll); void pause(); void resume(); void swapSides(); void reset(); void clearLists(); void exclude(SynchronizerFileItem *); void restore(SynchronizerFileItem *); void reverseDirection(SynchronizerFileItem *); void copyToLeft(SynchronizerFileItem *); void copyToRight(SynchronizerFileItem *); void deleteLeft(SynchronizerFileItem *); QString leftBaseDirectory(); QString rightBaseDirectory(); static QString getTaskTypeName(TaskType taskType); - static QUrl fsUrl(QString strUrl); + static QUrl fsUrl(const QString& strUrl); SynchronizerFileItem *getItemAt(unsigned ndx); void setParentWidget(QWidget * widget) { parentWidget = widget; } void compareContentResult(SynchronizerFileItem * item, bool result); signals: void comparedFileData(SynchronizerFileItem *); void markChanged(SynchronizerFileItem *, bool); void synchronizationFinished(); void processedSizes(int, KIO::filesize_t, int, KIO::filesize_t, int, KIO::filesize_t); void pauseAccepted(); void statusInfo(QString); public slots: void slotTaskFinished(KJob*); void slotProcessedSize(KJob * , qulonglong); private: bool isDir(const FileItem * file); QString readLink(const FileItem * file); void compareDirectory(SynchronizerFileItem *, SynchronizerDirList *, SynchronizerDirList *, const QString &leftDir, const QString &rightDir); void addSingleDirectory(SynchronizerFileItem *, SynchronizerDirList *, const QString &, bool); SynchronizerFileItem * addItem(SynchronizerFileItem *, const QString &, const QString &, const QString &, const QString &, bool, bool, KIO::filesize_t, KIO::filesize_t, time_t, time_t, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, mode_t, mode_t, const QString &, const QString &, TaskType, bool, bool); SynchronizerFileItem * addLeftOnlyItem(SynchronizerFileItem *, const QString &, const QString &, KIO::filesize_t, time_t, const QString &, const QString &, const QString &, mode_t, const QString &, bool isDir = false, bool isTemp = false); SynchronizerFileItem * addRightOnlyItem(SynchronizerFileItem *, const QString &, const QString &, KIO::filesize_t, time_t, const QString &, const QString &, const QString &, mode_t, const QString &, bool isDir = false, bool isTemp = false); SynchronizerFileItem * addDuplicateItem(SynchronizerFileItem *, const QString &, const QString &, const QString &, const QString &, KIO::filesize_t, KIO::filesize_t, time_t, time_t, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, mode_t, mode_t, const QString &, const QString &, bool isDir = false, bool isTemp = false); bool isMarked(TaskType task, bool dupl); bool markParentDirectories(SynchronizerFileItem *); void synchronizeLoop(); SynchronizerFileItem * getNextTask(); void executeTask(SynchronizerFileItem * task); void setPermanent(SynchronizerFileItem *); void operate(SynchronizerFileItem *item, void (*)(SynchronizerFileItem *)); void compareLoop(); static void excludeOperation(SynchronizerFileItem *item); static void restoreOperation(SynchronizerFileItem *item); static void reverseDirectionOperation(SynchronizerFileItem *item); static void copyToLeftOperation(SynchronizerFileItem *item); static void copyToRightOperation(SynchronizerFileItem *item); static void deleteLeftOperation(SynchronizerFileItem *item); protected: bool recurseSubDirs; // walk through subdirectories also bool followSymLinks; // follow the symbolic links bool ignoreDate; // don't use date info at comparing bool asymmetric; // asymmetric directory update bool cmpByContent; // compare the files by content bool ignoreCase; // case insensitive synchronization for Windows fs bool autoScroll; // automatic update of the directory QList resultList; // the found files QList temporaryList; // temporary files QString leftBaseDir; // the left-side base directory QString rightBaseDir; // the right-side base directory QStringList excludedPaths; // list of the excluded paths KRQuery *query; // the filter used for the query bool stopped; // 'Stop' button was pressed int equalsThreshold;// threshold to treat files equal int timeOffset; // time offset between the left and right sides bool ignoreHidden; // ignores the hidden files bool markEquals; // show the equal files bool markDiffers; // show the different files bool markCopyToLeft; // show the files to copy from right to left bool markCopyToRight;// show the files to copy from left to right bool markDeletable; // show the files to be deleted bool markDuplicates; // show the duplicated items bool markSingles; // show the single items bool leftCopyEnabled;// copy to left is enabled at synchronize bool rightCopyEnabled;// copy to right is enabled at synchronize bool deleteEnabled; // delete is enabled at synchronize bool overWrite; // overwrite or query each modification bool autoSkip; // automatic skipping bool paused; // pause flag bool disableNewTasks;// at mkdir the further task creation is disabled int leftCopyNr; // the file number copied to left int rightCopyNr; // the file number copied to right int deleteNr; // the number of the deleted files int parallelThreads;// the number of the parallel processing threads KIO::filesize_t leftCopySize; // the total size copied to left KIO::filesize_t rightCopySize; // the total size copied to right KIO::filesize_t deleteSize; // the size of the deleted files int comparedDirs; // the number of the compared directories int fileCount; // the number of counted files private: QList stack; // stack for comparing QMap jobMap; // job maps QMap receivedMap; // the received file size SynchronizerFileItem *lastTask; // reference to the last stack int inTaskFinished; // counter of quasy 'threads' in slotTaskFinished QStringList selectedFiles; // the selected files to compare QWidget *parentWidget; // the parent widget QWidget *syncDlgWidget; // the synchronizer dialog widget QListIterator resultListIt; // iterator for result list }; class QProgressBar; class KgetProgressDialog : public QDialog { Q_OBJECT public: explicit KgetProgressDialog(QWidget *parent = nullptr, const QString &caption = QString(), const QString &text = QString(), bool modal = false); QProgressBar *progressBar() { return mProgressBar; } public slots: void slotPause(); void slotCancel(); bool wasCancelled() { return mCancelled; } bool isPaused() { return mPaused; } private: QProgressBar *mProgressBar; QPushButton *mPauseButton; bool mCancelled; bool mPaused; }; #endif /* __SYNCHRONIZER_H__ */ diff --git a/krusader/Synchronizer/synchronizergui.cpp b/krusader/Synchronizer/synchronizergui.cpp index 7e3c88db..880113cf 100644 --- a/krusader/Synchronizer/synchronizergui.cpp +++ b/krusader/Synchronizer/synchronizergui.cpp @@ -1,1623 +1,1624 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 "synchronizergui.h" #include "../krglobal.h" #include "../filelisticon.h" #include "../defaults.h" #include "../krusaderview.h" #include "../Panel/listpanel.h" #include "../Panel/panelfunc.h" #include "../FileSystem/krpermhandler.h" #include "../KViewer/krviewer.h" #include "../Dialogs/krspwidgets.h" #include "../FileSystem/krquery.h" #include "../krservices.h" #include "../krslots.h" #include "synchronizedialog.h" #include "feedtolistboxdialog.h" #include "synchronizercolors.h" // QtCore #include #include #include #include // QtGui #include #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include class SynchronizerListView : public KrTreeWidget { private: Synchronizer *synchronizer; bool isLeft; public: SynchronizerListView(Synchronizer * sync, QWidget * parent) : KrTreeWidget(parent), synchronizer(sync) { } void mouseMoveEvent(QMouseEvent * e) Q_DECL_OVERRIDE { isLeft = ((e->modifiers() & Qt::ShiftModifier) == 0); KrTreeWidget::mouseMoveEvent(e); } void startDrag(Qt::DropActions /* supportedActs */) Q_DECL_OVERRIDE { QList urls; unsigned ndx = 0; SynchronizerFileItem *currentItem; while ((currentItem = synchronizer->getItemAt(ndx++)) != nullptr) { auto *viewItem = (SynchronizerGUI::SyncViewItem *)currentItem->userData(); if (!viewItem || !viewItem->isSelected() || viewItem->isHidden()) continue; SynchronizerFileItem *item = viewItem->synchronizerItemRef(); if (item) { if (isLeft && item->existsInLeft()) { QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QUrl leftURL = Synchronizer::fsUrl(synchronizer->leftBaseDirectory() + leftDirName + item->leftName()); urls.push_back(leftURL); } else if (!isLeft && item->existsInRight()) { QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; QUrl rightURL = Synchronizer::fsUrl(synchronizer->rightBaseDirectory() + rightDirName + item->rightName()); urls.push_back(rightURL); } } } if (urls.count() == 0) return; auto *drag = new QDrag(this); auto *mimeData = new QMimeData; mimeData->setImageData(FileListIcon(isLeft ? "arrow-left-double" : "arrow-right-double").pixmap()); mimeData->setUrls(urls); drag->setMimeData(mimeData); drag->start(); } }; SynchronizerGUI::SynchronizerGUI(QWidget* parent, QUrl leftURL, QUrl rightURL, QStringList selList) : QDialog(parent) { - initGUI(QString(), leftURL, rightURL, selList); + initGUI(QString(), std::move(leftURL), std::move(rightURL), std::move(selList)); } SynchronizerGUI::SynchronizerGUI(QWidget* parent, QString profile) : QDialog(parent) { - initGUI(profile, QUrl(), QUrl(), QStringList()); + initGUI(std::move(profile), QUrl(), QUrl(), QStringList()); } -void SynchronizerGUI::initGUI(QString profileName, QUrl leftURL, QUrl rightURL, QStringList selList) +void SynchronizerGUI::initGUI(const QString& profileName, QUrl leftURL, QUrl rightURL, QStringList selList) { setAttribute(Qt::WA_DeleteOnClose); - selectedFiles = selList; + selectedFiles = std::move(selList); isComparing = wasClosed = wasSync = false; firstResize = true; sizeX = sizeY = -1; bool equalSizes = false; hasSelectedFiles = (selectedFiles.count() != 0); if (leftURL.isEmpty()) leftURL = QUrl::fromLocalFile(ROOT_DIR); if (rightURL.isEmpty()) rightURL = QUrl::fromLocalFile(ROOT_DIR); setWindowTitle(i18n("Krusader::Synchronize Folders")); auto *synchGrid = new QGridLayout(this); synchGrid->setSpacing(6); synchGrid->setContentsMargins(11, 11, 11, 11); synchronizerTabs = new QTabWidget(this); /* ============================== Compare groupbox ============================== */ QWidget *synchronizerTab = new QWidget(this); auto *synchronizerGrid = new QGridLayout(synchronizerTab); synchronizerGrid->setSpacing(6); synchronizerGrid->setContentsMargins(11, 11, 11, 11); auto *compareDirs = new QGroupBox(synchronizerTab); compareDirs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); compareDirs->setTitle(i18n("Folder Comparison")); auto *grid = new QGridLayout(compareDirs); grid->setSpacing(6); grid->setContentsMargins(11, 11, 11, 11); leftDirLabel = new QLabel(compareDirs); leftDirLabel->setAlignment(Qt::AlignHCenter); grid->addWidget(leftDirLabel, 0 , 0); QLabel *filterLabel = new QLabel(compareDirs); filterLabel->setText(i18n("File &Filter:")); filterLabel->setAlignment(Qt::AlignHCenter); grid->addWidget(filterLabel, 0 , 1); rightDirLabel = new QLabel(compareDirs); rightDirLabel->setAlignment(Qt::AlignHCenter); grid->addWidget(rightDirLabel, 0 , 2); KConfigGroup group(krConfig, "Synchronize"); leftLocation = new KHistoryComboBox(false, compareDirs); leftLocation->setMaxCount(25); // remember 25 items leftLocation->setDuplicatesEnabled(false); leftLocation->setEditable(true); leftLocation->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); QStringList list = group.readEntry("Left Folder History", QStringList()); leftLocation->setHistoryItems(list); auto *leftUrlReq = new KUrlRequester(leftLocation, compareDirs); leftUrlReq->setUrl(leftURL); leftUrlReq->setMode(KFile::Directory); leftUrlReq->setMinimumWidth(250); grid->addWidget(leftUrlReq, 1 , 0); leftLocation->setWhatsThis(i18n("The left base folder used during the synchronization process.")); leftUrlReq->setEnabled(!hasSelectedFiles); leftLocation->setEnabled(!hasSelectedFiles); leftDirLabel->setBuddy(leftLocation); fileFilter = new KHistoryComboBox(false, compareDirs); fileFilter->setMaxCount(25); // remember 25 items fileFilter->setDuplicatesEnabled(false); fileFilter->setMinimumWidth(100); fileFilter->setMaximumWidth(100); fileFilter->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); list = group.readEntry("File Filter", QStringList()); fileFilter->setHistoryItems(list); fileFilter->setEditText("*"); grid->addWidget(fileFilter, 1 , 1); filterLabel->setBuddy(fileFilter); QString wtFilter = "

" + i18n("

The filename filtering criteria is defined here.

You can make use of wildcards. Multiple patterns are separated by space (means logical OR) and patterns are excluded from the search using the pipe symbol.

If the pattern is ended with a slash (*pattern*/), that means that pattern relates to recursive search of folders.

  • pattern - means to search those files/folders that name is pattern, recursive search goes through all subfolders independently of the value of pattern
  • pattern/ - means to search all files/folders, but recursive search goes through/excludes the folders that name is pattern

It is allowed to use quotation marks for names that contain space. Filter \"Program Files\" searches out those files/folders that name is Program Files.

Examples:

  • *.o
  • *.h *.c\?\?
  • *.cpp *.h | *.moc.cpp
  • * | .svn/ .git/

Note: the search term 'text' is equivalent to '*text*'.

"); fileFilter->setWhatsThis(wtFilter); filterLabel->setWhatsThis(wtFilter); rightLocation = new KHistoryComboBox(compareDirs); rightLocation->setMaxCount(25); // remember 25 items rightLocation->setDuplicatesEnabled(false); rightLocation->setEditable(true); rightLocation->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); list = group.readEntry("Right Folder History", QStringList()); rightLocation->setHistoryItems(list); auto *rightUrlReq = new KUrlRequester(rightLocation, compareDirs); rightUrlReq->setUrl(rightURL); rightUrlReq->setMode(KFile::Directory); rightUrlReq->setMinimumWidth(250); grid->addWidget(rightUrlReq, 1 , 2); rightLocation->setWhatsThis(i18n("The right base folder used during the synchronization process.")); rightUrlReq->setEnabled(!hasSelectedFiles); rightLocation->setEnabled(!hasSelectedFiles); rightDirLabel->setBuddy(rightLocation); QWidget *optionWidget = new QWidget(compareDirs); auto *optionBox = new QHBoxLayout(optionWidget); optionBox->setContentsMargins(0, 0, 0, 0); QWidget *optionGridWidget = new QWidget(optionWidget); auto *optionGrid = new QGridLayout(optionGridWidget); optionBox->addWidget(optionGridWidget); cbSubdirs = new QCheckBox(i18n("Recurse subfolders"), optionGridWidget); cbSubdirs->setChecked(group.readEntry("Recurse Subdirectories", _RecurseSubdirs)); optionGrid->addWidget(cbSubdirs, 0, 0); cbSubdirs->setWhatsThis(i18n("Compare not only the base folders but their subfolders as well.")); cbSymlinks = new QCheckBox(i18n("Follow symlinks"), optionGridWidget); cbSymlinks->setChecked(group.readEntry("Follow Symlinks", _FollowSymlinks)); cbSymlinks->setEnabled(cbSubdirs->isChecked()); optionGrid->addWidget(cbSymlinks, 0, 1); cbSymlinks->setWhatsThis(i18n("Follow symbolic links during the compare process.")); cbByContent = new QCheckBox(i18n("Compare by content"), optionGridWidget); cbByContent->setChecked(group.readEntry("Compare By Content", _CompareByContent)); optionGrid->addWidget(cbByContent, 0, 2); cbByContent->setWhatsThis(i18n("Compare duplicated files with same size by content.")); cbIgnoreDate = new QCheckBox(i18n("Ignore Date"), optionGridWidget); cbIgnoreDate->setChecked(group.readEntry("Ignore Date", _IgnoreDate)); optionGrid->addWidget(cbIgnoreDate, 1, 0); cbIgnoreDate->setWhatsThis(i18n("

Ignore date information during the compare process.

Note: useful if the files are located on network filesystems or in archives.

")); cbAsymmetric = new QCheckBox(i18n("Asymmetric"), optionGridWidget); cbAsymmetric->setChecked(group.readEntry("Asymmetric", _Asymmetric)); optionGrid->addWidget(cbAsymmetric, 1, 1); cbAsymmetric->setWhatsThis(i18n("

Asymmetric mode

The left side is the destination, the right is the source folder. Files existing only in the left folder will be deleted, the other differing ones will be copied from right to left.

Note: useful when updating a folder from a file server.

")); cbIgnoreCase = new QCheckBox(i18n("Ignore Case"), optionGridWidget); cbIgnoreCase->setChecked(group.readEntry("Ignore Case", _IgnoreCase)); optionGrid->addWidget(cbIgnoreCase, 1, 2); cbIgnoreCase->setWhatsThis(i18n("

Case insensitive filename compare.

Note: useful when synchronizing Windows filesystems.

")); /* =========================== Show options groupbox ============================= */ auto *showOptions = new QGroupBox(optionWidget); optionBox->addWidget(showOptions); showOptions->setTitle(i18n("S&how options")); showOptions->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); auto *showOptionsLayout = new QGridLayout(showOptions); showOptionsLayout->setSpacing(4); showOptionsLayout->setContentsMargins(11, 11, 11, 11); bool checked; QString description; checked = group.readEntry("LeftToRight Button", _BtnLeftToRight); description = i18n("Show files marked to Copy from left to right."); btnLeftToRight = createButton(showOptions, "arrow-right", checked, Qt::CTRL + Qt::Key_L, description, ">"); showOptionsLayout->addWidget(btnLeftToRight, 0, 0); checked = group.readEntry("Equals Button", _BtnEquals); description = i18n("Show files considered to be identical."); btnEquals = createButton(showOptions, "equals", checked, Qt::CTRL + Qt::Key_E, description, "="); showOptionsLayout->addWidget(btnEquals, 0, 1); checked = group.readEntry("Differents Button", _BtnDifferents); description = i18n("Show excluded files."); btnDifferents = createButton(showOptions, "unequals", checked, Qt::CTRL + Qt::Key_D, description, "!="); showOptionsLayout->addWidget(btnDifferents, 0, 2); checked = group.readEntry("RightToLeft Button", _BtnRightToLeft); description = i18n("Show files marked to Copy from right to left."); btnRightToLeft = createButton(showOptions, "arrow-left", checked, Qt::CTRL + Qt::Key_R, description, "<"); showOptionsLayout->addWidget(btnRightToLeft, 0, 3); checked = group.readEntry("Deletable Button", _BtnDeletable); description = i18n("Show files marked to delete."); btnDeletable = createButton(showOptions, "user-trash", checked, Qt::CTRL + Qt::Key_T, description); showOptionsLayout->addWidget(btnDeletable, 0, 4); checked = group.readEntry("Duplicates Button", _BtnDuplicates); description = i18n("Show files that exist on both sides."); btnDuplicates = createButton(showOptions, "arrow-up", checked, Qt::CTRL + Qt::Key_I, description, i18n("Duplicates"), true); showOptionsLayout->addWidget(btnDuplicates, 0, 5); checked = group.readEntry("Singles Button", _BtnSingles); description = i18n("Show files that exist on one side only."); btnSingles = createButton(showOptions, "arrow-down", checked, Qt::CTRL + Qt::Key_N, description, i18n("Singles"), true); showOptionsLayout->addWidget(btnSingles, 0, 6); grid->addWidget(optionWidget, 2, 0, 1, 3); synchronizerGrid->addWidget(compareDirs, 0, 0); /* ========================= Synchronization list view ========================== */ syncList = new SynchronizerListView(&synchronizer, synchronizerTab); // create the main container syncList->setWhatsThis(i18n("The compare results of the synchronizer (Ctrl+M).")); syncList->setAutoFillBackground(true); syncList->installEventFilter(this); KConfigGroup gl(krConfig, "Look&Feel"); syncList->setFont(gl.readEntry("Filelist Font", _FilelistFont)); syncList->setBackgroundRole(QPalette::Window); syncList->setAutoFillBackground(true); QStringList labels; labels << i18nc("@title:column file name", "Name"); labels << i18nc("@title:column", "Size"); labels << i18nc("@title:column", "Date"); labels << i18n("<=>"); labels << i18nc("@title:column", "Date"); labels << i18nc("@title:column", "Size"); labels << i18nc("@title:column file name", "Name"); syncList->setHeaderLabels(labels); syncList->header()->setSectionResizeMode(QHeaderView::Interactive); if (group.hasKey("State")) syncList->header()->restoreState(group.readEntry("State", QByteArray())); else { int i = QFontMetrics(syncList->font()).width("W"); int j = QFontMetrics(syncList->font()).width("0"); j = (i > j ? i : j); int typeWidth = j * 7 / 2; syncList->setColumnWidth(0, typeWidth * 4); syncList->setColumnWidth(1, typeWidth * 2); syncList->setColumnWidth(2, typeWidth * 3); syncList->setColumnWidth(3, typeWidth * 1); syncList->setColumnWidth(4, typeWidth * 3); syncList->setColumnWidth(5, typeWidth * 2); syncList->setColumnWidth(6, typeWidth * 4); equalSizes = true; } syncList->setAllColumnsShowFocus(true); syncList->setSelectionMode(QAbstractItemView::ExtendedSelection); syncList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); syncList->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); syncList->header()->setSortIndicatorShown(false); syncList->setSortingEnabled(false); syncList->setRootIsDecorated(true); syncList->setIndentation(10); syncList->setDragEnabled(true); syncList->setAutoFillBackground(true); synchronizerGrid->addWidget(syncList, 1, 0); synchronizerTabs->addTab(synchronizerTab, i18n("&Synchronizer")); synchGrid->addWidget(synchronizerTabs, 0, 0); filterTabs = FilterTabs::addTo(synchronizerTabs, FilterTabs::HasDontSearchIn); generalFilter = (GeneralFilter *)filterTabs->get("GeneralFilter"); generalFilter->searchFor->setEditText(fileFilter->currentText()); generalFilter->searchForCase->setChecked(true); // creating the time shift, equality threshold, hidden files options auto *optionsGroup = new QGroupBox(generalFilter); optionsGroup->setTitle(i18n("&Options")); auto *optionsLayout = new QGridLayout(optionsGroup); optionsLayout->setAlignment(Qt::AlignTop); optionsLayout->setSpacing(6); optionsLayout->setContentsMargins(11, 11, 11, 11); QLabel * parallelThreadsLabel = new QLabel(i18n("Parallel threads:"), optionsGroup); optionsLayout->addWidget(parallelThreadsLabel, 0, 0); parallelThreadsSpinBox = new QSpinBox(optionsGroup); parallelThreadsSpinBox->setMinimum(1); parallelThreadsSpinBox->setMaximum(15); int parThreads = group.readEntry("Parallel Threads", 1); parallelThreadsSpinBox->setValue(parThreads); optionsLayout->addWidget(parallelThreadsSpinBox, 0, 1); QLabel * equalityLabel = new QLabel(i18n("Equality threshold:"), optionsGroup); optionsLayout->addWidget(equalityLabel, 1, 0); equalitySpinBox = new QSpinBox(optionsGroup); equalitySpinBox->setMaximum(9999); optionsLayout->addWidget(equalitySpinBox, 1, 1); equalityUnitCombo = new QComboBox(optionsGroup); equalityUnitCombo->addItem(i18n("sec")); equalityUnitCombo->addItem(i18n("min")); equalityUnitCombo->addItem(i18n("hour")); equalityUnitCombo->addItem(i18n("day")); optionsLayout->addWidget(equalityUnitCombo, 1, 2); QLabel * timeShiftLabel = new QLabel(i18n("Time shift (right-left):"), optionsGroup); optionsLayout->addWidget(timeShiftLabel, 2, 0); timeShiftSpinBox = new QSpinBox(optionsGroup); timeShiftSpinBox->setMinimum(-9999); timeShiftSpinBox->setMaximum(9999); optionsLayout->addWidget(timeShiftSpinBox, 2, 1); timeShiftUnitCombo = new QComboBox(optionsGroup); timeShiftUnitCombo->addItem(i18n("sec")); timeShiftUnitCombo->addItem(i18n("min")); timeShiftUnitCombo->addItem(i18n("hour")); timeShiftUnitCombo->addItem(i18n("day")); optionsLayout->addWidget(timeShiftUnitCombo, 2, 2); QFrame *line = new QFrame(optionsGroup); line->setFrameStyle(QFrame::HLine | QFrame::Sunken); optionsLayout->addWidget(line, 3, 0, 1, 3); ignoreHiddenFilesCB = new QCheckBox(i18n("Ignore hidden files"), optionsGroup); optionsLayout->addWidget(ignoreHiddenFilesCB, 4, 0, 1, 3); generalFilter->middleLayout->addWidget(optionsGroup); /* ================================== Buttons =================================== */ auto *buttons = new QHBoxLayout; buttons->setSpacing(6); buttons->setContentsMargins(0, 0, 0, 0); profileManager = new ProfileManager("SynchronizerProfile", this); profileManager->setShortcut(Qt::CTRL + Qt::Key_P); profileManager->setWhatsThis(i18n("Profile manager (Ctrl+P).")); buttons->addWidget(profileManager); btnSwapSides = new QPushButton(this); btnSwapSides->setIcon(Icon("document-swap")); btnSwapSides->setShortcut(Qt::CTRL + Qt::Key_S); btnSwapSides->setWhatsThis(i18n("Swap sides (Ctrl+S).")); buttons->addWidget(btnSwapSides); statusLabel = new QLabel(this); buttons->addWidget(statusLabel); auto* spacer = new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); buttons->addItem(spacer); btnCompareDirs = new QPushButton(this); btnCompareDirs->setText(i18n("Compare")); btnCompareDirs->setIcon(Icon("kr_comparedirs")); btnCompareDirs->setDefault(true); buttons->addWidget(btnCompareDirs); btnScrollResults = new QPushButton(this); btnScrollResults->setCheckable(true); btnScrollResults->setChecked(group.readEntry("Scroll Results", _ScrollResults)); btnScrollResults->hide(); if (btnScrollResults->isChecked()) btnScrollResults->setText(i18n("Quiet")); else btnScrollResults->setText(i18n("Scroll Results")); buttons->addWidget(btnScrollResults); btnStopComparing = new QPushButton(this); btnStopComparing->setText(i18n("Stop")); btnStopComparing->setIcon(Icon("process-stop")); btnStopComparing->setEnabled(false); buttons->addWidget(btnStopComparing); btnFeedToListBox = new QPushButton(this); btnFeedToListBox->setText(i18n("Feed to listbox")); btnFeedToListBox->setIcon(Icon("list-add")); btnFeedToListBox->setEnabled(false); btnFeedToListBox->hide(); buttons->addWidget(btnFeedToListBox); btnSynchronize = new QPushButton(this); btnSynchronize->setText(i18n("Synchronize")); btnSynchronize->setIcon(Icon("folder-sync")); btnSynchronize->setEnabled(false); buttons->addWidget(btnSynchronize); auto *btnCloseSync = new QPushButton(this); btnCloseSync->setText(i18n("Close")); btnCloseSync->setIcon(Icon("dialog-close")); buttons->addWidget(btnCloseSync); synchGrid->addLayout(buttons, 1, 0); /* =============================== Connect table ================================ */ connect(syncList, &KrTreeWidget::itemRightClicked, this, &SynchronizerGUI::rightMouseClicked); connect(syncList, &KrTreeWidget::itemActivated, this, &SynchronizerGUI::doubleClicked); connect(profileManager, &ProfileManager::loadFromProfile, this, &SynchronizerGUI::loadFromProfile); connect(profileManager, &ProfileManager::saveToProfile, this, &SynchronizerGUI::saveToProfile); connect(btnSwapSides, &QPushButton::clicked, this, &SynchronizerGUI::swapSides); connect(btnCompareDirs, &QPushButton::clicked, this, &SynchronizerGUI::compare); connect(btnStopComparing, &QPushButton::clicked, this, &SynchronizerGUI::stop); connect(btnFeedToListBox, &QPushButton::clicked, this, &SynchronizerGUI::feedToListBox); connect(btnSynchronize, &QPushButton::clicked, this, &SynchronizerGUI::synchronize); connect(btnScrollResults, &QPushButton::toggled, this, &SynchronizerGUI::setScrolling); connect(btnCloseSync, &QPushButton::clicked, this, &SynchronizerGUI::closeDialog); connect(cbSubdirs, &QCheckBox::toggled, this, &SynchronizerGUI::subdirsChecked); connect(cbAsymmetric, &QCheckBox::toggled, this, &SynchronizerGUI::setPanelLabels); connect(&synchronizer, &Synchronizer::comparedFileData, this, &SynchronizerGUI::addFile); connect(&synchronizer, &Synchronizer::markChanged, this, &SynchronizerGUI::markChanged); connect(&synchronizer, &Synchronizer::statusInfo, this, &SynchronizerGUI::statusInfo); connect(btnLeftToRight, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnEquals, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnDifferents, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnRightToLeft, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnDeletable, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnDuplicates, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(btnSingles, &QPushButton::toggled, this, &SynchronizerGUI::refresh); connect(fileFilter, &KHistoryComboBox::currentTextChanged, this, &SynchronizerGUI::connectFilters); connect(generalFilter->searchFor, &KHistoryComboBox::currentTextChanged, this, &SynchronizerGUI::connectFilters); connect(generalFilter->searchFor, &KHistoryComboBox::currentTextChanged, this, &SynchronizerGUI::setCompletion); connect(generalFilter->dontSearchIn, &KURLListRequester::checkValidity, this, &SynchronizerGUI::checkExcludeURLValidity); connect(profileManager, &ProfileManager::loadFromProfile, filterTabs, &FilterTabs::loadFromProfile); connect(profileManager, &ProfileManager::saveToProfile, filterTabs, &FilterTabs::saveToProfile); setPanelLabels(); setCompletion(); /* =============================== Loading the colors ================================ */ KConfigGroup gc(krConfig, "Colors"); QString COLOR_NAMES[] = { "Equals", "Differs", "LeftCopy", "RightCopy", "Delete" }; QPalette defaultPalette = QGuiApplication::palette(); DECLARE_SYNCHRONIZER_BACKGROUND_DEFAULTS; DECLARE_SYNCHRONIZER_FOREGROUND_DEFAULTS; for (int clr = 0; clr != TT_MAX; clr ++) { QString colorName = clr > 4 ? "Equals" : COLOR_NAMES[ clr ]; QColor backgroundDefault = clr > 4 ? defaultPalette.color(QPalette::Active, QPalette::Base) : SYNCHRONIZER_BACKGROUND_DEFAULTS[ clr ]; QColor foregroundDefault = clr > 4 ? defaultPalette.color(QPalette::Active, QPalette::Text) : SYNCHRONIZER_FOREGROUND_DEFAULTS[ clr ]; QString foreEntry = QString("Synchronizer ") + colorName + QString(" Foreground"); QString bckgEntry = QString("Synchronizer ") + colorName + QString(" Background"); if (gc.readEntry(foreEntry, QString()) == "KDE default") foreGrounds[ clr ] = QColor(); else if (gc.readEntry(foreEntry, QString()).isEmpty()) // KDE4 workaround, default color doesn't work foreGrounds[ clr ] = foregroundDefault; else foreGrounds[ clr ] = gc.readEntry(foreEntry, foregroundDefault); if (gc.readEntry(bckgEntry, QString()) == "KDE default") backGrounds[ clr ] = QColor(); else if (gc.readEntry(foreEntry, QString()).isEmpty()) // KDE4 workaround, default color doesn't work backGrounds[ clr ] = backgroundDefault; else backGrounds[ clr ] = gc.readEntry(bckgEntry, backgroundDefault); } if (backGrounds[ TT_EQUALS ].isValid()) { QPalette pal = syncList->palette(); pal.setColor(QPalette::Base, backGrounds[ TT_EQUALS ]); syncList->setPalette(pal); } int sx = group.readEntry("Window Width", -1); int sy = group.readEntry("Window Height", -1); if (sx != -1 && sy != -1) resize(sx, sy); if (group.readEntry("Window Maximized", false)) { setWindowState(windowState() | Qt::WindowMaximized); } if (equalSizes) { int newSize6 = syncList->header()->sectionSize(6); int newSize0 = syncList->header()->sectionSize(0); int delta = newSize6 - newSize0 + (newSize6 & 1); newSize0 += (delta / 2); if (newSize0 > 20) syncList->header()->resizeSection(0, newSize0); } if (!profileName.isNull()) profileManager->loadProfile(profileName); synchronizer.setParentWidget(this); } SynchronizerGUI::~SynchronizerGUI() { syncList->clear(); // for sanity: deletes the references to the synchronizer list } void SynchronizerGUI::setPanelLabels() { if (hasSelectedFiles && cbAsymmetric->isChecked()) { leftDirLabel->setText(i18n("Selected files from targ&et folder:")); rightDirLabel->setText(i18n("Selected files from sou&rce folder:")); } else if (hasSelectedFiles && !cbAsymmetric->isChecked()) { leftDirLabel->setText(i18n("Selected files from &left folder:")); rightDirLabel->setText(i18n("Selected files from &right folder:")); } else if (cbAsymmetric->isChecked()) { leftDirLabel->setText(i18n("Targ&et folder:")); rightDirLabel->setText(i18n("Sou&rce folder:")); } else { leftDirLabel->setText(i18n("&Left folder:")); rightDirLabel->setText(i18n("&Right folder:")); } } void SynchronizerGUI::setCompletion() { generalFilter->dontSearchIn->setCompletionDir(Synchronizer::fsUrl(rightLocation->currentText())); } void SynchronizerGUI::checkExcludeURLValidity(QString &text, QString &error) { QUrl url = Synchronizer::fsUrl(text); if (url.isRelative()) return; QString leftBase = leftLocation->currentText(); if (!leftBase.endsWith('/')) leftBase += '/'; QUrl leftBaseURL = Synchronizer::fsUrl(leftBase); if (leftBaseURL.isParentOf(url) && !url.isParentOf(leftBaseURL)) { text = QDir(leftBaseURL.path()).relativeFilePath(url.path()); return; } QString rightBase = rightLocation->currentText(); if (!rightBase.endsWith('/')) rightBase += '/'; QUrl rightBaseURL = Synchronizer::fsUrl(rightBase); if (rightBaseURL.isParentOf(url) && !url.isParentOf(rightBaseURL)) { text = QDir(rightBaseURL.path()).relativeFilePath(url.path()); return; } error = i18n("URL must be the descendant of either the left or the right base URL."); } void SynchronizerGUI::doubleClicked(QTreeWidgetItem *itemIn) { if (!itemIn) return; auto *syncItem = (SyncViewItem *)itemIn; SynchronizerFileItem *item = syncItem->synchronizerItemRef(); if (item && item->existsInLeft() && item->existsInRight() && !item->isDir()) { QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; QUrl leftURL = Synchronizer::fsUrl(synchronizer.leftBaseDirectory() + leftDirName + item->leftName()); QUrl rightURL = Synchronizer::fsUrl(synchronizer.rightBaseDirectory() + rightDirName + item->rightName()); SLOTS->compareContent(leftURL,rightURL); } else if (item && item->isDir()) { itemIn->setExpanded(!itemIn->isExpanded()); } } void SynchronizerGUI::rightMouseClicked(QTreeWidgetItem *itemIn, const QPoint &pos) { // these are the values that will exist in the menu #define EXCLUDE_ID 90 #define RESTORE_ID 91 #define COPY_TO_LEFT_ID 92 #define COPY_TO_RIGHT_ID 93 #define REVERSE_DIR_ID 94 #define DELETE_ID 95 #define VIEW_LEFT_FILE_ID 96 #define VIEW_RIGHT_FILE_ID 97 #define COMPARE_FILES_ID 98 #define SELECT_ITEMS_ID 99 #define DESELECT_ITEMS_ID 100 #define INVERT_SELECTION_ID 101 #define SYNCH_WITH_KGET_ID 102 #define COPY_CLPBD_LEFT_ID 103 #define COPY_CLPBD_RIGHT_ID 104 ////////////////////////////////////////////////////////// if (!itemIn) return; auto *syncItem = (SyncViewItem *)itemIn; if (syncItem == nullptr) return; SynchronizerFileItem *item = syncItem->synchronizerItemRef(); bool isDuplicate = item->existsInLeft() && item->existsInRight(); bool isDir = item->isDir(); // create the menu QMenu popup; QAction *myact; QHash< QAction *, int > actHash; popup.setTitle(i18n("Synchronize Folders")); myact = popup.addAction(i18n("E&xclude")); actHash[ myact ] = EXCLUDE_ID; myact = popup.addAction(i18n("Restore ori&ginal operation")); actHash[ myact ] = RESTORE_ID; myact = popup.addAction(i18n("Re&verse direction")); actHash[ myact ] = REVERSE_DIR_ID; myact = popup.addAction(i18n("Copy from &right to left")); actHash[ myact ] = COPY_TO_LEFT_ID; myact = popup.addAction(i18n("Copy from &left to right")); actHash[ myact ] = COPY_TO_RIGHT_ID; myact = popup.addAction(i18n("&Delete (left single)")); actHash[ myact ] = DELETE_ID; popup.addSeparator(); myact = popup.addAction(i18n("V&iew left file")); myact->setEnabled(!isDir && item->existsInLeft()); actHash[ myact ] = VIEW_LEFT_FILE_ID; myact = popup.addAction(i18n("Vi&ew right file")); myact->setEnabled(!isDir && item->existsInRight()); actHash[ myact ] = VIEW_RIGHT_FILE_ID; myact = popup.addAction(i18n("&Compare Files")); myact->setEnabled(!isDir && isDuplicate); actHash[ myact ] = COMPARE_FILES_ID; popup.addSeparator(); myact = popup.addAction(i18n("C&opy selected to clipboard (left)")); actHash[ myact ] = COPY_CLPBD_LEFT_ID; myact = popup.addAction(i18n("Co&py selected to clipboard (right)")); actHash[ myact ] = COPY_CLPBD_RIGHT_ID; popup.addSeparator(); myact = popup.addAction(i18n("&Select items")); actHash[ myact ] = SELECT_ITEMS_ID; myact = popup.addAction(i18n("Deselec&t items")); actHash[ myact ] = DESELECT_ITEMS_ID; myact = popup.addAction(i18n("I&nvert selection")); actHash[ myact ] = INVERT_SELECTION_ID; QUrl leftBDir = Synchronizer::fsUrl(synchronizer.leftBaseDirectory()); QUrl rightBDir = Synchronizer::fsUrl(synchronizer.rightBaseDirectory()); if (KrServices::cmdExist("kget") && ((!leftBDir.isLocalFile() && rightBDir.isLocalFile() && btnLeftToRight->isChecked()) || (leftBDir.isLocalFile() && !rightBDir.isLocalFile() && btnRightToLeft->isChecked()))) { popup.addSeparator(); myact = popup.addAction(i18n("Synchronize with &KGet")); actHash[ myact ] = SYNCH_WITH_KGET_ID; } QAction * res = popup.exec(pos); int result = -1; if (actHash.contains(res)) result = actHash[ res ]; if (result != -1) executeOperation(item, result); } void SynchronizerGUI::executeOperation(SynchronizerFileItem *item, int op) { // check out the user's option QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; QUrl leftURL = Synchronizer::fsUrl(synchronizer.leftBaseDirectory() + leftDirName + item->leftName()); QUrl rightURL = Synchronizer::fsUrl(synchronizer.rightBaseDirectory() + rightDirName + item->rightName()); switch (op) { case EXCLUDE_ID: case RESTORE_ID: case COPY_TO_LEFT_ID: case COPY_TO_RIGHT_ID: case REVERSE_DIR_ID: case DELETE_ID: { unsigned ndx = 0; SynchronizerFileItem *currentItem; while ((currentItem = synchronizer.getItemAt(ndx++)) != nullptr) { auto *viewItem = (SyncViewItem *)currentItem->userData(); if (!viewItem || !viewItem->isSelected() || viewItem->isHidden()) continue; switch (op) { case EXCLUDE_ID: synchronizer.exclude(viewItem->synchronizerItemRef()); break; case RESTORE_ID: synchronizer.restore(viewItem->synchronizerItemRef()); break; case REVERSE_DIR_ID: synchronizer.reverseDirection(viewItem->synchronizerItemRef()); break; case COPY_TO_LEFT_ID: synchronizer.copyToLeft(viewItem->synchronizerItemRef()); break; case COPY_TO_RIGHT_ID: synchronizer.copyToRight(viewItem->synchronizerItemRef()); break; case DELETE_ID: synchronizer.deleteLeft(viewItem->synchronizerItemRef()); break; } } refresh(); } break; case VIEW_LEFT_FILE_ID: KrViewer::view(leftURL, this); // view the file break; case VIEW_RIGHT_FILE_ID: KrViewer::view(rightURL, this); // view the file break; case COMPARE_FILES_ID: SLOTS->compareContent(leftURL,rightURL); break; case SELECT_ITEMS_ID: case DESELECT_ITEMS_ID: { KRQuery query = KRSpWidgets::getMask((op == SELECT_ITEMS_ID ? i18n("Select items") : i18n("Deselect items")), true, this); if (query.isNull()) break; unsigned ndx = 0; SynchronizerFileItem *currentItem; while ((currentItem = synchronizer.getItemAt(ndx++)) != nullptr) { auto *viewItem = (SyncViewItem *)currentItem->userData(); if (!viewItem || viewItem->isHidden()) continue; if (query.match(currentItem->leftName()) || query.match(currentItem->rightName())) viewItem->setSelected(op == SELECT_ITEMS_ID); } } break; case INVERT_SELECTION_ID: { unsigned ndx = 0; SynchronizerFileItem *currentItem; while ((currentItem = synchronizer.getItemAt(ndx++)) != nullptr) { auto *viewItem = (SyncViewItem *)currentItem->userData(); if (!viewItem || viewItem->isHidden()) continue; viewItem->setSelected(!viewItem->isSelected()); } } break; case SYNCH_WITH_KGET_ID: synchronizer.synchronizeWithKGet(); closeDialog(); break; case COPY_CLPBD_LEFT_ID: copyToClipboard(true); break; case COPY_CLPBD_RIGHT_ID: copyToClipboard(false); break; case -1 : return; // the user clicked outside of the menu } } void SynchronizerGUI::closeDialog() { if (isComparing) { stop(); wasClosed = true; return; } KConfigGroup group(krConfig, "Synchronize"); QStringList list; foreach(const QString &item, leftLocation->historyItems()) { QUrl url(item); // make sure no passwords are saved in config url.setPassword(QString()); list << url.toDisplayString(QUrl::PreferLocalFile); } group.writeEntry("Left Folder History", list); list.clear(); foreach(const QString &item, rightLocation->historyItems()) { QUrl url(item); // make sure no passwords are saved in config url.setPassword(QString()); list << url.toDisplayString(QUrl::PreferLocalFile); } group.writeEntry("Right Folder History", list); list = fileFilter->historyItems(); group.writeEntry("File Filter", list); group.writeEntry("Recurse Subdirectories", cbSubdirs->isChecked()); group.writeEntry("Follow Symlinks", cbSymlinks->isChecked()); group.writeEntry("Compare By Content", cbByContent->isChecked()); group.writeEntry("Ignore Date", cbIgnoreDate->isChecked()); group.writeEntry("Asymmetric", cbAsymmetric->isChecked()); group.writeEntry("Ignore Case", cbIgnoreCase->isChecked()); group.writeEntry("LeftToRight Button", btnLeftToRight->isChecked()); group.writeEntry("Equals Button", btnEquals->isChecked()); group.writeEntry("Differents Button", btnDifferents->isChecked()); group.writeEntry("RightToLeft Button", btnRightToLeft->isChecked()); group.writeEntry("Deletable Button", btnDeletable->isChecked()); group.writeEntry("Duplicates Button", btnDuplicates->isChecked()); group.writeEntry("Singles Button", btnSingles->isChecked()); group.writeEntry("Scroll Results", btnScrollResults->isChecked()); group.writeEntry("Parallel Threads", parallelThreadsSpinBox->value()); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); group.writeEntry("State", syncList->header()->saveState()); QDialog::reject(); this->deleteLater(); if (wasSync) { LEFT_PANEL->func->refresh(); RIGHT_PANEL->func->refresh(); ACTIVE_PANEL->gui->slotFocusOnMe(); } } void SynchronizerGUI::compare() { KRQuery query; if (!filterTabs->fillQuery(&query)) return; // perform some previous tests QString leftLocationTrimmed = leftLocation->currentText().trimmed(); QString rightLocationTrimmed = rightLocation->currentText().trimmed(); if (leftLocationTrimmed.isEmpty()) { KMessageBox::error(this, i18n("The target folder must not be empty.")); leftLocation->setFocus(); return; } if (rightLocationTrimmed.isEmpty()) { KMessageBox::error(this, i18n("The source folder must not be empty.")); rightLocation->setFocus(); return; } if (leftLocationTrimmed == rightLocationTrimmed) { if (KMessageBox::warningContinueCancel(this, i18n("Warning: The left and the right side are showing the same folder.")) != KMessageBox::Continue) { return; } } query.setNameFilter(fileFilter->currentText(), query.isCaseSensitive()); synchronizerTabs->setCurrentIndex(0); syncList->clear(); lastItem = nullptr; leftLocation->addToHistory(leftLocation->currentText()); rightLocation->addToHistory(rightLocation->currentText()); fileFilter->addToHistory(fileFilter->currentText()); setMarkFlags(); btnCompareDirs->setEnabled(false); profileManager->setEnabled(false); btnSwapSides->setEnabled(false); btnStopComparing->setEnabled(isComparing = true); btnStopComparing->show(); btnFeedToListBox->setEnabled(false); btnFeedToListBox->hide(); btnSynchronize->setEnabled(false); btnCompareDirs->hide(); btnScrollResults->show(); disableMarkButtons(); int fileCount = synchronizer.compare(leftLocation->currentText(), rightLocation->currentText(), &query, cbSubdirs->isChecked(), cbSymlinks->isChecked(), cbIgnoreDate->isChecked(), cbAsymmetric->isChecked(), cbByContent->isChecked(), cbIgnoreCase->isChecked(), btnScrollResults->isChecked(), selectedFiles, convertToSeconds(equalitySpinBox->value(), equalityUnitCombo->currentIndex()), convertToSeconds(timeShiftSpinBox->value(), timeShiftUnitCombo->currentIndex()), parallelThreadsSpinBox->value(), ignoreHiddenFilesCB->isChecked()); enableMarkButtons(); btnStopComparing->setEnabled(isComparing = false); btnStopComparing->hide(); btnFeedToListBox->show(); btnCompareDirs->setEnabled(true); profileManager->setEnabled(true); btnSwapSides->setEnabled(true); btnCompareDirs->show(); btnScrollResults->hide(); if (fileCount) { btnSynchronize->setEnabled(true); btnFeedToListBox->setEnabled(true); } syncList->setFocus(); if (wasClosed) closeDialog(); } void SynchronizerGUI::stop() { synchronizer.stop(); } void SynchronizerGUI::feedToListBox() { FeedToListBoxDialog listBox(this, &synchronizer, syncList, btnEquals->isChecked()); if (listBox.isAccepted()) closeDialog(); } void SynchronizerGUI::reject() { closeDialog(); } void SynchronizerGUI::addFile(SynchronizerFileItem *item) { QString leftName = "", rightName = "", leftDate = "", rightDate = "", leftSize = "", rightSize = ""; bool isDir = item->isDir(); QColor textColor = foreGrounds[ item->task()]; QColor baseColor = backGrounds[ item->task()]; if (item->existsInLeft()) { leftName = item->leftName(); leftSize = isDir ? dirLabel() + ' ' : KRpermHandler::parseSize(item->leftSize()); leftDate = SynchronizerGUI::convertTime(item->leftDate()); } if (item->existsInRight()) { rightName = item->rightName(); rightSize = isDir ? dirLabel() + ' ' : KRpermHandler::parseSize(item->rightSize()); rightDate = SynchronizerGUI::convertTime(item->rightDate()); } SyncViewItem *listItem = nullptr; SyncViewItem *dirItem; if (item->parent() == nullptr) { listItem = new SyncViewItem(item, textColor, baseColor, syncList, lastItem, leftName, leftSize, leftDate, Synchronizer::getTaskTypeName(item->task()), rightDate, rightSize, rightName); lastItem = listItem; } else { dirItem = (SyncViewItem *)item->parent()->userData(); if (dirItem) { dirItem->setExpanded(true); listItem = new SyncViewItem(item, textColor, baseColor, dirItem, dirItem->lastItem(), leftName, leftSize, leftDate, Synchronizer::getTaskTypeName(item->task()), rightDate, rightSize, rightName); dirItem->setLastItem(listItem); } } if (listItem) { listItem->setIcon(0, Icon(isDir ? "folder" : "document-new")); if (!item->isMarked()) listItem->setHidden(true); else syncList->scrollTo(syncList->indexOf(listItem)); } } void SynchronizerGUI::markChanged(SynchronizerFileItem *item, bool ensureVisible) { auto *listItem = (SyncViewItem *)item->userData(); if (listItem) { if (!item->isMarked()) { listItem->setHidden(true); } else { QString leftName = "", rightName = "", leftDate = "", rightDate = "", leftSize = "", rightSize = ""; bool isDir = item->isDir(); if (item->existsInLeft()) { leftName = item->leftName(); leftSize = isDir ? dirLabel() + ' ' : KRpermHandler::parseSize(item->leftSize()); leftDate = SynchronizerGUI::convertTime(item->leftDate()); } if (item->existsInRight()) { rightName = item->rightName(); rightSize = isDir ? dirLabel() + ' ' : KRpermHandler::parseSize(item->rightSize()); rightDate = SynchronizerGUI::convertTime(item->rightDate()); } listItem->setHidden(false); listItem->setText(0, leftName); listItem->setText(1, leftSize); listItem->setText(2, leftDate); listItem->setText(3, Synchronizer::getTaskTypeName(item->task())); listItem->setText(4, rightDate); listItem->setText(5, rightSize); listItem->setText(6, rightName); listItem->setColors(foreGrounds[ item->task()], backGrounds[ item->task()]); if (ensureVisible) syncList->scrollTo(syncList->indexOf(listItem)); } } } void SynchronizerGUI::subdirsChecked(bool isOn) { cbSymlinks->setEnabled(isOn); } void SynchronizerGUI::disableMarkButtons() { btnLeftToRight->setEnabled(false); btnEquals->setEnabled(false); btnDifferents->setEnabled(false); btnRightToLeft->setEnabled(false); btnDeletable->setEnabled(false); btnDuplicates->setEnabled(false); btnSingles->setEnabled(false); } void SynchronizerGUI::enableMarkButtons() { btnLeftToRight->setEnabled(true); btnEquals->setEnabled(true); btnDifferents->setEnabled(true); btnRightToLeft->setEnabled(true); btnDeletable->setEnabled(true); btnDuplicates->setEnabled(true); btnSingles->setEnabled(true); } QString SynchronizerGUI::convertTime(time_t time) const { // convert the time_t to struct tm struct tm* t = localtime((time_t *) & time); QDateTime tmp(QDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday), QTime(t->tm_hour, t->tm_min)); return QLocale().toString(tmp, QLocale::ShortFormat); } void SynchronizerGUI::setMarkFlags() { synchronizer.setMarkFlags(btnRightToLeft->isChecked(), btnEquals->isChecked(), btnDifferents->isChecked(), btnLeftToRight->isChecked(), btnDuplicates->isChecked(), btnSingles->isChecked(), btnDeletable->isChecked()); } void SynchronizerGUI::refresh() { if (!isComparing) { syncList->clearSelection(); setMarkFlags(); btnCompareDirs->setEnabled(false); profileManager->setEnabled(false); btnSwapSides->setEnabled(false); btnSynchronize->setEnabled(false); btnFeedToListBox->setEnabled(false); disableMarkButtons(); int fileCount = synchronizer.refresh(); enableMarkButtons(); btnCompareDirs->setEnabled(true); profileManager->setEnabled(true); btnSwapSides->setEnabled(true); if (fileCount) { btnFeedToListBox->setEnabled(true); btnSynchronize->setEnabled(true); } } } void SynchronizerGUI::synchronize() { int copyToLeftNr, copyToRightNr, deleteNr; KIO::filesize_t copyToLeftSize, copyToRightSize, deleteSize; if (!synchronizer.totalSizes(©ToLeftNr, ©ToLeftSize, ©ToRightNr, ©ToRightSize, &deleteNr, &deleteSize)) { KMessageBox::sorry(parentWidget(), i18n("Synchronizer has nothing to do.")); return; } auto *sd = new SynchronizeDialog(this, &synchronizer, copyToLeftNr, copyToLeftSize, copyToRightNr, copyToRightSize, deleteNr, deleteSize, parallelThreadsSpinBox->value()); wasSync = sd->wasSyncronizationStarted(); delete sd; if (wasSync) closeDialog(); } void SynchronizerGUI::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } if (!firstResize) { int delta = e->size().width() - e->oldSize().width() + (e->size().width() & 1); int newSize = syncList->header()->sectionSize(0) + delta / 2; if (newSize > 20) syncList->header()->resizeSection(0, newSize); } firstResize = false; QDialog::resizeEvent(e); } -void SynchronizerGUI::statusInfo(QString info) +void SynchronizerGUI::statusInfo(const QString& info) { statusLabel->setText(info); qApp->processEvents(); } void SynchronizerGUI::swapSides() { if (btnCompareDirs->isEnabled()) { QString leftCurrent = leftLocation->currentText(); leftLocation->lineEdit()->setText(rightLocation->currentText()); rightLocation->lineEdit()->setText(leftCurrent); synchronizer.swapSides(); refresh(); } } void SynchronizerGUI::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_M : { if (e->modifiers() == Qt::ControlModifier) { syncList->setFocus(); e->accept(); } break; } case Qt::Key_F3 : case Qt::Key_F4 : { e->accept(); syncList->setFocus(); QTreeWidgetItem *listItem = syncList->currentItem(); if (listItem == nullptr) break; bool isedit = e->key() == Qt::Key_F4; SynchronizerFileItem *item = ((SyncViewItem *)listItem)->synchronizerItemRef(); QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; if (item->isDir()) return; if (e->modifiers() == Qt::ShiftModifier && item->existsInRight()) { QUrl rightURL = Synchronizer::fsUrl(synchronizer.rightBaseDirectory() + rightDirName + item->rightName()); if (isedit) KrViewer::edit(rightURL, this); // view the file else KrViewer::view(rightURL, this); // view the file return; } else if (e->modifiers() == 0 && item->existsInLeft()) { QUrl leftURL = Synchronizer::fsUrl(synchronizer.leftBaseDirectory() + leftDirName + item->leftName()); if (isedit) KrViewer::edit(leftURL, this); // view the file else KrViewer::view(leftURL, this); // view the file return; } } break; case Qt::Key_U : if (e->modifiers() != Qt::ControlModifier) break; e->accept(); swapSides(); return; case Qt::Key_Escape: if (!btnStopComparing->isHidden() && btnStopComparing->isEnabled()) { // is it comparing? e->accept(); btnStopComparing->animateClick(); // just click the stop button } else { e->accept(); if (syncList->topLevelItemCount() != 0) { int result = KMessageBox::warningYesNo(this, i18n("The synchronizer window contains data from a previous compare. If you exit, this data will be lost. Do you really want to exit?"), i18n("Krusader::Synchronize Folders"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "syncGUIexit"); if (result != KMessageBox::Yes) return; } QDialog::reject(); } return; } QDialog::keyPressEvent(e); } bool SynchronizerGUI::eventFilter(QObject * /* watched */, QEvent * e) { if (e->type() == QEvent::KeyPress) { auto* ke = (QKeyEvent*) e; switch (ke->key()) { case Qt::Key_Down: case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Delete: case Qt::Key_W: { if (ke->key() == Qt::Key_W) { if (ke->modifiers() != Qt::ControlModifier) return false; } else if (ke->modifiers() != Qt::AltModifier) return false; int op = -1; switch (ke->key()) { case Qt::Key_Down: op = EXCLUDE_ID; break; case Qt::Key_Left: op = COPY_TO_LEFT_ID; break; case Qt::Key_Right: op = COPY_TO_RIGHT_ID; break; case Qt::Key_Up: op = RESTORE_ID; break; case Qt::Key_Delete: op = DELETE_ID; break; case Qt::Key_W: op = REVERSE_DIR_ID; break; } ke->accept(); QTreeWidgetItem *listItem = syncList->currentItem(); if (listItem == nullptr) return true; SynchronizerFileItem *item = ((SyncViewItem *)listItem)->synchronizerItemRef(); bool hasSelected = false; QList selected = syncList->selectedItems(); for (int i = 0; i != selected.count(); i++) if (selected[ i ]->isSelected() && !selected[ i ]->isHidden()) hasSelected = true; if (!hasSelected) listItem->setSelected(true); executeOperation(item, op); return true; } } } return false; } -void SynchronizerGUI::loadFromProfile(QString profile) +void SynchronizerGUI::loadFromProfile(const QString& profile) { syncList->clear(); synchronizer.reset(); isComparing = wasClosed = false; btnSynchronize->setEnabled(false); btnFeedToListBox->setEnabled(false); KConfigGroup pg(krConfig, profile); if (!hasSelectedFiles) { leftLocation->lineEdit()->setText(pg.readEntry("Left Location", QString())); rightLocation->lineEdit()->setText(pg.readEntry("Right Location", QString())); } fileFilter->lineEdit()->setText(pg.readEntry("Search For", QString())); cbSubdirs-> setChecked(pg.readEntry("Recurse Subdirectories", true)); cbSymlinks-> setChecked(pg.readEntry("Follow Symlinks", false)); cbByContent-> setChecked(pg.readEntry("Compare By Content", false)); cbIgnoreDate->setChecked(pg.readEntry("Ignore Date", false)); cbAsymmetric->setChecked(pg.readEntry("Asymmetric", false)); cbIgnoreCase->setChecked(pg.readEntry("Ignore Case", false)); btnScrollResults->setChecked(pg.readEntry("Scroll Results", false)); btnLeftToRight->setChecked(pg.readEntry("Show Left To Right", true)); btnEquals ->setChecked(pg.readEntry("Show Equals", true)); btnDifferents ->setChecked(pg.readEntry("Show Differents", true)); btnRightToLeft->setChecked(pg.readEntry("Show Right To Left", true)); btnDeletable ->setChecked(pg.readEntry("Show Deletable", true)); btnDuplicates ->setChecked(pg.readEntry("Show Duplicates", true)); btnSingles ->setChecked(pg.readEntry("Show Singles", true)); int equalityThreshold = pg.readEntry("Equality Threshold", 0); int equalityCombo = 0; convertFromSeconds(equalityThreshold, equalityCombo, equalityThreshold); equalitySpinBox->setValue(equalityThreshold); equalityUnitCombo->setCurrentIndex(equalityCombo); int timeShift = pg.readEntry("Time Shift", 0); int timeShiftCombo = 0; convertFromSeconds(timeShift, timeShiftCombo, timeShift); timeShiftSpinBox->setValue(timeShift); timeShiftUnitCombo->setCurrentIndex(timeShiftCombo); int parallelThreads = pg.readEntry("Parallel Threads", 1); parallelThreadsSpinBox->setValue(parallelThreads); bool ignoreHidden = pg.readEntry("Ignore Hidden Files", false); ignoreHiddenFilesCB->setChecked(ignoreHidden); refresh(); } -void SynchronizerGUI::saveToProfile(QString profile) +void SynchronizerGUI::saveToProfile(const QString& profile) { KConfigGroup group(krConfig, profile); group.writeEntry("Left Location", leftLocation->currentText()); group.writeEntry("Search For", fileFilter->currentText()); group.writeEntry("Right Location", rightLocation->currentText()); group.writeEntry("Recurse Subdirectories", cbSubdirs->isChecked()); group.writeEntry("Follow Symlinks", cbSymlinks->isChecked()); group.writeEntry("Compare By Content", cbByContent->isChecked()); group.writeEntry("Ignore Date", cbIgnoreDate->isChecked()); group.writeEntry("Asymmetric", cbAsymmetric->isChecked()); group.writeEntry("Ignore Case", cbIgnoreCase->isChecked()); group.writeEntry("Scroll Results", btnScrollResults->isChecked()); group.writeEntry("Show Left To Right", btnLeftToRight->isChecked()); group.writeEntry("Show Equals", btnEquals->isChecked()); group.writeEntry("Show Differents", btnDifferents->isChecked()); group.writeEntry("Show Right To Left", btnRightToLeft->isChecked()); group.writeEntry("Show Deletable", btnDeletable->isChecked()); group.writeEntry("Show Duplicates", btnDuplicates->isChecked()); group.writeEntry("Show Singles", btnSingles->isChecked()); group.writeEntry("Equality Threshold", convertToSeconds(equalitySpinBox->value(), equalityUnitCombo->currentIndex())); group.writeEntry("Time Shift", convertToSeconds(timeShiftSpinBox->value(), timeShiftUnitCombo->currentIndex())); group.writeEntry("Parallel Threads", parallelThreadsSpinBox->value()); group.writeEntry("Ignore Hidden Files", ignoreHiddenFilesCB->isChecked()); } void SynchronizerGUI::connectFilters(const QString &newString) { if (synchronizerTabs->currentIndex()) fileFilter->setEditText(newString); else generalFilter->searchFor->setEditText(newString); } void SynchronizerGUI::setScrolling(bool isOn) { if (isOn) btnScrollResults->setText(i18n("Quiet")); else btnScrollResults->setText(i18n("Scroll Results")); synchronizer.setScrolling(isOn); } int SynchronizerGUI::convertToSeconds(int time, int unit) { switch (unit) { case 1: return time * 60; case 2: return time * 3600; case 3: return time * 86400; default: return time; } } void SynchronizerGUI::convertFromSeconds(int &time, int &unit, int second) { unit = 0; time = second; int absTime = (time < 0) ? -time : time; if (absTime >= 86400 && (absTime % 86400) == 0) { time /= 86400; unit = 3; } else if (absTime >= 3600 && (absTime % 3600) == 0) { time /= 3600; unit = 2; } else if (absTime >= 60 && (absTime % 60) == 0) { time /= 60; unit = 1; } } QPushButton *SynchronizerGUI::createButton(QWidget *parent, const QString &iconName, bool checked, const QKeySequence &shortCut, const QString &description, const QString &text, bool textAndIcon) { auto *button = new QPushButton(parent); bool iconExists = Icon::exists(iconName); if (!text.isEmpty() && (textAndIcon || !iconExists)) { button->setText(text); } if (iconExists) { button->setIcon(Icon(iconName)); } button->setCheckable(true); button->setChecked(checked); button->setShortcut(shortCut); const QString infoText = QString("%1 (%2)").arg(description, shortCut.toString(QKeySequence::NativeText)); button->setWhatsThis(infoText); button->setToolTip(infoText); return button; } void SynchronizerGUI::copyToClipboard(bool isLeft) { QList urls; unsigned ndx = 0; SynchronizerFileItem *currentItem; while ((currentItem = synchronizer.getItemAt(ndx++)) != nullptr) { auto *viewItem = (SynchronizerGUI::SyncViewItem *)currentItem->userData(); if (!viewItem || !viewItem->isSelected() || viewItem->isHidden()) continue; SynchronizerFileItem *item = viewItem->synchronizerItemRef(); if (item) { if (isLeft && item->existsInLeft()) { QString leftDirName = item->leftDirectory().isEmpty() ? "" : item->leftDirectory() + '/'; QUrl leftURL = Synchronizer::fsUrl(synchronizer.leftBaseDirectory() + leftDirName + item->leftName()); urls.push_back(leftURL); } else if (!isLeft && item->existsInRight()) { QString rightDirName = item->rightDirectory().isEmpty() ? "" : item->rightDirectory() + '/'; QUrl rightURL = Synchronizer::fsUrl(synchronizer.rightBaseDirectory() + rightDirName + item->rightName()); urls.push_back(rightURL); } } } if (urls.count() == 0) return; auto *mimeData = new QMimeData; mimeData->setImageData(FileListIcon(isLeft ? "arrow-left-double" : "arrow-right-double").pixmap()); mimeData->setUrls(urls); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } QString SynchronizerGUI::dirLabel() { //HACK add <> brackets AFTER translating - otherwise KUIT thinks it's a tag static QString label = QString("<") + i18nc("Show the string 'DIR' instead of file size in detailed view (for folders)", "DIR") + '>'; return label; } diff --git a/krusader/Synchronizer/synchronizergui.h b/krusader/Synchronizer/synchronizergui.h index 732b9edd..eccd9aec 100644 --- a/krusader/Synchronizer/synchronizergui.h +++ b/krusader/Synchronizer/synchronizergui.h @@ -1,252 +1,253 @@ /***************************************************************************** * Copyright (C) 2003 Csaba Karai * * Copyright (C) 2004-2018 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 SYNCHRONIZERGUI_H #define SYNCHRONIZERGUI_H // QtCore #include // QtGui #include #include #include // QtWidgets #include #include #include #include #include +#include #include "synchronizer.h" #include "../GUI/profilemanager.h" #include "../GUI/krtreewidget.h" #include "../Filter/filtertabs.h" #include "../Filter/generalfilter.h" class QSpinBox; class SynchronizerGUI : public QDialog { Q_OBJECT public: class SyncViewItem : public QTreeWidgetItem { private: SynchronizerFileItem *syncItemRef; SyncViewItem *lastItemRef; public: - SyncViewItem(SynchronizerFileItem *item, QColor txt, QColor base, QTreeWidget * parent, QTreeWidgetItem *after, QString label1, - QString label2 = QString(), QString label3 = QString(), QString label4 = QString(), - QString label5 = QString(), QString label6 = QString(), - QString label7 = QString(), QString label8 = QString()) : + SyncViewItem(SynchronizerFileItem *item, QColor txt, QColor base, QTreeWidget * parent, QTreeWidgetItem *after, const QString& label1, + const QString& label2 = QString(), const QString& label3 = QString(), const QString& label4 = QString(), + const QString& label5 = QString(), const QString& label6 = QString(), + const QString& label7 = QString(), const QString& label8 = QString()) : QTreeWidgetItem(parent, after), syncItemRef(item), lastItemRef(nullptr) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setTextAlignment(1, Qt::AlignRight); setTextAlignment(3, Qt::AlignHCenter); setTextAlignment(5, Qt::AlignRight); item->setUserData((void *)this); - setColors(txt, base); + setColors(std::move(txt), std::move(base)); } - SyncViewItem(SynchronizerFileItem *item, QColor txt, QColor base, QTreeWidgetItem * parent, QTreeWidgetItem *after, QString label1, - QString label2 = QString(), QString label3 = QString(), QString label4 = QString(), - QString label5 = QString(), QString label6 = QString(), - QString label7 = QString(), QString label8 = QString()) : + SyncViewItem(SynchronizerFileItem *item, QColor txt, QColor base, QTreeWidgetItem * parent, QTreeWidgetItem *after, const QString& label1, + const QString& label2 = QString(), const QString& label3 = QString(), const QString& label4 = QString(), + const QString& label5 = QString(), const QString& label6 = QString(), + const QString& label7 = QString(), const QString& label8 = QString()) : QTreeWidgetItem(parent, after), syncItemRef(item), lastItemRef(nullptr) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setTextAlignment(1, Qt::AlignRight); setTextAlignment(3, Qt::AlignHCenter); setTextAlignment(5, Qt::AlignRight); item->setUserData((void *)this); - setColors(txt, base); + setColors(std::move(txt), std::move(base)); } ~SyncViewItem() override { syncItemRef->setUserData(nullptr); } inline SynchronizerFileItem * synchronizerItemRef() { return syncItemRef; } inline SyncViewItem * lastItem() { return lastItemRef; } inline void setLastItem(SyncViewItem*s) { lastItemRef = s; } - void setColors(QColor fore, QColor back) { + void setColors(const QColor& fore, const QColor& back) { QBrush textColor(fore); QBrush baseColor(back); for (int i = 0; i != columnCount(); i++) { if (back.isValid()) setBackground(i, baseColor); if (fore.isValid()) setForeground(i, textColor); } } }; public: // if rightDirectory is null, leftDirectory is actually the profile name to load SynchronizerGUI(QWidget* parent, QUrl leftDirectory, QUrl rightDirectory = QUrl(), QStringList selList = QStringList()); SynchronizerGUI(QWidget* parent, QString profile); ~SynchronizerGUI() override; inline bool wasSynchronization() { return wasSync; } public slots: void rightMouseClicked(QTreeWidgetItem *, const QPoint &); void doubleClicked(QTreeWidgetItem *); void compare(); void synchronize(); void stop(); void feedToListBox(); void closeDialog(); void refresh(); void swapSides(); - void loadFromProfile(QString); - void saveToProfile(QString); + void loadFromProfile(const QString&); + void saveToProfile(const QString&); protected slots: void reject() Q_DECL_OVERRIDE; void addFile(SynchronizerFileItem *); void markChanged(SynchronizerFileItem *, bool); void setScrolling(bool); - void statusInfo(QString); + void statusInfo(const QString&); void subdirsChecked(bool); void setPanelLabels(); void setCompletion(); void checkExcludeURLValidity(QString &text, QString &error); void connectFilters(const QString &); private: - void initGUI(QString profile, QUrl leftURL, QUrl rightURL, QStringList selList); + void initGUI(const QString& profile, QUrl leftURL, QUrl rightURL, QStringList selList); QString convertTime(time_t time) const; void setMarkFlags(); void disableMarkButtons(); void enableMarkButtons(); void copyToClipboard(bool isLeft); int convertToSeconds(int time, int unit); void convertFromSeconds(int &time, int &unit, int second); static QPushButton *createButton(QWidget *parent, const QString &iconName, bool checked, const QKeySequence &shortCut, const QString &description, const QString &text = QString(), bool textAndIcon = false); protected: void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; void executeOperation(SynchronizerFileItem *item, int op); ProfileManager *profileManager; FilterTabs *filterTabs; GeneralFilter *generalFilter; QTabWidget *synchronizerTabs; KHistoryComboBox *leftLocation; KHistoryComboBox *rightLocation; KHistoryComboBox *fileFilter; KrTreeWidget *syncList; Synchronizer synchronizer; QCheckBox *cbSubdirs; QCheckBox *cbSymlinks; QCheckBox *cbByContent; QCheckBox *cbIgnoreDate; QCheckBox *cbAsymmetric; QCheckBox *cbIgnoreCase; QPushButton *btnSwapSides; QPushButton *btnCompareDirs; QPushButton *btnStopComparing; QPushButton *btnSynchronize; QPushButton *btnFeedToListBox; QPushButton *btnScrollResults; QPushButton *btnLeftToRight; QPushButton *btnEquals; QPushButton *btnDifferents; QPushButton *btnRightToLeft; QPushButton *btnDeletable; QPushButton *btnDuplicates; QPushButton *btnSingles; QLabel *statusLabel; QLabel *leftDirLabel; QLabel *rightDirLabel; QStringList selectedFiles; QSpinBox *parallelThreadsSpinBox; QSpinBox *equalitySpinBox; QComboBox *equalityUnitCombo; QSpinBox *timeShiftSpinBox; QComboBox *timeShiftUnitCombo; QCheckBox *ignoreHiddenFilesCB; private: static QString dirLabel(); // returns translated '' bool isComparing; bool wasClosed; bool wasSync; bool firstResize; bool hasSelectedFiles; SyncViewItem *lastItem; int sizeX; int sizeY; QColor foreGrounds[ TT_MAX ]; QColor backGrounds[ TT_MAX ]; }; #endif /* __SYNCHRONIZERGUI_H__ */ diff --git a/krusader/UserAction/kraction.cpp b/krusader/UserAction/kraction.cpp index 34f70a9c..e874fd75 100644 --- a/krusader/UserAction/kraction.cpp +++ b/krusader/UserAction/kraction.cpp @@ -1,699 +1,699 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2006 Jonas Bähr * * Copyright (C) 2004-2018 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 "kraction.h" // QtCore #include #include #include #include #include #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include // QtXml #include #include #include #include #include #include #include #include "expander.h" #include "useraction.h" #include "../GUI/terminaldock.h" #include "../krglobal.h" #include "../icon.h" #include "../krusaderview.h" #include "../krservices.h" #include "../defaults.h" // KrActionProcDlg -KrActionProcDlg::KrActionProcDlg(QString caption, bool enableStderr, QWidget *parent) : +KrActionProcDlg::KrActionProcDlg(const QString& caption, bool enableStderr, QWidget *parent) : QDialog(parent), _stdout(nullptr), _stderr(nullptr), _currentTextEdit(nullptr) { setWindowTitle(caption); setWindowModality(Qt::NonModal); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); // do we need to separate stderr and stdout? if (enableStderr) { auto *splitt = new QSplitter(Qt::Horizontal, this); mainLayout->addWidget(splitt); // create stdout QWidget *stdoutWidget = new QWidget(splitt); auto *stdoutBox = new QVBoxLayout(stdoutWidget); stdoutBox->addWidget(new QLabel(i18n("Standard Output (stdout)"), stdoutWidget)); _stdout = new KTextEdit(stdoutWidget); _stdout->setReadOnly(true); stdoutBox->addWidget(_stdout); // create stderr QWidget *stderrWidget = new QWidget(splitt); auto *stderrBox = new QVBoxLayout(stderrWidget); stderrBox->addWidget(new QLabel(i18n("Standard Error (stderr)"), stderrWidget)); _stderr = new KTextEdit(stderrWidget); _stderr->setReadOnly(true); stderrBox->addWidget(_stderr); } else { // create stdout mainLayout->addWidget(new QLabel(i18n("Output"))); _stdout = new KTextEdit; _stdout->setReadOnly(true); mainLayout->addWidget(_stdout); } _currentTextEdit = _stdout; connect(_stdout, &KTextEdit::textChanged, this, &KrActionProcDlg::currentTextEditChanged); if (_stderr) connect(_stderr, &KTextEdit::textChanged, this, &KrActionProcDlg::currentTextEditChanged); KConfigGroup group(krConfig, "UserActions"); normalFont = group.readEntry("Normal Font", _UserActions_NormalFont); fixedFont = group.readEntry("Fixed Font", _UserActions_FixedFont); bool startupState = group.readEntry("Use Fixed Font", _UserActions_UseFixedFont); toggleFixedFont(startupState); auto *hbox = new QHBoxLayout; QCheckBox* useFixedFont = new QCheckBox(i18n("Use font with fixed width")); useFixedFont->setChecked(startupState); hbox->addWidget(useFixedFont); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); hbox->addWidget(buttonBox); mainLayout->addLayout(hbox); closeButton = buttonBox->button(QDialogButtonBox::Close); closeButton->setEnabled(false); auto *saveAsButton = new QPushButton; KGuiItem::assign(saveAsButton, KStandardGuiItem::saveAs()); buttonBox->addButton(saveAsButton, QDialogButtonBox::ActionRole); killButton = new QPushButton(i18n("Kill")); killButton->setToolTip(i18n("Kill the running process")); killButton->setDefault(true); buttonBox->addButton(killButton, QDialogButtonBox::ActionRole); connect(killButton, &QPushButton::clicked, this, &KrActionProcDlg::killClicked); connect(saveAsButton, &QPushButton::clicked, this, &KrActionProcDlg::slotSaveAs); connect(buttonBox, &QDialogButtonBox::rejected, this, &KrActionProcDlg::reject); connect(useFixedFont, &QCheckBox::toggled, this, &KrActionProcDlg::toggleFixedFont); resize(sizeHint() * 2); } void KrActionProcDlg::addStderr(const QString& str) { if (_stderr) _stderr->append(str); else { _stdout->setFontItalic(true); _stdout->append(str); _stdout->setFontItalic(false); } } void KrActionProcDlg::addStdout(const QString& str) { _stdout->append(str); } void KrActionProcDlg::toggleFixedFont(bool state) { if (state) { _stdout->setFont(fixedFont); if (_stderr) _stderr->setFont(fixedFont); } else { _stdout->setFont(normalFont); if (_stderr) _stderr->setFont(normalFont); } } void KrActionProcDlg::slotSaveAs() { QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), i18n("*.txt|Text files\n*|All files")); if (filename.isEmpty()) return; QFile file(filename); int answer = KMessageBox::Yes; if (file.exists()) answer = KMessageBox::warningYesNoCancel(this, //parent i18n("This file already exists.\nDo you want to overwrite it or append the output?"), //text i18n("Overwrite or append?"), //caption KStandardGuiItem::overwrite(), //label for Yes-Button KGuiItem(i18n("Append")) //label for No-Button ); if (answer == KMessageBox::Cancel) return; bool open; if (answer == KMessageBox::No) // this means to append open = file.open(QIODevice::WriteOnly | QIODevice::Append); else open = file.open(QIODevice::WriteOnly); if (! open) { KMessageBox::error(this, i18n("Cannot open %1 for writing.\nNothing exported.", filename), i18n("Export failed") ); return; } QTextStream stream(&file); stream << _currentTextEdit->toPlainText(); file.close(); } void KrActionProcDlg::slotProcessFinished() { closeButton->setEnabled(true); killButton->setEnabled(false); } void KrActionProcDlg::currentTextEditChanged() { if (_stderr && _stderr->hasFocus()) _currentTextEdit = _stderr; else _currentTextEdit = _stdout; } // KrActionProc KrActionProc::KrActionProc(KrActionBase* action) : QObject(), _action(action), _proc(nullptr), _output(nullptr) { } KrActionProc::~KrActionProc() { delete _proc; } -void KrActionProc::start(QString cmdLine) +void KrActionProc::start(const QString& cmdLine) { QStringList list; list << cmdLine; start(list); } void KrActionProc::start(QStringList cmdLineList) { QString cmd; // this is the command which is really executed (with maybe kdesu, maybe konsole, ...) // in case no specific working directory has been requested, execute in a relatively safe place QString workingDir = QDir::tempPath(); if (! _action->startpath().isEmpty()) workingDir = _action->startpath(); if (!_action->user().isEmpty()) { if (!KrServices::isExecutable(KDESU_PATH)) { KMessageBox::sorry(nullptr, i18n("Cannot run user action, %1 not found or not executable. " "Please verify that kde-cli-tools are installed.", QString(KDESU_PATH))); return; } } if (_action->execType() == KrAction::RunInTE && (! MAIN_VIEW->terminalDock()->initialise())) { KMessageBox::sorry(nullptr, i18n("Embedded terminal emulator does not work, using output collection instead.")); } for (QStringList::Iterator it = cmdLineList.begin(); it != cmdLineList.end(); ++it) { if (! cmd.isEmpty()) cmd += " ; "; //TODO make this separator configurable (users may want && or ||) //TODO: read header fom config or action-properties and place it on top of each command if (cmdLineList.count() > 1) cmd += "echo --------------------------------------- ; "; cmd += *it; } // make sure the command gets executed in the right directory cmd = "(cd " + KrServices::quote(workingDir) + " && (" + cmd + "))"; if (_action->execType() == KrAction::RunInTE && MAIN_VIEW->terminalDock()->isInitialised()) { //send the commandline contents to the terminal emulator if (!_action->user().isEmpty()) { // "-t" is necessary that kdesu displays the terminal-output of the command cmd = KrServices::quote(KDESU_PATH) + " -t -u " + _action->user() + " -c " + KrServices::quote(cmd); } MAIN_VIEW->terminalDock()->sendInput(cmd + '\n'); deleteLater(); } else { // will start a new process _proc = new KProcess(this); _proc->clearProgram(); // this clears the arglist too _proc->setWorkingDirectory(workingDir); connect(_proc, QOverload::of(&KProcess::finished), this, &KrActionProc::processExited); if (_action->execType() == KrAction::Normal || _action->execType() == KrAction::Terminal) { // not collect output if (_action->execType() == KrAction::Terminal) { // run in terminal KConfigGroup group(krConfig, "UserActions"); QString term = group.readEntry("Terminal", _UserActions_Terminal); QStringList termArgs = KShell::splitArgs(term, KShell::TildeExpand); if (termArgs.isEmpty()) { KMessageBox::error(nullptr, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in terminal command:\n%1", term)); deleteLater(); return; } for (int i = 0; i != termArgs.size(); i++) { if (termArgs[i] == "%t") termArgs[i] = cmdLineList.join(" ; "); else if (termArgs[i] == "%d") termArgs[i] = workingDir; } termArgs << "sh" << "-c" << cmd; cmd = KrServices::quote(termArgs).join(" "); } if (!_action->user().isEmpty()) { cmd = KrServices::quote(KDESU_PATH) + " -u " + _action->user() + " -c " + KrServices::quote(cmd); } } else { // collect output bool separateStderr = false; if (_action->execType() == KrAction::CollectOutputSeparateStderr) separateStderr = true; _output = new KrActionProcDlg(_action->text(), separateStderr); // connect the output to the dialog _proc->setOutputChannelMode(KProcess::SeparateChannels); connect(_proc, &KProcess::readyReadStandardError, this, &KrActionProc::addStderr); connect(_proc, &KProcess::readyReadStandardOutput, this, &KrActionProc::addStdout); connect(_output, &KrActionProcDlg::killClicked, this, &KrActionProc::kill); _output->show(); if (!_action->user().isEmpty()) { // "-t" is necessary that kdesu displays the terminal-output of the command cmd = KrServices::quote(KDESU_PATH) + " -t -u " + _action->user() + " -c " + KrServices::quote(cmd); } } //printf("cmd: %s\n", cmd.toAscii().data()); _proc->setShellCommand(cmd); _proc->start(); } } void KrActionProc::processExited(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { // enable the 'close' button on the dialog (if active), disable 'kill' button if (_output) { // TODO tell the user the program exit code _output->slotProcessFinished(); } delete this; // banzai!! } void KrActionProc::addStderr() { if (_output) { _output->addStderr(QString::fromLocal8Bit(_proc->readAllStandardError().data())); } } void KrActionProc::addStdout() { if (_output) { _output->addStdout(QString::fromLocal8Bit(_proc->readAllStandardOutput().data())); } } // KrAction -KrAction::KrAction(KActionCollection *parent, QString name) : QAction((QObject *)parent) +KrAction::KrAction(KActionCollection *parent, const QString& name) : QAction((QObject *)parent) { _actionCollection = parent; setObjectName(name); parent->addAction(name, this); connect(this, &KrAction::triggered, this, &KrAction::exec); } KrAction::~KrAction() { foreach(QWidget *w, associatedWidgets()) w->removeAction(this); krUserAction->removeKrAction(this); // Importent! Else Krusader will crash when writing the actions to file } bool KrAction::isAvailable(const QUrl ¤tURL) { bool available = true; //show per default (FIXME: make the default an attribute of ) //check protocol if (! _showonlyProtocol.empty()) { available = false; for (auto & it : _showonlyProtocol) { //qDebug() << "KrAction::isAvailable currentProtocol: " << currentURL.scheme() << " =?= " << *it; if (currentURL.scheme() == it) { // FIXME remove trailing slashes at the xml-parsing (faster because done only once) available = true; break; } } } //check protocol: done //check the Path-list: if (available && ! _showonlyPath.empty()) { available = false; for (auto & it : _showonlyPath) { if (it.right(1) == "*") { if (currentURL.path().indexOf(it.left(it.length() - 1)) == 0) { available = true; break; } } else if (currentURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() == it) { // FIXME remove trailing slashes at the xml-parsing (faster because done only once) available = true; break; } } } //check the Path-list: done //check mime-type if (available && ! _showonlyMime.empty()) { available = false; QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(currentURL); if (mime.isValid()) { for (auto & it : _showonlyMime) { if (it.contains("/")) { if (mime.inherits(it)) { // don't use ==; use 'inherits()' instead, which is aware of inheritance (ie: text/x-makefile is also text/plain) available = true; break; } } else { if (mime.name().indexOf(it) == 0) { // 0 is the beginning, -1 is not found available = true; break; } } } //for } } //check the mime-type: done //check filename if (available && ! _showonlyFile.empty()) { available = false; for (auto & it : _showonlyFile) { QRegExp regex = QRegExp(it, Qt::CaseInsensitive, QRegExp::Wildcard); // case-sensitive = false; wildcards = true if (regex.exactMatch(currentURL.fileName())) { available = true; break; } } } //check the filename: done return available; } bool KrAction::xmlRead(const QDomElement& element) { /* This has to be done elsewhere!! if ( element.tagName() != "action" ) return false; Also the name has to be checked before the action is created! setName( element.attribute( "name" ).toLatin1() ); */ QString attr; attr = element.attribute("enabled", "true"); // default: "true" if (attr == "false") { setEnabled(false); setVisible(false); } for (QDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { QDomElement e = node.toElement(); if (e.isNull()) continue; // this should skip nodes which are not elements ( i.e. comments, , or text nodes) if (e.tagName() == "title") setText(i18n(e.text().toUtf8().constData())); else if (e.tagName() == "tooltip") setToolTip(i18n(e.text().toUtf8().constData())); else if (e.tagName() == "icon") setIcon(Icon(_iconName = e.text())); else if (e.tagName() == "category") setCategory(i18n(e.text().toUtf8().constData())); else if (e.tagName() == "description") setWhatsThis(i18n(e.text().toUtf8().constData())); else if (e.tagName() == "command") readCommand(e); else if (e.tagName() == "startpath") setStartpath(e.text()); else if (e.tagName() == "availability") readAvailability(e); else if (e.tagName() == "defaultshortcut") _actionCollection->setDefaultShortcut(this, QKeySequence(e.text())); else // unknown but not empty if (! e.tagName().isEmpty()) qWarning() << "KrAction::xmlRead() - unrecognized tag found: <" << e.tagName() << ">"; } // for ( QDomNode node = action->firstChild(); !node.isNull(); node = node.nextSibling() ) return true; } //KrAction::xmlRead QDomElement KrAction::xmlDump(QDomDocument& doc) const { QDomElement actionElement = doc.createElement("action"); actionElement.setAttribute("name", objectName()); if (! isVisible()) { actionElement.setAttribute("enabled", "false"); } #define TEXT_ELEMENT( TAGNAME, TEXT ) \ { \ QDomElement e = doc.createElement( TAGNAME ); \ e.appendChild( doc.createTextNode( TEXT ) ); \ actionElement.appendChild( e ); \ } TEXT_ELEMENT("title", text()) if (! toolTip().isEmpty()) TEXT_ELEMENT("tooltip", toolTip()) if (! _iconName.isEmpty()) TEXT_ELEMENT("icon", _iconName) if (! category().isEmpty()) TEXT_ELEMENT("category", category()) if (! whatsThis().isEmpty()) TEXT_ELEMENT("description", whatsThis()) actionElement.appendChild(dumpCommand(doc)); if (! startpath().isEmpty()) TEXT_ELEMENT("startpath", startpath()) QDomElement availabilityElement = dumpAvailability(doc); if (availabilityElement.hasChildNodes()) actionElement.appendChild(availabilityElement); if (! shortcut().isEmpty()) TEXT_ELEMENT("defaultshortcut", shortcut().toString()) //.toString() would return a localised string which can't be read again return actionElement; } //KrAction::xmlDump void KrAction::readCommand(const QDomElement& element) { QString attr; attr = element.attribute("executionmode", "normal"); // default: "normal" if (attr == "normal") setExecType(Normal); else if (attr == "terminal") setExecType(Terminal); else if (attr == "collect_output") setExecType(CollectOutput); else if (attr == "collect_output_separate_stderr") setExecType(CollectOutputSeparateStderr); else if (attr == "embedded_terminal") setExecType(RunInTE); else qWarning() << "KrAction::readCommand() - unrecognized attribute value found: , or text nodes) QStringList* showlist = nullptr; if (e.tagName() == "protocol") showlist = &_showonlyProtocol; else if (e.tagName() == "path") showlist = &_showonlyPath; else if (e.tagName() == "mimetype") showlist = & _showonlyMime; else if (e.tagName() == "filename") showlist = & _showonlyFile; else { qWarning() << "KrAction::readAvailability() - unrecognized element found: <" << e.tagName() << ">"; showlist = nullptr; } if (showlist) { for (QDomNode subnode = e.firstChild(); ! subnode.isNull(); subnode = subnode.nextSibling()) { QDomElement subelement = subnode.toElement(); if (subelement.tagName() == "show") showlist->append(subelement.text()); } // for } // if ( showlist ) } // for } //KrAction::readAvailability QDomElement KrAction::dumpAvailability(QDomDocument& doc) const { QDomElement availabilityElement = doc.createElement("availability"); # define LIST_ELEMENT( TAGNAME, LIST ) \ { \ QDomElement e = doc.createElement( TAGNAME ); \ for ( QStringList::const_iterator it = LIST.constBegin(); it != LIST.constEnd(); ++it ) { \ QDomElement show = doc.createElement( "show" ); \ show.appendChild( doc.createTextNode( *it ) ); \ e.appendChild( show ); \ } \ availabilityElement.appendChild( e ); \ } if (! _showonlyProtocol.isEmpty()) LIST_ELEMENT("protocol", _showonlyProtocol) if (! _showonlyPath.isEmpty()) LIST_ELEMENT("path", _showonlyPath) if (! _showonlyMime.isEmpty()) LIST_ELEMENT("mimetype", _showonlyMime) if (! _showonlyFile.isEmpty()) LIST_ELEMENT("filename", _showonlyFile) return availabilityElement; } //KrAction::dumpAvailability diff --git a/krusader/UserAction/kraction.h b/krusader/UserAction/kraction.h index 1c5a563f..6df65cfc 100644 --- a/krusader/UserAction/kraction.h +++ b/krusader/UserAction/kraction.h @@ -1,250 +1,250 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2006 Jonas Bähr * * Copyright (C) 2004-2018 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 KRACTION_H #define KRACTION_H // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include "kractionbase.h" class QDomDocument; class QDomElement; class KActionCollection; /** * This subclass of QAction extends it with an individual executor and * a struct UserActionProperties. * It is used to integrate useractions into KDE's QAction-System */ class KrAction: public QAction, public KrActionBase { Q_OBJECT public: - explicit KrAction(KActionCollection *parent, QString name = QString()); + explicit KrAction(KActionCollection *parent, const QString& name = QString()); ~KrAction() override; /** * This chekcs if the KrAction is for a specific file / location available * @param currentURL Check for this file * @return true if the KrAction if available */ bool isAvailable(const QUrl ¤tURL); const QString& iconName() const { return _iconName; } // TODO: added for kde4 porting (functionality is missing) void setIconName(const QString& name) { _iconName = name; } bool xmlRead(const QDomElement& element); QDomElement xmlDump(QDomDocument& doc) const; QString category() const { return _category; }; void setCategory(const QString& category) { _category = category; }; QString command() const Q_DECL_OVERRIDE { return _command; }; void setCommand(const QString& command) { _command = command; }; QString user() const Q_DECL_OVERRIDE { return _user; }; void setUser(const QString& user) { _user = user; }; QString startpath() const Q_DECL_OVERRIDE { return _startpath; }; void setStartpath(const QString& startpath) { _startpath = startpath; }; ExecType execType() const Q_DECL_OVERRIDE { return _execType; }; void setExecType(ExecType execType) { _execType = execType; }; bool acceptURLs() const Q_DECL_OVERRIDE { return _acceptURLs; }; void setAcceptURLs(const bool& acceptURLs) { _acceptURLs = acceptURLs; }; bool confirmExecution() const Q_DECL_OVERRIDE { return _confirmExecution; }; void setConfirmExecution(const bool& confirmExecution) { _confirmExecution = confirmExecution; }; QStringList showonlyProtocol() const { return _showonlyProtocol; }; void setShowonlyProtocol(const QStringList& showonlyProtocol) { _showonlyProtocol = showonlyProtocol; }; QStringList showonlyPath() const { return _showonlyPath; }; void setShowonlyPath(const QStringList& showonlyPath) { _showonlyPath = showonlyPath; }; QStringList showonlyMime() const { return _showonlyMime; }; void setShowonlyMime(const QStringList& showonlyMime) { _showonlyMime = showonlyMime; }; QStringList showonlyFile() const { return _showonlyFile; }; void setShowonlyFile(const QStringList& showonlyFile) { _showonlyFile = showonlyFile; }; bool doSubstitution() const Q_DECL_OVERRIDE { return true; } QString text() const Q_DECL_OVERRIDE { return QAction::text(); } public slots: void exec() { KrActionBase::exec(); } private: void readCommand(const QDomElement& element); QDomElement dumpCommand(QDomDocument& doc) const; void readAvailability(const QDomElement& element); QDomElement dumpAvailability(QDomDocument& doc) const; QString _iconName; QString _category; QString _command; QString _user; QString _startpath; ExecType _execType; bool _acceptURLs; bool _confirmExecution; QStringList _showonlyProtocol; QStringList _showonlyPath; QStringList _showonlyMime; QStringList _showonlyFile; KActionCollection *_actionCollection; }; /** * This displays the output of a process */ class KrActionProcDlg: public QDialog { Q_OBJECT public: - explicit KrActionProcDlg(QString caption, bool enableStderr = false, QWidget *parent = nullptr); + explicit KrActionProcDlg(const QString& caption, bool enableStderr = false, QWidget *parent = nullptr); public slots: void addStderr(const QString& str); void addStdout(const QString& str); void slotProcessFinished(); protected slots: void toggleFixedFont(bool state); void slotSaveAs(); signals: void killClicked(); private: KTextEdit *_stdout; KTextEdit *_stderr; KTextEdit *_currentTextEdit; QFont normalFont; QFont fixedFont; QPushButton *closeButton; QPushButton *killButton; private slots: void currentTextEditChanged(); }; /** * This executes a command of a UserAction */ // TODO jonas: call a list of commands separately (I began it but it doesn't work) class KrActionProc: public QObject { Q_OBJECT public: explicit KrActionProc(KrActionBase* action); ~KrActionProc() override; - void start(QString cmdLine); + void start(const QString& cmdLine); void start(QStringList cmdLineList); protected slots: void kill() { _proc->kill(); } void processExited(int exitCode, QProcess::ExitStatus exitStatus); void addStderr(); void addStdout(); private: KrActionBase* _action; KProcess *_proc; QString _stdout; QString _stderr; KrActionProcDlg *_output; }; #endif //KRACTION_H diff --git a/krusader/actionsbase.cpp b/krusader/actionsbase.cpp index 61fede3c..670366c1 100644 --- a/krusader/actionsbase.cpp +++ b/krusader/actionsbase.cpp @@ -1,119 +1,120 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 "actionsbase.h" #include "krmainwindow.h" #include "icon.h" // QtWidgets #include #include #include +#include void ActionsBase::ActionGroup::reconnect(QObject *recv) { foreach(QAction *action, _slots.keys()) { disconnect(action, nullptr, nullptr, nullptr); connect(action, SIGNAL(triggered(bool)), recv, _slots[action]); } } void ActionsBase::ActionGroup::addAction(QAction *action, const char *slot) { _slots.insert(action, slot); } -QAction *ActionsBase::createAction(QString text, QString icon, bool isToggleAction) +QAction *ActionsBase::createAction(const QString& text, const QString& icon, bool isToggleAction) { if(isToggleAction) { if (icon == nullptr) return (QAction *)(new KToggleAction(text, this)); else return (QAction *)(new KToggleAction(Icon(icon), text, this)); } else { if (icon == nullptr) return new QAction(text, this); else return new QAction(Icon(icon), text, this); } } -QAction *ActionsBase::action(QString text, QString icon, QKeySequence shortcut, - QObject *recv, const char *slot, QString name, bool isToggleAction) +QAction *ActionsBase::action(QString text, QString icon, const QKeySequence& shortcut, + QObject *recv, const char *slot, const QString& name, bool isToggleAction) { - QAction *a = createAction(text, icon, isToggleAction); + QAction *a = createAction(std::move(text), std::move(icon), isToggleAction); connect(a, SIGNAL(triggered(bool)), recv, slot); _mainWindow->actions()->addAction(name, a); _mainWindow->actions()->setDefaultShortcut(a, shortcut); return a; } QAction *ActionsBase::action(QString text, QString icon, const QList &shortcuts, - QObject *recv, const char *slot, QString name, bool isToggleAction) + QObject *recv, const char *slot, const QString& name, bool isToggleAction) { - QAction *a = createAction(text, icon, isToggleAction); + QAction *a = createAction(std::move(text), std::move(icon), isToggleAction); connect(a, SIGNAL(triggered(bool)), recv, slot); _mainWindow->actions()->addAction(name, a); _mainWindow->actions()->setDefaultShortcuts(a, shortcuts); return a; } -QAction *ActionsBase::action(QString text, QString icon, QKeySequence shortcut, - ActionGroup &group, const char *slot, QString name, bool isToggleAction) +QAction *ActionsBase::action(QString text, QString icon, const QKeySequence& shortcut, + ActionGroup &group, const char *slot, const QString& name, bool isToggleAction) { - QAction *action = createAction(text, icon, isToggleAction); + QAction *action = createAction(std::move(text), std::move(icon), isToggleAction); group.addAction(action, slot); _mainWindow->actions()->addAction(name, action); _mainWindow->actions()->setDefaultShortcut(action, shortcut); return action; } -KToggleAction *ActionsBase::toggleAction(QString text, QString icon, QKeySequence shortcut, +KToggleAction *ActionsBase::toggleAction(QString text, QString icon, const QKeySequence& shortcut, QObject *recv, const char *slot, QString name) { - return (KToggleAction *)(action(text, icon, shortcut, recv, slot, name, true)); + return (KToggleAction *)(action(std::move(text), std::move(icon), shortcut, recv, slot, std::move(name), true)); } -KToggleAction *ActionsBase::toggleAction(QString text, QString icon, QKeySequence shortcut, +KToggleAction *ActionsBase::toggleAction(QString text, QString icon, const QKeySequence& shortcut, ActionGroup &group, const char *slot, QString name) { - return (KToggleAction *)(action(text, icon, shortcut, group, slot, name, true)); + return (KToggleAction *)(action(std::move(text), std::move(icon), shortcut, group, slot, std::move(name), true)); } QAction *ActionsBase::stdAction(KStandardAction::StandardAction id, QObject *recv, const char *slot) { return KStandardAction::create(id, recv, slot, _mainWindow->actions()); } QAction *ActionsBase::stdAction(KStandardAction::StandardAction id, ActionGroup &group, const char *slot) { QAction *action = KStandardAction::create(id, nullptr, nullptr, _mainWindow->actions()); group.addAction(action, slot); return action; } diff --git a/krusader/actionsbase.h b/krusader/actionsbase.h index f50db4bd..200790b2 100644 --- a/krusader/actionsbase.h +++ b/krusader/actionsbase.h @@ -1,82 +1,82 @@ /***************************************************************************** * Copyright (C) 2010 Jan Lepper * * Copyright (C) 2010-2018 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 ACTIONSBASE_H #define ACTIONSBASE_H // QtCore #include #include // QtGui #include // QtWidgets #include #include class KrMainWindow; class ActionsBase : public QObject { Q_OBJECT protected: ActionsBase(QObject *parent, KrMainWindow *mainWindow) : QObject(parent), _mainWindow(mainWindow) {} class ActionGroup { QHash _slots; public: void reconnect(QObject *recv); void addAction(QAction *action, const char *slot); }; - QAction *createAction(QString text, QString icon, bool isToggleAction); + QAction *createAction(const QString& text, const QString& icon, bool isToggleAction); - QAction *action(QString text, QString icon, QKeySequence shortcut, - QObject *recv, const char *slot, QString name, bool isToggleAction = false); + QAction *action(QString text, QString icon, const QKeySequence& shortcut, + QObject *recv, const char *slot, const QString& name, bool isToggleAction = false); QAction *action(QString text, QString icon, QKeySequence shortcut, const char *slot, QString name) { return action(text, icon, shortcut, this, slot, name); } QAction *action(QString text, QString icon, const QList &shortcuts, - QObject *recv, const char *slot, QString name, bool isToggleAction = false); - QAction *action(QString text, QString icon, QKeySequence shortcut, - ActionGroup &group, const char *slot, QString name, bool isToggleAction = false); + QObject *recv, const char *slot, const QString& name, bool isToggleAction = false); + QAction *action(QString text, QString icon, const QKeySequence& shortcut, + ActionGroup &group, const char *slot, const QString& name, bool isToggleAction = false); - KToggleAction *toggleAction(QString text, QString icon, QKeySequence shortcut, + KToggleAction *toggleAction(QString text, QString icon, const QKeySequence& shortcut, QObject *recv, const char *slot, QString name); KToggleAction *toggleAction(QString text, QString icon, QKeySequence shortcut, const char *slot, QString name) { return toggleAction(text, icon, shortcut, this, slot, name); } - KToggleAction *toggleAction(QString text, QString icon, QKeySequence shortcut, + KToggleAction *toggleAction(QString text, QString icon, const QKeySequence& shortcut, ActionGroup &group, const char *slot, QString name); QAction *stdAction(KStandardAction::StandardAction id, QObject *recv, const char *slot); QAction *stdAction(KStandardAction::StandardAction id, const char *slot) { return stdAction(id, this, slot); } QAction *stdAction(KStandardAction::StandardAction id, ActionGroup &group, const char *slot); KrMainWindow *_mainWindow; }; #endif // ACTIONSBASE_H diff --git a/krusader/icon.cpp b/krusader/icon.cpp index 27414ec2..ea2bf69d 100644 --- a/krusader/icon.cpp +++ b/krusader/icon.cpp @@ -1,310 +1,311 @@ /***************************************************************************** * Copyright (C) 2018 Nikita Melnichenko * * Copyright (C) 2018 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 "icon.h" #include "krglobal.h" // QtCore #include #include #include #include // QtGui #include #include #include #include #include +#include static const int cacheSize = 500; static const char *missingIconPath = ":/icons/icon-missing.svgz"; static inline QStringList getThemeFallbackList() { QStringList themes; // add user fallback theme if set if (krConfig) { const KConfigGroup group(krConfig, QStringLiteral("Startup")); QString userFallbackTheme = group.readEntry("Fallback Icon Theme", QString()); if (!userFallbackTheme.isEmpty()) { themes << userFallbackTheme; } } // Breeze and Oxygen are weak dependencies of Krusader, // i.e. each of the themes provide a complete set of icons used in the interface const QString breeze(Icon::isLightWindowThemeActive() ? "breeze" : "breeze-dark"); themes << breeze << "oxygen"; return themes; } class IconEngine : public QIconEngine { public: IconEngine(QString iconName, QIcon fallbackIcon, QStringList overlays = QStringList()) : - _iconName(iconName), _fallbackIcon(fallbackIcon), _overlays(overlays) + _iconName(std::move(iconName)), _fallbackIcon(std::move(fallbackIcon)), _overlays(std::move(overlays)) { _themeFallbackList = getThemeFallbackList(); } void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; IconEngine *clone() const override { return new IconEngine(*this); } private: QString _iconName; QStringList _themeFallbackList; QIcon _fallbackIcon; QStringList _overlays; }; Icon::Icon() : QIcon() { } Icon::Icon(QString name, QStringList overlays) : - QIcon(new IconEngine(name, QIcon(missingIconPath), overlays)) + QIcon(new IconEngine(std::move(name), QIcon(missingIconPath), std::move(overlays))) { } Icon::Icon(QString name, QIcon fallbackIcon, QStringList overlays) : - QIcon(new IconEngine(name, fallbackIcon, overlays)) + QIcon(new IconEngine(std::move(name), std::move(fallbackIcon), std::move(overlays))) { } struct IconSearchResult { QIcon icon; ///< icon returned by search; null icon if not found QString originalThemeName; ///< original theme name if theme is modified by search IconSearchResult(QIcon icon, QString originalThemeName) : - icon(icon), originalThemeName(originalThemeName) {} + icon(std::move(icon)), originalThemeName(std::move(originalThemeName)) {} }; // Search icon in the configured themes. // If this call modifies active theme, the original theme name will be specified in the result. -static inline IconSearchResult searchIcon(QString iconName, QStringList themeFallbackList) +static inline IconSearchResult searchIcon(const QString& iconName, QStringList themeFallbackList) { if (QDir::isAbsolutePath(iconName)) { // a path is used - directly load the icon return IconSearchResult(QIcon(iconName), QString()); } else if (QIcon::hasThemeIcon(iconName)) { // current theme has the icon - load seamlessly return IconSearchResult(QIcon::fromTheme(iconName), QString()); } else if (KIconLoader::global()->hasIcon(iconName)) { // KF icon loader does a wider search and helps with mime icons return IconSearchResult(KDE::icon(iconName), QString()); } else { // search the icon in fallback themes auto currentTheme = QIcon::themeName(); for (auto fallbackThemeName : themeFallbackList) { QIcon::setThemeName(fallbackThemeName); if (QIcon::hasThemeIcon(iconName)) { return IconSearchResult(QIcon::fromTheme(iconName), currentTheme); } } QIcon::setThemeName(currentTheme); // not found return IconSearchResult(QIcon(), QString()); } } -bool Icon::exists(QString iconName) +bool Icon::exists(const QString& iconName) { static QCache cache(cacheSize); static QString cachedTheme; // invalidate cache if system theme is changed if (cachedTheme != QIcon::themeName()) { cache.clear(); cachedTheme = QIcon::themeName(); } // return cached result when possible if (cache.contains(iconName)) { return *cache.object(iconName); } auto searchResult = searchIcon(iconName, getThemeFallbackList()); if (!searchResult.originalThemeName.isNull()) { QIcon::setThemeName(searchResult.originalThemeName); } auto *result = new bool(!searchResult.icon.isNull()); // update the cache; the cache takes ownership over the result cache.insert(iconName, result); return *result; } void Icon::applyOverlays(QPixmap *pixmap, QStringList overlays) { auto iconLoader = KIconLoader::global(); // Since KIconLoader loadIcon is not virtual method, we can't redefine loadIcon // that is called by drawOverlays. The best we can do is to go over the overlays // and ensure they exist from the icon loader point of view. // If not, we replace the overlay with "emblem-unreadable" which should be available // per freedesktop icon name specification: // https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html QStringList fixedOverlays; for (auto overlay : overlays) { if (overlay.isEmpty() || iconLoader->hasIcon(overlay)) { fixedOverlays << overlay; } else { fixedOverlays << "emblem-unreadable"; } } iconLoader->drawOverlays(fixedOverlays, *pixmap, KIconLoader::Desktop); } bool Icon::isLightWindowThemeActive() { const QColor textColor = QPalette().brush(QPalette::Text).color(); return (textColor.red() + textColor.green() + textColor.blue()) / 3 < 128; } class IconCacheKey { public: - IconCacheKey(const QString &name, QStringList overlays, + IconCacheKey(const QString &name, const QStringList& overlays, const QSize &size, QIcon::Mode mode, QIcon::State state) : name(name), overlays(overlays), size(size), mode(mode), state(state) { auto repr = QString("%1 [%2] %3x%4 %5 %6").arg(name).arg(overlays.join(';')) .arg(size.width()).arg(size.height()) .arg((int)mode).arg((int)state); _hash = qHash(repr); } bool operator ==(const IconCacheKey &x) const { return name == x.name && overlays == x.overlays && size == x.size && mode == x.mode && state == x.state; } uint hash() const { return _hash; } QString name; QStringList overlays; QSize size; QIcon::Mode mode; QIcon::State state; private: uint _hash; }; uint qHash(const IconCacheKey &key) { return key.hash(); } QPixmap IconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) { static QCache cache(cacheSize); static QString cachedTheme; QString systemTheme = QIcon::themeName(); // [WORKAROUND] If system theme is Breeze, pick light or dark variant of the theme explicitly // This type of selection works implicitly when QIcon::fromTheme is used, // however after QIcon::setThemeName it stops working for unknown reason. if (systemTheme == "breeze" || systemTheme == "breeze-dark") { const QString pickedSystemTheme(Icon::isLightWindowThemeActive() ? "breeze" : "breeze-dark"); if (systemTheme != pickedSystemTheme) { qDebug() << "System icon theme variant changed:" << systemTheme << "->" << pickedSystemTheme; systemTheme = pickedSystemTheme; QIcon::setThemeName(systemTheme); } } // invalidate cache if system theme is changed if (cachedTheme != systemTheme) { if (!cachedTheme.isEmpty()) { qDebug() << "System icon theme changed:" << cachedTheme << "->" << systemTheme; } cache.clear(); cachedTheme = systemTheme; } // an empty icon name is a special case - we don't apply any fallback if (_iconName.isEmpty()) { return QPixmap(); } auto key = IconCacheKey(_iconName, _overlays, size, mode, state); // return cached icon when possible if (cache.contains(key)) { return *cache.object(key); } // search icon and extract pixmap auto pixmap = new QPixmap; auto searchResult = searchIcon(_iconName, _themeFallbackList); if (!searchResult.icon.isNull()) { *pixmap = searchResult.icon.pixmap(size, mode, state); } if (!searchResult.originalThemeName.isNull()) { QIcon::setThemeName(searchResult.originalThemeName); } // can't find the icon neither in system theme nor in fallback themes - load fallback icon if (pixmap->isNull()) { qWarning() << "Unable to find icon" << _iconName << "of size" << size << "in any configured theme"; *pixmap = _fallbackIcon.pixmap(size, mode, state); } // apply overlays in a safe manner Icon::applyOverlays(pixmap, _overlays); // update the cache; the cache takes ownership over the pixmap cache.insert(key, pixmap); return *pixmap; } void IconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { QSize pixmapSize = rect.size(); QPixmap px = pixmap(pixmapSize, mode, state); painter->drawPixmap(rect, px); } diff --git a/krusader/icon.h b/krusader/icon.h index 58d32949..b66d9e71 100644 --- a/krusader/icon.h +++ b/krusader/icon.h @@ -1,55 +1,55 @@ /***************************************************************************** * Copyright (C) 2018 Nikita Melnichenko * * Copyright (C) 2018 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 ICON_H #define ICON_H // QtGui #include #include /** * @class Icon * * Universal icon class for Krusader. * * There is a list of configured themes that Icon searches in. * The order of themes is the following: active theme, theme specified by user, * fallback themes that provide complete icon set for Krusader. * If icon is not found in any configured theme, the fallback icon is used. */ class Icon : public QIcon { public: Icon(); explicit Icon(QString name, QStringList overlays = QStringList()); explicit Icon(QString name, QIcon fallbackIcon, QStringList overlays = QStringList()); /// Check if icon exists in any configured theme - static bool exists(QString iconName); + static bool exists(const QString& iconName); /// Apply overlays to the pixmap with fallbacks to standard emblems static void applyOverlays(QPixmap *pixmap, QStringList overlays); /// Determine if light window theme is currently enabled static bool isLightWindowThemeActive(); }; #endif // ICON_H diff --git a/krusader/kractions.cpp b/krusader/kractions.cpp index e86de784..bc3d97be 100644 --- a/krusader/kractions.cpp +++ b/krusader/kractions.cpp @@ -1,325 +1,325 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "kractions.h" // QtWidgets #include #include #include #include #include #include #include #include #include "defaults.h" #include "krusader.h" #include "icon.h" #include "krusaderview.h" #include "krslots.h" #include "krtrashhandler.h" #include "Dialogs/popularurls.h" #include "GUI/krremoteencodingmenu.h" #include "JobMan/jobman.h" #include "MountMan/kmountman.h" #include "Panel/PanelView/krviewfactory.h" #include "UserAction/useraction.h" QAction *KrActions::actCompare = nullptr; QAction *KrActions::actDiskUsage = nullptr; QAction *KrActions::actHomeTerminal = nullptr; QAction *KrActions::actRemoteEncoding = nullptr; QAction *KrActions::actProfiles = nullptr; QAction *KrActions::actMultiRename = nullptr; QAction *KrActions::actMountMan = nullptr; QAction *KrActions::actNewTool = nullptr; QAction *KrActions::actKonfigurator = nullptr; QAction *KrActions::actToolsSetup = nullptr; QAction *KrActions::actSwapPanels = nullptr; QAction *KrActions::actSwapSides = nullptr; QAction *KrActions::actFind = nullptr; QAction *KrActions::actLocate = nullptr; QAction *KrActions::actSwitchFullScreenTE = nullptr; QAction *KrActions::actAddBookmark = nullptr; QAction *KrActions::actSavePosition = nullptr; QAction *KrActions::actSelectColorMask = nullptr; QAction *KrActions::actOpenLeftBm = nullptr; QAction *KrActions::actOpenRightBm = nullptr; QAction *KrActions::actCmdlinePopup = nullptr; QAction *KrActions::actSplit = nullptr; QAction *KrActions::actCombine = nullptr; QAction *KrActions::actUserMenu = nullptr; QAction *KrActions::actManageUseractions = nullptr; #ifdef SYNCHRONIZER_ENABLED QAction *KrActions::actSyncDirs = nullptr; #endif QAction *KrActions::actF10Quit = nullptr; QAction *KrActions::actEmptyTrash = nullptr; QAction *KrActions::actTrashBin = nullptr; QAction *KrActions::actPopularUrls = nullptr; KToggleAction *KrActions::actToggleTerminal = nullptr; QAction *KrActions::actVerticalMode = nullptr; QAction *KrActions::actSelectNewerAndSingle = nullptr; QAction *KrActions::actSelectSingle = nullptr; QAction *KrActions::actSelectNewer = nullptr; QAction *KrActions::actSelectDifferentAndSingle = nullptr; QAction *KrActions::actSelectDifferent = nullptr; QAction **KrActions::compareArray[] = {&actSelectNewerAndSingle, &actSelectNewer, &actSelectSingle, &actSelectDifferentAndSingle, &actSelectDifferent, nullptr }; QAction *KrActions::actExecStartAndForget = nullptr; QAction *KrActions::actExecCollectSeparate = nullptr; QAction *KrActions::actExecCollectTogether = nullptr; QAction *KrActions::actExecTerminalExternal = nullptr; QAction *KrActions::actExecTerminalEmbedded = nullptr; QAction **KrActions::execTypeArray[] = {&actExecStartAndForget, &actExecCollectSeparate, &actExecCollectTogether, &actExecTerminalExternal, &actExecTerminalEmbedded, nullptr }; KToggleAction *KrActions::actToggleFnkeys = nullptr; KToggleAction *KrActions::actToggleCmdline = nullptr; KToggleAction *KrActions::actShowStatusBar = nullptr; KToggleAction *KrActions::actToggleHidden = nullptr; KToggleAction *KrActions::actCompareDirs = nullptr; QAction *KrActions::actJobProgress = nullptr; QAction *KrActions::actJobControl = nullptr; QAction *KrActions::actJobMode = nullptr; QAction *KrActions::actJobUndo = nullptr; #ifdef __KJSEMBED__ static QAction *actShowJSConsole; #endif -QAction *createAction(QString text, QString iconName, QKeySequence shortcut, - QObject *recv, const char *slot, QString name, Krusader *krusaderApp) +QAction *createAction(const QString& text, const QString& iconName, const QKeySequence& shortcut, + QObject *recv, const char *slot, const QString& name, Krusader *krusaderApp) { QAction *a; if (iconName.isEmpty()) a = new QAction(text, krusaderApp); else a = new QAction(Icon(iconName), text, krusaderApp); krusaderApp->connect(a, SIGNAL(triggered(bool)), recv, slot); krusaderApp->actionCollection()->addAction(name, a); krusaderApp->actionCollection()->setDefaultShortcut(a, shortcut); return a; } -QAction *createAction(QString text, QString iconName, QList shortcuts, - QObject *recv, const char *slot, QString name, Krusader *krusaderApp) +QAction *createAction(const QString& text, const QString& iconName, const QList& shortcuts, + QObject *recv, const char *slot, const QString& name, Krusader *krusaderApp) { QAction *a; if (iconName.isEmpty()) a = new QAction(text, krusaderApp); else a = new QAction(Icon(iconName), text, krusaderApp); krusaderApp->connect(a, SIGNAL(triggered(bool)), recv, slot); krusaderApp->actionCollection()->addAction(name, a); krusaderApp->actionCollection()->setDefaultShortcuts(a, shortcuts); return a; } -KToggleAction *createToggleAction(QString text, QString iconName, QKeySequence shortcut, - QObject *recv, const char *slot, QString name, Krusader *krusaderApp) +KToggleAction *createToggleAction(const QString& text, const QString& iconName, const QKeySequence& shortcut, + QObject *recv, const char *slot, const QString& name, Krusader *krusaderApp) { KToggleAction *a; if (iconName == nullptr) a = new KToggleAction(text, krusaderApp); else a = new KToggleAction(Icon(iconName), text, krusaderApp); krusaderApp->connect(a, SIGNAL(triggered(bool)), recv, slot); krusaderApp->actionCollection()->addAction(name, a); krusaderApp->actionCollection()->setDefaultShortcut(a, shortcut); return a; } void KrActions::setupActions(Krusader *krusaderApp) { #define NEW_KACTION(VAR, TEXT, ICON_NAME, SHORTCUT, RECV_OBJ, SLOT_NAME, NAME) \ VAR = createAction(TEXT, ICON_NAME, SHORTCUT, RECV_OBJ, SLOT_NAME, NAME, krusaderApp); #define NEW_KTOGGLEACTION(VAR, TEXT, ICON_NAME, SHORTCUT, RECV_OBJ, SLOT_NAME, NAME) \ VAR = createToggleAction(TEXT, ICON_NAME, SHORTCUT, RECV_OBJ, SLOT_NAME, NAME, krusaderApp); // first come the TODO actions //actSync = 0;//new QAction(i18n("S&ynchronize Dirs"), 0, krusaderApp, 0, actionCollection(), "sync dirs"); //actNewTool = 0;//new QAction(i18n("&Add a new tool"), 0, krusaderApp, 0, actionCollection(), "add tool"); //actToolsSetup = 0;//new QAction(i18n("&Tools Menu Setup"), 0, 0, krusaderApp, 0, actionCollection(), "tools setup"); //KStandardAction::print(SLOTS, 0,actionCollection(),"std_print"); //KStandardAction::showMenubar( SLOTS, SLOT(showMenubar()), actionCollection(), "std_menubar" ); /* Shortcut disabled because of the Terminal Emulator bug. */ KConfigGroup group(krConfig, "Private"); int compareMode = group.readEntry("Compare Mode", 0); int cmdExecMode = group.readEntry("Command Execution Mode", 0); QAction *tmp; Q_UNUSED(tmp); NEW_KACTION(tmp, i18n("Tab-Switch panel"), nullptr, Qt::Key_Tab, MAIN_VIEW, SLOT(panelSwitch()), "tab"); KToggleToolBarAction *actShowToolBar = new KToggleToolBarAction(krusaderApp->toolBar(), i18n("Show Main Toolbar"), krusaderApp); krusaderApp->actionCollection()->addAction(KStandardAction::name(KStandardAction::ShowToolbar), actShowToolBar); KToggleToolBarAction *actShowJobToolBar = new KToggleToolBarAction(krusaderApp->toolBar("jobToolBar"), i18n("Show Job Toolbar"), krusaderApp); krusaderApp->actionCollection()->addAction("toggle show jobbar", actShowJobToolBar); KToggleToolBarAction *actShowActionsToolBar = new KToggleToolBarAction(krusaderApp->toolBar("actionsToolBar"), i18n("Show Actions Toolbar"), krusaderApp); krusaderApp->actionCollection()->addAction("toggle actions toolbar", actShowActionsToolBar); actShowStatusBar = KStandardAction::showStatusbar(SLOTS, SLOT(updateStatusbarVisibility()), krusaderApp->actionCollection()); KStandardAction::quit(krusaderApp, SLOT(quit()), krusaderApp->actionCollection()); KStandardAction::configureToolbars(krusaderApp, SLOT(configureToolbars()), krusaderApp->actionCollection()); KStandardAction::keyBindings(krusaderApp->guiFactory(), SLOT(configureShortcuts()), krusaderApp->actionCollection()); // the toggle actions NEW_KTOGGLEACTION(actToggleFnkeys, i18n("Show &FN Keys Bar"), nullptr, 0, SLOTS, SLOT(toggleFnkeys()), "toggle fn bar"); NEW_KTOGGLEACTION(actToggleCmdline, i18n("Show &Command Line"), nullptr, 0, SLOTS, SLOT(toggleCmdline()), "toggle command line"); NEW_KTOGGLEACTION(actToggleTerminal, i18n("Show &Embedded Terminal"), nullptr, Qt::ALT + Qt::CTRL + Qt::Key_T, SLOTS, SLOT(toggleTerminal()), "toggle terminal emulator"); NEW_KTOGGLEACTION(actToggleHidden, i18n("Show &Hidden Files"), nullptr, Qt::ALT + Qt::Key_Period, SLOTS, SLOT(showHiddenFiles(bool)), "toggle hidden files"); NEW_KACTION(actSwapPanels, i18n("S&wap Panels"), nullptr, Qt::CTRL + Qt::Key_U, SLOTS, SLOT(swapPanels()), "swap panels"); NEW_KACTION(actEmptyTrash, i18n("Empty Trash"), "trash-empty", 0, SLOTS, SLOT(emptyTrash()), "emptytrash"); NEW_KACTION(actTrashBin, i18n("Trash Popup Menu"), KrTrashHandler::trashIconName(), 0, SLOTS, SLOT(trashPopupMenu()), "trashbin"); NEW_KACTION(actSwapSides, i18n("Sw&ap Sides"), nullptr, Qt::CTRL + Qt::SHIFT + Qt::Key_U, SLOTS, SLOT(toggleSwapSides()), "toggle swap sides"); actToggleHidden->setChecked(KConfigGroup(krConfig, "Look&Feel").readEntry("Show Hidden", _ShowHidden)); // and then the DONE actions NEW_KACTION(actCmdlinePopup, i18n("popup cmdline"), nullptr, Qt::CTRL + Qt::Key_Slash, SLOTS, SLOT(cmdlinePopup()), "cmdline popup"); NEW_KACTION(tmp, i18n("Start &Root Mode Krusader"), "krusader_root", Qt::ALT + Qt::SHIFT + Qt::Key_K, SLOTS, SLOT(rootKrusader()), "root krusader"); NEW_KACTION(actProfiles, i18n("Pro&files"), "user-identity", Qt::ALT + Qt::SHIFT + Qt::Key_L, MAIN_VIEW, SLOT(profiles()), "profile"); NEW_KACTION(actSplit, i18n("Sp&lit File..."), "split", Qt::CTRL + Qt::Key_P, SLOTS, SLOT(slotSplit()), "split"); NEW_KACTION(actCombine, i18n("Com&bine Files..."), "kr_combine", 0, SLOTS, SLOT(slotCombine()), "combine"); NEW_KACTION(actSelectNewerAndSingle, i18n("&Select Newer and Single"), nullptr, 0, SLOTS, SLOT(compareSetup()), "select_newer_and_single"); NEW_KACTION(actSelectNewer, i18n("Select &Newer"), nullptr, 0, SLOTS, SLOT(compareSetup()), "select_newer"); NEW_KACTION(actSelectSingle, i18n("Select &Single"), nullptr, 0, SLOTS, SLOT(compareSetup()), "select_single"); NEW_KACTION(actSelectDifferentAndSingle, i18n("Select Different &and Single"), nullptr, 0, SLOTS, SLOT(compareSetup()), "select_different_and_single"); NEW_KACTION(actSelectDifferent, i18n("Select &Different"), nullptr, 0, SLOTS, SLOT(compareSetup()), "select_different"); actSelectNewerAndSingle->setCheckable(true); actSelectNewer->setCheckable(true); actSelectSingle->setCheckable(true); actSelectDifferentAndSingle->setCheckable(true); actSelectDifferent->setCheckable(true); auto *selectGroup = new QActionGroup(krusaderApp); selectGroup->setExclusive(true); selectGroup->addAction(actSelectNewerAndSingle); selectGroup->addAction(actSelectNewer); selectGroup->addAction(actSelectSingle); selectGroup->addAction(actSelectDifferentAndSingle); selectGroup->addAction(actSelectDifferent); if (compareMode < (int)(sizeof(compareArray) / sizeof(QAction **)) - 1) (*compareArray[ compareMode ])->setChecked(true); NEW_KACTION(actExecStartAndForget, i18n("Start and &Forget"), nullptr, 0, SLOTS, SLOT(execTypeSetup()), "exec_start_and_forget"); NEW_KACTION(actExecCollectSeparate, i18n("Display &Separated Standard and Error Output"), nullptr, 0, SLOTS, SLOT(execTypeSetup()), "exec_collect_separate"); NEW_KACTION(actExecCollectTogether, i18n("Display &Mixed Standard and Error Output"), nullptr, 0, SLOTS, SLOT(execTypeSetup()), "exec_collect_together"); NEW_KACTION(actExecTerminalExternal, i18n("Start in &New Terminal"), nullptr, 0, SLOTS, SLOT(execTypeSetup()), "exec_terminal_external"); NEW_KACTION(actExecTerminalEmbedded, i18n("Send to &Embedded Terminal Emulator"), nullptr, 0, SLOTS, SLOT(execTypeSetup()), "exec_terminal_embedded"); actExecStartAndForget->setCheckable(true); actExecCollectSeparate->setCheckable(true); actExecCollectTogether->setCheckable(true); actExecTerminalExternal->setCheckable(true); actExecTerminalEmbedded->setCheckable(true); auto *actionGroup = new QActionGroup(krusaderApp); actionGroup->setExclusive(true); actionGroup->addAction(actExecStartAndForget); actionGroup->addAction(actExecCollectSeparate); actionGroup->addAction(actExecCollectTogether); actionGroup->addAction(actExecTerminalExternal); actionGroup->addAction(actExecTerminalEmbedded); if (cmdExecMode < (int)(sizeof(execTypeArray) / sizeof(QAction **)) - 1) (*execTypeArray[ cmdExecMode ])->setChecked(true); NEW_KACTION(actHomeTerminal, i18n("Start &Terminal"), "utilities-terminal", 0, SLOTS, SLOT(homeTerminal()), "terminal@home"); actMountMan = krMtMan.action(); krusaderApp->actionCollection()->addAction("mountman", actMountMan); krusaderApp->actionCollection()->setDefaultShortcut(actMountMan, Qt::ALT + Qt::Key_Slash); NEW_KACTION(actFind, i18n("&Search..."), "system-search", Qt::CTRL + Qt::Key_S, SLOTS, SLOT(search()), "find"); NEW_KACTION(actLocate, i18n("&Locate..."), "edit-find", Qt::SHIFT + Qt::CTRL + Qt::Key_L, SLOTS, SLOT(locate()), "locate"); #ifdef SYNCHRONIZER_ENABLED NEW_KACTION(actSyncDirs, i18n("Synchronize Fol&ders..."), "folder-sync", Qt::CTRL + Qt::Key_Y, SLOTS, SLOT(slotSynchronizeDirs()), "sync dirs"); #endif NEW_KACTION(actDiskUsage, i18n("D&isk Usage..."), "kr_diskusage", Qt::ALT + Qt::SHIFT + Qt::Key_S, SLOTS, SLOT(slotDiskUsage()), "disk usage"); NEW_KACTION(actKonfigurator, i18n("Configure &Krusader..."), "configure", 0, SLOTS, SLOT(startKonfigurator()), "konfigurator"); NEW_KACTION(actSavePosition, i18n("Save &Position"), nullptr, 0, krusaderApp, SLOT(savePosition()), "save position"); NEW_KACTION(actCompare, i18n("Compare b&y Content..."), "kr_comparedirs", 0, SLOTS, SLOT(compareContent()), "compare"); NEW_KACTION(actMultiRename, i18n("Multi &Rename..."), "edit-rename", Qt::SHIFT + Qt::Key_F2, SLOTS, SLOT(multiRename()), "multirename"); NEW_KACTION(actAddBookmark, i18n("Add Bookmark"), "bookmark-new", KStandardShortcut::addBookmark(), SLOTS, SLOT(addBookmark()), "add bookmark"); NEW_KACTION(actVerticalMode, i18n("Vertical Mode"), "view-split-top-bottom", Qt::ALT + Qt::CTRL + Qt::Key_R, MAIN_VIEW, SLOT(toggleVerticalMode()), "toggle vertical mode"); actUserMenu = new KActionMenu(i18n("User&actions"), krusaderApp); krusaderApp->actionCollection()->addAction("useractionmenu", actUserMenu); NEW_KACTION(actManageUseractions, i18n("Manage User Actions..."), nullptr, 0, SLOTS, SLOT(manageUseractions()), "manage useractions"); actRemoteEncoding = new KrRemoteEncodingMenu(i18n("Select Remote Charset"), "character-set", krusaderApp->actionCollection()); NEW_KACTION(actF10Quit, i18n("Quit"), nullptr, Qt::Key_F10, krusaderApp, SLOT(quit()) , "F10_Quit"); actF10Quit->setToolTip(i18n("Quit Krusader.")); NEW_KACTION(actPopularUrls, i18n("Popular URLs..."), nullptr, Qt::CTRL + Qt::Key_Z, krusaderApp->popularUrls(), SLOT(showDialog()), "Popular_Urls"); NEW_KACTION(actSwitchFullScreenTE, i18n("Toggle Fullscreen Embedded Terminal"), nullptr, Qt::CTRL + Qt::ALT + Qt::Key_F, MAIN_VIEW, SLOT(toggleFullScreenTerminalEmulator()), "switch_fullscreen_te"); NEW_KACTION(tmp, i18n("Move Focus Up"), nullptr, Qt::CTRL + Qt::SHIFT + Qt::Key_Up, MAIN_VIEW, SLOT(focusUp()), "move_focus_up"); NEW_KACTION(tmp, i18n("Move Focus Down"), nullptr, Qt::CTRL + Qt::SHIFT + Qt::Key_Down, MAIN_VIEW, SLOT(focusDown()), "move_focus_down"); // job manager actions actJobControl = krJobMan->controlAction(); krusaderApp->actionCollection()->addAction("job control", actJobControl); krusaderApp->actionCollection()->setDefaultShortcut(actJobControl, Qt::CTRL + Qt::ALT + Qt::Key_P); actJobProgress = krJobMan->progressAction(); krusaderApp->actionCollection()->addAction("job progress", actJobProgress); actJobMode = krJobMan->modeAction(); krusaderApp->actionCollection()->addAction("job mode", actJobMode); actJobUndo = krJobMan->undoAction(); krusaderApp->actionCollection()->addAction("job undo", actJobUndo); krusaderApp->actionCollection()->setDefaultShortcut(actJobUndo, Qt::CTRL + Qt::ALT + Qt::Key_Z); // and at last we can set the tool-tips actKonfigurator->setToolTip(i18n("Setup Krusader the way you like it")); actFind->setToolTip(i18n("Search for files")); // setup all UserActions krUserAction = new UserAction(); #ifdef __KJSEMBED__ actShowJSConsole = new QAction(i18n("JavaScript Console..."), Qt::ALT + Qt::CTRL + Qt::Key_J, SLOTS, SLOT(jsConsole()), krusaderApp->actionCollection(), "JS_Console"); #endif } diff --git a/krusader/krservices.cpp b/krusader/krservices.cpp index c2a333d7..81ea9eea 100644 --- a/krusader/krservices.cpp +++ b/krusader/krservices.cpp @@ -1,319 +1,320 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "krservices.h" // QtCore #include #include #include #include #include #include +#include #include "krglobal.h" #include "defaults.h" QMap* KrServices::slaveMap = nullptr; QSet KrServices::krarcArchiveMimetypes = KrServices::generateKrarcArchiveMimetypes(); #ifdef KRARC_QUERY_ENABLED QSet KrServices::isoArchiveMimetypes = QSet::fromList(KProtocolInfo::archiveMimetypes("iso")); #else QSet KrServices::isoArchiveMimetypes; #endif QString KrServices::GLOBAL_MESSAGE_PATTERN = "%{time hh:mm:ss.zzz}-%{type} %{category} %{function}@%{line} # %{message}"; QSet KrServices::generateKrarcArchiveMimetypes() { // Hard-code these proven mimetypes openable by krarc protocol. // They cannot be listed in krarc.protocol itself // because it would baffle other file managers (like Dolphin). QSet mimes; mimes += QString("application/x-deb"); mimes += QString("application/x-debian-package"); mimes += QString("application/vnd.debian.binary-package"); mimes += QString("application/x-java-archive"); mimes += QString("application/x-rpm"); mimes += QString("application/x-source-rpm"); mimes += QString("application/vnd.oasis.opendocument.chart"); mimes += QString("application/vnd.oasis.opendocument.database"); mimes += QString("application/vnd.oasis.opendocument.formula"); mimes += QString("application/vnd.oasis.opendocument.graphics"); mimes += QString("application/vnd.oasis.opendocument.presentation"); mimes += QString("application/vnd.oasis.opendocument.spreadsheet"); mimes += QString("application/vnd.oasis.opendocument.text"); mimes += QString("application/vnd.openxmlformats-officedocument.presentationml.presentation"); mimes += QString("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); mimes += QString("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); mimes += QString("application/x-cbz"); mimes += QString("application/x-cbr"); mimes += QString("application/epub+zip"); mimes += QString("application/x-webarchive"); mimes += QString("application/x-plasma"); mimes += QString("application/vnd.rar"); #ifdef KRARC_QUERY_ENABLED mimes += QSet::fromList(KProtocolInfo::archiveMimetypes("krarc")); #endif return mimes; } -bool KrServices::cmdExist(QString cmdName) +bool KrServices::cmdExist(const QString& cmdName) { KConfigGroup group(krConfig, "Dependencies"); if (QFile(group.readEntry(cmdName, QString())).exists()) return true; return !QStandardPaths::findExecutable(cmdName).isEmpty(); } -QString KrServices::fullPathName(QString name, QString confName) +QString KrServices::fullPathName(const QString& name, QString confName) { QString supposedName; if (confName.isNull()) confName = name; KConfigGroup config(krConfig, "Dependencies"); if (QFile(supposedName = config.readEntry(confName, QString())).exists()) return supposedName; if ((supposedName = QStandardPaths::findExecutable(name)).isEmpty()) return ""; config.writeEntry(confName, supposedName); return supposedName; } -QString KrServices::chooseFullPathName(QStringList names, QString confName) +QString KrServices::chooseFullPathName(QStringList names, const QString& confName) { foreach(const QString &name, names) { QString foundTool = KrServices::fullPathName(name, confName); if (! foundTool.isEmpty()) { return foundTool; } } return ""; } bool KrServices::isExecutable(const QString &path) { QFileInfo info(path); return info.isFile() && info.isExecutable(); } -QString KrServices::registeredProtocol(QString mimetype) +QString KrServices::registeredProtocol(const QString& mimetype) { if (slaveMap == nullptr) { slaveMap = new QMap(); KConfigGroup group(krConfig, "Protocols"); QStringList protList = group.readEntry("Handled Protocols", QStringList()); for (auto & it : protList) { QStringList mimes = group.readEntry(QString("Mimes For %1").arg(it), QStringList()); for (auto & mime : mimes) (*slaveMap)[mime] = it; } } QString protocol = (*slaveMap)[mimetype]; if (protocol.isEmpty()) { if (krarcArchiveMimetypes.contains(mimetype)) { return "krarc"; } protocol = KProtocolManager::protocolForArchiveMimetype(mimetype); } return protocol; } -bool KrServices::isoSupported(QString mimetype) +bool KrServices::isoSupported(const QString& mimetype) { return isoArchiveMimetypes.contains(mimetype); } void KrServices::clearProtocolCache() { if (slaveMap) delete slaveMap; slaveMap = nullptr; } bool KrServices::fileToStringList(QTextStream *stream, QStringList& target, bool keepEmptyLines) { if (!stream) return false; QString line; while (!stream->atEnd()) { line = stream->readLine().trimmed(); if (keepEmptyLines || !line.isEmpty()) target.append(line); } return true; } bool KrServices::fileToStringList(QFile *file, QStringList& target, bool keepEmptyLines) { QTextStream stream(file); return fileToStringList(&stream, target, keepEmptyLines); } -QString KrServices::quote(QString name) +QString KrServices::quote(const QString& name) { if (!name.contains('\'')) return '\'' + name + '\''; if (!name.contains('"') && !name.contains('$')) return '\"' + name + '\"'; return escape(name); } QStringList KrServices::quote(const QStringList& names) { QStringList result; for (int i = 0; i < names.size(); ++i) result.append(quote(names[i])); return result; } QList KrServices::toUrlList(const QStringList &list) { QList result; for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { result.append(QUrl::fromUserInput(*it, QDir::currentPath(), QUrl::AssumeLocalFile)); } return result; } QStringList KrServices::toStringList(const QList &list) { QStringList result; for(QList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { result.append(it->toString()); } return result; } // Adds one tool to the list in the supportedTools method -void supportedTool(QStringList &tools, QString toolType, +void supportedTool(QStringList &tools, const QString& toolType, QStringList names, QString confName) { - QString foundTool = KrServices::chooseFullPathName(names, confName); + QString foundTool = KrServices::chooseFullPathName(std::move(names), std::move(confName)); if (! foundTool.isEmpty()) { tools.append(toolType); tools.append(foundTool); } } // return a list in the format of TOOLS,PATH. for example // DIFF,kdiff,TERMINAL,konsole,... // // currently supported tools: DIFF, MAIL, RENAME // // to use it: QStringList lst = supportedTools(); // int i = lst.indexOf("DIFF"); // if (i!=-1) pathToDiff=lst[i+1]; QStringList KrServices::supportedTools() { QStringList tools; // first, a diff program: kdiff supportedTool(tools, "DIFF", QStringList() << "kdiff3" << "kompare" << "xxdiff", "diff utility"); // a mailer: kmail or thunderbird supportedTool(tools, "MAIL", QStringList() << "thunderbird" << "kmail", "mailer"); // rename tool: krename supportedTool(tools, "RENAME", QStringList() << "krename", "krename"); // checksum utility supportedTool(tools, "MD5", QStringList() << "md5sum", "checksum utility"); return tools; } QString KrServices::escape(QString name) { const QString evilstuff = "\\\"'`()[]{}!?;$&<>| \t\r\n"; // stuff that should get escaped for (auto i : evilstuff) name.replace(i, ('\\' + i)); return name; } QString KrServices::escapeFileUrl(QString urlString) { // Avoid that if a path contains a '#' then what follows the '#' be interpreted as the fragment identifier of // the URL and not a part of the file path; for more information https://bugs.kde.org/show_bug.cgi?id=270150 can be seen return urlString.replace('#', "%23").replace('?', "%3F"); } QUrl KrServices::escapeFileUrl(const QUrl &url) { return QUrl(KrServices::escapeFileUrl(url.toString())); } QString KrServices::urlToLocalPath(const QUrl &url) { QUrl fileUrl = QUrl(url); // QUrl::toLocalFile() does not work if the protocol is "file" e.g. when opening an archive fileUrl.setScheme("file"); QString path = fileUrl.toLocalFile(); REPLACE_DIR_SEP2(path); #ifdef Q_WS_WIN if (path.startsWith(DIR_SEPARATOR)) { int p = 1; while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR) p++; /* /C:/Folder */ if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') { path = path.mid(p); } } #endif return path; } static bool s_withDebugMessages; static QtMessageHandler s_defaultMessageHandler; void KrServices::setGlobalKrMessageHandler(bool withDebugMessages) { s_withDebugMessages = withDebugMessages; s_defaultMessageHandler = qInstallMessageHandler(nullptr); qInstallMessageHandler(&krMessageHandler); } void KrServices::krMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { // filter debug if not enabled if (type != QtDebugMsg || s_withDebugMessages) { s_defaultMessageHandler(type, context, msg); } } diff --git a/krusader/krservices.h b/krusader/krservices.h index 6ee5d21e..fb5b3b68 100644 --- a/krusader/krservices.h +++ b/krusader/krservices.h @@ -1,79 +1,79 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 KRSERVICES_H #define KRSERVICES_H // QtCore #include #include #include #include class QTextStream; class QFile; /** * Global static utility functions. */ class KrServices { public: - static bool cmdExist(QString cmdName); - static QString chooseFullPathName(QStringList names, QString confName); - static QString fullPathName(QString name, QString confName = QString()); + static bool cmdExist(const QString& cmdName); + static QString chooseFullPathName(QStringList names, const QString& confName); + static QString fullPathName(const QString& name, QString confName = QString()); static bool isExecutable(const QString &path); - static QString registeredProtocol(QString mimetype); - static bool isoSupported(QString mimetype); + static QString registeredProtocol(const QString& mimetype); + static bool isoSupported(const QString& mimetype); static QString urlToLocalPath(const QUrl &url); static void clearProtocolCache(); static bool fileToStringList(QTextStream *stream, QStringList& target, bool keepEmptyLines = false); static bool fileToStringList(QFile *file, QStringList& target, bool keepEmptyLines = false); - static QString quote(QString name); + static QString quote(const QString& name); static QStringList quote(const QStringList& names); static QList toUrlList(const QStringList &list); static QStringList toStringList(const QList &list); static QStringList supportedTools(); // find supported tools static QString escapeFileUrl(QString urlString); static QUrl escapeFileUrl(const QUrl &url); /** * Sets the global logging message handler for qDebug(), qWarning()... messages to a custom one * with the ability to filter debug messages. */ static void setGlobalKrMessageHandler(bool withDebugMessages); static QString GLOBAL_MESSAGE_PATTERN; protected: static QString escape(QString name); private: KrServices() {} ~KrServices() {} static QSet generateKrarcArchiveMimetypes(); static QMap* slaveMap; static QSet krarcArchiveMimetypes; static QSet isoArchiveMimetypes; static void krMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); }; // TODO: make KrServices a namespace and move it there #endif diff --git a/krusader/krslots.cpp b/krusader/krslots.cpp index cfe346a9..78d9f2f9 100644 --- a/krusader/krslots.cpp +++ b/krusader/krslots.cpp @@ -1,747 +1,748 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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(QUrl url1, QUrl url2) +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()) 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()) 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 { 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(), selected); + 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(); } diff --git a/krusader/krslots.h b/krusader/krslots.h index 1109dc0e..c379bc98 100644 --- a/krusader/krslots.h +++ b/krusader/krslots.h @@ -1,117 +1,118 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2018 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 KRSLOTS_H #define KRSLOTS_H // QtCore #include #include #include #include +#include class KrMainWindow; class QUrl; class KrProcess: public KProcess { Q_OBJECT QString tmp1, tmp2; public: KrProcess(QString in1, QString in2) { - tmp1 = in1; - tmp2 = in2; + tmp1 = std::move(in1); + tmp2 = std::move(in2); connect(this, QOverload::of(&KrProcess::finished), this, &KrProcess::processHasExited); } public slots: void processHasExited() { if (!tmp1.isEmpty()) QFile::remove(tmp1); if (!tmp2.isEmpty()) QFile::remove(tmp2); deleteLater(); } }; class KRslots : public QObject { Q_OBJECT public: enum compareMode { full }; explicit KRslots(QObject *parent); ~KRslots() override = default; public slots: void sendFileByEmail(const QList &filename); void compareContent(); - void compareContent(QUrl, QUrl); + void compareContent(const QUrl&, const QUrl&); void insertFileName(bool fullPath); void rootKrusader(); void swapPanels(); void showHiddenFiles(bool show); void toggleSwapSides(); void updateStatusbarVisibility(); void toggleTerminal(); void compareSetup(); void emptyTrash(); void trashPopupMenu(); /** called by actExec* actions to choose the built-in command line mode */ void execTypeSetup(); void refresh(const QUrl &u); void runKonfigurator(bool firstTime = false); void startKonfigurator() { runKonfigurator(false); } void search(); // call the search module void locate(); void runTerminal(const QString & dir); void homeTerminal(); void addBookmark(); void toggleFnkeys(); void toggleCmdline(); void multiRename(); void cmdlinePopup(); void slotSplit(); void slotCombine(); void manageUseractions(); #ifdef SYNCHRONIZER_ENABLED void slotSynchronizeDirs(QStringList selected = QStringList()); #endif void slotDiskUsage(); void applicationStateChanged(); void jsConsole(); protected slots: void configChanged(bool isGUIRestartNeeded); protected: KrMainWindow *_mainWindow; }; #endif diff --git a/krusader/krusader.cpp b/krusader/krusader.cpp index b5230fc8..f2e34bfd 100644 --- a/krusader/krusader.cpp +++ b/krusader/krusader.cpp @@ -1,639 +1,640 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "krusader.h" // QtCore #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include // QtDBus #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "defaults.h" #include "kractions.h" #include "krglobal.h" #include "krservices.h" #include "krslots.h" #include "krtrashhandler.h" #include "krusaderversion.h" #include "krusaderview.h" #include "panelmanager.h" #include "tabactions.h" #include "BookMan/krbookmarkhandler.h" #include "Dialogs/checksumdlg.h" #include "Dialogs/krpleasewait.h" #include "Dialogs/popularurls.h" #include "FileSystem/fileitem.h" #include "FileSystem/krpermhandler.h" #include "GUI/kcmdline.h" #include "GUI/kfnkeys.h" #include "GUI/krremoteencodingmenu.h" #include "GUI/krusaderstatus.h" #include "GUI/terminaldock.h" #include "JobMan/jobman.h" #include "KViewer/krviewer.h" #include "Konfigurator/kgprotocols.h" #include "MountMan/kmountman.h" #include "Panel/PanelView/krview.h" #include "Panel/PanelView/krviewfactory.h" #include "Panel/krcolorcache.h" #include "Panel/krpanel.h" #include "Panel/listpanelactions.h" #include "Panel/viewactions.h" #include "UserAction/expander.h" // This makes gcc-4.1 happy. Warning about possible problem with KrAction's dtor not called #include "UserAction/kraction.h" #include "UserAction/useraction.h" #ifdef __KJSEMBED__ #include "KrJS/krjs.h" #endif // define the static members Krusader *Krusader::App = nullptr; QString Krusader::AppName; // KrBookmarkHandler *Krusader::bookman = 0; //QTextOStream *Krusader::_krOut = QTextOStream(::stdout); #ifdef __KJSEMBED__ KrJS *Krusader::js = 0; QAction *Krusader::actShowJSConsole = 0; #endif // construct the views, statusbar and menu bars and prepare Krusader to start Krusader::Krusader(const QCommandLineParser &parser) : KParts::MainWindow(nullptr, Qt::Window | Qt::WindowTitleHint | Qt::WindowContextHelpButtonHint), _listPanelActions(nullptr), isStarting(true), isExiting(false), _quit(false) { // create the "krusader" App = this; krMainWindow = this; SLOTS = new KRslots(this); setXMLFile("krusaderui.rc"); // kpart-related xml file plzWait = new KRPleaseWaitHandler(this); const bool runKonfig = versionControl(); QString message; switch (krConfig->accessMode()) { case KConfigBase::NoAccess : message = "Krusader's configuration file can't be found. Default values will be used."; break; case KConfigBase::ReadOnly : message = "Krusader's configuration file is in READ ONLY mode (why is that!?) Changed values will not be saved"; break; case KConfigBase::ReadWrite : message = ""; break; } if (!message.isEmpty()) { KMessageBox::error(krApp, message); } // create MountMan KrGlobal::mountMan = new KMountMan(this); connect(KrGlobal::mountMan, &KMountMan::refreshPanel, SLOTS, &KRslots::refresh); // create popular URLs container _popularUrls = new PopularUrls(this); // create bookman krBookMan = new KrBookmarkHandler(this); // create job manager krJobMan = new JobMan(this); // create the main view MAIN_VIEW = new KrusaderView(this); // setup all the krusader's actions setupActions(); // init the permission handler class KRpermHandler::init(); // init the protocol handler KgProtocols::init(); const KConfigGroup lookFeelGroup(krConfig, "Look&Feel"); FileItem::loadUserDefinedFolderIcons(lookFeelGroup.readEntry("Load User Defined Folder Icons", _UserDefinedFolderIcons)); const KConfigGroup startupGroup(krConfig, "Startup"); QString startProfile = startupGroup.readEntry("Starter Profile Name", QString()); QList leftTabs; QList rightTabs; // get command-line arguments if (parser.isSet("left")) { leftTabs = KrServices::toUrlList(parser.value("left").split(',')); startProfile.clear(); } if (parser.isSet("right")) { rightTabs = KrServices::toUrlList(parser.value("right").split(',')); startProfile.clear(); } if (parser.isSet("profile")) startProfile = parser.value("profile"); if (!startProfile.isEmpty()) { leftTabs.clear(); rightTabs.clear(); } // starting the panels MAIN_VIEW->start(startupGroup, startProfile.isEmpty(), leftTabs, rightTabs); // create a status bar auto *status = new KrusaderStatus(this); setStatusBar(status); status->setWhatsThis(i18n("Statusbar will show basic information " "about file below mouse pointer.")); // create tray icon (if needed) const bool startToTray = startupGroup.readEntry("Start To Tray", _StartToTray); setTray(startToTray); setCentralWidget(MAIN_VIEW); // manage our keyboard short-cuts //KAcceleratorManager::manage(this,true); setCursor(Qt::ArrowCursor); if (! startProfile.isEmpty()) MAIN_VIEW->profiles(startProfile); // restore gui settings { // now, check if we need to create a konsole_part // call the XML GUI function to draw the UI createGUI(MAIN_VIEW->terminalDock()->part()); // this needs to be called AFTER createGUI() !!! updateUserActions(); _listPanelActions->guiUpdated(); // not using this. See savePosition() //applyMainWindowSettings(); const KConfigGroup cfgToolbar(krConfig, "Main Toolbar"); toolBar()->applySettings(cfgToolbar); const KConfigGroup cfgJobBar(krConfig, "Job Toolbar"); toolBar("jobToolBar")->applySettings(cfgJobBar); const KConfigGroup cfgActionsBar(krConfig, "Actions Toolbar"); toolBar("actionsToolBar")->applySettings(cfgActionsBar); // restore toolbars position and visibility restoreState(startupGroup.readEntry("State", QByteArray())); statusBar()->setVisible(startupGroup.readEntry("Show status bar", _ShowStatusBar)); MAIN_VIEW->updateGUI(startupGroup); // popular urls _popularUrls->load(); } if (runKonfig) SLOTS->runKonfigurator(true); KConfigGroup viewerModuleGrp(krConfig, "ViewerModule"); if (viewerModuleGrp.readEntry("FirstRun", true)) { KrViewer::configureDeps(); viewerModuleGrp.writeEntry("FirstRun", false); } if (!runKonfig) { KConfigGroup cfg(krConfig, "Private"); move(cfg.readEntry("Start Position", _StartPosition)); resize(cfg.readEntry("Start Size", _StartSize)); } // view initialized; show window or only tray if (!startToTray) { show(); } KrTrashHandler::startWatcher(); isStarting = false; //HACK - used by [ListerTextArea|KrSearchDialog|LocateDlg]:keyPressEvent() KrGlobal::copyShortcut = _listPanelActions->actCopy->shortcut(); //HACK: make sure the active view becomes focused // for some reason sometimes the active view cannot be focused immediately at this point, // so queue it for the main loop QTimer::singleShot(0, ACTIVE_PANEL->view->widget(), QOverload<>::of(&QWidget::setFocus)); _openUrlTimer.setSingleShot(true); connect(&_openUrlTimer, &QTimer::timeout, this, &Krusader::doOpenUrl); auto *startupInfo = new KStartupInfo(0, this); connect(startupInfo, &KStartupInfo::gotNewStartup, this, &Krusader::slotGotNewStartup); connect(startupInfo, &KStartupInfo::gotRemoveStartup, this, &Krusader::slotGotRemoveStartup); } Krusader::~Krusader() { KrTrashHandler::stopWatcher(); if (!isExiting) // save the settings if it was not saved (SIGTERM received) saveSettings(); delete MAIN_VIEW; MAIN_VIEW = nullptr; App = nullptr; } void Krusader::setTray(bool forceCreation) { const bool trayIsNeeded = forceCreation || KConfigGroup(krConfig, "Look&Feel") .readEntry("Minimize To Tray", _ShowTrayIcon); if (!sysTray && trayIsNeeded) { sysTray = new KStatusNotifierItem(this); sysTray->setIconByName(appIconName()); // we have our own "quit" method, re-connect QAction *quitAction = sysTray->action(QStringLiteral("quit")); if (quitAction) { disconnect(quitAction, &QAction::triggered, nullptr, nullptr); connect(quitAction, &QAction::triggered, this, &Krusader::quit); } } else if (sysTray && !trayIsNeeded) { // user does not want tray anymore :( sysTray->deleteLater(); } } bool Krusader::versionControl() { // create config file krConfig = KSharedConfig::openConfig().data(); KConfigGroup nogroup(krConfig, QString()); const bool firstRun = nogroup.readEntry("First Time", true); KrGlobal::sCurrentConfigVersion = nogroup.readEntry("Config Version", -1); // first installation of krusader if (firstRun) { KMessageBox::information( krApp, i18n("Welcome to Krusader.

As this is your first run, your machine " "will now be checked for external applications. Then the Konfigurator will " "be launched where you can customize Krusader to your needs.

")); } nogroup.writeEntry("Version", VERSION); nogroup.writeEntry("First Time", false); krConfig->sync(); QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/krusader/")); return firstRun; } void Krusader::statusBarUpdate(const QString& mess) { // change the message on the statusbar for 5 seconds if (statusBar()->isVisible()) statusBar()->showMessage(mess, 5000); } bool Krusader::event(QEvent *e) { if(e->type() == QEvent::ApplicationPaletteChange) { KrColorCache::getColorCache().refreshColors(); } return KParts::MainWindow::event(e); } // Moving from Pixmap actions to generic filenames - thanks to Carsten Pfeiffer void Krusader::setupActions() { QAction *bringToTopAct = new QAction(i18n("Bring Main Window to Top"), this); actionCollection()->addAction("bring_main_window_to_top", bringToTopAct); connect(bringToTopAct, &QAction::triggered, this, &Krusader::moveToTop); KrActions::setupActions(this); _krActions = new KrActions(this); _viewActions = new ViewActions(this, this); _listPanelActions = new ListPanelActions(this, this); _tabActions = new TabActions(this, this); } /////////////////////////////////////////////////////////////////////////// //////////////////// implementation of slots ////////////////////////////// /////////////////////////////////////////////////////////////////////////// void Krusader::savePosition() { KConfigGroup cfg(krConfig, "Private"); cfg.writeEntry("Start Position", pos()); cfg.writeEntry("Start Size", size()); cfg = krConfig->group("Startup"); MAIN_VIEW->saveSettings(cfg); // NOTE: this would save current window state/size, statusbar and settings for each toolbar. // We are not using this and saving everything manually because // - it does not save window position // - window size save/restore does sometimes not work (multi-monitor setup) // - saving the statusbar visibility should be independent from window position and restoring it // does not work properly. //KConfigGroup cfg = KConfigGroup(&cfg, "MainWindowSettings"); //saveMainWindowSettings(cfg); //statusBar()->setVisible(cfg.readEntry("StatusBar", "Enabled") != "Disabled"); krConfig->sync(); } void Krusader::saveSettings() { // workaround: revert terminal fullscreen mode before saving widget and toolbar visibility if (MAIN_VIEW->isTerminalEmulatorFullscreen()) { MAIN_VIEW->setTerminalEmulator(false, true); } KConfigGroup noGroup(krConfig, QString()); noGroup.writeEntry("Config Version", KrGlobal::sConfigVersion); // save toolbar settings KConfigGroup cfg(krConfig, "Main Toolbar"); toolBar()->saveSettings(cfg); cfg = krConfig->group("Job Toolbar"); toolBar("jobToolBar")->saveSettings(cfg); cfg = krConfig->group("Actions Toolbar"); toolBar("actionsToolBar")->saveSettings(cfg); cfg = krConfig->group("Startup"); // save toolbar visibility and position cfg.writeEntry("State", saveState()); cfg.writeEntry("Show status bar", statusBar()->isVisible()); // save panel and window settings if (cfg.readEntry("Remember Position", _RememberPos)) savePosition(); // save the gui components visibility if (cfg.readEntry("UI Save Settings", _UiSave)) { cfg.writeEntry("Show FN Keys", KrActions::actToggleFnkeys->isChecked()); cfg.writeEntry("Show Cmd Line", KrActions::actToggleCmdline->isChecked()); cfg.writeEntry("Show Terminal Emulator", KrActions::actToggleTerminal->isChecked()); } // save popular links _popularUrls->save(); krConfig->sync(); } void Krusader::closeEvent(QCloseEvent *event) { if (!sysTray || _quit) { _quit = false; // in case quit will be aborted KParts::MainWindow::closeEvent(event); // (may) quit, continues with queryClose()... } else { // close window to tray event->ignore(); hide(); } } void Krusader::showEvent(QShowEvent *event) { const KConfigGroup lookFeelGroup(krConfig, "Look&Feel"); if (sysTray && !lookFeelGroup.readEntry("Minimize To Tray", _ShowTrayIcon)) { // restoring from "start to tray", tray icon is not needed anymore sysTray->deleteLater(); } KParts::MainWindow::showEvent(event); } bool Krusader::queryClose() { if (isStarting || isExiting) return false; if (qApp->isSavingSession()) { // KDE is logging out, accept the close acceptClose(); return true; } const KConfigGroup cfg = krConfig->group("Look&Feel"); const bool confirmExit = cfg.readEntry("Warn On Exit", _WarnOnExit); // ask user and wait until all KIO::job operations are terminated. Krusader won't exit before // that anyway if (!krJobMan->waitForJobs(confirmExit)) return false; /* First try to close the child windows, because it's the safer way to avoid crashes, then close the main window. If closing a child is not successful, then we cannot let the main window close. */ for (;;) { QWidgetList list = QApplication::topLevelWidgets(); QWidget *activeModal = QApplication::activeModalWidget(); QWidget *w = list.at(0); if (activeModal && activeModal != this && activeModal != menuBar() && list.contains(activeModal) && !activeModal->isHidden()) { w = activeModal; } else { int i = 1; for (; i < list.count(); ++i) { w = list.at(i); if (!(w && (w == this || w->isHidden() || w == menuBar()))) break; } if (i == list.count()) w = nullptr; } if (!w) break; if (!w->close()) { if (w->inherits("QDialog")) { fprintf(stderr, "Failed to close: %s\n", w->metaObject()->className()); } return false; } } acceptClose(); return true; } void Krusader::acceptClose() { saveSettings(); emit shutdown(); // Removes the DBUS registration of the application. Single instance mode requires unique appid. // As Krusader is exiting, we release that unique appid, so new Krusader instances // can be started. QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.unregisterObject("/Instances/" + Krusader::AppName); isExiting = true; } // the please wait dialog functions void Krusader::startWaiting(QString msg, int count , bool cancel) { - plzWait->startWaiting(msg , count, cancel); + plzWait->startWaiting(std::move(msg) , count, cancel); } bool Krusader::wasWaitingCancelled() const { return plzWait->wasCancelled(); } void Krusader::stopWait() { plzWait->stopWait(); } void Krusader::updateUserActions() { auto *userActionMenu = (KActionMenu *) KrActions::actUserMenu; if (userActionMenu) { userActionMenu->menu()->clear(); userActionMenu->addAction(KrActions::actManageUseractions); userActionMenu->addSeparator(); krUserAction->populateMenu(userActionMenu, nullptr); } } const char* Krusader::appIconName() { if (geteuid()) return "krusader_user"; else return "krusader_root"; } void Krusader::quit() { _quit = true; // remember that we want to quit and not close to tray close(); // continues with closeEvent()... } void Krusader::moveToTop() { if (isHidden()) show(); KWindowSystem::forceActiveWindow(winId()); } bool Krusader::isRunning() { moveToTop(); //FIXME - doesn't belong here return true; } bool Krusader::isLeftActive() { return MAIN_VIEW->isLeftActive(); } bool Krusader::openUrl(QString url) { - _urlToOpen = url; + _urlToOpen = std::move(url); _openUrlTimer.start(0); return true; } void Krusader::doOpenUrl() { QUrl url = QUrl::fromUserInput(_urlToOpen, QDir::currentPath(), QUrl::AssumeLocalFile); _urlToOpen.clear(); int tab = ACTIVE_MNG->findTab(url); if(tab >= 0) ACTIVE_MNG->setActiveTab(tab); else if((tab = OTHER_MNG->findTab(url)) >= 0) { OTHER_MNG->setActiveTab(tab); OTHER_MNG->currentPanel()->view->widget()->setFocus(); } else ACTIVE_MNG->slotNewTab(url); } void Krusader::slotGotNewStartup(const KStartupInfoId &id, const KStartupInfoData &data) { Q_UNUSED(id) Q_UNUSED(data) // This is here to show busy mouse cursor when _other_ applications are launched, not for krusader itself. qApp->setOverrideCursor(Qt::BusyCursor); } void Krusader::slotGotRemoveStartup(const KStartupInfoId &id, const KStartupInfoData &data) { Q_UNUSED(id) Q_UNUSED(data) qApp->restoreOverrideCursor(); } KrView *Krusader::activeView() { return ACTIVE_PANEL->view; } AbstractPanelManager *Krusader::activeManager() { return MAIN_VIEW->activeManager(); } AbstractPanelManager *Krusader::leftManager() { return MAIN_VIEW->leftManager(); } AbstractPanelManager *Krusader::rightManager() { return MAIN_VIEW->rightManager(); } diff --git a/krusader/krusaderview.cpp b/krusader/krusaderview.cpp index b0e209a5..d228987d 100644 --- a/krusader/krusaderview.cpp +++ b/krusader/krusaderview.cpp @@ -1,530 +1,530 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 "krusaderview.h" // QtCore #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include "krusader.h" #include "icon.h" #include "kractions.h" #include "krslots.h" #include "defaults.h" #include "Panel/listpanel.h" #include "Panel/panelfunc.h" #include "GUI/kcmdline.h" #include "GUI/kfnkeys.h" #include "GUI/terminaldock.h" #include "panelmanager.h" #include "GUI/profilemanager.h" #include "Dialogs/percentalsplitter.h" #include "krservices.h" KrusaderView::KrusaderView(QWidget *parent) : QWidget(parent), activeMng(nullptr) { } void KrusaderView::start(const KConfigGroup &cfg, bool restoreSettings, const QList &leftTabs, const QList &rightTabs) { //////////////////////////////// // make a 1x1 mainLayout, it will auto-expand: mainLayout = new QGridLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); // vertical splitter vert_splitter = new QSplitter(this); // splits between panels and terminal/cmdline vert_splitter->setOrientation(Qt::Vertical); // horizontal splitter horiz_splitter = new PercentalSplitter(vert_splitter); (_terminalDock = new TerminalDock(vert_splitter, krApp))->hide(); // create it hidden // create a command line thing _cmdLine = new KCMDLine(this); // add a panel manager for each side of the splitter leftMng = createManager(true); rightMng = createManager(false); leftMng->setOtherManager(rightMng); rightMng->setOtherManager(leftMng); // make the left panel focused at program start activeMng = leftMng; // create the function keys widget _fnKeys = new KFnKeys(this, krApp); _fnKeys->hide(); _fnKeys->setWhatsThis(i18n("Function keys allow performing fast " "operations on files.")); // and insert the whole thing into the main layout... at last mainLayout->addWidget(vert_splitter, 0, 0); //<> mainLayout->addWidget(_cmdLine, 1, 0); mainLayout->addWidget(_fnKeys, 2, 0); mainLayout->activate(); // get the last saved sizes of the splitter QList lst = cfg.readEntry("Splitter Sizes", QList()); if (lst.count() != 2) { lst.clear(); lst.push_back(100); lst.push_back(100); } else if (lst[0] < 1 && lst[1] < 1) { lst[ 0 ] = 100; lst[ 1 ] = 100; } horiz_splitter->setSizes(lst); verticalSplitterSizes = cfg.readEntry("Terminal Emulator Splitter Sizes", QList ()); if (verticalSplitterSizes.count() != 2 || (verticalSplitterSizes[0] < 1 && verticalSplitterSizes[1] < 1)) { verticalSplitterSizes.clear(); verticalSplitterSizes << 100 << 100; } leftPanel()->start(leftTabs.isEmpty() ? QUrl::fromLocalFile(QDir::homePath()) : leftTabs.at(0)); rightPanel()->start(rightTabs.isEmpty() ? QUrl::fromLocalFile(QDir::homePath()) : rightTabs.at(0)); activePanel()->gui->slotFocusOnMe(); // left starts out active for (int i = 1; i < leftTabs.count(); i++) leftMng->slotNewTab(leftTabs.at(i), false); for (int j = 1; j < rightTabs.count(); j++) rightMng->slotNewTab(rightTabs.at(j), false); // this is needed so that all tab labels get updated leftMng->layoutTabs(); rightMng->layoutTabs(); if(restoreSettings) { if(leftTabs.isEmpty()) leftMng->loadSettings(KConfigGroup(&cfg, "Left Tab Bar")); if(rightTabs.isEmpty()) rightMng->loadSettings(KConfigGroup(&cfg, "Right Tab Bar")); if (cfg.readEntry("Left Side Is Active", false)) leftPanel()->slotFocusOnMe(); else rightPanel()->slotFocusOnMe(); } } void KrusaderView::updateGUI(const KConfigGroup &cfg) { if (!cfg.readEntry("Show Cmd Line", _ShowCmdline)) { cmdLine()->hide(); KrActions::actToggleCmdline->setChecked(false); } else { cmdLine()->show(); KrActions::actToggleCmdline->setChecked(true); } // update the Fn bar to the shortcuts selected by the user fnKeys()->updateShortcuts(); if (!cfg.readEntry("Show FN Keys", _ShowFNkeys)) { fnKeys()->hide(); KrActions::actToggleFnkeys->setChecked(false); } else { fnKeys()->show(); KrActions::actToggleFnkeys->setChecked(true); } // set vertical mode if (cfg.readEntry("Vertical Mode", false)) { toggleVerticalMode(); } if (cfg.readEntry("Show Terminal Emulator", _ShowTerminalEmulator)) { setTerminalEmulator(true); // create konsole_part }; } void KrusaderView::setPanelSize(bool leftPanel, int percent) { QList panelSizes = horiz_splitter->sizes(); int totalSize = panelSizes[0] + panelSizes[1]; if (leftPanel) { panelSizes[0] = totalSize * percent / 100; panelSizes[1] = totalSize * (100 - percent) / 100; } else { // == RIGHT_PANEL panelSizes[0] = totalSize * (100 - percent) / 100; panelSizes[1] = totalSize * percent / 100; } horiz_splitter->setSizes(panelSizes); } PanelManager *KrusaderView::createManager(bool left) { auto *panelManager = new PanelManager(horiz_splitter, krApp, left); connect(panelManager, &PanelManager::draggingTab, this, &KrusaderView::draggingTab); connect(panelManager, &PanelManager::draggingTabFinished, this, &KrusaderView::draggingTabFinished); connect(panelManager, &PanelManager::pathChanged, this, &KrusaderView::slotPathChanged); connect(panelManager, &PanelManager::setActiveManager, this, &KrusaderView::slotSetActiveManager); return panelManager; } void KrusaderView::updateCurrentActivePath() { const QString path = activePanel()->gui->lastLocalPath(); _cmdLine->setCurrent(path); KConfigGroup cfg = krConfig->group("General"); if (_terminalDock->isInitialised() && cfg.readEntry("Send CDs", _SendCDs)) { _terminalDock->sendCd(path); } } KrPanel *KrusaderView::activePanel() const { // active manager might not be set yet return activeMng ? activeMng->currentPanel() : nullptr; } ListPanel *KrusaderView::leftPanel() const { return leftMng->currentPanel()->gui; } ListPanel *KrusaderView::rightPanel() const { return rightMng->currentPanel()->gui; } // updates the command line whenever current panel or its path changes void KrusaderView::slotPathChanged(ListPanel *listPanel) { if (listPanel == activePanel()) { updateCurrentActivePath(); } } int KrusaderView::getFocusCandidates(QVector &widgets) { activePanel()->gui->getFocusCandidates(widgets); if(_terminalDock->isTerminalVisible()) widgets << _terminalDock; if(_cmdLine->isVisible()) widgets << _cmdLine; for(int i = 0; i < widgets.count(); i++) { if(widgets[i] == focusWidget() || widgets[i]->focusWidget() == focusWidget()) return i; } return -1; } void KrusaderView::focusUp() { qDebug() << "focus UP"; QVector widgets; int currentFocus = getFocusCandidates(widgets); if(currentFocus < 0) return; currentFocus--; if(currentFocus >= 0 && currentFocus < widgets.count()) widgets[currentFocus]->setFocus(); } void KrusaderView::focusDown() { qDebug() << "focus DOWN"; QVector widgets; int currentFocus = getFocusCandidates(widgets); if(currentFocus < 0) return; currentFocus++; if(currentFocus < widgets.count()) widgets[currentFocus]->setFocus(); } void KrusaderView::cmdLineFocus() // command line receives keyboard focus { _cmdLine->setFocus(); } void KrusaderView::cmdLineUnFocus() // return focus to the active panel { activePanel()->gui->slotFocusOnMe(); } // Tab - switch focus void KrusaderView::panelSwitch() { activePanel()->otherPanel()->gui->slotFocusOnMe(); } void KrusaderView::slotSetActiveManager(PanelManager *manager) { activeMng = manager; updateCurrentActivePath(); } void KrusaderView::swapSides() { QList lst = horiz_splitter->sizes(); horiz_splitter->addWidget(leftMng); int old = lst[ 0 ]; lst[ 0 ] = lst [ 1 ]; lst[ 1 ] = old; horiz_splitter->setSizes(lst); PanelManager *tmpMng = leftMng; leftMng = rightMng; rightMng = tmpMng; leftMng->setLeft(true); rightMng->setLeft(false); leftPanel()->updateGeometry(); rightPanel()->updateGeometry(); } void KrusaderView::setTerminalEmulator(bool show, bool fullscreen) { qDebug() << "show=" << show << " fullscreen=" << fullscreen; static bool fnKeysShown = true; // first time init. should be overridden static bool cmdLineShown = true; static bool statusBarShown = true; static bool mainToolBarShown = true; static bool jobToolBarShown = true; static bool actionToolBarShown = true; static bool menuBarShown = true; static bool terminalEmulatorShown = true; if (show) { if (fullscreen) { // save what is shown fnKeysShown = !_fnKeys->isHidden(); cmdLineShown = !_cmdLine->isHidden(); statusBarShown = !krApp->statusBar()->isHidden(); mainToolBarShown = !krApp->toolBar()->isHidden(); jobToolBarShown = !krApp->toolBar("jobToolBar")->isHidden(); actionToolBarShown = !krApp->toolBar("actionToolBar")->isHidden(); menuBarShown = !krApp->menuBar()->isHidden(); terminalEmulatorShown = _terminalDock->isTerminalVisible(); } if(!_terminalDock->isTerminalVisible()) { // show terminal const bool isInitialized = _terminalDock->initialise(); if (!isInitialized) { _terminalDock->hide(); KrActions::actToggleTerminal->setChecked(false); return; } _terminalDock->show(); _terminalDock->setFocus(); updateCurrentActivePath(); KrActions::actToggleTerminal->setChecked(true); } else if (fullscreen) { // save current terminal size before going to fullscreen verticalSplitterSizes = vert_splitter->sizes(); } if (fullscreen) { // hide everything else leftMng->hide(); rightMng->hide(); _fnKeys->hide(); _cmdLine->hide(); krApp->statusBar()->hide(); krApp->toolBar()->hide(); krApp->toolBar("jobToolBar")->hide(); krApp->toolBar("actionToolBar")->hide(); krApp->menuBar()->hide(); // fix showing nothing if terminal is open but splitter widget size is zero vert_splitter->setSizes(QList() << 0 << vert_splitter->height()); } else { vert_splitter->setSizes(verticalSplitterSizes); } } else { // hide const bool isFullscreen = isTerminalEmulatorFullscreen(); if (!(fullscreen && terminalEmulatorShown)) { // hide terminal emulator activePanel()->gui->slotFocusOnMe(); if (_terminalDock->isTerminalVisible() && !isFullscreen) { verticalSplitterSizes = vert_splitter->sizes(); } _terminalDock->hide(); KrActions::actToggleTerminal->setChecked(false); } else { // not fullscreen anymore but terminal is still visible vert_splitter->setSizes(verticalSplitterSizes); } if (isFullscreen) { // restore: unhide everything that was hidden before leftMng->show(); rightMng->show(); if (fnKeysShown) _fnKeys->show(); if (cmdLineShown) _cmdLine->show(); if (statusBarShown) krApp->statusBar()->show(); if (mainToolBarShown) krApp->toolBar()->show(); if (jobToolBarShown) krApp->toolBar("jobToolBar")->show(); if (actionToolBarShown) krApp->toolBar("actionToolBar")->show(); if (menuBarShown) krApp->menuBar()->show(); } } } void KrusaderView::focusTerminalEmulator() { if (_terminalDock->isTerminalVisible()) _terminalDock->setFocus(); } void KrusaderView::toggleFullScreenTerminalEmulator() { setTerminalEmulator(!isTerminalEmulatorFullscreen(), true); } bool KrusaderView::isTerminalEmulatorFullscreen() { return leftMng->isHidden() && rightMng->isHidden(); } -void KrusaderView::profiles(QString profileName) +void KrusaderView::profiles(const QString& profileName) { ProfileManager profileManager("Panel", this); profileManager.hide(); connect(&profileManager, &ProfileManager::saveToProfile, this, &KrusaderView::savePanelProfiles); connect(&profileManager, &ProfileManager::loadFromProfile, this, &KrusaderView::loadPanelProfiles); if (profileName.isEmpty()) profileManager.profilePopup(); else profileManager.loadProfile(profileName); } -void KrusaderView::loadPanelProfiles(QString group) +void KrusaderView::loadPanelProfiles(const QString& group) { KConfigGroup ldg(krConfig, group); leftMng->loadSettings(KConfigGroup(&ldg, "Left Tabs")); rightMng->loadSettings(KConfigGroup(&ldg, "Right Tabs")); if (ldg.readEntry("Left Side Is Active", true)) leftPanel()->slotFocusOnMe(); else rightPanel()->slotFocusOnMe(); } -void KrusaderView::savePanelProfiles(QString group) +void KrusaderView::savePanelProfiles(const QString& group) { KConfigGroup svr(krConfig, group); svr.writeEntry("Vertical Mode", isVertical()); svr.writeEntry("Left Side Is Active", activePanel()->gui->isLeft()); leftMng->saveSettings(KConfigGroup(&svr, "Left Tabs"), false); rightMng->saveSettings(KConfigGroup(&svr, "Right Tabs"), false); } void KrusaderView::toggleVerticalMode() { if (horiz_splitter->orientation() == Qt::Vertical) { horiz_splitter->setOrientation(Qt::Horizontal); KrActions::actVerticalMode->setText(i18n("Vertical Mode")); KrActions::actVerticalMode->setIcon(Icon("view-split-top-bottom")); } else { horiz_splitter->setOrientation(Qt::Vertical); KrActions::actVerticalMode->setText(i18n("Horizontal Mode")); KrActions::actVerticalMode->setIcon(Icon("view-split-left-right")); } } void KrusaderView::saveSettings(KConfigGroup &cfg) { QList lst = horiz_splitter->sizes(); cfg.writeEntry("Splitter Sizes", lst); QList vertSplitterSizes = _terminalDock->isVisible() && !isTerminalEmulatorFullscreen() // fix sizes() not returning correct values on fullscreen+shutdown && vert_splitter->sizes().first() != 0 ? vert_splitter->sizes() : verticalSplitterSizes; cfg.writeEntry("Terminal Emulator Splitter Sizes", vertSplitterSizes); cfg.writeEntry("Vertical Mode", isVertical()); cfg.writeEntry("Left Side Is Active", activePanel()->gui->isLeft()); leftMng->saveSettings(KConfigGroup(&cfg, "Left Tab Bar"), true); rightMng->saveSettings(KConfigGroup(&cfg, "Right Tab Bar"), true); } bool KrusaderView::cursorIsOnOtherSide(PanelManager *of, const QPoint &globalPos) { int border = -1; int pos = -1; if (horiz_splitter->orientation() == Qt::Horizontal) { pos = globalPos.x(); if(of == leftMng) border = leftMng->mapToGlobal(QPoint(leftMng->width(), 0)).x(); else border = rightMng->mapToGlobal(QPoint(0, 0)).x(); } else { pos = globalPos.y(); if(of == leftMng) border = leftMng->mapToGlobal(QPoint(0, leftMng->height())).y(); else border = rightMng->mapToGlobal(QPoint(0, 0)).y(); } return (of == leftMng) ? pos > border : pos < border; } void KrusaderView::draggingTab(PanelManager *from, QMouseEvent *e) { QString icon; if (horiz_splitter->orientation() == Qt::Horizontal) icon = (from == leftMng) ? "arrow-right" : "arrow-left"; else icon = (from == leftMng) ? "arrow-down" : "arrow-up"; QCursor cursor(Icon(icon).pixmap(22)); if (cursorIsOnOtherSide(from, e->globalPos())) { if(!qApp->overrideCursor()) qApp->setOverrideCursor(cursor); } else qApp->restoreOverrideCursor(); } void KrusaderView::draggingTabFinished(PanelManager *from, QMouseEvent *e) { qApp->restoreOverrideCursor(); if (cursorIsOnOtherSide(from, e->globalPos())) from->moveTabToOtherSide(); } diff --git a/krusader/krusaderview.h b/krusader/krusaderview.h index 626e8df8..30683089 100644 --- a/krusader/krusaderview.h +++ b/krusader/krusaderview.h @@ -1,112 +1,112 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 KRUSADERVIEW_H #define KRUSADERVIEW_H // QtCore #include // QtWidgets #include #include #include #include #include #include "krglobal.h" class PanelManager; class ListPanel; class KFnKeys; class KCMDLine; class TerminalDock; class KrusaderView : public QWidget { Q_OBJECT public: explicit KrusaderView(QWidget *parent = nullptr); ~KrusaderView() override = default; void start(const KConfigGroup &cfg, bool restoreSettings, const QList &leftTabs, const QList &rightTabs); void updateGUI(const KConfigGroup &cfg); void saveSettings(KConfigGroup &cfg); void cmdLineFocus(); // command line receives keyboard focus void cmdLineUnFocus();// return focus from command line to active panel bool isLeftActive() const { return leftMng == activeMng; } // used by krGlobal macros PanelManager *activeManager() const { return activeMng; } PanelManager *inactiveManager() const { return activeMng == leftMng ? rightMng : leftMng; } PanelManager *leftManager() const { return leftMng; } PanelManager *rightManager() const { return rightMng; } KrPanel *activePanel() const; ListPanel *leftPanel() const; ListPanel *rightPanel() const; KFnKeys *fnKeys() const { return _fnKeys; } KCMDLine *cmdLine() const { return _cmdLine; } TerminalDock *terminalDock() const { return _terminalDock; } bool isVertical() const { return horiz_splitter != nullptr ? horiz_splitter->orientation() == Qt::Vertical : false; } void swapSides(); void setPanelSize(bool leftPanel, int percent); bool isTerminalEmulatorFullscreen(); public slots: void slotSetActiveManager(PanelManager *manager); void slotPathChanged(ListPanel *listPanel); // Tab - switch focus void panelSwitch(); void toggleVerticalMode(); void setTerminalEmulator(bool show, bool fullscreen = false); void focusTerminalEmulator(); void toggleFullScreenTerminalEmulator(); void focusUp(); void focusDown(); - void profiles(QString profileName = QString()); - void loadPanelProfiles(QString group); - void savePanelProfiles(QString group); + void profiles(const QString& profileName = QString()); + void loadPanelProfiles(const QString& group); + void savePanelProfiles(const QString& group); void draggingTab(PanelManager *from, QMouseEvent *e); void draggingTabFinished(PanelManager *from, QMouseEvent *e); private: int getFocusCandidates(QVector &widgets); bool cursorIsOnOtherSide(PanelManager *of, const QPoint &globalPos); PanelManager *createManager(bool left); void updateCurrentActivePath(); KFnKeys *_fnKeys; // function keys KCMDLine *_cmdLine; // command line widget TerminalDock *_terminalDock; // docking widget for terminal emulator QSplitter *horiz_splitter, *vert_splitter; QList verticalSplitterSizes; PanelManager *activeMng, *leftMng, *rightMng; // saving them for panel swaps QGridLayout *mainLayout, *terminal_layout; }; #endif diff --git a/krusader/main.cpp b/krusader/main.cpp index 8d378d8b..b2e6ec63 100644 --- a/krusader/main.cpp +++ b/krusader/main.cpp @@ -1,317 +1,317 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 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 #include // QtCore #include #include #include #include #include #include #include // QtGui #include // QtDBus #include #include // QtWidgets #include #include #include #include #include #include #include #include "../Archive/krarchandler.h" #include "defaults.h" #include "krservices.h" #include "krslots.h" #include "krusader.h" #include "icon.h" #include "krusaderversion.h" #include "krusaderview.h" #include "panelmanager.h" static const char *description = I18N_NOOP("Krusader\nTwin-Panel File Manager by KDE"); static void sigterm_handler(int i) { fprintf(stderr, "Signal: %d\n", i); QAbstractEventDispatcher *instance = QAbstractEventDispatcher::instance(); if (instance) instance->wakeUp(); QApplication::exit(- 15); } -void openTabsRemote(QStringList tabs, bool left, QString appName) +void openTabsRemote(QStringList tabs, bool left, const QString& appName) { // make sure left or right are not relative paths for (int i = 0; i != tabs.count(); i++) { tabs[ i ] = tabs[ i ].trimmed(); if (!tabs[ i ].startsWith('/') && tabs[ i ].indexOf(":/") < 0) tabs[ i ] = QDir::currentPath() + '/' + tabs[ i ]; } QDBusInterface remoteApp("org.krusader", "/Instances/" + appName + (left ? "/left_manager" : "/right_manager"), "org.krusader.PanelManager", QDBusConnection::sessionBus()); QDBusReply reply; if (remoteApp.isValid()) reply = remoteApp.call("newTabs", tabs); if (!reply.isValid()) fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData()); } //! An object that manages archives in several parts of the source code. KRarcHandler arcHandler; int main(int argc, char *argv[]) { // set global log message format qSetMessagePattern(KrServices::GLOBAL_MESSAGE_PATTERN); // prevent qt5-webengine crashing QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // create the application and set application domain so that calls to i18n get strings from right place. QApplication app(argc, argv); KLocalizedString::setApplicationDomain("krusader"); // init icon theme qDebug() << "System icon theme:" << QIcon::themeName(); // [WORKAROUND] setThemeName sets user theme in QIconLoader and allows to avoid Qt issues with invalid icon caching later // IMPORTANT: this must be done before the first QIcon::fromTheme / QIcon::hasThemeIcon call QIcon::setThemeName(QIcon::themeName()); // ABOUT data information #ifdef RELEASE_NAME QString versionName = QString("%1 \"%2\"").arg(VERSION).arg(RELEASE_NAME); #else QString versionName = VERSION; #endif KAboutData aboutData(QStringLiteral("krusader"), (geteuid() ? i18n("Krusader") : i18n("Krusader - ROOT PRIVILEGES")), versionName, i18n(description), KAboutLicense::GPL_V2, i18n("© 2000-2003 Shie Erlich, Rafi Yanai\n© 2004-2018 Krusader Krew"), i18n("Feedback:\nhttps://forum.kde.org/viewforum.php?f=225\n\nIRC\nserver: " "irc.freenode.net, channel: #krusader"), QStringLiteral("https://krusader.org")); aboutData.setOrganizationDomain(QByteArray("kde.org")); aboutData.setDesktopFileName(QStringLiteral("org.kde.krusader")); aboutData.addAuthor(i18n("Davide Gianforte"), i18n("Developer"), QStringLiteral("davide@gengisdave.org"), nullptr); aboutData.addAuthor(i18n("Toni Asensi Esteve"), i18n("Developer"), QStringLiteral("toni.asensi@kdemail.net"), nullptr); aboutData.addAuthor(i18n("Alexander Bikadorov"), i18n("Developer"), QStringLiteral("alex.bikadorov@kdemail.net"), nullptr); aboutData.addAuthor(i18n("Martin Kostolný"), i18n("Developer"), QStringLiteral("clearmartin@gmail.com"), nullptr); aboutData.addAuthor(i18n("Nikita Melnichenko"), i18n("Developer"), QStringLiteral("nikita+kde@melnichenko.name"), nullptr); aboutData.addAuthor(i18n("Yuri Chornoivan"), i18n("Documentation"), QStringLiteral("yurchor@ukr.net"), nullptr); aboutData.addAuthor(i18n("Rafi Yanai"), i18n("Author (retired)"), QStringLiteral("yanai@users.sourceforge.net")); aboutData.addAuthor(i18n("Shie Erlich"), i18n("Author (retired)"), QStringLiteral("erlich@users.sourceforge.net")); aboutData.addAuthor(i18n("Csaba Karai"), i18n("Developer (retired)"), QStringLiteral("ckarai@users.sourceforge.net"), nullptr); aboutData.addAuthor(i18n("Heiner Eichmann"), i18n("Developer (retired)"), QStringLiteral("h.eichmann@gmx.de"), nullptr); aboutData.addAuthor(i18n("Jonas Bähr"), i18n("Developer (retired)"), QStringLiteral("jonas.baehr@web.de"), nullptr); aboutData.addAuthor(i18n("Václav Jůza"), i18n("Developer (retired)"), QStringLiteral("vaclavjuza@gmail.com"), nullptr); aboutData.addAuthor(i18n("Jan Lepper"), i18n("Developer (retired)"), QStringLiteral("jan_lepper@gmx.de"), nullptr); aboutData.addAuthor(i18n("Andrey Matveyakin"), i18n("Developer (retired)"), QStringLiteral("a.matveyakin@gmail.com"), nullptr); aboutData.addAuthor(i18n("Simon Persson"), i18n("Developer (retired)"), QStringLiteral("simon.persson@mykolab.com"), nullptr); aboutData.addAuthor(i18n("Dirk Eschler"), i18n("Webmaster (retired)"), QStringLiteral("deschler@users.sourceforge.net"), nullptr); aboutData.addAuthor(i18n("Frank Schoolmeesters"), i18n("Documentation and marketing coordinator (retired)"), QStringLiteral("frank_schoolmeesters@yahoo.com"), nullptr); aboutData.addAuthor(i18n("Richard Holt"), i18n("Documentation & Proofing (retired)"), QStringLiteral("richard.holt@gmail.com"), nullptr); aboutData.addAuthor(i18n("Matej Urbancic"), i18n("Marketing & Product Research (retired)"), QStringLiteral("matej.urban@gmail.com"), nullptr); aboutData.addCredit(i18n("kde.org"), i18n("Everyone involved in KDE"), nullptr, nullptr); aboutData.addCredit(i18n("l10n.kde.org"), i18n("KDE Translation Teams"), nullptr, nullptr); aboutData.addCredit(i18n("Jiří Paleček"), i18n("QA, bug-hunting, patches and general help"), QStringLiteral("jpalecek@web.de"), nullptr); aboutData.addCredit(i18n("Jiří Klement"), i18n("Important help in KDE 4 porting"), nullptr, nullptr); aboutData.addCredit(i18n("Andrew Neupokoev"), i18n("Killer Logo and Icons for Krusader (contest winner)"), QStringLiteral("doom-blue@yandex.ru"), nullptr); aboutData.addCredit(i18n("The UsefulArts Organization"), i18n("Icon for Krusader"), QStringLiteral("mail@usefularts.org"), nullptr); aboutData.addCredit(i18n("Gábor Lehel"), i18n("Viewer module for 3rd Hand"), QStringLiteral("illissius@gmail.com"), nullptr); aboutData.addCredit(i18n("Mark Eatough"), i18n("Handbook Proof-Reader"), QStringLiteral("markeatough@yahoo.com"), nullptr); aboutData.addCredit(i18n("Jan Halasa"), i18n("The old Bookmark Module"), QStringLiteral("xhalasa@fi.muni.cz"), nullptr); aboutData.addCredit(i18n("Hans Löffler"), i18n("Dir history button"), nullptr, nullptr); aboutData.addCredit(i18n("Szombathelyi György"), i18n("ISO KIO slave"), nullptr, nullptr); aboutData.addCredit(i18n("Jan Willem van de Meent (Adios)"), i18n("Icons for Krusader"), QStringLiteral("janwillem@lorentz.leidenuniv.nl"), nullptr); aboutData.addCredit(i18n("Mikolaj Machowski"), i18n("Usability and QA"), QStringLiteral(""), nullptr); aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("QA, bug-hunting, patches and general help"), QStringLiteral("cristid@chip.ro"), nullptr); aboutData.addCredit(i18n("Aurelien Gateau"), i18n("patch for KViewer"), QStringLiteral("aurelien.gateau@free.fr"), nullptr); aboutData.addCredit(i18n("Milan Brabec"), i18n("the first patch ever!"), QStringLiteral("mbrabec@volny.cz"), nullptr); aboutData.addCredit(i18n("Asim Husanovic"), i18n("Bosnian translation"), QStringLiteral("asim@megatel.ba"), nullptr); aboutData.addCredit(i18n("Doutor Zero"), i18n("Brazilian Portuguese translation"), QStringLiteral("doutor.zero@gmail.com"), nullptr); aboutData.addCredit(i18n("Milen Ivanov"), i18n("Bulgarian translation"), QStringLiteral("milen.ivanov@abv.bg"), nullptr); aboutData.addCredit(i18n("Quim Perez"), i18n("Catalan translation"), QStringLiteral("noguer@osona.com"), nullptr); aboutData.addCredit(i18n("Jinghua Luo"), i18n("Chinese Simplified translation"), QStringLiteral("luojinghua@msn.com"), nullptr); aboutData.addCredit(i18n("Mitek"), i18n("Old Czech translation"), QStringLiteral("mitek@email.cz"), nullptr); aboutData.addCredit(i18n("Martin Sixta"), i18n("Old Czech translation"), QStringLiteral("lukumo84@seznam.cz"), nullptr); aboutData.addCredit(i18n("Vaclav Jůza"), i18n("Czech translation"), QStringLiteral("VaclavJuza@gmail.com"), nullptr); aboutData.addCredit(i18n("Anders Bruun Olsen"), i18n("Old Danish translation"), QStringLiteral("anders@bruun-olsen.net"), nullptr); aboutData.addCredit(i18n("Peter H. Sorensen"), i18n("Danish translation"), QStringLiteral("peters@skydebanen.net"), nullptr); aboutData.addCredit(i18n("Frank Schoolmeesters"), i18n("Dutch translation"), QStringLiteral("frank_schoolmeesters@yahoo.com"), nullptr); aboutData.addCredit(i18n("Rene-Pierre Lehmann"), i18n("Old French translation"), QStringLiteral("ripi@lepi.org"), nullptr); aboutData.addCredit(i18n("David Guillerm"), i18n("French translation"), QStringLiteral("dguillerm@gmail.com"), nullptr); aboutData.addCredit(i18n("Christoph Thielecke"), i18n("Old German translation"), QStringLiteral("crissi99@gmx.de"), nullptr); aboutData.addCredit(i18n("Dirk Eschler"), i18n("German translation"), QStringLiteral("deschler@users.sourceforge.net"), nullptr); aboutData.addCredit(i18n("Spiros Georgaras"), i18n("Greek translation"), QStringLiteral("sngeorgaras@gmail.com"), nullptr); aboutData.addCredit(i18n("Kukk Zoltan"), i18n("Old Hungarian translation"), QStringLiteral("kukkzoli@freemail.hu"), nullptr); aboutData.addCredit(i18n("Arpad Biro"), i18n("Hungarian translation"), QStringLiteral("biro_arpad@yahoo.com"), nullptr); aboutData.addCredit(i18n("Giuseppe Bordoni"), i18n("Italian translation"), QStringLiteral("geppo@geppozone.com"), nullptr); aboutData.addCredit(i18n("Hideki Kimura"), i18n("Japanese translation"), QStringLiteral("hangyo1973@gmail.com"), nullptr); aboutData.addCredit(i18n("UTUMI Hirosi"), i18n("Old Japanese translation"), QStringLiteral("utuhiro@mx12.freecom.ne.jp"), nullptr); aboutData.addCredit(i18n("Dovydas Sankauskas"), i18n("Lithuanian translation"), QStringLiteral("laisve@gmail.com"), nullptr); aboutData.addCredit(i18n("Bruno Queiros"), i18n("Portuguese translation"), QStringLiteral("brunoqueiros@portugalmail.com"), nullptr); aboutData.addCredit(i18n("Lukasz Janyst"), i18n("Old Polish translation"), QStringLiteral("ljan@wp.pl"), nullptr); aboutData.addCredit(i18n("Pawel Salawa"), i18n("Polish translation"), QStringLiteral("boogie@myslenice.one.pl"), nullptr); aboutData.addCredit(i18n("Tomek Grzejszczyk"), i18n("Polish translation"), QStringLiteral("tgrzej@onet.eu"), nullptr); aboutData.addCredit(i18n("Dmitry A. Bugay"), i18n("Russian translation"), QStringLiteral("sam@vhnet.ru"), nullptr); aboutData.addCredit(i18n("Dmitry Chernyak"), i18n("Old Russian translation"), QStringLiteral("chernyak@mail.ru"), nullptr); aboutData.addCredit(i18n("Sasa Tomic"), i18n("Serbian translation"), QStringLiteral("stomic@gmx.net"), nullptr); aboutData.addCredit(i18n("Zdenko Podobný and Ondrej Pačay (Yogi)"), i18n("Slovak translation"), QStringLiteral("zdenop@gmail.com"), nullptr); aboutData.addCredit(i18n("Matej Urbancic"), i18n("Slovenian translation"), QStringLiteral("matej.urban@gmail.com"), nullptr); aboutData.addCredit(i18n("Rafael Munoz"), i18n("Old Spanish translation"), QStringLiteral("muror@hotpop.com"), nullptr); aboutData.addCredit(i18n("Alejandro Araiza Alvarado"), i18n("Spanish translation"), QStringLiteral("mebrelith@gmail.com"), nullptr); aboutData.addCredit(i18n("Erik Johanssen"), i18n("Old Swedish translation"), QStringLiteral("erre@telia.com"), nullptr); aboutData.addCredit(i18n("Anders Linden"), i18n("Old Swedish translation"), QStringLiteral("connyosis@gmx.net"), nullptr); aboutData.addCredit(i18n("Peter Landgren"), i18n("Swedish translation"), QStringLiteral("peter.talken@telia.com"), nullptr); aboutData.addCredit(i18n("Bekir Sonat"), i18n("Turkish translation"), QStringLiteral("bekirsonat@kde.org.tr"), nullptr); aboutData.addCredit(i18n("Ivan Petrouchtchak"), i18n("Ukrainian translation"), QStringLiteral("connyosis@gmx.net"), nullptr); aboutData.addCredit(i18n("Seongnam Jee"), i18n("Korean translation"), QStringLiteral("snjee@intellicam.com"), nullptr); // This will call QCoreApplication::setApplicationName, etc for us by using info in the KAboutData instance. // The only thing not called for us is setWindowIcon(), which is why we do it ourselves here. KAboutData::setApplicationData(aboutData); app.setWindowIcon(Icon(Krusader::appIconName())); // Command line arguments ... QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("left"), i18n("Start left panel at "), QLatin1String("path"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("right"), i18n("Start right panel at "), QLatin1String("path"))); parser.addOption(QCommandLineOption(QStringList() << QLatin1String("profile"), i18n("Load this profile on startup"), QLatin1String("panel-profile"))); parser.addOption(QCommandLineOption(QStringList() << "d" << QLatin1String("debug"), i18n("Enable debug output"))); parser.addPositionalArgument(QLatin1String("url"), i18n("URL to open")); // check for command line arguments parser.process(app); aboutData.processCommandLine(&parser); // set global message handler KrServices::setGlobalKrMessageHandler(parser.isSet("debug")); KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Look&Feel")); bool singleInstanceMode = cfg.readEntry("Single Instance Mode", _SingleInstanceMode); QString url; if(!parser.positionalArguments().isEmpty()) { url = parser.positionalArguments().first(); } QString appName = "krusader"; if (!singleInstanceMode) appName += QString("%1").arg(getpid()); if (!QDBusConnection::sessionBus().isConnected()) { fprintf(stderr, "Cannot connect to the D-BUS session bus.\n" "To start it, run:\n" "\teval `dbus-launch --auto-syntax`\n"); } if (singleInstanceMode) { QDBusInterface remoteApp("org.krusader", "/Instances/" + appName, "org.krusader.Instance", QDBusConnection::sessionBus()); QDBusReply reply; if (remoteApp.isValid()) reply = remoteApp.call("isRunning"); if (!reply.isValid() && reply.error().type() != QDBusError::ServiceUnknown && reply.error().type() != QDBusError::UnknownObject) fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData()); if (reply.isValid() && (bool)reply) { KStartupInfo::appStarted(); if (parser.isSet("left")) openTabsRemote(parser.value("left").split(','), true, appName); if (parser.isSet("right")) openTabsRemote(parser.value("right").split(','), false, appName); if(!url.isEmpty()) { reply = remoteApp.call("openUrl", url); if (!reply.isValid()) fprintf(stderr, "DBus Error: %s, %s\n", reply.error().name().toLocal8Bit().constData(), reply.error().message().toLocal8Bit().constData()); } return 0; } } // splash screen - if the user wants one QSplashScreen *splash = nullptr; { // don't remove bracket KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("Look&Feel")); if (cfg.readEntry("Show splashscreen", _ShowSplashScreen)) { QString splashFilename = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("splash.png")); QPixmap pixmap(splashFilename); if (!pixmap.isNull()) { splash = new QSplashScreen(pixmap); splash->show(); } } } // don't remove bracket Krusader::AppName = appName; auto *krusader = new Krusader(parser); if(!url.isEmpty()) krusader->openUrl(url); QDBusConnection dbus = QDBusConnection::sessionBus(); if (!dbus.interface()->isServiceRegistered("org.krusader") && !dbus.registerService("org.krusader")) { fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData()); } if (!dbus.registerObject("/Instances/" + appName, krusader, QDBusConnection::ExportScriptableSlots)) { fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData()); } if (!dbus.registerObject("/Instances/" + appName + "/left_manager", LEFT_MNG, QDBusConnection::ExportScriptableSlots)) { fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData()); } if (!dbus.registerObject("/Instances/" + appName + "/right_manager", RIGHT_MNG, QDBusConnection::ExportScriptableSlots)) { fprintf(stderr, "DBus Error: %s, %s\n", dbus.lastError().name().toLocal8Bit().constData(), dbus.lastError().message().toLocal8Bit().constData()); } // catching SIGTERM, SIGHUP, SIGQUIT signal(SIGTERM, sigterm_handler); signal(SIGPIPE, sigterm_handler); signal(SIGHUP, sigterm_handler); QObject::connect(&app, &QGuiApplication::applicationStateChanged, SLOTS, &KRslots::applicationStateChanged); // hide splashscreen if (splash) { splash->finish(krusader); delete splash; } // let's go. return app.exec(); } diff --git a/krusader/panelmanager.cpp b/krusader/panelmanager.cpp index 909bc47e..73454244 100644 --- a/krusader/panelmanager.cpp +++ b/krusader/panelmanager.cpp @@ -1,462 +1,462 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 "panelmanager.h" #include "defaults.h" #include "icon.h" #include "tabactions.h" #include "krusaderview.h" #include "krmainwindow.h" #include "Panel/listpanel.h" #include "Panel/panelfunc.h" #include "Panel/PanelView/krviewfactory.h" #include // QtGui #include // QtWidgets #include #include #include #include #include PanelManager::PanelManager(QWidget *parent, KrMainWindow* mainWindow, bool left) : QWidget(parent), _otherManager(nullptr), _actions(mainWindow->tabActions()), _layout(nullptr), _left(left), _currentPanel(nullptr) { _layout = new QGridLayout(this); _layout->setContentsMargins(0, 0, 0, 0); _layout->setSpacing(0); _stack = new QStackedWidget(this); // new tab button _newTab = new QToolButton(this); _newTab->setAutoRaise(true); _newTab->setText(i18n("Open a new tab in home")); _newTab->setToolTip(i18n("Open a new tab in home")); _newTab->setIcon(Icon("tab-new")); _newTab->adjustSize(); connect(_newTab, &QToolButton::clicked, this, QOverload<>::of(&PanelManager::slotNewTab)); // tab-bar _tabbar = new PanelTabBar(this, _actions); connect(_tabbar, &PanelTabBar::currentChanged, this, &PanelManager::slotCurrentTabChanged); connect(_tabbar, &PanelTabBar::tabCloseRequested, this, QOverload::of(&PanelManager::slotCloseTab)); connect(_tabbar, &PanelTabBar::closeCurrentTab, this, QOverload<>::of(&PanelManager::slotCloseTab)); connect(_tabbar, &PanelTabBar::newTab, this, [=] (const QUrl &url) { slotNewTab(url); }); connect(_tabbar, &PanelTabBar::draggingTab, this, &PanelManager::slotDraggingTab); connect(_tabbar, &PanelTabBar::draggingTabFinished, this, &PanelManager::slotDraggingTabFinished); auto *tabbarLayout = new QHBoxLayout; tabbarLayout->setSpacing(0); tabbarLayout->setContentsMargins(0, 0, 0, 0); tabbarLayout->addWidget(_tabbar); tabbarLayout->addWidget(_newTab); _layout->addWidget(_stack, 0, 0); _layout->addLayout(tabbarLayout, 1, 0); updateTabbarPos(); setLayout(_layout); addPanel(true); tabsCountChanged(); } void PanelManager::tabsCountChanged() { const KConfigGroup cfg(krConfig, "Look&Feel"); const bool showTabbar = _tabbar->count() > 1 || cfg.readEntry("Show Tab Bar On Single Tab", true); const bool showButtons = showTabbar && cfg.readEntry("Show Tab Buttons", true); _tabbar->setVisible(showTabbar); _newTab->setVisible(showButtons); // disable close button if only 1 tab is left _tabbar->setTabsClosable(showButtons && _tabbar->count() > 1); _actions->refreshActions(); } void PanelManager::activate() { assert(sender() == (currentPanel()->gui)); emit setActiveManager(this); _actions->refreshActions(); } void PanelManager::slotCurrentTabChanged(int index) { ListPanel *panel = _tabbar->getPanel(index); if (!panel || panel == _currentPanel) return; ListPanel *previousPanel = _currentPanel; _currentPanel = panel; _stack->setCurrentWidget(_currentPanel); if (previousPanel) { previousPanel->slotFocusOnMe(false); // FIXME - necessary ? } _currentPanel->slotFocusOnMe(this == ACTIVE_MNG); emit pathChanged(panel); if (otherManager()) { otherManager()->currentPanel()->otherPanelChanged(); } // go back to pinned url if tab is pinned and switched back to active if (panel->isPinned()) { panel->func->openUrl(panel->pinnedUrl()); } } -ListPanel* PanelManager::createPanel(KConfigGroup cfg) +ListPanel* PanelManager::createPanel(const KConfigGroup& cfg) { ListPanel * p = new ListPanel(_stack, this, cfg); connectPanel(p); return p; } void PanelManager::connectPanel(ListPanel *p) { connect(p, &ListPanel::activate, this, &PanelManager::activate); connect(p, &ListPanel::pathChanged, this, [=]() { pathChanged(p); }); connect(p, &ListPanel::pathChanged, this, [=]() { _tabbar->updateTab(p); }); } void PanelManager::disconnectPanel(ListPanel *p) { disconnect(p, &ListPanel::activate, this, nullptr); disconnect(p, &ListPanel::pathChanged, this, nullptr); } -ListPanel* PanelManager::addPanel(bool setCurrent, KConfigGroup cfg, KrPanel *nextTo) +ListPanel* PanelManager::addPanel(bool setCurrent, const KConfigGroup& cfg, KrPanel *nextTo) { // create the panel and add it into the widgetstack ListPanel * p = createPanel(cfg); _stack->addWidget(p); // now, create the corresponding tab int index = _tabbar->addPanel(p, setCurrent, nextTo); tabsCountChanged(); if (setCurrent) slotCurrentTabChanged(index); return p; } void PanelManager::saveSettings(KConfigGroup config, bool saveHistory) { config.writeEntry("ActiveTab", activeTab()); KConfigGroup grpTabs(&config, "Tabs"); foreach(const QString &grpTab, grpTabs.groupList()) grpTabs.deleteGroup(grpTab); for(int i = 0; i < _tabbar->count(); i++) { ListPanel *panel = _tabbar->getPanel(i); KConfigGroup grpTab(&grpTabs, "Tab" + QString::number(i)); panel->saveSettings(grpTab, saveHistory); } } void PanelManager::loadSettings(KConfigGroup config) { KConfigGroup grpTabs(&config, "Tabs"); int numTabsOld = _tabbar->count(); int numTabsNew = grpTabs.groupList().count(); for(int i = 0; i < numTabsNew; i++) { KConfigGroup grpTab(&grpTabs, "Tab" + QString::number(i)); // TODO workaround for bug 371453. Remove this when bug is fixed if (grpTab.keyList().isEmpty()) continue; ListPanel *panel = i < numTabsOld ? _tabbar->getPanel(i) : addPanel(false, grpTab); panel->restoreSettings(grpTab); _tabbar->updateTab(panel); } for(int i = numTabsOld - 1; i >= numTabsNew && i > 0; i--) slotCloseTab(i); setActiveTab(config.readEntry("ActiveTab", 0)); // this is needed so that all tab labels get updated layoutTabs(); } void PanelManager::layoutTabs() { // delayed url refreshes may be pending - // delay the layout too so it happens after them QTimer::singleShot(0, _tabbar, &PanelTabBar::layoutTabs); } KrPanel *PanelManager::currentPanel() const { return _currentPanel; } void PanelManager::moveTabToOtherSide() { if (tabCount() < 2) return; ListPanel *p; _tabbar->removeCurrentPanel(p); _stack->removeWidget(p); disconnectPanel(p); p->reparent(_otherManager->_stack, _otherManager); _otherManager->connectPanel(p); _otherManager->_stack->addWidget(p); _otherManager->_tabbar->addPanel(p, true); _otherManager->tabsCountChanged(); tabsCountChanged(); p->slotFocusOnMe(); } void PanelManager::slotNewTab(const QUrl &url, bool setCurrent, KrPanel *nextTo) { ListPanel *p = addPanel(setCurrent, KConfigGroup(), nextTo); if(nextTo && nextTo->gui) { // We duplicate tab settings by writing original settings to a temporary // group and making the new tab read settings from it. Duplicating // settings directly would add too much complexity. QString grpName = "PanelManager_" + QString::number(qApp->applicationPid()); krConfig->deleteGroup(grpName); // make sure the group is empty KConfigGroup cfg(krConfig, grpName); nextTo->gui->saveSettings(cfg, true); // reset undesired duplicated settings cfg.writeEntry("Properties", 0); p->restoreSettings(cfg); krConfig->deleteGroup(grpName); } p->start(url); } void PanelManager::slotNewTab() { slotNewTab(QUrl::fromLocalFile(QDir::home().absolutePath())); _currentPanel->slotFocusOnMe(); } void PanelManager::slotCloseTab() { slotCloseTab(_tabbar->currentIndex()); } void PanelManager::slotCloseTab(int index) { if (_tabbar->count() <= 1) /* if this is the last tab don't close it */ return; ListPanel *oldp; _tabbar->removePanel(index, oldp); //this automatically changes the current panel _stack->removeWidget(oldp); deletePanel(oldp); tabsCountChanged(); } void PanelManager::updateTabbarPos() { KConfigGroup group(krConfig, "Look&Feel"); if(group.readEntry("Tab Bar Position", "bottom") == "top") { _layout->addWidget(_stack, 2, 0); _tabbar->setShape(QTabBar::RoundedNorth); } else { _layout->addWidget(_stack, 0, 0); _tabbar->setShape(QTabBar::RoundedSouth); } } int PanelManager::activeTab() { return _tabbar->currentIndex(); } void PanelManager::setActiveTab(int index) { _tabbar->setCurrentIndex(index); } void PanelManager::slotRecreatePanels() { updateTabbarPos(); for (int i = 0; i != _tabbar->count(); i++) { QString grpName = "PanelManager_" + QString::number(qApp->applicationPid()); krConfig->deleteGroup(grpName); // make sure the group is empty KConfigGroup cfg(krConfig, grpName); ListPanel *oldPanel = _tabbar->getPanel(i); oldPanel->saveSettings(cfg, true); disconnect(oldPanel); ListPanel *newPanel = createPanel(cfg); _stack->insertWidget(i, newPanel); _tabbar->changePanel(i, newPanel); if (_currentPanel == oldPanel) { _currentPanel = newPanel; _stack->setCurrentWidget(_currentPanel); } _stack->removeWidget(oldPanel); deletePanel(oldPanel); newPanel->restoreSettings(cfg); _tabbar->updateTab(newPanel); krConfig->deleteGroup(grpName); } tabsCountChanged(); _currentPanel->slotFocusOnMe(this == ACTIVE_MNG); emit pathChanged(_currentPanel); } void PanelManager::slotNextTab() { int currTab = _tabbar->currentIndex(); int nextInd = (currTab == _tabbar->count() - 1 ? 0 : currTab + 1); _tabbar->setCurrentIndex(nextInd); } void PanelManager::slotPreviousTab() { int currTab = _tabbar->currentIndex(); int nextInd = (currTab == 0 ? _tabbar->count() - 1 : currTab - 1); _tabbar->setCurrentIndex(nextInd); } void PanelManager::reloadConfig() { for (int i = 0; i < _tabbar->count(); i++) { ListPanel *panel = _tabbar->getPanel(i); if (panel) { panel->func->refresh(); } } } void PanelManager::deletePanel(ListPanel * p) { disconnect(p); delete p; } void PanelManager::slotCloseInactiveTabs() { int i = 0; while (i < _tabbar->count()) { if (i == activeTab()) i++; else slotCloseTab(i); } } void PanelManager::slotCloseDuplicatedTabs() { int i = 0; while (i < _tabbar->count() - 1) { ListPanel * panel1 = _tabbar->getPanel(i); if (panel1 != nullptr) { for (int j = i + 1; j < _tabbar->count(); j++) { ListPanel * panel2 = _tabbar->getPanel(j); if (panel2 != nullptr && panel1->virtualPath().matches(panel2->virtualPath(), QUrl::StripTrailingSlash)) { if (j == activeTab()) { slotCloseTab(i); i--; break; } else { slotCloseTab(j); j--; } } } } i++; } } int PanelManager::findTab(QUrl url) { url.setPath(QDir::cleanPath(url.path())); for(int i = 0; i < _tabbar->count(); i++) { if(_tabbar->getPanel(i)) { QUrl panelUrl = _tabbar->getPanel(i)->virtualPath(); panelUrl.setPath(QDir::cleanPath(panelUrl.path())); if(panelUrl.matches(url, QUrl::StripTrailingSlash)) return i; } } return -1; } void PanelManager::slotLockTab() { ListPanel *panel = _currentPanel; panel->gui->setTabState(panel->gui->isLocked() ? ListPanel::TabState::DEFAULT : ListPanel::TabState::LOCKED); _actions->refreshActions(); _tabbar->updateTab(panel); } void PanelManager::slotPinTab() { ListPanel *panel = _currentPanel; panel->gui->setTabState(panel->gui->isPinned() ? ListPanel::TabState::DEFAULT : ListPanel::TabState::PINNED); if (panel->gui->isPinned()) { QUrl virtualPath = panel->virtualPath(); panel->setPinnedUrl(virtualPath); } _actions->refreshActions(); _tabbar->updateTab(panel); } void PanelManager::newTabs(const QStringList& urls) { for(int i = 0; i < urls.count(); i++) slotNewTab(QUrl::fromUserInput(urls[i], QString(), QUrl::AssumeLocalFile)); } diff --git a/krusader/panelmanager.h b/krusader/panelmanager.h index 1d156a9e..9793aa92 100644 --- a/krusader/panelmanager.h +++ b/krusader/panelmanager.h @@ -1,147 +1,147 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2004-2018 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 PANELMANAGER_H #define PANELMANAGER_H #include "abstractpanelmanager.h" // QtWidgets #include #include #include #include #include #include "paneltabbar.h" class QStackedWidget; class QToolButton; class ListPanel; class KrMainWindow; class TabActions; /** * Implements tabbed-browsing by managing a list of tabs and corresponding panels. */ class PanelManager: public QWidget, public AbstractPanelManager { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.krusader.PanelManager") public: /** * PanelManager is created where once panels were created. It accepts three references to pointers * (self, other, active), which enables it to manage pointers held by the panels transparently. * It also receives a bool (left) which is true if the manager is the left one, or false otherwise. */ PanelManager(QWidget *parent, KrMainWindow* mainWindow, bool left); void saveSettings(KConfigGroup config, bool saveHistory); void loadSettings(KConfigGroup config); int findTab(QUrl url); int tabCount() { return _tabbar->count(); } int activeTab(); void setActiveTab(int index); void moveTabToOtherSide(); /** Refresh all tabs after config changes. */ void reloadConfig(); void layoutTabs(); void setLeft(bool left) { _left = left; } void setOtherManager(PanelManager *other) { _otherManager = other; } // AbstractPanelManager implementation bool isLeft() const Q_DECL_OVERRIDE { return _left; } AbstractPanelManager *otherManager() const Q_DECL_OVERRIDE { return _otherManager; } KrPanel *currentPanel() const Q_DECL_OVERRIDE; void newTab(const QUrl &url, KrPanel *nextTo) Q_DECL_OVERRIDE { slotNewTab(url, true, nextTo); } signals: void draggingTab(PanelManager *from, QMouseEvent*); void draggingTabFinished(PanelManager *from, QMouseEvent*); void setActiveManager(PanelManager *manager); void pathChanged(ListPanel *panel); public slots: /** * Called externally to start a new tab. Example of usage would be the "open in a new tab" * action, from the context-menu. */ Q_SCRIPTABLE void newTab(const QString& url) { slotNewTab(QUrl::fromUserInput(url, QString(), QUrl::AssumeLocalFile)); } Q_SCRIPTABLE void newTabs(const QStringList& urls); void slotNewTab(const QUrl &url, bool setCurrent = true, KrPanel *nextTo = nullptr); void slotNewTab(); void slotLockTab(); void slotPinTab(); void slotNextTab(); void slotPreviousTab(); void slotCloseTab(); void slotCloseTab(int index); void slotRecreatePanels(); void slotCloseInactiveTabs(); void slotCloseDuplicatedTabs(); protected slots: void slotCurrentTabChanged(int index); void activate(); void slotDraggingTab(QMouseEvent *e) { emit draggingTab(this, e); } void slotDraggingTabFinished(QMouseEvent* e) { emit draggingTabFinished(this, e); } private: void deletePanel(ListPanel *p); void updateTabbarPos(); void tabsCountChanged(); - ListPanel* addPanel(bool setCurrent = true, KConfigGroup cfg = KConfigGroup(), KrPanel *nextTo = nullptr); - ListPanel* createPanel(KConfigGroup cfg); + ListPanel* addPanel(bool setCurrent = true, const KConfigGroup& cfg = KConfigGroup(), KrPanel *nextTo = nullptr); + ListPanel* createPanel(const KConfigGroup& cfg); void connectPanel(ListPanel *p); void disconnectPanel(ListPanel *p); PanelManager *_otherManager; TabActions *_actions; QGridLayout *_layout; QHBoxLayout *_barLayout; bool _left; PanelTabBar *_tabbar; QStackedWidget *_stack; QToolButton *_newTab; ListPanel *_currentPanel; }; #endif // _PANEL_MANAGER_H