feat: add missing KF6 framework recipes

This commit is contained in:
2026-05-07 07:53:26 +01:00
parent d8d498f831
commit a69f479b52
2374 changed files with 2610246 additions and 0 deletions
@@ -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
View File
@@ -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