diff --git a/iso/iso.cpp b/iso/iso.cpp index ca679de5..d0dbc2b0 100644 --- a/iso/iso.cpp +++ b/iso/iso.cpp @@ -1,517 +1,519 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * - * Copyright (C) 2003 Leo Savernik * * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2003 Leo Savernik * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * This file is heavily based on ktar from kdelibs * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "iso.h" #include // QtCore #include #include #include #include #include #include #include #include "libisofs/iso_fs.h" #include "kiso.h" #include "kisofile.h" #include "kisodirectory.h" using namespace KIO; extern "C" { int Q_DECL_EXPORT kdemain(int argc, char **argv) { //qDebug() << "Starting " << getpid() << endl; if (argc != 4) { fprintf(stderr, "Usage: kio_iso protocol domain-socket1 domain-socket2\n"); exit(-1); } kio_isoProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); //qDebug() << "Done" << endl; return 0; } } // extern "C" typedef struct { char magic[8]; char uncompressed_len[4]; unsigned char header_size; unsigned char block_size; char reserved[2]; /* Reserved for future use, MBZ */ } compressed_file_header; static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; kio_isoProtocol::kio_isoProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("iso", pool, app) { //qDebug() << "kio_isoProtocol::kio_isoProtocol" << endl; m_isoFile = 0L; } kio_isoProtocol::~kio_isoProtocol() { delete m_isoFile; } bool kio_isoProtocol::checkNewFile(QString fullPath, QString & path, int startsec) { //qDebug() << "kio_isoProtocol::checkNewFile " << fullPath << " startsec: " << //startsec << endl; // Are we already looking at that file ? if (m_isoFile && startsec == m_isoFile->startSec() && m_isoFile->fileName() == fullPath.left(m_isoFile->fileName().length())) { // Has it changed ? QT_STATBUF statbuf; if (QT_STAT(QFile::encodeName(m_isoFile->fileName()), &statbuf) == 0) { if (m_mtime == statbuf.st_mtime) { path = fullPath.mid(m_isoFile->fileName().length()); //qDebug() << "kio_isoProtocol::checkNewFile returning " << path << endl; if(path.endsWith(DIR_SEPARATOR_CHAR)) { path.chop(1); } if(path.isEmpty()) { path = DIR_SEPARATOR_CHAR; } return true; } } } //qDebug() << "Need to open a new file" << endl; // Close previous file if (m_isoFile) { m_isoFile->close(); delete m_isoFile; m_isoFile = 0L; } // Find where the iso file is in the full path int pos = 0; QString isoFile; path.clear(); int len = fullPath.length(); if (len != 0 && fullPath[ len - 1 ] != DIR_SEPARATOR_CHAR) fullPath += DIR_SEPARATOR_CHAR; //qDebug() << "the full path is " << fullPath << endl; while ((pos = fullPath.indexOf(DIR_SEPARATOR_CHAR, pos + 1)) != -1) { QString tryPath = fullPath.left(pos); //qDebug() << fullPath << " trying " << tryPath << endl; QT_STATBUF statbuf; if (QT_LSTAT(QFile::encodeName(tryPath), &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) { bool isFile = true; if (S_ISLNK(statbuf.st_mode)) { char symDest[256]; memset(symDest, 0, 256); int endOfName = readlink(QFile::encodeName(tryPath), symDest, 256); if (endOfName != -1) { if (QDir(QString::fromLocal8Bit(symDest)).exists()) isFile = false; } } if (isFile) { isoFile = tryPath; m_mtime = statbuf.st_mtime; m_mode = statbuf.st_mode; path = fullPath.mid(pos + 1); //qDebug() << "fullPath=" << fullPath << " path=" << path << endl; if(path.endsWith(DIR_SEPARATOR_CHAR)) { path.chop(1); } if(path.isEmpty()) { path = DIR_SEPARATOR_CHAR; } //qDebug() << "Found. isoFile=" << isoFile << " path=" << path << endl; break; } } } if (isoFile.isEmpty()) { //qDebug() << "kio_isoProtocol::checkNewFile: not found" << endl; return false; } // Open new file //qDebug() << "Opening KIso on " << isoFile << endl; m_isoFile = new KIso(isoFile); m_isoFile->setStartSec(startsec); if (!m_isoFile->open(QIODevice::ReadOnly)) { //qDebug() << "Opening " << isoFile << " failed." << endl; delete m_isoFile; m_isoFile = 0L; return false; } return true; } void kio_isoProtocol::createUDSEntry(const KArchiveEntry * isoEntry, UDSEntry & entry) { entry.clear(); entry.insert(UDSEntry::UDS_NAME, isoEntry->name()); entry.insert(UDSEntry::UDS_FILE_TYPE, isoEntry->permissions() & S_IFMT); // keep file type only entry.insert(UDSEntry::UDS_ACCESS, isoEntry->permissions() & 07777); // keep permissions only if (isoEntry->isFile()) { long long si = ((KIsoFile *)isoEntry)->realsize(); if (!si) si = ((KIsoFile *)isoEntry)->size(); entry.insert(UDSEntry::UDS_SIZE, si); } else { entry.insert(UDSEntry::UDS_SIZE, 0L); } entry.insert(UDSEntry::UDS_USER, isoEntry->user()); entry.insert(UDSEntry::UDS_GROUP, isoEntry->group()); entry.insert((uint)UDSEntry::UDS_MODIFICATION_TIME, isoEntry->date().toTime_t()); entry.insert(UDSEntry::UDS_ACCESS_TIME, isoEntry->isFile() ? ((KIsoFile *)isoEntry)->adate() : ((KIsoDirectory *)isoEntry)->adate()); entry.insert(UDSEntry::UDS_CREATION_TIME, isoEntry->isFile() ? ((KIsoFile *)isoEntry)->cdate() : ((KIsoDirectory *)isoEntry)->cdate()); entry.insert(UDSEntry::UDS_LINK_DEST, isoEntry->symLinkTarget()); } void kio_isoProtocol::listDir(const QUrl &url) { //qDebug() << "kio_isoProtocol::listDir " << url.url() << endl; QString path; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { QByteArray _path(QFile::encodeName(getPath(url))); //qDebug() << "Checking (stat) on " << _path << endl; QT_STATBUF buff; if (QT_STAT(_path.data(), &buff) == -1 || !S_ISDIR(buff.st_mode)) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } // It's a real dir -> redirect QUrl redir; redir.setPath(getPath(url)); if (url.hasFragment()) redir.setFragment(url.fragment(QUrl::FullyDecoded)); //qDebug() << "Ok, redirection to " << redir.url() << endl; redir.setScheme("file"); redirection(redir); finished(); // And let go of the iso file - for people who want to unmount a cdrom after that delete m_isoFile; m_isoFile = 0L; return; } if (path.isEmpty()) { QUrl redir(QStringLiteral("iso:/")); //qDebug() << "url.path()==" << getPath(url) << endl; if (url.hasFragment()) redir.setFragment(url.fragment(QUrl::FullyDecoded)); redir.setPath(getPath(url) + QString::fromLatin1(DIR_SEPARATOR)); //qDebug() << "kio_isoProtocol::listDir: redirection " << redir.url() << endl; redir.setScheme("file"); redirection(redir); finished(); return; } //qDebug() << "checkNewFile done" << endl; const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveDirectory* dir; if (!path.isEmpty() && path != DIR_SEPARATOR) { //qDebug() << QString("Looking for entry %1").arg(path) << endl; const KArchiveEntry* e = root->entry(path); if (!e) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } if (! e->isDirectory()) { error(KIO::ERR_IS_FILE, path); return; } dir = (KArchiveDirectory*)e; } else { dir = root; } QStringList l = dir->entries(); totalSize(l.count()); UDSEntry entry; QStringList::Iterator it = l.begin(); for (; it != l.end(); ++it) { //qDebug() << (*it) << endl; const KArchiveEntry* isoEntry = dir->entry((*it)); createUDSEntry(isoEntry, entry); listEntry(entry); } finished(); //qDebug() << "kio_isoProtocol::listDir done" << endl; } void kio_isoProtocol::stat(const QUrl &url) { QString path; UDSEntry entry; //qDebug() << "kio_isoProtocol::stat " << url.url() << endl; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { // We may be looking at a real directory - this happens // when pressing up after being in the root of an archive QByteArray _path(QFile::encodeName(getPath(url))); //qDebug() << "kio_isoProtocol::stat (stat) on " << _path << endl; QT_STATBUF buff; if (QT_STAT(_path.data(), &buff) == -1 || !S_ISDIR(buff.st_mode)) { //qDebug() << "isdir=" << S_ISDIR(buff.st_mode) << " errno=" << strerror(errno) << endl; error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } // Real directory. Return just enough information for KRun to work entry.insert(UDSEntry::UDS_NAME, url.fileName()); //qDebug() << "kio_isoProtocol::stat returning name=" << url.fileName() << endl; entry.insert(UDSEntry::UDS_FILE_TYPE, buff.st_mode & S_IFMT); statEntry(entry); finished(); // And let go of the iso file - for people who want to unmount a cdrom after that delete m_isoFile; m_isoFile = 0L; return; } const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveEntry* isoEntry; if (path.isEmpty()) { path = QString::fromLatin1(DIR_SEPARATOR); isoEntry = root; } else { isoEntry = root->entry(path); } if (!isoEntry) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } createUDSEntry(isoEntry, entry); statEntry(entry); finished(); } void kio_isoProtocol::getFile(const KIsoFile *isoFileEntry, const QString &path) { unsigned long long size, pos = 0; bool mime = false, zlib = false; QByteArray fileData, pointer_block, inbuf, outbuf; char *pptr = 0; compressed_file_header *hdr; int block_shift; unsigned long nblocks; unsigned long fullsize = 0, block_size = 0, block_size2 = 0; size_t ptrblock_bytes; unsigned long cstart, cend, csize; uLong bytes; size = isoFileEntry->realsize(); if (size >= sizeof(compressed_file_header)) zlib = true; if (!size) size = isoFileEntry->size(); totalSize(size); if (!size) mimeType("application/x-zerosize"); if (size && !m_isoFile->device()->isOpen()) m_isoFile->device()->open(QIODevice::ReadOnly); if (zlib) { fileData = isoFileEntry->dataAt(0, sizeof(compressed_file_header)); if (fileData.size() == sizeof(compressed_file_header) && !memcmp(fileData.data(), zisofs_magic, sizeof(zisofs_magic))) { hdr = (compressed_file_header*) fileData.data(); block_shift = hdr->block_size; block_size = 1UL << block_shift; block_size2 = block_size << 1; fullsize = isonum_731(hdr->uncompressed_len); nblocks = (fullsize + block_size - 1) >> block_shift; ptrblock_bytes = (nblocks + 1) * 4; pointer_block = isoFileEntry->dataAt(hdr->header_size << 2, ptrblock_bytes); if ((unsigned long)pointer_block.size() == ptrblock_bytes) { inbuf.resize(block_size2); if (inbuf.size()) { outbuf.resize(block_size); if (outbuf.size()) pptr = pointer_block.data(); else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { zlib = false; } } while (pos < size) { if (zlib) { cstart = isonum_731(pptr); pptr += 4; cend = isonum_731(pptr); csize = cend - cstart; if (csize == 0) { outbuf.fill(0, -1); } else { if (csize > block_size2) { //err = EX_DATAERR; break; } inbuf = isoFileEntry->dataAt(cstart, csize); if ((unsigned long)inbuf.size() != csize) { break; } bytes = block_size; // Max output buffer size if ((uncompress((Bytef*) outbuf.data(), &bytes, (Bytef*) inbuf.data(), csize)) != Z_OK) { break; } } if (((fullsize > block_size) && (bytes != block_size)) || ((fullsize <= block_size) && (bytes < fullsize))) { break; } if (bytes > fullsize) bytes = fullsize; fileData = outbuf; fileData.resize(bytes); fullsize -= bytes; } else { fileData = isoFileEntry->dataAt(pos, 65536); if (fileData.size() == 0) break; } if (!mime) { QMimeDatabase db; QMimeType mt = db.mimeTypeForFileNameAndData(path, fileData); if (mt.isValid()) { //qDebug() << "Emitting mimetype " << mt.name() << endl; mimeType(mt.name()); mime = true; } } data(fileData); pos += fileData.size(); processedSize(pos); } if (pos != size) { error(KIO::ERR_COULD_NOT_READ, path); return; } fileData.resize(0); data(fileData); processedSize(pos); finished(); } void kio_isoProtocol::get(const QUrl &url) { //qDebug() << "kio_isoProtocol::get" << url.url() << endl; QString path; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveEntry* isoEntry = root->entry(path); if (!isoEntry) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } if (isoEntry->isDirectory()) { error(KIO::ERR_IS_DIRECTORY, path); return; } const KIsoFile* isoFileEntry = static_cast(isoEntry); if (!isoEntry->symLinkTarget().isEmpty()) { //qDebug() << "Redirection to " << isoEntry->symLinkTarget() << endl; QUrl realURL = QUrl(url).resolved(QUrl(isoEntry->symLinkTarget())); //qDebug() << "realURL= " << realURL.url() << endl; realURL.setScheme("file"); redirection(realURL); finished(); return; } getFile(isoFileEntry, path); if (m_isoFile->device()->isOpen()) m_isoFile->device()->close(); } QString kio_isoProtocol::getPath(const QUrl &url) { QString path = url.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; } diff --git a/iso/iso.h b/iso/iso.h index 3433f106..dc8e979b 100644 --- a/iso/iso.h +++ b/iso/iso.h @@ -1,56 +1,58 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * - * Copyright (C) 2003 Leo Savernik * * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2003 Leo Savernik * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * This file is heavily based on ktar from kdelibs * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ISO_H #define ISO_H // QtCore #include #include #include #include "kisofile.h" class KIso; class kio_isoProtocol : public KIO::SlaveBase { public: kio_isoProtocol(const QByteArray &pool, const QByteArray &app); virtual ~kio_isoProtocol(); virtual void listDir(const QUrl &url) Q_DECL_OVERRIDE; virtual void stat(const QUrl &url) Q_DECL_OVERRIDE; virtual void get(const QUrl &url) Q_DECL_OVERRIDE; protected: void getFile(const KIsoFile *isoFileEntry, const QString &path); void createUDSEntry(const KArchiveEntry * isoEntry, KIO::UDSEntry & entry); bool checkNewFile(QString fullPath, QString & path, int startsec); QString getPath(const QUrl &url); KIso * m_isoFile; time_t m_mtime; int m_mode; }; #endif diff --git a/iso/kiso.cpp b/iso/kiso.cpp index 24dc9e6a..0b44efa7 100644 --- a/iso/kiso.cpp +++ b/iso/kiso.cpp @@ -1,514 +1,516 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * - * Copyright (C) 2003 Leo Savernik * * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2003 Leo Savernik * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * This file is heavily based on ktar from kdelibs * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "kiso.h" // QtCore #include #include #include #include #include #include #include #include #include #include #include "libisofs/isofs.h" #include "qfilehack.h" #ifdef Q_OS_LINUX #undef __STRICT_ANSI__ #include #define __STRICT_ANSI__ #include #include #endif //////////////////////////////////////////////////////////////////////// /////////////////////////// KIso /////////////////////////////////// //////////////////////////////////////////////////////////////////////// /** * puts the track layout of the device 'fname' into 'tracks' * tracks structure: start sector, track number, ... * tracks should be 100*2 entry long (this is the maximum in the CD-ROM standard) * currently it's linux only, porters are welcome */ static int getTracks(const char *fname, int *tracks) { KRFUNC; int ret = 0; memset(tracks, 0, 200*sizeof(int)); #ifdef Q_OS_LINUX int fd, i; struct cdrom_tochdr tochead; struct cdrom_tocentry tocentry; //qDebug() << "getTracks open:" << fname << endl; fd = QT_OPEN(fname, O_RDONLY | O_NONBLOCK); if (fd > 0) { if (ioctl(fd, CDROMREADTOCHDR, &tochead) != -1) { // qDebug() << "getTracks first track:" << tochead.cdth_trk0 // << " last track " << tochead.cdth_trk1 << endl; for (i = tochead.cdth_trk0;i <= tochead.cdth_trk1;++i) { if (ret > 99) break; memset(&tocentry, 0, sizeof(struct cdrom_tocentry)); tocentry.cdte_track = i; tocentry.cdte_format = CDROM_LBA; if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) < 0) break; // qDebug() << "getTracks got track " << i << " starting at: " << // tocentry.cdte_addr.lba << endl; if ((tocentry.cdte_ctrl & 0x4) == 0x4) { tracks[ret<<1] = tocentry.cdte_addr.lba; tracks[(ret<<1)+1] = i; ret++; } } } close(fd); } #endif return ret; } class KIso::KIsoPrivate { public: KIsoPrivate() {} QStringList dirList; }; KIso::KIso(const QString& filename, const QString & _mimetype) : KArchive(0L) { KRFUNC; KRDEBUG("Starting KIso: " << filename << " - type: " << _mimetype); m_startsec = -1; m_filename = filename; d = new KIsoPrivate; QString mimetype(_mimetype); bool forced = true; if (mimetype.isEmpty()) { QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent); if (mt.isValid()) mimetype = mt.name(); //qDebug() << "KIso::KIso mimetype=" << mimetype << endl; // Don't move to prepareDevice - the other constructor theoretically allows ANY filter if (mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around mimetype == "application/x-webarchive") // that's a gzipped tar file, so ask for gzip filter mimetype = "application/x-gzip"; else if (mimetype == "application/x-tbz") // that's a bzipped2 tar file, so ask for bz2 filter mimetype = "application/x-bzip2"; else { // Something else. Check if it's not really gzip though (e.g. for KOffice docs) QFile file(filename); if (file.open(QIODevice::ReadOnly)) { char firstByte; char secondByte; char thirdByte; file.getChar(&firstByte); file.getChar(&secondByte); file.getChar(&thirdByte); if (firstByte == 0037 && secondByte == (char)0213) mimetype = "application/x-gzip"; else if (firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h') mimetype = "application/x-bzip2"; else if (firstByte == 'P' && secondByte == 'K' && thirdByte == 3) { char fourthByte; file.getChar(&fourthByte); if (fourthByte == 4) mimetype = "application/x-zip"; } } } forced = false; } prepareDevice(filename, mimetype, forced); } void KIso::prepareDevice(const QString & filename, const QString & mimetype, bool forced) { KRFUNC; KRDEBUG("Preparing: " << filename << " - type: " << mimetype << " - using the force: " << forced); /* 'hack' for Qt's false assumption that only S_ISREG is seekable */ if ("inode/blockdevice" == mimetype) setDevice(new QFileHack(filename)); else { if ("application/x-gzip" == mimetype || "application/x-bzip2" == mimetype) forced = true; KCompressionDevice *device; if(mimetype.isEmpty()) { device = new KFilterDev(filename); } else { device = new KCompressionDevice(filename, KFilterDev::compressionTypeForMimeType(mimetype)); } if (device->compressionType() == KCompressionDevice::None && forced) { delete device; } else { setDevice(device); } } } KIso::KIso(QIODevice * dev) : KArchive(dev) { d = new KIsoPrivate; } KIso::~KIso() { // mjarrett: Closes to prevent ~KArchive from aborting w/o device if (isOpen()) close(); if (!m_filename.isEmpty()) delete device(); // we created it ourselves delete d; } /* callback function for libisofs */ static int readf(char *buf, unsigned int start, unsigned int len, void *udata) { KRFUNC; QIODevice* dev = (static_cast(udata))->device(); if (dev->seek((qint64)start << (qint64)11)) { if ((dev->read(buf, len << 11u)) != -1) return (len); } //qDebug() << "KIso::ReadRequest failed start: " << start << " len: " << len << endl; return -1; } /* callback function for libisofs */ static int mycallb(struct iso_directory_record *idr, void *udata) { KRFUNC; KIso *iso = static_cast(udata); QString path, user, group, symlink; int i; int access; int time, cdate, adate; rr_entry rr; bool special = false; KArchiveEntry *entry = NULL, *oldentry = NULL; char z_algo[2], z_params[2]; long long z_size = 0; if ((idr->flags[0] & 1) && !iso->showhidden) return 0; if (iso->level) { if (isonum_711(idr->name_len) == 1) { switch (idr->name[0]) { case 0: path += ("."); special = true; break; case 1: path += (".."); special = true; break; } } if (iso->showrr && ParseRR(idr, &rr) > 0) { if (!special) path = rr.name; symlink = rr.sl; access = rr.mode; time = rr.t_mtime; adate = rr.t_atime; cdate = rr.t_ctime; user.setNum(rr.uid); group.setNum(rr.gid); z_algo[0] = rr.z_algo[0];z_algo[1] = rr.z_algo[1]; z_params[0] = rr.z_params[0];z_params[1] = rr.z_params[1]; z_size = rr.z_size; } else { access = iso->dirent->permissions() & ~S_IFMT; adate = cdate = time = isodate_915(idr->date, 0); user = iso->dirent->user(); group = iso->dirent->group(); if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG; if (!special) { if (iso->joliet) { for (i = 0;i < (isonum_711(idr->name_len) - 1);i += 2) { void *p = &(idr->name[i]); QChar ch(be2me_16(*(ushort *)p)); if (ch == ';') break; path += ch; } } else { for (i = 0;i < isonum_711(idr->name_len);++i) { if (idr->name[i] == ';') break; if (idr->name[i]) path += (idr->name[i]); } } if (path.endsWith('.')) path.resize(path.length() - 1); } } if (iso->showrr) FreeRR(&rr); if (idr->flags[0] & 2) { entry = new KIsoDirectory(iso, path, access | S_IFDIR, time, adate, cdate, user, group, symlink); } else { entry = new KIsoFile(iso, path, access, time, adate, cdate, user, group, symlink, (long long)(isonum_733(idr->extent)) << (long long)11, isonum_733(idr->size)); if (z_size)(static_cast (entry))->setZF(z_algo, z_params, z_size); } iso->dirent->addEntry(entry); } if ((idr->flags[0] & 2) && (iso->level == 0 || !special)) { if (iso->level) { oldentry = iso->dirent; iso->dirent = static_cast(entry); } iso->level++; ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata); iso->level--; if (iso->level) iso->dirent = static_cast(oldentry); } return 0; } void KIso::addBoot(struct el_torito_boot_descriptor* bootdesc) { KRFUNC; int i; long long size; boot_head boot; boot_entry *be; QString path; KIsoFile *entry; path = "Catalog"; entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), (long long)isonum_731(bootdesc->boot_catalog) << (long long)11, (long long)2048); dirent->addEntry(entry); if (!ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, this)) { i = 1; be = boot.defentry; while (be) { size = BootImageSize(isonum_711(be->data.d_e.media), isonum_721(be->data.d_e.seccount)); path = "Default Image"; if (i > 1) path += " (" + QString::number(i) + ')'; entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), (long long)isonum_731(be->data.d_e.start) << (long long)11, size << (long long)9); dirent->addEntry(entry); be = be->next; i++; } FreeBootTable(&boot); } } void KIso::readParams() { KRFUNC; KConfig *config; config = new KConfig("kio_isorc"); KConfigGroup group(config, QString()); showhidden = group.readEntry("showhidden", false); showrr = group.readEntry("showrr", true); delete config; } bool KIso::openArchive(QIODevice::OpenMode mode) { KRFUNC; iso_vol_desc *desc; QString path, uid, gid; QT_STATBUF buf; int tracks[2*100], trackno = 0, i, access, c_b, c_i, c_j; KArchiveDirectory *root; struct iso_directory_record* idr; struct el_torito_boot_descriptor* bootdesc; if (mode == QIODevice::WriteOnly) return false; readParams(); d->dirList.clear(); tracks[0] = 0; if (m_startsec > 0) tracks[0] = m_startsec; //qDebug() << " m_startsec: " << m_startsec << endl; /* We'll use the permission and user/group of the 'host' file except * in Rock Ridge, where the permissions are stored on the file system */ if (QT_STAT(m_filename.toLocal8Bit(), &buf) < 0) { /* defaults, if stat fails */ memset(&buf, 0, sizeof(struct stat)); buf.st_mode = 0777; } else { /* If it's a block device, try to query the track layout (for multisession) */ if (m_startsec == -1 && S_ISBLK(buf.st_mode)) trackno = getTracks(m_filename.toLatin1(), (int*) & tracks); } uid.setNum(buf.st_uid); gid.setNum(buf.st_gid); access = buf.st_mode & ~S_IFMT; //qDebug() << "KIso::openArchive number of tracks: " << trackno << endl; if (trackno == 0) trackno = 1; for (i = 0;i < trackno;++i) { c_b = 1;c_i = 1;c_j = 1; root = rootDir(); if (trackno > 1) { path.clear(); QTextStream(&path) << "Track " << tracks[(i<<1)+1]; root = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); rootDir()->addEntry(root); } desc = ReadISO9660(&readf, tracks[i<<1], this); if (!desc) { //qDebug() << "KIso::openArchive no volume descriptors" << endl; continue; } while (desc) { switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: bootdesc = (struct el_torito_boot_descriptor*) & (desc->data); if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) { path = "El Torito Boot"; if (c_b > 1) path += " (" + QString::number(c_b) + ')'; dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); root->addEntry(dirent); addBoot(bootdesc); c_b++; } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: idr = (struct iso_directory_record*) & (((struct iso_primary_descriptor*) & desc->data)->root_directory_record); joliet = JolietLevel(&desc->data); if (joliet) { QTextStream(&path) << "Joliet level " << joliet; if (c_j > 1) path += " (" + QString::number(c_j) + ')'; } else { path = "ISO9660"; if (c_i > 1) path += " (" + QString::number(c_i) + ')'; } dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); root->addEntry(dirent); level = 0; mycallb(idr, this); if (joliet) c_j++; else c_i++; break; } desc = desc->next; } free(desc); } device()->close(); return true; } bool KIso::closeArchive() { KRFUNC; d->dirList.clear(); return true; } bool KIso::writeDir(const QString&, const QString&, const QString&, mode_t, time_t, time_t, time_t) { return false; } bool KIso::prepareWriting(const QString&, const QString&, const QString&, qint64, mode_t, time_t, time_t, time_t) { return false; } bool KIso::finishWriting(qint64) { return false; } bool KIso::writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t) { return false; } bool KIso::doWriteDir(const QString&, const QString&, const QString&, mode_t, const QDateTime&, const QDateTime &, const QDateTime &) { return false; } bool KIso::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime&, const QDateTime&, const QDateTime&) { return false; } bool KIso::doPrepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, const QDateTime&, const QDateTime&, const QDateTime&) { return false; } bool KIso::doFinishWriting(qint64) { return false; } void KIso::virtual_hook(int id, void* data) { KArchive::virtual_hook(id, data); } diff --git a/iso/kiso.h b/iso/kiso.h index 5fb7aa41..af67fec6 100644 --- a/iso/kiso.h +++ b/iso/kiso.h @@ -1,121 +1,123 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * - * Copyright (C) 2003 Leo Savernik * * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2003 Leo Savernik * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * This file is heavily based on ktar from kdelibs * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KISO_H #define KISO_H // QtCore #include #include #include #include "../krusader/krdebuglogger.h" #include "kisofile.h" #include "kisodirectory.h" /** * @short A class for reading (optionally compressed) iso9660 files. */ class KIso : public KArchive { public: /** * Creates an instance that operates on the given filename. * using the compression filter associated to given mimetype. * * @param filename is a local path (e.g. "/home/weis/myfile.tgz") * @param mimetype "application/x-gzip" or "application/x-bzip2" * Do not use application/x-tgz or so. Only the compression layer ! * If the mimetype is omitted, it will be determined from the filename. */ explicit KIso(const QString& filename, const QString & mimetype = QString()); /** * Creates an instance that operates on the given device. * The device can be compressed (KFilterDev) or not (QFile, etc.). * WARNING: don't assume that giving a QFile here will decompress the file, * in case it's compressed! */ explicit KIso(QIODevice * dev); /** * If the .iso is still opened, then it will be * closed automatically by the destructor. */ virtual ~KIso(); /** * The name of the os file, as passed to the constructor * Null if you used the QIODevice constructor. */ QString fileName() { return m_filename; } bool writeDir(const QString& , const QString& , const QString&, mode_t, time_t, time_t, time_t); bool writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t); bool prepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, time_t, time_t, time_t); bool finishWriting(qint64); void setStartSec(int startsec) { m_startsec = startsec; } int startSec() { return m_startsec; } bool showhidden, showrr; int level, joliet; KIsoDirectory *dirent; protected: /** * Opens the archive for reading. * Parses the directory listing of the archive * and creates the KArchiveDirectory/KArchiveFile entries. * */ void readParams(); virtual bool openArchive(QIODevice::OpenMode mode) Q_DECL_OVERRIDE; virtual bool closeArchive() Q_DECL_OVERRIDE; virtual bool doWriteDir(const QString&, const QString&, const QString&, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doPrepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doFinishWriting(qint64) Q_DECL_OVERRIDE; private: /** * @internal */ void addBoot(struct el_torito_boot_descriptor* bootdesc); void prepareDevice(const QString & filename, const QString & mimetype, bool forced = false); int m_startsec; QString m_filename; protected: virtual void virtual_hook(int id, void* data) Q_DECL_OVERRIDE; private: class KIsoPrivate; KIsoPrivate * d; }; #endif diff --git a/iso/kisodirectory.cpp b/iso/kisodirectory.cpp index c6f5b7a3..6cde6be6 100644 --- a/iso/kisodirectory.cpp +++ b/iso/kisodirectory.cpp @@ -1,34 +1,35 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "kisodirectory.h" KIsoDirectory::KIsoDirectory(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink) : KArchiveDirectory(archive, name, access, QDateTime::fromTime_t(date), user, group, symlink) { m_adate = adate; m_cdate = cdate; } KIsoDirectory::~KIsoDirectory() { } diff --git a/iso/kisodirectory.h b/iso/kisodirectory.h index 3f751786..7383a990 100644 --- a/iso/kisodirectory.h +++ b/iso/kisodirectory.h @@ -1,47 +1,48 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KISODIRECTORY_H #define KISODIRECTORY_H // QtCore #include #include class KIsoDirectory : public KArchiveDirectory { public: KIsoDirectory(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink); ~KIsoDirectory(); int date() const { return m_date; } int adate() const { return m_adate; } int cdate() const { return m_cdate; } private: int m_date, m_adate, m_cdate; }; #endif diff --git a/iso/kisofile.cpp b/iso/kisofile.cpp index cd654612..074412e2 100644 --- a/iso/kisofile.cpp +++ b/iso/kisofile.cpp @@ -1,59 +1,60 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "kisofile.h" KIsoFile::KIsoFile(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, long long pos, long long size) : KArchiveFile(archive, name, access, QDateTime::fromTime_t(date), user, group, symlink, pos, size) { m_adate = adate; m_cdate = cdate; m_algo[0] = 0;m_algo[1] = 0;m_parms[0] = 0;m_parms[1] = 0;m_realsize = 0; } KIsoFile::~KIsoFile() { } void KIsoFile::setZF(char algo[2], char parms[2], long long realsize) { m_algo[0] = algo[0];m_algo[1] = algo[1]; m_parms[0] = parms[0];m_parms[1] = parms[1]; m_realsize = realsize; } QByteArray KIsoFile::dataAt(long long pos, int count) const { QByteArray r; int rlen; if (archive()->device()->seek(position() + pos)) { r.resize(((pos + count) < size()) ? count : size() - pos); if (r.size()) { rlen = archive()->device()->read(r.data(), r.size()); if (rlen == - 1) r.resize(0); else if (rlen != (int)r.size()) r.resize(rlen); } } return r; } diff --git a/iso/kisofile.h b/iso/kisofile.h index 2a505c08..b40b9752 100644 --- a/iso/kisofile.h +++ b/iso/kisofile.h @@ -1,55 +1,56 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KISOFILE_H #define KISOFILE_H // QtCore #include #include class KIsoFile : public KArchiveFile { public: KIsoFile(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, long long pos, long long size); ~KIsoFile(); void setZF(char algo[2], char parms[2], long long realsize); int adate() const { return m_adate; } int cdate() const { return m_cdate; } long long realsize() const { return m_realsize; } virtual QByteArray dataAt(long long pos, int count) const; private: /* hide this member function, it's broken by design, because the full data often requires too much memory */ char m_algo[2], m_parms[2]; long long m_realsize; int m_adate, m_cdate; long long m_curpos; }; #endif diff --git a/iso/libisofs/bswap.h b/iso/libisofs/bswap.h index 5866e201..3cfed8d0 100644 --- a/iso/libisofs/bswap.h +++ b/iso/libisofs/bswap.h @@ -1,115 +1,117 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * From the mplayer project (www.mplayerhq.hu) * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef BSWAP_H #define BSWAP_H /* this file doesn't exist #ifdef HAVE_CONFIG_H #include "config.h" #endif */ #ifdef HAVE_BYTESWAP_H #include #else #ifdef ARCH_X86 inline static unsigned short ByteSwap16(unsigned short x) { __asm("xchgb %b0,%h0" : "=q"(x) : "0"(x)); return x; } #define bswap_16(x) ByteSwap16(x) inline static unsigned int ByteSwap32(unsigned int x) { #if __CPU__ > 386 __asm("bswap %0": "=r"(x) : #else __asm("xchgb %b0,%h0\n" " rorl $16,%0\n" " xchgb %b0,%h0": "=q"(x) : #endif "0"(x)); return x; } #define bswap_32(x) ByteSwap32(x) inline static unsigned long long int ByteSwap64(unsigned long long int x) { register union { __extension__ unsigned long long int __ll; unsigned int __l[2]; } __x; asm("xchgl %0,%1": "=r"(__x.__l[0]), "=r"(__x.__l[1]): "0"(bswap_32((unsigned long)x)), "1"(bswap_32((unsigned long)(x >> 32)))); return __x.__ll; } #define bswap_64(x) ByteSwap64(x) #else #define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8) /* code from bits/byteswap.h (C) 1997, 1998 Free Software Foundation, Inc. */ #define bswap_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #define bswap_64(x) \ (__extension__ \ ({ union { __extension__ unsigned long long int __ll; \ unsigned int __l[2]; } __w, __r; \ __w.__ll = (x); \ __r.__l[0] = bswap_32 (__w.__l[1]); \ __r.__l[1] = bswap_32 (__w.__l[0]); \ __r.__ll; })) #endif /* !ARCH_X86 */ #endif /* !HAVE_BYTESWAP_H */ /* be2me ... BigEndian to MachineEndian le2me ... LittleEndian to MachineEndian */ #ifdef WORDS_BIGENDIAN #define be2me_16(x) (x) #define be2me_32(x) (x) #define be2me_64(x) (x) #define le2me_16(x) bswap_16(x) #define le2me_32(x) bswap_32(x) #define le2me_64(x) bswap_64(x) #else #define be2me_16(x) bswap_16(x) #define be2me_32(x) bswap_32(x) #define be2me_64(x) bswap_64(x) #define le2me_16(x) (x) #define le2me_32(x) (x) #define le2me_64(x) (x) #endif #endif diff --git a/iso/libisofs/el_torito.h b/iso/libisofs/el_torito.h index a05bdd71..d6196385 100644 --- a/iso/libisofs/el_torito.h +++ b/iso/libisofs/el_torito.h @@ -1,83 +1,85 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * From the linux kernel * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef EL_TORITO_H #define EL_TORITO_H #include "iso_fs.h" #define EL_TORITO_ID "EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0" struct el_torito_boot_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char system_id [ISODCL(8, 39)]; /* achars */ char unused [ISODCL(40, 71)]; char boot_catalog [ISODCL(72, 75)]; /* 731 */ }; struct validation_entry { char type [ISODCL(1, 1)]; /* 1 */ char platform [ISODCL(2, 2)]; char unused [ISODCL(3, 4)]; char id [ISODCL(5, 28)]; char cheksum [ISODCL(29, 30)]; char key [ISODCL(31, 31)]; /* 0x55 */ char key2 [ISODCL(32, 32)]; /* 0xaa */ }; struct default_entry { char bootid [ISODCL(1, 1)]; char media [ISODCL(2, 2)]; char loadseg [ISODCL(3, 4)]; char systype [ISODCL(5, 5)]; char unused [ISODCL(6, 6)]; char seccount [ISODCL(7, 8)]; char start [ISODCL(9, 12)]; char unused2 [ISODCL(13, 32)]; }; struct section_header { char headerid [ISODCL(1, 1)]; char platform [ISODCL(2, 2)]; char entries [ISODCL(3, 4)]; char id [ISODCL(5, 32)]; }; struct section_entry { char bootid [ISODCL(1, 1)]; char media [ISODCL(2, 2)]; char loadseg [ISODCL(3, 4)]; char systype [ISODCL(5, 5)]; char unused [ISODCL(6, 6)]; char seccount [ISODCL(7, 8)]; char start [ISODCL(9, 12)]; char selcrit [ISODCL(13, 13)]; char vendor_selcrit [ISODCL(14, 32)]; }; struct section_entry_ext { char extid [ISODCL(1, 1)]; char extrec [ISODCL(2, 2)]; char vendor_selcrit [ISODCL(3, 32)]; }; #endif diff --git a/iso/libisofs/iso_fs.h b/iso/libisofs/iso_fs.h index 0ee89e46..c726de44 100644 --- a/iso/libisofs/iso_fs.h +++ b/iso/libisofs/iso_fs.h @@ -1,249 +1,251 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * From the linux kernel * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ISO_FS_H #define ISO_FS_H #include "bswap.h" #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 ); #else #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "/" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '/' #define REPLACE_DIR_SEP2(x) #endif /* * The isofs filesystem constants/structures */ /* This part borrowed from the bsd386 isofs */ #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* volume descriptor types */ #define ISO_VD_BOOT 0 #define ISO_VD_PRIMARY 1 #define ISO_VD_SUPPLEMENTARY 2 #define ISO_VD_END 255 #define ISO_STANDARD_ID "CD001" struct iso_primary_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char unused1 [ISODCL(8, 8)]; char system_id [ISODCL(9, 40)]; /* achars */ char volume_id [ISODCL(41, 72)]; /* dchars */ char unused2 [ISODCL(73, 80)]; char volume_space_size [ISODCL(81, 88)]; /* 733 */ char unused3 [ISODCL(89, 120)]; char volume_set_size [ISODCL(121, 124)]; /* 723 */ char volume_sequence_number [ISODCL(125, 128)]; /* 723 */ char logical_block_size [ISODCL(129, 132)]; /* 723 */ char path_table_size [ISODCL(133, 140)]; /* 733 */ char type_l_path_table [ISODCL(141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL(145, 148)]; /* 731 */ char type_m_path_table [ISODCL(149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL(153, 156)]; /* 732 */ char root_directory_record [ISODCL(157, 190)]; /* 9.1 */ char volume_set_id [ISODCL(191, 318)]; /* dchars */ char publisher_id [ISODCL(319, 446)]; /* achars */ char preparer_id [ISODCL(447, 574)]; /* achars */ char application_id [ISODCL(575, 702)]; /* achars */ char copyright_file_id [ISODCL(703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL(740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL(777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL(814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL(831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL(848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL(865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL(882, 882)]; /* 711 */ char unused4 [ISODCL(883, 883)]; char application_data [ISODCL(884, 1395)]; char unused5 [ISODCL(1396, 2048)]; }; /* Almost the same as the primary descriptor but two fields are specified */ struct iso_supplementary_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char flags [ISODCL(8, 8)]; /* 853 */ char system_id [ISODCL(9, 40)]; /* achars */ char volume_id [ISODCL(41, 72)]; /* dchars */ char unused2 [ISODCL(73, 80)]; char volume_space_size [ISODCL(81, 88)]; /* 733 */ char escape [ISODCL(89, 120)]; /* 856 */ char volume_set_size [ISODCL(121, 124)]; /* 723 */ char volume_sequence_number [ISODCL(125, 128)]; /* 723 */ char logical_block_size [ISODCL(129, 132)]; /* 723 */ char path_table_size [ISODCL(133, 140)]; /* 733 */ char type_l_path_table [ISODCL(141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL(145, 148)]; /* 731 */ char type_m_path_table [ISODCL(149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL(153, 156)]; /* 732 */ char root_directory_record [ISODCL(157, 190)]; /* 9.1 */ char volume_set_id [ISODCL(191, 318)]; /* dchars */ char publisher_id [ISODCL(319, 446)]; /* achars */ char preparer_id [ISODCL(447, 574)]; /* achars */ char application_id [ISODCL(575, 702)]; /* achars */ char copyright_file_id [ISODCL(703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL(740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL(777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL(814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL(831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL(848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL(865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL(882, 882)]; /* 711 */ char unused4 [ISODCL(883, 883)]; char application_data [ISODCL(884, 1395)]; char unused5 [ISODCL(1396, 2048)]; }; #define HS_STANDARD_ID "CDROM" struct hs_volume_descriptor { char foo [ISODCL(1, 8)]; /* 733 */ char type [ISODCL(9, 9)]; /* 711 */ char id [ISODCL(10, 14)]; char version [ISODCL(15, 15)]; /* 711 */ char data[ISODCL(16,2048)]; }; struct hs_primary_descriptor { char foo [ISODCL(1, 8)]; /* 733 */ char type [ISODCL(9, 9)]; /* 711 */ char id [ISODCL(10, 14)]; char version [ISODCL(15, 15)]; /* 711 */ char unused1 [ISODCL(16, 16)]; /* 711 */ char system_id [ISODCL(17, 48)]; /* achars */ char volume_id [ISODCL(49, 80)]; /* dchars */ char unused2 [ISODCL(81, 88)]; /* 733 */ char volume_space_size [ISODCL(89, 96)]; /* 733 */ char unused3 [ISODCL(97, 128)]; /* 733 */ char volume_set_size [ISODCL(129, 132)]; /* 723 */ char volume_sequence_number [ISODCL(133, 136)]; /* 723 */ char logical_block_size [ISODCL(137, 140)]; /* 723 */ char path_table_size [ISODCL(141, 148)]; /* 733 */ char type_l_path_table [ISODCL(149, 152)]; /* 731 */ char unused4 [ISODCL(153, 180)]; /* 733 */ char root_directory_record [ISODCL(181, 214)]; /* 9.1 */ }; /* We use this to help us look up the parent inode numbers. */ struct iso_path_table { char name_len[1]; /* 711 */ char ext_attr_length[1]; /* 711 */ char extent[4]; /* 731 */ char parent[2]; /* 721 */ char name[1]; }; /* high sierra is identical to iso, except that the date is only 6 bytes, and there is an extra reserved byte after the flags */ struct iso_directory_record { char length [ISODCL(1, 1)]; /* 711 */ char ext_attr_length [ISODCL(2, 2)]; /* 711 */ char extent [ISODCL(3, 10)]; /* 733 */ char size [ISODCL(11, 18)]; /* 733 */ char date [ISODCL(19, 25)]; /* 7 by 711 */ char flags [ISODCL(26, 26)]; char file_unit_size [ISODCL(27, 27)]; /* 711 */ char interleave [ISODCL(28, 28)]; /* 711 */ char volume_sequence_number [ISODCL(29, 32)]; /* 723 */ char name_len [ISODCL(33, 33)]; /* 711 */ char name [1]; }; /* 8 bit numbers */ static __inline unsigned char isonum_711(char *p); static __inline char isonum_712(char *p); /* 16 bit numbers */ static __inline unsigned short isonum_721(char *p); static __inline unsigned short isonum_722(char *p); static __inline unsigned short isonum_723(char *p); /* 32 bit numbers */ static __inline unsigned int isonum_731(char *p); static __inline unsigned int isonum_732(char *p); static __inline unsigned int isonum_733(char *p); /* 8 bit numbers */ static __inline unsigned char isonum_711(char *p) { return *(unsigned char *)p; } static __inline char isonum_712(char *p) { return *p; } /* 16 bit numbers */ static __inline unsigned short isonum_721(char *p) { return le2me_16(*(unsigned short *)p); } static __inline unsigned short isonum_722(char *p) { return be2me_16(*(unsigned short *)p); } static __inline unsigned short isonum_723(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le2me_16(*(unsigned short *)p); } /* 32 bit numbers */ static __inline unsigned int isonum_731(char *p) { return le2me_32(*(unsigned int *)p); } static __inline unsigned int isonum_732(char *p) { return be2me_32(*(unsigned int *)p); } static __inline unsigned int isonum_733(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le2me_32(*(unsigned int *)p); } #endif /*_ISOFS_H*/ diff --git a/iso/libisofs/isofs.c b/iso/libisofs/isofs.c index d74aee3d..c4ef0526 100644 --- a/iso/libisofs/isofs.c +++ b/iso/libisofs/isofs.c @@ -1,913 +1,914 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "isofs.h" #include #include #include "iso_fs.h" /**************************************************************/ /* internal function from the linux kernel (isofs fs) */ static time_t getisotime(int year, int month, int day, int hour, int minute, int second, int tz) { int days, i; time_t crtime; year -= 1970; if (year < 0) { crtime = 0; } else { int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; days = year * 365; if (year > 2) days += (year + 1) / 4; for (i = 1; i < month; i++) days += monlen[i-1]; if (((year + 2) % 4) == 0 && month > 2) days++; days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; /* sign extend */ if (tz & 0x80) tz |= (-1 << 8); /* * The timezone offset is unreliable on some disks, * so we make a sanity check. In no case is it ever * more than 13 hours from GMT, which is 52*15min. * The time is always stored in localtime with the * timezone offset being what get added to GMT to * get to localtime. Thus we need to subtract the offset * to get to true GMT, which is what we store the time * as internally. On the local system, the user may set * their timezone any way they wish, of course, so GMT * gets converted back to localtime on the receiving * system. * * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how * it will matter that much. * * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) * for pointing out the sign error. */ if (-52 <= tz && tz <= 52) crtime -= tz * 15 * 60; } return crtime; } /** * Returns the Unix from the ISO9660 9.1.5 time format */ time_t isodate_915(char * p, int hs) { return getisotime(1900 + p[0], p[1], p[2], p[3], p[4], p[5], hs == 0 ? p[6] : 0); } /** * Returns the Unix from the ISO9660 8.4.26.1 time format * BUG: hundredth of seconds are ignored, because Unix time_t has one second * resolution (I think it's no problem at all) */ time_t isodate_84261(char * p, int hs) { int year, month, day, hour, minute, second; year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + p[3] - '0'; month = (p[4] - '0') * 10 + (p[5] - '0'); day = (p[6] - '0') * 10 + (p[7] - '0'); hour = (p[8] - '0') * 10 + (p[9] - '0'); minute = (p[10] - '0') * 10 + (p[11] - '0'); second = (p[12] - '0') * 10 + (p[13] - '0'); return getisotime(year, month, day, hour, minute, second, hs == 0 ? p[16] : 0); } void FreeBootTable(boot_head *boot) { boot_entry *be, *next; be = boot->defentry; while (be) { next = be->next; free(be); be = next; } boot->defentry = NULL; } long long BootImageSize(int media, unsigned int len) { long long ret; switch (media & 0xf) { case 0: ret = len; /* No emulation */ break; case 1: ret = 80 * 2 * 15; /* 1.2 MB */ break; case 2: ret = 80 * 2 * 18; /* 1.44 MB */ break; case 3: ret = 80 * 2 * 36; /* 2.88 MB */ break; case 4: /* FIXME!!! */ ret = len; /* Hard Disk */ break; default: ret = len; } return ret; } static boot_entry *CreateBootEntry(char *be) { boot_entry *entry; entry = (boot_entry*) malloc(sizeof(boot_entry)); if (!entry) return NULL; memset(entry, 0, sizeof(boot_entry)); memcpy(&(entry->data), be, sizeof(entry->data)); return entry; } int ReadBootTable(readfunc *read, unsigned int sector, boot_head *head, void *udata) { char buf[2048], *c, *be; int i, end = 0; unsigned short sum; boot_entry *defcur = NULL, *deflast = NULL; register struct validation_entry *ventry = NULL; head->sections = NULL; head->defentry = NULL; while (1) { be = (char*) & buf; if (read(be, sector, 1, udata) != 1) goto err; /* first entry needs to be a validation entry */ if (!ventry) { ventry = (struct validation_entry *) be; if (isonum_711(ventry->type) != 1) goto err; sum = 0; c = (char*) ventry; for (i = 0;i < 16;i++) { sum += isonum_721(c); c += 2; } if (sum) goto err; memcpy(&head->ventry, be, 0x20); be += 0x20; } while (!end && (be < (char *)(&buf + 1))) { switch (isonum_711(be)) { case 0x88: defcur = CreateBootEntry(be); if (!defcur) goto err; if (deflast) deflast->next = defcur; else head->defentry = defcur; defcur->prev = deflast; deflast = defcur; break; case 0x90: case 0x91: break; default: end = 1; break; } be += 0x20; } if (end) break; sector ++; } return 0; err: FreeBootTable(head); return -1; } /** * Creates the linked list of the volume descriptors */ iso_vol_desc *ReadISO9660(readfunc *read, unsigned int sector, void *udata) { int i; struct iso_volume_descriptor buf; iso_vol_desc *first = NULL, *current = NULL, *prev = NULL; for (i = 0;i < 100;i++) { if (read((char*) &buf, sector + i + 16, 1, udata) != 1) { FreeISO9660(first); return NULL; } if (!memcmp(ISO_STANDARD_ID, &buf.id, 5)) { switch (isonum_711(&buf.type[0])) { case ISO_VD_BOOT: case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: current = (iso_vol_desc*) malloc(sizeof(iso_vol_desc)); if (!current) { FreeISO9660(first); return NULL; } current->prev = prev; current->next = NULL; if (prev) prev->next = current; memcpy(&(current->data), &buf, 2048); if (!first) first = current; prev = current; break; case ISO_VD_END: return first; break; } } else if (!memcmp(HS_STANDARD_ID, (struct hs_volume_descriptor*) &buf, 5)) { /* High Sierra format not supported (yet) */ } } return first; } /** * Frees the linked list of volume descriptors */ void FreeISO9660(iso_vol_desc *data) { iso_vol_desc *current; while (data) { current = data; data = current->next; free(current); } } /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry) { if (rrentry->name) { free(rrentry->name); rrentry->name = NULL; } if (rrentry->sl) { free(rrentry->sl); rrentry->name = NULL; } } static int str_nappend(char **d, char *s, int n) { int i = 0; char *c; /* i=strnlen(s,n)+1; */ while (i < n && s[i]) i++; i++; if (*d) i += (strlen(*d) + 1); c = (char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c, *d); strncat(c, s, n); free(*d); } else strncpy(c, s, n); c[i-1] = 0; *d = c; return 0; } static int str_append(char **d, char *s) { int i; char *c; i = strlen(s) + 1; if (*d) i += (strlen(*d) + 1); c = (char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c, *d); strcat(c, s); free(*d); } else strcpy(c, s); c[i-1] = 0; *d = c; return 0; } #define rrtlen(c) (((unsigned char) c & 0x80) ? 17 : 7) #define rrctime(f,c) ((unsigned char) f & 0x80) ? isodate_84261(c,0) : isodate_915(c,0) /** * Parses the System Use area and fills rr_entry with values */ int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry) { int suspoffs, susplen, i, f, ret = 0; char *r, *c; struct rock_ridge *rr; suspoffs = 33 + isonum_711(idr->name_len); if (!(isonum_711(idr->name_len) & 1)) suspoffs++; susplen = isonum_711(idr->length) - suspoffs; r = & (((char*) idr)[suspoffs]); rr = (struct rock_ridge*) r; memset(rrentry, 0, sizeof(rr_entry)); rrentry->len = sizeof(rr_entry); while (susplen > 0) { if (isonum_711(&rr->len) > susplen || rr->len == 0) break; if (rr->signature[0] == 'N' && rr->signature[1] == 'M') { if (!(rr->u.NM.flags & 0x26) && rr->len > 5 && !rrentry->name) { if (str_nappend(&rrentry->name, rr->u.NM.name, isonum_711(&rr->len) - 5)) { FreeRR(rrentry); return -ENOMEM; } ret++; } } else if (rr->signature[0] == 'P' && rr->signature[1] == 'X' && (isonum_711(&rr->len) == 44 || isonum_711(&rr->len) == 36)) { rrentry->mode = isonum_733(rr->u.PX.mode); rrentry->nlink = isonum_733(rr->u.PX.n_links); rrentry->uid = isonum_733(rr->u.PX.uid); rrentry->gid = isonum_733(rr->u.PX.gid); if (isonum_711(&rr->len) == 44) rrentry->serno = isonum_733(rr->u.PX.serno); ret++; } else if (rr->signature[0] == 'P' && rr->signature[1] == 'N' && isonum_711(&rr->len) == 20) { rrentry->dev_major = isonum_733(rr->u.PN.dev_high); rrentry->dev_minor = isonum_733(rr->u.PN.dev_low); ret++; } else if (rr->signature[0] == 'P' && rr->signature[1] == 'L' && isonum_711(&rr->len) == 12) { rrentry->pl = isonum_733(rr->u.PL.location); ret++; } else if (rr->signature[0] == 'C' && rr->signature[1] == 'L' && isonum_711(&rr->len) == 12) { rrentry->cl = isonum_733(rr->u.CL.location); ret++; } else if (rr->signature[0] == 'R' && rr->signature[1] == 'E' && isonum_711(&rr->len) == 4) { rrentry->re = 1; ret++; } else if (rr->signature[0] == 'S' && rr->signature[1] == 'L' && isonum_711(&rr->len) > 7) { i = isonum_711(&rr->len) - 5; c = (char*) rr; c += 5; while (i > 0) { switch (c[0] & ~1) { case 0x2: if (str_append(&rrentry->sl, (char *)".")) { FreeRR(rrentry); return -ENOMEM; } break; case 0x4: if (str_append(&rrentry->sl, (char *)"..")) { FreeRR(rrentry); return -ENOMEM; } break; } if ((c[0] & 0x08) == 0x08 || (c[1] && rrentry->sl && strlen(rrentry->sl) > 1)) { if (str_append(&rrentry->sl, (char *) DIR_SEPARATOR)) { FreeRR(rrentry); return -ENOMEM; } } if ((unsigned char)c[1] > 0) { if (str_nappend(&rrentry->sl, c + 2, (unsigned char)c[1])) { FreeRR(rrentry); return -ENOMEM; } } i -= ((unsigned char)c[1] + 2); c += ((unsigned char)c[1] + 2); } ret++; } else if (rr->signature[0] == 'T' && rr->signature[1] == 'F' && isonum_711(&rr->len) > 5) { i = isonum_711(&rr->len) - 5; f = rr->u.TF.flags; c = (char*) rr; c += 5; while (i >= rrtlen(f)) { if (f & 1) { rrentry->t_creat = rrctime(f, c); f &= ~1; } else if (f & 2) { rrentry->t_mtime = rrctime(f, c); f &= ~2; } else if (f & 4) { rrentry->t_atime = rrctime(f, c); f &= ~4; } else if (f & 8) { rrentry->t_ctime = rrctime(f, c); f &= ~8; } else if (f & 16) { rrentry->t_backup = rrctime(f, c); f &= ~16; } else if (f & 32) { rrentry->t_expire = rrctime(f, c); f &= ~32; } else if (f & 64) { rrentry->t_effect = rrctime(f, c); f &= ~64; } i -= rrtlen(f); c += rrtlen(f); } ret++; } else if (rr->signature[0] == 'Z' && rr->signature[1] == 'F' && isonum_711(&rr->len) == 16) { /* Linux-specific extension: transparent decompression */ rrentry->z_algo[0] = rr->u.ZF.algorithm[0]; rrentry->z_algo[1] = rr->u.ZF.algorithm[1]; rrentry->z_params[0] = rr->u.ZF.parms[0]; rrentry->z_params[1] = rr->u.ZF.parms[1]; rrentry->z_size = isonum_733(rr->u.ZF.real_size); ret++; } else { /* printf("SUSP sign: %c%c\n",rr->signature[0],rr->signature[1]); */ } susplen -= isonum_711(&rr->len); r += isonum_711(&rr->len); rr = (struct rock_ridge*) r; } return ret; } /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read, int extent, int size, dircallback *callback, void *udata) { int pos = 0, ret = 0, siz; char *buf; struct iso_directory_record *idr; if (size & 2047) { siz = ((size >> 11) + 1) << 11; } else { siz = size; } buf = (char*) malloc(siz); if (!buf) return -ENOMEM; if (read(buf, extent, siz >> 11, udata) != siz >> 11) { free(buf); return -EIO; } while (size > 0) { idr = (struct iso_directory_record*) & buf[pos]; if (isonum_711(idr->length) == 0) { size -= (2048 - (pos & 0x7ff)); if (size <= 2) break; pos += 0x800; pos &= 0xfffff800; idr = (struct iso_directory_record*) & buf[pos]; } pos += isonum_711(idr->length); pos += isonum_711(idr->ext_attr_length); size -= isonum_711(idr->length); size -= isonum_711(idr->ext_attr_length); if (size < 0) break; if (isonum_711(idr->length) < 33 || isonum_711(idr->length) < 33 + isonum_711(idr->name_len)) { /* Invalid directory entry */ continue; } if ((ret = callback(idr, udata))) break; } free(buf); return ret; } /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd) { int ret = 0; register struct iso_supplementary_descriptor *isd; isd = (struct iso_supplementary_descriptor *) ivd; if (isonum_711(ivd->type) == ISO_VD_SUPPLEMENTARY) { if (isd->escape[0] == 0x25 && isd->escape[1] == 0x2f) { switch (isd->escape[2]) { case 0x40: ret = 1; break; case 0x43: ret = 2; break; case 0x45: ret = 3; break; } } } return ret; } /********************************************************************/ #ifdef ISOFS_MAIN #include #include #include #include #include #include #include int level = 0, joliet = 0, dirs, files; iconv_t iconv_d; int fd; int readf(char *buf, unsigned int start, unsigned int len, void *udata) { int ret; if ((ret = lseek64(fd, (long long)start << (long long)11, SEEK_SET)) < 0) return ret; ret = read(fd, buf, len << 11u); if (ret < 0) return ret; return (ret >> 11u); } void dumpchars(char *c, int len) { while (len > 0) { printf("%c", *c); len--; c++; } } void sp(int num) { int i; for (i = 0;i < num*5;i++) { printf(" "); }; } void dumpflags(char flags) { if (flags & 1) printf("HIDDEN "); if (flags & 2) printf("DIR "); if (flags & 4) printf("ASF "); } void dumpjoliet(char *c, int len) { char outbuf[255]; size_t out; int ret; char *outptr; outptr = (char*) & outbuf; out = 255; if ((iconv(iconv_d, &c, &len, &outptr, &out)) < 0) { printf("conversion error=%d", errno); return; } ret = 255 - out; dumpchars((char*) &outbuf, ret); } void dumpchardesc(char *c, int len) { if (joliet) dumpjoliet(c, len); else { dumpchars(c, len); } } void dumpiso915time(char *t, int hs) { time_t time; char *c; time = isodate_915(t, hs); c = (char*) ctime(&time); if (c && c[strlen(c)-1] == 0x0a) c[strlen(c)-1] = 0; if (c) printf("%s", c); } void dumpiso84261time(char *t, int hs) { time_t time; char *c; time = isodate_84261(t, hs); c = (char*) ctime(&time); if (c && c[strlen(c)-1] == 0x0a) c[strlen(c)-1] = 0; if (c) printf("%s", c); } void dumpdirrec(struct iso_directory_record *dir) { if (isonum_711(dir->name_len) == 1) { switch (dir->name[0]) { case 0: printf("."); break; case 1: printf(".."); break; default: printf("%c", dir->name[0]); break; } } dumpchardesc(dir->name, isonum_711(dir->name_len)); printf(" size=%d", isonum_733(dir->size)); printf(" extent=%d ", isonum_733(dir->extent)); dumpflags(isonum_711(dir->flags)); dumpiso915time((char*) &(dir->date), 0); } void dumprrentry(rr_entry *rr) { printf(" NM=[%s] uid=%d gid=%d nlink=%d mode=%o ", rr->name, rr->uid, rr->gid, rr->nlink, rr->mode); if (S_ISCHR(rr->mode) || S_ISBLK(rr->mode)) printf("major=%d minor=%d ", rr->dev_major, rr->dev_minor); if (rr->mode & S_IFLNK && rr->sl) printf("slink=%s ", rr->sl); /* printf("\n"); if (rr->t_creat) printf("t_creat: %s",ctime(&rr->t_creat)); if (rr->st_mtime) printf("st_mtime: %s",ctime(&rr->st_mtime)); if (rr->st_atime) printf("st_atime: %s",ctime(&rr->st_atime)); if (rr->st_ctime) printf("st_ctime: %s",ctime(&rr->st_ctime)); if (rr->t_backup) printf("t_backup: %s",ctime(&rr->t_backup)); if (rr->t_expire) printf("t_expire: %s",ctime(&rr->t_expire)); if (rr->t_effect) printf("t_effect: %s",ctime(&rr->t_effect)); */ } void dumpsusp(char *c, int len) { dumpchars(c, len); } void dumpboot(struct el_torito_boot_descriptor *ebd) { printf("version: %d\n", isonum_711(ebd->version)); printf("system id: ");dumpchars(ebd->system_id, ISODCL(8, 39));printf("\n"); printf("boot catalog start: %d\n", isonum_731(ebd->boot_catalog)); } void dumpdefentry(struct default_entry *de) { printf("Default entry: \n"); printf(" bootid=%x\n", isonum_711(de->bootid)); printf(" media emulation=%d (", isonum_711(de->media)); switch (isonum_711(de->media) & 0xf) { case 0: printf("No emulation"); break; case 1: printf("1.2 Mb floppy"); break; case 2: printf("1.44 Mb floppy"); break; case 3: printf("2.88 Mb floppy"); break; case 4: printf("Hard Disk"); break; default: printf("Unknown/Invalid"); break; } printf(")\n"); printf(" loadseg=%d\n", isonum_721(de->loadseg)); printf(" systype=%d\n", isonum_711(de->systype)); printf(" start lba=%d count=%d\n", isonum_731(de->start), isonum_721(de->seccount)); } void dumpbootcat(boot_head *bh) { boot_entry *be; printf("System id: ");dumpchars(bh->ventry.id, ISODCL(28, 5));printf("\n"); be = bh->defentry; while (be) { dumpdefentry(be->data); be = be->next; } } void dumpdesc(struct iso_primary_descriptor *ipd) { printf("system id: ");dumpchardesc(ipd->system_id, ISODCL(9, 40));printf("\n"); printf("volume id: ");dumpchardesc(ipd->volume_id, ISODCL(41, 72));printf("\n"); printf("volume space size: %d\n", isonum_733(ipd->volume_space_size)); printf("volume set size: %d\n", isonum_723(ipd->volume_set_size)); printf("volume seq num: %d\n", isonum_723(ipd->volume_set_size)); printf("logical block size: %d\n", isonum_723(ipd->logical_block_size)); printf("path table size: %d\n", isonum_733(ipd->path_table_size)); printf("location of type_l path table: %d\n", isonum_731(ipd->type_l_path_table)); printf("location of optional type_l path table: %d\n", isonum_731(ipd->opt_type_l_path_table)); printf("location of type_m path table: %d\n", isonum_732(ipd->type_m_path_table)); printf("location of optional type_m path table: %d\n", isonum_732(ipd->opt_type_m_path_table)); /* printf("Root dir record:\n");dumpdirrec((struct iso_directory_record*) &ipd->root_directory_record); */ printf("Volume set id: ");dumpchardesc(ipd->volume_set_id, ISODCL(191, 318));printf("\n"); printf("Publisher id: ");dumpchardesc(ipd->publisher_id, ISODCL(319, 446));printf("\n"); printf("Preparer id: ");dumpchardesc(ipd->preparer_id, ISODCL(447, 574));printf("\n"); printf("Application id: ");dumpchardesc(ipd->application_id, ISODCL(575, 702));printf("\n"); printf("Copyright id: ");dumpchardesc(ipd->copyright_file_id, ISODCL(703, 739));printf("\n"); printf("Abstract file id: ");dumpchardesc(ipd->abstract_file_id, ISODCL(740, 776));printf("\n"); printf("Bibliographic file id: ");dumpchardesc(ipd->bibliographic_file_id, ISODCL(777, 813));printf("\n"); printf("Volume creation date: ");dumpiso84261time(ipd->creation_date, 0);printf("\n"); printf("Volume modification date: ");dumpiso84261time(ipd->modification_date, 0);printf("\n"); printf("Volume expiration date: ");dumpiso84261time(ipd->expiration_date, 0);printf("\n"); printf("Volume effective date: ");dumpiso84261time(ipd->effective_date, 0);printf("\n"); printf("File structure version: %d\n", isonum_711(ipd->file_structure_version)); } int mycallb(struct iso_directory_record *idr, void *udata) { rr_entry rrentry; sp(level);dumpdirrec(idr); if (level == 0) printf(" (Root directory) "); printf("\n"); if (ParseRR(idr, &rrentry) > 0) { sp(level);printf(" ");dumprrentry(&rrentry);printf("\n"); } FreeRR(&rrentry); if (!(idr->flags[0] & 2)) files++; if ((idr->flags[0] & 2) && (level == 0 || isonum_711(idr->name_len) > 1)) { level++; dirs++; ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata); level--; } return 0; } /************************************************/ int main(int argc, char *argv[]) { int i = 1, sector = 0; iso_vol_desc *desc; boot_head boot; if (argc < 2) { fprintf(stderr, "\nUsage: %s iso-file-name or device [starting sector]\n\n", argv[0]); return 0; } if (argc >= 3) { sector = atoi(argv[2]); printf("Using starting sector number %d\n", sector); } fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "open error\n"); return -1; } iconv_d = iconv_open("ISO8859-2", "UTF16BE"); if (iconv_d == 0) { fprintf(stderr, "iconv open error\n"); return -1; } desc = ReadISO9660(&readf, sector, NULL); if (!desc) { printf("No volume descriptors\n"); return -1; } while (desc) { printf("\n\n--------------- Volume descriptor (%d.) type %d: ---------------\n\n", i, isonum_711(desc->data.type)); switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: { struct el_torito_boot_descriptor* bootdesc; bootdesc = &(desc->data); dumpboot(bootdesc); if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) { if (ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, NULL)) { printf("Boot Catalog Error\n"); } else { dumpbootcat(&boot); FreeBootTable(&boot); } } } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: joliet = 0; joliet = JolietLevel(&desc->data); printf("Joliet level: %d\n", joliet); dumpdesc((struct iso_primary_descriptor*) &desc->data); printf("\n\n--------------- Directory structure: -------------------\n\n"); dirs = 0;files = 0; mycallb(&(((struct iso_primary_descriptor*) &desc->data)->root_directory_record), NULL); printf("\nnumber of directories: %d\n", dirs); printf("\nnumber of files: %d\n", files); break; } desc = desc->next; i++; } iconv_close(iconv_d); close(fd); FreeISO9660(desc); return 0; } #endif /* ISOFS_MAIN */ diff --git a/iso/libisofs/isofs.h b/iso/libisofs/isofs.h index ebe35e8f..545edcb2 100644 --- a/iso/libisofs/isofs.h +++ b/iso/libisofs/isofs.h @@ -1,170 +1,171 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ISOFS_H #define ISOFS_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iso_fs.h" #include "el_torito.h" #include "rock.h" typedef struct _rr_entry { int len; /* length of structure */ char *name; /* Name from 'NM' */ char *sl; /* symbolic link data */ time_t t_creat; time_t t_mtime; time_t t_atime; time_t t_ctime; time_t t_backup; time_t t_expire; time_t t_effect; int mode; /* POSIX file modes */ int nlink; int uid; int gid; int serno; int dev_major; int dev_minor; int pl; /* parent location */ int cl; /* child location */ int re; /* relocated */ char z_algo[2]; /* zizofs algorithm */ char z_params[2]; /* zizofs parameters */ unsigned int z_size; /* zizofs real_size */ } rr_entry; typedef struct _iso_vol_desc { struct _iso_vol_desc *next; struct _iso_vol_desc *prev; struct iso_volume_descriptor data; } iso_vol_desc; union default_entry_as_data { char c[32]; struct default_entry d_e; }; typedef struct _boot_entry { struct _boot_entry *next; struct _boot_entry *prev; struct _boot_entry *parent; struct _boot_entry *child; union default_entry_as_data data; } boot_entry; typedef struct _boot_head { struct validation_entry ventry; struct _boot_entry *defentry; struct _boot_entry *sections; } boot_head; /** * this callback function needs to read 'len' sectors from 'start' into 'buf' */ typedef int readfunc(char *buf, unsigned int start, unsigned int len, void *); /** * ProcessDir uses this callback */ typedef int dircallback(struct iso_directory_record *, void *); /** * Returns the Unix from the ISO9660 9.1.5 (7 bytes) time format * This function is from the linux kernel. * Set 'hs' to non-zero if it's a HighSierra volume */ time_t isodate_915(char * p, int hs); /** * Returns the Unix time from the ISO9660 8.4.26.1 (17 bytes) time format * BUG: hundredth of seconds are ignored, because time_t has one second * resolution (I think it's no problem at all) * Set 'hs' to non-zero if it's a HighSierra volume */ time_t isodate_84261(char * p, int hs); /** * Creates the linked list of the volume descriptors * 'sector' is the starting sector number of where the filesystem start * (starting sector of a session on a CD-ROM) * If the function fails, returns NULL * Don't forget to call FreeISO9660 after using the volume descriptor list! */ iso_vol_desc *ReadISO9660(readfunc *read, unsigned int sector, void *udata); /** * Frees the linked list of volume descriptors . */ void FreeISO9660(iso_vol_desc *data); /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read, int extent, int size, dircallback *callback, void *udata); /** * Parses the System Use area and fills rr_entry with values */ int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry); /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry); /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd); /** * Returns the size of the boot image (in 512 byte sectors) */ long long BootImageSize(int media, unsigned int len); /** * Frees the boot catalog entries in 'boot'. If you ever called ReadBootTable, * then don't forget to call FreeBootTable! */ void FreeBootTable(boot_head *boot); /** * Reads the boot catalog into 'head'. Don't forget to call FreeBootTable! */ int ReadBootTable(readfunc *read, unsigned int sector, boot_head *head, void *udata); #ifdef __cplusplus } //extern "C" #endif #endif diff --git a/iso/libisofs/rock.h b/iso/libisofs/rock.h index ca115cca..e0b9c6d5 100644 --- a/iso/libisofs/rock.h +++ b/iso/libisofs/rock.h @@ -1,145 +1,147 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * * * From the linux kernel * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ROCK_H #define ROCK_H 1 /* These structs are used by the system-use-sharing protocol, in which the Rock Ridge extensions are embedded. It is quite possible that other extensions are present on the disk, and this is fine as long as they all use SUSP */ struct SU_SP { unsigned char magic[2]; unsigned char skip; }; struct SU_CE { char extent[8]; char offset[8]; char size[8]; }; struct SU_ER { unsigned char len_id; unsigned char len_des; unsigned char len_src; unsigned char ext_ver; char data[1]; }; struct RR_RR { char flags[1]; }; struct RR_PX { char mode[8]; char n_links[8]; char uid[8]; char gid[8]; char serno[8]; }; struct RR_PN { char dev_high[8]; char dev_low[8]; }; struct SL_component { unsigned char flags; unsigned char len; char text[1]; }; struct RR_SL { unsigned char flags; struct SL_component link; }; struct RR_NM { unsigned char flags; char name[1]; }; struct RR_CL { char location[8]; }; struct RR_PL { char location[8]; }; struct stamp { char time[7]; }; struct RR_TF { char flags; struct stamp times[1]; /* Variable number of these beasts */ }; /* Linux-specific extension for transparent decompression */ struct RR_ZF { char algorithm[2]; char parms[2]; char real_size[8]; }; /* These are the bits and their meanings for flags in the TF structure. */ #define TF_CREATE 1 #define TF_MODIFY 2 #define TF_ACCESS 4 #define TF_ATTRIBUTES 8 #define TF_BACKUP 16 #define TF_EXPIRATION 32 #define TF_EFFECTIVE 64 #define TF_LONG_FORM 128 struct rock_ridge { char signature[2]; char len; /* 711 */ char version; /* 711 */ union { struct SU_SP SP; struct SU_CE CE; struct SU_ER ER; struct RR_RR RR; struct RR_PX PX; struct RR_PN PN; struct RR_SL SL; struct RR_NM NM; struct RR_CL CL; struct RR_PL PL; struct RR_TF TF; struct RR_ZF ZF; } u; }; #define RR_PX 1 /* POSIX attributes */ #define RR_PN 2 /* POSIX devices */ #define RR_SL 4 /* Symbolic link */ #define RR_NM 8 /* Alternate Name */ #define RR_CL 16 /* Child link */ #define RR_PL 32 /* Parent link */ #define RR_RE 64 /* Relocation directory */ #define RR_TF 128 /* Timestamps */ #endif /* ROCK_H */ diff --git a/iso/qfilehack.cpp b/iso/qfilehack.cpp index 7dbb39ed..ef314070 100644 --- a/iso/qfilehack.cpp +++ b/iso/qfilehack.cpp @@ -1,45 +1,46 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "qfilehack.h" QFileHack::QFileHack() { } QFileHack::QFileHack(const QString & name) : QFile(name) { } QFileHack::~QFileHack() { } bool QFileHack::open(QFile::OpenMode m) { bool ret; #ifdef Q_OS_UNIX // m |= IO_Async; // On linux, set O_NONBLOCK, opens CD-ROMs faster #endif ret = QFile::open(m); // if (ret && isSequential() ) { // setOpenMode(m | (QFile::OpenMode)IO_Direct); // } return ret; } diff --git a/iso/qfilehack.h b/iso/qfilehack.h index 782b17a8..c25923f5 100644 --- a/iso/qfilehack.h +++ b/iso/qfilehack.h @@ -1,39 +1,40 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef QFILEHACK_H #define QFILEHACK_H // QtCore #include #include /** * Qt thinks if a file is not S_IFREG, you cannot seek in it. * It's false (what about block devices for example ?) */ class QFileHack : public QFile { public: QFileHack(); explicit QFileHack(const QString & name); ~QFileHack(); virtual bool open(QFile::OpenMode m) Q_DECL_OVERRIDE; }; #endif diff --git a/krusader/ActionMan/actionman.cpp b/krusader/ActionMan/actionman.cpp index 861108d8..d06b1b55 100644 --- a/krusader/ActionMan/actionman.cpp +++ b/krusader/ActionMan/actionman.cpp @@ -1,85 +1,86 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "actionman.h" // QtWidgets #include #include #include #include #include #include "useractionpage.h" #include "../krusader.h" #include "../UserAction/useraction.h" ActionMan::ActionMan(QWidget * parent) : QDialog(parent) { setWindowModality(Qt::WindowModal); setWindowTitle(i18n("ActionMan - Manage Your Useractions")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); userActionPage = new UserActionPage(this); mainLayout->addWidget(userActionPage); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close|QDialogButtonBox::Apply); mainLayout->addWidget(buttonBox); applyButton = buttonBox->button(QDialogButtonBox::Apply); applyButton->setEnabled(false); connect(buttonBox, SIGNAL(rejected()), SLOT(slotClose())); connect(applyButton, SIGNAL(clicked()), SLOT(slotApply())); connect(userActionPage, SIGNAL(changed()), SLOT(slotEnableApplyButton())); connect(userActionPage, SIGNAL(applied()), SLOT(slotDisableApplyButton())); exec(); krApp->updateUserActions(); } ActionMan::~ActionMan() { } void ActionMan::slotClose() { if (userActionPage->readyToQuit()) reject(); } void ActionMan::slotApply() { userActionPage->applyChanges(); } void ActionMan::slotEnableApplyButton() { applyButton->setEnabled(true); } void ActionMan::slotDisableApplyButton() { applyButton->setEnabled(false); } diff --git a/krusader/ActionMan/actionman.h b/krusader/ActionMan/actionman.h index c1b8f72e..e18d6430 100644 --- a/krusader/ActionMan/actionman.h +++ b/krusader/ActionMan/actionman.h @@ -1,48 +1,49 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ACTIONMAN_H #define ACTIONMAN_H // QtWidgets #include class UserActionPage; /** * This manages all useractions */ class ActionMan : public QDialog { Q_OBJECT public: explicit ActionMan(QWidget* parent = 0); ~ActionMan(); protected slots: void slotClose(); void slotApply(); void slotEnableApplyButton(); void slotDisableApplyButton(); private: UserActionPage* userActionPage; QPushButton *applyButton; }; #endif // ifndef ACTIONMAN_H diff --git a/krusader/ActionMan/actionproperty.cpp b/krusader/ActionMan/actionproperty.cpp index 4e542620..2133d4e2 100644 --- a/krusader/ActionMan/actionproperty.cpp +++ b/krusader/ActionMan/actionproperty.cpp @@ -1,518 +1,519 @@ /***************************************************************************** * Copyright (C) 2004-2007 Jonas Bähr * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "actionproperty.h" #include "addplaceholderpopup.h" #include "../UserAction/useraction.h" #include "../UserAction/kraction.h" #include "../krusader.h" #include "../krglobal.h" // QtWidgets #include #include #include #include #include #include #define ICON(N) KIconLoader::global()->loadIcon(N, KIconLoader::Small) ActionProperty::ActionProperty(QWidget *parent, KrAction *action) : QWidget(parent), _modified(false) { setupUi(this); if (action) { _action = action; updateGUI(_action); } ButtonAddPlaceholder->setIcon(ICON("list-add")); ButtonAddStartpath->setIcon(ICON("document-open")); // fill with all existing categories cbCategory->addItems(krUserAction->allCategories()); connect(ButtonAddPlaceholder, SIGNAL(clicked()), this, SLOT(addPlaceholder())); connect(ButtonAddStartpath, SIGNAL(clicked()), this, SLOT(addStartpath())); connect(ButtonNewProtocol, SIGNAL(clicked()), this, SLOT(newProtocol())); connect(ButtonEditProtocol, SIGNAL(clicked()), this, SLOT(editProtocol())); connect(ButtonRemoveProtocol, SIGNAL(clicked()), this, SLOT(removeProtocol())); connect(ButtonAddPath, SIGNAL(clicked()), this, SLOT(addPath())); connect(ButtonEditPath, SIGNAL(clicked()), this, SLOT(editPath())); connect(ButtonRemovePath, SIGNAL(clicked()), this, SLOT(removePath())); connect(ButtonAddMime, SIGNAL(clicked()), this, SLOT(addMime())); connect(ButtonEditMime, SIGNAL(clicked()), this, SLOT(editMime())); connect(ButtonRemoveMime, SIGNAL(clicked()), this, SLOT(removeMime())); connect(ButtonNewFile, SIGNAL(clicked()), this, SLOT(newFile())); connect(ButtonEditFile, SIGNAL(clicked()), this, SLOT(editFile())); connect(ButtonRemoveFile, SIGNAL(clicked()), this, SLOT(removeFile())); connect(KeyButtonShortcut, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(changedShortcut(QKeySequence))); // track modifications: connect(leDistinctName, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(leTitle, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(ButtonIcon, SIGNAL(iconChanged(QString)), SLOT(setModified())); connect(cbCategory, SIGNAL(currentTextChanged(QString)), SLOT(setModified())); connect(leTooltip, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(textDescription, SIGNAL(textChanged()), SLOT(setModified())); connect(leCommandline, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(leStartpath, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(chkSeparateStdError, SIGNAL(clicked()), SLOT(setModified())); connect(radioCollectOutput, SIGNAL(clicked()), SLOT(setModified())); connect(radioNormal, SIGNAL(clicked()), SLOT(setModified())); connect(radioTE, SIGNAL(clicked()), SLOT(setModified())); connect(radioTerminal, SIGNAL(clicked()), SLOT(setModified())); connect(radioLocal, SIGNAL(clicked()), SLOT(setModified())); connect(radioUrl, SIGNAL(clicked()), SLOT(setModified())); connect(KeyButtonShortcut, SIGNAL(keySequenceChanged(QKeySequence)), SLOT(setModified())); connect(chkEnabled, SIGNAL(clicked()), SLOT(setModified())); connect(leDifferentUser, SIGNAL(textChanged(QString)), SLOT(setModified())); connect(chkDifferentUser, SIGNAL(clicked()), SLOT(setModified())); connect(chkConfirmExecution, SIGNAL(clicked()), SLOT(setModified())); connect(chkSeparateStdError, SIGNAL(clicked()), SLOT(setModified())); // The modified-state of the ShowOnly-lists is tracked in the access-functions below } ActionProperty::~ActionProperty() { } void ActionProperty::changedShortcut(const QKeySequence& shortcut) { KeyButtonShortcut->setKeySequence(shortcut); } void ActionProperty::clear() { _action = 0; // This prevents the changed-signal from being emitted during the GUI-update _modified = true; // The real state is set at the end of this function. leDistinctName->clear(); cbCategory->clearEditText(); leTitle->clear(); leTooltip->clear(); textDescription->clear(); leCommandline->clear(); leStartpath->clear(); KeyButtonShortcut->clearKeySequence(); lbShowonlyProtocol->clear(); lbShowonlyPath->clear(); lbShowonlyMime->clear(); lbShowonlyFile->clear(); chkSeparateStdError->setChecked(false); radioNormal->setChecked(true); radioLocal->setChecked(true); chkEnabled->setChecked(true); chkConfirmExecution->setChecked(false); ButtonIcon->resetIcon(); leDifferentUser->clear(); chkDifferentUser->setChecked(false); setModified(false); } void ActionProperty::updateGUI(KrAction *action) { if (action) _action = action; if (! _action) return; // This prevents the changed-signal from being emitted during the GUI-update. _modified = true; // The real state is set at the end of this function. leDistinctName->setText(_action->objectName()); cbCategory->lineEdit()->setText(_action->category()); leTitle->setText(_action->text()); leTooltip->setText(_action->toolTip()); textDescription->setText(_action->whatsThis()); leCommandline->setText(_action->command()); leCommandline->home(false); leStartpath->setText(_action->startpath()); KeyButtonShortcut->setKeySequence(_action->shortcut()); lbShowonlyProtocol->clear(); lbShowonlyProtocol->addItems(_action->showonlyProtocol()); lbShowonlyPath->clear(); lbShowonlyPath->addItems(_action->showonlyPath()); lbShowonlyMime->clear(); lbShowonlyMime->addItems(_action->showonlyMime()); lbShowonlyFile->clear(); lbShowonlyFile->addItems(_action->showonlyFile()); chkSeparateStdError->setChecked(false); switch (_action->execType()) { case KrAction::CollectOutputSeparateStderr: chkSeparateStdError->setChecked(true); radioCollectOutput->setChecked(true); break; case KrAction::CollectOutput: radioCollectOutput->setChecked(true); break; case KrAction::Terminal: radioTerminal->setChecked(true); break; case KrAction::RunInTE: radioTE->setChecked(true); break; default: // case KrAction::Normal: radioNormal->setChecked(true); break; } if (_action->acceptURLs()) radioUrl->setChecked(true); else radioLocal->setChecked(true); chkEnabled->setChecked(_action->isVisible()); chkConfirmExecution->setChecked(_action->confirmExecution()); if (! _action->icon().isNull()) ButtonIcon->setIcon(_action->icon()); else ButtonIcon->resetIcon(); leDifferentUser->setText(_action->user()); if (_action->user().isEmpty()) chkDifferentUser->setChecked(false); else chkDifferentUser->setChecked(true); setModified(false); } void ActionProperty::updateAction(KrAction *action) { if (action) _action = action; if (! _action) return; if (_action->category() != cbCategory->currentText()) { _action->setCategory(cbCategory->currentText()); // Update the category-list cbCategory->clear(); cbCategory->addItems(krUserAction->allCategories()); cbCategory->lineEdit()->setText(_action->category()); } _action->setObjectName(leDistinctName->text()); _action->setText(leTitle->text()); _action->setToolTip(leTooltip->text()); _action->setWhatsThis(textDescription->toPlainText()); _action->setCommand(leCommandline->text()); _action->setStartpath(leStartpath->text()); _action->setShortcut(KeyButtonShortcut->keySequence()); QStringList list; for (int i1 = 0; i1 != lbShowonlyProtocol->count(); i1++) { QListWidgetItem* lbi = lbShowonlyProtocol->item(i1); list << lbi->text(); } _action->setShowonlyProtocol(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyPath->count(); i1++) { QListWidgetItem* lbi = lbShowonlyPath->item(i1); list << lbi->text(); } _action->setShowonlyPath(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyMime->count(); i1++) { QListWidgetItem* lbi = lbShowonlyMime->item(i1); list << lbi->text(); } _action->setShowonlyMime(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyFile->count(); i1++) { QListWidgetItem* lbi = lbShowonlyFile->item(i1); list << lbi->text(); } _action->setShowonlyFile(list); if (radioCollectOutput->isChecked() && chkSeparateStdError->isChecked()) _action->setExecType(KrAction::CollectOutputSeparateStderr); else if (radioCollectOutput->isChecked() && ! chkSeparateStdError->isChecked()) _action->setExecType(KrAction::CollectOutput); else if (radioTerminal->isChecked()) _action->setExecType(KrAction::Terminal); else if (radioTE->isChecked()) _action->setExecType(KrAction::RunInTE); else _action->setExecType(KrAction::Normal); if (radioUrl->isChecked()) _action->setAcceptURLs(true); else _action->setAcceptURLs(false); _action->setEnabled(chkEnabled->isChecked()); _action->setVisible(chkEnabled->isChecked()); _action->setConfirmExecution(chkConfirmExecution->isChecked()); _action->setIcon(QIcon::fromTheme(ButtonIcon->icon())); _action->setIconName(ButtonIcon->icon()); _action->setUser(leDifferentUser->text()); setModified(false); } void ActionProperty::addPlaceholder() { AddPlaceholderPopup popup(this); QString exp = popup.getPlaceholder(mapToGlobal( QPoint( ButtonAddPlaceholder->pos().x() + ButtonAddPlaceholder->width() + 6, // 6 is the default margin ButtonAddPlaceholder->pos().y() ) )); leCommandline->insert(exp); } void ActionProperty::addStartpath() { QString folder = QFileDialog::getExistingDirectory(this); if (!folder.isEmpty()) { leStartpath->setText(folder); } } void ActionProperty::newProtocol() { bool ok; QString currentText; if (lbShowonlyProtocol->currentItem()) currentText = lbShowonlyProtocol->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New protocol"), i18n("Set a protocol:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyProtocol->addItems(text.split(';')); setModified(); } } void ActionProperty::editProtocol() { if (lbShowonlyProtocol->currentItem() == 0) return; bool ok; QString currentText = lbShowonlyProtocol->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit Protocol"), i18n("Set another protocol:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyProtocol->currentItem()->setText(text); setModified(); } } void ActionProperty::removeProtocol() { if (lbShowonlyProtocol->currentItem() != 0) { delete lbShowonlyProtocol->currentItem(); setModified(); } } void ActionProperty::addPath() { QString folder = QFileDialog::getExistingDirectory(this); if (!folder.isEmpty()) { lbShowonlyPath->addItem(folder); setModified(); } } void ActionProperty::editPath() { if (lbShowonlyPath->currentItem() == 0) return; bool ok; QString currentText = lbShowonlyPath->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit Path"), i18n("Set another path:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyPath->currentItem()->setText(text); setModified(); } } void ActionProperty::removePath() { if (lbShowonlyPath->currentItem() != 0) { delete lbShowonlyPath->currentItem(); setModified(); } } void ActionProperty::addMime() { bool ok; QString currentText; if (lbShowonlyMime->currentItem()) currentText = lbShowonlyMime->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New MIME Type"), i18n("Set a MIME type:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyMime->addItems(text.split(';')); setModified(); } } void ActionProperty::editMime() { if (lbShowonlyMime->currentItem() == 0) return; bool ok; QString currentText = lbShowonlyMime->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit MIME Type"), i18n("Set another MIME type:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyMime->currentItem()->setText(text); setModified(); } } void ActionProperty::removeMime() { if (lbShowonlyMime->currentItem() != 0) { delete lbShowonlyMime->currentItem(); setModified(); } } void ActionProperty::newFile() { bool ok; QString currentText; if (lbShowonlyFile->currentItem()) currentText = lbShowonlyFile->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New File Name"), i18n("Set a file name:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyFile->addItems(text.split(';')); setModified(); } } void ActionProperty::editFile() { if (lbShowonlyFile->currentItem() == 0) return; bool ok; QString currentText = lbShowonlyFile->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit File Name"), i18n("Set another file name:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyFile->currentItem()->setText(text); setModified(); } } void ActionProperty::removeFile() { if (lbShowonlyFile->currentItem() != 0) { delete lbShowonlyFile->currentItem(); setModified(); } } bool ActionProperty::validProperties() { if (leDistinctName->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Please set a unique name for the useraction")); leDistinctName->setFocus(); return false; } if (leTitle->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Please set a title for the menu entry")); leTitle->setFocus(); return false; } if (leCommandline->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Command line is empty")); leCommandline->setFocus(); return false; } if (leDistinctName->isEnabled()) if (krApp->actionCollection()->action(leDistinctName->text())) { KMessageBox::error(this, i18n("There already is an action with this name.\n" "If you do not have such a useraction the name is used by Krusader for an internal action.") ); leDistinctName->setFocus(); return false; } return true; } void ActionProperty::setModified(bool m) { if (m && !_modified) { // emit only when the state _changes_to_true_, emit changed(); } _modified = m; } diff --git a/krusader/ActionMan/actionproperty.h b/krusader/ActionMan/actionproperty.h index 4a482986..3169884b 100644 --- a/krusader/ActionMan/actionproperty.h +++ b/krusader/ActionMan/actionproperty.h @@ -1,155 +1,156 @@ /***************************************************************************** * Copyright (C) 2004-2007 Jonas Bähr * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ACTIONPROPERTY_H #define ACTIONPROPERTY_H #include "ui_actionproperty.h" class KrAction; /** * Use this widget where ever you need to manipulate a UserAction */ class ActionProperty : public QWidget, public Ui::ActionProperty { Q_OBJECT public: explicit ActionProperty(QWidget *parent = 0, KrAction *action = 0); ~ActionProperty(); /** * @return the currently displayed action */ KrAction* action() { return _action; }; /** * This inits the widget with the actions properties. * If no action is provided, the last used will be taken! * It also resets the changed() state. * @param action the action which should be displayd */ void updateGUI(KrAction *action = 0); /** * This writes the displayed properties back into the action. * If no action is provided, the last used will be taken! * It also resets the changed() state. * @param action the action which should be manipulated */ void updateAction(KrAction *action = 0); /** * clears everything */ void clear(); /** * @return true if all properties got valid values */ bool validProperties(); /** * @return true if any property got changed */ bool isModified() { return _modified; }; signals: /** * emitted when any actionproperty changed. This signal is only emitted when * the _modified attribute changes to true. If there are changes made and * _modified is already true, no signal is emitted! */ void changed(); protected slots: void setModified(bool m = true); /** * executes the AddPlaceholderPopup */ void addPlaceholder(); /** * asks for an existing path */ void addStartpath(); /** * (availability) asks for a new protocol */ void newProtocol(); /** * (availability) changes a protocol of the list */ void editProtocol(); /** * (availability) removes a protocol from the list */ void removeProtocol(); /** * (availability) asks for a new path */ void addPath(); /** * (availability) edits a path of the list */ void editPath(); /** * (availability) removes a path from the list */ void removePath(); /** * (availability) asks for a new mime-type */ void addMime(); /** * (availability) changes a mime-type of the list */ void editMime(); /** * (availability) removes a mime-type from the list */ void removeMime(); /** * (availability) asks for a new file-filter */ void newFile(); /** * (availability) edits a file-filter of the list */ void editFile(); /** * (availability) removes a file-filter from the lsit */ void removeFile(); private: KrAction *_action; bool _modified; private slots: /** * This updates the ShortcutButton * @internal */ void changedShortcut(const QKeySequence& shortcut); }; #endif diff --git a/krusader/ActionMan/addplaceholderpopup.cpp b/krusader/ActionMan/addplaceholderpopup.cpp index b0c9cee4..5bf4b3c3 100644 --- a/krusader/ActionMan/addplaceholderpopup.cpp +++ b/krusader/ActionMan/addplaceholderpopup.cpp @@ -1,700 +1,701 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * - * Copyright (C) Jonas Bähr * + * Copyright (C) 2004 Jonas Bähr * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "addplaceholderpopup.h" // for ParameterDialog #include "../krglobal.h" // for konfig-access #include "../BookMan/krbookmarkbutton.h" #include "../GUI/profilemanager.h" // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACTIVE_MASK 0x0100 #define OTHER_MASK 0x0200 #define LEFT_MASK 0x0400 #define RIGHT_MASK 0x0800 #define INDEPENDENT_MASK 0x1000 #define EXECUTABLE_ID 0xFFFF AddPlaceholderPopup::AddPlaceholderPopup(QWidget *parent) : QMenu(parent) { _activeSub = new QMenu(i18n("Active panel"), this); _otherSub = new QMenu(i18n("Other panel"), this); _leftSub = new QMenu(i18n("Left panel"), this); _rightSub = new QMenu(i18n("Right panel"), this); _independentSub = new QMenu(i18n("Panel independent"), this); addMenu(_activeSub); addMenu(_otherSub); addMenu(_leftSub); addMenu(_rightSub); addMenu(_independentSub); QAction *chooseExecAct = _independentSub->addAction(i18n("Choose executable...")); chooseExecAct->setData(QVariant(EXECUTABLE_ID)); _independentSub->addSeparator(); // read the expressions array from the user menu and populate menus Expander expander; for (int i = 0; i < expander.placeholderCount(); ++i) { if (expander.placeholder(i)->expression().isEmpty()) { if (expander.placeholder(i)->needPanel()) { _activeSub->addSeparator(); _otherSub->addSeparator(); _leftSub->addSeparator(); _rightSub->addSeparator(); } else _independentSub->addSeparator(); } else { QAction * action; if (expander.placeholder(i)->needPanel()) { action = _activeSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | ACTIVE_MASK)); action = _otherSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | OTHER_MASK)); action = _leftSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | LEFT_MASK)); action = _rightSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | RIGHT_MASK)); } else { action = _independentSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | INDEPENDENT_MASK)); } } } } QString AddPlaceholderPopup::getPlaceholder(const QPoint& pos) { QAction *res = exec(pos); if (res == 0) return QString(); // add the selected flag to the command line if (res->data().toInt() == EXECUTABLE_ID) { // did the user need an executable ? // select an executable QString filename = QFileDialog::getOpenFileName(this); if (!filename.isEmpty()) { return filename + ' '; // with extra space // return filename; // without extra space } } else { // user selected something from the menus Expander expander; const exp_placeholder* currentPlaceholder = expander.placeholder(res->data().toInt() & ~(ACTIVE_MASK | OTHER_MASK | LEFT_MASK | RIGHT_MASK | INDEPENDENT_MASK)); // if ( ¤tPlaceholder->expFunc == 0 ) { // KMessageBox::sorry( this, "BOFH Excuse #93:\nFeature not yet implemented" ); // return QString(); // } ParameterDialog* parameterDialog = new ParameterDialog(currentPlaceholder, this); QString panel, parameter = parameterDialog->getParameter(); delete parameterDialog; // indicate the panel with 'a' 'o', 'l', 'r' or '_'. if (res->data().toInt() & ACTIVE_MASK) panel = 'a'; else if (res->data().toInt() & OTHER_MASK) panel = 'o'; else if (res->data().toInt() & LEFT_MASK) panel = 'l'; else if (res->data().toInt() & RIGHT_MASK) panel = 'r'; else if (res->data().toInt() & INDEPENDENT_MASK) panel = '_'; //return '%' + panel + currentPlaceholder->expression() + parameter + "% "; // with extra space return '%' + panel + currentPlaceholder->expression() + parameter + '%'; // without extra space } return QString(); } //////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////// ParameterDialog //////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// ParameterDialog::ParameterDialog(const exp_placeholder* currentPlaceholder, QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("User Action Parameter Dialog")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); _parameter.clear(); _parameterCount = currentPlaceholder->parameterCount(); QWidget *page = new QWidget(this); mainLayout->addWidget(page); QVBoxLayout* layout = new QVBoxLayout(page); layout->setSpacing(11); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n("This placeholder allows some parameter:"), page)); for (int i = 0; i < _parameterCount; ++i) { if (currentPlaceholder->parameter(i).preset() == "__placeholder") _parameter.append(new ParameterPlaceholder(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__yes") _parameter.append(new ParameterYes(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__no") _parameter.append(new ParameterNo(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__file") _parameter.append(new ParameterFile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset().indexOf("__choose") != -1) _parameter.append(new ParameterChoose(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__select") _parameter.append(new ParameterSelect(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__goto") _parameter.append(new ParameterGoto(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__syncprofile") _parameter.append(new ParameterSyncprofile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__searchprofile") _parameter.append(new ParameterSearch(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__panelprofile") _parameter.append(new ParameterPanelprofile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset().indexOf("__int") != -1) _parameter.append(new ParameterInt(currentPlaceholder->parameter(i), page)); else _parameter.append(new ParameterText(currentPlaceholder->parameter(i), page)); layout->addWidget(_parameter.last()); } QFrame * line = new QFrame(page); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); layout->addWidget(line); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(okButton, SIGNAL(clicked()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(reset())); } QString ParameterDialog::getParameter() { if (_parameterCount == 0) // meaning no parameters return QString(); if (exec() == -1) return QString(); int lastParameter = _parameterCount; while (--lastParameter > -1) { if (_parameter[ lastParameter ]->text() != _parameter[ lastParameter ]->preset() || _parameter[ lastParameter ]->necessary()) break; } if (lastParameter < 0) // all parameters have default-values return QString(); QString parameter; for (int i = 0; i <= lastParameter; ++i) { if (i > 0) parameter += ", "; parameter += '\"' + _parameter[ i ]->text().replace('\"', "\\\"") + '\"'; } return '(' + parameter + ')'; } void ParameterDialog::reset() { for (int i = 0; i < _parameterCount; ++i) _parameter[ i ]->reset(); } void ParameterDialog::slotOk() { bool valid = true; for (int i = 0; i < _parameterCount; ++i) { if (_parameter[ i ]->necessary() && ! _parameter[ i ]->valid()) valid = false; } if (valid) accept(); } ///////////// ParameterText ParameterText::ParameterText(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_lineEdit = new KLineEdit(parameter.preset(), this)); _preset = parameter.preset(); } QString ParameterText::text() { return _lineEdit->text(); } QString ParameterText::preset() { return _preset; } void ParameterText::reset() { _lineEdit->setText(_preset); } bool ParameterText::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } ///////////// ParameterPlaceholder ParameterPlaceholder::ParameterPlaceholder(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); layout->addWidget(hboxWidget); QHBoxLayout * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); hbox->addWidget(_lineEdit); _button = new QToolButton(hboxWidget); _button->setIcon(QIcon::fromTheme("list-add")); hbox->addWidget(_button); connect(_button, SIGNAL(clicked()), this, SLOT(addPlaceholder())); } QString ParameterPlaceholder::text() { return _lineEdit->text(); } QString ParameterPlaceholder::preset() { return QString(); } void ParameterPlaceholder::reset() { _lineEdit->setText(QString()); } bool ParameterPlaceholder::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterPlaceholder::addPlaceholder() { AddPlaceholderPopup* popup = new AddPlaceholderPopup(this); QString exp = popup->getPlaceholder(mapToGlobal(QPoint(_button->pos().x() + _button->width() + 6, _button->pos().y() + _button->height() / 2))); _lineEdit->insert(exp); delete popup; } ///////////// ParameterYes ParameterYes::ParameterYes(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_checkBox = new QCheckBox(i18n(parameter.description().toUtf8()), this)); _checkBox->setChecked(true); } QString ParameterYes::text() { if (_checkBox->isChecked()) return QString(); else return "No"; } QString ParameterYes::preset() { return QString(); } void ParameterYes::reset() { _checkBox->setChecked(true); } bool ParameterYes::valid() { return true; } ///////////// ParameterNo ParameterNo::ParameterNo(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_checkBox = new QCheckBox(i18n(parameter.description().toUtf8()), this)); _checkBox->setChecked(false); } QString ParameterNo::text() { if (_checkBox->isChecked()) return "Yes"; else return QString(); } QString ParameterNo::preset() { return QString(); } void ParameterNo::reset() { _checkBox->setChecked(false); } bool ParameterNo::valid() { return true; } ///////////// ParameterFile ParameterFile::ParameterFile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); layout->addWidget(hboxWidget); QHBoxLayout * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); hbox->addWidget(_lineEdit); _button = new QToolButton(hboxWidget); hbox->addWidget(_button); _button->setIcon(QIcon::fromTheme("document-open")); connect(_button, SIGNAL(clicked()), this, SLOT(addFile())); } QString ParameterFile::text() { return _lineEdit->text(); } QString ParameterFile::preset() { return QString(); } void ParameterFile::reset() { _lineEdit->setText(QString()); } bool ParameterFile::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterFile::addFile() { QString filename = QFileDialog::getOpenFileName(this); _lineEdit->insert(filename); } ///////////// ParameterChoose ParameterChoose::ParameterChoose(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(parameter.preset().section(':', 1).split(';')); } QString ParameterChoose::text() { return _combobox->currentText(); } QString ParameterChoose::preset() { return _combobox->itemText(0); } void ParameterChoose::reset() { _combobox->setCurrentIndex(0); } bool ParameterChoose::valid() { return true; } ///////////// ParameterSelect ParameterSelect::ParameterSelect(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->setEditable(true); KConfigGroup group(krConfig, "Private"); QStringList lst = group.readEntry("Predefined Selections", QStringList()); if (lst.size() > 0) _combobox->addItems(lst); _combobox->lineEdit()->setText("*"); } QString ParameterSelect::text() { return _combobox->currentText(); } QString ParameterSelect::preset() { return "*"; } void ParameterSelect::reset() { _combobox->lineEdit()->setText("*"); } bool ParameterSelect::valid() { return true; } ///////////// ParameterGoto ParameterGoto::ParameterGoto(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); QHBoxLayout * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); _lineEdit->setCompletionObject(new KUrlCompletion(KUrlCompletion::DirCompletion)); hbox->addWidget(_lineEdit); _dirButton = new QToolButton(hboxWidget); hbox->addWidget(_dirButton); _dirButton->setIcon(QIcon::fromTheme("document-open")); connect(_dirButton, SIGNAL(clicked()), this, SLOT(setDir())); _placeholderButton = new QToolButton(hboxWidget); _placeholderButton->setIcon(QIcon::fromTheme("list-add")); hbox->addWidget(_placeholderButton); connect(_placeholderButton, SIGNAL(clicked()), this, SLOT(addPlaceholder())); layout->addWidget(hboxWidget); } QString ParameterGoto::text() { return _lineEdit->text(); } QString ParameterGoto::preset() { return QString(); } void ParameterGoto::reset() { _lineEdit->setText(QString()); } bool ParameterGoto::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterGoto::setDir() { QString folder = QFileDialog::getExistingDirectory(this); _lineEdit->setText(folder); } void ParameterGoto::addPlaceholder() { AddPlaceholderPopup* popup = new AddPlaceholderPopup(this); QString exp = popup->getPlaceholder(mapToGlobal(QPoint(_placeholderButton->pos().x() + _placeholderButton->width() + 6, _placeholderButton->pos().y() + _placeholderButton->height() / 2))); _lineEdit->insert(exp); delete popup; } ///////////// ParameterSyncprofile ParameterSyncprofile::ParameterSyncprofile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("SynchronizerProfile")); } QString ParameterSyncprofile::text() { return _combobox->currentText(); } QString ParameterSyncprofile::preset() { return _combobox->itemText(0); } void ParameterSyncprofile::reset() { _combobox->setCurrentIndex(0); } bool ParameterSyncprofile::valid() { return true; } ///////////// ParameterSearch ParameterSearch::ParameterSearch(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("SearcherProfile")); } QString ParameterSearch::text() { return _combobox->currentText(); } QString ParameterSearch::preset() { return _combobox->itemText(0); } void ParameterSearch::reset() { _combobox->setCurrentIndex(0); } bool ParameterSearch::valid() { return true; } ///////////// ParameterPanelprofile ParameterPanelprofile::ParameterPanelprofile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("Panel")); } QString ParameterPanelprofile::text() { return _combobox->currentText(); } QString ParameterPanelprofile::preset() { return _combobox->itemText(0); } void ParameterPanelprofile::reset() { _combobox->setCurrentIndex(0); } bool ParameterPanelprofile::valid() { return true; } ///////////// ParameterInt ParameterInt::ParameterInt(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_spinbox = new QSpinBox(this)); QStringList para = parameter.preset().section(':', 1).split(';'); _spinbox->setMinimum(para[0].toInt()); _spinbox->setMaximum(para[1].toInt()); _spinbox->setSingleStep(para[2].toInt()); _spinbox->setValue(para[3].toInt()); _default = _spinbox->value(); } QString ParameterInt::text() { return _spinbox->text(); } QString ParameterInt::preset() { return QString("%1").arg(_default); } void ParameterInt::reset() { return _spinbox->setValue(_default); } bool ParameterInt::valid() { return true; } diff --git a/krusader/ActionMan/addplaceholderpopup.h b/krusader/ActionMan/addplaceholderpopup.h index 2d61f8ea..935faa81 100644 --- a/krusader/ActionMan/addplaceholderpopup.h +++ b/krusader/ActionMan/addplaceholderpopup.h @@ -1,346 +1,347 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * - * Copyright (C) Jonas Bähr * + * Copyright (C) 2004 Jonas Bähr * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef ADDPLACEHOLDERPOPUP_H #define ADDPLACEHOLDERPOPUP_H // QtCore #include #include // QtWidgets #include #include #include "../UserAction/expander.h" class KLineEdit; class QToolButton; class QCheckBox; class KComboBox; class QSpinBox; /** * This reads Expander::placeholder[] and * fills a popup for easy access to the UserAction Placeholder */ class AddPlaceholderPopup : public QMenu { public: explicit AddPlaceholderPopup(QWidget *parent); /** * Use this to exec the popup. * @param pos Position where the popup should appear * @return the expression which can be placed in the UserAction commandline */ QString getPlaceholder(const QPoint& pos); protected: /** * This is calles when a Placeholder got parameter. * @param currentPlaceholder A pointer to the Placeholder the user has chosen * @return a parameter-string */ QString getParameter(exp_placeholder* currentPlaceholder); private: QMenu *_activeSub, *_otherSub, *_leftSub, *_rightSub, *_independentSub; }; //////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Parameter Widgets /////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// /** * abstract baseclass for all Parameter widgets */ class ParameterBase : public QWidget { public: inline ParameterBase(const exp_parameter& parameter, QWidget* parent) : QWidget(parent) { _necessary = parameter.necessary(); } /** * @return the text for the parameter */ virtual QString text() = 0; /** * @return the default of the parameter */ virtual QString preset() = 0; /** * re-init the parameter with the default */ virtual void reset() = 0; /** * @return true if the Parameter as a valid value */ virtual bool valid() = 0; /** * @return true if the Placeholder really needs this parameter */ inline bool necessary() { return _necessary; } private: bool _necessary; }; /** * The simple Parameter widgets: a line-edit with the description above * used by default */ class ParameterText : public ParameterBase { public: ParameterText(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KLineEdit * _lineEdit; QString _preset; }; /** * A line-edit with the "addPlaceholder"-button * used with default = "__placeholder" */ class ParameterPlaceholder : public ParameterBase { Q_OBJECT public: ParameterPlaceholder(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KLineEdit * _lineEdit; QToolButton* _button; private slots: void addPlaceholder(); }; /** * A Checkbox, default: checked; retuns "No" if unchecked * used with default = "__yes" */ class ParameterYes : public ParameterBase { public: ParameterYes(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: QCheckBox* _checkBox; }; /** * A Checkbox, default: unchecked; retuns "Yes" if checked * used with default = "__no" */ class ParameterNo : public ParameterBase { public: ParameterNo(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: QCheckBox* _checkBox; }; /** * A line-edit with the "file open"-button * used with default = "__file" */ class ParameterFile : public ParameterBase { Q_OBJECT public: ParameterFile(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KLineEdit * _lineEdit; QToolButton* _button; private slots: void addFile(); }; /** * A ComboBox with the description above * used with default = "__choose:item1;item2;..." */ class ParameterChoose : public ParameterBase { public: ParameterChoose(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KComboBox * _combobox; }; /** * An editable ComboBox with the predifined selections * used with default = "__select" */ class ParameterSelect : public ParameterBase { public: ParameterSelect(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KComboBox * _combobox; }; /** * A line-edit with a "choose dir"- and a bookmark-button * used with default = "__goto" */ class ParameterGoto : public ParameterBase { Q_OBJECT public: ParameterGoto(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KLineEdit * _lineEdit; QToolButton* _dirButton, *_placeholderButton; private slots: void setDir(); void addPlaceholder(); }; /** * A ComboBox with all profiles available for the Synchronizer * used with default = "__syncprofile" */ class ParameterSyncprofile : public ParameterBase { public: ParameterSyncprofile(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KComboBox * _combobox; }; /** * A ComboBox with all profiles available for the panels * used with default = "__panelprofile" */ class ParameterPanelprofile : public ParameterBase { public: ParameterPanelprofile(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KComboBox * _combobox; }; /** * A ComboBox with all profiles available for the Searchmodule * used with default = "__searchprofile" */ class ParameterSearch : public ParameterBase { public: ParameterSearch(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: KComboBox * _combobox; }; /** * A SpinBox for integer * used with default = "__int:min;max;step;value" */ class ParameterInt : public ParameterBase { public: ParameterInt(const exp_parameter& parameter, QWidget* parent); QString text(); QString preset(); void reset(); bool valid(); private: QSpinBox * _spinbox; int _default; }; //////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////// ParameterDialog //////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// /** * Opens a dialog for the parameter. Depending on the default (preset) a differend widget is used. * See Parameter-Classes for details */ class ParameterDialog : public QDialog { Q_OBJECT public: ParameterDialog(const exp_placeholder* currentPlaceholder, QWidget *parent); /** * Use this to execute the dialog. * @return a QString with all paremeters; omitting the optional ones if they have the default-value. */ QString getParameter(); private: typedef QList ParameterList; ParameterList _parameter; int _parameterCount; private slots: void reset(); void slotOk(); }; #endif // ADDPLACEHOLDERPOPUP_H diff --git a/krusader/ActionMan/useractionlistview.cpp b/krusader/ActionMan/useractionlistview.cpp index 682cf0b1..c849df20 100644 --- a/krusader/ActionMan/useractionlistview.cpp +++ b/krusader/ActionMan/useractionlistview.cpp @@ -1,253 +1,254 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "useractionlistview.h" // QtCore #include // QtXml #include #include #include #include "../krglobal.h" #include "../UserAction/kraction.h" #include "../UserAction/useraction.h" #define COL_TITLE 0 // UserActionListView UserActionListView::UserActionListView(QWidget * parent) : KrTreeWidget(parent) { setHeaderLabel(i18n("Title")); setRootIsDecorated(true); setSelectionMode(QAbstractItemView::ExtendedSelection); // normaly select single items but one may use Ctrl or Shift to select multiple setSortingEnabled(true); sortItems(COL_TITLE, Qt::AscendingOrder); connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(slotCurrentItemChanged(QTreeWidgetItem*))); update(); } UserActionListView::~UserActionListView() { } QSize UserActionListView::sizeHint() const { return QSize(200, 400); } void UserActionListView::update() { clear(); UserAction::KrActionList list = krUserAction->actionList(); QListIterator it(list); while (it.hasNext()) insertAction(it.next()); } void UserActionListView::update(KrAction* action) { UserActionListViewItem* item = findActionItem(action); if (item) { // deleting & re-inserting is _much_easyer then tracking all possible cases of category changes! bool current = (item == currentItem()); bool selected = item->isSelected(); delete item; item = insertAction(action); if (current) setCurrentItem(item); if (selected) item->setSelected(true); } } UserActionListViewItem* UserActionListView::insertAction(KrAction* action) { if (! action) return 0; UserActionListViewItem* item; if (action->category().isEmpty()) item = new UserActionListViewItem(this, action); else { QTreeWidgetItem* categoryItem = findCategoryItem(action->category()); if (! categoryItem) { categoryItem = new QTreeWidgetItem(this); // create the new category item it not already present categoryItem->setText(0, action->category()); categoryItem->setFlags(Qt::ItemIsEnabled); } item = new UserActionListViewItem(categoryItem, action); } item->setAction(action); return item; } QTreeWidgetItem* UserActionListView::findCategoryItem(const QString& category) { QTreeWidgetItemIterator it(this); while (*it) { if ((*it)->text(COL_TITLE) == category && !((*it)->flags() & Qt::ItemIsSelectable)) return *it; it++; } return 0; } UserActionListViewItem* UserActionListView::findActionItem(const KrAction* action) { QTreeWidgetItemIterator it(this); while (*it) { if (UserActionListViewItem* item = dynamic_cast(*it)) { if (item->action() == action) return item; } it++; } return 0; } KrAction * UserActionListView::currentAction() const { if (UserActionListViewItem* item = dynamic_cast(currentItem())) return item->action(); else return 0; } void UserActionListView::setCurrentAction(const KrAction* action) { UserActionListViewItem* item = findActionItem(action); if (item) { setCurrentItem(item); } } void UserActionListView::setFirstActionCurrent() { QTreeWidgetItemIterator it(this); while (*it) { if (UserActionListViewItem* item = dynamic_cast(*it)) { setCurrentItem(item); break; } it++; } } void UserActionListView::slotCurrentItemChanged(QTreeWidgetItem* item) { if (! item) return; scrollTo(indexOf(item)); } QDomDocument UserActionListView::dumpSelectedActions(QDomDocument* mergeDoc) const { QList list = selectedItems(); QDomDocument doc; if (mergeDoc) doc = *mergeDoc; else doc = UserAction::createEmptyDoc(); QDomElement root = doc.documentElement(); for (int i = 0; i < list.size(); ++i) { QTreeWidgetItem* item = list.at(i); if (UserActionListViewItem* actionItem = dynamic_cast(item)) root.appendChild(actionItem->action()->xmlDump(doc)); } return doc; } void UserActionListView::removeSelectedActions() { QList list = selectedItems(); for (int i = 0; i < list.size(); ++i) { QTreeWidgetItem* item = list.at(i); if (UserActionListViewItem* actionItem = dynamic_cast(item)) { delete actionItem->action(); // remove the action itself delete actionItem; // remove the action from the list } // if } } // UserActionListViewItem UserActionListViewItem::UserActionListViewItem(QTreeWidget* view, KrAction* action) : QTreeWidgetItem(view) { setAction(action); } UserActionListViewItem::UserActionListViewItem(QTreeWidgetItem* item, KrAction * action) : QTreeWidgetItem(item) { setAction(action); } UserActionListViewItem::~UserActionListViewItem() { } void UserActionListViewItem::setAction(KrAction * action) { if (! action) return; _action = action; update(); } KrAction * UserActionListViewItem::action() const { return _action; } void UserActionListViewItem::update() { if (! _action) return; if (! _action->icon().isNull()) setIcon(COL_TITLE, KIconLoader::global()->loadIcon(_action->iconName(), KIconLoader::Small)); setText(COL_TITLE, _action->text()); } bool UserActionListViewItem::operator<(const QTreeWidgetItem &other) const { // FIXME some how this only produces bullshit :-/ // if ( i->text( COL_NAME ).isEmpty() ) { // categories only have titles // //qDebug() << "this->title: " << text(COL_TITLE) << " |=| i->title: " << i->text(COL_TITLE); // return ( ascending ? -1 : 1 ); // <0 means this is smaller then i // } // else return QTreeWidgetItem::operator<(other); } diff --git a/krusader/ActionMan/useractionlistview.h b/krusader/ActionMan/useractionlistview.h index 0a0f4ba4..39bba628 100644 --- a/krusader/ActionMan/useractionlistview.h +++ b/krusader/ActionMan/useractionlistview.h @@ -1,85 +1,86 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef USERACTIONLISTVIEW_H #define USERACTIONLISTVIEW_H #include "../GUI/krtreewidget.h" class KrAction; class QString; class UserActionListViewItem; class QDomDocument; class UserActionListView : public KrTreeWidget { Q_OBJECT public: explicit UserActionListView(QWidget* parent = 0); ~UserActionListView(); virtual QSize sizeHint() const Q_DECL_OVERRIDE; void update(); void update(KrAction* action); UserActionListViewItem* insertAction(KrAction* action); KrAction* currentAction() const; void setCurrentAction(const KrAction*); QDomDocument dumpSelectedActions(QDomDocument* mergeDoc = 0) const; void removeSelectedActions(); /** * makes the first action in the list current */ void setFirstActionCurrent(); /** * makes @e item current and ensures its visibility */ protected slots: void slotCurrentItemChanged(QTreeWidgetItem*); protected: QTreeWidgetItem* findCategoryItem(const QString& category); UserActionListViewItem* findActionItem(const KrAction* action); }; class UserActionListViewItem : public QTreeWidgetItem { public: UserActionListViewItem(QTreeWidget* view, KrAction* action); UserActionListViewItem(QTreeWidgetItem* item, KrAction* action); ~UserActionListViewItem(); void setAction(KrAction* action); KrAction* action() const; void update(); /** * This reimplements qt's compare-function in order to have categories on the top of the list */ virtual bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE; private: KrAction* _action; }; #endif diff --git a/krusader/ActionMan/useractionpage.cpp b/krusader/ActionMan/useractionpage.cpp index 63f37ae8..38b74e7b 100644 --- a/krusader/ActionMan/useractionpage.cpp +++ b/krusader/ActionMan/useractionpage.cpp @@ -1,354 +1,355 @@ /***************************************************************************** * Copyright (C) 2006 Shie Erlich * * Copyright (C) 2006 Rafi Yanai * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "useractionpage.h" // QtWidgets #include #include #include #include #include #include // QtGui #include // QtXml #include #include #include #include #include #include #include "actionproperty.h" #include "useractionlistview.h" #include "../UserAction/useraction.h" #include "../UserAction/kraction.h" #include "../krusader.h" #include "../krglobal.h" #define ICON(N) KIconLoader::global()->loadIcon(N, KIconLoader::Toolbar) //This is the filter in the QFileDialog of Import/Export: static const char* FILE_FILTER = I18N_NOOP("*.xml|XML files\n*|All files"); UserActionPage::UserActionPage(QWidget* parent) : QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(6); // 0px margin, 6px item-spacing // ======== pseudo-toolbar start ======== QHBoxLayout* toolbarLayout = new QHBoxLayout; // neither margin nor spacing for the toolbar with autoRaise toolbarLayout->setSpacing(0); toolbarLayout->setContentsMargins(0, 0, 0, 0); newButton = new QToolButton(this); newButton->setIcon(ICON("document-new")); newButton->setAutoRaise(true); newButton->setToolTip(i18n("Create new useraction")); importButton = new QToolButton(this); importButton->setIcon(ICON("document-import")); importButton->setAutoRaise(true); importButton->setToolTip(i18n("Import useractions")); exportButton = new QToolButton(this); exportButton->setIcon(ICON("document-export")); exportButton->setAutoRaise(true); exportButton->setToolTip(i18n("Export useractions")); copyButton = new QToolButton(this); copyButton->setIcon(ICON("edit-copy")); copyButton->setAutoRaise(true); copyButton->setToolTip(i18n("Copy useractions to clipboard")); pasteButton = new QToolButton(this); pasteButton->setIcon(ICON("edit-paste")); pasteButton->setAutoRaise(true); pasteButton->setToolTip(i18n("Paste useractions from clipboard")); removeButton = new QToolButton(this); removeButton->setIcon(ICON("edit-delete")); removeButton->setAutoRaise(true); removeButton->setToolTip(i18n("Delete selected useractions")); toolbarLayout->addWidget(newButton); toolbarLayout->addWidget(importButton); toolbarLayout->addWidget(exportButton); toolbarLayout->addWidget(copyButton); toolbarLayout->addWidget(pasteButton); toolbarLayout->addSpacing(6); // 6 pixel nothing toolbarLayout->addWidget(removeButton); toolbarLayout->addStretch(1000); // some very large stretch-factor // ======== pseudo-toolbar end ======== /* This seems obsolete now! // Display some help KMessageBox::information( this, // parent i18n( "When you apply changes to an action, the modifications " "become available in the current session immediately.\n" "When closing ActionMan, you will be asked to save the changes permanently." ), QString(), // caption "show UserAction help" //dontShowAgainName for the config ); */ layout->addLayout(toolbarLayout); QSplitter *split = new QSplitter(this); layout->addWidget(split, 1000); // again a very large stretch-factor to fix the height of the toolbar actionTree = new UserActionListView(split); actionTree->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); actionProperties = new ActionProperty(split); actionProperties->setEnabled(false); // if there are any actions in the list, the first is displayed and this widget is enabled connect(actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(slotChangeCurrent())); connect(newButton, SIGNAL(clicked()), SLOT(slotNewAction())); connect(removeButton, SIGNAL(clicked()), SLOT(slotRemoveAction())); connect(importButton, SIGNAL(clicked()), SLOT(slotImport())); connect(exportButton, SIGNAL(clicked()), SLOT(slotExport())); connect(copyButton, SIGNAL(clicked()), SLOT(slotToClip())); connect(pasteButton, SIGNAL(clicked()), SLOT(slotFromClip())); // forwards the changed signal of the properties connect(actionProperties, SIGNAL(changed()), SIGNAL(changed())); actionTree->setFirstActionCurrent(); actionTree->setFocus(); } UserActionPage::~UserActionPage() { } bool UserActionPage::continueInSpiteOfChanges() { if (! actionProperties->isModified()) return true; int answer = KMessageBox::questionYesNoCancel(this, i18n("The current action has been modified. Do you want to apply these changes?") ); if (answer == KMessageBox::Cancel) { disconnect(actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(slotChangeCurrent())); actionTree->setCurrentAction(actionProperties->action()); connect(actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(slotChangeCurrent())); return false; } if (answer == KMessageBox::Yes) { if (! actionProperties->validProperties()) { disconnect(actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(slotChangeCurrent())); actionTree->setCurrentAction(actionProperties->action()); connect(actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(slotChangeCurrent())); return false; } slotUpdateAction(); } // if Yes return true; } void UserActionPage::slotChangeCurrent() { if (! continueInSpiteOfChanges()) return; KrAction* action = actionTree->currentAction(); if (action) { actionProperties->setEnabled(true); // the discinct name is used as ID it is not allowd to change it afterwards because it is may referenced anywhere else actionProperties->leDistinctName->setEnabled(false); actionProperties->updateGUI(action); } else { // If the current item in the tree is no action (i.e. a cathegory), disable the properties actionProperties->clear(); actionProperties->setEnabled(false); } emit applied(); // to disable the apply-button } void UserActionPage::slotUpdateAction() { // check that we have a command line, title and a name if (! actionProperties->validProperties()) return; if (actionProperties->leDistinctName->isEnabled()) { // := new entry KrAction* action = new KrAction(krApp->actionCollection(), actionProperties->leDistinctName->text()); krUserAction->addKrAction(action); actionProperties->updateAction(action); UserActionListViewItem* item = actionTree->insertAction(action); actionTree->setCurrentItem(item); } else { // := edit an existing actionProperties->updateAction(); actionTree->update(actionProperties->action()); // update the listviewitem as well... } apply(); } void UserActionPage::slotNewAction() { if (continueInSpiteOfChanges()) { actionTree->clearSelection(); // else the user may think that he is overwriting the selected action actionProperties->clear(); actionProperties->setEnabled(true); // it may be disabled because the tree has the focus on a category actionProperties->leDistinctName->setEnabled(true); actionProperties->leDistinctName->setFocus(); } } void UserActionPage::slotRemoveAction() { if (! dynamic_cast(actionTree->currentItem())) return; int messageDelete = KMessageBox::warningContinueCancel(this, //parent i18n("Are you sure that you want to remove all selected actions?"), //text i18n("Remove Selected Actions?"), //caption KStandardGuiItem::remove(), //Label for the continue-button KStandardGuiItem::cancel(), "Confirm Remove UserAction", //dontAskAgainName (for the config-file) KMessageBox::Dangerous | KMessageBox::Notify); if (messageDelete != KMessageBox::Continue) return; actionProperties->clear(); actionProperties->setEnabled(false); actionTree->removeSelectedActions(); apply(); } void UserActionPage::slotImport() { QString filename = QFileDialog::getOpenFileName(this, QString(), QString(), i18n(FILE_FILTER)); if (filename.isEmpty()) return; UserAction::KrActionList newActions; krUserAction->readFromFile(filename, UserAction::renameDoublicated, &newActions); QListIterator it(newActions); while (it.hasNext()) actionTree->insertAction(it.next()); if (newActions.count() > 0) { apply(); } } void UserActionPage::slotExport() { if (! dynamic_cast(actionTree->currentItem())) return; QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), i18n(FILE_FILTER)); if (filename.isEmpty()) return; QDomDocument doc = QDomDocument(ACTION_DOCTYPE); QFile file(filename); int answer = 0; if (file.open(QIODevice::ReadOnly)) { // getting here, means the file already exists an can be read if (doc.setContent(&file)) // getting here means the file exists and already contains an UserAction-XML-tree answer = KMessageBox::warningYesNoCancel(this, //parent i18n("This file already contains some useractions.\nDo you want to overwrite it or should it be merged with the selected actions?"), //text i18n("Overwrite or Merge?"), //caption KStandardGuiItem::overwrite(), //label for Yes-Button KGuiItem(i18n("Merge")) //label for No-Button ); file.close(); } if (answer == 0 && file.exists()) answer = KMessageBox::warningContinueCancel(this, //parent i18n("This file already exists. Do you want to overwrite it?"), //text i18n("Overwrite Existing File?"), //caption KStandardGuiItem::overwrite() //label for Continue-Button ); if (answer == KMessageBox::Cancel) return; if (answer == KMessageBox::No) // that means the merge-button doc = actionTree->dumpSelectedActions(&doc); // merge else // Yes or Continue means overwrite doc = actionTree->dumpSelectedActions(); bool success = UserAction::writeToFile(doc, filename); if (! success) KMessageBox::error(this, i18n("Cannot open %1 for writing.\nNothing exported.", filename), i18n("Export Failed") ); } void UserActionPage::slotToClip() { if (! dynamic_cast(actionTree->currentItem())) return; QDomDocument doc = actionTree->dumpSelectedActions(); QApplication::clipboard()->setText(doc.toString()); } void UserActionPage::slotFromClip() { QDomDocument doc(ACTION_DOCTYPE); if (doc.setContent(QApplication::clipboard()->text())) { QDomElement root = doc.documentElement(); UserAction::KrActionList newActions; krUserAction->readFromElement(root, UserAction::renameDoublicated, &newActions); QListIterator it(newActions); while (it.hasNext()) actionTree->insertAction(it.next()); if (newActions.count() > 0) { apply(); } } // if ( doc.setContent ) } bool UserActionPage::readyToQuit() { // Check if the current UserAction has changed if (! continueInSpiteOfChanges()) return false; krUserAction->writeActionFile(); return true; } void UserActionPage::apply() { krUserAction->writeActionFile(); emit applied(); } void UserActionPage::applyChanges() { slotUpdateAction(); } diff --git a/krusader/ActionMan/useractionpage.h b/krusader/ActionMan/useractionpage.h index 021c2fc8..32529bc4 100644 --- a/krusader/ActionMan/useractionpage.h +++ b/krusader/ActionMan/useractionpage.h @@ -1,80 +1,81 @@ /***************************************************************************** * Copyright (C) 2006 Shie Erlich * * Copyright (C) 2006 Rafi Yanai * + * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef USERACTIONPAGE_H #define USERACTIONPAGE_H // QtWidgets #include class UserActionListView; class ActionProperty; class QToolButton; class UserActionPage : public QWidget { Q_OBJECT public: explicit UserActionPage(QWidget* parent); ~UserActionPage(); /** * Be sure to call this function before you delete this page!! * @return true if this page can be closed */ bool readyToQuit(); void applyChanges(); signals: void changed(); ///< emitted on changes to an action (used to enable the apply-button) void applied(); ///< emitted when changes are applied to an action (used to disable the apply-button) private: /** * If there are modifications in the property-widget, the user is asked * what to do. Apply, discard or continue editing. In the first case, * saving is done in this function. * @return true if a new action can be loaded in the property-widget. */ bool continueInSpiteOfChanges(); /** * applyes all changes by writing the actionfile and emits "applied" */ void apply(); //bool _modified; ///< true if the action-tree was changed (= changes were applied to an action) UserActionListView *actionTree; ActionProperty *actionProperties; QToolButton *importButton, *exportButton; QToolButton *copyButton, *pasteButton; QToolButton *removeButton, *newButton; private slots: void slotChangeCurrent(); //loads a new action into the detail-view void slotUpdateAction(); //updates the action to the xml-file void slotNewAction(); void slotRemoveAction(); void slotImport(); void slotExport(); void slotToClip(); void slotFromClip(); }; #endif diff --git a/krusader/BookMan/kraddbookmarkdlg.cpp b/krusader/BookMan/kraddbookmarkdlg.cpp index b3e19aac..dd264805 100644 --- a/krusader/BookMan/kraddbookmarkdlg.cpp +++ b/krusader/BookMan/kraddbookmarkdlg.cpp @@ -1,183 +1,184 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "kraddbookmarkdlg.h" #include "../krglobal.h" #include "krbookmarkhandler.h" // QtWidgets #include #include #include #include #include #include #include #include KrAddBookmarkDlg::KrAddBookmarkDlg(QWidget *parent, QUrl url): QDialog(parent) { setWindowModality(Qt::WindowModal); setWindowTitle(i18n("Add Bookmark")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QGridLayout *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(krLoader->loadIcon("go-down", KIconLoader::Small)); _createInBtn->setCheckable(true); connect(_createInBtn, SIGNAL(toggled(bool)), this, SLOT(toggleCreateIn(bool))); 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, SIGNAL(clicked()), this, SLOT(newFolder())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); _name->setFocus(); resize(sizeHint().width() * 2, sizeHint().height()); } void KrAddBookmarkDlg::toggleCreateIn(bool show) { _createInBtn->setIcon(krLoader->loadIcon(show ? "go-up" : "go-down", KIconLoader::Small)); 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 QTreeWidgetItem *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, SIGNAL(itemSelectionChanged()), this, SLOT(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()) { QTreeWidgetItem *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 QTreeWidgetItem *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 0; return _xr[ items[ 0 ] ]; } diff --git a/krusader/BookMan/kraddbookmarkdlg.h b/krusader/BookMan/kraddbookmarkdlg.h index 48b0677b..777a22b0 100644 --- a/krusader/BookMan/kraddbookmarkdlg.h +++ b/krusader/BookMan/kraddbookmarkdlg.h @@ -1,68 +1,69 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #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()); 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 7aa31e6f..2e146b71 100644 --- a/krusader/BookMan/krbookmark.cpp +++ b/krusader/BookMan/krbookmark.cpp @@ -1,122 +1,123 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krbookmark.h" #include "../krglobal.h" #include "../Archive/krarchandler.h" #include "../FileSystem/krtrashhandler.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 icon, QString actionName) : QAction(parent), _url(url), _icon(icon), _folder(false), _separator(false), _autoDelete(true) { QString actName = actionName.isNull() ? BM_NAME(name) : BM_NAME(actionName); setText(name); parent->addAction(actName, this); connect(this, SIGNAL(triggered()), this, SLOT(activatedProxy())); // do we have an icon? if (!icon.isEmpty()) setIcon(QIcon::fromTheme(icon)); else { // what kind of a url is it? if (_url.isLocalFile()) { setIcon(QIcon::fromTheme("folder")); } else { // is it an archive? if (KRarcHandler::isArchive(_url)) setIcon(QIcon::fromTheme("application-x-tar")); else setIcon(QIcon::fromTheme("folder-html")); } } } KrBookmark::KrBookmark(QString name, QString icon) : QAction(QIcon::fromTheme(icon), name, 0), _icon(icon), _folder(true), _separator(false), _autoDelete(false) { setIcon(QIcon::fromTheme(icon == "" ? "folder" : icon)); } KrBookmark::~KrBookmark() { if (_autoDelete) { QListIterator it(_children); while (it.hasNext()) delete it.next(); _children.clear(); } } KrBookmark* KrBookmark::getExistingBookmark(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(krLoader->loadIcon(KrTrashHandler::trashIcon(), KIconLoader::Small)); 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(krLoader->loadIcon("document-open-remote", KIconLoader::Small)); } 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(krLoader->loadIcon("network-workgroup", KIconLoader::Small)); } return bm; } 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 6d9cd894..6c6e4e4d 100644 --- a/krusader/BookMan/krbookmark.h +++ b/krusader/BookMan/krbookmark.h @@ -1,83 +1,84 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRBOOKMARK_H #define KRBOOKMARK_H // QtCore #include #include // QtWidgets #include class KActionCollection; class KrBookmark: public QAction { Q_OBJECT public: KrBookmark(QString name, QUrl url, KActionCollection *parent, QString icon = "", QString actionName = QString()); explicit KrBookmark(QString name, QString icon = ""); // creates a folder ~KrBookmark(); // text() and setText() to change the name of the bookmark // icon() and setIcon() to change icons inline const QString& iconName() const { return _icon; } 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); // ----- special bookmarks static KrBookmark* trash(KActionCollection *collection); static KrBookmark* virt(KActionCollection *collection); static KrBookmark* lan(KActionCollection *collection); static KrBookmark* separator(); signals: void activated(const QUrl &url); protected slots: void activatedProxy(); private: QUrl _url; QString _icon; bool _folder; bool _separator; bool _autoDelete; QList _children; }; #endif // KRBOOKMARK_H diff --git a/krusader/BookMan/krbookmarkbutton.cpp b/krusader/BookMan/krbookmarkbutton.cpp index 54f12fa7..be4771b3 100644 --- a/krusader/BookMan/krbookmarkbutton.cpp +++ b/krusader/BookMan/krbookmarkbutton.cpp @@ -1,64 +1,65 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krbookmarkbutton.h" #include "krbookmarkhandler.h" #include "../krglobal.h" // QtGui #include // QtWidgets #include #include #include #include KrBookmarkButton::KrBookmarkButton(QWidget *parent): QToolButton(parent) { setAutoRaise(true); setIcon(QIcon::fromTheme("bookmarks")); setText(i18n("BookMan II")); setToolTip(i18n("BookMan II")); setPopupMode(QToolButton::InstantPopup); setAcceptDrops(false); acmBookmarks = new KActionMenu(QIcon::fromTheme("bookmarks"), i18n("Bookmarks"), this); acmBookmarks->setDelayed(false); // TODO KF5 : explicit cast as QMenu doesn't have those methods //(acmBookmarks->menu())->setKeyboardShortcutsEnabled(true); //(acmBookmarks->menu())->setKeyboardShortcutsExecute(true); setMenu(acmBookmarks->menu()); connect(acmBookmarks->menu(), SIGNAL(aboutToShow()), this, SLOT(populate())); connect(acmBookmarks->menu(), SIGNAL(aboutToShow()), this, SIGNAL(aboutToShow())); populate(); } void KrBookmarkButton::populate() { krBookMan->populate(static_cast(menu())); } void KrBookmarkButton::showMenu() { populate(); menu()->exec(mapToGlobal(QPoint(0, height()))); } diff --git a/krusader/BookMan/krbookmarkbutton.h b/krusader/BookMan/krbookmarkbutton.h index cbc3bdcc..7efc7744 100644 --- a/krusader/BookMan/krbookmarkbutton.h +++ b/krusader/BookMan/krbookmarkbutton.h @@ -1,46 +1,47 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRBOOKMARKBUTTON_H #define KRBOOKMARKBUTTON_H // QtWidgets #include #include class KrBookmarkButton: public QToolButton { Q_OBJECT public: explicit KrBookmarkButton(QWidget *parent); void showMenu(); signals: void openUrl(const QUrl &url); void aboutToShow(); protected slots: void populate(); private: KActionMenu *acmBookmarks; }; #endif // KRBOOKMARK_BUTTON_H diff --git a/krusader/BookMan/krbookmarkhandler.cpp b/krusader/BookMan/krbookmarkhandler.cpp index 79386a39..985985c1 100644 --- a/krusader/BookMan/krbookmarkhandler.cpp +++ b/krusader/BookMan/krbookmarkhandler.cpp @@ -1,645 +1,646 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krbookmarkhandler.h" #include "kraddbookmarkdlg.h" #include "../krglobal.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 // 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(0), _specialBookmarks() { // 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(); // hack QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; manager = KBookmarkManager::managerForFile(filename, QStringLiteral("krusader")); connect(manager, SIGNAL(changed(QString,QString)), this, SLOT(bookmarksChanged(QString,QString))); } KrBookmarkHandler::~KrBookmarkHandler() { delete manager; delete _privateCollection; } void KrBookmarkHandler::bookmarkCurrent(QUrl url) { QPointer dlg = new KrAddBookmarkDlg(_mainWindow->widget(), 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 == 0) 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) { QString url, name, icon; // 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")) { icon = 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, icon, path + name); parent->children().append(bm); } return true; } bool KrBookmarkHandler::importFromFileFolder(QDomNode &first, KrBookmark *parent, 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); 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::populate(QMenu *menu) { _mainBookmarkPopup = menu; menu->clear(); _specialBookmarks.clear(); buildMenu(_root, menu); } void KrBookmarkHandler::buildMenu(KrBookmark *parent, QMenu *menu) { static int inSecondaryMenu = 0; // used to know if we're on the top menu // 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; QMenu *newMenu = new QMenu(menu); newMenu->setIcon(QIcon(krLoader->loadIcon(bm->iconName(), KIconLoader::Small))); newMenu->setTitle(bm->text()); QAction *menuAction = menu->addMenu(newMenu); QVariant v; v.setValue(bm); menuAction->setData(v); ++inSecondaryMenu; buildMenu(bm, newMenu); --inSecondaryMenu; } 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 (!inSecondaryMenu) { 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 QMenu *newMenu = new QMenu(menu); newMenu->setTitle(i18n("Popular URLs")); newMenu->setIcon(QIcon(krLoader->loadIcon("folder-bookmark", KIconLoader::Small))); 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 || hasJumpback) 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) { // add the jump-back button ListPanelActions *actions = _mainWindow->listPanelActions(); menu->addAction(actions->actJumpBack); _specialBookmarks.append(actions->actJumpBack); menu->addSeparator(); menu->addAction(actions->actSetJumpBack); _specialBookmarks.append(actions->actSetJumpBack); } } if (!hasJumpback) menu->addSeparator(); menu->addAction(KrActions::actAddBookmark); _specialBookmarks.append(KrActions::actAddBookmark); QAction *bmAct = menu->addAction(krLoader->loadIcon("bookmarks", KIconLoader::Small), i18n("Manage Bookmarks"), manager, SLOT(slotEditBookmarks())); _specialBookmarks.append(bmAct); // make sure the menu is connected to us disconnect(menu, SIGNAL(triggered(QAction*)), 0, 0); } menu->installEventFilter(this); } void KrBookmarkHandler::clearBookmarks(KrBookmark *root) { QList::iterator it = root->children().begin(); while (it != root->children().end()) { KrBookmark *bm = *it; if (bm->isFolder()) clearBookmarks(bm); else { foreach(QWidget *w, bm->associatedWidgets()) w->removeAction(bm); delete bm; } it = root->children().erase(it); } } void KrBookmarkHandler::bookmarksChanged(const QString&, const QString&) { importFromFile(); } bool KrBookmarkHandler::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() == QEvent::MouseButtonRelease) { switch (static_cast(ev)->button()) { case Qt::RightButton: _middleClick = false; if (obj->inherits("QMenu")) { QMenu *menu = static_cast(obj); QAction *act = menu->actionAt(static_cast(ev)->pos()); if (obj == _mainBookmarkPopup && _specialBookmarks.contains(act)) { rightClickOnSpecialBookmark(); return true; } KrBookmark *bm = dynamic_cast(act); if (bm != 0) { rightClicked(menu, bm); return true; } else if (act && act->data().canConvert()) { KrBookmark *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); } #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(krLoader->loadIcon("document-open", KIconLoader::Panel), i18n("Open")); act->setData(QVariant(OPEN_ID)); act = popup.addAction(krLoader->loadIcon("tab-new", KIconLoader::Panel), i18n("Open in a new tab")); act->setData(QVariant(OPEN_NEW_TAB_ID)); popup.addSeparator(); } act = popup.addAction(krLoader->loadIcon("edit-delete", KIconLoader::Panel), 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 8e089527..4fd15c8e 100644 --- a/krusader/BookMan/krbookmarkhandler.h +++ b/krusader/BookMan/krbookmarkhandler.h @@ -1,88 +1,89 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRBOOKMARKHANDLER_H #define KRBOOKMARKHANDLER_H // QtCore #include #include #include #include #include // QtXml #include // QtWidgets #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(); void populate(QMenu *menu); void addBookmark(KrBookmark *bm, KrBookmark *parent = 0); 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); void exportToFile(); void exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder); void exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm); void clearBookmarks(KrBookmark *root); void buildMenu(KrBookmark *parent, QMenu *menu); bool eventFilter(QObject *obj, QEvent *ev); 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 }; Q_DECLARE_METATYPE(KrBookmark *) #endif // KRBOOKMARK_HANDLER_H diff --git a/krusader/Dialogs/checksumdlg.cpp b/krusader/Dialogs/checksumdlg.cpp index 5c1f53a8..6ffc230f 100644 --- a/krusader/Dialogs/checksumdlg.cpp +++ b/krusader/Dialogs/checksumdlg.cpp @@ -1,576 +1,577 @@ /***************************************************************************** * 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 program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "checksumdlg.h" #include "../krglobal.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 #include #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, 0, this, 0); // QProcess emits finished() on destruction close(); } void ChecksumProcess::slotError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { KMessageBox::error(0, i18n("Could not start %1.", program().join(" "))); } } void ChecksumProcess::slotFinished(int, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit) { KMessageBox::error(0, 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(0, i18n("Error reading stdout or stderr")); return; } emit resultReady(); } // ------------- Generic Checksum Wizard ChecksumWizard::ChecksumWizard(const QString &path) : QWizard(krApp), m_path(path), m_process(0) { 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 = 0; restart(); } else { button(QWizard::BackButton)->hide(); button(QWizard::NextButton)->hide(); onProgressPage(); } } else if (id == m_resultId) { onResultPage(); } } QWizardPage *ChecksumWizard::createProgressPage(const QString &title) { QWizardPage *page = new QWizardPage; page->setTitle(title); page->setPixmap(QWizard::LogoPixmap, krLoader->loadIcon("process-working", KIconLoader::Desktop, 32)); page->setSubTitle(i18n("Please wait...")); QVBoxLayout *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // "busy" indicator QProgressBar *bar = new QProgressBar(); bar->setRange(0,0); mainLayout->addWidget(bar); return page; } 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 == 0); 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) { QTreeWidgetItem *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() { QWizardPage *page = new QWizardPage; page->setTitle(i18n("Create Checksums")); page->setPixmap(QWizard::LogoPixmap, krLoader->loadIcon("document-edit-sign", KIconLoader::Desktop, 32)); page->setSubTitle(i18n("About to calculate checksum for the following files or directories:")); QVBoxLayout *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // file list KrListWidget *listWidget = new KrListWidget; listWidget->addItems(m_fileNames); mainLayout->addWidget(listWidget); // checksum method QHBoxLayout *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() { QWizardPage *page = new QWizardPage; page->setTitle(i18n("Checksum Results")); QVBoxLayout *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, krLoader->loadIcon(errors || !successes ? "dialog-error" : "dialog-information", KIconLoader::Desktop, 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() { QWizardPage *page = new QWizardPage; page->setTitle(i18n("Verify Checksum File")); page->setPixmap(QWizard::LogoPixmap, krLoader->loadIcon("document-edit-verify", KIconLoader::Desktop, 32)); page->setSubTitle(i18n("About to verify the following checksum file")); QVBoxLayout *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // checksum file QHBoxLayout *hLayout = new QHBoxLayout; QLabel *checksumFileLabel = new QLabel(i18n("Checksum file:")); hLayout->addWidget(checksumFileLabel); KUrlRequester *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() { QWizardPage *page = new QWizardPage; page->setTitle(i18n("Verify Result")); QVBoxLayout *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, krLoader->loadIcon(errors ? "dialog-error" : "dialog-information", KIconLoader::Desktop, 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 459b1bf9..a632eb02 100644 --- a/krusader/Dialogs/checksumdlg.h +++ b/krusader/Dialogs/checksumdlg.h @@ -1,178 +1,179 @@ /***************************************************************************** * 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 program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #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(); 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); virtual ~ChecksumWizard(); 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); 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/krdialogs.cpp b/krusader/Dialogs/krdialogs.cpp index 176cb88e..26828a9a 100644 --- a/krusader/Dialogs/krdialogs.cpp +++ b/krusader/Dialogs/krdialogs.cpp @@ -1,201 +1,202 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krdialogs.h" // QtCore #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../FileSystem/filesystem.h" #include "../defaults.h" #include "../JobMan/jobman.h" QUrl KChooseDir::getFile(const QString &text, const QUrl& url, const QUrl& cwd) { return get(text, url, cwd, KFile::File); } QUrl KChooseDir::getDir(const QString &text, const QUrl& url, const QUrl& cwd) { return get(text, url, cwd, KFile::Directory); } QUrl KChooseDir::get(const QString &text, const QUrl &url, const QUrl &cwd, KFile::Modes mode) { QScopedPointer dlg(new KUrlRequesterDialog(FileSystem::ensureTrailingSlash(url), text, krMainWindow)); dlg->urlRequester()->setStartDir(cwd); dlg->urlRequester()->setMode(mode); dlg->exec(); QUrl u = dlg->selectedUrl(); // empty if cancelled if (u.scheme() == "zip" || u.scheme() == "krarc" || u.scheme() == "tar" || u.scheme() == "iso") { if (QDir(u.path()).exists()) { u.setScheme("file"); } } return u; } KChooseDir::ChooseResult KChooseDir::getCopyDir(const QString &text, const QUrl &url, const QUrl &cwd) { QScopedPointer dlg(new KUrlRequesterDlgForCopy(url, text, krMainWindow, true)); dlg->urlRequester()->setStartDir(cwd); dlg->urlRequester()->setMode(KFile::Directory); dlg->exec(); QUrl u = dlg->selectedURL(); if (u.scheme() == "zip" || u.scheme() == "krarc" || u.scheme() == "tar" || u.scheme() == "iso") { if (QDir(u.path()).exists()) { u.setScheme("file"); } } ChooseResult result; result.url = u; result.enqueue = dlg->isQueued(); return result; } KUrlRequesterDlgForCopy::KUrlRequesterDlgForCopy(const QUrl &urlName, const QString &_text, QWidget *parent, bool modal) : QDialog(parent) { setWindowModality(modal ? Qt::WindowModal : Qt::NonModal); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(new QLabel(_text)); urlRequester_ = new KUrlRequester(urlName, this); urlRequester_->setMinimumWidth(urlRequester_->sizeHint().width() * 3); mainLayout->addWidget(urlRequester_); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); QPushButton *queueButton = new QPushButton( krJobMan->isQueueModeEnabled() ? i18n("F2 Delay Job Start") : i18n("F2 Queue"), this); queueButton->setToolTip(krJobMan->isQueueModeEnabled() ? i18n("Do not start the job now.") : i18n("Enqueue the job if another job is running. Otherwise start immediately.")); buttonBox->addButton(queueButton, QDialogButtonBox::ActionRole); connect(buttonBox, SIGNAL(accepted()), SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), SLOT(reject())); connect(queueButton, SIGNAL(clicked()), SLOT(slotQueueButtonClicked())); connect(urlRequester_, SIGNAL(textChanged(QString)), SLOT(slotTextChanged(QString))); urlRequester_->setFocus(); bool state = !urlName.isEmpty(); okButton->setEnabled(state); } void KUrlRequesterDlgForCopy::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_F2: slotQueueButtonClicked(); return; default: QDialog::keyPressEvent(e); } } void KUrlRequesterDlgForCopy::slotQueueButtonClicked() { queueStart = true; accept(); } void KUrlRequesterDlgForCopy::slotTextChanged(const QString & text) { bool state = !text.trimmed().isEmpty(); okButton->setEnabled(state); } QUrl KUrlRequesterDlgForCopy::selectedURL() const { if (result() == QDialog::Accepted) { QUrl url = urlRequester_->url(); qDebug() << "requester returned URL=" << url.toDisplayString(); if (url.isValid()) KRecentDocument::add(url); return url; } else return QUrl(); } KUrlRequester * KUrlRequesterDlgForCopy::urlRequester() { return urlRequester_; } KRGetDate::KRGetDate(QDate date, QWidget *parent) : QDialog(parent, Qt::MSWindowsFixedSizeDialogHint) { setWindowModality(Qt::WindowModal); dateWidget = new KDatePicker(this); dateWidget->setDate(date); dateWidget->resize(dateWidget->sizeHint()); setMinimumSize(dateWidget->sizeHint()); setMaximumSize(dateWidget->sizeHint()); resize(minimumSize()); connect(dateWidget, SIGNAL(dateSelected(QDate)), this, SLOT(setDate(QDate))); connect(dateWidget, SIGNAL(dateEntered(QDate)), this, SLOT(setDate(QDate))); // keep the original date - incase ESC is pressed originalDate = date; } QDate KRGetDate::getDate() { if (exec() == QDialog::Rejected) chosenDate = QDate(); hide(); return chosenDate; } void KRGetDate::setDate(QDate date) { chosenDate = date; accept(); } diff --git a/krusader/Dialogs/krdialogs.h b/krusader/Dialogs/krdialogs.h index a495a183..f7a50f31 100644 --- a/krusader/Dialogs/krdialogs.h +++ b/krusader/Dialogs/krdialogs.h @@ -1,117 +1,118 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRDIALOGS_H #define KRDIALOGS_H // QtCore #include #include // QtWidgets #include #include #include #include #include #include #include // QtGui #include #include #include #include #include /** \class KChooseDir * Used for asking the user for a folder. * example: * \code * QUrl u = KChooseDir::getDir("target folder", "/suggested/path", ACTIVE_PANEL->virtualPath()); * if (u.isEmpty()) { * // user canceled (either by pressing cancel, or esc * } else { * // do you thing here: you've got a safe url to use * } * \endcode */ class KChooseDir { public: struct ChooseResult { QUrl url; bool enqueue; }; /** * \param text - description of the info requested from the user * \param url - a suggested url to appear in the box as a default choice * \param cwd - a path which is the current working directory (usually ACTIVE_PANEL->virtualPath()). * this is used for completion of partial urls */ static QUrl getFile(const QString &text, const QUrl &url, const QUrl &cwd); static QUrl getDir(const QString &text, const QUrl &url, const QUrl &cwd); static ChooseResult getCopyDir(const QString &text, const QUrl &url, const QUrl &cwd); private: static QUrl get(const QString &text, const QUrl &url, const QUrl &cwd, KFile::Modes mode); }; class KUrlRequesterDlgForCopy : public QDialog { Q_OBJECT public: KUrlRequesterDlgForCopy(const QUrl& url, const QString& text, QWidget *parent, bool modal = true); QUrl selectedURL() const; bool isQueued() { return queueStart; } KUrlRequester *urlRequester(); protected: virtual void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; private slots: void slotQueueButtonClicked(); void slotTextChanged(const QString &); private: KUrlRequester *urlRequester_; QPushButton *okButton; bool queueStart = false; }; class KRGetDate : public QDialog { Q_OBJECT public: explicit KRGetDate(QDate date = QDate::currentDate(), QWidget *parent = 0); QDate getDate(); private slots: void setDate(QDate); private: KDatePicker *dateWidget; QDate chosenDate, originalDate; }; #endif diff --git a/krusader/Dialogs/krsqueezedtextlabel.cpp b/krusader/Dialogs/krsqueezedtextlabel.cpp index 618d98b3..1f1c20ae 100644 --- a/krusader/Dialogs/krsqueezedtextlabel.cpp +++ b/krusader/Dialogs/krsqueezedtextlabel.cpp @@ -1,101 +1,102 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krsqueezedtextlabel.h" // QtGui #include #include // QtWidgets #include #include #include #include KrSqueezedTextLabel::KrSqueezedTextLabel(QWidget *parent): KSqueezedTextLabel(parent), _index(-1), _length(-1) { setAutoFillBackground(true); } KrSqueezedTextLabel::~KrSqueezedTextLabel() { } void KrSqueezedTextLabel::mousePressEvent(QMouseEvent *e) { e->ignore(); emit clicked(e); } void KrSqueezedTextLabel::squeezeTextToLabel(int index, int length) { if (index == -1 || length == -1) KSqueezedTextLabel::squeezeTextToLabel(); else { QString sqtext = fullText; QFontMetrics fm(fontMetrics()); int labelWidth = size().width(); int textWidth = fm.width(sqtext); if (textWidth > labelWidth) { int avgCharSize = textWidth / sqtext.length(); int numOfExtraChars = (textWidth - labelWidth) / avgCharSize; int delta; // remove as much as possible from the left, and then from the right if (index > 3) { delta = qMin(index, numOfExtraChars); numOfExtraChars -= delta; sqtext.replace(0, delta, "..."); } if (numOfExtraChars > 0 && ((int)sqtext.length() > length + 3)) { delta = qMin(numOfExtraChars, (int)sqtext.length() - (length + 3)); sqtext.replace(sqtext.length() - delta, delta, "..."); } QLabel::setText(sqtext); setToolTip(QString()); setToolTip(fullText); } else { QLabel::setText(fullText); setToolTip(QString()); QToolTip::hideText(); } } } void KrSqueezedTextLabel::setText(const QString &text, int index, int length) { _index = index; _length = length; fullText = text; KSqueezedTextLabel::setText(fullText); squeezeTextToLabel(_index, _length); } void KrSqueezedTextLabel::paintEvent(QPaintEvent * e) { KSqueezedTextLabel::paintEvent(e); } diff --git a/krusader/Dialogs/krsqueezedtextlabel.h b/krusader/Dialogs/krsqueezedtextlabel.h index 8645aee1..b587b197 100644 --- a/krusader/Dialogs/krsqueezedtextlabel.h +++ b/krusader/Dialogs/krsqueezedtextlabel.h @@ -1,68 +1,69 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRSQUEEZEDTEXTLABEL_H #define KRSQUEEZEDTEXTLABEL_H // QtGui #include #include #include #include class QMouseEvent; class QDragEnterEvent; class QPaintEvent; /** This class overloads KSqueezedTextLabel and simply adds a clicked signal, so that users will be able to click the label and switch focus between panels. NEW: a special setText() method allows to choose which part of the string should be displayed (example: make sure that search results won't be cut out) */ class KrSqueezedTextLabel : public KSqueezedTextLabel { Q_OBJECT public: explicit KrSqueezedTextLabel(QWidget *parent = 0); ~KrSqueezedTextLabel(); public slots: void setText(const QString &text, int index = -1, int length = -1); signals: void clicked(QMouseEvent *); /**< emitted when someone clicks on the label */ protected: void resizeEvent(QResizeEvent *) { squeezeTextToLabel(_index, _length); } virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE; virtual void paintEvent(QPaintEvent * e) Q_DECL_OVERRIDE; void squeezeTextToLabel(int index = -1, int length = -1); QString fullText; private: int _index, _length; }; #endif diff --git a/krusader/Dialogs/newftpgui.cpp b/krusader/Dialogs/newftpgui.cpp index c06adf97..b9129f4f 100644 --- a/krusader/Dialogs/newftpgui.cpp +++ b/krusader/Dialogs/newftpgui.cpp @@ -1,203 +1,204 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2009 Fathi Boudra * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "newftpgui.h" // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #define SIZE_MINIMUM QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) const QStringList sProtocols = QStringList() << QStringLiteral("ftp") << QStringLiteral("ftps") << QStringLiteral("sftp") << QStringLiteral("fish") << QStringLiteral("nfs") << QStringLiteral("smb") << QStringLiteral("webdav") << QStringLiteral("svn") << QStringLiteral("svn+file") << QStringLiteral("svn+http") << QStringLiteral("svn+https") << QStringLiteral("svn+ssh"); /** * Constructs a newFTPGUI which is a child of 'parent', * with the name 'name' and widget flags set to 'f' */ newFTPGUI::newFTPGUI(QWidget* parent) : QDialog(parent) { setModal(true); setWindowTitle(i18n("New Network Connection")); resize(500, 240); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred); policy.setHeightForWidth(sizePolicy().hasHeightForWidth()); setSizePolicy(policy); iconLabel = new QLabel(this); iconLabel->setPixmap(krLoader->loadIcon("network-wired", KIconLoader::Desktop, 32)); iconLabel->setSizePolicy(SIZE_MINIMUM); aboutLabel = new QLabel(i18n("About to connect to..."), this); QFont font(aboutLabel->font()); font.setBold(true); aboutLabel->setFont(font); protocolLabel = new QLabel(i18n("Protocol:"), this); hostLabel = new QLabel(i18n("Host:"), this); portLabel = new QLabel(i18n("Port:"), this); prefix = new KComboBox(this); prefix->setObjectName(QString::fromUtf8("protocol")); prefix->setSizePolicy(SIZE_MINIMUM); url = new KHistoryComboBox(this); url->setMaxCount(50); url->setMinimumContentsLength(10); const QStringList availableProtocols = KProtocolInfo::protocols(); for (const QString protocol : sProtocols) { if (availableProtocols.contains(protocol)) prefix->addItem(protocol + QStringLiteral("://")); } // load the history and completion list after creating the history combo KConfigGroup group(krConfig, "Private"); QStringList list = group.readEntry("newFTP Completion list", QStringList()); url->completionObject()->setItems(list); list = group.readEntry("newFTP History list", QStringList()); url->setHistoryItems(list); // Select last used protocol const QString lastUsedProtocol = group.readEntry("newFTP Protocol", QString()); if(!lastUsedProtocol.isEmpty()) { prefix->setCurrentItem(lastUsedProtocol); } port = new QSpinBox(this); port->setMaximum(65535); port->setValue(21); port->setSizePolicy(SIZE_MINIMUM); usernameLabel = new QLabel(i18n("Username:"), this); username = new KLineEdit(this); passwordLabel = new QLabel(i18n("Password:"), this); password = new KLineEdit(this); password->setEchoMode(QLineEdit::Password); QHBoxLayout *horizontalLayout = new QHBoxLayout(); horizontalLayout->addWidget(iconLabel); horizontalLayout->addWidget(aboutLabel); QGridLayout *gridLayout = new QGridLayout(); gridLayout->addWidget(protocolLabel, 0, 0, 1, 1); gridLayout->addWidget(hostLabel, 0, 1, 1, 1); gridLayout->addWidget(portLabel, 0, 2, 1, 1); gridLayout->addWidget(prefix, 1, 0, 1, 1); gridLayout->addWidget(url, 1, 1, 1, 1); gridLayout->addWidget(port, 1, 2, 1, 1); gridLayout->addWidget(usernameLabel, 2, 0, 1, 1); gridLayout->addWidget(username, 3, 0, 1, 3); gridLayout->addWidget(passwordLabel, 4, 0, 1, 1); gridLayout->addWidget(password, 5, 0, 1, 3); QGridLayout *widgetLayout = new QGridLayout(); widgetLayout->addLayout(horizontalLayout, 0, 0, 1, 1); widgetLayout->addLayout(gridLayout, 1, 0, 1, 1); mainLayout->addLayout(widgetLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Connect")); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(prefix, SIGNAL(activated(QString)), this, SLOT(slotTextChanged(QString))); connect(url, SIGNAL(activated(QString)), url, SLOT(addToHistory(QString))); if(!lastUsedProtocol.isEmpty()) { // update the port field slotTextChanged(lastUsedProtocol); } setTabOrder(url, username); setTabOrder(username, password); setTabOrder(password, prefix); } /** * Destroys the object and frees any allocated resources */ newFTPGUI::~newFTPGUI() { // no need to delete child widgets, Qt does it all for us } void newFTPGUI::slotTextChanged(const QString &string) { if (string.startsWith(QLatin1String("ftp")) || string.startsWith(QLatin1String("sftp")) || string.startsWith(QLatin1String("fish"))) { if (port->value() == 21 || port->value() == 22) { port->setValue(string.startsWith(QLatin1String("ftp")) ? 21 : 22); } port->setEnabled(true); } else { port->setEnabled(false); } } /** * Main event handler. Reimplemented to handle application font changes */ bool newFTPGUI::event(QEvent *ev) { bool ret = QDialog::event(ev); if (ev->type() == QEvent::ApplicationFontChange) { QFont font(aboutLabel->font()); font.setBold(true); aboutLabel->setFont(font); } return ret; } diff --git a/krusader/Dialogs/newftpgui.h b/krusader/Dialogs/newftpgui.h index b066ef1e..90d180f6 100644 --- a/krusader/Dialogs/newftpgui.h +++ b/krusader/Dialogs/newftpgui.h @@ -1,66 +1,67 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2009 Fathi Boudra * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef NEWFTPGUI_H #define NEWFTPGUI_H // QtWidgets #include #include #include #include #include #include /** * The "New Network Connection" dialog */ class newFTPGUI : public QDialog { Q_OBJECT public: explicit newFTPGUI(QWidget *parent = 0); ~newFTPGUI(); KComboBox* prefix; KHistoryComboBox* url; QSpinBox* port; KLineEdit* username; KLineEdit* password; protected: bool event(QEvent *); private slots: void slotTextChanged(const QString &); private: QLabel* iconLabel; QLabel* aboutLabel; QLabel* protocolLabel; QLabel* passwordLabel; QLabel* hostLabel; QLabel* usernameLabel; QLabel* portLabel; }; #endif diff --git a/krusader/Dialogs/popularurls.cpp b/krusader/Dialogs/popularurls.cpp index 64ec4fd8..0e2999a9 100644 --- a/krusader/Dialogs/popularurls.cpp +++ b/krusader/Dialogs/popularurls.cpp @@ -1,373 +1,374 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "popularurls.h" #include // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../krslots.h" #include "../GUI/krtreewidget.h" #define STARTING_RANK 20 #define INCREASE 2 #define DECREASE 1 PopularUrls::PopularUrls(QObject *parent) : QObject(parent), head(0), tail(0), count(0) { dlg = new PopularUrlsDlg(); } PopularUrls::~PopularUrls() { clearList(); delete dlg; } void PopularUrls::clearList() { if (head) { UrlNodeP p = head, tmp; while (p) { tmp = p; p = p->next; delete tmp; } } ranks.clear(); head = tail = 0; } void PopularUrls::save() { KConfigGroup svr(krConfig, "Private"); // prepare the string list containing urls and int list with ranks QStringList urlList; QList rankList; UrlNodeP p = head; while (p) { urlList << p->url.toDisplayString(); rankList << p->rank; p = p->next; } svr.writeEntry("PopularUrls", urlList); svr.writeEntry("PopularUrlsRank", rankList); } void PopularUrls::load() { KConfigGroup svr(krConfig, "Private"); QStringList urlList = svr.readEntry("PopularUrls", QStringList()); QList rankList = svr.readEntry("PopularUrlsRank", QList()); if (urlList.count() != rankList.count()) { KMessageBox::error(krMainWindow, i18n("The saved 'Popular URLs' are invalid. The list will be cleared.")); return; } clearList(); count = 0; // iterate through both lists and QStringList::Iterator uit; QList::Iterator rit; for (uit = urlList.begin(), rit = rankList.begin(); uit != urlList.end() && rit != rankList.end(); ++uit, ++rit) { UrlNodeP node = new UrlNode; node->url = QUrl(*uit); node->rank = *rit; appendNode(node); ranks.insert(*uit, node); } } // returns a url list with the 'max' top popular urls QList PopularUrls::getMostPopularUrls(int max) { // get at most 'max' urls QList list; UrlNodeP p = head; int tmp = 0; if (maxUrls < max) max = maxUrls; // don't give more than maxUrls while (p && tmp < max) { list << p->url; p = p->next; ++tmp; } return list; } // adds a url to the list, or increase rank of an existing url, making // sure to bump it up the list if needed void PopularUrls::addUrl(const QUrl& url) { QUrl tmpurl = url; tmpurl.setPassword(QString()); // make sure no passwords are permanently stored if (!tmpurl.path().endsWith('/')) // make a uniform trailing slash policy tmpurl.setPath(tmpurl.path() + '/'); UrlNodeP pnode; decreaseRanks(); if (!head) { // if the list is empty ... (assumes dict to be empty as well) pnode = new UrlNode; pnode->rank = STARTING_RANK; pnode->url = tmpurl; appendNode(pnode); ranks.insert(tmpurl.url(), head); } else { if (ranks.find(tmpurl.url()) == ranks.end()) { // is the added url new? if so, append it pnode = new UrlNode; pnode->rank = STARTING_RANK; pnode->url = tmpurl; appendNode(pnode); ranks.insert(tmpurl.url(), pnode); } else { pnode = ranks[ tmpurl.url()]; pnode->rank += INCREASE; } } // do we need to change location for this one? relocateIfNeeded(pnode); // too many urls? if (count > maxUrls) removeNode(tail); //dumpList(); } // checks if 'node' needs to be bumped-up the ranking list and does it if needed void PopularUrls::relocateIfNeeded(UrlNodeP node) { if (node->prev && (node->prev->rank < node->rank)) { // iterate until we find the correct place to put it UrlNodeP tmp = node->prev->prev; while (tmp) { if (tmp->rank >= node->rank) break; // found it! else tmp = tmp->prev; } // now, if tmp isn't null, we need to move node to tmp->next // else move it to become head removeNode(node); insertNode(node, tmp); } } // iterate over the list, decreasing each url's rank // this is very naive, but a 1..30 for loop is acceptable (i hope) void PopularUrls::decreaseRanks() { if (head) { UrlNodeP p = head; while (p) { if (p->rank - DECREASE >= 0) p->rank -= DECREASE; else p->rank = 0; p = p->next; } } } // removes a node from the list, but doesn't free memory! // note: this will be buggy in case the list becomes empty (which should never happen) void PopularUrls::removeNode(UrlNodeP node) { if (node->prev) { if (tail == node) tail = node->prev; node->prev->next = node->next; } if (node->next) { if (head == node) head = node->next; node->next->prev = node->prev; } --count; } void PopularUrls::insertNode(UrlNodeP node, UrlNodeP after) { if (!after) { // make node head node->next = head; node->prev = 0; head->prev = node; head = node; } else { if (tail == after) tail = node; node->prev = after; node->next = after->next; if (node->next) { after->next->prev = node; after->next = node; } } ++count; } // appends 'node' to the end of the list, collecting garbage if needed void PopularUrls::appendNode(UrlNodeP node) { if (!tail) { // creating the first element head = tail = node; node->prev = node->next = 0; } else { node->next = 0; node->prev = tail; tail->next = node; tail = node; } ++count; } void PopularUrls::dumpList() { UrlNodeP p = head; printf("====start %d====\n", count); while (p) { printf("%d : %s\n", p->rank, p->url.url().toLatin1().data()); p = p->next; } fflush(stdout); } void PopularUrls::showDialog() { QList list = getMostPopularUrls(maxUrls); dlg->run(list); if (dlg->result() == -1) return; SLOTS->refresh(list[dlg->result()]); //printf("running %s\n", list[dlg->result()].url().toLatin1());fflush(stdout); } // ===================================== PopularUrlsDlg ====================================== PopularUrlsDlg::PopularUrlsDlg(): QDialog(krMainWindow) { setWindowTitle(i18n("Popular URLs")); setWindowModality(Qt::WindowModal); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QGridLayout *layout = new QGridLayout; layout->setContentsMargins(0, 0, 0, 0); // listview to contain the urls urls = new KrTreeWidget(this); urls->header()->hide(); urls->setSortingEnabled(false); urls->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // quick search search = new KTreeWidgetSearchLine(this, urls); QLabel *lbl = new QLabel(i18n("&Search:"), this); lbl->setBuddy(search); layout->addWidget(lbl, 0, 0); layout->addWidget(search, 0, 1); layout->addWidget(urls, 1, 0, 1, 2); mainLayout->addLayout(layout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); setTabOrder(search, urls); setTabOrder((QWidget *)urls, buttonBox->button(QDialogButtonBox::Close)); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(urls, SIGNAL(activated(QModelIndex)), this, SLOT(slotItemSelected(QModelIndex))); connect(search, SIGNAL(hiddenChanged(QTreeWidgetItem*,bool)), this, SLOT(slotVisibilityChanged())); } void PopularUrlsDlg::slotItemSelected(const QModelIndex & ndx) { selection = ndx.row(); accept(); } void PopularUrlsDlg::slotVisibilityChanged() { // select the first visible item QList list = urls->selectedItems(); if (list.count() > 0 && !list[0]->isHidden()) return; urls->clearSelection(); urls->setCurrentItem(0); QTreeWidgetItemIterator it(urls); while (*it) { if (!(*it)->isHidden()) { (*it)->setSelected(true); urls->setCurrentItem(*it); break; } it++; } } PopularUrlsDlg::~PopularUrlsDlg() { delete search; delete urls; } void PopularUrlsDlg::run(QList list) { // populate the listview urls->clear(); QList::Iterator it; QTreeWidgetItem * lastItem = 0; for (it = list.begin(); it != list.end(); ++it) { QTreeWidgetItem *item = new QTreeWidgetItem(urls, lastItem); lastItem = item; item->setText(0, (*it).isLocalFile() ? (*it).path() : (*it).toDisplayString()); item->setIcon(0, (*it).isLocalFile() ? SmallIcon("folder") : SmallIcon("folder-html")); } setMinimumSize(urls->sizeHint().width() + 45, 400); search->clear(); search->setFocus(); selection = -1; slotVisibilityChanged(); exec(); } diff --git a/krusader/Dialogs/popularurls.h b/krusader/Dialogs/popularurls.h index eaeda43e..029d2f4d 100644 --- a/krusader/Dialogs/popularurls.h +++ b/krusader/Dialogs/popularurls.h @@ -1,109 +1,110 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef POPULARURLS_H #define POPULARURLS_H // QtCore #include #include #include // QtWidgets #include // the class holds a list of most popular links in a dual data structure // * linked list, with head and tail: for fast append/prepend support // * dictionary that maps urls to list nodes: to save the need to iterate // over the list in order to find the correct node for each new url // // also, the class holds a maximum number of urls. two variables affect this: // * maxUrls - the num. of urls the user can see // * hardLimit - the actual number of urls kept. // when the number of urls reaches hardLimit, a garbage collection is done and // the bottom (hardLimit-maxUrls) entries are removed from the list typedef struct _UrlNode* UrlNodeP; typedef struct _UrlNode { UrlNodeP prev; QUrl url; int rank; UrlNodeP next; } UrlNode; class PopularUrlsDlg; class PopularUrls : public QObject { Q_OBJECT public: explicit PopularUrls(QObject *parent = 0); ~PopularUrls(); void save(); void load(); void addUrl(const QUrl& url); QList getMostPopularUrls(int max); public slots: void showDialog(); protected: // NOTE: the following methods append/insert/remove a node to the list // but NEVER free memory or allocate memory! void appendNode(UrlNodeP node); void insertNode(UrlNodeP node, UrlNodeP after); void removeNode(UrlNodeP node); void relocateIfNeeded(UrlNodeP node); void clearList(); void dumpList(); void decreaseRanks(); private: UrlNodeP head, tail; QHash ranks; // actually holds UrlNode* int count; static const int maxUrls = 30; PopularUrlsDlg *dlg; }; class KrTreeWidget; class KTreeWidgetSearchLine; class QModelIndex; class PopularUrlsDlg: public QDialog { Q_OBJECT public: PopularUrlsDlg(); ~PopularUrlsDlg(); void run(QList list); // use this to open the dialog inline int result() const { return selection; } // returns index 0 - topmost, or -1 protected slots: void slotVisibilityChanged(); void slotItemSelected(const QModelIndex &); private: KrTreeWidget *urls; KTreeWidgetSearchLine *search; int selection; }; #endif diff --git a/krusader/DiskUsage/filelightParts/Config.cpp b/krusader/DiskUsage/filelightParts/Config.cpp index b85dc058..9ed73845 100644 --- a/krusader/DiskUsage/filelightParts/Config.cpp +++ b/krusader/DiskUsage/filelightParts/Config.cpp @@ -1,69 +1,70 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "Config.h" // QtGui #include #include #include #include bool Config::varyLabelFontSizes = true; bool Config::showSmallFiles = false; uint Config::contrast = 50; uint Config::antiAliasFactor = 2; uint Config::minFontPitch = 10; uint Config::defaultRingDepth = 4; Filelight::MapScheme Config::scheme; inline KConfigGroup Filelight::Config::kconfig() { KSharedConfigPtr config = KSharedConfig::openConfig(); return KConfigGroup(config, "DiskUsage"); } void Filelight::Config::read() { KConfigGroup group = kconfig(); varyLabelFontSizes = group.readEntry("varyLabelFontSizes", true); showSmallFiles = group.readEntry("showSmallFiles", false); contrast = group.readEntry("contrast", 50); antiAliasFactor = group.readEntry("antiAliasFactor", 2); minFontPitch = group.readEntry("minFontPitch", QFont().pointSize() - 3); scheme = (MapScheme) group.readEntry("scheme", 0); defaultRingDepth = 4; } void Filelight::Config::write() { KConfigGroup group = kconfig(); group.writeEntry("varyLabelFontSizes", varyLabelFontSizes); group.writeEntry("showSmallFiles", showSmallFiles); group.writeEntry("contrast", contrast); group.writeEntry("antiAliasFactor", antiAliasFactor); group.writeEntry("minFontPitch", minFontPitch); group.writeEntry("scheme", (int)scheme); } diff --git a/krusader/DiskUsage/filelightParts/Config.h b/krusader/DiskUsage/filelightParts/Config.h index 5684dd3c..6d8dc083 100644 --- a/krusader/DiskUsage/filelightParts/Config.h +++ b/krusader/DiskUsage/filelightParts/Config.h @@ -1,54 +1,55 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef CONFIG_H #define CONFIG_H // QtCore #include class KConfigGroup; namespace Filelight { enum MapScheme { Rainbow, HighContrast, KDE, FileDensity, ModTime }; class Config { static KConfigGroup kconfig(); public: static void read(); static void write(); //keep everything positive, avoid using DON'T, NOT or NO static bool varyLabelFontSizes; static bool showSmallFiles; static uint contrast; static uint antiAliasFactor; static uint minFontPitch; static uint defaultRingDepth; static MapScheme scheme; }; } using Filelight::Config; #endif diff --git a/krusader/DiskUsage/filelightParts/fileTree.cpp b/krusader/DiskUsage/filelightParts/fileTree.cpp index 371d3057..566a8a48 100644 --- a/krusader/DiskUsage/filelightParts/fileTree.cpp +++ b/krusader/DiskUsage/filelightParts/fileTree.cpp @@ -1,89 +1,90 @@ /***************************************************************************** * Copyright (C) 2004 Max Howell * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "fileTree.h" // QtCore #include #include //static definitions const FileSize File::DENOMINATOR[4] = { 1ull, 1ull << 10, 1ull << 20, 1ull << 30 }; const char File::PREFIX[5][2] = { "", "K", "M", "G", "T" }; QString File::fullPath(const Directory *root /*= 0*/) const { QString path; if (root == this) root = 0; //prevent returning empty string when there is something we could return const File *d; for (d = this; d != root && d && d->parent() != 0; d = d->parent()) { if (!path.isEmpty()) path = '/' + path; path = d->name() + path; } if (d) { while (d->parent()) d = d->parent(); if (d->directory().endsWith('/')) return d->directory() + path; else return d->directory() + '/' + path; } else return path; } QString File::humanReadableSize(UnitPrefix key /*= mega*/) const //FIXME inline { return humanReadableSize(m_size, key); } QString File::humanReadableSize(FileSize size, UnitPrefix key /*= mega*/) //static { QString s; double prettySize = (double)size / (double)DENOMINATOR[key]; const QLocale locale; if (prettySize >= 0.01) { if (prettySize < 1) s = locale.toString(prettySize, 'f', 2); else if (prettySize < 100) s = locale.toString(prettySize, 'f', 1); else s = locale.toString(prettySize, 'f', 0); s += ' '; s += PREFIX[key]; s += 'B'; } if (prettySize < 0.1) { s += " ("; s += locale.toString(size / DENOMINATOR[ key ? key - 1 : 0 ]); s += ' '; s += PREFIX[key]; s += "B)"; } return s; } diff --git a/krusader/DiskUsage/filelightParts/fileTree.h b/krusader/DiskUsage/filelightParts/fileTree.h index f369260c..da95b80c 100644 --- a/krusader/DiskUsage/filelightParts/fileTree.h +++ b/krusader/DiskUsage/filelightParts/fileTree.h @@ -1,383 +1,384 @@ /***************************************************************************** * Copyright (C) 2004 Max Howell * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef FILETREE_H #define FILETREE_H #include #include #include #include #include // TODO these are pointlessly general purpose now, make them incredibly specific typedef KIO::filesize_t FileSize; template class Iterator; template class ConstIterator; template class Chain; template class Link { public: explicit Link(T* const t) : prev(this), next(this), data(t) {} Link() : prev(this), next(this), data(0) {} //TODO unlinking is slow and you don't use it very much in this context. // ** Perhaps you can make a faster deletion system that doesn't bother tidying up first // ** and then you MUST call some kind of detach() function when you remove elements otherwise ~Link() { delete data; unlink(); } friend class Iterator; friend class ConstIterator; friend class Chain; private: void unlink() { prev->next = next; next->prev = prev; prev = next = this; } Link* prev; Link* next; T* data; //ensure only iterators have access to this }; template class Iterator { public: Iterator() : link(0) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be derefenced explicit Iterator(Link *p) : link(p) { } bool operator==(const Iterator& it) const { return link == it.link; } bool operator!=(const Iterator& it) const { return link != it.link; } bool operator!=(const Link *p) const { return p != link; } //here we have a choice, really I should make two classes one const the other not const T* operator*() const { return link->data; } T* operator*() { return link->data; } Iterator& operator++() { link = link->next; return *this; } //**** does it waste time returning in places where we don't use the retval? bool isNull() const { return (link == 0); } //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible void transferTo(Chain &chain) { chain.append(remove()); } T* remove() { //remove from list, delete Link, data is returned NOT deleted T* const d = link->data; Link* const p = link->prev; link->data = 0; delete link; link = p; //make iterator point to previous element, YOU must check this points to an element return d; } private: Link *link; }; template class ConstIterator { public: explicit ConstIterator(Link *p) : link(p) { } bool operator==(const Iterator& it) const { return link == it.link; } bool operator!=(const Iterator& it) const { return link != it.link; } bool operator!=(const Link *p) const { return p != link; } const T* operator*() const { return link->data; } ConstIterator& operator++() { link = link->next; return *this; } private: const Link *link; }; //**** try to make a generic list class and then a brief full list template that inlines // thus reducing code bloat template class Chain { public: Chain() { } virtual ~Chain() { empty(); } void append(T* const data) { Link* const link = new Link(data); link->prev = head.prev; link->next = &head; head.prev->next = link; head.prev = link; } void transferTo(Chain &c) { if (isEmpty()) return; Link* const first = head.next; Link* const last = head.prev; head.unlink(); first->prev = c.head.prev; c.head.prev->next = first; last->next = &c.head; c.head.prev = last; } void empty() { while (head.next != &head) { delete head.next; } } Iterator iterator() const { return Iterator(head.next); } ConstIterator constIterator() const { return ConstIterator(head.next); } const Link *end() const { return &head; } bool isEmpty() const { return (head.next == &head); } private: Link head; void operator=(const Chain&) {} }; class Directory; class File { protected: Directory *m_parent; //0 if this is treeRoot QString m_name; //< file name QString m_directory;//< the directory of the file FileSize m_size; //< size with subdirectories FileSize m_ownSize; //< size without subdirectories mode_t m_mode; //< file mode QString m_owner; //< file owner name QString m_group; //< file group name QString m_perm; //< file permissions string time_t m_time; //< file modification in time_t format bool m_symLink; //< true if the file is a symlink QString m_mimeType; //< file mimetype bool m_excluded; //< flag if the file is excluded from du int m_percent; //< percent flag public: File(Directory *parentIn, const QString &nameIn, const QString &dir, FileSize sizeIn, mode_t modeIn, const QString &ownerIn, const QString &groupIn, const QString &permIn, time_t timeIn, bool symLinkIn, const QString &mimeTypeIn) : m_parent(parentIn), m_name(nameIn), m_directory(dir), m_size(sizeIn), m_ownSize(sizeIn), m_mode(modeIn), m_owner(ownerIn), m_group(groupIn), m_perm(permIn), m_time(timeIn), m_symLink(symLinkIn), m_mimeType(mimeTypeIn), m_excluded(false), m_percent(-1) {} File(const QString &nameIn, FileSize sizeIn) : m_parent(0), m_name(nameIn), m_directory(QString()), m_size(sizeIn), m_ownSize(sizeIn), m_mode(0), m_owner(QString()), m_group(QString()), m_perm(QString()), m_time(-1), m_symLink(false), m_mimeType(QString()), m_excluded(false), m_percent(-1) { } virtual ~File() {} inline const QString & name() const { return m_name; } inline const QString & directory() const { return m_directory; } inline FileSize size() const { return m_excluded ? 0 : m_size; } inline FileSize ownSize() const { return m_excluded ? 0 : m_ownSize; } inline mode_t mode() const { return m_mode; } inline const QString & owner() const { return m_owner; } inline const QString & group() const { return m_group; } inline const QString & perm() const { return m_perm; } inline time_t time() const { return m_time; } inline const QString & mime() const { return m_mimeType; } inline bool isSymLink() const { return m_symLink; } virtual bool isDir() const { return false; } inline bool isExcluded() const { return m_excluded; } inline void exclude(bool flag) { m_excluded = flag; } inline int intPercent() const { return m_percent; } inline const QString percent() const { if (m_percent < 0) return "INV"; QString buf; buf.sprintf("%d.%02d%%", m_percent / 100, m_percent % 100); return buf; } inline void setPercent(int p) { m_percent = p; } inline const Directory* parent() const { return m_parent; } inline void setSizes(KIO::filesize_t totalSize, KIO::filesize_t ownSize) { m_ownSize = ownSize; m_size = totalSize; } enum UnitPrefix { kilo, mega, giga, tera }; static const FileSize DENOMINATOR[4]; static const char PREFIX[5][2]; QString fullPath(const Directory* = 0) const; QString humanReadableSize(UnitPrefix key = mega) const; static QString humanReadableSize(FileSize size, UnitPrefix Key = mega); friend class Directory; }; //TODO when you modify this to take into account hardlinks you should make the Chain layered not inherited class Directory : public Chain, public File { public: Directory(Directory *parentIn, const QString &nameIn, const QString &dir, FileSize sizeIn, mode_t modeIn, const QString &ownerIn, const QString &groupIn, const QString &permIn, time_t timeIn, bool symLinkIn, const QString &mimeTypeIn) : File(parentIn, nameIn, dir, sizeIn, modeIn, ownerIn, groupIn, permIn, timeIn, symLinkIn, mimeTypeIn), m_fileCount(0) {} Directory(const QString &name, QString url) : File(name, 0), m_fileCount(0) { m_directory = url; } virtual ~Directory() {} virtual bool isDir() const { return true; } void append(File *p) { ++m_fileCount; Directory *parent = m_parent; while (parent) { parent->m_fileCount++; parent = parent->m_parent; } Chain::append(p); p->m_parent = this; } void remove(File *p) { for (Iterator it = Chain::iterator(); it != Chain::end(); ++it) if ((*it) == p) { --m_fileCount; Directory *parent = m_parent; while (parent) { parent->m_fileCount--; parent = parent->m_parent; } it.remove(); break; } } uint fileCount() const { return m_fileCount; } private: Directory(const Directory&); void operator=(const Directory&); uint m_fileCount; }; #endif diff --git a/krusader/DiskUsage/radialMap/builder.cpp b/krusader/DiskUsage/radialMap/builder.cpp index d5ada3ca..36280a70 100644 --- a/krusader/DiskUsage/radialMap/builder.cpp +++ b/krusader/DiskUsage/radialMap/builder.cpp @@ -1,147 +1,148 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "builder.h" #include "Config.h" #include "widget.h" // QtCore #include #include //**** REMOVE NEED FOR the +1 with MAX_RING_DEPTH uses //**** add some angle bounds checking (possibly in Segment ctor? can I delete in a ctor?) //**** this class is a mess RadialMap::Builder::Builder(RadialMap::Map *m, const Directory* const d, bool fast) : m_map(m) , m_root(d) , m_minSize(static_cast((d->size() * 3) / (PI * m->height() - m->MAP_2MARGIN))) , m_depth(&m->m_visibleDepth) { m_signature = new Chain [*m_depth + 1]; if (!fast) { //|| *m_depth == 0 ) //depth 0 is special case usability-wise //**** WHY?! //determine depth rather than use old one findVisibleDepth(d); //sets m_depth } m_map->setRingBreadth(); setLimits(m_map->m_ringBreadth); build(d); m_map->m_signature = m_signature; delete []m_limits; } void RadialMap::Builder::findVisibleDepth(const Directory* const dir, const unsigned int depth) { //**** because I don't use the same minimumSize criteria as in the visual function // this can lead to incorrect visual representation //**** BUT, you can't set those limits until you know m_depth! //**** also this function doesn't check to see if anything is actually visible // it just assumes that when it reaches a new level everything in it is visible // automatically. This isn't right especially as there might be no files in the // dir provided to this function! static uint stopDepth = 0; if (dir == m_root) { stopDepth = *m_depth; *m_depth = 0; } if (*m_depth < depth) *m_depth = depth; if (*m_depth >= stopDepth) return; for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) if ((*it)->isDir() && (*it)->size() > m_minSize) findVisibleDepth((Directory *)*it, depth + 1); //if no files greater than min size the depth is still recorded } void RadialMap::Builder::setLimits(const uint &b) //b = breadth? { double size3 = m_root->size() * 3; double pi2B = PI * 2 * b; m_limits = new FileSize [*m_depth + 1]; //FIXME delete! for (unsigned int d = 0; d <= *m_depth; ++d) m_limits[d] = (FileSize)(size3 / (double)(pi2B * (d + 1))); //min is angle that gives 3px outer diameter for that depth } //**** segments currently overlap at edges (i.e. end of first is start of next) bool RadialMap::Builder::build(const Directory* const dir, const unsigned int depth, unsigned int a_start, const unsigned int a_end) { //first iteration: dir == m_root if (dir->fileCount() == 0) //we do fileCount rather than size to avoid chance of divide by zero later return false; FileSize hiddenSize = 0; uint hiddenFileCount = 0; for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) { if ((*it)->size() > m_limits[depth]) { unsigned int a_len = (unsigned int)(5760 * ((double)(*it)->size() / (double)m_root->size())); Segment *s = new Segment(*it, a_start, a_len); (m_signature + depth)->append(s); if ((*it)->isDir()) { if (depth != *m_depth) { //recurse s->m_hasHiddenChildren = build((Directory*) * it, depth + 1, a_start, a_start + a_len); } else s->m_hasHiddenChildren = true; } a_start += a_len; //**** should we add 1? } else { hiddenSize += (*it)->size(); if ((*it)->isDir()) //**** considered virtual, but dir wouldn't count itself! hiddenFileCount += static_cast(*it)->fileCount(); //need to add one to count the dir as well ++hiddenFileCount; } } if (hiddenFileCount == dir->fileCount() && !Config::showSmallFiles) return true; else if ((Config::showSmallFiles && hiddenSize > m_limits[depth]) || (depth == 0 && (hiddenSize > dir->size() / 8)) /*|| > size() * 0.75*/) { //append a segment for unrepresented space - a "fake" segment const QString s = i18np("%1 file: ~ %2", "%1 files: ~ %2", QLocale().toString(hiddenFileCount), File::humanReadableSize(hiddenSize / hiddenFileCount)); (m_signature + depth)->append(new Segment(new File(s, hiddenSize), a_start, a_end - a_start, true)); } return false; } diff --git a/krusader/DiskUsage/radialMap/builder.h b/krusader/DiskUsage/radialMap/builder.h index 7ce4ca64..264b866e 100644 --- a/krusader/DiskUsage/radialMap/builder.h +++ b/krusader/DiskUsage/radialMap/builder.h @@ -1,52 +1,53 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef BUILDER_H #define BUILDER_H #include "radialMap.h" //Segment, defines #include "fileTree.h" template class Chain; namespace RadialMap { class Map; //temporary class that builds the Map signature class Builder { public: Builder(Map*, const Directory* const, bool fast = false); private: void findVisibleDepth(const Directory* const dir, const uint = 0); void setLimits(const uint&); bool build(const Directory* const, const uint = 0, uint = 0, const uint = 5760); Map *m_map; const Directory* const m_root; const FileSize m_minSize; uint *m_depth; Chain *m_signature; FileSize *m_limits; }; } #endif diff --git a/krusader/DiskUsage/radialMap/labels.cpp b/krusader/DiskUsage/radialMap/labels.cpp index 4b6dea3c..dc1454c0 100644 --- a/krusader/DiskUsage/radialMap/labels.cpp +++ b/krusader/DiskUsage/radialMap/labels.cpp @@ -1,364 +1,365 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * + * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This program 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. * * * * This package 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 this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ // QtCore #include // QtGui #include #include #include #include #include "Config.h" #include "fileTree.h" #include "radialMap.h" #include "sincos.h" #include "widget.h" namespace RadialMap { struct Label { Label(const RadialMap::Segment *s, int l) : segment(s), lvl(l), a(segment->start() + (segment->length() / 2)) { } bool tooClose(const int &aa) const { return (a > aa - LABEL_ANGLE_MARGIN && a < aa + LABEL_ANGLE_MARGIN); } const RadialMap::Segment *segment; const unsigned int lvl; const int a; int x1, y1, x2, y2, x3; int tx, ty; QString qs; }; class LabelList : public QList