diff --git a/app/compressfileitemaction.cpp b/app/compressfileitemaction.cpp index 9edf1f0d..bcfddc2f 100644 --- a/app/compressfileitemaction.cpp +++ b/app/compressfileitemaction.cpp @@ -1,99 +1,95 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2016 Elvis Angelaccio * * 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 "compressfileitemaction.h" #include #include #include #include #include #include "pluginmanager.h" K_PLUGIN_FACTORY_WITH_JSON(CompressFileItemActionFactory, "compressfileitemaction.json", registerPlugin();) using namespace Kerfuffle; CompressFileItemAction::CompressFileItemAction(QObject* parent, const QVariantList&) : KAbstractFileItemActionPlugin(parent) , m_pluginManager(new PluginManager(this)) {} QList CompressFileItemAction::actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget) { // #268163: don't offer compression on already compressed archives. if (m_pluginManager->supportedMimeTypes().contains(fileItemInfos.mimeType())) { return {}; } QList actions; const QIcon icon = QIcon::fromTheme(QStringLiteral("ark")); QMenu *compressMenu = new QMenu(parentWidget); compressMenu->addAction(createAction(icon, i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Here (as TAR.GZ)"), parentWidget, fileItemInfos.urlList(), QStringLiteral("ark --changetofirstpath --add --autofilename tar.gz %F"))); const QMimeType zipMime = QMimeDatabase().mimeTypeForName(QStringLiteral("application/zip")); // Don't offer zip compression if no zip plugin is available. if (!m_pluginManager->preferredWritePluginsFor(zipMime).isEmpty()) { compressMenu->addAction(createAction(icon, i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Here (as ZIP)"), parentWidget, fileItemInfos.urlList(), QStringLiteral("ark --changetofirstpath --add --autofilename zip %F"))); } compressMenu->addAction(createAction(icon, i18nc("@action:inmenu Part of Compress submenu in Dolphin context menu", "Compress to..."), parentWidget, fileItemInfos.urlList(), QStringLiteral("ark --add --changetofirstpath --dialog %F"))); QAction *compressMenuAction = new QAction(i18nc("@action:inmenu Compress submenu in Dolphin context menu", "Compress"), parentWidget); compressMenuAction->setMenu(compressMenu); - - // #189177: disable compress menu in read-only folders. - if (!fileItemInfos.supportsWriting()) { - compressMenuAction->setEnabled(false); - } + compressMenuAction->setEnabled(fileItemInfos.supportsWriting() && !m_pluginManager->availableWritePlugins().isEmpty()); actions << compressMenuAction; return actions; } QAction *CompressFileItemAction::createAction(const QIcon& icon, const QString& name, QWidget *parent, const QList& urls, const QString& exec) { QAction *action = new QAction(icon, name, parent); connect(action, &QAction::triggered, this, [exec, urls, parent]() { KRun::run(exec, urls, parent); }); return action; } #include "compressfileitemaction.moc" diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index e2268046..71f60acd 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -1,350 +1,354 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2002-2003: Georg Robbers * Copyright (C) 2003: Helio Chissini de Castro * Copyright (C) 2007 Henrique Pinto * Copyright (C) 2008 Harald Hvaal * * 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 "mainwindow.h" #include "ark_debug.h" #include "archive_kerfuffle.h" #include "createdialog.h" #include "settingsdialog.h" #include "settingspage.h" #include "pluginmanager.h" #include "interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool isValidArchiveDrag(const QMimeData *data) { return ((data->hasUrls()) && (data->urls().count() == 1)); } MainWindow::MainWindow(QWidget *) : KParts::MainWindow() { setupActions(); setAcceptDrops(true); // Ark doesn't provide a fullscreen mode; remove the corresponding window button setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint); } MainWindow::~MainWindow() { if (m_recentFilesAction) { m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group("Recent Files")); } guiFactory()->removeClient(m_part); delete m_part; m_part = nullptr; } void MainWindow::dragEnterEvent(QDragEnterEvent * event) { qCDebug(ARK) << "dragEnterEvent" << event; Interface *iface = qobject_cast(m_part); if (iface->isBusy()) { return; } const bool partAcceptsDrops = !m_part->url().isEmpty() && m_part->isReadWrite(); if (!event->source() && isValidArchiveDrag(event->mimeData()) && !partAcceptsDrops) { event->acceptProposedAction(); } return; } void MainWindow::dropEvent(QDropEvent * event) { qCDebug(ARK) << "dropEvent" << event; Interface *iface = qobject_cast(m_part); if (iface->isBusy()) { return; } if ((event->source() == nullptr) && (isValidArchiveDrag(event->mimeData()))) { event->acceptProposedAction(); } //TODO: if this call provokes a message box the drag will still be going //while the box is onscreen. looks buggy, do something about it openUrl(event->mimeData()->urls().at(0)); } void MainWindow::dragMoveEvent(QDragMoveEvent * event) { qCDebug(ARK) << "dragMoveEvent" << event; Interface *iface = qobject_cast(m_part); if (iface->isBusy()) { return; } if ((event->source() == nullptr) && (isValidArchiveDrag(event->mimeData()))) { event->acceptProposedAction(); } } bool MainWindow::loadPart() { KPluginFactory *factory = nullptr; const auto plugins = KPluginLoader::findPlugins(QString(), [](const KPluginMetaData& metaData) { return metaData.pluginId() == QStringLiteral("arkpart") && metaData.serviceTypes().contains(QStringLiteral("KParts/ReadOnlyPart")) && metaData.serviceTypes().contains(QStringLiteral("Browser/View")); }); if (!plugins.isEmpty()) { factory = KPluginLoader(plugins.first().fileName()).factory(); } m_part = factory ? static_cast(factory->create(this)) : nullptr; if (!m_part) { KMessageBox::error(this, i18n("Unable to find Ark's KPart component, please check your installation.")); qCWarning(ARK) << "Error loading Ark KPart."; return false; } m_part->setObjectName(QStringLiteral("ArkPart")); setCentralWidget(m_part->widget()); setXMLFile(QStringLiteral("arkui.rc")); setupGUI(ToolBar | Keys | Save); createGUI(m_part); statusBar()->hide(); connect(m_part, SIGNAL(ready()), this, SLOT(updateActions())); connect(m_part, SIGNAL(quit()), this, SLOT(quit())); // #365200: this will disable m_recentFilesAction, while openUrl() will enable it. // So updateActions() needs to be called after openUrl() returns. connect(m_part, SIGNAL(busy()), this, SLOT(updateActions()), Qt::QueuedConnection); connect(m_part, QOverload<>::of(&KParts::ReadOnlyPart::completed), this, &MainWindow::addPartUrl); + updateActions(); + return true; } void MainWindow::setupActions() { m_newAction = KStandardAction::openNew(this, &MainWindow::newArchive, nullptr); actionCollection()->addAction(QStringLiteral("ark_file_new"), m_newAction); m_openAction = KStandardAction::open(this, &MainWindow::openArchive, nullptr); actionCollection()->addAction(QStringLiteral("ark_file_open"), m_openAction); auto quitAction = KStandardAction::quit(this, &MainWindow::quit, nullptr); actionCollection()->addAction(QStringLiteral("ark_quit"), quitAction); m_recentFilesAction = KStandardAction::openRecent(this, &MainWindow::openUrl, nullptr); actionCollection()->addAction(QStringLiteral("ark_file_open_recent"), m_recentFilesAction); m_recentFilesAction->setToolBarMode(KRecentFilesAction::MenuMode); m_recentFilesAction->setToolButtonPopupMode(QToolButton::DelayedPopup); m_recentFilesAction->setIconText(i18nc("action, to open an archive", "Open")); m_recentFilesAction->setToolTip(i18n("Open an archive")); m_recentFilesAction->loadEntries(KSharedConfig::openConfig()->group("Recent Files")); KStandardAction::preferences(this, &MainWindow::showSettings, actionCollection()); } void MainWindow::updateActions() { Interface *iface = qobject_cast(m_part); - m_newAction->setEnabled(!iface->isBusy()); + Kerfuffle::PluginManager pluginManager; + m_newAction->setEnabled(!iface->isBusy() && !pluginManager.availableWritePlugins().isEmpty()); m_openAction->setEnabled(!iface->isBusy()); m_recentFilesAction->setEnabled(!iface->isBusy()); } void MainWindow::openArchive() { Interface *iface = qobject_cast(m_part); Q_ASSERT(iface); Q_UNUSED(iface); Kerfuffle::PluginManager pluginManager; auto dlg = new QFileDialog(this, i18nc("to open an archive", "Open Archive")); dlg->setMimeTypeFilters(pluginManager.supportedMimeTypes(Kerfuffle::PluginManager::SortByComment)); dlg->setFileMode(QFileDialog::ExistingFile); dlg->setAcceptMode(QFileDialog::AcceptOpen); connect(dlg, &QDialog::finished, this, [this, dlg](int result) { if (result == QDialog::Accepted) { openUrl(dlg->selectedUrls().at(0)); } dlg->deleteLater(); }); dlg->open(); } void MainWindow::openUrl(const QUrl& url) { if (url.isEmpty()) { return; } m_part->setArguments(m_openArgs); m_part->openUrl(url); } void MainWindow::setShowExtractDialog(bool option) { if (option) { m_openArgs.metaData()[QStringLiteral("showExtractDialog")] = QStringLiteral("true"); } else { m_openArgs.metaData().remove(QStringLiteral("showExtractDialog")); } } void MainWindow::closeEvent(QCloseEvent *event) { // Preview windows don't have a parent, so we need to manually close them. foreach (QWidget *widget, qApp->topLevelWidgets()) { if (widget->isVisible()) { widget->close(); } } KParts::MainWindow::closeEvent(event); } void MainWindow::quit() { close(); } void MainWindow::showSettings() { if (KConfigDialog::showDialog(QStringLiteral("settings"))) { return; } Interface *iface = qobject_cast(m_part); Q_ASSERT(iface); auto dialog = new Kerfuffle::SettingsDialog(this, QStringLiteral("settings"), iface->config()); foreach (Kerfuffle::SettingsPage *page, iface->settingsPages(this)) { dialog->addPage(page, page->name(), page->iconName()); connect(dialog, &KConfigDialog::settingsChanged, page, &Kerfuffle::SettingsPage::slotSettingsChanged); connect(dialog, &Kerfuffle::SettingsDialog::defaultsButtonClicked, page, &Kerfuffle::SettingsPage::slotDefaultsButtonClicked); } // Hide the icons list if only one page has been added. dialog->setFaceType(KPageDialog::Auto); dialog->setModal(true); connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::writeSettings); + connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::updateActions); dialog->show(); } void MainWindow::writeSettings() { Interface *iface = qobject_cast(m_part); Q_ASSERT(iface); iface->config()->save(); } void MainWindow::addPartUrl() { m_recentFilesAction->addUrl(m_part->url()); } void MainWindow::newArchive() { qCDebug(ARK) << "Creating new archive"; Interface *iface = qobject_cast(m_part); Q_ASSERT(iface); Q_UNUSED(iface); QPointer dialog = new Kerfuffle::CreateDialog( nullptr, // parent i18n("Create New Archive"), // caption QUrl()); // startDir if (dialog.data()->exec()) { const QUrl saveFileUrl = dialog.data()->selectedUrl(); const QString password = dialog.data()->password(); const QString fixedMimeType = dialog.data()->currentMimeType().name(); qCDebug(ARK) << "CreateDialog returned URL:" << saveFileUrl.toString(); qCDebug(ARK) << "CreateDialog returned mime:" << fixedMimeType; m_openArgs.metaData()[QStringLiteral("createNewArchive")] = QStringLiteral("true"); m_openArgs.metaData()[QStringLiteral("fixedMimeType")] = fixedMimeType; if (dialog.data()->compressionLevel() > -1) { m_openArgs.metaData()[QStringLiteral("compressionLevel")] = QString::number(dialog.data()->compressionLevel()); } if (dialog.data()->volumeSize() > 0) { qCDebug(ARK) << "Setting volume size:" << QString::number(dialog.data()->volumeSize()); m_openArgs.metaData()[QStringLiteral("volumeSize")] = QString::number(dialog.data()->volumeSize()); } if (!dialog.data()->compressionMethod().isEmpty()) { m_openArgs.metaData()[QStringLiteral("compressionMethod")] = dialog.data()->compressionMethod(); } if (!dialog.data()->encryptionMethod().isEmpty()) { m_openArgs.metaData()[QStringLiteral("encryptionMethod")] = dialog.data()->encryptionMethod(); } m_openArgs.metaData()[QStringLiteral("encryptionPassword")] = password; if (dialog.data()->isHeaderEncryptionEnabled()) { m_openArgs.metaData()[QStringLiteral("encryptHeader")] = QStringLiteral("true"); } openUrl(saveFileUrl); m_openArgs.metaData().remove(QStringLiteral("showExtractDialog")); m_openArgs.metaData().remove(QStringLiteral("createNewArchive")); m_openArgs.metaData().remove(QStringLiteral("fixedMimeType")); m_openArgs.metaData().remove(QStringLiteral("compressionLevel")); m_openArgs.metaData().remove(QStringLiteral("encryptionPassword")); m_openArgs.metaData().remove(QStringLiteral("encryptHeader")); } delete dialog.data(); }