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:
@@ -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 ®ion)
|
||||
{
|
||||
// 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 ®ionOrCountry)
|
||||
: 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 ®ionOrCountry);
|
||||
~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
|
||||
+123
@@ -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
|
||||
Reference in New Issue
Block a user