feat: add missing KF6 framework recipes
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
add_library(KF6Parts)
|
||||
add_library(KF6::Parts ALIAS KF6Parts)
|
||||
|
||||
set_target_properties(KF6Parts PROPERTIES
|
||||
VERSION ${KPARTS_VERSION}
|
||||
SOVERSION ${KPARTS_SOVERSION}
|
||||
EXPORT_NAME Parts
|
||||
)
|
||||
|
||||
target_sources(KF6Parts PRIVATE
|
||||
partbase.cpp
|
||||
part.cpp
|
||||
partloader.cpp
|
||||
openurlarguments.cpp
|
||||
readonlypart.cpp
|
||||
readwritepart.cpp
|
||||
partmanager.cpp
|
||||
mainwindow.cpp
|
||||
guiactivateevent.cpp
|
||||
partactivateevent.cpp
|
||||
navigationextension.cpp
|
||||
openurlevent.cpp
|
||||
statusbarextension.cpp
|
||||
fileinfoextension.cpp
|
||||
listingfilterextension.cpp
|
||||
listingnotificationextension.cpp
|
||||
)
|
||||
|
||||
include(ECMGenerateHeaders)
|
||||
ecm_generate_headers(KParts_CamelCase_HEADERS
|
||||
HEADER_NAMES
|
||||
FileInfoExtension
|
||||
GUIActivateEvent
|
||||
ListingFilterExtension
|
||||
ListingNotificationExtension
|
||||
MainWindow
|
||||
NavigationExtension
|
||||
OpenUrlArguments
|
||||
OpenUrlEvent
|
||||
Part
|
||||
PartActivateEvent
|
||||
PartBase
|
||||
PartLoader
|
||||
PartManager
|
||||
ReadOnlyPart
|
||||
ReadWritePart
|
||||
StatusBarExtension
|
||||
REQUIRED_HEADERS KParts_HEADERS
|
||||
PREFIX KParts
|
||||
)
|
||||
install(FILES ${KParts_CamelCase_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KParts/KParts
|
||||
COMPONENT Devel)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6Parts
|
||||
HEADER kparts_logging.h
|
||||
IDENTIFIER KPARTSLOG
|
||||
CATEGORY_NAME kf.parts
|
||||
DESCRIPTION "KParts"
|
||||
EXPORT KPARTSLOG
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6Parts
|
||||
EXPORT_FILE_NAME ${KParts_BINARY_DIR}/kparts/kparts_export.h
|
||||
BASE_NAME KParts
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6Parts
|
||||
PUBLIC "$<BUILD_INTERFACE:${KParts_BINARY_DIR}>"
|
||||
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KParts>"
|
||||
)
|
||||
|
||||
target_link_libraries(KF6Parts
|
||||
PUBLIC
|
||||
KF6::KIOCore # KFileItem used in FileInfoExtension API
|
||||
KF6::XmlGui # essential to the technology
|
||||
PRIVATE
|
||||
KF6::Service
|
||||
KF6::I18n
|
||||
KF6::JobWidgets
|
||||
KF6::KIOWidgets
|
||||
)
|
||||
|
||||
install(TARGETS KF6Parts EXPORT KF6PartsTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${KParts_BINARY_DIR}/kparts/kparts_export.h
|
||||
${KParts_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KParts/kparts COMPONENT Devel
|
||||
)
|
||||
|
||||
install(FILES kde_terminal_interface.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KParts )
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KPARTSLOG
|
||||
FILE kparts.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6Parts_QCH
|
||||
NAME KParts
|
||||
BASE_NAME KF6Parts
|
||||
VERSION ${KF_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
|
||||
KF6KIO_QCH
|
||||
KF6XmlGui_QCH
|
||||
INCLUDE_DIRS
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
BLANK_MACROS
|
||||
KPARTS_EXPORT
|
||||
KPARTS_DEPRECATED
|
||||
KPARTS_DEPRECATED_EXPORT
|
||||
"KPARTS_DEPRECATED_VERSION(x, y, t)"
|
||||
"KPARTS_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
"KPARTS_DEPRECATED_VERSION(x, y, t)"
|
||||
"KPARTS_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources.
|
||||
# The results are stored in a pseudo .cpp file to be picked up by xgettext.
|
||||
lst=`find . -name \*.rc -o -name \*.ui -o -name \*.kcfg`
|
||||
if [ -n "$lst" ] ; then
|
||||
$EXTRACTRC $lst >> rc.cpp
|
||||
fi
|
||||
|
||||
# Extract strings from all source files.
|
||||
# If your framework depends on KI18n, use $XGETTEXT. If it uses Qt translation
|
||||
# system, use $EXTRACT_TR_STRINGS.
|
||||
$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kparts6.pot
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2010 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "fileinfoextension.h"
|
||||
|
||||
#include "readonlypart.h"
|
||||
|
||||
class KParts::FileInfoExtensionPrivate
|
||||
{
|
||||
};
|
||||
|
||||
KParts::FileInfoExtension::FileInfoExtension(KParts::ReadOnlyPart *parent)
|
||||
: QObject(parent)
|
||||
, d(nullptr)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
KParts::FileInfoExtension::~FileInfoExtension()
|
||||
{
|
||||
}
|
||||
|
||||
KParts::FileInfoExtension *KParts::FileInfoExtension::childObject(QObject *obj)
|
||||
{
|
||||
return obj->findChild<KParts::FileInfoExtension *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
KParts::FileInfoExtension::QueryModes KParts::FileInfoExtension::supportedQueryModes() const
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
bool KParts::FileInfoExtension::hasSelection() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "moc_fileinfoextension.cpp"
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2010 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_FILEINFOEXTENSION_H
|
||||
#define KPARTS_FILEINFOEXTENSION_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <KFileItem>
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class KFileItemList;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPart;
|
||||
class FileInfoExtensionPrivate;
|
||||
|
||||
/**
|
||||
* @class FileInfoExtension fileinfoextension.h <KParts/FileInfoExtension>
|
||||
*
|
||||
* @short An extension for obtaining file information from the part.
|
||||
*
|
||||
* This extension provides information about file and directory resources
|
||||
* that are present in the part the implements it.
|
||||
*
|
||||
* The main purpose of for this extension is to provide information about
|
||||
* files and directories located on remote servers so that download managers
|
||||
* such as kget can easily retrieve these resources.
|
||||
*
|
||||
* @since 4.6
|
||||
*/
|
||||
class KPARTS_EXPORT FileInfoExtension : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Supported file information retrieval modes.
|
||||
* @see QueryModes
|
||||
*/
|
||||
enum QueryMode {
|
||||
None = 0x00, /*!< Querying for file information is NOT possible */
|
||||
AllItems = 0x01, /*!< Retrieve or can retrieve file information for all items.*/
|
||||
SelectedItems = 0x02, /*!< Retrieve or can retrieve file information for selected items.*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a combination of #QueryMode values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(QueryModes, QueryMode)
|
||||
|
||||
/*! Constructor */
|
||||
explicit FileInfoExtension(KParts::ReadOnlyPart *parent);
|
||||
|
||||
/*! Destructor */
|
||||
~FileInfoExtension() override;
|
||||
|
||||
/**
|
||||
* Queries @p obj for a child object which inherits from this class.
|
||||
*/
|
||||
static FileInfoExtension *childObject(QObject *obj);
|
||||
|
||||
/**
|
||||
* Returns true if any of the items in the current view of the part that
|
||||
* implements this extension are selected.
|
||||
*
|
||||
* By default this function returns false.
|
||||
*/
|
||||
virtual bool hasSelection() const;
|
||||
|
||||
/**
|
||||
* Returns the file information retrieve modes supported by the part
|
||||
* that implements this extension.
|
||||
*
|
||||
* By default this function returns None.
|
||||
*/
|
||||
virtual QueryModes supportedQueryModes() const;
|
||||
|
||||
/**
|
||||
* Returns a information for files that match the specified query @p mode.
|
||||
*
|
||||
* If the mode specified by @p mode is not supported or cannot be
|
||||
* handled, then an empty list is returned.
|
||||
*/
|
||||
virtual KFileItemList queryFor(QueryMode mode) const = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileInfoExtensionPrivate> const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(FileInfoExtension::QueryModes)
|
||||
|
||||
}
|
||||
|
||||
#endif /* KPARTS_FILEINFOEXTENSION_H */
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "guiactivateevent.h"
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
class KParts::GUIActivateEventPrivate
|
||||
{
|
||||
public:
|
||||
GUIActivateEventPrivate(bool activated)
|
||||
: m_bActivated(activated)
|
||||
{
|
||||
}
|
||||
const bool m_bActivated;
|
||||
};
|
||||
|
||||
const QEvent::Type GUIActivateEventType = (QEvent::Type)1970;
|
||||
|
||||
GUIActivateEvent::GUIActivateEvent(bool activated)
|
||||
: QEvent(GUIActivateEventType)
|
||||
, d(new GUIActivateEventPrivate(activated))
|
||||
{
|
||||
}
|
||||
|
||||
GUIActivateEvent::~GUIActivateEvent() = default;
|
||||
|
||||
bool GUIActivateEvent::activated() const
|
||||
{
|
||||
return d->m_bActivated;
|
||||
}
|
||||
|
||||
bool GUIActivateEvent::test(const QEvent *event)
|
||||
{
|
||||
return event->type() == GUIActivateEventType;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kparts_guiactivateevent_h__
|
||||
#define __kparts_guiactivateevent_h__
|
||||
|
||||
#include <QEvent>
|
||||
#include <memory>
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class GUIActivateEventPrivate;
|
||||
/**
|
||||
* @class GUIActivateEvent guiactivateevent.h <KParts/GUIActivateEvent>
|
||||
*
|
||||
* @short This event is sent to a Part when its GUI has been activated or deactivated.
|
||||
* This is related to PartActivateEvent, but the difference is that
|
||||
* GUIActivateEvent happens later (when the GUI is actually built),
|
||||
* only for parts that have GUI elements, and only if using KParts::MainWindow.
|
||||
* @see KParts::Part::guiActivateEvent()
|
||||
*/
|
||||
class KPARTS_EXPORT GUIActivateEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
GUIActivateEvent(bool activated);
|
||||
~GUIActivateEvent() override;
|
||||
|
||||
bool activated() const;
|
||||
|
||||
static bool test(const QEvent *event);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<GUIActivateEventPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
interface.h
|
||||
SPDX-FileCopyrightText: 2002 Dominique Devriese <devriese@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KDELIBS_KDE_TERMINAL_INTERFACE_H
|
||||
#define KDELIBS_KDE_TERMINAL_INTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
* TerminalInterface is an interface implemented by KonsolePart to
|
||||
* allow developers access to the KonsolePart in ways that are not
|
||||
* possible through the normal KPart interface.
|
||||
*
|
||||
* Note that besides the functions below here, KonsolePart also has
|
||||
* some signals you can connect to. They aren't in this class cause
|
||||
* we can't have signals without having a QObject, which
|
||||
* TerminalInterface is not.
|
||||
*
|
||||
* These are some signals you can connect to:
|
||||
* void currentDirectoryChanged(const QString& dir);
|
||||
*
|
||||
* See the example code below for how to connect to these.
|
||||
*
|
||||
* Use it like this:
|
||||
* \code
|
||||
* // Query the .desktop file to get service information about konsolepart
|
||||
* KService::Ptr service = KService::serviceByDesktopName("konsolepart");
|
||||
*
|
||||
* if (!service) {
|
||||
* QMessageBox::critical(this, tr("Konsole not installed"), tr("Please install the kde konsole and try again!"), QMessageBox::Ok);
|
||||
* return ;
|
||||
* }
|
||||
*
|
||||
* // Create one instance of konsolepart
|
||||
* KParts::ReadOnlyPart *part = service->createInstance<KParts::ReadOnlyPart>(parent, parentWidget, QVariantList());
|
||||
*
|
||||
* if (!part) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* // Cast the konsolepart to the TerminalInterface..
|
||||
* TerminalInterface *terminalIface = qobject_cast<TerminalInterface *>(part);
|
||||
*
|
||||
* if (!terminalIface) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* // Now use the interface in all sorts of ways, e.g.
|
||||
* // terminalIface->showShellInDir(QDir::home().path());
|
||||
* // Or:
|
||||
* // QStringList list;
|
||||
* // list.append("python");
|
||||
* // terminalIface->startProgram( QString::fromUtf8( "/usr/bin/python" ), list);
|
||||
* // Or connect to one of the signals. Connect to the Part object,
|
||||
* // not to the TerminalInterface, since the latter is not a QObject,
|
||||
* // and as such does not have signals..:
|
||||
* connect(part, &KParts::ReadOnlyPart::currentDirectoryChanged, this, [this](const QString &dirPath) {
|
||||
* currentDirectoryChanged(dirPath);
|
||||
* });
|
||||
* // etc.
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* @author Dominique Devriese <devriese@kde.org>
|
||||
*/
|
||||
class TerminalInterface
|
||||
{
|
||||
public:
|
||||
virtual ~TerminalInterface()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* This starts @p program, with arguments @p args
|
||||
*/
|
||||
virtual void startProgram(const QString &program, const QStringList &args) = 0;
|
||||
/**
|
||||
* If no shell is running, this starts a shell with the
|
||||
* @dir as the starting directory.
|
||||
* If a shell is already running, nothing is done.
|
||||
*/
|
||||
virtual void showShellInDir(const QString &dir) = 0;
|
||||
|
||||
/**
|
||||
* This sends @param text as input to the currently running
|
||||
* program..
|
||||
*/
|
||||
virtual void sendInput(const QString &text) = 0;
|
||||
|
||||
/**
|
||||
* Return terminal PID. If no process is currently running, returns 0.
|
||||
*/
|
||||
virtual int terminalProcessId() = 0;
|
||||
|
||||
/**
|
||||
* Return foregound PID, If there is no foreground process running, returns -1
|
||||
*/
|
||||
virtual int foregroundProcessId() = 0;
|
||||
|
||||
/**
|
||||
* Returns sub process name. If there is no sub process running, returns empty QString
|
||||
*/
|
||||
virtual QString foregroundProcessName() = 0;
|
||||
|
||||
/**
|
||||
* Returns the current working directory
|
||||
*/
|
||||
virtual QString currentWorkingDirectory() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the names of available profiles.
|
||||
*/
|
||||
virtual QStringList availableProfiles() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the name of the currently active profile.
|
||||
*/
|
||||
virtual QString currentProfileName() const = 0;
|
||||
|
||||
/**
|
||||
* Changes the currently active profile to @p profileName.
|
||||
* @returns Returns true if setting the profile was successful
|
||||
*/
|
||||
virtual bool setCurrentProfile(const QString &profileName) = 0;
|
||||
|
||||
/**
|
||||
* Returns the property @p profileProperty of the currently active profile.
|
||||
*/
|
||||
virtual QVariant profileProperty(const QString &profileProperty) const = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE(TerminalInterface, "org.kde.TerminalInterface")
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "listingfilterextension.h"
|
||||
|
||||
#include "readonlypart.h"
|
||||
|
||||
class KParts::ListingFilterExtensionPrivate
|
||||
{
|
||||
};
|
||||
|
||||
KParts::ListingFilterExtension::ListingFilterExtension(KParts::ReadOnlyPart *parent)
|
||||
: QObject(parent)
|
||||
, d(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
KParts::ListingFilterExtension::~ListingFilterExtension()
|
||||
{
|
||||
}
|
||||
|
||||
KParts::ListingFilterExtension *KParts::ListingFilterExtension::childObject(QObject *obj)
|
||||
{
|
||||
return obj->findChild<KParts::ListingFilterExtension *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
KParts::ListingFilterExtension::FilterModes KParts::ListingFilterExtension::supportedFilterModes() const
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
bool KParts::ListingFilterExtension::supportsMultipleFilters(KParts::ListingFilterExtension::FilterMode) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "moc_listingfilterextension.cpp"
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_LISTINGFILTEREXTENSION_H
|
||||
#define KPARTS_LISTINGFILTEREXTENSION_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class KFileItemList;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPart;
|
||||
class ListingFilterExtensionPrivate;
|
||||
|
||||
/**
|
||||
* @class ListingFilterExtension listingfilterextension.h <KParts/ListingFilterExtension>
|
||||
*
|
||||
* @short An extension for filtering listings.
|
||||
*
|
||||
* This extension is intended to be implemented by parts that provide listing
|
||||
* services, e.g. file management parts and is intended to provide a generic
|
||||
* API for filtering any listing through keywords, wildcard characters and/or
|
||||
* content-type.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* To show items that only match the term "kde"
|
||||
* \code
|
||||
* KParts::ListingFilterExtension* ext = KParts::ListingFilterExtension::childObject(part);
|
||||
* if (ext && (ext->supportedFilterModes() & KParts::ListingFilterExtension::SubString)) {
|
||||
* ext->setFilter(KParts::ListingFilterExtension::SubString, QLatin1String("kde"));
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To show items that only match "text/html"
|
||||
* \code
|
||||
* KParts::ListingFilterExtension* ext = KParts::ListingFilterExtension::childObject(part);
|
||||
* if (ext && (ext->supportedFilterModes() & KParts::ListingFilterExtension::MimeType)) {
|
||||
* ext->setFilter(KParts::ListingFilterExtension::MimeType, QLatin1String("text/html"));
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To show items that only match the wildcard string "*.txt"
|
||||
* \code
|
||||
* KParts::ListingFilterExtension* ext = KParts::ListingFilterExtension::childObject(part);
|
||||
* if (ext && (ext->supportedFilterModes() & KParts::ListingFilterExtension::WildCard)) {
|
||||
* ext->setFilter(KParts::ListingFilterExtension::WildCard, QLatin1String("*.txt"));
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To show items that match multiple mime types, e.g. text/html & application/xml:
|
||||
*
|
||||
* \code
|
||||
* KParts::ListingFilterExtension* ext = KParts::ListingFilterExtension::childObject(part);
|
||||
* if (ext &&
|
||||
* (ext->supportedFilterModes() & KParts::ListingFilterExtension::MimeType) &&
|
||||
* ext->supportsMultipleFilters(KParts::ListingFilterExtension::MimeType)) {
|
||||
* QStringList mimeTypes = ext->filter(KParts::ListingFilterExtension::MimeType).toStringList();
|
||||
* mimeTypes << QLatin1String("text/html") << QLatin1String("application/xml");
|
||||
* ext->setFilter(KParts::ListingFilterExtension::MimeType, mimeTypes);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* @since 4.9.2
|
||||
*/
|
||||
class KPARTS_EXPORT ListingFilterExtension : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Supported file filtering modes modes.
|
||||
* @FilterModes
|
||||
*/
|
||||
enum FilterMode {
|
||||
None = 0x00,
|
||||
MimeType = 0x01, /*!< Filter by mime type, e.g. "text/plain". */
|
||||
SubString = 0x02, /*!< Filter by matching any part of a file or directory name, e.g. "Documents" */
|
||||
WildCard = 0x04, /*!< Filter by using wildcard matches, e.g. "*.txt" */
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a combination of #FilterMode values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(FilterModes, FilterMode)
|
||||
|
||||
/*! Constructor */
|
||||
explicit ListingFilterExtension(KParts::ReadOnlyPart *parent);
|
||||
|
||||
/*! Destructor */
|
||||
~ListingFilterExtension() override;
|
||||
|
||||
/**
|
||||
* Queries @p obj for a child object which inherits from this class.
|
||||
*/
|
||||
static ListingFilterExtension *childObject(QObject *obj);
|
||||
|
||||
/**
|
||||
* Returns the OR'ed value of the file filter modes supported by the part
|
||||
* that implements this extension.
|
||||
*
|
||||
* By default this function returns None.
|
||||
*/
|
||||
virtual FilterModes supportedFilterModes() const;
|
||||
|
||||
/**
|
||||
* Returns true if the part that implements this extension allows
|
||||
* the use of multiple filters for the given filtering @p mode.
|
||||
*
|
||||
* By default this function returns false.
|
||||
*/
|
||||
virtual bool supportsMultipleFilters(FilterMode mode) const;
|
||||
|
||||
/**
|
||||
* Returns the currently set filters for the given @p mode.
|
||||
*
|
||||
* @param mode the desired filter mode as specified in @ref FilterMode.
|
||||
*/
|
||||
virtual QVariant filter(FilterMode mode) const = 0;
|
||||
|
||||
/**
|
||||
* Sets the file @p filter that should be applied by the part that
|
||||
* implements this extension for the given filtering @p mode.
|
||||
*
|
||||
* To remove a filter for a given filter mode, simply call this function with
|
||||
* the desired mode and the @p filter parameter set to a NULL variant.
|
||||
*
|
||||
* The second parameter can be
|
||||
*
|
||||
* @param mode the desired filter mode as specified in @ref FilterMode.
|
||||
* @param filter a list of filter texts based on the selected mode.
|
||||
*/
|
||||
virtual void setFilter(FilterMode mode, const QVariant &filter) = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ListingFilterExtension> const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ListingFilterExtension::FilterModes)
|
||||
|
||||
}
|
||||
|
||||
#endif /* KPARTS_LISTINGFILTEREXTENSION_H */
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "listingnotificationextension.h"
|
||||
|
||||
#include "readonlypart.h"
|
||||
|
||||
class KParts::ListingNotificationExtensionPrivate
|
||||
{
|
||||
};
|
||||
|
||||
KParts::ListingNotificationExtension::ListingNotificationExtension(KParts::ReadOnlyPart *parent)
|
||||
: QObject(parent)
|
||||
, d(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
KParts::ListingNotificationExtension::~ListingNotificationExtension()
|
||||
{
|
||||
}
|
||||
|
||||
KParts::ListingNotificationExtension *KParts::ListingNotificationExtension::childObject(QObject *obj)
|
||||
{
|
||||
return obj->findChild<KParts::ListingNotificationExtension *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
KParts::ListingNotificationExtension::NotificationEventTypes KParts::ListingNotificationExtension::supportedNotificationEventTypes() const
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
#include "moc_listingnotificationextension.cpp"
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_LISTINGNOTIFICATIONEXTENSION_H
|
||||
#define KPARTS_LISTINGNOTIFICATIONEXTENSION_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class KFileItemList;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPart;
|
||||
class ListingNotificationExtensionPrivate;
|
||||
|
||||
/**
|
||||
* @class ListingNotificationExtension listingnotificationextension.h <KParts/ListingNotificationExtension>
|
||||
*
|
||||
* @short An extension for receiving listing change notification.
|
||||
*
|
||||
* This extension is intended for implementation by parts that provide listing
|
||||
* services, e.g. file management and is intended to notify about changes to
|
||||
* a given listing. For example, if file management part implemented this extension
|
||||
* it would emit @ref itemsDeleted and @ref itemsAdded signal whenever new files
|
||||
* or folders are deleted and added to a directory respectively.
|
||||
*
|
||||
* @since 4.9.2
|
||||
*/
|
||||
class KPARTS_EXPORT ListingNotificationExtension : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Supported notification event types.
|
||||
* @see NotificationEventTypes
|
||||
*/
|
||||
enum NotificationEventType {
|
||||
None = 0x00,
|
||||
ItemsAdded = 0x01, /*!< New items added to the listing. */
|
||||
ItemsDeleted = 0x02, /*!< Items deleted from the listing. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a combination of #NotificationEventType values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(NotificationEventTypes, NotificationEventType)
|
||||
|
||||
/*! Constructor */
|
||||
ListingNotificationExtension(KParts::ReadOnlyPart *parent);
|
||||
|
||||
/*! Destructor */
|
||||
~ListingNotificationExtension() override;
|
||||
|
||||
/**
|
||||
* Returns the OR'ed value of the notification types supported by the part
|
||||
* that implements this extension.
|
||||
*
|
||||
* By default this function returns None.
|
||||
*/
|
||||
virtual NotificationEventTypes supportedNotificationEventTypes() const;
|
||||
|
||||
/**
|
||||
* Queries @p obj for a child object which inherits from this class.
|
||||
*/
|
||||
static ListingNotificationExtension *childObject(QObject *obj);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when one of the notification events listed
|
||||
* in @ref NotificationEventType occur.
|
||||
*/
|
||||
void listingEvent(KParts::ListingNotificationExtension::NotificationEventType, const KFileItemList &);
|
||||
|
||||
private:
|
||||
std::unique_ptr<ListingNotificationExtension> const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ListingNotificationExtension::NotificationEventTypes)
|
||||
|
||||
}
|
||||
|
||||
#endif /* KPARTS_LISTINGNOTIFICATIONEXTENSION_H */
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "guiactivateevent.h"
|
||||
#include "part.h"
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KActionCollection>
|
||||
#include <KConfigGroup>
|
||||
#include <KEditToolBar>
|
||||
#include <KHelpMenu>
|
||||
#include <KSharedConfig>
|
||||
#include <KXMLGUIFactory>
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QPointer>
|
||||
#include <QStatusBar>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class MainWindowPrivate
|
||||
{
|
||||
public:
|
||||
MainWindowPrivate()
|
||||
: m_activePart(nullptr)
|
||||
{
|
||||
}
|
||||
~MainWindowPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
QPointer<Part> m_activePart;
|
||||
bool m_bShellGUIActivated = false;
|
||||
KHelpMenu *m_helpMenu = nullptr;
|
||||
bool m_manageWindowTitle = true;
|
||||
};
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags f)
|
||||
: KXmlGuiWindow(parent, f)
|
||||
, d(new MainWindowPrivate())
|
||||
{
|
||||
PartBase::setPartObject(this);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() = default;
|
||||
|
||||
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<void (MainWindow::*)(const QString &)>(&MainWindow::setCaption));
|
||||
disconnect(d->m_activePart.data(), &Part::setStatusBarText, this, &MainWindow::slotSetStatusBarText);
|
||||
}
|
||||
|
||||
if (!d->m_bShellGUIActivated) {
|
||||
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<void (MainWindow::*)(const QString &)>(&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());
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#include "moc_mainwindow.cpp"
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __MAINWINDOW_H
|
||||
#define __MAINWINDOW_H
|
||||
|
||||
#include <kparts/part.h>
|
||||
|
||||
#include <KXmlGuiWindow>
|
||||
#include <memory>
|
||||
|
||||
class QString;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class MainWindowPrivate;
|
||||
|
||||
/**
|
||||
* @class MainWindow mainwindow.h <KParts/MainWindow>
|
||||
*
|
||||
* @short A KPart-aware main window, whose user interface is described in XML.
|
||||
*
|
||||
* It implements all internal interfaces in the case of a
|
||||
* KMainWindow as host: the builder and servant interface (for menu
|
||||
* merging).
|
||||
*
|
||||
* Inherit your main window from this class
|
||||
* and make sure to call @c setXMLFile() and @c setupGUI() before you
|
||||
* call @c createGUI() on the KPart.
|
||||
*
|
||||
* For example:
|
||||
* \code
|
||||
* setCentralWidget(m_part->widget());
|
||||
* setXMLFile(QStringLiteral("appui.rc"));
|
||||
* setupGUI(ToolBar | Keys | StatusBar | Save); // Never Create flag here
|
||||
* createGUI(m_part);
|
||||
* \endcode
|
||||
*
|
||||
* @warning You should not pass the @c Default flag set to @c setupGUI(),
|
||||
* since it contains the @c Create flag, which is not supposed to be used
|
||||
* from this class.
|
||||
* @see KXmlGuiWindow::Create, @see setupGUI, @see createGUI
|
||||
*
|
||||
*/
|
||||
class KPARTS_EXPORT MainWindow : public KXmlGuiWindow, virtual public PartBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructor, same signature as KMainWindow.
|
||||
*/
|
||||
explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~MainWindow() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void configureToolbars() override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Create the GUI (by merging the host's and the active part's)
|
||||
* You _must_ call this in order to see any GUI being created.
|
||||
*
|
||||
* In a main window with multiple parts being shown (e.g. as in Konqueror)
|
||||
* you need to connect this slot to the
|
||||
* KPartManager::activePartChanged() signal
|
||||
*
|
||||
* @param part The active part (set to 0L if no part).
|
||||
*/
|
||||
void createGUI(KParts::Part *part);
|
||||
|
||||
/**
|
||||
* Enable or disable the automatic setting of window titles by the part's document title.
|
||||
* By default, a part always changes the window title when the document changes.
|
||||
* @note This value must be set before calling createGUI().
|
||||
*
|
||||
* @param enabled boolean to enable or disable the window title handling
|
||||
* @since 5.24
|
||||
*/
|
||||
void setWindowTitleHandling(bool enabled);
|
||||
|
||||
/**
|
||||
* Called when the active part wants to change the statusbar message
|
||||
* Reimplement if your mainwindow has a complex statusbar
|
||||
* (with several items)
|
||||
*/
|
||||
virtual void slotSetStatusBarText(const QString &);
|
||||
|
||||
/**
|
||||
* Rebuilds the GUI after KEditToolBar changed the toolbar layout.
|
||||
* @see configureToolbars()
|
||||
*/
|
||||
void saveNewToolbarConfig() override;
|
||||
|
||||
protected:
|
||||
virtual void createShellGUI(bool create = true);
|
||||
|
||||
private:
|
||||
std::unique_ptr<MainWindowPrivate> const d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "navigationextension.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KMessageBox>
|
||||
#include <KUriFilter>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QMap>
|
||||
#include <QRegularExpression>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
// Internal class, use to store the status of the actions
|
||||
class KBitArray
|
||||
{
|
||||
public:
|
||||
int 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 NavigationExtensionPrivate
|
||||
{
|
||||
public:
|
||||
NavigationExtensionPrivate(KParts::ReadOnlyPart *parent)
|
||||
: m_urlDropHandlingEnabled(false)
|
||||
, m_part(parent)
|
||||
{
|
||||
}
|
||||
|
||||
struct DelayedRequest {
|
||||
QUrl m_delayedURL;
|
||||
KParts::OpenUrlArguments m_delayedArgs;
|
||||
};
|
||||
|
||||
QList<DelayedRequest> m_requests;
|
||||
bool m_urlDropHandlingEnabled;
|
||||
KBitArray m_actionStatus;
|
||||
QMap<int, QString> m_actionText;
|
||||
|
||||
static void createActionSlotMap();
|
||||
|
||||
KParts::ReadOnlyPart *m_part;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(NavigationExtension::ActionSlotMap, s_actionSlotMap)
|
||||
Q_GLOBAL_STATIC(NavigationExtension::ActionNumberMap, s_actionNumberMap)
|
||||
|
||||
void NavigationExtensionPrivate::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
|
||||
NavigationExtension::ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
|
||||
NavigationExtension::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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NavigationExtension::NavigationExtension(KParts::ReadOnlyPart *parent)
|
||||
: QObject(parent)
|
||||
, d(new NavigationExtensionPrivate(parent))
|
||||
{
|
||||
if (s_actionSlotMap()->isEmpty())
|
||||
// Create the action-slot map
|
||||
{
|
||||
NavigationExtensionPrivate::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(this, &NavigationExtension::openUrlRequest, this, &NavigationExtension::slotOpenUrlRequest);
|
||||
connect(this, &NavigationExtension::enableAction, this, &NavigationExtension::slotEnableAction);
|
||||
connect(this, &NavigationExtension::setActionText, this, &NavigationExtension::slotSetActionText);
|
||||
}
|
||||
|
||||
NavigationExtension::~NavigationExtension()
|
||||
{
|
||||
// qDebug() << "BrowserExtension::~BrowserExtension() " << this;
|
||||
}
|
||||
|
||||
int NavigationExtension::xOffset()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NavigationExtension::yOffset()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NavigationExtension::saveState(QDataStream &stream)
|
||||
{
|
||||
// TODO add d->m_part->mimeType()
|
||||
stream << d->m_part->url() << static_cast<qint32>(xOffset()) << static_cast<qint32>(yOffset());
|
||||
}
|
||||
|
||||
void NavigationExtension::restoreState(QDataStream &stream)
|
||||
{
|
||||
QUrl u;
|
||||
qint32 xOfs;
|
||||
qint32 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 NavigationExtension::isURLDropHandlingEnabled() const
|
||||
{
|
||||
return d->m_urlDropHandlingEnabled;
|
||||
}
|
||||
|
||||
void NavigationExtension::setURLDropHandlingEnabled(bool enable)
|
||||
{
|
||||
d->m_urlDropHandlingEnabled = enable;
|
||||
}
|
||||
|
||||
void NavigationExtension::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::error(d->m_part->widget(), filterData.errorMsg());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (KUriFilter::self()->filterUri(filterData, QStringList(QStringLiteral("kuriikwsfilter"))) && url.length() < 250) {
|
||||
if (KMessageBox::questionTwoActions(d->m_part->widget(),
|
||||
i18n("<qt>Do you want to search the Internet for <b>%1</b>?</qt>", url.toHtmlEscaped()),
|
||||
i18n("Internet Search"),
|
||||
KGuiItem(i18n("&Search"), QStringLiteral("edit-find")),
|
||||
KStandardGuiItem::cancel(),
|
||||
QStringLiteral("MiddleClickSearch"))
|
||||
== KMessageBox::PrimaryAction) {
|
||||
slotOpenUrlRequest(filterData.uri());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationExtension::slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &args)
|
||||
{
|
||||
// qDebug() << this << " BrowserExtension::slotOpenURLRequest(): url=" << url.url();
|
||||
NavigationExtensionPrivate::DelayedRequest req;
|
||||
req.m_delayedURL = url;
|
||||
req.m_delayedArgs = args;
|
||||
d->m_requests.append(req);
|
||||
QTimer::singleShot(0, this, &NavigationExtension::slotEmitOpenUrlRequestDelayed);
|
||||
}
|
||||
|
||||
void NavigationExtension::slotEmitOpenUrlRequestDelayed()
|
||||
{
|
||||
if (d->m_requests.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
NavigationExtensionPrivate::DelayedRequest req = d->m_requests.front();
|
||||
d->m_requests.pop_front();
|
||||
Q_EMIT openUrlRequestDelayed(req.m_delayedURL, req.m_delayedArgs);
|
||||
// tricky: do not do anything here! (no access to member variables, etc.)
|
||||
}
|
||||
|
||||
void NavigationExtension::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 {
|
||||
qCWarning(KPARTSLOG) << "BrowserExtension::slotEnableAction unknown action " << name;
|
||||
}
|
||||
}
|
||||
|
||||
bool NavigationExtension::isActionEnabled(const char *name) const
|
||||
{
|
||||
int actionNumber = (*s_actionNumberMap())[name];
|
||||
return d->m_actionStatus[actionNumber];
|
||||
}
|
||||
|
||||
void NavigationExtension::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 {
|
||||
qCWarning(KPARTSLOG) << "BrowserExtension::slotSetActionText unknown action " << name;
|
||||
}
|
||||
}
|
||||
|
||||
QString NavigationExtension::actionText(const char *name) const
|
||||
{
|
||||
int actionNumber = (*s_actionNumberMap())[name];
|
||||
QMap<int, QString>::ConstIterator it = d->m_actionText.constFind(actionNumber);
|
||||
if (it != d->m_actionText.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
NavigationExtension::ActionSlotMap *NavigationExtension::actionSlotMap()
|
||||
{
|
||||
if (s_actionSlotMap()->isEmpty()) {
|
||||
NavigationExtensionPrivate::createActionSlotMap();
|
||||
}
|
||||
return s_actionSlotMap();
|
||||
}
|
||||
|
||||
NavigationExtension *NavigationExtension::childObject(QObject *obj)
|
||||
{
|
||||
return obj->findChild<KParts::NavigationExtension *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
#include "moc_navigationextension.cpp"
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_NAVIGATIONEXTENSION
|
||||
#define KPARTS_NAVIGATIONEXTENSION
|
||||
|
||||
#include <kparts/openurlarguments.h>
|
||||
#include <kparts/readonlypart.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QAction>
|
||||
#include <qplatformdefs.h> //mode_t
|
||||
|
||||
template<class Key, class T>
|
||||
class QMap;
|
||||
template<typename T>
|
||||
class QList;
|
||||
|
||||
class KFileItem;
|
||||
class KFileItemList;
|
||||
class QDataStream;
|
||||
class QPoint;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class NavigationExtensionPrivate;
|
||||
|
||||
/**
|
||||
* @class NavigationExtension navigationextension.h <KParts/NavigationExtension>
|
||||
*
|
||||
* @short An extension to KParts::ReadOnlyPart, which allows a better integration of parts
|
||||
* with browsers (in particular Konqueror).
|
||||
*
|
||||
* Remember that ReadOnlyPart only has openUrl(QUrl) and a few arguments() but not much more.
|
||||
* For full-fledged browsing, we need much more than that, including
|
||||
* enabling/disabling of standard actions (print, copy, paste...),
|
||||
* allowing parts to save and restore their data into the back/forward history,
|
||||
* allowing parts to control the location bar URL, to requests URLs
|
||||
* to be opened by the hosting browser, etc.
|
||||
*
|
||||
* The part developer needs to define its own class derived from BrowserExtension,
|
||||
* to implement the virtual methods [and the standard-actions slots, see below].
|
||||
*
|
||||
* The way to associate the BrowserExtension with the part is to simply
|
||||
* create the BrowserExtension as a child of the part (in QObject's terms).
|
||||
* The hosting application will look for it automatically.
|
||||
*
|
||||
* Another aspect of the browser integration is that a set of standard
|
||||
* actions are provided by the browser, but implemented by the part
|
||||
* (for the actions it supports).
|
||||
*
|
||||
* The following standard actions are defined by the host of the view:
|
||||
*
|
||||
* [selection-dependent actions]
|
||||
* @li @p cut : Copy selected items to clipboard and store 'not cut' in clipboard.
|
||||
* @li @p copy : Copy selected items to clipboard and store 'cut' in clipboard.
|
||||
* @li @p paste : Paste clipboard into view URL.
|
||||
* @li @p pasteTo(const QUrl &) : Paste clipboard into given URL.
|
||||
* @li @p searchProvider : Lookup selected text at default search provider
|
||||
*
|
||||
* [normal actions]
|
||||
* @li None anymore.
|
||||
*
|
||||
*
|
||||
* The view defines a slot with the name of the action in order to implement the action.
|
||||
* The browser will detect the slot automatically and connect its action to it when
|
||||
* appropriate (i.e. when the view is active).
|
||||
*
|
||||
*
|
||||
* The selection-dependent actions are disabled by default and the view should
|
||||
* enable them when the selection changes, emitting enableAction().
|
||||
*
|
||||
* The normal actions do not depend on the selection.
|
||||
*
|
||||
* A special case is the configuration slots, not connected to any action directly.
|
||||
*
|
||||
* [configuration slot]
|
||||
* @li @p reparseConfiguration : Re-read configuration and apply it.
|
||||
* @li @p disableScrolling: no scrollbars
|
||||
*/
|
||||
class KPARTS_EXPORT NavigationExtension : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool urlDropHandling READ isURLDropHandlingEnabled WRITE setURLDropHandlingEnabled)
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param parent The KParts::ReadOnlyPart that this extension ... "extends" :)
|
||||
*/
|
||||
explicit NavigationExtension(KParts::ReadOnlyPart *parent);
|
||||
|
||||
~NavigationExtension() override;
|
||||
|
||||
/**
|
||||
* Set of flags passed via the popupMenu signal, to ask for some items in the popup menu.
|
||||
* @see PopupFlags
|
||||
*/
|
||||
enum PopupFlag {
|
||||
DefaultPopupItems = 0x0000, /**< default value, no additional menu item */
|
||||
ShowBookmark = 0x0008, /**< show "add to bookmarks" (usually not done on the local filesystem) */
|
||||
ShowCreateDirectory = 0x0010, /**< show "create directory" (usually only done on the background of the view, or
|
||||
* in hierarchical views like directory trees, where the new dir would be visible) */
|
||||
ShowTextSelectionItems = 0x0020, /**< set when selecting text, for a popup that only contains text-related items. */
|
||||
NoDeletion = 0x0040, /**< deletion, trashing and renaming not allowed (e.g. parent dir not writeable).
|
||||
* (this is only needed if the protocol itself supports deletion, unlike e.g. HTTP) */
|
||||
IsLink = 0x0080, /**< show "Bookmark This Link" and other link-related actions (linkactions merging group) */
|
||||
ShowUrlOperations = 0x0100, /**< show copy, paste, as well as cut if NoDeletion is not set. */
|
||||
ShowProperties = 0x200, /**< show "Properties" action (usually done by directory views) */
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a combination of #PopupFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(PopupFlags, PopupFlag)
|
||||
|
||||
/**
|
||||
* Returns the current x offset.
|
||||
*
|
||||
* For a scrollview, implement this using contentsX().
|
||||
*/
|
||||
virtual int xOffset();
|
||||
/**
|
||||
* Returns the current y offset.
|
||||
*
|
||||
* For a scrollview, implement this using contentsY().
|
||||
*/
|
||||
virtual int yOffset();
|
||||
|
||||
/**
|
||||
* Used by the browser to save the current state of the view
|
||||
* (in order to restore it if going back in navigation).
|
||||
*
|
||||
* If you want to save additional properties, reimplement it
|
||||
* but don't forget to call the parent method (probably first).
|
||||
*/
|
||||
virtual void saveState(QDataStream &stream);
|
||||
|
||||
/**
|
||||
* Used by the browser to restore the view in the state
|
||||
* it was when we left it.
|
||||
*
|
||||
* If you saved additional properties, reimplement it
|
||||
* but don't forget to call the parent method (probably first).
|
||||
*/
|
||||
virtual void restoreState(QDataStream &stream);
|
||||
|
||||
/**
|
||||
* Returns whether url drop handling is enabled.
|
||||
* See setURLDropHandlingEnabled for more information about this
|
||||
* property.
|
||||
*/
|
||||
bool isURLDropHandlingEnabled() const;
|
||||
|
||||
/**
|
||||
* Enables or disables url drop handling. URL drop handling is a property
|
||||
* describing whether the hosting shell component is allowed to install an
|
||||
* event filter on the part's widget, to listen for URI drop events.
|
||||
* Set it to true if you are exporting a BrowserExtension implementation and
|
||||
* do not provide any special URI drop handling. If set to false you can be
|
||||
* sure to receive all those URI drop events unfiltered. Also note that the
|
||||
* implementation as of Konqueror installs the event filter only on the part's
|
||||
* widget itself, not on child widgets.
|
||||
*/
|
||||
void setURLDropHandlingEnabled(bool enable);
|
||||
|
||||
/**
|
||||
* @return the status (enabled/disabled) of an action.
|
||||
* When the enableAction signal is emitted, the browserextension
|
||||
* stores the status of the action internally, so that it's possible
|
||||
* to query later for the status of the action, using this method.
|
||||
*/
|
||||
bool isActionEnabled(const char *name) const;
|
||||
|
||||
/**
|
||||
* @return the text of an action, if it was set explicitly by the part.
|
||||
* When the setActionText signal is emitted, the browserextension
|
||||
* stores the text of the action internally, so that it's possible
|
||||
* to query later for the text of the action, using this method.
|
||||
*/
|
||||
QString actionText(const char *name) const;
|
||||
|
||||
typedef QMap<QByteArray, QByteArray> ActionSlotMap;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the static map containing the action names as keys and corresponding
|
||||
* SLOT()'ified method names as data entries.
|
||||
* The map is created if it doesn't exist yet.
|
||||
*
|
||||
* This is very useful for
|
||||
* the host component, when connecting the own signals with the
|
||||
* extension's slots.
|
||||
* Basically you iterate over the map, check if the extension implements
|
||||
* the slot and connect to the slot using the data value of your map
|
||||
* iterator.
|
||||
* Checking if the extension implements a certain slot can be done like this:
|
||||
*
|
||||
* \code
|
||||
* extension->metaObject()->slotNames().contains( actionName + "()" )
|
||||
* \endcode
|
||||
*
|
||||
* (note that @p actionName is the iterator's key value if already
|
||||
* iterating over the action slot map, returned by this method)
|
||||
*
|
||||
* Connecting to the slot can be done like this:
|
||||
*
|
||||
* \code
|
||||
* connect( yourObject, SIGNAL( yourSignal() ),
|
||||
* extension, mapIterator.data() )
|
||||
* \endcode
|
||||
*
|
||||
* (where "mapIterator" is your ActionSlotMap iterator)
|
||||
*/
|
||||
static ActionSlotMap *actionSlotMap();
|
||||
|
||||
/**
|
||||
* Queries @p obj for a child object which inherits from this
|
||||
* BrowserExtension class. Convenience method.
|
||||
*/
|
||||
static NavigationExtension *childObject(QObject *obj);
|
||||
|
||||
/**
|
||||
* Asks the hosting browser to perform a paste (using openUrlRequestDelayed())
|
||||
*/
|
||||
void pasteRequest();
|
||||
|
||||
/**
|
||||
* Associates a list of actions with a predefined name known by the host's popupmenu:
|
||||
* "editactions" for actions related text editing,
|
||||
* "linkactions" for actions related to hyperlinks,
|
||||
* "partactions" for any other actions provided by the part
|
||||
*/
|
||||
typedef QMap<QString, QList<QAction *>> ActionGroupMap;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Enables or disable a standard action held by the browser.
|
||||
*
|
||||
* See class documentation for the list of standard actions.
|
||||
*/
|
||||
void enableAction(const char *name, bool enabled);
|
||||
|
||||
/**
|
||||
* Change the text of a standard action held by the browser.
|
||||
* This can be used to change "Paste" into "Paste Image" for instance.
|
||||
*
|
||||
* See class documentation for the list of standard actions.
|
||||
*/
|
||||
void setActionText(const char *name, const QString &text);
|
||||
|
||||
/**
|
||||
* Asks the host (browser) to open @p url.
|
||||
* To set a reload, the x and y offsets, the service type etc., fill in the
|
||||
* appropriate fields in the @p args structure.
|
||||
* Hosts should not connect to this signal but to openUrlRequestDelayed().
|
||||
*/
|
||||
void openUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &arguments = KParts::OpenUrlArguments());
|
||||
|
||||
/**
|
||||
* This signal is emitted when openUrlRequest() is called, after a 0-seconds timer.
|
||||
* This allows the caller to terminate what it's doing first, before (usually)
|
||||
* being destroyed. Parts should never use this signal, hosts should only connect
|
||||
* to this signal.
|
||||
*/
|
||||
void openUrlRequestDelayed(const QUrl &url, const KParts::OpenUrlArguments &arguments);
|
||||
|
||||
/**
|
||||
* Tells the hosting browser that the part opened a new URL (which can be
|
||||
* queried via KParts::Part::url().
|
||||
*
|
||||
* This helps the browser to update/create an entry in the history.
|
||||
* The part may @em not emit this signal together with openUrlRequest().
|
||||
* Emit openUrlRequest() if you want the browser to handle a URL the user
|
||||
* asked to open (from within your part/document). This signal however is
|
||||
* useful if you want to handle URLs all yourself internally, while still
|
||||
* telling the hosting browser about new opened URLs, in order to provide
|
||||
* a proper history functionality to the user.
|
||||
* An example of usage is a html rendering component which wants to emit
|
||||
* this signal when a child frame document changed its URL.
|
||||
* Conclusion: you probably want to use openUrlRequest() instead.
|
||||
*/
|
||||
void openUrlNotify();
|
||||
|
||||
/**
|
||||
* Updates the URL shown in the browser's location bar to @p url.
|
||||
*/
|
||||
void setLocationBarUrl(const QString &url);
|
||||
|
||||
/**
|
||||
* Sets the URL of an icon for the currently displayed page.
|
||||
*/
|
||||
void setIconUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Asks the hosting browser to open a new window for the given @p url
|
||||
* and return a reference to the content part.
|
||||
*/
|
||||
void createNewWindow(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Since the part emits the jobid in the started() signal,
|
||||
* progress information is automatically displayed.
|
||||
*
|
||||
* However, if you don't use a KIO::Job in the part,
|
||||
* you can use loadingProgress() and speedProgress()
|
||||
* to display progress information.
|
||||
*/
|
||||
void loadingProgress(int percent);
|
||||
/**
|
||||
* @see loadingProgress
|
||||
*/
|
||||
void speedProgress(int bytesPerSecond);
|
||||
|
||||
void infoMessage(const QString &);
|
||||
|
||||
/**
|
||||
* Emit this to make the browser show a standard popup menu for the files @p items.
|
||||
*
|
||||
* @param global global coordinates where the popup should be shown
|
||||
* @param items list of file items which the popup applies to
|
||||
* @param args OpenUrlArguments, mostly for metadata here
|
||||
* @param flags enables/disables certain builtin actions in the popupmenu
|
||||
* @param actionGroups named groups of actions which should be inserted into the popup, see ActionGroupMap
|
||||
*/
|
||||
void popupMenu(const QPoint &global,
|
||||
const KFileItemList &items,
|
||||
const KParts::OpenUrlArguments &arguments = KParts::OpenUrlArguments(),
|
||||
KParts::NavigationExtension::PopupFlags flags = KParts::NavigationExtension::DefaultPopupItems,
|
||||
const KParts::NavigationExtension::ActionGroupMap &actionGroups = ActionGroupMap());
|
||||
|
||||
/**
|
||||
* Emit this to make the browser show a standard popup menu for the given @p url.
|
||||
*
|
||||
* Give as much information about this URL as possible,
|
||||
* like @p args.mimeType and the file type @p mode
|
||||
*
|
||||
* @param global global coordinates where the popup should be shown
|
||||
* @param url the URL this popup applies to
|
||||
* @param mode the file type of the url (S_IFREG, S_IFDIR...)
|
||||
* @param args OpenUrlArguments, set the mimetype of the URL using setMimeType()
|
||||
* @param flags enables/disables certain builtin actions in the popupmenu
|
||||
* @param actionGroups named groups of actions which should be inserted into the popup, see ActionGroupMap
|
||||
*/
|
||||
void popupMenu(const QPoint &global,
|
||||
const QUrl &url,
|
||||
mode_t mode = static_cast<mode_t>(-1),
|
||||
const KParts::OpenUrlArguments &arguments = KParts::OpenUrlArguments(),
|
||||
KParts::NavigationExtension::PopupFlags flags = KParts::NavigationExtension::DefaultPopupItems,
|
||||
const KParts::NavigationExtension::ActionGroupMap &actionGroups = ActionGroupMap());
|
||||
|
||||
/**
|
||||
* Inform the hosting application about the current selection.
|
||||
* Used when a set of files/URLs is selected (with full information
|
||||
* about those URLs, including size, permissions etc.)
|
||||
*/
|
||||
void selectionInfo(const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* Inform the hosting application that the user moved the mouse over an item.
|
||||
* Used when the mouse is on an URL.
|
||||
*/
|
||||
void mouseOverInfo(const KFileItem &item);
|
||||
|
||||
/**
|
||||
* Ask the hosting application to add a new HTML (aka Mozilla/Netscape)
|
||||
* SideBar entry.
|
||||
*/
|
||||
void addWebSideBar(const QUrl &url, const QString &name);
|
||||
|
||||
/**
|
||||
* Ask the hosting application to move the top level widget.
|
||||
*/
|
||||
void moveTopLevelWidget(int x, int y);
|
||||
|
||||
/**
|
||||
* Ask the hosting application to resize the top level widget.
|
||||
*/
|
||||
void resizeTopLevelWidget(int w, int h);
|
||||
|
||||
/**
|
||||
* Ask the hosting application to focus @p part.
|
||||
*/
|
||||
void requestFocus(KParts::ReadOnlyPart *part);
|
||||
|
||||
/**
|
||||
* Tell the host (browser) about security state of current page
|
||||
* enum PageSecurity { NotCrypted, Encrypted, Mixed };
|
||||
*/
|
||||
void setPageSecurity(int);
|
||||
|
||||
/**
|
||||
* Inform the host about items that have been removed.
|
||||
*/
|
||||
void itemsRemoved(const KFileItemList &items);
|
||||
|
||||
private Q_SLOTS:
|
||||
KPARTS_NO_EXPORT void slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &arguments = KParts::OpenUrlArguments());
|
||||
|
||||
KPARTS_NO_EXPORT void slotEmitOpenUrlRequestDelayed();
|
||||
KPARTS_NO_EXPORT void slotEnableAction(const char *, bool);
|
||||
KPARTS_NO_EXPORT void slotSetActionText(const char *, const QString &);
|
||||
|
||||
public:
|
||||
typedef QMap<QByteArray, int> ActionNumberMap;
|
||||
|
||||
private:
|
||||
std::unique_ptr<NavigationExtensionPrivate> const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(NavigationExtension::PopupFlags)
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "openurlarguments.h"
|
||||
|
||||
class KParts::OpenUrlArgumentsPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
bool reload = false;
|
||||
bool actionRequestedByUser = true;
|
||||
int xOffset = 0;
|
||||
int yOffset = 0;
|
||||
QString mimeType;
|
||||
QMap<QString, QString> metaData;
|
||||
};
|
||||
|
||||
KParts::OpenUrlArguments::OpenUrlArguments()
|
||||
: d(new OpenUrlArgumentsPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KParts::OpenUrlArguments::OpenUrlArguments(const OpenUrlArguments &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
KParts::OpenUrlArguments &KParts::OpenUrlArguments::operator=(const OpenUrlArguments &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
KParts::OpenUrlArguments::~OpenUrlArguments()
|
||||
{
|
||||
}
|
||||
|
||||
bool KParts::OpenUrlArguments::reload() const
|
||||
{
|
||||
return d->reload;
|
||||
}
|
||||
|
||||
void KParts::OpenUrlArguments::setReload(bool b)
|
||||
{
|
||||
d->reload = b;
|
||||
}
|
||||
|
||||
int KParts::OpenUrlArguments::xOffset() const
|
||||
{
|
||||
return d->xOffset;
|
||||
}
|
||||
|
||||
void KParts::OpenUrlArguments::setXOffset(int x)
|
||||
{
|
||||
d->xOffset = x;
|
||||
}
|
||||
|
||||
int KParts::OpenUrlArguments::yOffset() const
|
||||
{
|
||||
return d->yOffset;
|
||||
}
|
||||
|
||||
void KParts::OpenUrlArguments::setYOffset(int y)
|
||||
{
|
||||
d->yOffset = y;
|
||||
}
|
||||
|
||||
QString KParts::OpenUrlArguments::mimeType() const
|
||||
{
|
||||
return d->mimeType;
|
||||
}
|
||||
|
||||
void KParts::OpenUrlArguments::setMimeType(const QString &mime)
|
||||
{
|
||||
d->mimeType = mime;
|
||||
}
|
||||
|
||||
QMap<QString, QString> &KParts::OpenUrlArguments::metaData()
|
||||
{
|
||||
return d->metaData;
|
||||
}
|
||||
|
||||
const QMap<QString, QString> &KParts::OpenUrlArguments::metaData() const
|
||||
{
|
||||
return d->metaData;
|
||||
}
|
||||
|
||||
bool KParts::OpenUrlArguments::actionRequestedByUser() const
|
||||
{
|
||||
return d->actionRequestedByUser;
|
||||
}
|
||||
|
||||
void KParts::OpenUrlArguments::setActionRequestedByUser(bool userRequested)
|
||||
{
|
||||
d->actionRequestedByUser = userRequested;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_OPENURLARGUMENTS_H
|
||||
#define _KPARTS_OPENURLARGUMENTS_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QMap>
|
||||
#include <QSharedDataPointer>
|
||||
#include <QString>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class OpenUrlArgumentsPrivate;
|
||||
|
||||
/**
|
||||
* @class OpenUrlArguments openurlarguments.h <KParts/OpenUrlArguments>
|
||||
*
|
||||
* @short OpenUrlArguments is the set of arguments that specify
|
||||
* how a URL should be opened by KParts::ReadOnlyPart::openUrl().
|
||||
*
|
||||
* For instance reload() indicates that the url should be loaded
|
||||
* from the network even if it matches the current url of the part.
|
||||
*
|
||||
* All setter methods in this class are for the class that calls openUrl
|
||||
* (usually the hosting application), all the getter methods are for the part.
|
||||
*/
|
||||
class KPARTS_EXPORT OpenUrlArguments
|
||||
{
|
||||
public:
|
||||
OpenUrlArguments();
|
||||
OpenUrlArguments(const OpenUrlArguments &other);
|
||||
OpenUrlArguments &operator=(const OpenUrlArguments &other);
|
||||
~OpenUrlArguments();
|
||||
|
||||
/**
|
||||
* @return true to indicate that the part should reload the URL,
|
||||
* i.e. the cache shouldn't be used (forced reload).
|
||||
*/
|
||||
bool reload() const;
|
||||
/**
|
||||
* Indicates that the url should be loaded
|
||||
* from the network even if it matches the current url of the part.
|
||||
*/
|
||||
void setReload(bool b);
|
||||
|
||||
/**
|
||||
* xOffset is the horizontal scrolling of the part's widget
|
||||
* (in case it's a scrollview). This is saved into the history
|
||||
* and restored when going back in the history.
|
||||
*/
|
||||
int xOffset() const;
|
||||
void setXOffset(int x);
|
||||
|
||||
/**
|
||||
* yOffset is the vertical scrolling of the part's widget
|
||||
* (in case it's a scrollview). This is saved into the history
|
||||
* and restored when going back in the history.
|
||||
*/
|
||||
int yOffset() const;
|
||||
void setYOffset(int y);
|
||||
|
||||
/**
|
||||
* The mimetype to use when opening the url, when known by the calling application.
|
||||
*/
|
||||
QString mimeType() const;
|
||||
void setMimeType(const QString &mime);
|
||||
|
||||
/**
|
||||
* True if the user requested that the URL be opened.
|
||||
* False if the URL should be opened due to an external event, like javascript popups
|
||||
* or automatic redirections.
|
||||
* This is true by default
|
||||
* @since 4.1
|
||||
*/
|
||||
bool actionRequestedByUser() const;
|
||||
void setActionRequestedByUser(bool userRequested);
|
||||
|
||||
/**
|
||||
* Meta-data to associate with the KIO operation that will be used to open the URL.
|
||||
* This method can be used to add or retrieve metadata.
|
||||
* @see KIO::TransferJob etc.
|
||||
*/
|
||||
QMap<QString, QString> &metaData();
|
||||
const QMap<QString, QString> &metaData() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<OpenUrlArgumentsPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "openurlevent.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
class KParts::OpenUrlEventPrivate
|
||||
{
|
||||
public:
|
||||
OpenUrlEventPrivate(ReadOnlyPart *part, const QUrl &url, const OpenUrlArguments &args)
|
||||
: m_part(part)
|
||||
, m_url(url)
|
||||
, m_args(args)
|
||||
{
|
||||
}
|
||||
ReadOnlyPart *const m_part;
|
||||
const QUrl m_url;
|
||||
const OpenUrlArguments m_args;
|
||||
};
|
||||
|
||||
const QEvent::Type openUrlEventType = (QEvent::Type)8958;
|
||||
OpenUrlEvent::OpenUrlEvent(ReadOnlyPart *part, const QUrl &url, const OpenUrlArguments &args)
|
||||
: QEvent(openUrlEventType)
|
||||
, d(new OpenUrlEventPrivate(part, url, args))
|
||||
{
|
||||
}
|
||||
|
||||
OpenUrlEvent::~OpenUrlEvent() = default;
|
||||
|
||||
ReadOnlyPart *OpenUrlEvent::part() const
|
||||
{
|
||||
return d->m_part;
|
||||
}
|
||||
|
||||
QUrl OpenUrlEvent::url() const
|
||||
{
|
||||
return d->m_url;
|
||||
}
|
||||
|
||||
OpenUrlArguments OpenUrlEvent::arguments() const
|
||||
{
|
||||
return d->m_args;
|
||||
}
|
||||
|
||||
bool OpenUrlEvent::test(const QEvent *event)
|
||||
{
|
||||
return event->type() == openUrlEventType;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kparts_openurlevent_h__
|
||||
#define __kparts_openurlevent_h__
|
||||
|
||||
#include <QEvent>
|
||||
#include <kparts/openurlarguments.h>
|
||||
#include <memory>
|
||||
|
||||
class QUrl;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPart;
|
||||
class OpenUrlEventPrivate;
|
||||
|
||||
/**
|
||||
* @class OpenUrlEvent openurlevent.h <KParts/OpenUrlEvent>
|
||||
*
|
||||
* @short The KParts::OpenUrlEvent event informs that a given part has opened a given URL.
|
||||
* Applications can use this event to send this information to interested plugins.
|
||||
*
|
||||
* The event should be sent before opening the URL in the part, so that the plugins
|
||||
* can use part()->url() to get the old URL.
|
||||
*/
|
||||
class KPARTS_EXPORT OpenUrlEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
OpenUrlEvent(ReadOnlyPart *part, const QUrl &url, const OpenUrlArguments &args = OpenUrlArguments());
|
||||
~OpenUrlEvent() override;
|
||||
|
||||
ReadOnlyPart *part() const;
|
||||
QUrl url() const;
|
||||
OpenUrlArguments arguments() const;
|
||||
|
||||
static bool test(const QEvent *event);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<OpenUrlEventPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "part.h"
|
||||
#include "part_p.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
#include "partmanager.h"
|
||||
|
||||
// The events have randomly generated numbers using `shuf -i1000-65535 -n 1`
|
||||
#include "guiactivateevent.h"
|
||||
#include "partactivateevent.h"
|
||||
|
||||
#include <KXMLGUIFactory>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
Part::Part(QObject *parent, const KPluginMetaData &data)
|
||||
: Part(*new PartPrivate(this, data), parent)
|
||||
{
|
||||
}
|
||||
|
||||
Part::Part(PartPrivate &dd, QObject *parent)
|
||||
: QObject(parent)
|
||||
, PartBase(dd)
|
||||
{
|
||||
Q_D(Part);
|
||||
|
||||
PartBase::setPartObject(this);
|
||||
if (d->m_metaData.isValid()) {
|
||||
KXMLGUIClient::setComponentName(d->m_metaData.pluginId(), d->m_metaData.name());
|
||||
}
|
||||
}
|
||||
|
||||
Part::~Part()
|
||||
{
|
||||
Q_D(Part);
|
||||
|
||||
// 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) {
|
||||
// qCDebug(KPARTSLOG) << "deleting widget" << d->m_widget << d->m_widget->objectName();
|
||||
delete static_cast<QWidget *>(d->m_widget);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
KPluginMetaData Part::metaData() const
|
||||
{
|
||||
Q_D(const Part);
|
||||
|
||||
return d->m_metaData;
|
||||
}
|
||||
|
||||
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::customEvent(QEvent *ev)
|
||||
{
|
||||
if (PartActivateEvent::test(ev)) {
|
||||
partActivateEvent(static_cast<PartActivateEvent *>(ev));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUIActivateEvent::test(ev)) {
|
||||
guiActivateEvent(static_cast<GUIActivateEvent *>(ev));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::customEvent(ev);
|
||||
}
|
||||
|
||||
void Part::partActivateEvent(PartActivateEvent *)
|
||||
{
|
||||
}
|
||||
|
||||
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) {
|
||||
// qCDebug(KPARTSLOG) << "deleting part" << objectName();
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_part.cpp"
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_PART_H
|
||||
#define _KPARTS_PART_H
|
||||
|
||||
#include <KPluginMetaData>
|
||||
#include <kparts/partbase.h>
|
||||
|
||||
class QWidget;
|
||||
class QEvent;
|
||||
class QPoint;
|
||||
|
||||
/**
|
||||
* The KParts namespace,
|
||||
*/
|
||||
namespace KParts
|
||||
{
|
||||
class PartManager;
|
||||
class PartPrivate;
|
||||
class PartActivateEvent;
|
||||
class GUIActivateEvent;
|
||||
|
||||
/**
|
||||
* @class Part part.h <KParts/Part>
|
||||
*
|
||||
* @short Base class for parts.
|
||||
*
|
||||
* A "part" is a GUI component, featuring:
|
||||
* @li A widget embeddedable in any application.
|
||||
* @li GUI elements that will be merged in the "host" user interface
|
||||
* (menubars, toolbars... ).
|
||||
*
|
||||
* <b>About the widget:</b>\n
|
||||
*
|
||||
* Note that KParts::Part does not inherit QWidget.
|
||||
* This is due to the fact that the "visual representation"
|
||||
* will probably not be a mere QWidget, but an elaborate one.
|
||||
* That's why when implementing your KParts::Part (or derived)
|
||||
* you should call KParts::Part::setWidget() in your constructor.
|
||||
*
|
||||
* <b>About the GUI elements:</b>\n
|
||||
*
|
||||
* Those elements trigger actions, defined by the part ( action()).
|
||||
* The layout of the actions in the GUI is defined by an XML file ( setXMLFile()).
|
||||
*
|
||||
* See also ReadOnlyPart and ReadWritePart, which define the
|
||||
* framework for a "viewer" part and for an "editor"-like part.
|
||||
* Use Part directly only if your part doesn't fit into those.
|
||||
*/
|
||||
class KPARTS_EXPORT Part : public QObject, public PartBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
KPARTS_DECLARE_PRIVATE(Part)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parent Parent object of the part.
|
||||
* @param data KPluginMetaData associated with this part, see Part::metaData()
|
||||
*/
|
||||
explicit Part(QObject *parent = nullptr, const KPluginMetaData &data = {});
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Part() override;
|
||||
|
||||
/**
|
||||
* @return The widget defined by this part, set by setWidget().
|
||||
*/
|
||||
virtual QWidget *widget();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Used by the part manager.
|
||||
*/
|
||||
virtual void setManager(PartManager *manager);
|
||||
|
||||
/**
|
||||
* Returns the part manager handling this part, if any (0L otherwise).
|
||||
*/
|
||||
PartManager *manager() const;
|
||||
|
||||
/**
|
||||
* By default, the widget is deleted by the part when the part is deleted.
|
||||
* The hosting application can call setAutoDeleteWidget(false) to
|
||||
* disable this behavior, given that the widget is usually deleted by
|
||||
* its parent widget anyway.
|
||||
* This is a method for the hosting application only, Part subclasses
|
||||
* should never call this.
|
||||
*/
|
||||
void setAutoDeleteWidget(bool autoDeleteWidget);
|
||||
|
||||
/**
|
||||
* By default, the part deletes itself when its widget is deleted.
|
||||
* The hosting application can call setAutoDeletePart(false) to
|
||||
* disable this behavior, to be able to delete the widget and then the part,
|
||||
* independently.
|
||||
* This is a method for the hosting application only, Part subclasses
|
||||
* should never call this.
|
||||
*/
|
||||
void setAutoDeletePart(bool autoDeletePart);
|
||||
|
||||
/**
|
||||
* Returns the part (this, or a child part) at the given global position.
|
||||
* This is called by the part manager to ask whether a part should be activated
|
||||
* when clicking somewhere. In most cases the default implementation is enough.
|
||||
* Reimplement this if your part has child parts in some areas (like in khtml or koffice)
|
||||
* @param widget the part widget being clicked - usually the same as widget(), except in koffice.
|
||||
* @param globalPos the mouse coordinates in global coordinates
|
||||
*/
|
||||
virtual Part *hitTest(QWidget *widget, const QPoint &globalPos);
|
||||
|
||||
/**
|
||||
* @since 5.77
|
||||
*/
|
||||
KPluginMetaData metaData() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted by the part, to set the caption of the window(s)
|
||||
* hosting this part
|
||||
*
|
||||
* @note this signal has only an effect on the window title if window title
|
||||
* handling is enabled @see KParts::MainWindow::setWindowTitleHandling
|
||||
*/
|
||||
void setWindowCaption(const QString &caption);
|
||||
/**
|
||||
* Emitted by the part, to set a text in the statusbar of the window(s)
|
||||
* hosting this part
|
||||
*/
|
||||
void setStatusBarText(const QString &text);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Set the main widget.
|
||||
*
|
||||
* Call this in the Part-inherited class constructor.
|
||||
*/
|
||||
virtual void setWidget(QWidget *widget);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void customEvent(QEvent *event) override;
|
||||
|
||||
/**
|
||||
* Convenience method which is called when the Part received a PartActivateEvent .
|
||||
* Reimplement this if you don't want to reimplement event and test for the event yourself
|
||||
* or even install an event filter.
|
||||
*/
|
||||
virtual void partActivateEvent(PartActivateEvent *event);
|
||||
|
||||
/**
|
||||
* Convenience method which is called when the Part received a
|
||||
* GUIActivateEvent .
|
||||
* Reimplement this if you don't want to reimplement event and
|
||||
* test for the event yourself or even install an event filter.
|
||||
*/
|
||||
virtual void guiActivateEvent(GUIActivateEvent *event);
|
||||
|
||||
/**
|
||||
* Convenience method for KXMLGUIFactory::container.
|
||||
* @return a container widget owned by the Part's GUI.
|
||||
*/
|
||||
QWidget *hostContainer(const QString &containerName);
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void slotWidgetDestroyed();
|
||||
|
||||
protected:
|
||||
KPARTS_NO_EXPORT Part(PartPrivate &dd, QObject *parent);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(Part)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_PART_P_H
|
||||
#define _KPARTS_PART_P_H
|
||||
|
||||
#include "part.h"
|
||||
#include "partbase_p.h"
|
||||
// KF
|
||||
#include <KPluginMetaData>
|
||||
// Qt
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class PartPrivate : public PartBasePrivate
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(Part)
|
||||
|
||||
explicit PartPrivate(Part *qq, const KPluginMetaData &data)
|
||||
: PartBasePrivate(qq)
|
||||
, m_metaData(data)
|
||||
, m_autoDeleteWidget(true)
|
||||
, m_autoDeletePart(true)
|
||||
, m_manager(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~PartPrivate() override
|
||||
{
|
||||
}
|
||||
|
||||
const KPluginMetaData m_metaData;
|
||||
bool m_autoDeleteWidget;
|
||||
bool m_autoDeletePart;
|
||||
PartManager *m_manager;
|
||||
QPointer<QWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "partactivateevent.h"
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
class KParts::PartActivateEventPrivate
|
||||
{
|
||||
public:
|
||||
PartActivateEventPrivate(bool activated, Part *part, QWidget *widget)
|
||||
: m_bActivated(activated)
|
||||
, m_part(part)
|
||||
, m_widget(widget)
|
||||
{
|
||||
}
|
||||
const bool m_bActivated;
|
||||
Part *const m_part;
|
||||
QWidget *const m_widget;
|
||||
};
|
||||
|
||||
const QEvent::Type partActivateEvent = (QEvent::Type)11769;
|
||||
|
||||
PartActivateEvent::PartActivateEvent(bool activated, Part *part, QWidget *widget)
|
||||
: QEvent(partActivateEvent)
|
||||
, d(new PartActivateEventPrivate(activated, part, widget))
|
||||
{
|
||||
}
|
||||
|
||||
PartActivateEvent::~PartActivateEvent() = default;
|
||||
|
||||
bool PartActivateEvent::activated() const
|
||||
{
|
||||
return d->m_bActivated;
|
||||
}
|
||||
|
||||
Part *PartActivateEvent::part() const
|
||||
{
|
||||
return d->m_part;
|
||||
}
|
||||
|
||||
QWidget *PartActivateEvent::widget() const
|
||||
{
|
||||
return d->m_widget;
|
||||
}
|
||||
|
||||
bool PartActivateEvent::test(const QEvent *event)
|
||||
{
|
||||
return event->type() == partActivateEvent;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef __kparts_partactivateevent_h__
|
||||
#define __kparts_partactivateevent_h__
|
||||
|
||||
#include <QEvent>
|
||||
#include <kparts/kparts_export.h>
|
||||
#include <memory>
|
||||
|
||||
class QWidget;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class Part;
|
||||
|
||||
class PartActivateEventPrivate;
|
||||
/**
|
||||
* @class PartActivateEvent partactivateevent.h <KParts/PartActivateEvent>
|
||||
*
|
||||
* @short This event is sent by the part manager when the active part changes.
|
||||
* Each time the active part changes, it will send first a PartActivateEvent
|
||||
* with activated=false, part=oldActivePart, widget=oldActiveWidget
|
||||
* and then another PartActivateEvent
|
||||
* with activated=true, part=newPart, widget=newWidget.
|
||||
* @see KParts::Part::partActivateEvent
|
||||
*/
|
||||
class KPARTS_EXPORT PartActivateEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
PartActivateEvent(bool activated, Part *part, QWidget *widget);
|
||||
~PartActivateEvent() override;
|
||||
bool activated() const;
|
||||
|
||||
Part *part() const;
|
||||
QWidget *widget() const;
|
||||
|
||||
static bool test(const QEvent *event);
|
||||
|
||||
private:
|
||||
const std::unique_ptr<PartActivateEventPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "partbase.h"
|
||||
#include "partbase_p.h"
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
PartBase::PartBase()
|
||||
: d_ptr(new PartBasePrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
PartBase::PartBase(PartBasePrivate &dd)
|
||||
: d_ptr(&dd)
|
||||
{
|
||||
}
|
||||
|
||||
PartBase::~PartBase() = default;
|
||||
|
||||
void PartBase::setPartObject(QObject *obj)
|
||||
{
|
||||
Q_D(PartBase);
|
||||
|
||||
d->m_obj = obj;
|
||||
}
|
||||
|
||||
QObject *PartBase::partObject() const
|
||||
{
|
||||
Q_D(const PartBase);
|
||||
|
||||
return d->m_obj;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_PARTBASE_H
|
||||
#define _KPARTS_PARTBASE_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <KXMLGUIClient>
|
||||
#include <memory>
|
||||
|
||||
// Internal:
|
||||
// As KParts::PartBase is inherited by KParts::Part which also inheriting from QObject,
|
||||
// which already has a protected d_ptr member, the macro Q_DECLARE_PRIVATE cannot be used
|
||||
// as it references d_ptr without any class qualifier, which is ambiguous then.
|
||||
#define KPARTS_DECLARE_PRIVATE(Class) Q_DECLARE_PRIVATE_D(PartBase::d_ptr, Class)
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class PartBasePrivate;
|
||||
|
||||
/**
|
||||
* @class PartBase partbase.h <KParts/PartBase>
|
||||
*
|
||||
* @short Base class for all parts.
|
||||
*/
|
||||
class KPARTS_EXPORT PartBase : virtual public KXMLGUIClient
|
||||
{
|
||||
KPARTS_DECLARE_PRIVATE(PartBase)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PartBase();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PartBase() override;
|
||||
|
||||
/**
|
||||
* Internal method. Called by KParts::Part to specify the parent object for plugin objects.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
void setPartObject(QObject *object);
|
||||
QObject *partObject() const;
|
||||
|
||||
protected:
|
||||
KPARTS_NO_EXPORT explicit PartBase(PartBasePrivate &dd);
|
||||
|
||||
std::unique_ptr<PartBasePrivate> const d_ptr;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(PartBase)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_PARTBASE_P_H
|
||||
#define _KPARTS_PARTBASE_P_H
|
||||
|
||||
#include "partbase.h"
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class PartBasePrivate
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(PartBase)
|
||||
|
||||
PartBasePrivate(PartBase *qq)
|
||||
: q_ptr(qq)
|
||||
, m_obj(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PartBasePrivate()
|
||||
{
|
||||
}
|
||||
|
||||
PartBase *q_ptr;
|
||||
QObject *m_obj;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "partloader.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KLocalizedString>
|
||||
#include <KService>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QMetaEnum>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
|
||||
static QList<KPluginMetaData> partsFromUserPreference(const QString &mimeType)
|
||||
{
|
||||
auto config = KSharedConfig::openConfig(QStringLiteral("kpartsrc"), KConfig::NoGlobals);
|
||||
const QStringList pluginIds = config->group(QStringLiteral("Added KDE Part Associations")).readXdgListEntry(mimeType);
|
||||
QList<KPluginMetaData> plugins;
|
||||
plugins.reserve(pluginIds.size());
|
||||
for (const QString &pluginId : pluginIds) {
|
||||
if (KPluginMetaData data(QLatin1String("kf6/parts/") + pluginId); data.isValid()) {
|
||||
plugins << data;
|
||||
}
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
KParts::PartCapabilities KParts::PartLoader::partCapabilities(const KPluginMetaData &data)
|
||||
{
|
||||
QJsonValue capsArrayRaw = data.rawData().value(QLatin1String("KParts")).toObject().value(QLatin1String("Capabilities"));
|
||||
KParts::PartCapabilities parsedCapabilties = {};
|
||||
const static QMetaEnum metaEnum = QMetaEnum::fromType<KParts::PartCapability>();
|
||||
QJsonArray capabilities = capsArrayRaw.toArray();
|
||||
for (const QJsonValue &capability : capabilities) {
|
||||
bool ok = true;
|
||||
PartCapability parsedCapability = (PartCapability)metaEnum.keyToValue(capability.toString().toLocal8Bit().constData(), &ok);
|
||||
if (ok) {
|
||||
parsedCapabilties |= parsedCapability;
|
||||
} else {
|
||||
qCWarning(KPARTSLOG) << "Could not find capability value" << capability.toString().toLocal8Bit().constData();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't bother looking at fallback API
|
||||
if (!capsArrayRaw.isUndefined()) {
|
||||
return parsedCapabilties;
|
||||
}
|
||||
|
||||
static QMap<QString, KParts::PartCapability> capabilityMapping = {
|
||||
{QStringLiteral("KParts/ReadOnlyPart"), PartCapability::ReadOnly},
|
||||
{QStringLiteral("KParts/ReadWritePart"), PartCapability::ReadWrite},
|
||||
{QStringLiteral("Browser/View"), PartCapability::BrowserView},
|
||||
};
|
||||
const auto serviceTypes = data.rawData().value(QLatin1String("KPlugin")).toObject().value(QLatin1String("ServiceTypes")).toVariant().toStringList();
|
||||
if (!serviceTypes.isEmpty()) {
|
||||
qCWarning(KPARTSLOG) << data
|
||||
<< "still defined ServiceTypes - this is deprecated in favor of providing a "
|
||||
" \"Capabilities\" list in the \"KParts\" object in the root of the metadata";
|
||||
for (const QString &serviceType : serviceTypes) {
|
||||
auto it = capabilityMapping.find(serviceType);
|
||||
if (it == capabilityMapping.cend()) {
|
||||
qCWarning(KPARTSLOG) << "ServiceType" << serviceType << "from" << data
|
||||
<< "is not a known value that can be mapped to new Capability enum values";
|
||||
} else {
|
||||
parsedCapabilties |= *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsedCapabilties;
|
||||
}
|
||||
|
||||
QList<KPluginMetaData> KParts::PartLoader::partsForMimeType(const QString &mimeType)
|
||||
{
|
||||
auto supportsMime = [&mimeType](const KPluginMetaData &md) {
|
||||
if (md.supportsMimeType(mimeType)) {
|
||||
return true;
|
||||
}
|
||||
auto pluginJson = md.rawData();
|
||||
auto pluginNamespace = pluginJson.value(QLatin1String("KParts")).toObject().value(QLatin1String("PluginNamespace")).toString();
|
||||
if (pluginNamespace.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
auto plugins = KPluginMetaData::findPlugins(pluginNamespace, [&mimeType](const KPluginMetaData &pluginMd) {
|
||||
return pluginMd.supportsMimeType(mimeType);
|
||||
});
|
||||
return !plugins.isEmpty();
|
||||
};
|
||||
QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/parts"), supportsMime);
|
||||
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
|
||||
const auto getInitialPreference = [](const KPluginMetaData &data) {
|
||||
const QJsonObject obj = data.rawData();
|
||||
if (const QJsonValue initialPref = obj.value(QLatin1String("KParts")).toObject().value(QLatin1String("InitialPreference"));
|
||||
!initialPref.isUndefined()) {
|
||||
return initialPref.toInt();
|
||||
}
|
||||
return data.rawData().value(QLatin1String("KPlugin")).toObject().value(QLatin1String("InitialPreference")).toInt();
|
||||
};
|
||||
return getInitialPreference(left) > getInitialPreference(right);
|
||||
};
|
||||
std::sort(plugins.begin(), plugins.end(), orderPredicate);
|
||||
|
||||
const QList<KPluginMetaData> userParts = partsFromUserPreference(mimeType);
|
||||
if (!userParts.isEmpty()) {
|
||||
plugins = userParts;
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
void KParts::PartLoader::Private::getErrorStrings(QString *errorString, QString *errorText, const QString &argument, ErrorType type)
|
||||
{
|
||||
switch (type) {
|
||||
case CouldNotLoadPlugin:
|
||||
*errorString = i18n("KPluginFactory could not load the plugin: %1", argument);
|
||||
*errorText = QStringLiteral("KPluginFactory could not load the plugin: %1").arg(argument);
|
||||
break;
|
||||
case NoPartFoundForMimeType:
|
||||
*errorString = i18n("No part was found for mimeType %1", argument);
|
||||
*errorText = QStringLiteral("No part was found for mimeType %1").arg(argument);
|
||||
break;
|
||||
case NoPartInstantiatedForMimeType:
|
||||
*errorString = i18n("No part could be instantiated for mimeType %1", argument);
|
||||
*errorText = QStringLiteral("No part could be instantiated for mimeType %1").arg(argument);
|
||||
break;
|
||||
default:
|
||||
qCWarning(KPARTSLOG) << "PartLoader::Private::getErrorStrings got unexpected error type" << type;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
#include "moc_partloader.cpp"
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_PARTLOADER_H
|
||||
#define KPARTS_PARTLOADER_H
|
||||
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginMetaData>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
Q_NAMESPACE_EXPORT(KPARTS_EXPORT)
|
||||
|
||||
/**
|
||||
* Enum for standardized capabilities of KParts
|
||||
*
|
||||
* @code
|
||||
* {
|
||||
* "KPlugin": {...},
|
||||
* "KParts": {
|
||||
* "Capabilities": [
|
||||
* "ReadOnly"
|
||||
* ],
|
||||
* "InitialPreference": 42
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @since 6.4
|
||||
*/
|
||||
enum class PartCapability {
|
||||
ReadOnly = 1,
|
||||
ReadWrite = 2,
|
||||
BrowserView = 4,
|
||||
};
|
||||
Q_ENUM_NS(PartCapability)
|
||||
Q_DECLARE_FLAGS(PartCapabilities, PartCapability)
|
||||
Q_FLAG_NS(PartCapabilities)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(PartCapabilities)
|
||||
|
||||
/**
|
||||
* Helper methods for locating and loading parts.
|
||||
* This is based upon KPluginFactory, but it takes
|
||||
* care of querying by mimetype, sorting the available parts by builtin
|
||||
* preference and by user preference.
|
||||
* @since 5.69
|
||||
*/
|
||||
namespace PartLoader
|
||||
{
|
||||
namespace Private
|
||||
{
|
||||
|
||||
enum ErrorType {
|
||||
CouldNotLoadPlugin,
|
||||
NoPartFoundForMimeType,
|
||||
NoPartInstantiatedForMimeType,
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param errorString translated, user-visible error string
|
||||
* @param errorText untranslated error text
|
||||
* @param argument argument for the text
|
||||
*/
|
||||
KPARTS_EXPORT void getErrorStrings(QString *errorString, QString *errorText, const QString &argument, ErrorType type);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the associated capabilities from the KPart. This parses the deprecated "ServiceTypes" array of the "KPlugin" object
|
||||
*
|
||||
* @since 6.4
|
||||
*/
|
||||
KPARTS_EXPORT PartCapabilities partCapabilities(const KPluginMetaData &data);
|
||||
|
||||
/**
|
||||
* Locate all available KParts using KPluginMetaData::findPlugins for a mimetype.
|
||||
* @return a list of plugin metadata, sorted by preference.
|
||||
* This takes care both of the builtin preference (set by developers)
|
||||
* and of user preference (stored in mimeapps.list).
|
||||
*
|
||||
* To load a part from one of the KPluginMetaData instances returned here,
|
||||
* use \ref instantiatePart()
|
||||
*
|
||||
* @since 5.69
|
||||
*/
|
||||
KPARTS_EXPORT QList<KPluginMetaData> partsForMimeType(const QString &mimeType);
|
||||
|
||||
/**
|
||||
* Attempts to create a KPart from the given metadata.
|
||||
*
|
||||
* @code
|
||||
* if (auto result = KParts::PartLoader::instantiatePart<MyPart>(metaData, parentWidget, parent, args)) {
|
||||
* // result.plugin is valid and can be accessed
|
||||
* } else {
|
||||
* // result contains information about the error
|
||||
* }
|
||||
* @endcode
|
||||
* @param data KPluginMetaData from which the plugin should be loaded
|
||||
* @param parentWidget The parent widget
|
||||
* @param parent The parent object
|
||||
* @param args A list of arguments to be passed to the part
|
||||
* @return Result object which contains the plugin instance and potentially error information
|
||||
* @since 5.100
|
||||
*/
|
||||
template<typename T>
|
||||
static KPluginFactory::Result<T>
|
||||
instantiatePart(const KPluginMetaData &data, QWidget *parentWidget = nullptr, QObject *parent = nullptr, const QVariantList &args = {})
|
||||
{
|
||||
KPluginFactory::Result<T> result;
|
||||
KPluginFactory::Result<KPluginFactory> factoryResult = KPluginFactory::loadFactory(data);
|
||||
if (!factoryResult.plugin) {
|
||||
result.errorString = factoryResult.errorString;
|
||||
result.errorReason = factoryResult.errorReason;
|
||||
return result;
|
||||
}
|
||||
T *instance = factoryResult.plugin->create<T>(parentWidget, parent, args);
|
||||
if (!instance) {
|
||||
const QString fileName = data.fileName();
|
||||
Private::getErrorStrings(&result.errorString, &result.errorText, fileName, Private::CouldNotLoadPlugin);
|
||||
result.errorReason = KPluginFactory::INVALID_KPLUGINFACTORY_INSTANTIATION;
|
||||
} else {
|
||||
result.plugin = instance;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to create a KParts part. It will try to create an object which inherits
|
||||
* @p T.
|
||||
*
|
||||
* @code
|
||||
* if (auto result = KParts::PartLoader::instantiatePartForMimeType<KParts::ReadOnlyPart>(mimeType, parentWidget, parent, args)) {
|
||||
* // result.plugin is valid and can be accessed
|
||||
* } else {
|
||||
* // result contains information about the error
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam T The interface for which an object should be created. The object will inherit @p T.
|
||||
* @param mimeType The mimetype for which we need a KParts.
|
||||
* @param parentWidget The parent widget for the part's widget.
|
||||
* @param parent The parent of the part.
|
||||
* @return Result object which contains the plugin instance and potentially error information
|
||||
* @since 5.100
|
||||
*/
|
||||
template<class T>
|
||||
static KPluginFactory::Result<T>
|
||||
instantiatePartForMimeType(const QString &mimeType, QWidget *parentWidget = nullptr, QObject *parent = nullptr, const QVariantList &args = {})
|
||||
{
|
||||
const QList<KPluginMetaData> plugins = KParts::PartLoader::partsForMimeType(mimeType);
|
||||
if (plugins.isEmpty()) {
|
||||
KPluginFactory::Result<T> errorResult;
|
||||
errorResult.errorReason = KPluginFactory::ResultErrorReason::INVALID_PLUGIN;
|
||||
Private::getErrorStrings(&errorResult.errorString, &errorResult.errorText, mimeType, Private::NoPartFoundForMimeType);
|
||||
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
for (const KPluginMetaData &plugin : plugins) {
|
||||
if (const auto result = instantiatePart<T>(plugin, parentWidget, parent, args)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
KPluginFactory::Result<T> errorResult;
|
||||
errorResult.errorReason = KPluginFactory::ResultErrorReason::INVALID_PLUGIN;
|
||||
Private::getErrorStrings(&errorResult.errorString, &errorResult.errorText, mimeType, Private::NoPartInstantiatedForMimeType);
|
||||
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
Q_DECLARE_METATYPE(KParts::PartCapabilities)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "partmanager.h"
|
||||
|
||||
#include "guiactivateevent.h"
|
||||
#include "kparts_logging.h"
|
||||
#include "part.h"
|
||||
#include "partactivateevent.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class PartManagerPrivate
|
||||
{
|
||||
public:
|
||||
PartManagerPrivate()
|
||||
{
|
||||
m_activeWidget = nullptr;
|
||||
m_activePart = nullptr;
|
||||
m_bAllowNestedParts = false;
|
||||
m_bIgnoreScrollBars = false;
|
||||
m_activationButtonMask = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton;
|
||||
m_reason = PartManager::NoReason;
|
||||
m_bIgnoreExplicitFocusRequest = false;
|
||||
}
|
||||
~PartManagerPrivate()
|
||||
{
|
||||
}
|
||||
void setReason(QEvent *ev)
|
||||
{
|
||||
switch (ev->type()) {
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonDblClick: {
|
||||
// clang-format off
|
||||
QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
|
||||
m_reason = mev->button() == Qt::LeftButton
|
||||
? PartManager::ReasonLeftClick
|
||||
: (mev->button() == Qt::MiddleButton
|
||||
? PartManager::ReasonMidClick
|
||||
: PartManager::ReasonRightClick);
|
||||
// clang-format on
|
||||
break;
|
||||
}
|
||||
case QEvent::FocusIn:
|
||||
m_reason = static_cast<QFocusEvent *>(ev)->reason();
|
||||
break;
|
||||
default:
|
||||
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<QFocusEvent *>(ev);
|
||||
return (!m_bIgnoreExplicitFocusRequest || fev->reason() != Qt::OtherFocusReason);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Part *m_activePart;
|
||||
QWidget *m_activeWidget;
|
||||
|
||||
QList<Part *> m_parts;
|
||||
|
||||
PartManager::SelectionPolicy m_policy;
|
||||
|
||||
QList<const QWidget *> 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 : std::as_const(d->m_managedTopLevelWidgets)) {
|
||||
disconnect(w, &QWidget::destroyed, this, &PartManager::slotManagedTopLevelWidgetDestroyed);
|
||||
}
|
||||
|
||||
for (Part *it : std::as_const(d->m_parts)) {
|
||||
it->setManager(nullptr);
|
||||
}
|
||||
|
||||
// core dumps ... setActivePart( 0 );
|
||||
qApp->removeEventFilter(this);
|
||||
}
|
||||
|
||||
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<QWidget *>(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<QMouseEvent *>(ev);
|
||||
|
||||
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<QScrollBar *>(w)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mev) { // mouse press or mouse double-click event
|
||||
pos = mev->globalPosition().toPoint();
|
||||
part = findPartFromWidget(w, pos);
|
||||
} else {
|
||||
part = findPartFromWidget(w);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
const char *evType = (ev->type() == QEvent::MouseButtonPress) ? "MouseButtonPress"
|
||||
: (ev->type() == QEvent::MouseButtonDblClick) ? "MouseButtonDblClick"
|
||||
: (ev->type() == QEvent::FocusIn) ? "FocusIn" : "OTHER! ERROR!";
|
||||
// clang-format on
|
||||
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;
|
||||
}
|
||||
|
||||
qCDebug(KPARTSLOG) << "PartManager::eventFilter dblclick -> setActivePart" << part;
|
||||
|
||||
d->setReason(ev);
|
||||
setActivePart(part, w);
|
||||
d->m_reason = NoReason;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((d->m_activeWidget != w || d->m_activePart != part)) {
|
||||
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_activeWidget == w && d->m_activePart == part) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if (part != d->m_activePart && d->allowExplicitFocusEvent(ev)) {
|
||||
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))) {
|
||||
qCDebug(KPARTSLOG) << "No part made active although" << obj->objectName() << "/" << obj->metaObject()->className() << "got event - loop aborted";
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 : std::as_const(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 : std::as_const(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)) {
|
||||
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) {
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
Q_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);
|
||||
|
||||
Q_EMIT partRemoved(part);
|
||||
|
||||
if (part == d->m_activePart) {
|
||||
setActivePart(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void PartManager::replacePart(Part *oldPart, Part *newPart, bool 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);
|
||||
|
||||
Q_EMIT partRemoved(oldPart);
|
||||
|
||||
addPart(newPart, setActive);
|
||||
}
|
||||
|
||||
void PartManager::setActivePart(Part *part, QWidget *widget)
|
||||
{
|
||||
if (part && !d->m_parts.contains(part)) {
|
||||
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<KParts::Part *>(parentPart);
|
||||
if (parPart) {
|
||||
setActivePart(parPart, parPart->widget());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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());
|
||||
|
||||
qCDebug(KPARTSLOG) << this << "emitting activePartChanged" << d->m_activePart;
|
||||
|
||||
Q_EMIT activePartChanged(d->m_activePart);
|
||||
}
|
||||
|
||||
Part *PartManager::activePart() const
|
||||
{
|
||||
return d->m_activePart;
|
||||
}
|
||||
|
||||
QWidget *PartManager::activeWidget() const
|
||||
{
|
||||
return d->m_activeWidget;
|
||||
}
|
||||
|
||||
void PartManager::slotObjectDestroyed()
|
||||
{
|
||||
// qDebug();
|
||||
removePart(const_cast<Part *>(static_cast<const Part *>(sender())));
|
||||
}
|
||||
|
||||
void PartManager::slotWidgetDestroyed()
|
||||
{
|
||||
// qDebug();
|
||||
if (static_cast<const QWidget *>(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<Part *> PartManager::parts() const
|
||||
{
|
||||
return d->m_parts;
|
||||
}
|
||||
|
||||
void PartManager::addManagedTopLevelWidget(const QWidget *topLevel)
|
||||
{
|
||||
if (!topLevel->isWindow()) {
|
||||
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<const QWidget *>(sender());
|
||||
removeManagedTopLevelWidget(widget);
|
||||
}
|
||||
|
||||
int PartManager::reason() const
|
||||
{
|
||||
return d->m_reason;
|
||||
}
|
||||
|
||||
void PartManager::setIgnoreExplictFocusRequests(bool ignore)
|
||||
{
|
||||
d->m_bIgnoreExplicitFocusRequest = ignore;
|
||||
}
|
||||
|
||||
#include "moc_partmanager.cpp"
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kpartmanager_h__
|
||||
#define __kpartmanager_h__
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class Part;
|
||||
|
||||
class PartManagerPrivate;
|
||||
|
||||
/**
|
||||
* @class PartManager partmanager.h <KParts/PartManager>
|
||||
*
|
||||
* @short The part manager is an object which knows about a collection of parts
|
||||
* (even nested ones) and handles activation/deactivation.
|
||||
*
|
||||
* Applications that want to embed parts without merging GUIs
|
||||
* only use a KParts::PartManager. Those who want to merge GUIs use a
|
||||
* KParts::MainWindow for example, in addition to a part manager.
|
||||
*
|
||||
* Parts know about the part manager to add nested parts to it.
|
||||
* See also KParts::Part::manager() and KParts::Part::setManager().
|
||||
*/
|
||||
class KPARTS_EXPORT PartManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(SelectionPolicy selectionPolicy READ selectionPolicy WRITE setSelectionPolicy)
|
||||
Q_PROPERTY(bool allowNestedParts READ allowNestedParts WRITE setAllowNestedParts)
|
||||
Q_PROPERTY(bool ignoreScrollBars READ ignoreScrollBars WRITE setIgnoreScrollBars)
|
||||
public:
|
||||
/// Selection policy. The default policy of a PartManager is Direct.
|
||||
enum SelectionPolicy { Direct, TriState };
|
||||
Q_ENUM(SelectionPolicy)
|
||||
|
||||
/**
|
||||
* This extends QFocusEvent::Reason with the non-focus-event reasons for partmanager to activate a part.
|
||||
* To test for "any focusin reason", use < ReasonLeftClick
|
||||
* NoReason usually means: explicit activation with @ref setActivePart.
|
||||
*/
|
||||
enum Reason { ReasonLeftClick = 100, ReasonMidClick, ReasonRightClick, NoReason };
|
||||
|
||||
/**
|
||||
* Constructs a part manager.
|
||||
*
|
||||
* @param parent The toplevel widget (window / dialog) the
|
||||
* partmanager should monitor for activation/selection
|
||||
* events
|
||||
*/
|
||||
PartManager(QWidget *parent);
|
||||
/**
|
||||
* Constructs a part manager.
|
||||
*
|
||||
* @param topLevel The toplevel widget (window / dialog ) the
|
||||
* partmanager should monitor for activation/selection
|
||||
* events
|
||||
* @param parent The parent QObject.
|
||||
*/
|
||||
PartManager(QWidget *topLevel, QObject *parent);
|
||||
~PartManager() override;
|
||||
|
||||
/**
|
||||
* Sets the selection policy of the partmanager.
|
||||
*/
|
||||
void setSelectionPolicy(SelectionPolicy policy);
|
||||
/**
|
||||
* Returns the current selection policy.
|
||||
*/
|
||||
SelectionPolicy selectionPolicy() const;
|
||||
|
||||
/**
|
||||
* Specifies whether the partmanager should handle/allow nested parts
|
||||
* or not.
|
||||
*
|
||||
* This is a property the shell has to set/specify. Per
|
||||
* default we assume that the shell cannot handle nested
|
||||
* parts. However in case of a KOffice shell for example we allow
|
||||
* nested parts. A Part is nested (a child part) if its parent
|
||||
* object inherits KParts::Part. If a child part is activated and
|
||||
* nested parts are not allowed/handled, then the top parent part in
|
||||
* the tree is activated.
|
||||
*/
|
||||
void setAllowNestedParts(bool allow);
|
||||
/**
|
||||
* @see setAllowNestedParts
|
||||
*/
|
||||
bool allowNestedParts() const;
|
||||
|
||||
/**
|
||||
* Specifies whether the partmanager should ignore mouse click events for
|
||||
* scrollbars or not. If the partmanager ignores them, then clicking on the
|
||||
* scrollbars of a non-active/non-selected part will not change the selection
|
||||
* or activation state.
|
||||
*
|
||||
* The default value is false (read: scrollbars are NOT ignored).
|
||||
*/
|
||||
void setIgnoreScrollBars(bool ignore);
|
||||
/**
|
||||
* @see setIgnoreScrollBars
|
||||
*/
|
||||
bool ignoreScrollBars() const;
|
||||
|
||||
/**
|
||||
* Specifies which mouse buttons the partmanager should react upon.
|
||||
* By default it reacts on all mouse buttons (LMB/MMB/RMB).
|
||||
* @param buttonMask a combination of Qt::ButtonState values e.g. Qt::LeftButton | Qt::MiddleButton
|
||||
*/
|
||||
void setActivationButtonMask(short int buttonMask);
|
||||
/**
|
||||
* @see setActivationButtonMask
|
||||
*/
|
||||
short int activationButtonMask() const;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
bool eventFilter(QObject *obj, QEvent *ev) override;
|
||||
|
||||
/**
|
||||
* Adds a part to the manager.
|
||||
*
|
||||
* Sets it to the active part automatically if @p setActive is true (default).
|
||||
*/
|
||||
virtual void addPart(Part *part, bool setActive = true);
|
||||
|
||||
/**
|
||||
* Removes a part from the manager (this does not delete the object) .
|
||||
*
|
||||
* Sets the active part to 0 if @p part is the activePart() .
|
||||
*/
|
||||
virtual void removePart(Part *part);
|
||||
|
||||
/**
|
||||
* Replaces @p oldPart with @p newPart, and sets @p newPart as active if
|
||||
* @p setActive is true.
|
||||
* This is an optimised version of removePart + addPart
|
||||
*/
|
||||
virtual void replacePart(Part *oldPart, Part *newPart, bool setActive = true);
|
||||
|
||||
/**
|
||||
* Sets the active part.
|
||||
*
|
||||
* The active part receives activation events.
|
||||
*
|
||||
* @p widget can be used to specify which widget was responsible for the activation.
|
||||
* This is important if you have multiple views for a document/part , like in KOffice .
|
||||
*/
|
||||
virtual void setActivePart(Part *part, QWidget *widget = nullptr);
|
||||
|
||||
/**
|
||||
* Returns the active part.
|
||||
**/
|
||||
virtual Part *activePart() const;
|
||||
|
||||
/**
|
||||
* Returns the active widget of the current active part (see activePart ).
|
||||
*/
|
||||
virtual QWidget *activeWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the list of parts being managed by the partmanager.
|
||||
*/
|
||||
const QList<Part *> parts() const;
|
||||
|
||||
/**
|
||||
* Adds the @p topLevel widget to the list of managed toplevel widgets.
|
||||
* Usually a PartManager only listens for events (for activation/selection)
|
||||
* for one toplevel widget (and its children) , the one specified in the
|
||||
* constructor. Sometimes however (like for example when using the KDE dockwidget
|
||||
* library) , it is necessary to extend this.
|
||||
*/
|
||||
void addManagedTopLevelWidget(const QWidget *topLevel);
|
||||
/**
|
||||
* Removes the @p topLevel widget from the list of managed toplevel widgets.
|
||||
* @see addManagedTopLevelWidget
|
||||
*/
|
||||
void removeManagedTopLevelWidget(const QWidget *topLevel);
|
||||
|
||||
/**
|
||||
* @return the reason for the last activePartChanged signal emitted.
|
||||
* @see Reason
|
||||
*/
|
||||
int reason() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when a new part has been added.
|
||||
* @see addPart()
|
||||
**/
|
||||
void partAdded(KParts::Part *part);
|
||||
/**
|
||||
* Emitted when a part has been removed.
|
||||
* @see removePart()
|
||||
**/
|
||||
void partRemoved(KParts::Part *part);
|
||||
/**
|
||||
* Emitted when the active part has changed.
|
||||
* @see setActivePart()
|
||||
**/
|
||||
void activePartChanged(KParts::Part *newPart);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets whether the PartManager ignores explicit set focus requests
|
||||
* from the part.
|
||||
*
|
||||
* By default this option is set to false. Set it to true to prevent
|
||||
* the part from sending explicit set focus requests to the client
|
||||
* application.
|
||||
*
|
||||
* @since 4.10
|
||||
*/
|
||||
void setIgnoreExplictFocusRequests(bool);
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* Removes a part when it is destroyed.
|
||||
**/
|
||||
void slotObjectDestroyed();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void slotWidgetDestroyed();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void slotManagedTopLevelWidgetDestroyed();
|
||||
|
||||
private:
|
||||
KPARTS_NO_EXPORT Part *findPartFromWidget(QWidget *widget, const QPoint &pos);
|
||||
KPARTS_NO_EXPORT Part *findPartFromWidget(QWidget *widget);
|
||||
|
||||
private:
|
||||
std::unique_ptr<PartManagerPrivate> const d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "readonlypart.h"
|
||||
#include "readonlypart_p.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
|
||||
#include "guiactivateevent.h"
|
||||
#include "navigationextension.h"
|
||||
|
||||
#include <KIO/FileCopyJob>
|
||||
#include <KIO/StatJob>
|
||||
#include <KJobWidgets>
|
||||
#include <KProtocolInfo>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QMimeDatabase>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
ReadOnlyPart::ReadOnlyPart(QObject *parent, const KPluginMetaData &data)
|
||||
: Part(*new ReadOnlyPartPrivate(this, data), parent)
|
||||
{
|
||||
}
|
||||
|
||||
ReadOnlyPart::ReadOnlyPart(ReadOnlyPartPrivate &dd, QObject *parent)
|
||||
: Part(dd, parent)
|
||||
{
|
||||
}
|
||||
|
||||
ReadOnlyPart::~ReadOnlyPart()
|
||||
{
|
||||
Q_D(ReadOnlyPart);
|
||||
d->m_closeUrlFromDestructor = true;
|
||||
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;
|
||||
if (!d->m_closeUrlFromDestructor) {
|
||||
Q_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;
|
||||
}
|
||||
|
||||
void ReadOnlyPart::setProgressInfoEnabled(bool show)
|
||||
{
|
||||
Q_D(ReadOnlyPart);
|
||||
|
||||
d->m_showProgressInfo = show;
|
||||
}
|
||||
|
||||
bool ReadOnlyPart::isProgressInfoEnabled() const
|
||||
{
|
||||
Q_D(const ReadOnlyPart);
|
||||
|
||||
return d->m_showProgressInfo;
|
||||
}
|
||||
|
||||
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, &KJob::result, this, [d](KJob *job) {
|
||||
d->slotStatJobFinished(job);
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
d->openRemoteFile();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadOnlyPart::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);
|
||||
Q_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) {
|
||||
Q_EMIT q->setWindowCaption(m_url.toDisplayString(QUrl::PreferLocalFile));
|
||||
Q_EMIT q->completed();
|
||||
} else {
|
||||
Q_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('/') + m_metaData.pluginId() + 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);
|
||||
m_job->setFinishedNotificationHidden(true);
|
||||
KJobWidgets::setWindow(m_job, q->widget());
|
||||
Q_EMIT q->started(m_job);
|
||||
|
||||
QObject::connect(m_job, &KJob::result, q, [this](KJob *job) {
|
||||
slotJobFinished(job);
|
||||
});
|
||||
QObject::connect(m_job, &KIO::FileCopyJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &mimeType) {
|
||||
slotGotMimeType(job, mimeType);
|
||||
});
|
||||
}
|
||||
|
||||
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::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<KIO::StatJob *>(job)->mostLocalUrl();
|
||||
if (localUrl.isLocalFile()) {
|
||||
m_file = localUrl.toLocalFile();
|
||||
(void)openLocalFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
openRemoteFile();
|
||||
}
|
||||
|
||||
void ReadOnlyPartPrivate::slotJobFinished(KJob *job)
|
||||
{
|
||||
Q_Q(ReadOnlyPart);
|
||||
|
||||
Q_ASSERT(job == m_job);
|
||||
m_job = nullptr;
|
||||
if (job->error()) {
|
||||
Q_EMIT q->canceled(job->errorString());
|
||||
} else {
|
||||
if (q->openFile()) {
|
||||
Q_EMIT q->setWindowCaption(m_url.toDisplayString(QUrl::PreferLocalFile));
|
||||
Q_EMIT q->completed();
|
||||
} else {
|
||||
Q_EMIT q->canceled(QString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOnlyPartPrivate::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;
|
||||
Q_EMIT setWindowCaption(d->m_url.toDisplayString(QUrl::PreferLocalFile));
|
||||
} else {
|
||||
Q_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();
|
||||
}
|
||||
|
||||
NavigationExtension *ReadOnlyPart::navigationExtension() const
|
||||
{
|
||||
return findChild<KParts::NavigationExtension *>();
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_READONLYPART_H
|
||||
#define _KPARTS_READONLYPART_H
|
||||
|
||||
#include <kparts/part.h>
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
class KJob;
|
||||
namespace KIO
|
||||
{
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPartPrivate;
|
||||
class NavigationExtension;
|
||||
class OpenUrlArguments;
|
||||
|
||||
/**
|
||||
* @class ReadOnlyPart readonlypart.h <KParts/ReadOnlyPart>
|
||||
*
|
||||
* @short Base class for any "viewer" part.
|
||||
*
|
||||
* This class takes care of network transparency for you,
|
||||
* in the simplest way (downloading to a temporary file, then letting the part
|
||||
* load from the temporary file).
|
||||
* To use the built-in network transparency, you only need to implement
|
||||
* openFile(), not openUrl().
|
||||
*
|
||||
* To implement network transparency differently (e.g. for progressive loading,
|
||||
* like a web browser does for instance), or to prevent network transparency
|
||||
* (but why would you do that?), you can override openUrl().
|
||||
*
|
||||
* An application using KParts can use the signals to show feedback while
|
||||
* the URL is being loaded.
|
||||
*
|
||||
* ReadOnlyPart handles the window caption by setting it to the current URL
|
||||
* (set in openUrl(), and each time the part is activated).
|
||||
* If you want another caption, set it in openFile() and,
|
||||
* if the part might ever be used with a part manager, in guiActivateEvent().
|
||||
*/
|
||||
class KPARTS_EXPORT ReadOnlyPart : public Part
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QUrl url READ url)
|
||||
|
||||
KPARTS_DECLARE_PRIVATE(ReadOnlyPart)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* See also Part for the setXXX methods to call.
|
||||
*/
|
||||
explicit ReadOnlyPart(QObject *parent = nullptr, const KPluginMetaData &data = {});
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~ReadOnlyPart() override;
|
||||
|
||||
/**
|
||||
* Call this to turn off the progress info dialog used by
|
||||
* the internal KIO job. Use this if you provide another way
|
||||
* of displaying progress info (e.g. a statusbar), using the
|
||||
* signals emitted by this class, and/or those emitted by
|
||||
* the job given by started().
|
||||
*/
|
||||
void setProgressInfoEnabled(bool show);
|
||||
|
||||
/**
|
||||
* Returns whether the part shows the progress info dialog used by the internal
|
||||
* KIO job.
|
||||
*/
|
||||
bool isProgressInfoEnabled() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Only reimplement this if you don't want the network transparency support
|
||||
* to download from the URL into a temporary file (when the URL isn't local).
|
||||
* Otherwise, reimplement openFile() only.
|
||||
*
|
||||
* If you reimplement it, don't forget to set the caption, usually with
|
||||
* @code
|
||||
* Q_EMIT setWindowCaption( url.toDisplayString(QUrl::PreferLocalFile) );
|
||||
* @endcode
|
||||
* and also, if the URL refers to a local file, resolve it to a
|
||||
* local path and call setLocalFilePath().
|
||||
*/
|
||||
virtual bool openUrl(const QUrl &url);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns the URL currently opened in (or being opened by) this part.
|
||||
* @note The URL is not cleared if openUrl() fails to load the URL.
|
||||
* Call closeUrl() if you need to explicitly reset it.
|
||||
*
|
||||
* @return The current URL.
|
||||
*/
|
||||
QUrl url() const;
|
||||
|
||||
/**
|
||||
* Called when closing the current URL (for example, a document), for instance
|
||||
* when switching to another URL (note that openUrl() calls it
|
||||
* automatically in this case).
|
||||
* If the current URL is not fully loaded yet, aborts loading.
|
||||
* Deletes the temporary file used when the URL is remote.
|
||||
* Resets the current url() to QUrl().
|
||||
* @return always true, but the return value exists for reimplementations
|
||||
*/
|
||||
virtual bool closeUrl();
|
||||
|
||||
/**
|
||||
* This convenience method returns the NavigationExtension for this part,
|
||||
* or @c nullptr if there isn't one.
|
||||
*/
|
||||
NavigationExtension *navigationExtension() const;
|
||||
|
||||
/**
|
||||
* Sets the arguments to use for the next openUrl() call.
|
||||
*/
|
||||
void setArguments(const OpenUrlArguments &arguments);
|
||||
// TODO to avoid problems with the case where the loading fails, this could also be a openUrl() argument (heavy porting!).
|
||||
// However we need to have setArguments in any case for updated made by the part, see e.g. KHTMLPart::openUrl.
|
||||
// Well, maybe we should have setArguments (affects next openurl call) and updateArguments?
|
||||
|
||||
/**
|
||||
* @return the arguments that were used to open this URL.
|
||||
*/
|
||||
OpenUrlArguments arguments() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initiate sending data to this part.
|
||||
* This is an alternative to openUrl(), which allows the user of the part
|
||||
* to load the data itself, and send it progressively to the part.
|
||||
*
|
||||
* @param mimeType the type of data that is going to be sent to this part.
|
||||
* @param url the URL representing this data. Although not directly used,
|
||||
* every ReadOnlyPart has a URL (see url()), so this simply sets it.
|
||||
* @return true if the part supports progressive loading and accepts data, false otherwise.
|
||||
*/
|
||||
bool openStream(const QString &mimeType, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Send some data to the part. openStream must have been called previously,
|
||||
* and must have returned true.
|
||||
* @return true if the data was accepted by the part. If false is returned,
|
||||
* the application should stop sending data, and doesn't have to call closeStream.
|
||||
*/
|
||||
bool writeStream(const QByteArray &data);
|
||||
|
||||
/**
|
||||
* Terminate the sending of data to the part.
|
||||
* With some data types (text, html...) closeStream might never actually be called,
|
||||
* in the case of continuous streams, for instance plain text or HTML data.
|
||||
*/
|
||||
bool closeStream();
|
||||
|
||||
#ifdef K_DOXYGEN
|
||||
protected: // are parsed by doxygen (kapidox/ecm_add_qch): unhide for doxygen configured to skip private methods
|
||||
#else
|
||||
private: // Makes no sense for inherited classes to call those. But make it protected there.
|
||||
#endif // K_DOXYGEN
|
||||
|
||||
/**
|
||||
* Called by openStream to initiate sending of data.
|
||||
* Parts which implement progress loading should check the @p mimeType
|
||||
* parameter, and return true if they can accept a data stream of that type.
|
||||
*/
|
||||
virtual bool doOpenStream(const QString &mimeType)
|
||||
{
|
||||
Q_UNUSED(mimeType);
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Receive some data from the hosting application.
|
||||
* In this method the part should attempt to display the data progressively.
|
||||
* With some data types (text, html...) closeStream might never actually be called,
|
||||
* in the case of continuous streams. This can't happen with e.g. images.
|
||||
*/
|
||||
virtual bool doWriteStream(const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* This is called by closeStream(), to indicate that all the data has been sent.
|
||||
* Parts should ensure that all of the data is displayed at this point.
|
||||
* @return whether the data could be displayed correctly.
|
||||
*/
|
||||
virtual bool doCloseStream()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* The part emits this when starting to load data.
|
||||
* If using a KIO::Job, it provides the @p job so that
|
||||
* progress information can be shown. Otherwise, @p job is @c nullptr.
|
||||
**/
|
||||
void started(KIO::Job *job);
|
||||
|
||||
/**
|
||||
* Emit this when you have completed loading data.
|
||||
* Hosting applications will want to know when the process of loading the data
|
||||
* is finished, so that they can access the data when everything is loaded.
|
||||
**/
|
||||
void completed();
|
||||
|
||||
/**
|
||||
* This signal is similar to the @c KParts::ReadOnlyPart::completed() signal
|
||||
* except it is only emitted if there is still a pending action to be executed
|
||||
* on a delayed timer.
|
||||
*
|
||||
* An example of this is the meta-refresh tags on web pages used to reload/redirect
|
||||
* after a certain period of time. This signal is useful if you want to give the
|
||||
* user the ability to cancel such pending actions.
|
||||
*
|
||||
* @since 5.81
|
||||
*/
|
||||
void completedWithPendingAction();
|
||||
|
||||
/**
|
||||
* Emit this if loading is canceled by the user or by an error.
|
||||
* @param errMsg the error message, empty if the user canceled the loading voluntarily.
|
||||
*/
|
||||
void canceled(const QString &errMsg);
|
||||
|
||||
/**
|
||||
* Emitted by the part when url() changes
|
||||
* @since 4.10
|
||||
*/
|
||||
void urlChanged(const QUrl &url);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* If the part uses the standard implementation of openUrl(),
|
||||
* it must reimplement this to open the local file.
|
||||
* The default implementation simply returns false.
|
||||
*
|
||||
* If this method returns true the part emits completed(),
|
||||
* otherwise it emits canceled().
|
||||
*
|
||||
* @see completed(), canceled()
|
||||
*/
|
||||
virtual bool openFile();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void abortLoad();
|
||||
|
||||
/**
|
||||
* Reimplemented from Part, so that the window caption is set to
|
||||
* the current URL (decoded) when the part is activated.
|
||||
* This is the usual behavior in 99% of applications.
|
||||
* Reimplement if you don't like it - test for event->activated()!
|
||||
*
|
||||
* @note This is done with GUIActivateEvent and not with
|
||||
* PartActivateEvent because it's handled by the main window
|
||||
* (which gets the event after the PartActivateEvent events have been sent).
|
||||
*/
|
||||
void guiActivateEvent(GUIActivateEvent *event) override;
|
||||
|
||||
/**
|
||||
* Sets the URL associated with this part.
|
||||
*/
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns the local file path associated with this part.
|
||||
*
|
||||
* @note The result will only be valid if openUrl() or
|
||||
* setLocalFilePath() has previously been called.
|
||||
*/
|
||||
QString localFilePath() const;
|
||||
|
||||
/**
|
||||
* Sets the local file path associated with this part.
|
||||
*/
|
||||
void setLocalFilePath(const QString &localFilePath);
|
||||
|
||||
protected:
|
||||
KPARTS_NO_EXPORT ReadOnlyPart(ReadOnlyPartPrivate &dd, QObject *parent);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ReadOnlyPart)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_READONLYPART_P_H
|
||||
#define _KPARTS_READONLYPART_P_H
|
||||
|
||||
#include "openurlarguments.h"
|
||||
#include "part_p.h"
|
||||
#include "readonlypart.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class FileCopyJob;
|
||||
class StatJob;
|
||||
}
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadOnlyPartPrivate : public PartPrivate
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(ReadOnlyPart)
|
||||
|
||||
explicit ReadOnlyPartPrivate(ReadOnlyPart *qq, const KPluginMetaData &data)
|
||||
: PartPrivate(qq, data)
|
||||
{
|
||||
m_job = nullptr;
|
||||
m_statJob = nullptr;
|
||||
m_uploadJob = nullptr;
|
||||
m_showProgressInfo = true;
|
||||
m_saveOk = false;
|
||||
m_waitForSave = false;
|
||||
m_duringSaveAs = false;
|
||||
m_bTemp = false;
|
||||
m_bAutoDetectedMime = false;
|
||||
m_closeUrlFromOpenUrl = false;
|
||||
m_closeUrlFromDestructor = false;
|
||||
}
|
||||
|
||||
~ReadOnlyPartPrivate() override
|
||||
{
|
||||
}
|
||||
|
||||
void slotJobFinished(KJob *job);
|
||||
void slotStatJobFinished(KJob *job);
|
||||
void slotGotMimeType(KIO::Job *job, const QString &mime);
|
||||
bool openLocalFile();
|
||||
void openRemoteFile();
|
||||
|
||||
KIO::FileCopyJob *m_job;
|
||||
KIO::StatJob *m_statJob;
|
||||
KIO::FileCopyJob *m_uploadJob;
|
||||
QUrl m_originalURL; // for saveAs
|
||||
QString m_originalFilePath; // for saveAs
|
||||
bool m_showProgressInfo : 1;
|
||||
bool m_saveOk : 1;
|
||||
bool m_waitForSave : 1;
|
||||
bool m_duringSaveAs : 1;
|
||||
|
||||
/**
|
||||
* If @p true, @p m_file is a temporary file that needs to be deleted later.
|
||||
*/
|
||||
bool m_bTemp : 1;
|
||||
|
||||
// whether the mimetype in the arguments was detected by the part itself
|
||||
bool m_bAutoDetectedMime : 1;
|
||||
// Whether we are calling closeUrl() from openUrl().
|
||||
bool m_closeUrlFromOpenUrl;
|
||||
// Whether we are calling closeUrl() from ~ReadOnlyPart().
|
||||
bool m_closeUrlFromDestructor;
|
||||
|
||||
/**
|
||||
* Remote (or local) url - the one displayed to the user.
|
||||
*/
|
||||
QUrl m_url;
|
||||
|
||||
/**
|
||||
* Local file - the only one the part implementation should deal with.
|
||||
*/
|
||||
QString m_file;
|
||||
|
||||
OpenUrlArguments m_arguments;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "readwritepart.h"
|
||||
#include "readwritepart_p.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
|
||||
#define HAVE_KDIRNOTIFY __has_include(<KDirNotify>)
|
||||
#if HAVE_KDIRNOTIFY
|
||||
#include <KDirNotify>
|
||||
#endif
|
||||
|
||||
#include <KIO/FileCopyJob>
|
||||
#include <KJobWidgets>
|
||||
#include <KLocalizedString>
|
||||
#include <KMessageBox>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <qt_windows.h> //CreateHardLink()
|
||||
#endif
|
||||
|
||||
using namespace KParts;
|
||||
|
||||
ReadWritePart::ReadWritePart(QObject *parent, const KPluginMetaData &data)
|
||||
: ReadOnlyPart(*new ReadWritePartPrivate(this, data), 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) {
|
||||
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::warningTwoActionsCancel(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::PrimaryAction:
|
||||
Q_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::SecondaryAction:
|
||||
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 {
|
||||
Q_EMIT canceled(QString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadWritePart::saveAs(const QUrl &url)
|
||||
{
|
||||
Q_D(ReadWritePart);
|
||||
|
||||
if (!url.isValid()) {
|
||||
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) {
|
||||
Q_EMIT urlChanged(d->m_url);
|
||||
}
|
||||
|
||||
Q_EMIT setWindowCaption(d->m_url.toDisplayString(QUrl::PreferLocalFile));
|
||||
} 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);
|
||||
Q_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, &KJob::result, this, [d](KJob *job) {
|
||||
d->slotUploadFinished(job);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWritePartPrivate::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;
|
||||
}
|
||||
Q_EMIT q->canceled(error);
|
||||
} else {
|
||||
#if HAVE_KDIRNOTIFY
|
||||
::org::kde::KDirNotify::emitFilesAdded(m_url.adjusted(QUrl::RemoveFilename));
|
||||
#endif
|
||||
|
||||
m_uploadJob = nullptr;
|
||||
q->setModified(false);
|
||||
Q_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"
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_READWRITEPART_H
|
||||
#define _KPARTS_READWRITEPART_H
|
||||
|
||||
#include <kparts/readonlypart.h>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadWritePartPrivate;
|
||||
|
||||
/**
|
||||
* @class ReadWritePart readwritepart.h <KParts/ReadWritePart>
|
||||
*
|
||||
* @short Base class for an "editor" part.
|
||||
*
|
||||
* This class handles network transparency for you.
|
||||
* Anything that can open a URL, allow modifications, and save
|
||||
* (to the same URL or a different one).
|
||||
*
|
||||
* A read-write part can be set to read-only mode, using setReadWrite().
|
||||
*
|
||||
* Part writers :
|
||||
* Any part inheriting ReadWritePart should check isReadWrite
|
||||
* before allowing any action that modifies the part.
|
||||
* The part probably wants to reimplement setReadWrite, disable those
|
||||
* actions. Don't forget to call the parent setReadWrite.
|
||||
*/
|
||||
class KPARTS_EXPORT ReadWritePart : public ReadOnlyPart
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
KPARTS_DECLARE_PRIVATE(ReadWritePart)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* See parent constructor for instructions.
|
||||
*/
|
||||
explicit ReadWritePart(QObject *parent = nullptr, const KPluginMetaData &data = {});
|
||||
/**
|
||||
* Destructor
|
||||
* Applications using a ReadWritePart should make sure, before
|
||||
* destroying it, to call closeUrl().
|
||||
* In KMainWindow::queryClose(), for instance, they should allow
|
||||
* closing only if the return value of closeUrl() was true.
|
||||
* This allows to cancel.
|
||||
*/
|
||||
~ReadWritePart() override;
|
||||
|
||||
/**
|
||||
* @return true if the part is in read-write mode
|
||||
*/
|
||||
bool isReadWrite() const;
|
||||
|
||||
/**
|
||||
* Changes the behavior of this part to readonly or readwrite.
|
||||
* @param readwrite set to true to enable readwrite mode
|
||||
*/
|
||||
virtual void setReadWrite(bool readwrite = true);
|
||||
|
||||
/**
|
||||
* @return true if the document has been modified.
|
||||
*/
|
||||
bool isModified() const;
|
||||
|
||||
/**
|
||||
* If the document has been modified, ask the user to save changes.
|
||||
* This method is meant to be called from KMainWindow::queryClose().
|
||||
* It will also be called from closeUrl().
|
||||
*
|
||||
* @return true if closeUrl() can be called without the user losing
|
||||
* important data, false if the user chooses to cancel.
|
||||
*/
|
||||
virtual bool queryClose();
|
||||
|
||||
/**
|
||||
* Called when closing the current url (e.g. document), for instance
|
||||
* when switching to another url (note that openUrl() calls it
|
||||
* automatically in this case).
|
||||
*
|
||||
* If the current URL is not fully loaded yet, aborts loading.
|
||||
*
|
||||
* If isModified(), queryClose() will be called.
|
||||
*
|
||||
* @return false on cancel
|
||||
*/
|
||||
bool closeUrl() override;
|
||||
|
||||
/**
|
||||
* Call this method instead of the above if you need control if
|
||||
* the save prompt is shown. For example, if you call queryClose()
|
||||
* from KMainWindow::queryClose(), you would not want to prompt
|
||||
* again when closing the url.
|
||||
*
|
||||
* Equivalent to promptToSave ? closeUrl() : ReadOnlyPart::closeUrl()
|
||||
*/
|
||||
virtual bool closeUrl(bool promptToSave);
|
||||
|
||||
/**
|
||||
* Save the file to a new location.
|
||||
*
|
||||
* Calls save(), no need to reimplement
|
||||
*/
|
||||
virtual bool saveAs(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Sets the modified flag of the part.
|
||||
*/
|
||||
virtual void setModified(bool modified);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* set handled to true, if you don't want the default handling
|
||||
* set abortClosing to true, if you handled the request,
|
||||
* but for any reason don't want to allow closing the document
|
||||
*/
|
||||
void sigQueryClose(bool *handled, bool *abortClosing);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Call setModified() whenever the contents get modified.
|
||||
* This is a slot for convenience, since it simply calls setModified(true),
|
||||
* so that you can connect it to a signal, like textChanged().
|
||||
*/
|
||||
void setModified();
|
||||
|
||||
/**
|
||||
* Save the file in the location from which it was opened.
|
||||
* You can connect this to the "save" action.
|
||||
* Calls saveFile() and saveToUrl(), no need to reimplement.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/**
|
||||
* Waits for any pending upload job to finish and returns whether the
|
||||
* last save() action was successful.
|
||||
*/
|
||||
bool waitSaveComplete();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Save to a local file.
|
||||
* You need to implement it, to save to the local file.
|
||||
* The framework takes care of re-uploading afterwards.
|
||||
*
|
||||
* @return true on success, false on failure.
|
||||
* On failure the function should inform the user about the
|
||||
* problem with an appropriate message box. Standard error
|
||||
* messages can be constructed using KIO::buildErrorString()
|
||||
* in combination with the error codes defined in kio/global.h
|
||||
*/
|
||||
virtual bool saveFile() = 0;
|
||||
|
||||
/**
|
||||
* Save the file.
|
||||
*
|
||||
* Uploads the file, if @p url is remote.
|
||||
* This will emit started(), and either completed() or canceled(),
|
||||
* in case you want to provide feedback.
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
virtual bool saveToUrl();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ReadWritePart)
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
|
||||
SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _KPARTS_READWRITEPART_P_H
|
||||
#define _KPARTS_READWRITEPART_P_H
|
||||
|
||||
#include "readonlypart_p.h"
|
||||
#include "readwritepart.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class ReadWritePartPrivate : public ReadOnlyPartPrivate
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(ReadWritePart)
|
||||
|
||||
explicit ReadWritePartPrivate(ReadWritePart *qq, const KPluginMetaData &data)
|
||||
: ReadOnlyPartPrivate(qq, data)
|
||||
{
|
||||
m_bModified = false;
|
||||
m_bReadWrite = true;
|
||||
m_bClosing = false;
|
||||
}
|
||||
|
||||
void slotUploadFinished(KJob *job);
|
||||
|
||||
void prepareSaving();
|
||||
|
||||
bool m_bModified;
|
||||
bool m_bReadWrite;
|
||||
bool m_bClosing;
|
||||
QEventLoop m_eventLoop;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2003 Daniel Molkentin <molkentin@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "statusbarextension.h"
|
||||
|
||||
#include "kparts_logging.h"
|
||||
|
||||
#include "guiactivateevent.h"
|
||||
#include "readonlypart.h"
|
||||
|
||||
#include <KMainWindow>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QStatusBar>
|
||||
|
||||
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<QWidget> 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 *qq)
|
||||
: q(qq)
|
||||
, m_statusBar(nullptr)
|
||||
, m_activated(true)
|
||||
{
|
||||
}
|
||||
|
||||
StatusBarExtension *q;
|
||||
QList<StatusBarItem> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBarExtension *StatusBarExtension::childObject(QObject *obj)
|
||||
{
|
||||
return obj->findChild<KParts::StatusBarExtension *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
}
|
||||
|
||||
bool StatusBarExtension::eventFilter(QObject *watched, QEvent *ev)
|
||||
{
|
||||
if (!GUIActivateEvent::test(ev) || !::qobject_cast<KParts::Part *>(watched)) {
|
||||
return QObject::eventFilter(watched, ev);
|
||||
}
|
||||
|
||||
QStatusBar *sb = statusBar();
|
||||
if (!sb) {
|
||||
return QObject::eventFilter(watched, ev);
|
||||
}
|
||||
|
||||
GUIActivateEvent *gae = static_cast<GUIActivateEvent *>(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<KParts::Part *>(parent());
|
||||
QWidget *w = part ? part->widget() : nullptr;
|
||||
KMainWindow *mw = w ? qobject_cast<KMainWindow *>(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<StatusBarItem>::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;
|
||||
}
|
||||
}
|
||||
|
||||
qCWarning(KPARTSLOG) << "StatusBarExtension::removeStatusBarItem. Widget not found :" << widget;
|
||||
}
|
||||
|
||||
#include "moc_statusbarextension.cpp"
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2003 Daniel Molkentin <molkentin@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPARTS_STATUSBAREXTENSION_H
|
||||
#define KPARTS_STATUSBAREXTENSION_H
|
||||
|
||||
#include <kparts/kparts_export.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
class QStatusBar;
|
||||
class KMainWindow;
|
||||
class QEvent;
|
||||
|
||||
namespace KParts
|
||||
{
|
||||
class Part;
|
||||
class ReadOnlyPart;
|
||||
|
||||
// Defined in impl
|
||||
class StatusBarItem;
|
||||
class StatusBarExtensionPrivate;
|
||||
|
||||
/**
|
||||
* @class StatusBarExtension statusbarextension.h <KParts/StatusBarExtension>
|
||||
*
|
||||
* @short An extension for KParts that allows more sophisticated statusbar handling
|
||||
*
|
||||
* Every part can use this class to customize the statusbar as long as it is active.
|
||||
* Add items via addStatusBarItem and remove an item with removeStatusBarItem.
|
||||
*
|
||||
* IMPORTANT: do NOT add any items immediately after constructing the extension.
|
||||
* Give the application time to set the statusbar in the extension if necessary.
|
||||
*/
|
||||
class KPARTS_EXPORT StatusBarExtension : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StatusBarExtension(KParts::Part *parent);
|
||||
explicit StatusBarExtension(KParts::ReadOnlyPart *parent); // KF6: REMOVE
|
||||
~StatusBarExtension() override;
|
||||
|
||||
/**
|
||||
* This adds a widget to the statusbar for this part.
|
||||
* If you use this method instead of using statusBar() directly,
|
||||
* this extension will take care of removing the items when the parts GUI
|
||||
* is deactivated and will re-add them when it is reactivated.
|
||||
* The parameters are the same as QStatusBar::addWidget().
|
||||
*
|
||||
* Note that you can't use KStatusBar methods (inserting text items by id)
|
||||
* but you can create a KStatusBarLabel with a dummy id instead, and use
|
||||
* it directly in order to get the same look and feel.
|
||||
*
|
||||
* @param widget the widget to add
|
||||
* @param stretch the stretch factor. 0 for a minimum size.
|
||||
* @param permanent passed to QStatusBar::addWidget as the "permanent" bool.
|
||||
* Note that the item isn't really permanent though, it goes away when
|
||||
* the part is unactivated. This simply controls whether temporary messages
|
||||
* hide the @p widget, and whether it's added to the left or to the right side.
|
||||
*
|
||||
* @Note that the widget does not technically become a child of the
|
||||
* StatusBarExtension in a QObject sense. However, it @em will be deleted
|
||||
* when the StatusBarExtension is deleted.
|
||||
*
|
||||
* IMPORTANT: do NOT add any items immediately after constructing the extension.
|
||||
* Give the application time to set the statusbar in the extension if necessary.
|
||||
*/
|
||||
void addStatusBarItem(QWidget *widget, int stretch, bool permanent);
|
||||
|
||||
/**
|
||||
* Remove a widget from the statusbar for this part.
|
||||
*/
|
||||
void removeStatusBarItem(QWidget *widget);
|
||||
|
||||
/**
|
||||
* @return the statusbar of the KMainWindow in which this part is currently embedded.
|
||||
* WARNING: this could return 0L
|
||||
*/
|
||||
QStatusBar *statusBar() const;
|
||||
|
||||
/**
|
||||
* This allows the hosting application to set a particular QStatusBar
|
||||
* for this part. If it doesn't do this, the statusbar used will be
|
||||
* the one of the KMainWindow in which the part is embedded.
|
||||
* Konqueror uses this to assign a view-statusbar to the part.
|
||||
* The part should never call this method!
|
||||
*/
|
||||
void setStatusBar(QStatusBar *status);
|
||||
|
||||
/**
|
||||
* Queries @p obj for a child object which inherits from this
|
||||
* StatusBarExtension class. Convenience method.
|
||||
*/
|
||||
static StatusBarExtension *childObject(QObject *obj);
|
||||
|
||||
/** @internal */
|
||||
bool eventFilter(QObject *watched, QEvent *ev) override;
|
||||
|
||||
private:
|
||||
// for future extensions
|
||||
friend class StatusBarExtensionPrivate;
|
||||
std::unique_ptr<StatusBarExtensionPrivate> const d;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // KPARTS_STATUSBAREXTENSION_H
|
||||
Reference in New Issue
Block a user