diff --git a/CMakeLists.txt b/CMakeLists.txt index bf31d0db..d6b6ecdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,109 +1,108 @@ cmake_minimum_required (VERSION 3.0) # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "20") set (RELEASE_SERVICE_VERSION_MINOR "03") set (RELEASE_SERVICE_VERSION_MICRO "70") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(ark VERSION ${RELEASE_SERVICE_VERSION}) set(QT_MIN_VERSION 5.10.0) set(KF5_MIN_VERSION 5.61.0) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(GenerateExportHeader) include(FeatureSummary) include(ECMQtDeclareLoggingCategory) include(ECMSetupVersion) add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) ecm_setup_version(${RELEASE_SERVICE_VERSION} VARIABLE_PREFIX ARK VERSION_HEADER "ark_version.h") ecm_setup_version(PROJECT VARIABLE_PREFIX KERFUFFLE) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Concurrent Core Gui Widgets) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Archive Config Crash DBusAddons DocTools I18n - IconThemes ItemModels KIO Service Parts Pty WidgetsAddons) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET) set_package_properties(Qt5Test PROPERTIES PURPOSE "Required for tests" TYPE OPTIONAL) if(NOT Qt5Test_FOUND) set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree.") endif() find_package(LibArchive 3.3.3 REQUIRED) set_package_properties(LibArchive PROPERTIES URL "https://www.libarchive.org/" DESCRIPTION "A library for dealing with a wide variety of archive file formats" PURPOSE "Required for among others tar, tar.gz, tar.bz2 formats in Ark.") find_package(LibZip 1.3.0) set_package_properties(LibZip PROPERTIES URL "https://nih.at/libzip/" DESCRIPTION "A library for handling zip archives" PURPOSE "Optional for zip archives.") option(WITH_TEST_COVERAGE "Build with test coverage support" OFF) if (WITH_TEST_COVERAGE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") endif (WITH_TEST_COVERAGE) set(SUPPORTED_ARK_MIMETYPES "") add_definitions(-DTRANSLATION_DOMAIN="ark") # Until kf5 5.56 kconfig had some header which used Q_FOREACH # Q_FOREACH will be deprecated/removed in qt6. With QT_NO_FOREACH we prepare the migration to qt6. if (KF5Config_VERSION VERSION_GREATER "5.56.0") add_definitions(-DQT_NO_FOREACH) endif() add_subdirectory(plugins) add_subdirectory(kerfuffle) add_subdirectory(part) add_subdirectory(app) add_subdirectory(doc) if(BUILD_TESTING) add_subdirectory(autotests) endif() ki18n_install(po) kdoctools_install(po) if (NOT ECM_VERSION VERSION_LESS "5.59.0") install(FILES ark.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) else() install(FILES ark.categories DESTINATION ${KDE_INSTALL_CONFDIR}) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kerfuffle/CMakeLists.txt b/kerfuffle/CMakeLists.txt index 6352cb72..0014d38a 100644 --- a/kerfuffle/CMakeLists.txt +++ b/kerfuffle/CMakeLists.txt @@ -1,76 +1,75 @@ ########### next target ############### set(kerfuffle_SRCS archiveformat.cpp archive_kerfuffle.cpp archiveinterface.cpp extractionsettingspage.cpp generalsettingspage.cpp previewsettingspage.cpp settingsdialog.cpp settingspage.cpp jobs.cpp adddialog.cpp compressionoptionswidget.cpp createdialog.cpp extractiondialog.cpp propertiesdialog.cpp queries.cpp addtoarchive.cpp cliinterface.cpp cliproperties.cpp mimetypes.cpp plugin.cpp pluginmanager.cpp pluginsettingspage.cpp archiveentry.cpp options.cpp ) kconfig_add_kcfg_files(kerfuffle_SRCS settings.kcfgc GENERATE_MOC) ki18n_wrap_ui(kerfuffle_SRCS createdialog.ui extractiondialog.ui extractionsettingspage.ui generalsettingspage.ui pluginsettingspage.ui previewsettingspage.ui propertiesdialog.ui compressionoptionswidget.ui ) ecm_qt_declare_logging_category(kerfuffle_SRCS HEADER ark_debug.h IDENTIFIER ARK CATEGORY_NAME ark.kerfuffle) add_library(kerfuffle SHARED ${kerfuffle_SRCS}) generate_export_header(kerfuffle BASE_NAME kerfuffle) if (APPLE) target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL="otool") target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL_ARGS="-L") else() target_compile_definitions(kerfuffle PRIVATE -DDEPENDENCY_TOOL="ldd") endif() target_link_libraries(kerfuffle PUBLIC - KF5::IconThemes KF5::Pty KF5::I18n KF5::WidgetsAddons PRIVATE Qt5::Concurrent KF5::KIOCore KF5::KIOWidgets KF5::KIOFileWidgets ) set_target_properties(kerfuffle PROPERTIES VERSION ${KERFUFFLE_VERSION_STRING} SOVERSION ${KERFUFFLE_SOVERSION}) install(TARGETS kerfuffle ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) install(FILES kerfufflePlugin.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) install(FILES ark.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) diff --git a/kerfuffle/extractiondialog.cpp b/kerfuffle/extractiondialog.cpp index db5b3987..8841b5a1 100644 --- a/kerfuffle/extractiondialog.cpp +++ b/kerfuffle/extractiondialog.cpp @@ -1,327 +1,326 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2009 Harald Hvaal * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "extractiondialog.h" #include "ark_debug.h" #include "settings.h" #include #include -#include #include #include #include #include #include #include "ui_extractiondialog.h" namespace Kerfuffle { class ExtractionDialogUI: public QFrame, public Ui::ExtractionDialog { Q_OBJECT public: ExtractionDialogUI(QWidget *parent = nullptr) : QFrame(parent) { setupUi(this); } }; ExtractionDialog::ExtractionDialog(QWidget *parent) : QDialog(parent, Qt::Dialog) { setWindowTitle(i18nc("@title:window", "Extract")); QHBoxLayout *hlayout = new QHBoxLayout(); setLayout(hlayout); fileWidget = new KFileWidget(QUrl::fromLocalFile(QDir::homePath()), this); hlayout->addWidget(fileWidget); fileWidget->setMode(KFile::Directory | KFile::LocalOnly | KFile::ExistingOnly); fileWidget->setOperationMode(KFileWidget::Saving); // This signal is emitted e.g. when the user presses Return while in the location bar. connect(fileWidget, &KFileWidget::accepted, this, &ExtractionDialog::slotAccepted); fileWidget->okButton()->setText(i18n("Extract")); fileWidget->okButton()->show(); connect(fileWidget->okButton(), &QPushButton::clicked, this, &ExtractionDialog::slotAccepted); fileWidget->cancelButton()->show(); connect(fileWidget->cancelButton(), &QPushButton::clicked, this, &QDialog::reject); m_ui = new ExtractionDialogUI(this); hlayout->addWidget(m_ui); - m_ui->iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("archive-extract")).pixmap(IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop))); + m_ui->iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("archive-extract")).pixmap(48)); m_ui->filesToExtractGroupBox->hide(); m_ui->allFilesButton->setChecked(true); m_ui->extractAllLabel->show(); m_ui->autoSubfolders->hide(); loadSettings(); connect(this, &QDialog::finished, this, &ExtractionDialog::writeSettings); } void ExtractionDialog::slotAccepted() { // If an item is selected, enter it if it exists and is a dir. if (!fileWidget->dirOperator()->selectedItems().isEmpty()) { QFileInfo fi(fileWidget->dirOperator()->selectedItems().urlList().first().path()); if (fi.isDir() && fi.exists()) { fileWidget->locationEdit()->clear(); fileWidget->setUrl(QUrl::fromLocalFile(fi.absoluteFilePath())); } return; } // We extract to baseUrl(). const QString destinationPath = fileWidget->baseUrl().path(); // If extracting to a subfolder, we need to do some checks. if (extractToSubfolder()) { // Check if subfolder contains slashes. if (subfolder().contains(QLatin1String( "/" ))) { KMessageBox::error(this, i18n("The subfolder name may not contain the character '/'.")); return; } // Handle existing subfolder. const QString pathWithSubfolder = destinationPath + subfolder(); while (1) { if (QDir(pathWithSubfolder).exists()) { if (QFileInfo(pathWithSubfolder).isDir()) { int overwrite = KMessageBox::questionYesNoCancel(this, xi18nc("@info", "The folder %1 already exists. Are you sure you want to extract here?", pathWithSubfolder), i18n("Folder exists"), KGuiItem(i18n("Extract here")), KGuiItem(i18n("Retry"))); if (overwrite == KMessageBox::No) { // The user clicked Retry. continue; } else if (overwrite == KMessageBox::Cancel) { return; } } else { KMessageBox::detailedError(this, xi18nc("@info", "The folder %1 could not be created.", subfolder()), xi18nc("@info", "%1 already exists, but is not a folder.", subfolder())); return; } } else if (!QDir().mkdir(pathWithSubfolder)) { KMessageBox::detailedError(this, xi18nc("@info", "The folder %1 could not be created.", subfolder()), i18n("Please check your permissions to create it.")); return; } break; } } // Add new destination value to arkrc for quickExtractMenu. KConfigGroup conf(KSharedConfig::openConfig(), "ExtractDialog"); QStringList destHistory = conf.readPathEntry("DirHistory", QStringList()); destHistory.prepend(destinationPath); destHistory.removeDuplicates(); if (destHistory.size() > 10) { destHistory.removeLast(); } conf.writePathEntry ("DirHistory", destHistory); fileWidget->accept(); accept(); } void ExtractionDialog::loadSettings() { setOpenDestinationFolderAfterExtraction(ArkSettings::openDestinationFolderAfterExtraction()); setCloseAfterExtraction(ArkSettings::closeAfterExtraction()); setPreservePaths(ArkSettings::preservePaths()); } void ExtractionDialog::setExtractToSubfolder(bool extractToSubfolder) { m_ui->singleFolderGroup->setChecked(extractToSubfolder && ArkSettings::extractToSubfolder()); } void ExtractionDialog::batchModeOption() { m_ui->autoSubfolders->show(); m_ui->autoSubfolders->setEnabled(true); m_ui->singleFolderGroup->hide(); m_ui->extractAllLabel->setText(i18n("Extract multiple archives")); } void ExtractionDialog::setSubfolder(const QString& subfolder) { m_ui->subfolder->setText(subfolder); } QString ExtractionDialog::subfolder() const { return m_ui->subfolder->text(); } void ExtractionDialog::setBusyGui() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); fileWidget->setEnabled(false); m_ui->setEnabled(false); // TODO: tell the user why the dialog is busy (e.g. "archive is being loaded"). } void ExtractionDialog::setReadyGui() { QApplication::restoreOverrideCursor(); fileWidget->setEnabled(true); m_ui->setEnabled(true); } ExtractionDialog::~ExtractionDialog() { delete m_ui; m_ui = nullptr; } void ExtractionDialog::setShowSelectedFiles(bool value) { if (value) { m_ui->filesToExtractGroupBox->show(); m_ui->selectedFilesButton->setChecked(true); m_ui->extractAllLabel->hide(); } else { m_ui->filesToExtractGroupBox->hide(); m_ui->selectedFilesButton->setChecked(false); m_ui->extractAllLabel->show(); } } bool ExtractionDialog::extractAllFiles() const { return m_ui->allFilesButton->isChecked(); } void ExtractionDialog::setAutoSubfolder(bool value) { m_ui->autoSubfolders->setChecked(value); } bool ExtractionDialog::autoSubfolders() const { return m_ui->autoSubfolders->isChecked(); } bool ExtractionDialog::extractToSubfolder() const { return m_ui->singleFolderGroup->isChecked(); } void ExtractionDialog::setOpenDestinationFolderAfterExtraction(bool value) { m_ui->openFolderCheckBox->setChecked(value); } void ExtractionDialog::setCloseAfterExtraction(bool value) { m_ui->closeAfterExtraction->setChecked(value); } void ExtractionDialog::setPreservePaths(bool value) { m_ui->preservePaths->setChecked(value); } bool ExtractionDialog::preservePaths() const { return m_ui->preservePaths->isChecked(); } bool ExtractionDialog::openDestinationAfterExtraction() const { return m_ui->openFolderCheckBox->isChecked(); } bool ExtractionDialog::closeAfterExtraction() const { return m_ui->closeAfterExtraction->isChecked(); } QUrl ExtractionDialog::destinationDirectory() const { if (extractToSubfolder()) { QUrl subUrl = fileWidget->baseUrl(); if (subUrl.path().endsWith(QDir::separator())) { subUrl.setPath(subUrl.path() + subfolder()); } else { subUrl.setPath(subUrl.path() + QDir::separator() + subfolder()); } return subUrl; } else { return fileWidget->baseUrl(); } } void ExtractionDialog::writeSettings() { ArkSettings::setOpenDestinationFolderAfterExtraction(openDestinationAfterExtraction()); ArkSettings::setCloseAfterExtraction(closeAfterExtraction()); ArkSettings::setPreservePaths(preservePaths()); ArkSettings::self()->save(); // Save dialog window size KConfigGroup group(KSharedConfig::openConfig(), "ExtractDialog"); KWindowConfig::saveWindowSize(windowHandle(), group, KConfigBase::Persistent); } void ExtractionDialog::setCurrentUrl(const QUrl &url) { fileWidget->setUrl(url); } void ExtractionDialog::restoreWindowSize() { // Restore window size from config file, needs a windowHandle so must be called after show() KConfigGroup group(KSharedConfig::openConfig(), "ExtractDialog"); KWindowConfig::restoreWindowSize(windowHandle(), group); } } #include "extractiondialog.moc" diff --git a/kerfuffle/propertiesdialog.cpp b/kerfuffle/propertiesdialog.cpp index 53f80aa1..e56bfe36 100644 --- a/kerfuffle/propertiesdialog.cpp +++ b/kerfuffle/propertiesdialog.cpp @@ -1,145 +1,144 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2016 Ragnar Thomsen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "propertiesdialog.h" #include "archive_kerfuffle.h" #include "ark_debug.h" #include "ui_propertiesdialog.h" #include #include #include #include #include #include -#include #include namespace Kerfuffle { class PropertiesDialogUI: public QWidget, public Ui::PropertiesDialog { Q_OBJECT public: PropertiesDialogUI(QWidget *parent = nullptr) : QWidget(parent) { setupUi(this); } }; PropertiesDialog::PropertiesDialog(QWidget *parent, Archive *archive, qulonglong numberOfFiles, qulonglong numberOfFolders, qulonglong size) : QDialog(parent, Qt::Dialog) { QFileInfo fi(archive->fileName()); setWindowTitle(i18nc("@title:window", "Properties for %1", fi.fileName())); setModal(true); m_ui = new PropertiesDialogUI(this); m_ui->lblArchiveName->setText(archive->fileName()); m_ui->lblArchiveType->setText(archive->mimeType().comment()); m_ui->lblMimetype->setText(archive->mimeType().name()); m_ui->lblCompressionMethods->setText(archive->property("compressionMethods").toStringList().join(QLatin1String(", "))); m_ui->lblReadOnly->setText(archive->isReadOnly() ? i18n("yes") : i18n("no")); m_ui->lblMultiVolume->setText(archive->isMultiVolume() ? i18n("yes (%1 volumes)", archive->numberOfVolumes()) : i18n("no")); m_ui->lblHasComment->setText(archive->hasComment() ? i18n("yes") : i18n("no")); m_ui->lblNumberOfEntries->setText(i18np("%1 file", "%1 files", numberOfFiles) + i18np(", %1 folder", ", %1 folders", numberOfFolders)); m_ui->lblUnpackedSize->setText(KIO::convertSize(size)); m_ui->lblPackedSize->setText(KIO::convertSize(archive->packedSize())); m_ui->lblCompressionRatio->setText(QString::number(float(archive->unpackedSize()) / float(archive->packedSize()), 'f', 1)); m_ui->lblLastModified->setText(fi.lastModified().toString(QStringLiteral("yyyy-MM-dd HH:mm"))); m_ui->lblMD5->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_ui->lblSHA1->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_ui->lblSHA256->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); switch (archive->encryptionType()) { case Archive::Unencrypted: m_ui->lblPasswordProtected->setText(i18n("no")); break; case Archive::Encrypted: m_ui->lblPasswordProtected->setText(i18n("only file contents (%1)", archive->property("encryptionMethods").toStringList().join(QLatin1String(", ")))); break; case Archive::HeaderEncrypted: m_ui->lblPasswordProtected->setText(i18n("yes (%1)", archive->property("encryptionMethods").toStringList().join(QLatin1String(", ")))); break; } // The Sha256 label is populated with 64 chars in the ui file. We fix the // size of the label so the dialog won't resize when the hashes are // calculated. This is an ugly hack and requires e.g. that we use monospace // font for the hashes. m_ui->lblSHA256->setMinimumSize(m_ui->lblSHA256->sizeHint()); m_ui->adjustSize(); setFixedSize(m_ui->size()); // Show an icon representing the mimetype of the archive. QIcon icon = QIcon::fromTheme(archive->mimeType().iconName()); - m_ui->lblIcon->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop))); + m_ui->lblIcon->setPixmap(icon.pixmap(48)); m_ui->lblMD5->setText(i18n("Calculating...")); m_ui->lblSHA1->setText(i18n("Calculating...")); m_ui->lblSHA256->setText(i18n("Calculating...")); showChecksum(QCryptographicHash::Md5, archive->fileName(), m_ui->lblMD5); showChecksum(QCryptographicHash::Sha1, archive->fileName(), m_ui->lblSHA1); showChecksum(QCryptographicHash::Sha256, archive->fileName(), m_ui->lblSHA256); connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); } QString PropertiesDialog::calcHash(QCryptographicHash::Algorithm algorithm, const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { return QString(); } QCryptographicHash hash(algorithm); hash.addData(&file); return QString::fromLatin1(hash.result().toHex()); } void PropertiesDialog::showChecksum(QCryptographicHash::Algorithm algorithm, const QString &fileName, QLabel *label) { // Calculate checksum in another thread. auto futureWatcher = new QFutureWatcher(this); connect(futureWatcher, &QFutureWatcher::finished, this, [=]() { label->setText(futureWatcher->result()); futureWatcher->deleteLater(); }); auto future = QtConcurrent::run(this, &PropertiesDialog::calcHash, algorithm, fileName); futureWatcher->setFuture(future); } #include "propertiesdialog.moc" } diff --git a/part/arkviewer.cpp b/part/arkviewer.cpp index fde4bc52..660817d0 100644 --- a/part/arkviewer.cpp +++ b/part/arkviewer.cpp @@ -1,243 +1,243 @@ /* * ark: A program for modifying archives via a GUI. * * Copyright (C) 2004-2008 Henrique Pinto * * 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "arkviewer.h" #include "ark_debug.h" #include #include -#include #include #include #include #include #include #include #include #include #include +#include #include ArkViewer::ArkViewer() : KParts::MainWindow() { setupUi(this); m_buttonBox->button(QDialogButtonBox::Close)->setShortcut(Qt::Key_Escape); // Bug 369390: This prevents the Enter key from closing the window. m_buttonBox->button(QDialogButtonBox::Close)->setAutoDefault(false); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QMainWindow::close); setXMLFile(QStringLiteral("ark_viewer.rc")); setupGUI(ToolBar); } ArkViewer::~ArkViewer() { if (m_part) { QProgressDialog progressDialog(this); progressDialog.setWindowTitle(i18n("Closing preview")); progressDialog.setLabelText(i18n("Please wait while the preview is being closed...")); progressDialog.setMinimumDuration(500); progressDialog.setModal(true); progressDialog.setCancelButton(nullptr); progressDialog.setRange(0, 0); // #261785: this preview dialog is not modal, so we need to delete // the previewed file ourselves when the dialog is closed; m_part.data()->closeUrl(); if (!m_fileName.isEmpty()) { QFile::remove(m_fileName); } } guiFactory()->removeClient(m_part); delete m_part; } void ArkViewer::view(const QString& fileName) { QMimeDatabase db; QMimeType mimeType = db.mimeTypeForFile(fileName); qCDebug(ARK) << "viewing" << fileName << "with mime type:" << mimeType.name(); KService::Ptr viewer = ArkViewer::getViewer(mimeType.name()); const bool needsExternalViewer = (viewer && !viewer->hasServiceType(QStringLiteral("KParts/ReadOnlyPart"))); if (needsExternalViewer) { // We have already resolved the MIME type and the service above. // So there is no point in using KRun::runUrl() which would need // to do the same again. qCDebug(ARK) << "Using external viewer"; const QList fileUrlList = {QUrl::fromLocalFile(fileName)}; // The last argument (tempFiles) set to true means that the temporary // file will be removed when the viewer application exits. KRun::runService(*viewer, fileUrlList, nullptr, true); return; } qCDebug(ARK) << "Attempting to use internal viewer"; bool viewInInternalViewer = true; if (!viewer) { // No internal viewer available for the file. Ask the user if it // should be previewed as text/plain. qCDebug(ARK) << "Internal viewer not available"; int response; if (!mimeType.isDefault()) { // File has a defined MIME type, and not the default // application/octet-stream. So it could be viewable as // plain text, ask the user. response = KMessageBox::warningContinueCancel(nullptr, xi18n("The internal viewer cannot preview this type of file(%1).Do you want to try to view it as plain text?", mimeType.name()), i18nc("@title:window", "Cannot Preview File"), KGuiItem(i18nc("@action:button", "Preview as Text"), QIcon::fromTheme(QStringLiteral("text-plain"))), KStandardGuiItem::cancel(), QStringLiteral("PreviewAsText_%1").arg(mimeType.name())); } else { // No defined MIME type, or the default application/octet-stream. // There is still a possibility that it could be viewable as plain // text, so ask the user. Not the same as the message/question // above, because the wording and default are different. response = KMessageBox::warningContinueCancel(nullptr, xi18n("The internal viewer cannot preview this unknown type of file.Do you want to try to view it as plain text?"), i18nc("@title:window", "Cannot Preview File"), KGuiItem(i18nc("@action:button", "Preview as Text"), QIcon::fromTheme(QStringLiteral("text-plain"))), KStandardGuiItem::cancel(), QString(), KMessageBox::Dangerous); } if (response == KMessageBox::Cancel) { viewInInternalViewer = false; } else { // set for viewer later mimeType = db.mimeTypeForName(QStringLiteral("text/plain")); } } if (viewInInternalViewer) { qCDebug(ARK) << "Opening internal viewer"; ArkViewer *internalViewer = new ArkViewer(); internalViewer->show(); if (internalViewer->viewInInternalViewer(fileName, mimeType)) { // The internal viewer is showing the file, and will // remove the temporary file in its destructor. So there // is no more to do here. return; } else { KMessageBox::sorry(nullptr, i18n("The internal viewer cannot preview this file.")); delete internalViewer; } } // Only get here if there is no internal viewer available or could be // used for the file, and no external viewer was opened. Nothing can be // done with the temporary file, so remove it now. qCDebug(ARK) << "Removing temporary file:" << fileName; QFile::remove(fileName); } bool ArkViewer::viewInInternalViewer(const QString& fileName, const QMimeType &mimeType) { setWindowFilePath(fileName); // Set icon and comment for the mimetype. - m_iconLabel->setPixmap(QIcon::fromTheme(mimeType.iconName()).pixmap(IconSize(KIconLoader::Small), IconSize(KIconLoader::Small))); + m_iconLabel->setPixmap(QIcon::fromTheme(mimeType.iconName()).pixmap(style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize))); m_commentLabel->setText(mimeType.comment()); // Create the ReadOnlyPart instance. m_part = KMimeTypeTrader::self()->createPartInstanceFromQuery(mimeType.name(), this, this); // Drop the KHTMLPart, if necessary. const KService::Ptr service = KMimeTypeTrader::self()->preferredService(mimeType.name(), QStringLiteral("KParts/ReadOnlyPart")); qCDebug(ARK) << "Preferred service for mimetype" << mimeType.name() << "is" << service->library(); if (service.constData()->desktopEntryName() == QLatin1String("khtml")) { KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(), QStringLiteral("KParts/ReadOnlyPart")); offers.removeFirst(); qCDebug(ARK) << "Removed KHTMLPart from the offers for mimetype" << mimeType.name() << ". Using" << offers.first().constData()->desktopEntryName() << "instead."; m_part = offers.first().constData()->createInstance(this, this); } if (!m_part.data()) { return false; } // Insert the KPart into its placeholder. centralWidget()->layout()->replaceWidget(m_partPlaceholder, m_part.data()->widget()); createGUI(m_part.data()); setAutoSaveSettings(QStringLiteral("Viewer"), true); m_part.data()->openUrl(QUrl::fromLocalFile(fileName)); m_part.data()->widget()->setFocus(); m_fileName = fileName; return true; } KService::Ptr ArkViewer::getViewer(const QString &mimeType) { // No point in even trying to find anything for application/octet-stream if (mimeType == QLatin1String("application/octet-stream")) { return KService::Ptr(); } // Try to get a read-only kpart for the internal viewer KService::List offers = KMimeTypeTrader::self()->query(mimeType, QStringLiteral("KParts/ReadOnlyPart")); auto arkPartIt = std::find_if(offers.begin(), offers.end(), [](KService::Ptr service) { return service->storageId() == QLatin1String("ark_part.desktop"); }); // Use the Ark part only when the mime type matches an archive type directly. // Many file types (e.g. Open Document) are technically just archives // but browsing their internals is typically not what the user wants. if (arkPartIt != offers.end()) { // Not using hasMimeType() as we're explicitly not interested in inheritance. if (!(*arkPartIt)->mimeTypes().contains(mimeType)) { offers.erase(arkPartIt); } } // If we can't find a kpart, try to get an external application if (offers.isEmpty()) { offers = KMimeTypeTrader::self()->query(mimeType, QStringLiteral("Application")); } if (!offers.isEmpty()) { return offers.first(); } else { return KService::Ptr(); } } diff --git a/part/infopanel.cpp b/part/infopanel.cpp index 8ac5ca43..4bf8fedb 100644 --- a/part/infopanel.cpp +++ b/part/infopanel.cpp @@ -1,223 +1,222 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2007 Henrique Pinto * * 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "infopanel.h" #include "archiveentry.h" #include #include -#include #include #include #include using namespace Kerfuffle; -static QPixmap getDesktopIconForName(const QString& name) +QPixmap InfoPanel::getPixmap(const QString& name) { - return QIcon::fromTheme(name).pixmap(IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop)); + return QIcon::fromTheme(name).pixmap(48); } InfoPanel::InfoPanel(ArchiveModel *model, QWidget *parent) : QFrame(parent), m_model(model) { setupUi(this); // Make the file name font bigger than the rest QFont fnt = fileName->font(); if (fnt.pointSize() > -1) { fnt.setPointSize(fnt.pointSize() + 1); } else { fnt.setPixelSize(fnt.pixelSize() + 3); } fileName->setFont(fnt); updateWithDefaults(); } InfoPanel::~InfoPanel() { } void InfoPanel::updateWithDefaults() { - iconLabel->setPixmap(getDesktopIconForName(QStringLiteral("utilities-file-archiver"))); + iconLabel->setPixmap(getPixmap(QStringLiteral("utilities-file-archiver"))); const QString currentFileName = prettyFileName(); if (currentFileName.isEmpty()) { fileName->setText(i18n("No archive loaded")); } else { fileName->setText(currentFileName); } additionalInfo->setText(QString()); hideMetaData(); } QString InfoPanel::prettyFileName() const { if (m_prettyFileName.isEmpty()) { if (m_model->archive()) { QFileInfo fileInfo(m_model->archive()->fileName()); return fileInfo.fileName(); } } return m_prettyFileName; } void InfoPanel::setPrettyFileName(const QString& fileName) { m_prettyFileName = fileName; } void InfoPanel::setIndex(const QModelIndex& index) { if (!index.isValid()) { updateWithDefaults(); } else { const Archive::Entry *entry = m_model->entryForIndex(index); if (!entry) { return; } QMimeDatabase db; QMimeType mimeType; if (entry->isDir()) { mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); } else { mimeType = db.mimeTypeForFile(entry->fullPath(), QMimeDatabase::MatchExtension); } - iconLabel->setPixmap(getDesktopIconForName(mimeType.iconName())); + iconLabel->setPixmap(getPixmap(mimeType.iconName())); if (entry->isDir()) { uint dirs; uint files; entry->countChildren(dirs, files); additionalInfo->setText(KIO::itemsSummaryString(dirs + files, files, dirs, 0, false)); } else if (!entry->property("link").toString().isEmpty()) { additionalInfo->setText(i18n("Symbolic Link")); } else { if (entry->property("size") != 0) { additionalInfo->setText(KIO::convertSize(entry->property("size").toULongLong())); } else { additionalInfo->setText(i18n("Unknown size")); } } const QStringList nameParts = entry->fullPath().split(QLatin1Char( '/' ), QString::SkipEmptyParts); const QString name = (nameParts.count() > 0) ? nameParts.last() : entry->fullPath(); fileName->setText(name); showMetaDataFor(index); } } void InfoPanel::setIndexes(const QModelIndexList &list) { if (list.size() == 0) { setIndex(QModelIndex()); } else if (list.size() == 1) { setIndex(list[ 0 ]); } else { - iconLabel->setPixmap(getDesktopIconForName(QStringLiteral("utilities-file-archiver"))); + iconLabel->setPixmap(getPixmap(QStringLiteral("utilities-file-archiver"))); fileName->setText(i18np("One file selected", "%1 files selected", list.size())); quint64 totalSize = 0; for (const QModelIndex& index : list) { const Archive::Entry *entry = m_model->entryForIndex(index); totalSize += entry->property("size").toULongLong(); } additionalInfo->setText(KIO::convertSize(totalSize)); hideMetaData(); } } void InfoPanel::showMetaData() { m_separator->show(); m_metaDataWidget->show(); } void InfoPanel::hideMetaData() { m_separator->hide(); m_metaDataWidget->hide(); } void InfoPanel::showMetaDataFor(const QModelIndex &index) { showMetaData(); const Archive::Entry *entry = m_model->entryForIndex(index); QMimeDatabase db; QMimeType mimeType; if (entry->isDir()) { mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); } else { mimeType = db.mimeTypeForFile(entry->fullPath(), QMimeDatabase::MatchExtension); } if (entry->isExecutable() && mimeType.isDefault()) { m_typeValueLabel->setText(db.mimeTypeForName(QStringLiteral("application/x-executable")).comment()); } else { m_typeValueLabel->setText(mimeType.comment()); } if (!entry->property("owner").toString().isEmpty()) { m_ownerLabel->show(); m_ownerValueLabel->show(); m_ownerValueLabel->setText(entry->property("owner").toString()); } else { m_ownerLabel->hide(); m_ownerValueLabel->hide(); } if (!entry->property("group").toString().isEmpty()) { m_groupLabel->show(); m_groupValueLabel->show(); m_groupValueLabel->setText(entry->property("group").toString()); } else { m_groupLabel->hide(); m_groupValueLabel->hide(); } if (!entry->property("link").toString().isEmpty()) { m_targetLabel->show(); m_targetValueLabel->show(); m_targetValueLabel->setText(entry->property("link").toString()); } else { m_targetLabel->hide(); m_targetValueLabel->hide(); } if (entry->property("isPasswordProtected").toBool()) { m_passwordLabel->show(); m_passwordValueLabel->show(); } else { m_passwordLabel->hide(); m_passwordValueLabel->hide(); } } diff --git a/part/infopanel.h b/part/infopanel.h index c6255020..3d12a2d3 100644 --- a/part/infopanel.h +++ b/part/infopanel.h @@ -1,72 +1,74 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2007 Henrique Pinto * * 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef INFOPANEL_H #define INFOPANEL_H #include "archivemodel.h" #include "ui_infopanel.h" #include class InfoPanel: public QFrame, Ui::InformationPanel { Q_OBJECT public: explicit InfoPanel(ArchiveModel *model, QWidget *parent = nullptr); ~InfoPanel() override; void setIndex(const QModelIndex &); void setIndexes(const QModelIndexList &list); /** * Returns the file name that is displayed on the info panel. * * @return The current file name. If no pretty name has been * set, it returns the name of the loaded archive. */ QString prettyFileName() const; /** * Sets a different file name for the current open archive. * * This is particularly useful when a temporary archive (from * a remote location) is loaded, and the window title shows the * remote file name and the info panel, by default, would show * the name of the temporary downloaded file. * * @param fileName The new file name. */ void setPrettyFileName(const QString& fileName); void updateWithDefaults(); private: void showMetaData(); void hideMetaData(); void showMetaDataFor(const QModelIndex &index); + QPixmap getPixmap(const QString& name); + ArchiveModel *m_model; QString m_prettyFileName; }; #endif // INFOPANEL_H