diff --git a/CMakeLists.txt b/CMakeLists.txt index e1de82d..a1ecb9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,100 +1,100 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.71.0") # handled by release scripts set(KF5_DEP_VERSION "5.70.0") # handled by release scripts project(KParts VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.70.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(KDEPackageAppTemplates) include(ECMAddQch) include(ECMGenerateExportHeader) - +include(ECMQtDeclareLoggingCategory) include(ECMSetupVersion) set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control how much of deprecated API is build [default=0].") option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") ecm_setup_version(PROJECT VARIABLE_PREFIX KPARTS VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kparts_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5PartsConfigVersion.cmake" SOVERSION 5) set(REQUIRED_QT_VERSION 5.12.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core Widgets Xml) find_package(KF5Config ${KF5_DEP_VERSION} REQUIRED) find_package(KF5CoreAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5I18n ${KF5_DEP_VERSION} REQUIRED) find_package(KF5IconThemes ${KF5_DEP_VERSION} REQUIRED) find_package(KF5KIO ${KF5_DEP_VERSION} REQUIRED) find_package(KF5JobWidgets ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Service ${KF5_DEP_VERSION} REQUIRED) find_package(KF5TextWidgets ${KF5_DEP_VERSION} REQUIRED) find_package(KF5WidgetsAddons ${KF5_DEP_VERSION} REQUIRED) find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED) if(BUILD_TESTING) add_subdirectory( tests ) add_subdirectory( autotests ) endif() add_definitions(-DTRANSLATION_DOMAIN=\"kparts5\") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif() add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050d00) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054700) add_definitions(-DKIOWIDGETS_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054600) # We need KRun for BrowserRun add_definitions(-DQT_NO_FOREACH) add_subdirectory( src ) add_subdirectory(templates) # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Parts") if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Parts_QCH FILE KF5PartsQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5PartsQchTargets.cmake\")") endif() include(CMakePackageConfigHelpers) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5PartsConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5PartsConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5PartsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5PartsConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5PartsTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5PartsTargets.cmake NAMESPACE KF5:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kparts_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 38c7fa7..2f260f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,156 +1,169 @@ set(KParts_LIB_SRCS partbase.cpp part.cpp partloader.cpp openurlarguments.cpp readonlypart.cpp readwritepart.cpp plugin.cpp partmanager.cpp mainwindow.cpp event.cpp guiactivateevent.cpp partactivateevent.cpp partselectevent.cpp browserextension.cpp browserhostextension.cpp browserarguments.cpp liveconnectextension.cpp openurlevent.cpp windowargs.cpp historyprovider.cpp browserinterface.cpp browserrun.cpp browseropenorsavequestion.cpp statusbarextension.cpp scriptableextension.cpp textextension.cpp htmlextension.cpp htmlsettingsinterface.cpp selectorinterface.cpp fileinfoextension.cpp listingfilterextension.cpp listingnotificationextension.cpp ) include(ECMGenerateHeaders) ecm_generate_headers(KParts_CamelCase_HEADERS HEADER_NAMES BrowserArguments BrowserExtension BrowserHostExtension BrowserInterface BrowserOpenOrSaveQuestion BrowserRun Event FileInfoExtension GUIActivateEvent HistoryProvider HtmlExtension HtmlSettingsInterface ListingFilterExtension ListingNotificationExtension LiveConnectExtension MainWindow OpenUrlArguments OpenUrlEvent Part PartActivateEvent PartBase PartLoader PartManager PartSelectEvent Plugin ReadOnlyPart ReadWritePart ScriptableExtension SelectorInterface StatusBarExtension TextExtension WindowArgs REQUIRED_HEADERS KParts_HEADERS PREFIX KParts ) install(FILES ${KParts_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KParts/KParts COMPONENT Devel) +ecm_qt_declare_logging_category(KParts_LIB_SRCS + HEADER kparts_logging.h + IDENTIFIER KPARTSLOG + CATEGORY_NAME kf.parts + DESCRIPTION "KParts" + EXPORT KPARTSLOG +) + add_library(KF5Parts ${KParts_LIB_SRCS}) add_library(KF5::Parts ALIAS KF5Parts) ecm_generate_export_header(KF5Parts EXPORT_FILE_NAME ${KParts_BINARY_DIR}/kparts/kparts_export.h BASE_NAME KParts GROUP_BASE_NAME KF VERSION ${KF5_VERSION} DEPRECATED_BASE_VERSION 0 DEPRECATION_VERSIONS 3.0 4.4 5.0 EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} ) set(KParts_BUILD_INCLUDE_DIRS ${KParts_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(KF5Parts PUBLIC "$") target_include_directories(KF5Parts INTERFACE "$" ) target_link_libraries(KF5Parts PUBLIC KF5::KIOWidgets #browserrun.h uses krun.h KF5::XmlGui # essential to the technology KF5::TextWidgets # needed for KFind, as interface PRIVATE KF5::I18n #few uses of i18n and i18nc, can be probably stripped down KF5::IconThemes #only used by KPart::iconLoader() ) set_target_properties(KF5Parts PROPERTIES VERSION ${KPARTS_VERSION_STRING} SOVERSION ${KPARTS_SOVERSION} EXPORT_NAME Parts ) install(TARGETS KF5Parts EXPORT KF5PartsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES kpart.desktop krop.desktop krwp.desktop browserview.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} ) install(FILES ${KParts_BINARY_DIR}/kparts/kparts_export.h ${KParts_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KParts/kparts COMPONENT Devel ) install(FILES kde_terminal_interface.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KParts ) +ecm_qt_install_logging_categories( + EXPORT KPARTSLOG + FILE kparts.categories + DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} +) if(BUILD_QCH) ecm_add_qch( KF5Parts_QCH NAME KParts BASE_NAME KF5Parts VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KParts_HEADERS} kde_terminal_interface.h MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS KF5KIO_QCH KF5XmlGui_QCH KF5TextWidgets_QCH INCLUDE_DIRS ${KParts_BUILD_INCLUDE_DIRS} BLANK_MACROS KPARTS_EXPORT KPARTS_DEPRECATED KPARTS_DEPRECATED_EXPORT "KPARTS_DEPRECATED_VERSION(x, y, t)" PREDEFINED_MACROS "KIOWIDGETS_ENABLE_DEPRECATED_SINCE(x, y)=1" TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KParts LIB_NAME KF5Parts DEPS "KIOWidgets KXmlGui KTextWidgets" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KParts) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/browserextension.cpp b/src/browserextension.cpp index 56512d1..481cba0 100644 --- a/src/browserextension.cpp +++ b/src/browserextension.cpp @@ -1,336 +1,336 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "browserextension.h" +#include "kparts_logging.h" + #include #include #include #include #include #include #include #include -#include - using namespace KParts; namespace KParts { // Internal class, use to store the status of the actions class KBitArray { public: int val; KBitArray() { val = 0; } bool operator [](int index) { return (val & (1 << index)) ? true : false; } void setBit(int index, bool value) { if (value) { val = val | (1 << index); } else { val = val & ~(1 << index); } } }; class Q_DECL_HIDDEN BrowserExtension::BrowserExtensionPrivate { public: BrowserExtensionPrivate(KParts::ReadOnlyPart *parent) : m_urlDropHandlingEnabled(false), m_browserInterface(nullptr), m_part(parent) {} struct DelayedRequest { QUrl m_delayedURL; KParts::OpenUrlArguments m_delayedArgs; KParts::BrowserArguments m_delayedBrowserArgs; }; QList m_requests; bool m_urlDropHandlingEnabled; KBitArray m_actionStatus; QMap m_actionText; BrowserInterface *m_browserInterface; static void createActionSlotMap(); KParts::ReadOnlyPart *m_part; OpenUrlArguments m_args; BrowserArguments m_browserArgs; }; Q_GLOBAL_STATIC(BrowserExtension::ActionSlotMap, s_actionSlotMap) Q_GLOBAL_STATIC(BrowserExtension::ActionNumberMap, s_actionNumberMap) void BrowserExtension::BrowserExtensionPrivate::createActionSlotMap() { s_actionSlotMap()->insert("cut", SLOT(cut())); s_actionSlotMap()->insert("copy", SLOT(copy())); s_actionSlotMap()->insert("paste", SLOT(paste())); s_actionSlotMap()->insert("print", SLOT(print())); // Tricky. Those aren't actions in fact, but simply methods that a browserextension // can have or not. No need to return them here. //s_actionSlotMap()->insert( "reparseConfiguration", SLOT(reparseConfiguration()) ); //s_actionSlotMap()->insert( "refreshMimeTypes", SLOT(refreshMimeTypes()) ); // Create the action-number map ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin(); ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd(); for (int i = 0; it != itEnd; ++it, ++i) { //qDebug() << " action " << it.key() << " number " << i; s_actionNumberMap()->insert(it.key(), i); } } } BrowserExtension::BrowserExtension(KParts::ReadOnlyPart *parent) : QObject(parent), d(new BrowserExtensionPrivate(parent)) { //qDebug() << "BrowserExtension::BrowserExtension() " << this; if (s_actionSlotMap()->isEmpty()) // Create the action-slot map { BrowserExtensionPrivate::createActionSlotMap(); } // Set the initial status of the actions depending on whether // they're supported or not const QMetaObject *metaobj = metaObject(); ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin(); ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd(); for (int i = 0; it != itEnd; ++it, ++i) { // Does the extension have a slot with the name of this action ? QByteArray slotSig = it.key() + "()"; d->m_actionStatus.setBit(i, metaobj->indexOfMethod(slotSig.constData()) != -1); } connect(d->m_part, static_cast(&KParts::ReadOnlyPart::completed), this, &BrowserExtension::slotCompleted); connect(this, &BrowserExtension::openUrlRequest, this, &BrowserExtension::slotOpenUrlRequest); connect(this, &BrowserExtension::enableAction, this, &BrowserExtension::slotEnableAction); connect(this, &BrowserExtension::setActionText, this, &BrowserExtension::slotSetActionText); } BrowserExtension::~BrowserExtension() { //qDebug() << "BrowserExtension::~BrowserExtension() " << this; delete d; } void BrowserExtension::setBrowserArguments(const BrowserArguments &args) { d->m_browserArgs = args; } BrowserArguments BrowserExtension::browserArguments() const { return d->m_browserArgs; } int BrowserExtension::xOffset() { return 0; } int BrowserExtension::yOffset() { return 0; } void BrowserExtension::saveState(QDataStream &stream) { // TODO add d->m_part->mimeType() stream << d->m_part->url() << static_cast(xOffset()) << static_cast(yOffset()); } void BrowserExtension::restoreState(QDataStream &stream) { QUrl u; qint32 xOfs, yOfs; stream >> u >> xOfs >> yOfs; OpenUrlArguments args; args.setXOffset(xOfs); args.setYOffset(yOfs); // TODO add args.setMimeType d->m_part->setArguments(args); d->m_part->openUrl(u); } bool BrowserExtension::isURLDropHandlingEnabled() const { return d->m_urlDropHandlingEnabled; } void BrowserExtension::setURLDropHandlingEnabled(bool enable) { d->m_urlDropHandlingEnabled = enable; } void BrowserExtension::slotCompleted() { //empty the argument stuff, to avoid bogus/invalid values when opening a new url setBrowserArguments(BrowserArguments()); } void BrowserExtension::pasteRequest() { QString plain(QStringLiteral("plain")); QString url = QApplication::clipboard()->text(plain, QClipboard::Selection).trimmed(); // Remove linefeeds and any whitespace surrounding it. url.remove(QRegularExpression(QStringLiteral("[\\ ]*\\n+[\\ ]*"))); // Check if it's a URL QStringList filters = KUriFilter::self()->pluginNames(); filters.removeAll(QStringLiteral("kuriikwsfilter")); filters.removeAll(QStringLiteral("localdomainurifilter")); KUriFilterData filterData; filterData.setData(url); filterData.setCheckForExecutables(false); if (KUriFilter::self()->filterUri(filterData, filters)) { switch (filterData.uriType()) { case KUriFilterData::LocalFile: case KUriFilterData::LocalDir: case KUriFilterData::NetProtocol: slotOpenUrlRequest(filterData.uri()); break; case KUriFilterData::Error: KMessageBox::sorry(d->m_part->widget(), filterData.errorMsg()); break; default: break; } } else if (KUriFilter::self()->filterUri(filterData, QStringList(QStringLiteral("kuriikwsfilter"))) && url.length() < 250) { if (KMessageBox::questionYesNo(d->m_part->widget(), i18n("Do you want to search the Internet for %1?", url.toHtmlEscaped()), i18n("Internet Search"), KGuiItem(i18n("&Search"), QStringLiteral("edit-find")), KStandardGuiItem::cancel(), QStringLiteral("MiddleClickSearch")) == KMessageBox::Yes) { slotOpenUrlRequest(filterData.uri()); } } } void BrowserExtension::slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs) { //qDebug() << this << " BrowserExtension::slotOpenURLRequest(): url=" << url.url(); BrowserExtensionPrivate::DelayedRequest req; req.m_delayedURL = url; req.m_delayedArgs = args; req.m_delayedBrowserArgs = browserArgs; d->m_requests.append(req); QTimer::singleShot(0, this, &BrowserExtension::slotEmitOpenUrlRequestDelayed); } void BrowserExtension::slotEmitOpenUrlRequestDelayed() { if (d->m_requests.isEmpty()) { return; } BrowserExtensionPrivate::DelayedRequest req = d->m_requests.front(); d->m_requests.pop_front(); emit openUrlRequestDelayed(req.m_delayedURL, req.m_delayedArgs, req.m_delayedBrowserArgs); // tricky: do not do anything here! (no access to member variables, etc.) } void BrowserExtension::setBrowserInterface(BrowserInterface *impl) { d->m_browserInterface = impl; } BrowserInterface *BrowserExtension::browserInterface() const { return d->m_browserInterface; } void BrowserExtension::slotEnableAction(const char *name, bool enabled) { //qDebug() << "BrowserExtension::slotEnableAction " << name << " " << enabled; ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name); if (it != s_actionNumberMap()->constEnd()) { d->m_actionStatus.setBit(it.value(), enabled); //qDebug() << "BrowserExtension::slotEnableAction setting bit " << it.data() << " to " << enabled; } else { - qWarning() << "BrowserExtension::slotEnableAction unknown action " << name; + qCWarning(KPARTSLOG) << "BrowserExtension::slotEnableAction unknown action " << name; } } bool BrowserExtension::isActionEnabled(const char *name) const { int actionNumber = (*s_actionNumberMap())[ name ]; return d->m_actionStatus[ actionNumber ]; } void BrowserExtension::slotSetActionText(const char *name, const QString &text) { //qDebug() << "BrowserExtension::slotSetActionText " << name << " " << text; ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name); if (it != s_actionNumberMap()->constEnd()) { d->m_actionText[ it.value() ] = text; } else { - qWarning() << "BrowserExtension::slotSetActionText unknown action " << name; + qCWarning(KPARTSLOG) << "BrowserExtension::slotSetActionText unknown action " << name; } } QString BrowserExtension::actionText(const char *name) const { int actionNumber = (*s_actionNumberMap())[ name ]; QMap::ConstIterator it = d->m_actionText.constFind(actionNumber); if (it != d->m_actionText.constEnd()) { return *it; } return QString(); } // for compatibility BrowserExtension::ActionSlotMap BrowserExtension::actionSlotMap() { return *actionSlotMapPtr(); } BrowserExtension::ActionSlotMap *BrowserExtension::actionSlotMapPtr() { if (s_actionSlotMap()->isEmpty()) { BrowserExtensionPrivate::createActionSlotMap(); } return s_actionSlotMap(); } BrowserExtension *BrowserExtension::childObject(QObject *obj) { return obj->findChild(QString(), Qt::FindDirectChildrenOnly); } diff --git a/src/browseropenorsavequestion.cpp b/src/browseropenorsavequestion.cpp index 7e9c48e..b38dd69 100644 --- a/src/browseropenorsavequestion.cpp +++ b/src/browseropenorsavequestion.cpp @@ -1,365 +1,363 @@ /* Copyright (c) 2009, 2010 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "browseropenorsavequestion.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include - using namespace KParts; Q_DECLARE_METATYPE(KService::Ptr) class KParts::BrowserOpenOrSaveQuestionPrivate : public QDialog { Q_OBJECT public: enum { Save = QDialog::Accepted, OpenDefault = Save + 1, OpenWith = OpenDefault + 1, Cancel = QDialog::Rejected }; BrowserOpenOrSaveQuestionPrivate(QWidget *parent, const QUrl &url, const QString &mimeType) : QDialog(parent), url(url), mimeType(mimeType), features(BrowserOpenOrSaveQuestion::BasicFeatures) { const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); // Use askSave or askEmbedOrSave from filetypesrc dontAskConfig = KSharedConfig::openConfig(QStringLiteral("filetypesrc"), KConfig::NoGlobals); setWindowTitle(url.host()); setObjectName(QStringLiteral("questionYesNoCancel")); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setSpacing(spacingHint * 2); // provide extra spacing QHBoxLayout *hLayout = new QHBoxLayout(); mainLayout->addLayout(hLayout, 5); QLabel *iconLabel = new QLabel(this); QStyleOption option; option.initFrom(this); QIcon icon = QIcon::fromTheme(QStringLiteral("dialog-information")); iconLabel->setPixmap(icon.pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this))); hLayout->addWidget(iconLabel, 0, Qt::AlignCenter); hLayout->addSpacing(spacingHint); QVBoxLayout *textVLayout = new QVBoxLayout; questionLabel = new KSqueezedTextLabel(this); textVLayout->addWidget(questionLabel); fileNameLabel = new QLabel(this); fileNameLabel->hide(); textVLayout->addWidget(fileNameLabel); QMimeDatabase db; mime = db.mimeTypeForName(mimeType); QString mimeDescription(mimeType); if (mime.isValid()) { // Always prefer the mime-type comment over the raw type for display mimeDescription = (mime.comment().isEmpty() ? mime.name() : mime.comment()); } QLabel *mimeTypeLabel = new QLabel(this); mimeTypeLabel->setText(i18nc("@label Type of file", "Type: %1", mimeDescription)); mimeTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); textVLayout->addWidget(mimeTypeLabel); hLayout->addLayout(textVLayout, 5); mainLayout->addStretch(15); dontAskAgainCheckBox = new QCheckBox(this); dontAskAgainCheckBox->setText(i18nc("@label:checkbox", "Remember action for files of this type")); mainLayout->addWidget(dontAskAgainCheckBox); buttonBox = new QDialogButtonBox(this); saveButton = buttonBox->addButton(QDialogButtonBox::Yes); saveButton->setObjectName(QStringLiteral("saveButton")); KGuiItem::assign(saveButton, KStandardGuiItem::saveAs()); saveButton->setDefault(true); openDefaultButton = new QPushButton; openDefaultButton->setObjectName(QStringLiteral("openDefaultButton")); buttonBox->addButton(openDefaultButton, QDialogButtonBox::ActionRole); openWithButton = new QPushButton; openWithButton->setObjectName(QStringLiteral("openWithButton")); buttonBox->addButton(openWithButton, QDialogButtonBox::ActionRole); QPushButton *cancelButton = buttonBox->addButton(QDialogButtonBox::Cancel); cancelButton->setObjectName(QStringLiteral("cancelButton")); connect(saveButton, &QPushButton::clicked, this, &BrowserOpenOrSaveQuestionPrivate::slotYesClicked); connect(openDefaultButton, &QPushButton::clicked, this, &BrowserOpenOrSaveQuestionPrivate::slotOpenDefaultClicked); connect(openWithButton, &QPushButton::clicked, this, &BrowserOpenOrSaveQuestionPrivate::slotOpenWithClicked); connect(buttonBox, &QDialogButtonBox::rejected, this, &BrowserOpenOrSaveQuestionPrivate::reject); mainLayout->addWidget(buttonBox); } bool autoEmbedMimeType(int flags); int executeDialog(const QString &dontShowAgainName) { KConfigGroup cg(dontAskConfig, "Notification Messages"); // group name comes from KMessageBox const QString dontAsk = cg.readEntry(dontShowAgainName, QString()).toLower(); if (dontAsk == QLatin1String("yes") || dontAsk == QLatin1String("true")) { return Save; } else if (dontAsk == QLatin1String("no") || dontAsk == QLatin1String("false")) { return OpenDefault; } const int result = exec(); if (dontAskAgainCheckBox->isChecked()) { cg.writeEntry(dontShowAgainName, result == BrowserOpenOrSaveQuestion::Save); cg.sync(); } return result; } void showService(KService::Ptr selectedService) { KGuiItem openItem(i18nc("@label:button", "&Open with %1", selectedService->name()), selectedService->icon()); KGuiItem::assign(openWithButton, openItem); } QUrl url; QString mimeType; QMimeType mime; KService::Ptr selectedService; KSqueezedTextLabel *questionLabel; BrowserOpenOrSaveQuestion::Features features; QLabel *fileNameLabel; QDialogButtonBox *buttonBox; QPushButton *saveButton; QPushButton *openDefaultButton; QPushButton *openWithButton; private: QCheckBox *dontAskAgainCheckBox; KSharedConfig::Ptr dontAskConfig; public Q_SLOTS: void reject() override { selectedService = nullptr; QDialog::reject(); } void slotYesClicked() { selectedService = nullptr; done(Save); } void slotOpenDefaultClicked() { done(OpenDefault); } void slotOpenWithClicked() { if (!openWithButton->menu()) { selectedService = nullptr; done(OpenWith); } } void slotAppSelected(QAction *action) { selectedService = action->data().value(); //showService(selectedService); done(OpenDefault); } }; BrowserOpenOrSaveQuestion::BrowserOpenOrSaveQuestion(QWidget *parent, const QUrl &url, const QString &mimeType) : d(new BrowserOpenOrSaveQuestionPrivate(parent, url, mimeType)) { } BrowserOpenOrSaveQuestion::~BrowserOpenOrSaveQuestion() { delete d; } static QAction *createAppAction(const KService::Ptr &service, QObject *parent) { QString actionName(service->name().replace(QLatin1Char('&'), QLatin1String("&&"))); actionName = i18nc("@action:inmenu", "Open &with %1", actionName); QAction *act = new QAction(parent); act->setIcon(QIcon::fromTheme(service->icon())); act->setText(actionName); act->setData(QVariant::fromValue(service)); return act; } BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askOpenOrSave() { d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.toDisplayString(QUrl::PreferLocalFile))); d->questionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); d->openWithButton->hide(); KGuiItem openWithDialogItem(i18nc("@label:button", "&Open with..."), QStringLiteral("document-open")); // I thought about using KFileItemActions, but we don't want a submenu, nor the slots.... // and we want no menu at all if there's only one offer. // TODO: we probably need a setTraderConstraint(), to exclude the current application? const KService::List apps = KFileItemActions::associatedApplications(QStringList() << d->mimeType, QString() /* TODO trader constraint */); if (apps.isEmpty()) { KGuiItem::assign(d->openDefaultButton, openWithDialogItem); } else { KService::Ptr offer = apps.first(); KGuiItem openItem(i18nc("@label:button", "&Open with %1", offer->name()), offer->icon()); KGuiItem::assign(d->openDefaultButton, openItem); if (d->features & ServiceSelection) { // OpenDefault shall use this service d->selectedService = apps.first(); d->openWithButton->show(); QMenu *menu = new QMenu(d); if (apps.count() > 1) { // Provide an additional button with a menu of associated apps KGuiItem openWithItem(i18nc("@label:button", "&Open with"), QStringLiteral("document-open")); KGuiItem::assign(d->openWithButton, openWithItem); d->openWithButton->setMenu(menu); QObject::connect(menu, &QMenu::triggered, d, &BrowserOpenOrSaveQuestionPrivate::slotAppSelected); for (const auto &app : apps) { QAction *act = createAppAction(app, d); menu->addAction(act); } QAction *openWithDialogAction = new QAction(d); openWithDialogAction->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); openWithDialogAction->setText(openWithDialogItem.text()); menu->addAction(openWithDialogAction); } else { // Only one associated app, already offered by the other menu -> add "Open With..." button KGuiItem::assign(d->openWithButton, openWithDialogItem); } } else { // qDebug() << "Not using new feature ServiceSelection; port the caller to BrowserOpenOrSaveQuestion::setFeature(ServiceSelection)"; } } // KEEP IN SYNC with kdebase/runtime/keditfiletype/filetypedetails.cpp!!! const QString dontAskAgain = QLatin1String("askSave") + d->mimeType; const int choice = d->executeDialog(dontAskAgain); return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Open); } KService::Ptr BrowserOpenOrSaveQuestion::selectedService() const { return d->selectedService; } bool BrowserOpenOrSaveQuestionPrivate::autoEmbedMimeType(int flags) { // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC // NOTE: Keep this function in sync with // kdebase/runtime/keditfiletype/filetypedetails.cpp // FileTypeDetails::updateAskSave() // Don't ask for: // - html (even new tabs would ask, due to about:blank!) // - dirs obviously (though not common over HTTP :), // - images (reasoning: no need to save, most of the time, because fast to see) // e.g. postscript is different, because takes longer to read, so // it's more likely that the user might want to save it. // - multipart/* ("server push", see kmultipart) // KEEP IN SYNC!!! if (flags != static_cast(BrowserOpenOrSaveQuestion::AttachmentDisposition) && mime.isValid() && ( mime.inherits(QStringLiteral("text/html")) || mime.inherits(QStringLiteral("application/xml")) || mime.inherits(QStringLiteral("inode/directory")) || mimeType.startsWith(QLatin1String("image")) || mime.inherits(QStringLiteral("multipart/x-mixed-replace")) || mime.inherits(QStringLiteral("multipart/replace")))) { return true; } return false; } BrowserOpenOrSaveQuestion::Result BrowserOpenOrSaveQuestion::askEmbedOrSave(int flags) { if (d->autoEmbedMimeType(flags)) { return Embed; } // don't use KStandardGuiItem::open() here which has trailing ellipsis! KGuiItem::assign(d->openDefaultButton, KGuiItem(i18nc("@label:button", "&Open"), QStringLiteral("document-open"))); d->openWithButton->hide(); d->questionLabel->setText(i18nc("@info", "Open '%1'?", d->url.toDisplayString(QUrl::PreferLocalFile))); d->questionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); const QString dontAskAgain = QLatin1String("askEmbedOrSave") + d->mimeType; // KEEP IN SYNC!!! // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC const int choice = d->executeDialog(dontAskAgain); return choice == BrowserOpenOrSaveQuestionPrivate::Save ? Save : (choice == BrowserOpenOrSaveQuestionPrivate::Cancel ? Cancel : Embed); } void BrowserOpenOrSaveQuestion::setFeatures(Features features) { d->features = features; } void BrowserOpenOrSaveQuestion::setSuggestedFileName(const QString &suggestedFileName) { if (suggestedFileName.isEmpty()) { return; } d->fileNameLabel->setText(i18nc("@label File name", "Name: %1", suggestedFileName)); d->fileNameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); d->fileNameLabel->setWhatsThis(i18nc("@info:whatsthis", "This is the file name suggested by the server")); d->fileNameLabel->show(); } #include "browseropenorsavequestion.moc" diff --git a/src/browserrun.cpp b/src/browserrun.cpp index ca023ad..ec6afc2 100644 --- a/src/browserrun.cpp +++ b/src/browserrun.cpp @@ -1,588 +1,589 @@ /* This file is part of the KDE project * * Copyright (C) 2002 David Faure * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2, as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "browserrun.h" #include "browseropenorsavequestion.h" +#include "kparts_logging.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #if KIOWIDGETS_ENABLE_DEPRECATED_SINCE(5, 71) using namespace KParts; class Q_DECL_HIDDEN BrowserRun::BrowserRunPrivate { public: bool m_bHideErrorDialog; bool m_bRemoveReferrer; bool m_bTrustedSource; KParts::OpenUrlArguments m_args; KParts::BrowserArguments m_browserArgs; KParts::ReadOnlyPart *m_part; // QGuardedPtr? QPointer m_window; QString m_mimeType; QString m_contentDisposition; }; BrowserRun::BrowserRun(const QUrl &url, const KParts::OpenUrlArguments &args, const KParts::BrowserArguments &browserArgs, KParts::ReadOnlyPart *part, QWidget *window, bool removeReferrer, bool trustedSource, bool hideErrorDialog) : KRun(url, window, false /* no GUI */), d(new BrowserRunPrivate) { d->m_bHideErrorDialog = hideErrorDialog; d->m_bRemoveReferrer = removeReferrer; d->m_bTrustedSource = trustedSource; d->m_args = args; d->m_browserArgs = browserArgs; d->m_part = part; d->m_window = window; } BrowserRun::~BrowserRun() { delete d; } KParts::ReadOnlyPart *BrowserRun::part() const { return d->m_part; } QUrl BrowserRun::url() const { return KRun::url(); } void BrowserRun::init() { if (d->m_bHideErrorDialog) { // ### KRun doesn't call a virtual method when it finds out that the URL // is either malformed, or points to a non-existing local file... // So we need to reimplement some of the checks, to handle d->m_bHideErrorDialog if (!KRun::url().isValid()) { redirectToError(KIO::ERR_MALFORMED_URL, KRun::url().toString()); return; } if (isLocalFile()) { const QString localPath = KRun::url().toLocalFile(); if (!QFile::exists(localPath)) { // qDebug() << localPath << "doesn't exist."; redirectToError(KIO::ERR_DOES_NOT_EXIST, localPath); return; } } } KRun::init(); } void BrowserRun::scanFile() { const QUrl url = KRun::url(); // qDebug() << url; // Let's check for well-known extensions // Not when there is a query in the URL, in any case. // Optimization for http/https, findByURL doesn't trust extensions over http. QString protocol = url.scheme(); if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) { QString dummy; protocol = KProtocolManager::slaveProtocol(url, dummy); } if (!url.hasQuery() && !protocol.startsWith(QLatin1String("http")) && (!url.path().endsWith(QLatin1Char('/')) || KProtocolManager::supportsListing(url))) { QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(url); if (!mime.isDefault() || isLocalFile()) { // qDebug() << "MIME TYPE is" << mime.name(); mimeTypeDetermined(mime.name()); return; } } QMap &metaData = d->m_args.metaData(); if (d->m_part) { const QString proto = d->m_part->url().scheme(); if (proto == QLatin1String("https") || proto == QLatin1String("webdavs")) { metaData.insert(QStringLiteral("main_frame_request"), QStringLiteral("TRUE")); metaData.insert(QStringLiteral("ssl_was_in_use"), QStringLiteral("TRUE")); // metaData.insert(QStringLiteral("ssl_activate_warnings"), QStringLiteral("TRUE")); } else if (proto == QLatin1String("http") || proto == QLatin1String("webdav")) { // metaData.insert(QStringLiteral("ssl_activate_warnings"), QStringLiteral("TRUE")); metaData.insert(QStringLiteral("ssl_was_in_use"), QStringLiteral("FALSE")); } // Set the PropagateHttpHeader meta-data if it has not already been set... if (!metaData.contains(QStringLiteral("PropagateHttpHeader"))) { metaData.insert(QStringLiteral("PropagateHttpHeader"), QStringLiteral("TRUE")); } } KIO::TransferJob *job; if (d->m_browserArgs.doPost() && url.scheme().startsWith(QLatin1String("http"))) { job = KIO::http_post(url, d->m_browserArgs.postData, KIO::HideProgressInfo); job->addMetaData(QStringLiteral("content-type"), d->m_browserArgs.contentType()); } else { job = KIO::get(url, d->m_args.reload() ? KIO::Reload : KIO::NoReload, KIO::HideProgressInfo); } if (d->m_bRemoveReferrer) { metaData.remove(QStringLiteral("referrer")); } job->addMetaData(metaData); KJobWidgets::setWindow(job, d->m_window); connect(job, &KIO::TransferJob::result, this, &BrowserRun::slotBrowserScanFinished); connect(job, static_cast(&KIO::TransferJob::mimetype), this, &BrowserRun::slotBrowserMimetype); setJob(job); } void BrowserRun::slotBrowserScanFinished(KJob *job) { // qDebug() << job->error(); if (job->error() == KIO::ERR_IS_DIRECTORY) { // It is in fact a directory. This happens when HTTP redirects to FTP. // Due to the "protocol doesn't support listing" code in BrowserRun, we // assumed it was a file. // qDebug() << "It is in fact a directory!"; // Update our URL in case of a redirection KRun::setUrl(static_cast(job)->url()); setJob(nullptr); mimeTypeDetermined(QStringLiteral("inode/directory")); } else { KRun::slotScanFinished(job); } } static QMimeType fixupMimeType(const QString &mimeType, const QString &fileName) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(mimeType); if ((!mime.isValid() || mime.isDefault()) && !fileName.isEmpty()) { mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension); } return mime; } void BrowserRun::slotBrowserMimetype(KIO::Job *_job, const QString &type) { Q_ASSERT(_job == KRun::job()); Q_UNUSED(_job) KIO::TransferJob *job = static_cast(KRun::job()); // Update our URL in case of a redirection //qDebug() << "old URL=" << KRun::url(); //qDebug() << "new URL=" << job->url(); setUrl(job->url()); if (job->isErrorPage()) { d->m_mimeType = type; handleError(job); setJob(nullptr); } else { // qDebug() << "found" << type << "for" << KRun::url(); // Suggested filename given by the server (e.g. HTTP content-disposition) // When set, we should really be saving instead of embedding const QString suggestedFileName = job->queryMetaData(QStringLiteral("content-disposition-filename")); setSuggestedFileName(suggestedFileName); // store it (in KRun) //qDebug() << "suggestedFileName=" << suggestedFileName; d->m_contentDisposition = job->queryMetaData(QStringLiteral("content-disposition-type")); const QString modificationTime = job->queryMetaData(QStringLiteral("content-disposition-modification-date")); if (!modificationTime.isEmpty()) { d->m_args.metaData().insert(QStringLiteral("content-disposition-modification-date"), modificationTime); } QMapIterator it(job->metaData()); while (it.hasNext()) { it.next(); if (it.key().startsWith(QLatin1String("ssl_"), Qt::CaseInsensitive)) { d->m_args.metaData().insert(it.key(), it.value()); } } // Make a copy to avoid a dead reference QString _type = type; job->putOnHold(); setJob(nullptr); // If the current mime-type is the default mime-type, then attempt to // determine the "real" mimetype from the file name. QMimeType mime = fixupMimeType(_type, suggestedFileName.isEmpty() ? url().fileName() : suggestedFileName); if (mime.isValid() && mime.name() != _type) { _type = mime.name(); } mimeTypeDetermined(_type); } } BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable(const QString &mimeType) { return handleNonEmbeddable(mimeType, nullptr); } BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable(const QString &_mimeType, KService::Ptr *selectedService) { QString mimeType(_mimeType); Q_ASSERT(!hasFinished()); // only come here if the mimetype couldn't be embedded // Support for saving remote files. if (mimeType != QLatin1String("inode/directory") && // dirs can't be saved !KRun::url().isLocalFile()) { if (isTextExecutable(mimeType)) { mimeType = QStringLiteral("text/plain"); // view, don't execute } // ... -> ask whether to save BrowserOpenOrSaveQuestion question(d->m_window, KRun::url(), mimeType); question.setSuggestedFileName(suggestedFileName()); if (selectedService) { question.setFeatures(BrowserOpenOrSaveQuestion::ServiceSelection); } BrowserOpenOrSaveQuestion::Result res = question.askOpenOrSave(); if (res == BrowserOpenOrSaveQuestion::Save) { save(KRun::url(), suggestedFileName()); // qDebug() << "Save: returning Handled"; setFinished(true); return Handled; } else if (res == BrowserOpenOrSaveQuestion::Cancel) { // saving done or canceled // qDebug() << "Cancel: returning Handled"; setFinished(true); return Handled; } else { // "Open" chosen (done by KRun::foundMimeType, called when returning NotHandled) // If we were in a POST, we can't just pass a URL to an external application. // We must save the data to a tempfile first. if (d->m_browserArgs.doPost()) { // qDebug() << "request comes from a POST, can't pass a URL to another app, need to save"; d->m_mimeType = mimeType; QString extension; QString fileName = suggestedFileName().isEmpty() ? KRun::url().fileName() : suggestedFileName(); int extensionPos = fileName.lastIndexOf(QLatin1Char('.')); if (extensionPos != -1) { extension = fileName.mid(extensionPos); // keep the '.' } QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1String("XXXXXX") + extension); tempFile.setAutoRemove(false); tempFile.open(); QUrl destURL = QUrl::fromLocalFile(tempFile.fileName()); KIO::Job *job = KIO::file_copy(KRun::url(), destURL, 0600, KIO::Overwrite); KJobWidgets::setWindow(job, d->m_window); connect(job, &KIO::Job::result, this, &BrowserRun::slotCopyToTempFileResult); return Delayed; // We'll continue after the job has finished } if (selectedService && question.selectedService()) { *selectedService = question.selectedService(); // KRun will use this when starting an app KRun::setPreferredService(question.selectedService()->desktopEntryName()); } } } // Check if running is allowed if (!d->m_bTrustedSource && // ... and untrusted source... !allowExecution(mimeType, KRun::url())) { // ...and the user said no (for executables etc.) setFinished(true); return Handled; } KIO::Scheduler::publishSlaveOnHold(); // publish any slave on hold so it can be reused. return NotHandled; } //static bool BrowserRun::allowExecution(const QString &mimeType, const QUrl &url) { if (!KRun::isExecutable(mimeType)) { return true; } if (!url.isLocalFile()) { // Don't permit to execute remote files return false; } return (KMessageBox::warningContinueCancel(nullptr, i18n("Do you really want to execute '%1'?", url.toDisplayString()), i18n("Execute File?"), KGuiItem(i18n("Execute"))) == KMessageBox::Continue); } //static, deprecated #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0) BrowserRun::AskSaveResult BrowserRun::askSave(const QUrl &url, KService::Ptr offer, const QString &mimeType, const QString &suggestedFileName) { Q_UNUSED(offer); BrowserOpenOrSaveQuestion question(nullptr, url, mimeType); question.setSuggestedFileName(suggestedFileName); const BrowserOpenOrSaveQuestion::Result result = question.askOpenOrSave(); return result == BrowserOpenOrSaveQuestion::Save ? Save : BrowserOpenOrSaveQuestion::Open ? Open : Cancel; } #endif //static, deprecated #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0) BrowserRun::AskSaveResult BrowserRun::askEmbedOrSave(const QUrl &url, const QString &mimeType, const QString &suggestedFileName, int flags) { BrowserOpenOrSaveQuestion question(nullptr, url, mimeType); question.setSuggestedFileName(suggestedFileName); const BrowserOpenOrSaveQuestion::Result result = question.askEmbedOrSave(flags); return result == BrowserOpenOrSaveQuestion::Save ? Save : result == BrowserOpenOrSaveQuestion::Embed ? Open : Cancel; } #endif // Default implementation, overridden in KHTMLRun void BrowserRun::save(const QUrl &url, const QString &suggestedFileName) { saveUrl(url, suggestedFileName, d->m_window, d->m_args); } #if KPARTS_BUILD_DEPRECATED_SINCE(4, 4) // static void BrowserRun::simpleSave(const QUrl &url, const QString &suggestedFileName, QWidget *window) { saveUrl(url, suggestedFileName, window, KParts::OpenUrlArguments()); } #endif void KParts::BrowserRun::saveUrl(const QUrl &url, const QString &suggestedFileName, QWidget *window, const KParts::OpenUrlArguments &args) { // DownloadManager <-> konqueror integration // find if the integration is enabled // the empty key means no integration // only use the downloadmanager for non-local urls if (!url.isLocalFile()) { KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("konquerorrc"), KConfig::NoGlobals)->group("HTML Settings"); QString downloadManager = cfg.readPathEntry("DownloadManager", QString()); if (!downloadManager.isEmpty()) { // then find the download manager location // qDebug() << "Using: "<setExecutable(downloadManager); job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, window)); job->start(); return; } } } // no download manager available, let's do it ourself QFileDialog *dlg = new QFileDialog(window); dlg->setAcceptMode(QFileDialog::AcceptSave); dlg->setWindowTitle(i18n("Save As")); dlg->setOption(QFileDialog::DontConfirmOverwrite, false); QString name; if (!suggestedFileName.isEmpty()) { name = suggestedFileName; } else { name = url.fileName(); // can be empty, e.g. in case http://www.kde.org/ } dlg->selectFile(name); if (dlg->exec()) { QUrl destURL(dlg->selectedUrls().at(0)); if (destURL.isValid()) { saveUrlUsingKIO(url, destURL, window, args.metaData()); } } delete dlg; } void BrowserRun::saveUrlUsingKIO(const QUrl &srcUrl, const QUrl &destUrl, QWidget *window, const QMap &metaData) { KIO::FileCopyJob *job = KIO::file_copy(srcUrl, destUrl, -1, KIO::Overwrite); const QString modificationTime = metaData[QStringLiteral("content-disposition-modification-date")]; if (!modificationTime.isEmpty()) { job->setModificationTime(QDateTime::fromString(modificationTime, Qt::RFC2822Date)); } job->setMetaData(metaData); job->addMetaData(QStringLiteral("MaxCacheSize"), QStringLiteral("0")); // Don't store in http cache. job->addMetaData(QStringLiteral("cache"), QStringLiteral("cache")); // Use entry from cache if available. KJobWidgets::setWindow(job, window); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } void BrowserRun::handleError(KJob *job) { if (!job) { // Shouldn't happen - qWarning() << "handleError called with job=0! hideErrorDialog=" << d->m_bHideErrorDialog; + qCWarning(KPARTSLOG) << "handleError called with job=0! hideErrorDialog=" << d->m_bHideErrorDialog; return; } KIO::TransferJob *tjob = qobject_cast(job); if (tjob && tjob->isErrorPage() && !job->error()) { // The default handling of error pages is to show them like normal pages // But this is done here in handleError so that KHTMLRun can reimplement it tjob->putOnHold(); setJob(nullptr); if (!d->m_mimeType.isEmpty()) { mimeTypeDetermined(d->m_mimeType); } return; } if (d->m_bHideErrorDialog && job->error() != KIO::ERR_NO_CONTENT) { redirectToError(job->error(), job->errorText()); return; } // Reuse code in KRun, to benefit from d->m_showingError etc. KRun::handleError(job); } // static QUrl BrowserRun::makeErrorUrl(int error, const QString &errorText, const QUrl &initialUrl) { /* * The format of the error:/ URL is error:/?query#url, * where two variables are passed in the query: * error = int kio error code, errText = QString error text from kio * The sub-url is the URL that we were trying to open. */ QUrl newURL(QStringLiteral("error:/?error=%1&errText=%2") .arg(error) .arg(QString::fromUtf8(QUrl::toPercentEncoding(errorText)))); QString cleanedOrigUrl = initialUrl.toString(); QUrl runURL(cleanedOrigUrl); if (runURL.isValid()) { runURL.setPassword(QString()); // don't put the password in the error URL cleanedOrigUrl = runURL.toString(); } newURL.setFragment(cleanedOrigUrl); return newURL; } void BrowserRun::redirectToError(int error, const QString &errorText) { /** * To display this error in KHTMLPart instead of inside a dialog box, * we tell konq that the mimetype is text/html, and we redirect to * an error:/ URL that sends the info to khtml. */ KRun::setUrl(makeErrorUrl(error, errorText, url())); setJob(nullptr); mimeTypeDetermined(QStringLiteral("text/html")); } void BrowserRun::slotCopyToTempFileResult(KJob *job) { if (job->error()) { job->uiDelegate()->showErrorMessage(); } else { // Same as KRun::foundMimeType but with a different URL const QUrl destUrl = static_cast(job)->destUrl(); KIO::OpenUrlJob *job = new KIO::OpenUrlJob(destUrl, d->m_mimeType); job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, d->m_window)); job->setRunExecutables(true); job->start(); } setError(true); // see above setFinished(true); } bool BrowserRun::isTextExecutable(const QString &mimeType) { return (mimeType == QLatin1String("application/x-desktop") || mimeType == QLatin1String("application/x-shellscript")); } bool BrowserRun::hideErrorDialog() const { return d->m_bHideErrorDialog; } QString BrowserRun::contentDisposition() const { return d->m_contentDisposition; } bool BrowserRun::serverSuggestsSave() const { // RfC 2183, section 2.8: // Unrecognized disposition types should be treated as `attachment'. return !contentDisposition().isEmpty() && (contentDisposition() != QLatin1String("inline")); } KParts::OpenUrlArguments &KParts::BrowserRun::arguments() { return d->m_args; } KParts::BrowserArguments &KParts::BrowserRun::browserArguments() { return d->m_browserArgs; } #include "moc_browserrun.cpp" #endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6e01f46..067cc2a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,216 +1,214 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mainwindow.h" #include "guiactivateevent.h" #include "part.h" #include "plugin.h" #include #include #include #include #include #include #include #include #include #include #include -#include - using namespace KParts; namespace KParts { class MainWindowPrivate { public: MainWindowPrivate() : m_activePart(nullptr), m_bShellGUIActivated(false), m_helpMenu(nullptr), m_manageWindowTitle(true) { } ~MainWindowPrivate() { } QPointer m_activePart; bool m_bShellGUIActivated; KHelpMenu *m_helpMenu; bool m_manageWindowTitle; }; } MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags f) : KXmlGuiWindow(parent, f), d(new MainWindowPrivate()) { PartBase::setPartObject(this); } MainWindow::~MainWindow() { delete d; } void MainWindow::createGUI(Part *part) { #if 0 // qDebug() << "part=" << part << (part ? part->metaObject()->className() : "") << (part ? part->objectName() : ""); #endif KXMLGUIFactory *factory = guiFactory(); Q_ASSERT(factory); if (d->m_activePart) { #if 0 // qDebug() << "deactivating GUI for" << d->m_activePart << d->m_activePart->metaObject()->className() << d->m_activePart->objectName(); #endif GUIActivateEvent ev(false); QApplication::sendEvent(d->m_activePart, &ev); factory->removeClient(d->m_activePart); disconnect(d->m_activePart.data(), &Part::setWindowCaption, this, static_cast(&MainWindow::setCaption)); disconnect(d->m_activePart.data(), &Part::setStatusBarText, this, &MainWindow::slotSetStatusBarText); } if (!d->m_bShellGUIActivated) { loadPlugins(this, this, KAboutData::applicationData()); createShellGUI(); d->m_bShellGUIActivated = true; } if (part) { // do this before sending the activate event if (d->m_manageWindowTitle) { connect(part, &Part::setWindowCaption, this, static_cast(&MainWindow::setCaption)); } connect(part, &Part::setStatusBarText, this, &MainWindow::slotSetStatusBarText); factory->addClient(part); GUIActivateEvent ev(true); QApplication::sendEvent(part, &ev); } d->m_activePart = part; } void MainWindow::slotSetStatusBarText(const QString &text) { statusBar()->showMessage(text); } void MainWindow::createShellGUI(bool create) { Q_ASSERT(d->m_bShellGUIActivated != create); d->m_bShellGUIActivated = create; if (create) { if (isHelpMenuEnabled() && !d->m_helpMenu) { d->m_helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true); KActionCollection *actions = actionCollection(); QAction *helpContentsAction = d->m_helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->m_helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->m_helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->m_helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->m_helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->m_helpMenu->action(KHelpMenu::menuAboutKDE); QAction *donateAction = d->m_helpMenu->action(KHelpMenu::menuDonate); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } if (donateAction) { actions->addAction(donateAction->objectName(), donateAction); } } QString f = xmlFile(); setXMLFile(KXMLGUIClient::standardsXmlFileLocation()); if (!f.isEmpty()) { setXMLFile(f, true); } else { QString auto_file(componentName() + QLatin1String("ui.rc")); setXMLFile(auto_file, true); } GUIActivateEvent ev(true); QApplication::sendEvent(this, &ev); guiFactory()->addClient(this); checkAmbiguousShortcuts(); } else { GUIActivateEvent ev(false); QApplication::sendEvent(this, &ev); guiFactory()->removeClient(this); } } void KParts::MainWindow::setWindowTitleHandling(bool enabled) { d->m_manageWindowTitle = enabled; } void KParts::MainWindow::saveNewToolbarConfig() { createGUI(d->m_activePart); KConfigGroup cg(KSharedConfig::openConfig(), QString()); applyMainWindowSettings(cg); } void KParts::MainWindow::configureToolbars() { // No difference with base class anymore. KXmlGuiWindow::configureToolbars(); } diff --git a/src/part.cpp b/src/part.cpp index a355ee0..15cfc99 100644 --- a/src/part.cpp +++ b/src/part.cpp @@ -1,213 +1,214 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999-2005 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "part.h" #include "part_p.h" +#include "kparts_logging.h" #include "partactivateevent.h" #include "partselectevent.h" #include "guiactivateevent.h" #include "partmanager.h" #include #include using namespace KParts; Part::Part(QObject *parent) : QObject(parent), PartBase(*new PartPrivate(this)) { PartBase::setPartObject(this); } Part::Part(PartPrivate &dd, QObject *parent) : QObject(parent), PartBase(dd) { PartBase::setPartObject(this); } Part::~Part() { Q_D(Part); - //qDebug() << this; + //qCDebug(KPARTSLOG) << this; if (d->m_widget) { // We need to disconnect first, to avoid calling it ! disconnect(d->m_widget.data(), &QWidget::destroyed, this, &Part::slotWidgetDestroyed); } if (d->m_manager) { d->m_manager->removePart(this); } if (d->m_widget && d->m_autoDeleteWidget) { - // qDebug() << "deleting widget" << d->m_widget << d->m_widget->objectName(); + // qCDebug(KPARTSLOG) << "deleting widget" << d->m_widget << d->m_widget->objectName(); delete static_cast(d->m_widget); } delete d->m_iconLoader; } void Part::embed(QWidget *parentWidget) { if (widget()) { widget()->setParent(parentWidget, Qt::WindowFlags()); widget()->setGeometry(0, 0, widget()->width(), widget()->height()); widget()->show(); } } QWidget *Part::widget() { Q_D(Part); return d->m_widget; } void Part::setAutoDeleteWidget(bool autoDeleteWidget) { Q_D(Part); d->m_autoDeleteWidget = autoDeleteWidget; } void Part::setAutoDeletePart(bool autoDeletePart) { Q_D(Part); d->m_autoDeletePart = autoDeletePart; } KIconLoader *Part::iconLoader() { Q_D(Part); if (!d->m_iconLoader) { d->m_iconLoader = new KIconLoader(componentName()); } return d->m_iconLoader; } void Part::setManager(PartManager *manager) { Q_D(Part); d->m_manager = manager; } PartManager *Part::manager() const { Q_D(const Part); return d->m_manager; } Part *Part::hitTest(QWidget *widget, const QPoint &) { Q_D(Part); if ((QWidget *)d->m_widget != widget) { return nullptr; } return this; } void Part::setWidget(QWidget *widget) { Q_D(Part); d->m_widget = widget; connect(d->m_widget.data(), &QWidget::destroyed, this, &Part::slotWidgetDestroyed, Qt::UniqueConnection); } void Part::setSelectable(bool selectable) { Q_D(Part); d->m_bSelectable = selectable; } bool Part::isSelectable() const { Q_D(const Part); return d->m_bSelectable; } void Part::customEvent(QEvent *ev) { if (PartActivateEvent::test(ev)) { partActivateEvent(static_cast(ev)); return; } if (PartSelectEvent::test(ev)) { partSelectEvent(static_cast(ev)); return; } if (GUIActivateEvent::test(ev)) { guiActivateEvent(static_cast(ev)); return; } QObject::customEvent(ev); } void Part::partActivateEvent(PartActivateEvent *) { } void Part::partSelectEvent(PartSelectEvent *) { } void Part::guiActivateEvent(GUIActivateEvent *) { } QWidget *Part::hostContainer(const QString &containerName) { if (!factory()) { return nullptr; } return factory()->container(containerName, this); } void Part::slotWidgetDestroyed() { Q_D(Part); d->m_widget = nullptr; if (d->m_autoDeletePart) { - // qDebug() << "deleting part" << objectName(); + // qCDebug(KPARTSLOG) << "deleting part" << objectName(); this->deleteLater(); } } void Part::loadPlugins() { PartBase::loadPlugins(this, this, componentData()); } #include "moc_part.cpp" diff --git a/src/partloader.cpp b/src/partloader.cpp index 863f5d0..1a69379 100644 --- a/src/partloader.cpp +++ b/src/partloader.cpp @@ -1,199 +1,200 @@ /* This file is part of the KDE project Copyright (C) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "partloader.h" +#include "kparts_logging.h" + #include #include #include #include #include #include -#include #include #include // TODO KF6 REMOVE #if KPARTS_VERSION <= QT_VERSION_CHECK(5, 900, 0) #include #include #include #include #endif // We still use desktop files for translated descriptions in keditfiletype, // and desktop file names then end up in mimeapps.list. // Alternatively, that KCM could be ported to read the descriptions from the JSON metadata? // KF6 TODO: at least make the KCM write out library names (into a different config file) // so we don't need to do the lookup here every time. static QString pluginForDesktopFile(const QString &desktopFile) { KService::Ptr service = KService::serviceByStorageId(desktopFile); if (!service) { - qDebug() << "mimeapps.list specifies unknown service" << desktopFile; + qCDebug(KPARTSLOG) << "mimeapps.list specifies unknown service" << desktopFile; return {}; } return service->library(); } static QStringList partsFromUserPreference(const QString &mimeType) { auto config = KSharedConfig::openConfig(QStringLiteral("mimeapps.list")); const QStringList desktopFiles = config->group(QStringLiteral("Added KDE Service Associations")).readXdgListEntry(mimeType); QStringList parts; parts.reserve(desktopFiles.count()); for (const QString &desktopFile : desktopFiles) { const QString part = pluginForDesktopFile(desktopFile); if (!part.isEmpty()) { parts.append(part); } } return parts; } // A plugin can support N mimetypes. Pick the one that is closest to @parent in the inheritance tree // and return how far it is from that parent (0 = same mimetype, 1 = direct child, etc.) static int pluginDistanceToMimeType(const KPluginMetaData &md, const QString &parent) { QMimeDatabase db; auto distanceToMimeType = [&](const QString &mime) { if (mime == parent) return 0; const QStringList ancestors = db.mimeTypeForName(mime).allAncestors(); const int dist = ancestors.indexOf(parent); return dist == -1 ? 50 : dist + 1; }; const QStringList mimes = md.mimeTypes(); int minDistance = 50; for (const QString &mime : mimes) { minDistance = std::min(minDistance, distanceToMimeType(mime)); } return minDistance; } QVector KParts::PartLoader::partsForMimeType(const QString &mimeType) { auto supportsMime = [&](const KPluginMetaData &md) { return md.supportsMimeType(mimeType); }; QVector plugins = KPluginLoader::findPlugins(QStringLiteral("kf5/parts"), supportsMime); #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0) // KF5 compat code // I would compare library filenames, but KPluginMetaData::fileName looks like kf5/kparts/okteta and KService::library() is a full path // The user actually sees translated names, let's ensure those don't look duplicated in the list. auto isPluginForName = [](const QString &name) { return [name](const KPluginMetaData &plugin) { return plugin.name() == name; }; }; const KService::List offers = KMimeTypeTrader::self()->query(mimeType, QStringLiteral("KParts/ReadOnlyPart")); for (const KService::Ptr &service : offers) { QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") KPluginInfo info(service); QT_WARNING_POP if (info.isValid()) { if (std::find_if(plugins.cbegin(), plugins.cend(), isPluginForName(info.name())) == plugins.cend()) { plugins.append(info.toMetaData()); } } } #endif auto orderPredicate = [&](const KPluginMetaData &left, const KPluginMetaData &right) { // We filtered based on "supports mimetype", but this didn't order from most-specific to least-specific. const int leftDistance = pluginDistanceToMimeType(left, mimeType); const int rightDistance = pluginDistanceToMimeType(right, mimeType); if (leftDistance < rightDistance) return true; if (leftDistance > rightDistance) return false; // Plugins who support the same mimetype are then sorted by initial preference return left.initialPreference() > right.initialPreference(); }; std::sort(plugins.begin(), plugins.end(), orderPredicate); const QStringList userParts = partsFromUserPreference(mimeType); if (!userParts.isEmpty()) { //for (const KPluginMetaData &plugin : plugins) { // qDebug() << "unsorted:" << plugin.fileName() << plugin.initialPreference(); //} const auto defaultPlugins = plugins; plugins.clear(); for (const QString &userPart : userParts) { // e.g. kf5/parts/gvpart auto matchesLibrary = [&](const KPluginMetaData &plugin) { return plugin.fileName().contains(userPart); }; auto it = std::find_if(defaultPlugins.begin(), defaultPlugins.end(), matchesLibrary); if (it != defaultPlugins.end()) { plugins.push_back(*it); } else { - qDebug() << "Part not found" << userPart; + qCDebug(KPARTSLOG) << "Part not found" << userPart; } } // In case mimeapps.list lists "nothing good", append the default set to the end as fallback plugins += defaultPlugins; } //for (const KPluginMetaData &plugin : plugins) { // qDebug() << plugin.fileName() << plugin.initialPreference(); //} return plugins; } // KF6 TODO: make create(const char* iface...) public in KPluginFactory, remove this hack class KPluginFactoryHack : public KPluginFactory { public: QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args, const QString &keyword) override { return KPluginFactory::create(iface, parentWidget, parent, args, keyword); } }; QObject *KParts::PartLoader::Private::createPartInstanceForMimeTypeHelper(const char *iface, const QString &mimeType, QWidget *parentWidget, QObject *parent, QString *error) { const QVector plugins = KParts::PartLoader::partsForMimeType(mimeType); for (const KPluginMetaData &plugin : plugins) { KPluginLoader pluginLoader(plugin.fileName()); // ## How can the keyword feature work with JSON metadata? // ## Several desktop files could point to the same .so file, but a .so file only has one metadata builtin. // ## Unlikely to be a problem here (multiple KParts in the same .so file?), but to be solved for KCMs with an array of "KPlugin" in the JSON file... // ## It would then be used by KPluginLoader::findPlugins, and we'd use plugin.keyword() here. const QString pluginKeyword; KPluginFactory *factory = pluginLoader.factory(); if (factory) { QObject *obj = static_cast(factory)->create(iface, parentWidget, parent, QVariantList(), pluginKeyword); if (error) { if (!obj) { *error = i18n("The plugin '%1' does not provide an interface '%2' with keyword '%3'", plugin.fileName(), QString::fromLatin1(iface), pluginKeyword); } else { error->clear(); } } if (obj) { return obj; } } else if (error) { *error = pluginLoader.errorString(); } pluginLoader.unload(); } if (error) { *error = i18n("No part was found for mimeType %1", mimeType); } return nullptr; } diff --git a/src/partmanager.cpp b/src/partmanager.cpp index 2965ad9..c43d36b 100644 --- a/src/partmanager.cpp +++ b/src/partmanager.cpp @@ -1,591 +1,579 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "partmanager.h" +#include "kparts_logging.h" #include "partactivateevent.h" #include "partselectevent.h" #include "guiactivateevent.h" #include "part.h" #include #include #include -#include - -//#define DEBUG_PARTMANAGER - using namespace KParts; namespace KParts { class PartManagerPrivate { public: PartManagerPrivate() { m_activeWidget = nullptr; m_activePart = nullptr; m_selectedPart = nullptr; m_selectedWidget = nullptr; m_bAllowNestedParts = false; m_bIgnoreScrollBars = false; m_activationButtonMask = Qt::LeftButton | Qt::MidButton | Qt::RightButton; m_reason = PartManager::NoReason; m_bIgnoreExplicitFocusRequest = false; } ~PartManagerPrivate() { } void setReason(QEvent *ev) { switch (ev->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonDblClick: { QMouseEvent *mev = static_cast(ev); m_reason = mev->button() == Qt::LeftButton ? PartManager::ReasonLeftClick : (mev->button() == Qt::MidButton ? PartManager::ReasonMidClick : PartManager::ReasonRightClick); break; } case QEvent::FocusIn: m_reason = static_cast(ev)->reason(); break; default: - qWarning() << "PartManagerPrivate::setReason got unexpected ev type " << ev->type(); + qCWarning(KPARTSLOG) << "PartManagerPrivate::setReason got unexpected event type" << ev->type(); break; } } bool allowExplicitFocusEvent(QEvent *ev) const { if (ev->type() == QEvent::FocusIn) { QFocusEvent *fev = static_cast(ev); return (!m_bIgnoreExplicitFocusRequest || fev->reason() != Qt::OtherFocusReason); } return true; } Part *m_activePart; QWidget *m_activeWidget; QList m_parts; PartManager::SelectionPolicy m_policy; Part *m_selectedPart; QWidget *m_selectedWidget; QList m_managedTopLevelWidgets; short int m_activationButtonMask; bool m_bIgnoreScrollBars; bool m_bAllowNestedParts; int m_reason; bool m_bIgnoreExplicitFocusRequest; }; } PartManager::PartManager(QWidget *parent) : QObject(parent), d(new PartManagerPrivate) { qApp->installEventFilter(this); d->m_policy = Direct; addManagedTopLevelWidget(parent); } PartManager::PartManager(QWidget *topLevel, QObject *parent) : QObject(parent), d(new PartManagerPrivate) { qApp->installEventFilter(this); d->m_policy = Direct; addManagedTopLevelWidget(topLevel); } PartManager::~PartManager() { for (const QWidget *w : qAsConst(d->m_managedTopLevelWidgets)) { disconnect(w, &QWidget::destroyed, this, &PartManager::slotManagedTopLevelWidgetDestroyed); } for (Part *it : qAsConst(d->m_parts)) { it->setManager(nullptr); } // core dumps ... setActivePart( 0 ); qApp->removeEventFilter(this); delete d; } void PartManager::setSelectionPolicy(SelectionPolicy policy) { d->m_policy = policy; } PartManager::SelectionPolicy PartManager::selectionPolicy() const { return d->m_policy; } void PartManager::setAllowNestedParts(bool allow) { d->m_bAllowNestedParts = allow; } bool PartManager::allowNestedParts() const { return d->m_bAllowNestedParts; } void PartManager::setIgnoreScrollBars(bool ignore) { d->m_bIgnoreScrollBars = ignore; } bool PartManager::ignoreScrollBars() const { return d->m_bIgnoreScrollBars; } void PartManager::setActivationButtonMask(short int buttonMask) { d->m_activationButtonMask = buttonMask; } short int PartManager::activationButtonMask() const { return d->m_activationButtonMask; } bool PartManager::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::MouseButtonPress && ev->type() != QEvent::MouseButtonDblClick && ev->type() != QEvent::FocusIn) { return false; } if (!obj->isWidgetType()) { return false; } QWidget *w = static_cast(obj); if (((w->windowFlags().testFlag(Qt::Dialog)) && w->isModal()) || (w->windowFlags().testFlag(Qt::Popup)) || (w->windowFlags().testFlag(Qt::Tool))) { return false; } QMouseEvent *mev = nullptr; if (ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick) { mev = static_cast(ev); -#ifdef DEBUG_PARTMANAGER - qDebug() << "PartManager::eventFilter button: " << mev->button() << " " << "d->m_activationButtonMask=" << d->m_activationButtonMask; -#endif + + qCDebug(KPARTSLOG) << "PartManager::eventFilter button:" << mev->button() << "d->m_activationButtonMask=" << d->m_activationButtonMask; + if ((mev->button() & d->m_activationButtonMask) == 0) { return false; // ignore this button } } Part *part; while (w) { QPoint pos; if (!d->m_managedTopLevelWidgets.contains(w->topLevelWidget())) { return false; } if (d->m_bIgnoreScrollBars && ::qobject_cast(w)) { return false; } if (mev) { // mouse press or mouse double-click event pos = mev->globalPos(); part = findPartFromWidget(w, pos); } else { part = findPartFromWidget(w); } -#ifdef DEBUG_PARTMANAGER const char *evType = (ev->type() == QEvent::MouseButtonPress) ? "MouseButtonPress" : (ev->type() == QEvent::MouseButtonDblClick) ? "MouseButtonDblClick" : (ev->type() == QEvent::FocusIn) ? "FocusIn" : "OTHER! ERROR!"; -#endif + if (part) { // We found a part whose widget is w if (d->m_policy == PartManager::TriState) { if (ev->type() == QEvent::MouseButtonDblClick) { if (part == d->m_activePart && w == d->m_activeWidget) { return false; } -#ifdef DEBUG_PARTMANAGER - qDebug() << "PartManager::eventFilter dblclick -> setActivePart" << part; -#endif + qCDebug(KPARTSLOG) << "PartManager::eventFilter dblclick -> setActivePart" << part; + d->setReason(ev); setActivePart(part, w); d->m_reason = NoReason; return true; } if ((d->m_selectedWidget != w || d->m_selectedPart != part) && (d->m_activeWidget != w || d->m_activePart != part)) { if (part->isSelectable()) { setSelectedPart(part, w); } else { -#ifdef DEBUG_PARTMANAGER - qDebug() << "Part " << part << " (non-selectable) made active because " << w->metaObject()->className() << " got event" << " " << evType; -#endif + qCDebug(KPARTSLOG) << "Part" << part << "(non-selectable) made active because" << w->metaObject()->className() << "got event" << evType; + d->setReason(ev); setActivePart(part, w); d->m_reason = NoReason; } return true; } else if (d->m_selectedWidget == w && d->m_selectedPart == part) { -#ifdef DEBUG_PARTMANAGER - qDebug() << "Part " << part << " made active (from selected) because " << w->metaObject()->className() << " got event" << " " << evType; -#endif + qCDebug(KPARTSLOG) << "Part" << part << "made active (from selected) because" << w->metaObject()->className() << "got event" << evType; + d->setReason(ev); setActivePart(part, w); d->m_reason = NoReason; return true; } else if (d->m_activeWidget == w && d->m_activePart == part) { setSelectedPart(nullptr); return false; } return false; } else if (part != d->m_activePart && d->allowExplicitFocusEvent(ev)) { -#ifdef DEBUG_PARTMANAGER - qDebug() << "Part " << part << " made active because " << w->metaObject()->className() << " got event" << " " << evType; -#endif + qCDebug(KPARTSLOG) << "Part" << part << "made active because" << w->metaObject()->className() << "got event" << evType; + d->setReason(ev); setActivePart(part, w); d->m_reason = NoReason; } return false; } w = w->parentWidget(); if (w && (((w->windowFlags() & Qt::Dialog) && w->isModal()) || (w->windowFlags() & Qt::Popup) || (w->windowFlags() & Qt::Tool))) { -#ifdef DEBUG_PARTMANAGER - qDebug() << QString("No part made active although %1/%2 got event - loop aborted").arg(obj->objectName()).arg(obj->metaObject()->className()); -#endif + + qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() + << "got event - loop aborted"; + return false; } } -#ifdef DEBUG_PARTMANAGER - qDebug() << QString("No part made active although %1/%2 got event").arg(obj->objectName()).arg(obj->metaObject()->className()); -#endif + qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() + << "got event - loop aborted"; + return false; } Part *PartManager::findPartFromWidget(QWidget *widget, const QPoint &pos) { for (auto *p : qAsConst(d->m_parts)) { Part *part = p->hitTest(widget, pos); if (part && d->m_parts.contains(part)) { return part; } } return nullptr; } Part *PartManager::findPartFromWidget(QWidget *widget) { for (auto *part : qAsConst(d->m_parts)) { if (widget == part->widget()) { return part; } } return nullptr; } void PartManager::addPart(Part *part, bool setActive) { Q_ASSERT(part); // don't add parts more than once :) if (d->m_parts.contains(part)) { -#ifdef DEBUG_PARTMANAGER - qWarning() << part << " already added"; -#endif + qCWarning(KPARTSLOG) << part << " already added"; return; } d->m_parts.append(part); part->setManager(this); if (setActive) { setActivePart(part); if (QWidget *w = part->widget()) { // Prevent focus problems if (w->focusPolicy() == Qt::NoFocus) { - qWarning() << "Part '" << part->objectName() << "' has a widget " - << w->objectName() << " with a focus policy of NoFocus. It should have at least a" - << "ClickFocus policy, for part activation to work well."; + qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName() + << "with a focus policy of NoFocus. It should have at least a" + << "ClickFocus policy, for part activation to work well."; } if (part->widget() && part->widget()->focusPolicy() == Qt::TabFocus) { - qWarning() << "Part '" << part->objectName() << "' has a widget " - << w->objectName() << " with a focus policy of TabFocus. It should have at least a" - << "ClickFocus policy, for part activation to work well."; + qCWarning(KPARTSLOG) << "Part '" << part->objectName() << "' has a widget " << w->objectName() + << "with a focus policy of TabFocus. It should have at least a" + << "ClickFocus policy, for part activation to work well."; } w->setFocus(); w->show(); } } emit partAdded(part); } void PartManager::removePart(Part *part) { if (!d->m_parts.contains(part)) { return; } const int nb = d->m_parts.removeAll(part); Q_ASSERT(nb == 1); Q_UNUSED(nb); // no warning in release mode part->setManager(nullptr); emit partRemoved(part); if (part == d->m_activePart) { setActivePart(nullptr); } if (part == d->m_selectedPart) { setSelectedPart(nullptr); } } void PartManager::replacePart(Part *oldPart, Part *newPart, bool setActive) { - //qDebug() << "replacePart " << oldPart->name() << "-> " << newPart->name() << " setActive=" << setActive; + //qCDebug(KPARTSLOG) << "replacePart" << oldPart->name() << "->" << newPart->name() << "setActive=" << setActive; // This methods does exactly removePart + addPart but without calling setActivePart(0) in between if (!d->m_parts.contains(oldPart)) { qFatal("Can't remove part %s, not in KPartManager's list.", oldPart->objectName().toLocal8Bit().constData()); return; } d->m_parts.removeAll(oldPart); oldPart->setManager(nullptr); emit partRemoved(oldPart); addPart(newPart, setActive); } void PartManager::setActivePart(Part *part, QWidget *widget) { if (part && !d->m_parts.contains(part)) { - qWarning() << "trying to activate a non-registered part!" << part->objectName(); + qCWarning(KPARTSLOG) << "trying to activate a non-registered part!" << part->objectName(); return; // don't allow someone call setActivePart with a part we don't know about } //check whether nested parts are disallowed and activate the top parent part then, by traversing the //tree recursively (Simon) if (part && !d->m_bAllowNestedParts) { QObject *parentPart = part->parent(); // ### this relies on people using KParts::Factory! KParts::Part *parPart = ::qobject_cast(parentPart); if (parPart) { setActivePart(parPart, parPart->widget()); return; } } -#ifdef DEBUG_PARTMANAGER - qDebug() << "PartManager::setActivePart d->m_activePart=" << d->m_activePart << "<->part=" << part - << " d->m_activeWidget=" << d->m_activeWidget << "<->widget=" << widget; -#endif + qCDebug(KPARTSLOG) << "PartManager::setActivePart d->m_activePart=" << d->m_activePart << "<->part=" << part + << "d->m_activeWidget=" << d->m_activeWidget << "<->widget=" << widget; // don't activate twice if (d->m_activePart && part && d->m_activePart == part && (!widget || d->m_activeWidget == widget)) { return; } KParts::Part *oldActivePart = d->m_activePart; QWidget *oldActiveWidget = d->m_activeWidget; setSelectedPart(nullptr); d->m_activePart = part; d->m_activeWidget = widget; if (oldActivePart) { KParts::Part *savedActivePart = part; QWidget *savedActiveWidget = widget; PartActivateEvent ev(false, oldActivePart, oldActiveWidget); QApplication::sendEvent(oldActivePart, &ev); if (oldActiveWidget) { disconnect(oldActiveWidget, &QWidget::destroyed, this, &PartManager::slotWidgetDestroyed); QApplication::sendEvent(oldActiveWidget, &ev); } d->m_activePart = savedActivePart; d->m_activeWidget = savedActiveWidget; } if (d->m_activePart) { if (!widget) { d->m_activeWidget = part->widget(); } PartActivateEvent ev(true, d->m_activePart, d->m_activeWidget); QApplication::sendEvent(d->m_activePart, &ev); if (d->m_activeWidget) { connect(d->m_activeWidget, &QWidget::destroyed, this, &PartManager::slotWidgetDestroyed); QApplication::sendEvent(d->m_activeWidget, &ev); } } // Set the new active instance //setActiveComponent(d->m_activePart ? d->m_activePart->componentData() : KComponentData::mainComponent()); -#ifdef DEBUG_PARTMANAGER - qDebug() << this << " emitting activePartChanged " << d->m_activePart; -#endif + qCDebug(KPARTSLOG) << this << "emitting activePartChanged" << d->m_activePart; + emit activePartChanged(d->m_activePart); } Part *PartManager::activePart() const { return d->m_activePart; } QWidget *PartManager::activeWidget() const { return d->m_activeWidget; } void PartManager::setSelectedPart(Part *part, QWidget *widget) { if (part == d->m_selectedPart && widget == d->m_selectedWidget) { return; } Part *oldPart = d->m_selectedPart; QWidget *oldWidget = d->m_selectedWidget; d->m_selectedPart = part; d->m_selectedWidget = widget; if (part && !widget) { d->m_selectedWidget = part->widget(); } if (oldPart) { PartSelectEvent ev(false, oldPart, oldWidget); QApplication::sendEvent(oldPart, &ev); QApplication::sendEvent(oldWidget, &ev); } if (d->m_selectedPart) { PartSelectEvent ev(true, d->m_selectedPart, d->m_selectedWidget); QApplication::sendEvent(d->m_selectedPart, &ev); QApplication::sendEvent(d->m_selectedWidget, &ev); } } Part *PartManager::selectedPart() const { return d->m_selectedPart; } QWidget *PartManager::selectedWidget() const { return d->m_selectedWidget; } void PartManager::slotObjectDestroyed() { // qDebug(); removePart(const_cast(static_cast(sender()))); } void PartManager::slotWidgetDestroyed() { // qDebug(); if (static_cast(sender()) == d->m_activeWidget) { setActivePart(nullptr); //do not remove the part because if the part's widget dies, then the } //part will delete itself anyway, invoking removePart() in its destructor } const QList PartManager::parts() const { return d->m_parts; } void PartManager::addManagedTopLevelWidget(const QWidget *topLevel) { if (!topLevel->isTopLevel()) { return; } if (d->m_managedTopLevelWidgets.contains(topLevel)) { return; } d->m_managedTopLevelWidgets.append(topLevel); connect(topLevel, &QWidget::destroyed, this, &PartManager::slotManagedTopLevelWidgetDestroyed); } void PartManager::removeManagedTopLevelWidget(const QWidget *topLevel) { d->m_managedTopLevelWidgets.removeAll(topLevel); } void PartManager::slotManagedTopLevelWidgetDestroyed() { const QWidget *widget = static_cast(sender()); removeManagedTopLevelWidget(widget); } int PartManager::reason() const { return d->m_reason; } void PartManager::setIgnoreExplictFocusRequests(bool ignore) { d->m_bIgnoreExplicitFocusRequest = ignore; } diff --git a/src/plugin.cpp b/src/plugin.cpp index 7c823fe..0272fb6 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,293 +1,291 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugin.h" #include "part.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#include - using namespace KParts; class Q_DECL_HIDDEN Plugin::PluginPrivate { public: QString m_parentInstance; QString m_library; // filename of the library }; Plugin::Plugin(QObject *parent) : QObject(parent), d(new PluginPrivate()) { //qDebug() << className(); } Plugin::~Plugin() { delete d; } QString Plugin::xmlFile() const { QString path = KXMLGUIClient::xmlFile(); if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) { return path; } QString absPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->m_parentInstance + QLatin1Char('/') + path); Q_ASSERT(!absPath.isEmpty()); return absPath; } QString Plugin::localXMLFile() const { QString path = KXMLGUIClient::xmlFile(); if (d->m_parentInstance.isEmpty() || (!path.isEmpty() && QDir::isAbsolutePath(path))) { return path; } QString absPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + d->m_parentInstance + QLatin1Char('/') + path; return absPath; } //static QList Plugin::pluginInfos(const QString &componentName) { QList plugins; QMap sortedPlugins; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, componentName + QLatin1String("/kpartplugins"), QStandardPaths::LocateDirectory); for (const QString &dir : dirs) { const auto rcfiles = QDir(dir).entryList(QStringList(QStringLiteral("*.rc"))); for (const QString &file : rcfiles) { const QFileInfo fInfo(dir + QLatin1Char('/') + file); QMap::Iterator mapIt = sortedPlugins.find(fInfo.fileName()); if (mapIt == sortedPlugins.end()) { mapIt = sortedPlugins.insert(fInfo.fileName(), QStringList()); } mapIt.value().append(fInfo.absoluteFilePath()); } } QMap::ConstIterator mapIt = sortedPlugins.constBegin(); QMap::ConstIterator mapEnd = sortedPlugins.constEnd(); for (; mapIt != mapEnd; ++mapIt) { PluginInfo info; QString doc; info.m_absXMLFileName = KXMLGUIClient::findMostRecentXMLFile(mapIt.value(), doc); if (info.m_absXMLFileName.isEmpty()) { continue; } // qDebug() << "found KParts Plugin : " << info.m_absXMLFileName; info.m_relXMLFileName = QLatin1String("kpartplugins/") + mapIt.key(); info.m_document.setContent(doc); if (info.m_document.documentElement().isNull()) { continue; } plugins.append(info); } return plugins; } void Plugin::loadPlugins(QObject *parent, const QString &componentName) { loadPlugins(parent, pluginInfos(componentName), componentName); } void Plugin::loadPlugins(QObject *parent, const QList &pluginInfos, const QString &componentName) { for (const auto &pluginInfo : pluginInfos) { const QString library = pluginInfo.m_document.documentElement().attribute(QStringLiteral("library")); if (library.isEmpty() || hasPlugin(parent, library)) { continue; } Plugin *plugin = loadPlugin(parent, library, pluginInfo.m_document.documentElement().attribute(QStringLiteral("X-KDE-PluginKeyword"))); if (plugin) { plugin->d->m_parentInstance = componentName; plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false); plugin->setDOMDocument(pluginInfo.m_document); } } } void Plugin::loadPlugins(QObject *parent, const QList &pluginInfos) { loadPlugins(parent, pluginInfos, QString()); } // static Plugin *Plugin::loadPlugin(QObject *parent, const QString &libname, const QString &keyword) { KPluginLoader loader(libname); KPluginFactory *factory = loader.factory(); if (!factory) { return nullptr; } Plugin *plugin = factory->create(keyword, parent); if (!plugin) { return nullptr; } plugin->d->m_library = libname; return plugin; } QList Plugin::pluginObjects(QObject *parent) { QList objects; if (!parent) { return objects; } objects = parent->findChildren(QString(), Qt::FindDirectChildrenOnly); return objects; } bool Plugin::hasPlugin(QObject *parent, const QString &library) { const QObjectList plugins = parent->children(); return std::any_of(plugins.begin(), plugins.end(), [&library](QObject *p) { Plugin *plugin = qobject_cast(p); return (plugin && plugin->d->m_library == library); }); } void Plugin::setComponentData(const KAboutData &pluginData) { KAboutData::registerPluginData(pluginData); KXMLGUIClient::setComponentName(pluginData.componentName(), pluginData.displayName()); } void Plugin::loadPlugins(QObject *parent, KXMLGUIClient *parentGUIClient, const QString &componentName, bool enableNewPluginsByDefault, int interfaceVersionRequired) { KConfigGroup cfgGroup(KSharedConfig::openConfig(componentName + QLatin1String("rc")), "KParts Plugins"); const QList plugins = pluginInfos(componentName); for (const auto &pluginInfo : plugins) { QDomElement docElem = pluginInfo.m_document.documentElement(); QString library = docElem.attribute(QStringLiteral("library")); QString keyword; if (library.isEmpty()) { continue; } // Check configuration const QString name = docElem.attribute(QStringLiteral("name")); bool pluginEnabled = enableNewPluginsByDefault; if (cfgGroup.hasKey(name + QLatin1String("Enabled"))) { pluginEnabled = cfgGroup.readEntry(name + QLatin1String("Enabled"), false); } else { // no user-setting, load plugin default setting QString relPath = componentName + QLatin1Char('/') + pluginInfo.m_relXMLFileName; relPath.truncate(relPath.lastIndexOf(QLatin1Char('.'))); // remove extension relPath += QLatin1String(".desktop"); //qDebug() << "looking for " << relPath; const QString desktopfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relPath); if (!desktopfile.isEmpty()) { //qDebug() << "loadPlugins found desktop file for " << name << ": " << desktopfile; KDesktopFile _desktop(desktopfile); const KConfigGroup desktop = _desktop.desktopGroup(); keyword = desktop.readEntry("X-KDE-PluginKeyword", ""); pluginEnabled = desktop.readEntry("X-KDE-PluginInfo-EnabledByDefault", enableNewPluginsByDefault); if (interfaceVersionRequired != 0) { const int version = desktop.readEntry("X-KDE-InterfaceVersion", 1); if (version != interfaceVersionRequired) { // qDebug() << "Discarding plugin " << name << ", interface version " << version << ", expected " << interfaceVersionRequired; pluginEnabled = false; } } } else { //qDebug() << "loadPlugins no desktop file found in " << relPath; } } // search through already present plugins const QObjectList pluginList = parent->children(); bool pluginFound = false; for (auto *p : pluginList) { Plugin *plugin = qobject_cast(p); if (plugin && plugin->d->m_library == library) { // delete and unload disabled plugins if (!pluginEnabled) { // qDebug() << "remove plugin " << name; KXMLGUIFactory *factory = plugin->factory(); if (factory) { factory->removeClient(plugin); } delete plugin; } pluginFound = true; break; } } // if the plugin is already loaded or if it's disabled in the // configuration do nothing if (pluginFound || !pluginEnabled) { continue; } // qDebug() << "load plugin " << name << " " << library << " " << keyword; Plugin *plugin = loadPlugin(parent, library, keyword); if (plugin) { plugin->d->m_parentInstance = componentName; plugin->setXMLFile(pluginInfo.m_relXMLFileName, false, false); plugin->setDOMDocument(pluginInfo.m_document); parentGUIClient->insertChildClient(plugin); } } } diff --git a/src/readonlypart.cpp b/src/readonlypart.cpp index 63ee003..fa54ea3 100644 --- a/src/readonlypart.cpp +++ b/src/readonlypart.cpp @@ -1,368 +1,367 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999-2005 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "readonlypart.h" #include "readonlypart_p.h" +#include "kparts_logging.h" + #include "browserextension.h" #include "guiactivateevent.h" #include #include #include #include #include #include #include -#include - - using namespace KParts; ReadOnlyPart::ReadOnlyPart(QObject *parent) : Part(*new ReadOnlyPartPrivate(this), parent) { } ReadOnlyPart::ReadOnlyPart(ReadOnlyPartPrivate &dd, QObject *parent) : Part(dd, parent) { } ReadOnlyPart::~ReadOnlyPart() { ReadOnlyPart::closeUrl(); } QUrl ReadOnlyPart::url() const { Q_D(const ReadOnlyPart); return d->m_url; } void ReadOnlyPart::setUrl(const QUrl &url) { Q_D(ReadOnlyPart); if (d->m_url != url) { d->m_url = url; emit urlChanged(url); } } QString ReadOnlyPart::localFilePath() const { Q_D(const ReadOnlyPart); return d->m_file; } void ReadOnlyPart::setLocalFilePath(const QString &localFilePath) { Q_D(ReadOnlyPart); d->m_file = localFilePath; } #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0) bool ReadOnlyPart::isLocalFileTemporary() const { Q_D(const ReadOnlyPart); return d->m_bTemp; } #endif #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0) void ReadOnlyPart::setLocalFileTemporary(bool temp) { Q_D(ReadOnlyPart); d->m_bTemp = temp; } #endif void ReadOnlyPart::setProgressInfoEnabled(bool show) { Q_D(ReadOnlyPart); d->m_showProgressInfo = show; } bool ReadOnlyPart::isProgressInfoEnabled() const { Q_D(const ReadOnlyPart); return d->m_showProgressInfo; } #if KPARTS_BUILD_DEPRECATED_SINCE(3, 0) void ReadOnlyPart::showProgressInfo(bool show) { Q_D(ReadOnlyPart); d->m_showProgressInfo = show; } #endif bool ReadOnlyPart::openUrl(const QUrl &url) { Q_D(ReadOnlyPart); if (!url.isValid()) { return false; } if (d->m_bAutoDetectedMime) { d->m_arguments.setMimeType(QString()); d->m_bAutoDetectedMime = false; } OpenUrlArguments args = d->m_arguments; d->m_closeUrlFromOpenUrl = true; const bool closed = closeUrl(); d->m_closeUrlFromOpenUrl = false; if (!closed) { return false; } d->m_arguments = args; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); return d->openLocalFile(); } else if (KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) { // Maybe we can use a "local path", to avoid a temp copy? KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; d->m_statJob = KIO::mostLocalUrl(d->m_url, flags); KJobWidgets::setWindow(d->m_statJob, widget()); connect(d->m_statJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotStatJobFinished(KJob*))); return true; } else { d->openRemoteFile(); return true; } } bool ReadOnlyPart::openFile() { - qWarning() << "Default implementation of ReadOnlyPart::openFile called!" - << metaObject()->className() << "should reimplement either openUrl or openFile."; + qCWarning(KPARTSLOG) << "Default implementation of ReadOnlyPart::openFile called!" + << metaObject()->className() << "should reimplement either openUrl or openFile."; return false; } bool ReadOnlyPartPrivate::openLocalFile() { Q_Q(ReadOnlyPart); emit q->started(nullptr); m_bTemp = false; // set the mimetype only if it was not already set (for example, by the host application) if (m_arguments.mimeType().isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(m_url); if (!mime.isDefault()) { m_arguments.setMimeType(mime.name()); m_bAutoDetectedMime = true; } } const bool ret = q->openFile(); if (ret) { emit q->setWindowCaption(m_url.toDisplayString()); emit q->completed(); } else { emit q->canceled(QString()); } return ret; } void ReadOnlyPartPrivate::openRemoteFile() { Q_Q(ReadOnlyPart); m_bTemp = true; // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) QString fileName = m_url.fileName(); QFileInfo fileInfo(fileName); QString ext = fileInfo.completeSuffix(); QString extension; if (!ext.isEmpty() && !m_url.hasQuery()) { // not if the URL has a query, e.g. cgi.pl?something extension = QLatin1Char('.') + ext; // keep the '.' } QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + q->componentData().componentName() + QLatin1String("XXXXXX") + extension); tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); QUrl destURL = QUrl::fromLocalFile(m_file); KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; flags |= KIO::Overwrite; m_job = KIO::file_copy(m_url, destURL, 0600, flags); KJobWidgets::setWindow(m_job, q->widget()); emit q->started(m_job); QObject::connect(m_job, SIGNAL(result(KJob*)), q, SLOT(_k_slotJobFinished(KJob*))); QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*,QString)), q, SLOT(_k_slotGotMimeType(KIO::Job*,QString))); } void ReadOnlyPart::abortLoad() { Q_D(ReadOnlyPart); if (d->m_statJob) { //qDebug() << "Aborting job" << d->m_statJob; d->m_statJob->kill(); d->m_statJob = nullptr; } if (d->m_job) { //qDebug() << "Aborting job" << d->m_job; d->m_job->kill(); d->m_job = nullptr; } } bool ReadOnlyPart::closeUrl() { Q_D(ReadOnlyPart); abortLoad(); //just in case d->m_arguments = KParts::OpenUrlArguments(); if (!d->m_closeUrlFromOpenUrl) { setUrl(QUrl()); } if (d->m_bTemp) { QFile::remove(d->m_file); d->m_bTemp = false; } // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } void ReadOnlyPartPrivate::_k_slotStatJobFinished(KJob *job) { Q_ASSERT(job == m_statJob); m_statJob = nullptr; // We could emit canceled on error, but we haven't even emitted started yet, // this could maybe confuse some apps? So for now we'll just fallback to KIO::get // and error again. Well, maybe this even helps with wrong stat results. if (!job->error()) { const QUrl localUrl = static_cast(job)->mostLocalUrl(); if (localUrl.isLocalFile()) { m_file = localUrl.toLocalFile(); (void)openLocalFile(); return; } } openRemoteFile(); } void ReadOnlyPartPrivate::_k_slotJobFinished(KJob *job) { Q_Q(ReadOnlyPart); Q_ASSERT(job == m_job); m_job = nullptr; if (job->error()) { emit q->canceled(job->errorString()); } else { if (q->openFile()) { emit q->setWindowCaption(m_url.toDisplayString()); emit q->completed(); } else { emit q->canceled(QString()); } } } void ReadOnlyPartPrivate::_k_slotGotMimeType(KIO::Job *job, const QString &mime) { // qDebug() << mime; Q_ASSERT(job == m_job); Q_UNUSED(job) // set the mimetype only if it was not already set (for example, by the host application) if (m_arguments.mimeType().isEmpty()) { m_arguments.setMimeType(mime); m_bAutoDetectedMime = true; } } void ReadOnlyPart::guiActivateEvent(GUIActivateEvent *event) { Q_D(ReadOnlyPart); if (event->activated()) { if (!d->m_url.isEmpty()) { // qDebug() << d->m_url; emit setWindowCaption(d->m_url.toDisplayString()); } else { emit setWindowCaption(QString()); } } } bool ReadOnlyPart::openStream(const QString &mimeType, const QUrl &url) { Q_D(ReadOnlyPart); OpenUrlArguments args = d->m_arguments; if (!closeUrl()) { return false; } d->m_arguments = args; setUrl(url); return doOpenStream(mimeType); } bool ReadOnlyPart::writeStream(const QByteArray &data) { return doWriteStream(data); } bool ReadOnlyPart::closeStream() { return doCloseStream(); } BrowserExtension *ReadOnlyPart::browserExtension() const { return findChild(); } void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments &arguments) { Q_D(ReadOnlyPart); d->m_arguments = arguments; d->m_bAutoDetectedMime = arguments.mimeType().isEmpty(); } OpenUrlArguments KParts::ReadOnlyPart::arguments() const { Q_D(const ReadOnlyPart); return d->m_arguments; } #include "moc_readonlypart.cpp" diff --git a/src/readwritepart.cpp b/src/readwritepart.cpp index fb36ae5..98a5b85 100644 --- a/src/readwritepart.cpp +++ b/src/readwritepart.cpp @@ -1,329 +1,329 @@ /* This file is part of the KDE project Copyright (C) 1999 Simon Hausmann (C) 1999-2005 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "readwritepart.h" #include "readwritepart_p.h" +#include "kparts_logging.h" + #include #include #include #include #include #include #include #include -#include - #include #ifdef Q_OS_WIN #include //CreateHardLink() #endif using namespace KParts; ReadWritePart::ReadWritePart(QObject *parent) : ReadOnlyPart(*new ReadWritePartPrivate(this), parent) { } ReadWritePart::~ReadWritePart() { // parent destructor will delete temp file // we can't call our own closeUrl() here, because // "cancel" wouldn't cancel anything. We have to assume // the app called closeUrl() before destroying us. } void ReadWritePart::setReadWrite(bool readwrite) { Q_D(ReadWritePart); // Perhaps we should check isModified here and issue a warning if true d->m_bReadWrite = readwrite; } void ReadWritePart::setModified(bool modified) { Q_D(ReadWritePart); // qDebug() << "setModified(" << (modified ? "true" : "false") << ")"; if (!d->m_bReadWrite && modified) { - qCritical() << "Can't set a read-only document to 'modified' !"; + qCCritical(KPARTSLOG) << "Can't set a read-only document to 'modified' !"; return; } d->m_bModified = modified; } void ReadWritePart::setModified() { setModified(true); } bool ReadWritePart::queryClose() { Q_D(ReadWritePart); if (!isReadWrite() || !isModified()) { return true; } QString docName = url().fileName(); if (docName.isEmpty()) { docName = i18n("Untitled"); } QWidget *parentWidget = widget(); if (!parentWidget) { parentWidget = QApplication::activeWindow(); } int res = KMessageBox::warningYesNoCancel(parentWidget, i18n("The document \"%1\" has been modified.\n" "Do you want to save your changes or discard them?", docName), i18n("Close Document"), KStandardGuiItem::save(), KStandardGuiItem::discard()); bool abortClose = false; bool handled = false; switch (res) { case KMessageBox::Yes : emit sigQueryClose(&handled, &abortClose); if (!handled) { if (d->m_url.isEmpty()) { QUrl url = QFileDialog::getSaveFileUrl(parentWidget); if (url.isEmpty()) { return false; } saveAs(url); } else { save(); } } else if (abortClose) { return false; } return waitSaveComplete(); case KMessageBox::No : return true; default : // case KMessageBox::Cancel : return false; } } bool ReadWritePart::closeUrl() { abortLoad(); //just in case if (isReadWrite() && isModified()) { if (!queryClose()) { return false; } } // Not modified => ok and delete temp file. return ReadOnlyPart::closeUrl(); } bool ReadWritePart::closeUrl(bool promptToSave) { return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl(); } bool ReadWritePart::save() { Q_D(ReadWritePart); d->m_saveOk = false; if (d->m_file.isEmpty()) { // document was created empty d->prepareSaving(); } if (saveFile()) { return saveToUrl(); } else { emit canceled(QString()); } return false; } bool ReadWritePart::saveAs(const QUrl &url) { Q_D(ReadWritePart); if (!url.isValid()) { - qCritical() << "saveAs: Malformed URL " << url; + qCCritical(KPARTSLOG) << "saveAs: Malformed URL" << url; return false; } d->m_duringSaveAs = true; d->m_originalURL = d->m_url; d->m_originalFilePath = d->m_file; d->m_url = url; // Store where to upload in saveToURL d->prepareSaving(); bool result = save(); // Save local file and upload local file if (result) { if (d->m_originalURL != d->m_url) { emit urlChanged(d->m_url); } emit setWindowCaption(d->m_url.toDisplayString()); } else { d->m_url = d->m_originalURL; d->m_file = d->m_originalFilePath; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); } return result; } // Set m_file correctly for m_url void ReadWritePartPrivate::prepareSaving() { // Local file if (m_url.isLocalFile()) { if (m_bTemp) { // get rid of a possible temp file first // (happens if previous url was remote) QFile::remove(m_file); m_bTemp = false; } m_file = m_url.toLocalFile(); } else { // Remote file // We haven't saved yet, or we did but locally - provide a temp file if (m_file.isEmpty() || !m_bTemp) { QTemporaryFile tempFile; tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); m_bTemp = true; } // otherwise, we already had a temp file } } static inline bool makeHardLink(const QString& src, const QString& dest) { #ifndef Q_OS_WIN return ::link(QFile::encodeName(src).constData(), QFile::encodeName(dest).constData()) == 0; #else return CreateHardLinkW((LPCWSTR)dest.utf16(), (LPCWSTR)src.utf16(), nullptr) != 0; #endif } bool ReadWritePart::saveToUrl() { Q_D(ReadWritePart); if (d->m_url.isLocalFile()) { setModified(false); emit completed(); // if m_url is a local file there won't be a temp file -> nothing to remove Q_ASSERT(!d->m_bTemp); d->m_saveOk = true; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); return true; // Nothing to do } else { if (d->m_uploadJob) { QFile::remove(d->m_uploadJob->srcUrl().toLocalFile()); d->m_uploadJob->kill(); d->m_uploadJob = nullptr; } QTemporaryFile *tempFile = new QTemporaryFile(); tempFile->open(); QString uploadFile = tempFile->fileName(); delete tempFile; QUrl uploadUrl = QUrl::fromLocalFile(uploadFile); // Create hardlink if (!makeHardLink(d->m_file, uploadFile)) { // Uh oh, some error happened. return false; } d->m_uploadJob = KIO::file_move(uploadUrl, d->m_url, -1, KIO::Overwrite); KJobWidgets::setWindow(d->m_uploadJob, widget()); connect(d->m_uploadJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotUploadFinished(KJob*))); return true; } } void ReadWritePartPrivate::_k_slotUploadFinished(KJob *) { Q_Q(ReadWritePart); if (m_uploadJob->error()) { QFile::remove(m_uploadJob->srcUrl().toLocalFile()); QString error = m_uploadJob->errorString(); m_uploadJob = nullptr; if (m_duringSaveAs) { q->setUrl(m_originalURL); m_file = m_originalFilePath; } emit q->canceled(error); } else { ::org::kde::KDirNotify::emitFilesAdded(m_url.adjusted(QUrl::RemoveFilename)); m_uploadJob = nullptr; q->setModified(false); emit q->completed(); m_saveOk = true; } m_duringSaveAs = false; m_originalURL = QUrl(); m_originalFilePath.clear(); if (m_waitForSave) { m_eventLoop.quit(); } } bool ReadWritePart::isReadWrite() const { Q_D(const ReadWritePart); return d->m_bReadWrite; } bool ReadWritePart::isModified() const { Q_D(const ReadWritePart); return d->m_bModified; } bool ReadWritePart::waitSaveComplete() { Q_D(ReadWritePart); if (!d->m_uploadJob) { return d->m_saveOk; } d->m_waitForSave = true; d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); d->m_waitForSave = false; return d->m_saveOk; } #include "moc_readwritepart.cpp" diff --git a/src/scriptableextension.cpp b/src/scriptableextension.cpp index 353ac14..69b182e 100644 --- a/src/scriptableextension.cpp +++ b/src/scriptableextension.cpp @@ -1,464 +1,462 @@ /* This file is part of the KDE project Copyright (C) 2010 Maksim Orlovich Copyright (C) 2002, 2004 Koos Vriezen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "scriptableextension.h" #include "scriptableextension_p.h" -#include - namespace KParts { struct ScriptableExtensionPrivate { ScriptableExtension *hostContext; ScriptableExtensionPrivate(): hostContext(nullptr) {} }; ScriptableExtension::ScriptableExtension(QObject *parent): QObject(parent), d(new ScriptableExtensionPrivate) {} ScriptableExtension::~ScriptableExtension() { delete d; } ScriptableExtension *ScriptableExtension::childObject(QObject *obj) { return obj->findChild(QString(), Qt::FindDirectChildrenOnly); } ScriptableExtension *ScriptableExtension::adapterFromLiveConnect(QObject *parentObj, LiveConnectExtension *oldApi) { return new ScriptableLiveConnectExtension(parentObj, oldApi); } void ScriptableExtension::setHost(ScriptableExtension *host) { d->hostContext = host; } ScriptableExtension *ScriptableExtension::host() const { return d->hostContext; } QVariant ScriptableExtension::rootObject() { return QVariant::fromValue(Null()); } QVariant ScriptableExtension::enclosingObject() { if (d->hostContext) { return d->hostContext->encloserForKid(this); } else { return QVariant::fromValue(Null()); } } QVariant ScriptableExtension::encloserForKid(KParts::ScriptableExtension *kid) { Q_UNUSED(kid); return QVariant::fromValue(Null()); } static QVariant unimplemented() { ScriptableExtension::Exception except(QStringLiteral("[unimplemented]")); return QVariant::fromValue(except); } QVariant ScriptableExtension::callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(args); return unimplemented(); } QVariant ScriptableExtension::callFunctionReference(ScriptableExtension *callerPrincipal, quint64 objId, const QString &f, const ArgList &args) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(args); Q_UNUSED(f); return unimplemented(); } QVariant ScriptableExtension::callAsConstructor(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(args); return unimplemented(); } bool ScriptableExtension::hasProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(propName); return false; } QVariant ScriptableExtension::get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(propName); return unimplemented(); } bool ScriptableExtension::put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(propName); Q_UNUSED(value); return false; } bool ScriptableExtension::removeProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(propName); return false; } bool ScriptableExtension::enumerateProperties(ScriptableExtension *callerPrincipal, quint64 objId, QStringList *result) { Q_UNUSED(callerPrincipal); Q_UNUSED(objId); Q_UNUSED(result); return false; } bool ScriptableExtension::setException(ScriptableExtension *callerPrincipal, const QString &message) { Q_UNUSED(callerPrincipal); Q_UNUSED(message); return false; } QVariant ScriptableExtension::evaluateScript(ScriptableExtension *callerPrincipal, quint64 contextObjectId, const QString &code, ScriptLanguage language) { Q_UNUSED(callerPrincipal); Q_UNUSED(contextObjectId); Q_UNUSED(code); Q_UNUSED(language); return unimplemented(); } bool ScriptableExtension::isScriptLanguageSupported(ScriptLanguage lang) const { Q_UNUSED(lang); return false; } void ScriptableExtension::acquire(quint64 objId) { Q_UNUSED(objId); } QVariant ScriptableExtension::acquireValue(const QVariant &v) { if (v.canConvert()) { Object o = v.value(); o.owner->acquire(o.objId); } else if (v.canConvert()) { FunctionRef fr = v.value(); fr.base.owner->acquire(fr.base.objId); } return v; } void ScriptableExtension::release(quint64 objId) { Q_UNUSED(objId); } QVariant ScriptableExtension::releaseValue(const QVariant &v) { if (v.canConvert()) { Object o = v.value(); o.owner->release(o.objId); } else if (v.canConvert()) { FunctionRef fr = v.value(); fr.base.owner->release(fr.base.objId); } return v; } // LiveConnectExtension -> ScriptableExtension adapter. We use // lc object IDs as our own object IDs. // ---------------------------------------------------------------------------- ScriptableLiveConnectExtension::ScriptableLiveConnectExtension(QObject *p, LiveConnectExtension *old): ScriptableExtension(p), wrapee(old) { connect(wrapee, &LiveConnectExtension::partEvent, this, &ScriptableLiveConnectExtension::liveConnectEvent); } QVariant ScriptableLiveConnectExtension::rootObject() { // Plugin root is always LC object #0. return acquireValue(QVariant::fromValue(ScriptableExtension::Object(this, 0))); } bool ScriptableLiveConnectExtension::hasProperty(ScriptableExtension *, quint64 objId, const QString &propName) { QVariant val = get(nullptr, objId, propName); bool ok = !val.canConvert(); releaseValue(val); return ok; } // Note that since we wrap around a plugin, and do not implement the browser, // we do not perform XSS checks ourselves. QVariant ScriptableLiveConnectExtension::callFunctionReference(ScriptableExtension *, quint64 o, const QString &f, const ScriptableExtension::ArgList &a) { QStringList qargs; // Convert args to strings for LC use. qargs.reserve(a.size()); for (const auto &arg : a) { bool ok; qargs.append(toLC(arg, &ok)); if (!ok) { return unimplemented(); } } LiveConnectExtension::Type retType; unsigned long retObjId; QString retVal; if (wrapee->call((unsigned long)o, f, qargs, retType, retObjId, retVal)) { return acquireValue(fromLC(QString(), retType, retObjId, retVal)); } else { return unimplemented(); } } QVariant ScriptableLiveConnectExtension::get(ScriptableExtension *, quint64 objId, const QString &propName) { LiveConnectExtension::Type retType; unsigned long retObjId; QString retVal; if (wrapee->get((unsigned long)objId, propName, retType, retObjId, retVal)) { return acquireValue(fromLC(propName, retType, retObjId, retVal)); } else { // exception signals failure. ### inellegant return unimplemented(); } } bool ScriptableLiveConnectExtension::put(ScriptableExtension *, quint64 objId, const QString &propName, const QVariant &value) { bool ok; QString val = toLC(value, &ok); if (!ok) { return false; } return wrapee->put((unsigned long)objId, propName, val); } QVariant ScriptableLiveConnectExtension::fromLC(const QString &name, LiveConnectExtension::Type type, unsigned long objId, const QString &value) { switch (type) { case KParts::LiveConnectExtension::TypeBool: { bool ok; int i = value.toInt(&ok); if (ok) { return QVariant(bool(i)); } return QVariant(value.toLower() == QLatin1String("true")); } case KParts::LiveConnectExtension::TypeObject: case KParts::LiveConnectExtension::TypeFunction: { if (!refCounts.contains(objId)) { refCounts[objId] = 0; } Object o = ScriptableExtension::Object(this, objId); if (type == KParts::LiveConnectExtension::TypeObject) { return QVariant::fromValue(o); } else { return QVariant::fromValue(FunctionRef(o, name)); } } case KParts::LiveConnectExtension::TypeNumber: return QVariant(value.toDouble()); case KParts::LiveConnectExtension::TypeString: return QVariant(value); case KParts::LiveConnectExtension::TypeVoid: default: return QVariant::fromValue(ScriptableExtension::Undefined()); } } QString ScriptableLiveConnectExtension::toLC(const QVariant &in, bool *ok) { *ok = true; // most of the time. // Objects (or exceptions) can't be converted if (in.canConvert() || in.canConvert() || in.canConvert()) { *ok = false; return QString(); } // Convert null and undefined to appropriate strings // ### this matches old KHTML behavior, but is this sensible? if (in.canConvert()) { return QStringLiteral("null"); } if (in.canConvert()) { return QStringLiteral("undefined"); } if (in.type() == QVariant::Bool) { return in.toBool() ? QStringLiteral("true") : QStringLiteral("false"); } // Just stringify everything else, makes sense for nums as well. if (in.canConvert()) { return in.toString(); } // something really icky... *ok = false; return QString(); } void ScriptableLiveConnectExtension::acquire(quint64 objId) { ++refCounts[objId]; } void ScriptableLiveConnectExtension::release(quint64 objId) { int newRC = --refCounts[objId]; if (!newRC) { if (objId != 0) { wrapee->unregister((unsigned long)objId); } refCounts.remove(objId); } } void ScriptableLiveConnectExtension::liveConnectEvent(const unsigned long, const QString &event, const LiveConnectExtension::ArgList &args) { // We want to evaluate in the enclosure's context. QVariant enclosure = enclosingObject(); if (!enclosure.canConvert()) { releaseValue(enclosure); // qDebug() << "No enclosure, can't evaluate"; return; } Object enclosureObj = enclosure.value(); if (!host()->isScriptLanguageSupported(ECMAScript)) { releaseValue(enclosure); // qDebug() << "Host can't evaluate ECMAScript"; } // Compute a string to evaluate. We need to escape a lot of stuff // since we're composing a bunch of strings into one. QString script = event + QLatin1Char('('); LiveConnectExtension::ArgList::const_iterator i = args.begin(); const LiveConnectExtension::ArgList::const_iterator argsBegin = i; const LiveConnectExtension::ArgList::const_iterator argsEnd = args.end(); for (; i != argsEnd; ++i) { if (i != argsBegin) { script += QLatin1Char(','); } if ((*i).first == KParts::LiveConnectExtension::TypeString) { script += QLatin1Char('"') + QString((*i).second).replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(QLatin1Char('"'), QLatin1String("\\\"")) + QLatin1Char('"'); } else { script += (*i).second; } } script += QLatin1Char(')'); // qDebug() << script; // Ask host to evaluate.. (unfortunately, we can't do anything with the result, // but anything that uses this interface isn't expective one in the first place) QVariant result = host()->evaluateScript(this, enclosureObj.objId, script); releaseValue(result); releaseValue(enclosure); } // hash functions // ---------------------------------------------------------------------------- unsigned int qHash(const KParts::ScriptableExtension::Object &o, uint seed) { return qHash(qMakePair(o.owner, o.objId), seed); } unsigned int qHash(const KParts::ScriptableExtension::FunctionRef &f) { return qHash(qMakePair(f.base, f.field)); } } // namespace KParts #include "moc_scriptableextension.cpp" #include "moc_scriptableextension_p.cpp" diff --git a/src/statusbarextension.cpp b/src/statusbarextension.cpp index 7d382c5..e6425a9 100644 --- a/src/statusbarextension.cpp +++ b/src/statusbarextension.cpp @@ -1,201 +1,200 @@ /* This file is part of the KDE project Copyright (C) 2003 Daniel Molkentin Copyright (C) 2003 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "statusbarextension.h" +#include "kparts_logging.h" + #include "readonlypart.h" #include "guiactivateevent.h" #include #include #include #include -#include - using namespace KParts; /////////////////////////////////////////////////////////////////// // Helper Classes /////////////////////////////////////////////////////////////////// class KParts::StatusBarItem { public: StatusBarItem() // for QValueList : m_widget(nullptr), m_visible(false) {} StatusBarItem(QWidget *widget, int stretch, bool permanent) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_visible(false) {} QWidget *widget() const { return m_widget; } void ensureItemShown(QStatusBar *sb) { if (m_widget && !m_visible) { if (m_permanent) { sb->addPermanentWidget(m_widget, m_stretch); } else { sb->addWidget(m_widget, m_stretch); } m_visible = true; m_widget->show(); } } void ensureItemHidden(QStatusBar *sb) { if (m_widget && m_visible) { sb->removeWidget(m_widget); m_visible = false; m_widget->hide(); } } private: QPointer m_widget; int m_stretch; bool m_permanent; bool m_visible; // true when the item has been added to the statusbar }; class KParts::StatusBarExtensionPrivate { public: StatusBarExtensionPrivate(StatusBarExtension *q): q(q), m_statusBar(nullptr), m_activated(true) {} StatusBarExtension *q; QList m_statusBarItems; // Our statusbar items QStatusBar *m_statusBar; bool m_activated; }; /////////////////////////////////////////////////////////////////// StatusBarExtension::StatusBarExtension(KParts::Part *parent) : QObject(parent), d(new StatusBarExtensionPrivate(this)) { parent->installEventFilter(this); } StatusBarExtension::StatusBarExtension(KParts::ReadOnlyPart *parent) : QObject(parent), d(new StatusBarExtensionPrivate(this)) { parent->installEventFilter(this); } StatusBarExtension::~StatusBarExtension() { QStatusBar *sb = d->m_statusBar; for (int i = d->m_statusBarItems.count() - 1; i >= 0; --i) { if (d->m_statusBarItems[i].widget()) { if (sb) { d->m_statusBarItems[i].ensureItemHidden(sb); } d->m_statusBarItems[i].widget()->deleteLater(); } } delete d; } StatusBarExtension *StatusBarExtension::childObject(QObject *obj) { return obj->findChild(QString(), Qt::FindDirectChildrenOnly); } bool StatusBarExtension::eventFilter(QObject *watched, QEvent *ev) { if (!GUIActivateEvent::test(ev) || !::qobject_cast(watched)) { return QObject::eventFilter(watched, ev); } QStatusBar *sb = statusBar(); if (!sb) { return QObject::eventFilter(watched, ev); } GUIActivateEvent *gae = static_cast(ev); d->m_activated = gae->activated(); if (d->m_activated) { for (auto &item : d->m_statusBarItems) { item.ensureItemShown(sb); } } else { for (auto &item : d->m_statusBarItems) { item.ensureItemHidden(sb); } } return false; } QStatusBar *StatusBarExtension::statusBar() const { if (!d->m_statusBar) { KParts::Part *part = qobject_cast(parent()); QWidget *w = part ? part->widget() : nullptr; KMainWindow *mw = w ? qobject_cast(w->topLevelWidget()) : nullptr; if (mw) { d->m_statusBar = mw->statusBar(); } } return d->m_statusBar; } void StatusBarExtension::setStatusBar(QStatusBar *status) { d->m_statusBar = status; } void StatusBarExtension::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->m_statusBarItems.append(StatusBarItem(widget, stretch, permanent)); StatusBarItem &it = d->m_statusBarItems.last(); QStatusBar *sb = statusBar(); if (sb && d->m_activated) { it.ensureItemShown(sb); } } void StatusBarExtension::removeStatusBarItem(QWidget *widget) { QStatusBar *sb = statusBar(); QList::iterator it = d->m_statusBarItems.begin(); for (; it != d->m_statusBarItems.end(); ++it) if ((*it).widget() == widget) { if (sb) { (*it).ensureItemHidden(sb); } d->m_statusBarItems.erase(it); return; } - qWarning() << "StatusBarExtension::removeStatusBarItem. Widget not found : " << widget; + qCWarning(KPARTSLOG) << "StatusBarExtension::removeStatusBarItem. Widget not found :" << widget; } -