Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
// SPDX-FileCopyrightText: 2022-2023 Harald Sitter <sitter@kde.org>
#include "kcountryflagemojiiconengine.h"
#include <QDebug>
#include <QFont>
#include <QGuiApplication>
#include <QPainter>
#include <QPalette>
using namespace Qt::Literals::StringLiterals;
namespace
{
Q_GLOBAL_STATIC(QFont, s_globalDefaultFont, "emoji"_L1)
QString makeCountryEmoji(const QString &country)
{
// The way this was set up by unicode is actually pretty smart. Country flags are based on their two character
// country codes within a given range of code points. And even better, the offset inside the range is the same
// as the offset inside ASCII. Meaning the offset of 'A' from 0 is the same as the offset of 🇦 in the
// flag codepoint range. The way a flag is then denoted is e.g. <SURROGATEPAIR>🇦<SURROGATEPAIR>🇹 resulting in
// the Austrian flag.
// https://en.wikipedia.org/wiki/Regional_indicator_symbol
static constexpr auto surrogatePairCodePoint = 0xD83C; // U+D83C
static constexpr auto flagCodePointStart = 0xDDE6; // U+1F1E6 (🇦) - NB: we are in UTF-16
static constexpr auto offsetCodePointA = 'A'_L1.unicode(); // offset from 0, the flag code points have the same offsets
static constexpr auto basePoint = flagCodePointStart - offsetCodePointA;
QStringList emojiList;
emojiList.reserve(country.size());
for (const auto &c : country) {
emojiList.append(QChar(surrogatePairCodePoint) + QChar(basePoint + c.toUpper().unicode()));
}
// Valid flag country codes have only 2 characters.
// If we have more, separate the flag codepoints to avoid misrepresentations
if (country.size() != 2) {
return emojiList.join(QChar(0x200b)); // U+200B Zero-Width Space
}
return emojiList.join(QString());
}
QString makeRegionEmoji(const QString &region)
{
// Region flags work much the same as country flags but with a slightly different format in a slightly different
// code point region. Specifically they use ISO 3166-2 as input (e.g. GB-SCT for Scotland). It all happens in
// the Unicode Block “Tags” (starting at U+E0000) wherein it functions the same as the country codes do in their
// block. The offsets inside the block are the same as the ascii offsets and the emoji is constructed by combining
// the off set code points of the incoming region tag. They are prefixed with U+1F3F4 🏴 WAVING BLACK FLAG
// and suffixed with U+E007F CANCEL TAG.
// https://en.wikipedia.org/wiki/Regional_indicator_symbol
auto hyphenlessRegion = region;
hyphenlessRegion.remove('-'_L1);
static constexpr auto surrogatePairCodePoint = 0xdb40; // U+DB40
static constexpr auto flagCodePointStart = 0xDC41; // U+E0041 (Tag Latin Capital Letter A) - NB: we are in UTF-16
static constexpr auto offsetCodePointA = 'A'_L1.unicode(); // offset from 0, the flag code points have the same offsets
static constexpr auto basePoint = flagCodePointStart - offsetCodePointA;
auto emoji = u"🏴"_s;
emoji.reserve(emoji.size() + 2 * hyphenlessRegion.size() + 2);
for (const auto &c : hyphenlessRegion) {
emoji.append(QChar(surrogatePairCodePoint));
emoji.append(QChar(basePoint + c.toLower().unicode()));
}
static const auto cancelTag = QString().append(QChar(surrogatePairCodePoint)).append(QChar(0xDC7F));
return emoji.append(cancelTag);
}
} // namespace
class Q_DECL_HIDDEN KCountryFlagEmojiIconEnginePrivate
{
public:
explicit KCountryFlagEmojiIconEnginePrivate(const QString &regionOrCountry)
: m_country(regionOrCountry)
, m_emoji(regionOrCountry.contains("-"_L1) ? makeRegionEmoji(regionOrCountry) : makeCountryEmoji(regionOrCountry))
{
}
const QString m_country;
const QString m_emoji;
};
KCountryFlagEmojiIconEngine::KCountryFlagEmojiIconEngine(const QString &country)
: d(std::make_unique<KCountryFlagEmojiIconEnginePrivate>(country))
{
}
KCountryFlagEmojiIconEngine::~KCountryFlagEmojiIconEngine() = default;
QIconEngine *KCountryFlagEmojiIconEngine::clone() const
{
return new KCountryFlagEmojiIconEngine(d->m_country);
}
QString KCountryFlagEmojiIconEngine::key() const
{
return u"org.kde.KCountryFlagEmojiIconEngine"_s;
}
void KCountryFlagEmojiIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
{
// Not supported
Q_UNUSED(mode);
Q_UNUSED(state);
QFont font(*s_globalDefaultFont, painter->device());
font.setPixelSize(qMax(rect.width(), rect.height()));
font.setFixedPitch(true);
QFontMetricsF metrics(font, painter->device());
QRectF tightRect = metrics.tightBoundingRect(d->m_emoji);
if (tightRect.width() > rect.width() || tightRect.height() > rect.height()) {
const auto ratio = std::max({1.0, tightRect.width() / rect.width(), tightRect.height() / rect.height()});
font.setPixelSize(std::max(1.0, std::floor(font.pixelSize() / ratio)));
metrics = QFontMetricsF(font, painter->device());
tightRect = metrics.tightBoundingRect(d->m_emoji);
}
painter->setPen(qGuiApp->palette().color(QPalette::WindowText)); // in case we render the letters in absence of a flag
QRectF flagBoundingRect = metrics.boundingRect(rect, Qt::AlignCenter, d->m_emoji);
// Confusingly the pixelSize for drawing must actually be without DPR but the rect calculation above
// seems to be correct even with DPR in the pixelSize.
const auto dpr = painter->device()->devicePixelRatioF();
font.setPixelSize(std::floor(font.pixelSize() / dpr));
// The offset of the bounding rect needs to be also adjusted by the DPR
flagBoundingRect.moveTopLeft(flagBoundingRect.topLeft() / dpr);
painter->setFont(font);
painter->drawText(flagBoundingRect, d->m_emoji);
}
QPixmap KCountryFlagEmojiIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
return scaledPixmap(size, mode, state, 1.0);
}
QPixmap KCountryFlagEmojiIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
{
QPixmap pixmap(size);
pixmap.setDevicePixelRatio(scale);
pixmap.fill(Qt::transparent);
{
QPainter p(&pixmap);
paint(&p, QRect(QPoint(0, 0), size), mode, state);
}
return pixmap;
}
bool KCountryFlagEmojiIconEngine::isNull()
{
return d->m_emoji.isEmpty();
}
void KCountryFlagEmojiIconEngine::setGlobalDefaultFont(const QFont &font)
{
QFont swapable(font);
s_globalDefaultFont->swap(swapable);
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
// SPDX-FileCopyrightText: 2022-2023 Harald Sitter <sitter@kde.org>
#ifndef KCOUNTRYFLAGEMOJIICONENGINE_H
#define KCOUNTRYFLAGEMOJIICONENGINE_H
#include <QIconEngine>
#include <kguiaddons_export.h>
class KCountryFlagEmojiIconEnginePrivate;
/**
* @brief Provides emoji flags as icons
* This is a special icon engine that internally paints flags using emoji fonts.
* It provides access to country and region flags from the system emoji font.
* ```
* auto l = new QLabel;
* l->setMinimumSize(512, 512);
* l->setPixmap(QIcon(new KCountryFlagEmojiIconEngine("AT")).pixmap(512, 512));
* ```
* @since 6.0
*/
class KGUIADDONS_EXPORT KCountryFlagEmojiIconEngine : public QIconEngine
{
public:
/**
* @brief Construct a new KCountryFlagEmojiIconEngine object
* Please note that regional flag support can be spotty in emoji fonts.
* @param regionOrCountry either a ISO 3166-1 alpha-2 country code or a ISO 3166-2 region code (e.g. AT for Austria or GB-SCT for Scotland)
*/
explicit KCountryFlagEmojiIconEngine(const QString &regionOrCountry);
~KCountryFlagEmojiIconEngine() override;
Q_DISABLE_COPY_MOVE(KCountryFlagEmojiIconEngine)
QIconEngine *clone() const override;
QString key() const override;
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
/**
* @brief Check whether the internal emoji unicode sequence is null
* This does not necessarily mean that the pixmap output will be a valid flag - that entirely depends on the system's precise font configuration.
* @return true when the construction of the emoji string failed
* @return false when the construction of the emoji string succeeded
*/
bool isNull() override;
/**
* @brief Set the Global Default Font object
* This is primarily useful for platform themes that wish to force a specific font being used. By default the "emoji" font family will be used.
* Forcing a specific font and making sure it is available as runtime requirement is the most reliable way to ensure that flag support is working
* regardless of system configuration.
* @param font the default font to use
*/
static void setGlobalDefaultFont(const QFont &font);
private:
std::unique_ptr<KCountryFlagEmojiIconEnginePrivate> const d;
};
#endif
@@ -0,0 +1,54 @@
/*
SPDX-License-Identifier: LGPL-2.0-or-later
SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
SPDX-FileCopyrightText: 2020 Laurent Montel <montel@kde.org>
*/
#include "kcursorsaver.h"
#include "kguiaddons_debug.h"
#include <QGuiApplication>
class KCursorSaverPrivate
{
public:
bool ownsCursor = true;
};
KCursorSaver::KCursorSaver(Qt::CursorShape shape)
: d(new KCursorSaverPrivate)
{
QGuiApplication::setOverrideCursor(QCursor(shape));
d->ownsCursor = true;
}
KCursorSaver::KCursorSaver(KCursorSaver &&other)
: d(other.d)
{
*this = std::move(other);
}
KCursorSaver::~KCursorSaver()
{
if (d->ownsCursor) {
QGuiApplication::restoreOverrideCursor();
delete d;
}
}
void KCursorSaver::restoreCursor()
{
if (!d->ownsCursor) {
qCWarning(KGUIADDONS_LOG) << "This KCursorSaver doesn't own the cursor anymore, invalid call to restoreCursor().";
return;
}
d->ownsCursor = false;
QGuiApplication::restoreOverrideCursor();
}
KCursorSaver &KCursorSaver::operator=(KCursorSaver &&other)
{
if (this != &other) {
d->ownsCursor = false;
}
return *this;
}
@@ -0,0 +1,52 @@
/*
SPDX-License-Identifier: LGPL-2.0-or-later
SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
SPDX-FileCopyrightText: 2020 Laurent Montel <montel@kde.org>
*/
#ifndef KCURSORSAVER_H
#define KCURSORSAVER_H
#include <kguiaddons_export.h>
#include <QCursor>
class KCursorSaverPrivate;
/**
* @class KCursorSaver kcursorsaver.h KCursorSaver
*
* @short Class to temporarily set a mouse cursor and restore the previous one on destruction
*
* Create a KCursorSaver object when you want to set the cursor.
* As soon as it gets out of scope, it will restore the original
* cursor.
* @code
KCursorSaver saver(Qt::WaitCursor);
... long-running operation here ...
@endcode
* @since 5.73
*/
class KGUIADDONS_EXPORT KCursorSaver
{
public:
/// Creates a KCursorSaver, setting the mouse cursor to @p shape.
explicit KCursorSaver(Qt::CursorShape shape);
/// Move-constructs a KCursorSaver from other
KCursorSaver(KCursorSaver &&other);
/// restore the cursor
~KCursorSaver();
/// call this to explicitly restore the cursor
void restoreCursor();
KCursorSaver &operator=(KCursorSaver &&other);
private:
KCursorSaver(KCursorSaver &other) = delete;
void operator=(const KCursorSaver &rhs) = delete;
KCursorSaverPrivate *const d; ///< @internal
};
#endif
@@ -0,0 +1,226 @@
/*
SPDX-FileCopyrightText: 2013 Martin Klapetek <mklapetek@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include <array>
#include "kiconutils.h"
#include <QHash>
#include <QIconEngine>
#include <QLibraryInfo>
#include <QPainter>
#include <QVersionNumber>
class KOverlayIconEngine : public QIconEngine
{
public:
KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position);
KOverlayIconEngine(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays);
KOverlayIconEngine(const QIcon &icon, const QStringList &overlays);
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
QIconEngine *clone() const override;
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;
void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override;
void virtual_hook(int id, void *data) override;
private:
QIcon m_base;
QHash<Qt::Corner, QIcon> m_overlays;
};
KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position)
: QIconEngine()
, m_base(icon)
{
m_overlays.insert(position, overlay);
}
KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays)
: QIconEngine()
, m_base(icon)
, m_overlays(overlays)
{
}
KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QStringList &overlays)
: QIconEngine()
, m_base(icon)
{
const std::array<Qt::Corner, 4> indexToCorner{
Qt::BottomRightCorner,
Qt::BottomLeftCorner,
Qt::TopLeftCorner,
Qt::TopRightCorner,
};
// static_cast becaue size() returns a qsizetype in Qt6
const int count = std::min(4, static_cast<int>(overlays.size()));
m_overlays.reserve(count);
for (int i = 0; i < count; i++) {
m_overlays.insert(indexToCorner[i], QIcon::fromTheme(overlays.at(i)));
}
}
QIconEngine *KOverlayIconEngine::clone() const
{
return new KOverlayIconEngine(*this);
}
QSize KOverlayIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
return m_base.actualSize(size, mode, state);
}
QPixmap KOverlayIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
QPixmap pixmap(size);
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
paint(&p, pixmap.rect(), mode, state);
return pixmap;
}
void KOverlayIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
{
m_base.addPixmap(pixmap, mode, state);
}
void KOverlayIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
{
m_base.addFile(fileName, size, mode, state);
}
void KOverlayIconEngine::virtual_hook(int id, void *data)
{
if (id == QIconEngine::ScaledPixmapHook) {
auto *info = reinterpret_cast<ScaledPixmapArgument *>(data);
QSize phyiscalSize = info->size;
// Since https://codereview.qt-project.org/c/qt/qtbase/+/563553 size is in logical pixels
if (QLibraryInfo::version() >= QVersionNumber(6, 8, 0)) {
phyiscalSize *= info->scale;
}
QPixmap pixmap(phyiscalSize);
pixmap.setDevicePixelRatio(info->scale);
pixmap.fill(Qt::transparent);
QRect rect = pixmap.rect();
const QRect logicalRect(rect.x() / info->scale, rect.y() / info->scale, rect.width() / info->scale, rect.height() / info->scale);
QPainter p(&pixmap);
paint(&p, logicalRect, info->mode, info->state);
info->pixmap = pixmap;
return;
}
QIconEngine::virtual_hook(id, data);
}
void KOverlayIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
{
// Paint the base icon as the first layer
m_base.paint(painter, rect, Qt::AlignCenter, mode, state);
if (m_overlays.isEmpty()) {
return;
}
const int width = rect.width();
const int height = rect.height();
const int iconSize = qMin(width, height);
// Determine the overlay icon size
int overlaySize;
if (iconSize < 32) {
overlaySize = 8;
} else if (iconSize <= 48) {
overlaySize = 16;
} else if (iconSize <= 64) {
overlaySize = 22;
} else if (iconSize <= 96) {
overlaySize = 32;
} else if (iconSize <= 128) {
overlaySize = 48;
} else {
overlaySize = (int)(iconSize / 4);
}
// Iterate over stored overlays
QHash<Qt::Corner, QIcon>::const_iterator i = m_overlays.constBegin();
while (i != m_overlays.constEnd()) {
const QPixmap overlayPixmap = i.value().pixmap(overlaySize, overlaySize, mode, state);
if (overlayPixmap.isNull()) {
++i;
continue;
}
QPoint startPoint;
switch (i.key()) {
case Qt::BottomLeftCorner:
startPoint = QPoint(2, height - overlaySize - 2);
break;
case Qt::BottomRightCorner:
startPoint = QPoint(width - overlaySize - 2, height - overlaySize - 2);
break;
case Qt::TopRightCorner:
startPoint = QPoint(width - overlaySize - 2, 2);
break;
case Qt::TopLeftCorner:
startPoint = QPoint(2, 2);
break;
}
// Draw the overlay pixmap
painter->drawPixmap(startPoint, overlayPixmap);
++i;
}
}
// ============================================================================
namespace KIconUtils
{
QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position)
{
return QIcon(new KOverlayIconEngine(icon, overlay, position));
}
QIcon addOverlays(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays)
{
return QIcon(new KOverlayIconEngine(icon, overlays));
}
QIcon addOverlays(const QIcon &icon, const QStringList &overlays)
{
if (overlays.count() == 0) {
return icon;
}
return QIcon(new KOverlayIconEngine(icon, overlays));
}
QIcon addOverlays(const QString &iconName, const QStringList &overlays)
{
const QIcon icon = QIcon::fromTheme(iconName);
if (overlays.count() == 0) {
return icon;
}
return QIcon(new KOverlayIconEngine(icon, overlays));
}
}
@@ -0,0 +1,79 @@
/*
SPDX-FileCopyrightText: 2013 Martin Klapetek <mklapetek@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KICONUTILS_H
#define KICONUTILS_H
#include <kguiaddons_export.h>
#include <QIcon>
/**
* @namespace KIconUtils
* Provides utility functions for icons.
*/
namespace KIconUtils
{
/**
* Adds the \a overlay over the \a icon in the specified \a position
*
* The \a overlay icon is scaled down approx. to 1/3 or 1/4 (depending on the icon size)
* and placed in one of the corners of the base icon.
*/
KGUIADDONS_EXPORT QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position);
/**
* Adds \a overlays over the \a icon
*
* The \a overlays is a QHash of Qt::Corner and QIcon. The Qt::Corner value
* decides where the overlay icon will be painted, the QIcon value
* is the overlay icon to be painted.
*
* The overlay icon is scaled down to 1/3 or 1/4 (depending on the icon size)
* and placed in one of the corners of the base icon.
*/
KGUIADDONS_EXPORT QIcon addOverlays(const QIcon &icon, const QHash<Qt::Corner, QIcon> &overlays);
/**
* Adds up to four overlays over the @p icon.
*
* The @p overlays is a QStringList of icon names (e.g. the emblems that are drawn on
* icons in Dolphin and KFileWidget, e.g. symlink, un-mounted device ...etc).
*
* Overlays are added in this order:
* - first icon is used to paint an overlay on the bottom-right corner
* - second icon on the bottom-left corner
* - third icon on the top-left corner
* - fourth icon on the top-right corner
*
* Each overlay icon is scaled down to 1/3 or 1/4 (depending on the icon size).
*
* @since 5.90
*/
KGUIADDONS_EXPORT QIcon addOverlays(const QIcon &icon, const QStringList &overlays);
/**
* Adds up to four overlays on the icon constructed from @p iconName.
*
* The @p overlays is a QStringList of icon names (e.g. the emblems that are drawn on
* icons in Dolphin and KFileWidget, e.g. symlink, un-mounted device ...etc).
*
* Overlays are added in this order:
* - first icon is used to paint an overlay on the bottom-right corner
* - second icon on the bottom-left corner
* - third icon on the top-left corner
* - fourth icon on the top-right corner
*
* Each overlay icon is scaled down to 1/3 or 1/4 (depending on the icon size).
*
* All @c QIcon objects are constructed using @c QIcon::fromTheme().
*
* @since 5.82
*/
KGUIADDONS_EXPORT QIcon addOverlays(const QString &iconName, const QStringList &overlays);
}
#endif // KICONUTILS_H
@@ -0,0 +1,219 @@
/* This file is part of the KDE project.
SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KIMAGECACHE_H
#define KIMAGECACHE_H
// check that KGUIADDONS_LIB is defined in case the application is not using CMake
// (if KGUIADDONS_LIB is not defined, we cannot assume that KCOREADDONS_LIB not being
// defined means that we are not linked against KCoreAddons)
#if defined(KGUIADDONS_LIB) && !defined(KCOREADDONS_LIB)
#ifdef __GNUC__
#warning "KImageCache requires KF6CoreAddons (for kshareddatacache.h)"
#else
#pragma message("KImageCache requires KF6CoreAddons (for kshareddatacache.h)")
#endif
#endif
#include <klocalimagecacheimpl.h>
#include <kshareddatacache.h>
#include <QImage>
#include <QPixmap>
#define KImageCache KSharedPixmapCacheMixin<KSharedDataCache>
/**
* @brief A simple wrapping layer over KSharedDataCache to support caching
* images and pixmaps.
*
* This class can be used to share images between different processes, which
* is useful when it is known that such images will be used across many
* processes, or when creating the image is expensive.
*
* In addition, the class also supports caching QPixmaps <em>in a single
* process</em> using the setPixmapCaching() function.
*
* Tips for use: If you already have QPixmaps that you intend to use, and
* you do not need access to the actual image data, then try to store and
* retrieve QPixmaps for use.
*
* On the other hand, if you will need to store and retrieve actual image
* data (to modify the image after retrieval for instance) then you should
* use QImage to save the conversion cost from QPixmap to QImage.
*
* KSharedPixmapCacheMixin is a subclass of KSharedDataCache, so all of the methods that
* can be used with KSharedDataCache can be used with KSharedPixmapCacheMixin,
* <em>with the exception of KSharedDataCache::insert() and
* KSharedDataCache::find()</em>.
*
* @author Michael Pyne <mpyne@kde.org>
* @since 4.5
*/
template<class T>
class KSharedPixmapCacheMixin : public T, private KLocalImageCacheImplementation
{
public:
/**
* Constructs an image cache, named by @p cacheName, with a default
* size of @p defaultCacheSize.
*
* @param cacheName Name of the cache to use.
* @param defaultCacheSize The default size, in bytes, of the cache.
* The actual on-disk size will be slightly larger. If the cache already
* exists, it will not be resized. If it is required to resize the
* cache then use the deleteCache() function to remove that cache first.
* @param expectedItemSize The expected general size of the items to be
* added to the image cache, in bytes. Use 0 if you just want a default
* item size.
*/
KSharedPixmapCacheMixin(const QString &cacheName, unsigned defaultCacheSize, unsigned expectedItemSize = 0)
: T(cacheName, defaultCacheSize, expectedItemSize)
, KLocalImageCacheImplementation(defaultCacheSize)
{
}
/**
* Inserts the pixmap given by @p pixmap to the cache, accessible with
* @p key. The pixmap must be converted to a QImage in order to be stored
* into shared memory. In order to prevent unnecessary conversions from
* taking place @p pixmap will also be cached (but not in shared
* memory) and would be accessible using findPixmap() if pixmap caching is
* enabled.
*
* @param key Name to access @p pixmap with.
* @param pixmap The pixmap to add to the cache.
* @return true if the pixmap was successfully cached, false otherwise.
* @see setPixmapCaching()
*/
bool insertPixmap(const QString &key, const QPixmap &pixmap)
{
insertLocalPixmap(key, pixmap);
// One thing to think about is only inserting things to the shared cache
// that are frequently used. But that would require tracking the use count
// in our local cache too, which I think is probably too much work.
return insertImage(key, pixmap.toImage());
}
/**
* Inserts the @p image into the shared cache, accessible with @p key. This
* variant is preferred over insertPixmap() if your source data is already a
* QImage, if it is essential that the image be in shared memory (such as
* for SVG icons which have a high render time), or if it will need to be
* in QImage form after it is retrieved from the cache.
*
* @param key Name to access @p image with.
* @param image The image to add to the shared cache.
* @return true if the image was successfully cached, false otherwise.
*/
bool insertImage(const QString &key, const QImage &image)
{
if (this->insert(key, serializeImage(image))) {
updateModifiedTime();
return true;
}
return false;
}
/**
* Copies the cached pixmap identified by @p key to @p destination. If no such
* pixmap exists @p destination is unchanged.
*
* @return true if the pixmap identified by @p key existed, false otherwise.
* @see setPixmapCaching()
*/
bool findPixmap(const QString &key, QPixmap *destination) const
{
if (findLocalPixmap(key, destination)) {
return true;
}
QByteArray cachedData;
if (!this->find(key, &cachedData) || cachedData.isNull()) {
return false;
}
if (destination) {
destination->loadFromData(cachedData, "PNG");
// Manually re-insert to pixmap cache if we'll be using this one.
insertLocalPixmap(key, *destination);
}
return true;
}
/**
* Copies the cached image identified by @p key to @p destination. If no such
* image exists @p destination is unchanged.
*
* @return true if the image identified by @p key existed, false otherwise.
*/
bool findImage(const QString &key, QImage *destination) const
{
QByteArray cachedData;
if (!this->find(key, &cachedData) || cachedData.isNull()) {
return false;
}
if (destination) {
destination->loadFromData(cachedData, "PNG");
}
return true;
}
/**
* Removes all entries from the cache. In addition any cached pixmaps (as per
* setPixmapCaching()) are also removed.
*/
void clear()
{
clearLocalCache();
T::clear();
}
/**
* @return The time that an image or pixmap was last inserted into a cache.
*/
using KLocalImageCacheImplementation::lastModifiedTime;
/**
* @return if QPixmaps added with insertPixmap() will be stored in a local
* pixmap cache as well as the shared image cache. The default is to cache
* pixmaps locally.
*/
using KLocalImageCacheImplementation::pixmapCaching;
/**
* Enables or disables local pixmap caching. If it is anticipated that a pixmap
* will be frequently needed then this can actually save memory overall since the
* X server or graphics card will not have to store duplicate copies of the same
* image.
*
* @param enable Enables pixmap caching if true, disables otherwise.
*/
using KLocalImageCacheImplementation::setPixmapCaching;
/**
* @return The highest memory size in bytes to be used by cached pixmaps.
* @since 4.6
*/
using KLocalImageCacheImplementation::pixmapCacheLimit;
/**
* Sets the highest memory size the pixmap cache should use.
*
* @param size The size in bytes
* @since 4.6
*/
using KLocalImageCacheImplementation::setPixmapCacheLimit;
};
#endif /* KIMAGECACHE_H */
@@ -0,0 +1,25 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kjobwindows.h"
#include <QObject>
#include <QPointer>
#include <QVariant>
#include <QWindow>
void KJobWindows::setWindow(QObject *job, QWindow *window)
{
job->setProperty("window", QVariant::fromValue(QPointer<QWindow>(window)));
if (window) {
job->setProperty("window-id", QVariant::fromValue(window->winId()));
}
}
QWindow *KJobWindows::window(QObject *job)
{
return job->property("window").value<QPointer<QWindow>>();
}
@@ -0,0 +1,48 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KJOBWINDOWS_H
#define KJOBWINDOWS_H
#include <kguiaddons_export.h>
class QWindow;
class QObject;
/**
* KJobWindows namespace
*/
namespace KJobWindows
{
/**
* Associate this job with a window given by @p window.
*
* @param job should be a KJob subclass
*
* This is used:
* @li by KDialogJobUiDelegate as parent widget for error messages
* @li by KWidgetJobTracker as parent widget for progress dialogs
* @li by KIO::AbstractJobInteractionInterface as parent widget for rename/skip dialogs
* and possibly more.
* @li by KIO::DropJob as parent widget of popup menus.
* This is required on Wayland to properly position the menu.
*
* @since 6.0
*/
KGUIADDONS_EXPORT void setWindow(QObject *job, QWindow *window);
/**
* Return the window associated with this job.
*
* @param job should be a KJob subclass
*
* @since 6.0
*/
KGUIADDONS_EXPORT QWindow *window(QObject *job);
}
#endif
@@ -0,0 +1,144 @@
/* This file is part of the KDE project.
SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "klocalimagecacheimpl.h"
#include <QBuffer>
#include <QCache>
#include <QCoreApplication>
#include <QDateTime>
#include <QImage>
#include <QPixmap>
/**
* This is a QObject subclass so we can catch the signal that the application is about
* to close and properly release any QPixmaps we have cached.
*/
class KLocalImageCacheImplementationPrivate : public QObject
{
Q_OBJECT
public:
KLocalImageCacheImplementationPrivate(QObject *parent = nullptr)
: QObject(parent)
, timestamp(QDateTime::currentDateTime())
{
QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &KLocalImageCacheImplementationPrivate::clearPixmaps);
}
/**
* Inserts a pixmap into the pixmap cache if the pixmap cache is enabled, with
* weighting based on image size and bit depth.
*/
bool insertPixmap(const QString &key, QPixmap *pixmap)
{
if (enablePixmapCaching && pixmap && !pixmap->isNull()) {
// "cost" parameter is based on both image size and depth to make cost
// based on size in bytes instead of area on-screen.
return pixmapCache.insert(key, pixmap, pixmap->width() * pixmap->height() * pixmap->depth() / 8);
}
return false;
}
public Q_SLOTS:
void clearPixmaps()
{
pixmapCache.clear();
}
public:
QDateTime timestamp;
/**
* This is used to cache pixmaps as they are inserted, instead of always
* converting to image data and storing that in shared memory.
*/
QCache<QString, QPixmap> pixmapCache;
bool enablePixmapCaching = true;
};
KLocalImageCacheImplementation::KLocalImageCacheImplementation(unsigned defaultCacheSize)
: d(new KLocalImageCacheImplementationPrivate)
{
// Use at least 16 KiB for the pixmap cache
d->pixmapCache.setMaxCost(qMax(defaultCacheSize / 8, (unsigned int)16384));
}
KLocalImageCacheImplementation::~KLocalImageCacheImplementation() = default;
void KLocalImageCacheImplementation::updateModifiedTime()
{
d->timestamp = QDateTime::currentDateTime();
}
QByteArray KLocalImageCacheImplementation::serializeImage(const QImage &image) const
{
QBuffer buffer;
buffer.open(QBuffer::WriteOnly);
image.save(&buffer, "PNG");
return buffer.buffer();
}
bool KLocalImageCacheImplementation::insertLocalPixmap(const QString &key, const QPixmap &pixmap) const
{
return d->insertPixmap(key, new QPixmap(pixmap));
}
bool KLocalImageCacheImplementation::findLocalPixmap(const QString &key, QPixmap *destination) const
{
if (d->enablePixmapCaching) {
QPixmap *cachedPixmap = d->pixmapCache.object(key);
if (cachedPixmap) {
if (destination) {
*destination = *cachedPixmap;
}
return true;
}
}
return false;
}
void KLocalImageCacheImplementation::clearLocalCache()
{
d->pixmapCache.clear();
}
QDateTime KLocalImageCacheImplementation::lastModifiedTime() const
{
return d->timestamp;
}
bool KLocalImageCacheImplementation::pixmapCaching() const
{
return d->enablePixmapCaching;
}
void KLocalImageCacheImplementation::setPixmapCaching(bool enable)
{
if (enable != d->enablePixmapCaching) {
d->enablePixmapCaching = enable;
if (!enable) {
d->pixmapCache.clear();
}
}
}
int KLocalImageCacheImplementation::pixmapCacheLimit() const
{
return d->pixmapCache.maxCost();
}
void KLocalImageCacheImplementation::setPixmapCacheLimit(int size)
{
d->pixmapCache.setMaxCost(size);
}
#include "klocalimagecacheimpl.moc"
@@ -0,0 +1,58 @@
/* This file is part of the KDE project.
SPDX-FileCopyrightText: 2010 Michael Pyne <mpyne@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KLOCALIMAGECACHEIMPL_H
#define KLOCALIMAGECACHEIMPL_H
#include <kguiaddons_export.h>
#include <memory>
class KLocalImageCacheImplementationPrivate;
class QImage;
class QPixmap;
class QByteArray;
class QDateTime;
class QString;
/**
* You are not supposed to use this class directly, use KImageCache instead
*
* @internal
*/
class KGUIADDONS_EXPORT KLocalImageCacheImplementation
{
private:
explicit KLocalImageCacheImplementation(unsigned defaultCacheSize);
public:
virtual ~KLocalImageCacheImplementation();
QDateTime lastModifiedTime() const;
bool pixmapCaching() const;
void setPixmapCaching(bool enable);
int pixmapCacheLimit() const;
void setPixmapCacheLimit(int size);
protected:
void updateModifiedTime();
QByteArray serializeImage(const QImage &image) const;
bool insertLocalPixmap(const QString &key, const QPixmap &pixmap) const;
bool findLocalPixmap(const QString &key, QPixmap *destination) const;
void clearLocalCache();
private:
std::unique_ptr<KLocalImageCacheImplementationPrivate> const d; ///< @internal
template<class T>
friend class KSharedPixmapCacheMixin;
};
#endif /* KLOCALIMAGECACHEIMPL_H */
@@ -0,0 +1,97 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kmodifierkeyinfo.h"
#include "kmodifierkeyinfoprovider_p.h"
#include <kguiaddons_debug.h>
#include <QGuiApplication>
#include "config.h"
#if WITH_WAYLAND
#include "kmodifierkeyinfoprovider_wayland.h"
#endif
#if WITH_X11
#include "kmodifierkeyinfoprovider_xcb.h"
#endif
KModifierKeyInfoProvider *createProvider()
{
#if WITH_WAYLAND
if (qGuiApp->platformName() == QLatin1String("wayland")) {
return new KModifierKeyInfoProviderWayland;
}
#endif
#if WITH_X11
if (qGuiApp->platformName() == QLatin1String("xcb")) {
return new KModifierKeyInfoProviderXcb;
}
#endif
qCWarning(KGUIADDONS_LOG) << "No modifierkeyinfo backend for platform" << qGuiApp->platformName();
return new KModifierKeyInfoProvider;
}
KModifierKeyInfo::KModifierKeyInfo(QObject *parent)
: QObject(parent)
, p(createProvider())
{
connect(p.data(), &KModifierKeyInfoProvider::keyPressed, this, &KModifierKeyInfo::keyPressed);
connect(p.data(), &KModifierKeyInfoProvider::keyLatched, this, &KModifierKeyInfo::keyLatched);
connect(p.data(), &KModifierKeyInfoProvider::keyLocked, this, &KModifierKeyInfo::keyLocked);
connect(p.data(), &KModifierKeyInfoProvider::buttonPressed, this, &KModifierKeyInfo::buttonPressed);
connect(p.data(), &KModifierKeyInfoProvider::keyAdded, this, &KModifierKeyInfo::keyAdded);
connect(p.data(), &KModifierKeyInfoProvider::keyRemoved, this, &KModifierKeyInfo::keyRemoved);
}
KModifierKeyInfo::~KModifierKeyInfo()
{
}
bool KModifierKeyInfo::knowsKey(Qt::Key key) const
{
return p->knowsKey(key);
}
const QList<Qt::Key> KModifierKeyInfo::knownKeys() const
{
return p->knownKeys();
}
bool KModifierKeyInfo::isKeyPressed(Qt::Key key) const
{
return p->isKeyPressed(key);
}
bool KModifierKeyInfo::isKeyLatched(Qt::Key key) const
{
return p->isKeyLatched(key);
}
bool KModifierKeyInfo::setKeyLatched(Qt::Key key, bool latched)
{
return p->setKeyLatched(key, latched);
}
bool KModifierKeyInfo::isKeyLocked(Qt::Key key) const
{
return p->isKeyLocked(key);
}
bool KModifierKeyInfo::setKeyLocked(Qt::Key key, bool locked)
{
return p->setKeyLocked(key, locked);
}
bool KModifierKeyInfo::isButtonPressed(Qt::MouseButton button) const
{
return p->isButtonPressed(button);
}
#include "moc_kmodifierkeyinfo.cpp"
@@ -0,0 +1,178 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KMODIFIERKEYINFO_H
#define KMODIFIERKEYINFO_H
#include <kguiaddons_export.h>
#include <QExplicitlySharedDataPointer>
#include <QObject>
class KModifierKeyInfoProvider;
/**
* @class KModifierKeyInfo kmodifierkeyinfo.h KModifierKeyInfo
*
* Get information about the state of the keyboard's modifier keys.
*
* This class provides cross-platform information about the state of the
* keyboard's modifier keys and the mouse buttons and allows to change the
* state as well.
*
* It recognizes two states a key can be in:
* @li @em locked: eg. caps-locked (a.k.a. toggled)
* @li @em latched the key is temporarily locked but will be unlocked upon
* the next keypress.
*
* An application can either query the states synchronously (isKeyLatched(),
* isKeyLocked()) or connect to KModifierKeyInfo's signals to be notified about
* changes (keyLatched(), keyLocked()).
*/
class KGUIADDONS_EXPORT KModifierKeyInfo : public QObject
{
Q_OBJECT
public:
/**
* Default constructor
*/
explicit KModifierKeyInfo(QObject *parent = nullptr);
/**
* Destructor
*/
~KModifierKeyInfo() override;
/**
* Check if a key is known by the underlying window system and can be queried.
*
* @param key The key to check
* @return true if the key is available, false if it is unknown
*/
bool knowsKey(Qt::Key key) const;
/**
* Get a list of known keys.
*
* @return A list of known keys of which states will be reported.
*/
const QList<Qt::Key> knownKeys() const;
/**
* Synchronously check if a key is pressed.
*
* @param key The key to check
* @return true if the key is pressed, false if the key is not pressed or unknown.
* @see isKeyLatched, @see isKeyLocked, @see keyPressed
*/
bool isKeyPressed(Qt::Key key) const;
/**
* Synchronously check if a key is latched.
*
* @param key The key to check
* @return true if the key is latched, false if the key is not latched or unknown.
* @see isKeyPressed, @see isKeyLocked, @see keyLatched
*/
bool isKeyLatched(Qt::Key key) const;
/**
* Set the latched state of a key.
*
* @param key The key to latch
* @param latched true to latch the key, false to unlatch it.
* @return false if the key is unknown. true doesn't guarantee you the
* operation worked.
*/
bool setKeyLatched(Qt::Key key, bool latched);
/**
* Synchronously check if a key is locked.
*
* @param key The key to check
* @return true if the key is locked, false if the key is not locked or unknown.
* @see isKeyPressed, @see isKeyLatched, @see keyLocked
*/
bool isKeyLocked(Qt::Key key) const;
/**
* Set the locked state of a key.
*
* @param key The key to lock
* @param latched true to lock the key, false to unlock it.
* @return false if the key is unknown. true doesn't guarantee you the
* operation worked.
*/
bool setKeyLocked(Qt::Key key, bool locked);
/**
* Synchronously check if a mouse button is pressed.
*
* @param button The mouse button to check
* @return true if the mouse button is pressed, false if the mouse button
* is not pressed or its state is unknown.
*/
bool isButtonPressed(Qt::MouseButton button) const;
Q_SIGNALS:
/**
* This signal is emitted whenever the pressed state of a key changes
* (key press or key release).
*
* @param key The key that changed state
* @param pressed true if the key is now pressed, false if is released.
*/
void keyPressed(Qt::Key key, bool pressed);
/**
* This signal is emitted whenever the latched state of a key changes.
*
* @param key The key that changed state
* @param latched true if the key is now latched, false if it isn't
*/
void keyLatched(Qt::Key key, bool latched);
/**
* This signal is emitted whenever the locked state of a key changes.
*
* @param key The key that changed state
* @param locked true if the key is now locked, false if it isn't
*/
void keyLocked(Qt::Key key, bool locked);
/**
* This signal is emitted whenever the pressed state of a mouse button
* changes (mouse button press or release).
*
* @param button The mouse button that changed state
* @param pressed true if the mouse button is now pressed, false if
* is released.
*/
void buttonPressed(Qt::MouseButton button, bool pressed);
/**
* This signal is emitted whenever a new modifier is found due to
* the keyboard mapping changing.
*
* @param key The key that was discovered
*/
void keyAdded(Qt::Key key);
/**
* This signal is emitted whenever a previously known modifier no
* longer exists due to the keyboard mapping changing.
*
* @param key The key that vanished
*/
void keyRemoved(Qt::Key key);
private:
Q_DISABLE_COPY(KModifierKeyInfo)
QExplicitlySharedDataPointer<KModifierKeyInfoProvider> const p; // krazy:exclude=dpointer
};
#endif
@@ -0,0 +1,95 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kmodifierkeyinfoprovider_p.h"
KModifierKeyInfoProvider::KModifierKeyInfoProvider()
: QObject(nullptr)
{
}
KModifierKeyInfoProvider::~KModifierKeyInfoProvider()
{
}
bool KModifierKeyInfoProvider::setKeyLatched(Qt::Key key, bool latched)
{
Q_UNUSED(key);
Q_UNUSED(latched);
return false;
}
bool KModifierKeyInfoProvider::setKeyLocked(Qt::Key key, bool locked)
{
Q_UNUSED(key);
Q_UNUSED(locked);
return false;
}
bool KModifierKeyInfoProvider::isKeyPressed(Qt::Key key) const
{
auto it = m_modifierStates.constFind(key);
if (it != m_modifierStates.constEnd()) {
return *it & Pressed;
}
return false;
}
bool KModifierKeyInfoProvider::isKeyLatched(Qt::Key key) const
{
auto it = m_modifierStates.constFind(key);
if (it != m_modifierStates.constEnd()) {
return *it & Latched;
}
return false;
}
bool KModifierKeyInfoProvider::isKeyLocked(Qt::Key key) const
{
auto it = m_modifierStates.constFind(key);
if (it != m_modifierStates.constEnd()) {
return *it & Locked;
}
return false;
}
bool KModifierKeyInfoProvider::isButtonPressed(Qt::MouseButton button) const
{
if (m_buttonStates.contains(button)) {
return m_buttonStates[button];
}
return false;
}
bool KModifierKeyInfoProvider::knowsKey(Qt::Key key) const
{
return m_modifierStates.contains(key);
}
const QList<Qt::Key> KModifierKeyInfoProvider::knownKeys() const
{
return m_modifierStates.keys();
}
void KModifierKeyInfoProvider::stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates newState)
{
auto &state = m_modifierStates[key];
if (newState != state) {
const auto difference = (newState ^ state);
state = newState;
if (difference & Pressed) {
Q_EMIT keyPressed(key, newState & Pressed);
}
if (difference & Latched) {
Q_EMIT keyLatched(key, newState & Latched);
}
if (difference & Locked) {
Q_EMIT keyLocked(key, newState & Locked);
}
}
}
#include "moc_kmodifierkeyinfoprovider_p.cpp"
@@ -0,0 +1,116 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KMODIFIERKEYINFOPROVIDER_P_H
#define KMODIFIERKEYINFOPROVIDER_P_H
#include "kguiaddons_export.h"
#include <QHash>
#include <QObject>
#include <QSharedData>
/**
* Background class that implements the behaviour of KModifierKeyInfo for
* the different supported platforms.
* @internal
*/
class KGUIADDONS_EXPORT KModifierKeyInfoProvider : public QObject, public QSharedData
{
Q_OBJECT
public:
enum ModifierState {
Nothing = 0x0,
Pressed = 0x1,
Latched = 0x2,
Locked = 0x4,
};
Q_ENUM(ModifierState)
Q_DECLARE_FLAGS(ModifierStates, ModifierState)
KModifierKeyInfoProvider();
~KModifierKeyInfoProvider() override;
/**
* Detect if a key is pressed.
* @param key Modifier key to query
* @return true if the key is pressed, false if it isn't.
*/
bool isKeyPressed(Qt::Key key) const;
/**
* Detect if a key is latched.
* @param key Modifier key to query
* @return true if the key is latched, false if it isn't.
*/
bool isKeyLatched(Qt::Key key) const;
/**
* Set the latched state of a key.
* @param key Modifier to set the latched state for
* @param latched true to latch the key, false to unlatch it
* @return true if the key is known, false else
*/
virtual bool setKeyLatched(Qt::Key key, bool latched);
/**
* Detect if a key is locked.
* @param key Modifier key to query
* @return true if the key is locked, false if it isn't.
*/
bool isKeyLocked(Qt::Key key) const;
/**
* Set the locked state of a key.
* @param key Modifier to set the locked state for
* @param latched true to lock the key, false to unlock it
* @return true if the key is known, false else
*/
virtual bool setKeyLocked(Qt::Key key, bool locked);
/**
* Check if a mouse button is pressed.
* @param button Mouse button to check
* @return true if pressed, false else
*/
bool isButtonPressed(Qt::MouseButton button) const;
/**
* Check if a key is known/can be queried
* @param key Modifier key to check
* @return true if the key is known, false if it isn't.
*/
bool knowsKey(Qt::Key key) const;
/**
* Get a list of known keys
* @return List of known keys.
*/
const QList<Qt::Key> knownKeys() const;
Q_SIGNALS:
void keyLatched(Qt::Key key, bool state);
void keyLocked(Qt::Key key, bool state);
void keyPressed(Qt::Key key, bool state);
void buttonPressed(Qt::MouseButton button, bool state);
void keyAdded(Qt::Key key);
void keyRemoved(Qt::Key key);
protected:
void stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates state);
// the state of each known modifier
QHash<Qt::Key, ModifierStates> m_modifierStates;
// the state of each known mouse button
QHash<Qt::MouseButton, bool> m_buttonStates;
};
Q_DECLARE_INTERFACE(KModifierKeyInfoProvider, "org.kde.kguiaddons.KModifierKeyInfoProvider")
Q_DECLARE_OPERATORS_FOR_FLAGS(KModifierKeyInfoProvider::ModifierStates)
#endif
@@ -0,0 +1,123 @@
/*
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kmodifierkeyinfoprovider_wayland.h"
#include <QDebug>
#include <QGuiApplication>
#include <QWaylandClientExtensionTemplate>
#include <wayland-client-core.h>
#include "qwayland-keystate.h"
class KeyState : public QWaylandClientExtensionTemplate<KeyState>, public QtWayland::org_kde_kwin_keystate
{
Q_OBJECT
public:
KeyState()
: QWaylandClientExtensionTemplate<KeyState>(5)
{
}
~KeyState()
{
if (isInitialized() && qGuiApp) {
if (QtWayland::org_kde_kwin_keystate::version() >= ORG_KDE_KWIN_KEYSTATE_DESTROY_SINCE_VERSION) {
destroy();
} else {
wl_proxy_destroy(reinterpret_cast<struct wl_proxy *>(object()));
}
}
}
void org_kde_kwin_keystate_stateChanged(uint32_t key, uint32_t state) override
{
Q_EMIT stateChanged(toKey(static_cast<KeyState::key>(key)), toState(static_cast<KeyState::state>(state)));
}
KModifierKeyInfoProvider::ModifierState toState(KeyState::state state)
{
switch (state) {
case KeyState::state::state_unlocked:
return KModifierKeyInfoProvider::Nothing;
case KeyState::state::state_locked:
return KModifierKeyInfoProvider::Locked;
case KeyState::state::state_latched:
return KModifierKeyInfoProvider::Latched;
case KeyState::state::state_pressed:
return KModifierKeyInfoProvider::Pressed;
}
Q_UNREACHABLE();
return KModifierKeyInfoProvider::Nothing;
}
Qt::Key toKey(KeyState::key key)
{
switch (key) {
case KeyState::key::key_capslock:
return Qt::Key_CapsLock;
case KeyState::key::key_numlock:
return Qt::Key_NumLock;
case KeyState::key::key_scrolllock:
return Qt::Key_ScrollLock;
case KeyState::key_alt:
return Qt::Key_Alt;
case KeyState::key_shift:
return Qt::Key_Shift;
case KeyState::key_control:
return Qt::Key_Control;
case KeyState::key_meta:
return Qt::Key_Meta;
case KeyState::key_altgr:
return Qt::Key_AltGr;
}
Q_UNREACHABLE();
return {};
}
Q_SIGNAL void stateChanged(Qt::Key key, KModifierKeyInfoProvider::ModifierState state);
};
KModifierKeyInfoProviderWayland::KModifierKeyInfoProviderWayland()
{
m_keystate = new KeyState;
QObject::connect(m_keystate, &KeyState::activeChanged, this, [this]() {
if (m_keystate->isActive()) {
m_keystate->fetchStates();
}
});
connect(m_keystate, &KeyState::stateChanged, this, &KModifierKeyInfoProviderWayland::stateUpdated);
stateUpdated(Qt::Key_CapsLock, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_NumLock, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_ScrollLock, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_Alt, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_Shift, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_Control, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_Meta, KModifierKeyInfoProvider::Nothing);
stateUpdated(Qt::Key_AltGr, KModifierKeyInfoProvider::Nothing);
}
KModifierKeyInfoProviderWayland::~KModifierKeyInfoProviderWayland()
{
delete m_keystate;
}
bool KModifierKeyInfoProviderWayland::setKeyLatched(Qt::Key /*key*/, bool /*latched*/)
{
return false;
}
bool KModifierKeyInfoProviderWayland::setKeyLocked(Qt::Key /*key*/, bool /*locked*/)
{
return false;
}
#include "kmodifierkeyinfoprovider_wayland.moc"
#include "moc_kmodifierkeyinfoprovider_wayland.cpp"
@@ -0,0 +1,29 @@
/*
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KMODIFIERKEYINFOPROVIDERWAYLAND_H
#define KMODIFIERKEYINFOPROVIDERWAYLAND_H
#include <kmodifierkeyinfoprovider_p.h>
class KeyState;
class KModifierKeyInfoProviderWayland : public KModifierKeyInfoProvider
{
Q_OBJECT
public:
KModifierKeyInfoProviderWayland();
~KModifierKeyInfoProviderWayland();
bool setKeyLatched(Qt::Key key, bool latched) override;
bool setKeyLocked(Qt::Key key, bool locked) override;
private:
KeyState *m_keystate;
};
#endif
@@ -0,0 +1,345 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kmodifierkeyinfoprovider_xcb.h"
#include "kmodifierkeyinfo.h"
#include <QGuiApplication>
#define XK_MISCELLANY
#define XK_XKB_KEYS
#include <X11/XKBlib.h>
#include <X11/keysymdef.h>
#include <xcb/xcb.h>
struct ModifierDefinition {
ModifierDefinition(Qt::Key _key, unsigned int _mask, const char *_name, KeySym _keysym)
: key(_key)
, mask(_mask)
, name(_name)
, keysym(_keysym)
{
}
Qt::Key key;
unsigned int mask;
const char *name; // virtual modifier name
KeySym keysym;
};
/*
* Get the real modifiers related to a virtual modifier.
*/
unsigned int xkbVirtualModifier(XkbDescPtr xkb, const char *name)
{
Q_ASSERT(xkb != nullptr);
unsigned int mask = 0;
bool nameEqual;
for (int i = 0; i < XkbNumVirtualMods; ++i) {
char *modStr = XGetAtomName(xkb->dpy, xkb->names->vmods[i]);
if (modStr != nullptr) {
nameEqual = (strcmp(name, modStr) == 0);
XFree(modStr);
if (nameEqual) {
XkbVirtualModsToReal(xkb, 1 << i, &mask);
break;
}
}
}
return mask;
}
static Display *display()
{
return qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
}
KModifierKeyInfoProviderXcb::KModifierKeyInfoProviderXcb()
: KModifierKeyInfoProvider()
, m_xkbEv(0)
, m_xkbAvailable(false)
{
if (qGuiApp) {
if (qGuiApp->platformName() == QLatin1String("xcb")) {
int code;
int xkberr;
int maj;
int min;
m_xkbAvailable = XkbQueryExtension(display(), &code, &m_xkbEv, &xkberr, &maj, &min);
}
}
if (m_xkbAvailable) {
/* clang-format off */
XkbSelectEvents(display(),
XkbUseCoreKbd,
XkbStateNotifyMask | XkbMapNotifyMask,
XkbStateNotifyMask | XkbMapNotifyMask);
unsigned long int stateMask = XkbModifierStateMask
| XkbModifierBaseMask
| XkbModifierLatchMask
| XkbModifierLockMask
| XkbPointerButtonMask;
/* clang-format on */
XkbSelectEventDetails(display(), XkbUseCoreKbd, XkbStateNotifyMask, stateMask, stateMask);
}
xkbUpdateModifierMapping();
// add known pointer buttons
m_xkbButtons.insert(Qt::LeftButton, Button1Mask);
m_xkbButtons.insert(Qt::MiddleButton, Button2Mask);
m_xkbButtons.insert(Qt::RightButton, Button3Mask);
m_xkbButtons.insert(Qt::XButton1, Button4Mask);
m_xkbButtons.insert(Qt::XButton2, Button5Mask);
// get the initial state
if (m_xkbAvailable) {
XkbStateRec state;
XkbGetState(display(), XkbUseCoreKbd, &state);
xkbModifierStateChanged(state.mods, state.latched_mods, state.locked_mods);
xkbButtonStateChanged(state.ptr_buttons);
QCoreApplication::instance()->installNativeEventFilter(this);
}
}
KModifierKeyInfoProviderXcb::~KModifierKeyInfoProviderXcb()
{
if (m_xkbAvailable) {
QCoreApplication::instance()->removeNativeEventFilter(this);
}
}
bool KModifierKeyInfoProviderXcb::setKeyLatched(Qt::Key key, bool latched)
{
if (!m_xkbModifiers.contains(key)) {
return false;
}
return XkbLatchModifiers(display(), XkbUseCoreKbd, m_xkbModifiers[key], latched ? m_xkbModifiers[key] : 0);
}
bool KModifierKeyInfoProviderXcb::setKeyLocked(Qt::Key key, bool locked)
{
if (!m_xkbModifiers.contains(key)) {
return false;
}
return XkbLockModifiers(display(), XkbUseCoreKbd, m_xkbModifiers[key], locked ? m_xkbModifiers[key] : 0);
}
// HACK: xcb-xkb is not yet a public part of xcb. Because of that we have to include the event structure.
namespace
{
typedef struct _xcb_xkb_map_notify_event_t {
uint8_t response_type;
uint8_t xkbType;
uint16_t sequence;
xcb_timestamp_t time;
uint8_t deviceID;
uint8_t ptrBtnActions;
uint16_t changed;
xcb_keycode_t minKeyCode;
xcb_keycode_t maxKeyCode;
uint8_t firstType;
uint8_t nTypes;
xcb_keycode_t firstKeySym;
uint8_t nKeySyms;
xcb_keycode_t firstKeyAct;
uint8_t nKeyActs;
xcb_keycode_t firstKeyBehavior;
uint8_t nKeyBehavior;
xcb_keycode_t firstKeyExplicit;
uint8_t nKeyExplicit;
xcb_keycode_t firstModMapKey;
uint8_t nModMapKeys;
xcb_keycode_t firstVModMapKey;
uint8_t nVModMapKeys;
uint16_t virtualMods;
uint8_t pad0[2];
} _xcb_xkb_map_notify_event_t;
typedef struct _xcb_xkb_state_notify_event_t {
uint8_t response_type;
uint8_t xkbType;
uint16_t sequence;
xcb_timestamp_t time;
uint8_t deviceID;
uint8_t mods;
uint8_t baseMods;
uint8_t latchedMods;
uint8_t lockedMods;
uint8_t group;
int16_t baseGroup;
int16_t latchedGroup;
uint8_t lockedGroup;
uint8_t compatState;
uint8_t grabMods;
uint8_t compatGrabMods;
uint8_t lookupMods;
uint8_t compatLoockupMods;
uint16_t ptrBtnState;
uint16_t changed;
xcb_keycode_t keycode;
uint8_t eventType;
uint8_t requestMajor;
uint8_t requestMinor;
} _xcb_xkb_state_notify_event_t;
typedef union {
/* All XKB events share these fields. */
struct {
uint8_t response_type;
uint8_t xkbType;
uint16_t sequence;
xcb_timestamp_t time;
uint8_t deviceID;
} any;
_xcb_xkb_map_notify_event_t map_notify;
_xcb_xkb_state_notify_event_t state_notify;
} _xkb_event;
}
bool KModifierKeyInfoProviderXcb::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
{
if (!m_xkbAvailable || eventType != "xcb_generic_event_t") {
return false;
}
xcb_generic_event_t *event = static_cast<xcb_generic_event_t *>(message);
if ((event->response_type & ~0x80) == m_xkbEv + XkbEventCode) {
_xkb_event *kbevt = reinterpret_cast<_xkb_event *>(event);
unsigned int stateMask = XkbModifierStateMask | XkbModifierBaseMask | XkbModifierLatchMask | XkbModifierLockMask;
if (kbevt->any.xkbType == XkbMapNotify) {
xkbUpdateModifierMapping();
} else if (kbevt->any.xkbType == XkbStateNotify) {
if (kbevt->state_notify.changed & stateMask) {
xkbModifierStateChanged(kbevt->state_notify.mods, kbevt->state_notify.latchedMods, kbevt->state_notify.lockedMods);
} else if (kbevt->state_notify.changed & XkbPointerButtonMask) {
xkbButtonStateChanged(kbevt->state_notify.ptrBtnState);
}
}
}
return false;
}
void KModifierKeyInfoProviderXcb::xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, unsigned char locked_mods)
{
// detect keyboard modifiers
ModifierStates newState;
QHash<Qt::Key, unsigned int>::const_iterator it;
QHash<Qt::Key, unsigned int>::const_iterator end = m_xkbModifiers.constEnd();
for (it = m_xkbModifiers.constBegin(); it != end; ++it) {
if (!m_modifierStates.contains(it.key())) {
continue;
}
newState = Nothing;
// determine the new state
if (mods & it.value()) {
newState |= Pressed;
}
if (latched_mods & it.value()) {
newState |= Latched;
}
if (locked_mods & it.value()) {
newState |= Locked;
}
stateUpdated(it.key(), newState);
}
}
void KModifierKeyInfoProviderXcb::xkbButtonStateChanged(unsigned short ptr_buttons)
{
// detect mouse button states
bool newButtonState;
QHash<Qt::MouseButton, unsigned short>::const_iterator it;
QHash<Qt::MouseButton, unsigned short>::const_iterator end = m_xkbButtons.constEnd();
for (it = m_xkbButtons.constBegin(); it != end; ++it) {
newButtonState = (ptr_buttons & it.value());
if (newButtonState != m_buttonStates[it.key()]) {
m_buttonStates[it.key()] = newButtonState;
Q_EMIT buttonPressed(it.key(), newButtonState);
}
}
}
void KModifierKeyInfoProviderXcb::xkbUpdateModifierMapping()
{
if (!m_xkbAvailable) {
return;
}
m_xkbModifiers.clear();
QList<ModifierDefinition> srcModifiers;
/* clang-format off */
srcModifiers << ModifierDefinition(Qt::Key_Shift, ShiftMask, nullptr, 0)
<< ModifierDefinition(Qt::Key_Control, ControlMask, nullptr, 0)
<< ModifierDefinition(Qt::Key_Alt, 0, "Alt", XK_Alt_L)
// << { 0, 0, I18N_NOOP("Win"), "superkey", "" }
<< ModifierDefinition(Qt::Key_Meta, 0, "Meta", XK_Meta_L)
<< ModifierDefinition(Qt::Key_Super_L, 0, "Super", XK_Super_L)
<< ModifierDefinition(Qt::Key_Hyper_L, 0, "Hyper", XK_Hyper_L)
<< ModifierDefinition(Qt::Key_AltGr, 0, "AltGr", 0)
<< ModifierDefinition(Qt::Key_NumLock, 0, "NumLock", XK_Num_Lock)
<< ModifierDefinition(Qt::Key_CapsLock, LockMask, nullptr, 0)
<< ModifierDefinition(Qt::Key_ScrollLock, 0, "ScrollLock", XK_Scroll_Lock);
/* clang-format on */
XkbDescPtr xkb = XkbGetKeyboard(display(), XkbAllComponentsMask, XkbUseCoreKbd);
QList<ModifierDefinition>::const_iterator it;
QList<ModifierDefinition>::const_iterator end = srcModifiers.constEnd();
for (it = srcModifiers.constBegin(); it != end; ++it) {
unsigned int mask = it->mask;
if (mask == 0 && xkb != nullptr) {
// try virtual modifier first
if (it->name != nullptr) {
mask = xkbVirtualModifier(xkb, it->name);
}
if (mask == 0 && it->keysym != 0) {
mask = XkbKeysymToModifiers(display(), it->keysym);
} else if (mask == 0) {
// special case for AltGr
/* clang-format off */
mask = XkbKeysymToModifiers(display(), XK_Mode_switch)
| XkbKeysymToModifiers(display(), XK_ISO_Level3_Shift)
| XkbKeysymToModifiers(display(), XK_ISO_Level3_Latch)
| XkbKeysymToModifiers(display(), XK_ISO_Level3_Lock);
/* clang-format on */
}
}
if (mask != 0) {
m_xkbModifiers.insert(it->key, mask);
// previously unknown modifier
if (!m_modifierStates.contains(it->key)) {
m_modifierStates.insert(it->key, Nothing);
Q_EMIT keyAdded(it->key);
}
}
}
// remove modifiers which are no longer available
QMutableHashIterator<Qt::Key, ModifierStates> i(m_modifierStates);
while (i.hasNext()) {
i.next();
if (!m_xkbModifiers.contains(i.key())) {
Qt::Key key = i.key();
i.remove();
Q_EMIT keyRemoved(key);
}
}
if (xkb != nullptr) {
XkbFreeKeyboard(xkb, 0, true);
}
}
#include "moc_kmodifierkeyinfoprovider_xcb.cpp"
@@ -0,0 +1,40 @@
/*
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KMODIFIERKEYINFOPROVIDERXCB_H
#define KMODIFIERKEYINFOPROVIDERXCB_H
#include "kmodifierkeyinfoprovider_p.h"
#include <QAbstractNativeEventFilter>
class KModifierKeyInfoProviderXcb : public KModifierKeyInfoProvider, public QAbstractNativeEventFilter
{
Q_OBJECT
public:
KModifierKeyInfoProviderXcb();
~KModifierKeyInfoProviderXcb() override;
bool setKeyLatched(Qt::Key key, bool latched) override;
bool setKeyLocked(Qt::Key key, bool locked) override;
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override;
void xkbUpdateModifierMapping();
void xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, unsigned char locked_mods);
void xkbButtonStateChanged(unsigned short ptr_buttons);
private:
int m_xkbEv;
bool m_xkbAvailable;
// maps a Qt::Key to a modifier mask
QHash<Qt::Key, unsigned int> m_xkbModifiers;
// maps a Qt::MouseButton to a button mask
QHash<Qt::MouseButton, unsigned short> m_xkbButtons;
};
#endif
@@ -0,0 +1,129 @@
/*
SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kurlhandler_p.h"
#include <kguiaddons_debug.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDesktopServices>
#include <QLocale>
#include <QProcess>
#include <QStandardPaths>
#include <QUrl>
static const char s_khelpcenter_exec[] = "khelpcenter";
static bool openWithKHelpCenter(const QUrl &url)
{
const QString helpcenter = QStandardPaths::findExecutable(QString::fromLatin1(s_khelpcenter_exec));
if (!helpcenter.isEmpty()) {
QUrl u(url);
if (u.path() == QLatin1Char('/')) {
const QString appName = QCoreApplication::applicationName();
u.setPath(appName);
}
QProcess::startDetached(helpcenter, QStringList(u.toString()));
return true;
}
return false;
}
KUrlHandler::KUrlHandler(QObject *parent)
: QObject(parent)
{
}
void KUrlHandler::openHelp(const QUrl &url) const
{
if (openWithKHelpCenter(url)) {
return;
}
const QUrl docUrl = concatDocsUrl(url);
if (docUrl.isValid()) {
QDesktopServices::openUrl(docUrl);
} else {
qCWarning(KGUIADDONS_LOG) << "Could not find a suitable handler for" << url.toString();
}
}
QUrl KUrlHandler::concatDocsUrl(const QUrl &url) const
{
if (QCoreApplication::organizationDomain() != QLatin1String("kde.org")) {
return {};
}
// KHelpCenter is not available and it's a KDE application, open the docs at docs.kde.org
// with the default web browser on the system
QString path = url.path();
const QString fragment = url.fragment();
const QString common = QLatin1String("https://docs.kde.org/index.php?branch=stable5&language=") + QLocale().name();
const QString appName = QCoreApplication::applicationName();
// Special case for KCModules
if (appName == QLatin1String("systemsettings") && path.startsWith(QLatin1String("/kcontrol"))) {
// E.g. change "/kcontrol/fonts/index.html" to "&application=kcontrol/fonts&path=index.html"
// docs.kde.org will resolve the url and add the proper package name, e.g. plasma-workspace:
// https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/fonts/index.html
QString kcmAppName(path);
kcmAppName.remove(0, 1); // Remove leading "/"
const int idx = kcmAppName.indexOf(QLatin1String("/index.html"));
if (idx > 0) {
kcmAppName.truncate(idx);
}
// Some KCModules have a valid fragment, e.g. kcontrol/powerdevil/index.html#advanced-settings
const QString tail = QLatin1String("index.html") + (!fragment.isEmpty() ? QLatin1Char('#') + fragment : QString{});
return QUrl(common + QLatin1String("&application=") + kcmAppName + QLatin1String("&path=") + tail);
}
// E.g. "help:/" and appName is "okular", e.g. opening Help -> Okular HandBook
if (path == QLatin1Char('/')) {
return QUrl(common + QLatin1String("&application=") + appName + QLatin1String("&path=") + QLatin1String("index.html"));
}
// E.g. "help:/okular/configure.html", don't repeat "appName"; e.g. clicking Help button in
// the "Settings -> Configure Okular" dialog
const QString redundant = QLatin1Char('/') + appName + QLatin1Char('/');
if (path.startsWith(redundant)) {
path.remove(0, redundant.size());
if (!fragment.isEmpty()) {
// E.g. "help:/kinfocenter/index.html#kcm_memory", it's actually "kinfocenter/kcm_memory.html"
if (path == QLatin1String("index.html")) {
qCWarning(KGUIADDONS_LOG) << "X-DocPath entry in a .desktop file in" << appName << "is:" << appName + QLatin1String("/index.html#") + fragment
<< ", however it should be:" << appName + QLatin1Char('/') + fragment + QLatin1String(".html");
path = fragment + QLatin1String(".html");
} else {
// E.g. "help:/okular/signatures.html#adding_digital_signatures"
path += QLatin1Char('#') + fragment;
}
}
return QUrl(common + QLatin1String("&application=") + appName + QLatin1String("&path=") + path);
}
return {};
}
Q_GLOBAL_STATIC(KUrlHandler, s_handler)
static void initializeGlobalSettings()
{
QDesktopServices::setUrlHandler(QStringLiteral("help"), s_handler, "openHelp");
}
Q_COREAPP_STARTUP_FUNCTION(initializeGlobalSettings)
#include "moc_kurlhandler_p.cpp"
@@ -0,0 +1,29 @@
/*
SPDX-FileCopyrightText: 2021 Ahmad Samir <a.samirh78@gmail.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#ifndef KURLHANDLER_P_H
#define KURLHANDLER_P_H
#include <kguiaddons_export.h>
#include <QObject>
class QUrl;
class KGUIADDONS_EXPORT KUrlHandler : public QObject
{
Q_OBJECT
public:
explicit KUrlHandler(QObject *parent = nullptr);
QUrl concatDocsUrl(const QUrl &url) const;
public Q_SLOTS:
void openHelp(const QUrl &url) const;
};
#endif // KURLHANDLER_P_H