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,57 @@
|
||||
add_subdirectory(core)
|
||||
if(TARGET Qt6::Gui)
|
||||
add_subdirectory(gui)
|
||||
endif()
|
||||
if(KCONFIG_USE_QML)
|
||||
add_subdirectory(qml)
|
||||
endif()
|
||||
add_subdirectory(kconfig_compiler)
|
||||
add_subdirectory(kconf_update)
|
||||
add_subdirectory(kreadconfig)
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KCONFIG
|
||||
FILE kconfig.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6Config_QCH
|
||||
NAME KConfig
|
||||
BASE_NAME KF6Config
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KConfigCore_APIDOX_SRCS}
|
||||
${KConfigGui_APIDOX_SRCS}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/kconfig_compiler/README.dox"
|
||||
"${CMAKE_SOURCE_DIR}/docs/options.md"
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
LINK_QCHS
|
||||
Qt6Core_QCH
|
||||
Qt6Xml_QCH
|
||||
Qt6Gui_QCH
|
||||
INCLUDE_DIRS
|
||||
${KConfigCore_APIDOX_INCLUDE_DIRS}
|
||||
${KConfigGui_APIDOX_INCLUDE_DIRS}
|
||||
BLANK_MACROS
|
||||
KCONFIGCORE_EXPORT
|
||||
KCONFIGCORE_DEPRECATED_EXPORT
|
||||
KCONFIGCORE_DEPRECATED
|
||||
"KCONFIGCORE_DEPRECATED_VERSION(x, y, t)"
|
||||
"KCONFIGCORE_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
"KCONFIGCORE_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KCONFIGCORE_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
KCONFIGGUI_EXPORT
|
||||
KCONFIGGUI_DEPRECATED_EXPORT
|
||||
KCONFIGGUI_DEPRECATED
|
||||
"KCONFIGGUI_DEPRECATED_VERSION(x, y, t)"
|
||||
"KCONFIGGUI_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
"KCONFIGGUI_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KCONFIGGUI_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract strings from all source files.
|
||||
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
|
||||
# lconvert.
|
||||
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kconfig6_qt.pot
|
||||
@@ -0,0 +1,95 @@
|
||||
add_library(KF6ConfigCore)
|
||||
add_library(KF6::ConfigCore ALIAS KF6ConfigCore)
|
||||
|
||||
qt_extract_metatypes(KF6ConfigCore)
|
||||
|
||||
set_target_properties(KF6ConfigCore PROPERTIES
|
||||
VERSION ${KCONFIG_VERSION}
|
||||
SOVERSION ${KCONFIG_SOVERSION}
|
||||
EXPORT_NAME ConfigCore
|
||||
)
|
||||
|
||||
target_sources(KF6ConfigCore PRIVATE
|
||||
kconfig.cpp
|
||||
kconfigbase.cpp
|
||||
kconfigdata.cpp
|
||||
kconfiggroup.cpp
|
||||
kconfigini.cpp
|
||||
kdesktopfile.cpp
|
||||
kdesktopfileaction.cpp
|
||||
ksharedconfig.cpp
|
||||
kcoreconfigskeleton.cpp
|
||||
kauthorized.cpp
|
||||
kemailsettings.cpp
|
||||
kconfigwatcher.cpp
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6ConfigCore
|
||||
HEADER kconfig_core_log_settings.h
|
||||
IDENTIFIER KCONFIG_CORE_LOG
|
||||
CATEGORY_NAME kf.config.core
|
||||
OLD_CATEGORY_NAMES kf5.kconfig.core
|
||||
DESCRIPTION "KConfig Core"
|
||||
EXPORT KCONFIG
|
||||
)
|
||||
|
||||
configure_file(config-kconfig.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kconfig.h )
|
||||
|
||||
ecm_generate_export_header(KF6ConfigCore
|
||||
BASE_NAME KConfigCore
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME KConfig
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS 6.3
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_compile_definitions(KF6ConfigCore
|
||||
PRIVATE
|
||||
KCONF_UPDATE_INSTALL_LOCATION="${KDE_INSTALL_FULL_LIBEXECDIR_KF}/$<TARGET_FILE_NAME:KF6::kconf_update>"
|
||||
)
|
||||
|
||||
target_include_directories(KF6ConfigCore
|
||||
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KConfig;${KDE_INSTALL_INCLUDEDIR_KF}/KConfigCore>"
|
||||
)
|
||||
|
||||
target_link_libraries(KF6ConfigCore PUBLIC Qt6::Core)
|
||||
|
||||
if(KCONFIG_USE_DBUS)
|
||||
target_link_libraries(KF6ConfigCore PRIVATE Qt6::DBus)
|
||||
endif()
|
||||
|
||||
ecm_generate_headers(KConfigCore_HEADERS
|
||||
HEADER_NAMES
|
||||
KAuthorized
|
||||
KConfig
|
||||
KConfigBase
|
||||
KConfigGroup
|
||||
KDesktopFile
|
||||
KDesktopFileAction
|
||||
KSharedConfig
|
||||
KCoreConfigSkeleton
|
||||
KEMailSettings
|
||||
KConfigWatcher
|
||||
|
||||
REQUIRED_HEADERS KConfigCore_HEADERS
|
||||
)
|
||||
|
||||
list(APPEND KConfigCore_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kconfigconversioncheck_p.h # helper header included by kconfiggroup.h
|
||||
)
|
||||
|
||||
install(TARGETS KF6ConfigCore EXPORT KF6ConfigTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kconfigcore_export.h
|
||||
${KConfigCore_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KConfigCore COMPONENT Devel
|
||||
)
|
||||
|
||||
# make available to ecm_add_qch in parent folder
|
||||
set(KConfigCore_APIDOX_SRCS ${KConfigCore_HEADERS} PARENT_SCOPE)
|
||||
set(KConfigCore_APIDOX_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#cmakedefine01 KCONFIG_USE_DBUS
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
|
||||
#include <QString>
|
||||
|
||||
inline QString kconfigDBusSanitizePath(QString path)
|
||||
{
|
||||
for (auto &character : path) {
|
||||
if ((std::isalnum(character.toLatin1(), std::locale::classic()) == 0) && character != QLatin1Char('_') && character != QLatin1Char('/')) {
|
||||
character = QLatin1Char('_');
|
||||
}
|
||||
}
|
||||
// KConfig notifying or watching on / makes no sense
|
||||
Q_ASSERT_X(path.length() > 1, Q_FUNC_INFO, qUtf8Printable(path));
|
||||
// As per spec the path must start with a slash
|
||||
Q_ASSERT_X(path.at(0) == QLatin1Char('/'), Q_FUNC_INFO, qUtf8Printable(path));
|
||||
// ...but not end with a slash
|
||||
Q_ASSERT_X(path.at(path.size() - 1) != QLatin1Char('/'), Q_FUNC_INFO, qUtf8Printable(path));
|
||||
// ...it also must not contain multiple slashes in sequence
|
||||
Q_ASSERT_X(!path.contains(QLatin1String("//")), Q_FUNC_INFO, qUtf8Printable(path));
|
||||
return path;
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
SPDX-FileCopyrightText: 1998, 1999, 2000 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kauthorized.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
#include "kconfig_core_log_settings.h"
|
||||
#include <QCoreApplication>
|
||||
#include <ksharedconfig.h>
|
||||
#include <stdlib.h> // srand(), rand()
|
||||
#ifndef Q_OS_WIN
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <kconfiggroup.h>
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
extern bool kde_kiosk_exception;
|
||||
|
||||
class URLActionRule
|
||||
{
|
||||
public:
|
||||
#define checkExactMatch(s, b) \
|
||||
if (s.isEmpty()) \
|
||||
b = true; \
|
||||
else if (s[s.length() - 1] == QLatin1Char('!')) { \
|
||||
b = false; \
|
||||
s.chop(1); \
|
||||
} else \
|
||||
b = true;
|
||||
#define checkStartWildCard(s, b) \
|
||||
if (s.isEmpty()) \
|
||||
b = true; \
|
||||
else if (s[0] == QLatin1Char('*')) { \
|
||||
b = true; \
|
||||
s.remove(0, 1); \
|
||||
} else \
|
||||
b = false;
|
||||
#define checkEqual(s, b) b = (s == QLatin1String("="));
|
||||
|
||||
URLActionRule(const QByteArray &act,
|
||||
const QString &bProt,
|
||||
const QString &bHost,
|
||||
const QString &bPath,
|
||||
const QString &dProt,
|
||||
const QString &dHost,
|
||||
const QString &dPath,
|
||||
bool perm)
|
||||
: action(act)
|
||||
, baseProt(bProt)
|
||||
, baseHost(bHost)
|
||||
, basePath(bPath)
|
||||
, destProt(dProt)
|
||||
, destHost(dHost)
|
||||
, destPath(dPath)
|
||||
, permission(perm)
|
||||
{
|
||||
checkExactMatch(baseProt, baseProtWildCard);
|
||||
checkStartWildCard(baseHost, baseHostWildCard);
|
||||
checkExactMatch(basePath, basePathWildCard);
|
||||
checkExactMatch(destProt, destProtWildCard);
|
||||
checkStartWildCard(destHost, destHostWildCard);
|
||||
checkExactMatch(destPath, destPathWildCard);
|
||||
checkEqual(destProt, destProtEqual);
|
||||
checkEqual(destHost, destHostEqual);
|
||||
}
|
||||
|
||||
bool baseMatch(const QUrl &url, const QString &protClass) const
|
||||
{
|
||||
if (baseProtWildCard) {
|
||||
if (!baseProt.isEmpty() //
|
||||
&& !url.scheme().startsWith(baseProt) //
|
||||
&& (protClass.isEmpty() || (protClass != baseProt))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.scheme() != baseProt //
|
||||
&& (protClass.isEmpty() || (protClass != baseProt))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (baseHostWildCard) {
|
||||
if (!baseHost.isEmpty() && !url.host().endsWith(baseHost)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.host() != baseHost) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (basePathWildCard) {
|
||||
if (!basePath.isEmpty() && !url.path().startsWith(basePath)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.path() != basePath) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool destMatch(const QUrl &url, const QString &protClass, const QUrl &base, const QString &baseClass) const
|
||||
{
|
||||
if (destProtEqual) {
|
||||
if (url.scheme() != base.scheme() //
|
||||
&& (protClass.isEmpty() || baseClass.isEmpty() || protClass != baseClass)) {
|
||||
return false;
|
||||
}
|
||||
} else if (destProtWildCard) {
|
||||
if (!destProt.isEmpty() //
|
||||
&& !url.scheme().startsWith(destProt) //
|
||||
&& (protClass.isEmpty() || (protClass != destProt))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.scheme() != destProt //
|
||||
&& (protClass.isEmpty() || (protClass != destProt))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (destHostWildCard) {
|
||||
if (!destHost.isEmpty() && !url.host().endsWith(destHost)) {
|
||||
return false;
|
||||
}
|
||||
} else if (destHostEqual) {
|
||||
if (url.host() != base.host()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.host() != destHost) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (destPathWildCard) {
|
||||
if (!destPath.isEmpty() && !url.path().startsWith(destPath)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (url.path() != destPath) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray action;
|
||||
QString baseProt;
|
||||
QString baseHost;
|
||||
QString basePath;
|
||||
QString destProt;
|
||||
QString destHost;
|
||||
QString destPath;
|
||||
bool baseProtWildCard : 1;
|
||||
bool baseHostWildCard : 1;
|
||||
bool basePathWildCard : 1;
|
||||
bool destProtWildCard : 1;
|
||||
bool destHostWildCard : 1;
|
||||
bool destPathWildCard : 1;
|
||||
bool destProtEqual : 1;
|
||||
bool destHostEqual : 1;
|
||||
bool permission;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(URLActionRule, Q_RELOCATABLE_TYPE);
|
||||
|
||||
class KAuthorizedPrivate
|
||||
{
|
||||
public:
|
||||
KAuthorizedPrivate()
|
||||
: actionRestrictions(false)
|
||||
, blockEverything(false)
|
||||
{
|
||||
Q_ASSERT_X(QCoreApplication::instance(), "KAuthorizedPrivate()", "There has to be an existing QCoreApplication::instance() pointer");
|
||||
|
||||
KSharedConfig::Ptr config = KSharedConfig::openConfig();
|
||||
|
||||
Q_ASSERT_X(config, "KAuthorizedPrivate()", "There has to be an existing KSharedConfig::openConfig() pointer");
|
||||
if (!config) {
|
||||
blockEverything = true;
|
||||
return;
|
||||
}
|
||||
actionRestrictions = config->hasGroup(QStringLiteral("KDE Action Restrictions")) && !kde_kiosk_exception;
|
||||
}
|
||||
|
||||
~KAuthorizedPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
bool actionRestrictions : 1;
|
||||
bool blockEverything : 1;
|
||||
QList<URLActionRule> urlActionRestrictions;
|
||||
QRecursiveMutex mutex;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(KAuthorizedPrivate, authPrivate)
|
||||
#define KAUTHORIZED_D KAuthorizedPrivate *d = authPrivate()
|
||||
|
||||
KAuthorized::KAuthorized()
|
||||
: QObject(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool KAuthorized::authorize(const QString &genericAction)
|
||||
{
|
||||
KAUTHORIZED_D;
|
||||
if (d->blockEverything) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->actionRestrictions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Action Restrictions"));
|
||||
return cg.readEntry(genericAction, true);
|
||||
}
|
||||
|
||||
bool KAuthorized::authorize(KAuthorized::GenericRestriction action)
|
||||
{
|
||||
const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericRestriction>();
|
||||
|
||||
if (metaEnum.isValid() && action != 0) {
|
||||
return KAuthorized::authorize(QString::fromLatin1(metaEnum.valueToKey(action)).toLower());
|
||||
}
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericRestriction requested" << action;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAuthorized::authorizeAction(const QString &action)
|
||||
{
|
||||
KAUTHORIZED_D;
|
||||
if (d->blockEverything) {
|
||||
return false;
|
||||
}
|
||||
if (!d->actionRestrictions || action.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return authorize(QLatin1String("action/") + action);
|
||||
}
|
||||
|
||||
bool KAuthorized::authorizeAction(KAuthorized::GenericAction action)
|
||||
{
|
||||
const QMetaEnum metaEnum = QMetaEnum::fromType<KAuthorized::GenericAction>();
|
||||
if (metaEnum.isValid() && action != 0) {
|
||||
return KAuthorized::authorizeAction(QString::fromLatin1(metaEnum.valueToKey(action)).toLower());
|
||||
}
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Invalid GenericAction requested" << action;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAuthorized::authorizeControlModule(const QString &menuId)
|
||||
{
|
||||
if (menuId.isEmpty() || kde_kiosk_exception) {
|
||||
return true;
|
||||
}
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE Control Module Restrictions"));
|
||||
return cg.readEntry(menuId, true);
|
||||
}
|
||||
|
||||
// Exported for unittests (e.g. in KIO, we're missing tests for this in kconfig)
|
||||
KCONFIGCORE_EXPORT void loadUrlActionRestrictions(const KConfigGroup &cg)
|
||||
{
|
||||
KAUTHORIZED_D;
|
||||
const QString Any;
|
||||
|
||||
d->urlActionRestrictions.clear();
|
||||
d->urlActionRestrictions.append(URLActionRule("open", Any, Any, Any, Any, Any, Any, true));
|
||||
d->urlActionRestrictions.append(URLActionRule("list", Any, Any, Any, Any, Any, Any, true));
|
||||
// TEST:
|
||||
// d->urlActionRestrictions.append(
|
||||
// URLActionRule("list", Any, Any, Any, Any, Any, Any, false));
|
||||
// d->urlActionRestrictions.append(
|
||||
// URLActionRule("list", Any, Any, Any, "file", Any, QDir::homePath(), true));
|
||||
d->urlActionRestrictions.append(URLActionRule("link", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true));
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral(":internet"), Any, Any, true));
|
||||
|
||||
// We allow redirections to file: but not from internet protocols, redirecting to file:
|
||||
// is very popular among KIO workers and we don't want to break them
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("file"), Any, Any, true));
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":internet"), Any, Any, QStringLiteral("file"), Any, Any, false));
|
||||
|
||||
// local protocols may redirect everywhere
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral(":local"), Any, Any, Any, Any, Any, true));
|
||||
|
||||
// Anyone may redirect to about:
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("about"), Any, Any, true));
|
||||
|
||||
// Anyone may redirect to mailto:
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("mailto"), Any, Any, true));
|
||||
|
||||
// Anyone may redirect to itself, cq. within it's own group
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", Any, Any, Any, QStringLiteral("="), Any, Any, true));
|
||||
|
||||
d->urlActionRestrictions.append(URLActionRule("redirect", QStringLiteral("about"), Any, Any, Any, Any, Any, true));
|
||||
|
||||
int count = cg.readEntry("rule_count", 0);
|
||||
QString keyFormat = QStringLiteral("rule_%1");
|
||||
for (int i = 1; i <= count; i++) {
|
||||
QString key = keyFormat.arg(i);
|
||||
const QStringList rule = cg.readEntry(key, QStringList());
|
||||
if (rule.count() != 8) {
|
||||
continue;
|
||||
}
|
||||
const QByteArray action = rule[0].toLatin1();
|
||||
const QString refProt = rule[1];
|
||||
const QString refHost = rule[2];
|
||||
QString refPath = rule[3];
|
||||
const QString urlProt = rule[4];
|
||||
const QString urlHost = rule[5];
|
||||
QString urlPath = rule[6];
|
||||
const bool bEnabled = (rule[7].compare(QLatin1String("true"), Qt::CaseInsensitive) == 0);
|
||||
|
||||
if (refPath.startsWith(QLatin1String("$HOME"))) {
|
||||
refPath.replace(0, 5, QDir::homePath());
|
||||
} else if (refPath.startsWith(QLatin1Char('~'))) {
|
||||
refPath.replace(0, 1, QDir::homePath());
|
||||
}
|
||||
if (urlPath.startsWith(QLatin1String("$HOME"))) {
|
||||
urlPath.replace(0, 5, QDir::homePath());
|
||||
} else if (urlPath.startsWith(QLatin1Char('~'))) {
|
||||
urlPath.replace(0, 1, QDir::homePath());
|
||||
}
|
||||
|
||||
if (refPath.startsWith(QLatin1String("$TMP"))) {
|
||||
refPath.replace(0, 4, QDir::tempPath());
|
||||
}
|
||||
if (urlPath.startsWith(QLatin1String("$TMP"))) {
|
||||
urlPath.replace(0, 4, QDir::tempPath());
|
||||
}
|
||||
|
||||
d->urlActionRestrictions.append(URLActionRule(action, refProt, refHost, refPath, urlProt, urlHost, urlPath, bEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
namespace KAuthorizedInternal
|
||||
{
|
||||
/**
|
||||
* Helper for KAuthorized::allowUrlAction in KIO
|
||||
* @private
|
||||
*/
|
||||
KCONFIGCORE_EXPORT void allowUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL)
|
||||
{
|
||||
KAUTHORIZED_D;
|
||||
QMutexLocker locker((&d->mutex));
|
||||
|
||||
const QString basePath = _baseURL.adjusted(QUrl::StripTrailingSlash).path();
|
||||
const QString destPath = _destURL.adjusted(QUrl::StripTrailingSlash).path();
|
||||
|
||||
d->urlActionRestrictions.append(
|
||||
URLActionRule(action.toLatin1(), _baseURL.scheme(), _baseURL.host(), basePath, _destURL.scheme(), _destURL.host(), destPath, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for KAuthorized::authorizeUrlAction in KIO
|
||||
* @private
|
||||
*/
|
||||
KCONFIGCORE_EXPORT bool
|
||||
authorizeUrlAction(const QString &action, const QUrl &_baseURL, const QUrl &_destURL, const QString &baseClass, const QString &destClass)
|
||||
{
|
||||
KAUTHORIZED_D;
|
||||
QMutexLocker locker(&(d->mutex));
|
||||
if (d->blockEverything) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_destURL.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (d->urlActionRestrictions.isEmpty()) {
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE URL Restrictions"));
|
||||
loadUrlActionRestrictions(cg);
|
||||
}
|
||||
|
||||
QUrl baseURL(_baseURL);
|
||||
baseURL.setPath(QDir::cleanPath(baseURL.path()));
|
||||
|
||||
QUrl destURL(_destURL);
|
||||
destURL.setPath(QDir::cleanPath(destURL.path()));
|
||||
|
||||
for (const URLActionRule &rule : std::as_const(d->urlActionRestrictions)) {
|
||||
if ((result != rule.permission) && // No need to check if it doesn't make a difference
|
||||
(action == QLatin1String(rule.action.constData())) && rule.baseMatch(baseURL, baseClass)
|
||||
&& rule.destMatch(destURL, destClass, baseURL, baseClass)) {
|
||||
result = rule.permission;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#include "moc_kauthorized.cpp"
|
||||
@@ -0,0 +1,174 @@
|
||||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
SPDX-FileCopyrightText: 1998, 1999 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KAUTHORIZED_H
|
||||
#define KAUTHORIZED_H
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
#include <QMetaEnum>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class QUrl;
|
||||
class QString;
|
||||
class QQmlEngine;
|
||||
class QJSEngine;
|
||||
|
||||
/**
|
||||
* The functions in this namespace provide the core of the Kiosk action
|
||||
* restriction system; the KIO and KXMLGui frameworks build on this.
|
||||
*
|
||||
* The relevant settings are read from the application's KSharedConfig
|
||||
* instance, so actions can be disabled on a per-application or global
|
||||
* basis (by using the kdeglobals file).
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KAuthorized : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* The enum values lower cased represent the action that is authorized
|
||||
* For example the SHELL_ACCESS value is converted to the "shell_access" string.
|
||||
*
|
||||
* @since 5.88
|
||||
*/
|
||||
enum GenericRestriction {
|
||||
SHELL_ACCESS = 1, // if the user is authorized to open a shell or execute shell commands
|
||||
GHNS, /// if the collaborative data sharing framework KNewStuff is authorized
|
||||
// GUI behavior
|
||||
LINEEDIT_REVEAL_PASSWORD, /// if typed characters in password fields can be made visible
|
||||
LINEEDIT_TEXT_COMPLETION, /// if line edits should be allowed to display completions
|
||||
MOVABLE_TOOLBARS, /// if toolbars of apps should be movable
|
||||
RUN_DESKTOP_FILES, /// if .desktop files should be run as executables when clicked
|
||||
};
|
||||
Q_ENUM(GenericRestriction)
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 5.88
|
||||
*/
|
||||
enum GenericAction {
|
||||
OPEN_WITH = 1, /// if the open-with menu should be shown for files etc.
|
||||
EDITFILETYPE, /// if mime-type accociations are allowed to be configured
|
||||
|
||||
OPTIONS_SHOW_TOOLBAR, /// if the toolbar should be displayed in apps
|
||||
SWITCH_APPLICATION_LANGUAGE, /// if an action to switch the app language should be shown
|
||||
BOOKMARKS, /// saving bookmarks is allowed
|
||||
};
|
||||
Q_ENUM(GenericAction)
|
||||
|
||||
/**
|
||||
* Returns whether the user is permitted to perform a certain action.
|
||||
*
|
||||
* All settings are read from the "[KDE Action Restrictions]" group.
|
||||
* For example, if kdeglobals contains
|
||||
* @verbatim
|
||||
[KDE Action Restrictions][$i]
|
||||
shell_access=false
|
||||
@endverbatim
|
||||
* then
|
||||
* @code
|
||||
* KAuthorized::authorize("shell_access");
|
||||
* @endcode
|
||||
* will return @c false.
|
||||
*
|
||||
* This method is intended for actions that do not necessarily have a
|
||||
* one-to-one correspondence with a menu or toolbar item (ie: a QAction
|
||||
* in a KXMLGui application). "shell_access" is an example of such a
|
||||
* "generic" action.
|
||||
*
|
||||
* The convention for actions like "File->New" is to prepend the action
|
||||
* name with "action/", for example "action/file_new". This is what
|
||||
* authorizeAction() does.
|
||||
*
|
||||
* @param action The name of the action.
|
||||
* @return @c true if the action is authorized, @c false
|
||||
* otherwise.
|
||||
*
|
||||
* @see authorizeAction()
|
||||
*/
|
||||
Q_INVOKABLE static bool authorize(const QString &action);
|
||||
|
||||
/**
|
||||
* Returns whether the user is permitted to perform a common action.
|
||||
* The enum values lower cased represent the action that is
|
||||
* passed in to @p authorize(QString)
|
||||
*
|
||||
* @overload
|
||||
* @since 5.88
|
||||
*/
|
||||
Q_INVOKABLE static bool authorize(GenericRestriction action);
|
||||
|
||||
/**
|
||||
* Returns whether the user is permitted to perform a certain action.
|
||||
*
|
||||
* This behaves like authorize(), except that "action/" is prepended to
|
||||
* @p action. So if kdeglobals contains
|
||||
* @verbatim
|
||||
[KDE Action Restrictions][$i]
|
||||
action/file_new=false
|
||||
@endverbatim
|
||||
* then
|
||||
* @code
|
||||
* KAuthorized::authorizeAction("file_new");
|
||||
* @endcode
|
||||
* will return @c false.
|
||||
*
|
||||
* KXMLGui-based applications should not normally need to call this
|
||||
* function, as KActionCollection will do it automatically.
|
||||
*
|
||||
* @param action The name of a QAction action.
|
||||
* @return @c true if the QAction is authorized, @c false
|
||||
* otherwise.
|
||||
* @since 5.24
|
||||
*
|
||||
* @see authorize()
|
||||
*/
|
||||
Q_INVOKABLE static bool authorizeAction(const QString &action);
|
||||
|
||||
/**
|
||||
* Overload to authorize common actions.
|
||||
*
|
||||
* @overload
|
||||
* @since 5.88
|
||||
*/
|
||||
Q_INVOKABLE static bool authorizeAction(GenericAction action);
|
||||
|
||||
/**
|
||||
* Returns whether the user is permitted to use a certain control module.
|
||||
*
|
||||
* All settings are read from the "[KDE Control Module Restrictions]"
|
||||
* group. For example, if kdeglobals contains
|
||||
* @verbatim
|
||||
[KDE Control Module Restrictions][$i]
|
||||
kcm_desktop-settings=false
|
||||
@endverbatim
|
||||
* then
|
||||
* @code
|
||||
* KAuthorized::authorizeControlModule("kcm_desktop-settings");
|
||||
* @endcode
|
||||
* will return @c false.
|
||||
*
|
||||
* @param pluginId The desktop menu ID for the control module.
|
||||
* @return @c true if access to the module is authorized, @c false otherwise.
|
||||
*
|
||||
*/
|
||||
Q_INVOKABLE static bool authorizeControlModule(const QString &pluginId);
|
||||
|
||||
static KAuthorized *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
return new KAuthorized;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class KConfigQmlPlugin;
|
||||
explicit KAuthorized();
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIG_H
|
||||
#define KCONFIG_H
|
||||
|
||||
#include "kconfigbase.h"
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
class KConfigGroup;
|
||||
class KConfigPrivate;
|
||||
|
||||
/**
|
||||
* \class KConfig kconfig.h <KConfig>
|
||||
*
|
||||
* \brief The central class of the KDE configuration data system.
|
||||
*
|
||||
* Quickstart:
|
||||
*
|
||||
* Get the default application config object via KSharedConfig::openConfig().
|
||||
*
|
||||
* Load a specific configuration file:
|
||||
* \code
|
||||
* KConfig config("/etc/kderc", KConfig::SimpleConfig);
|
||||
* \endcode
|
||||
*
|
||||
* Load the configuration of a specific component:
|
||||
* \code
|
||||
* KConfig config("pluginrc");
|
||||
* \endcode
|
||||
*
|
||||
* In general it is recommended to use KSharedConfig instead of
|
||||
* creating multiple instances of KConfig to avoid the overhead of
|
||||
* separate objects or concerns about synchronizing writes to disk
|
||||
* even if the configuration object is updated from multiple code paths.
|
||||
* KSharedConfig provides a set of open methods as counterparts for the
|
||||
* KConfig constructors.
|
||||
*
|
||||
* \sa KSharedConfig, KConfigGroup, <a href="https://techbase.kde.org/index.php?title=Development/Tutorials/KConfig">the techbase HOWTO on KConfig</a>.
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KConfig : public KConfigBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Determines how the system-wide and user's global settings will affect
|
||||
* the reading of the configuration.
|
||||
*
|
||||
* If CascadeConfig is selected, system-wide configuration sources are used
|
||||
* to provide defaults for the settings accessed through this object, or
|
||||
* possibly to override those settings in certain cases.
|
||||
*
|
||||
* If IncludeGlobals is selected, the kdeglobals configuration is used
|
||||
* as additional configuration sources to provide defaults. Additionally
|
||||
* selecting CascadeConfig will result in the system-wide kdeglobals sources
|
||||
* also getting included.
|
||||
*
|
||||
* Note that the main configuration source overrides the cascaded sources,
|
||||
* which override those provided to addConfigSources(), which override the
|
||||
* global sources. The exception is that if a key or group is marked as
|
||||
* being immutable, it will not be overridden.
|
||||
*
|
||||
* Note that all values other than IncludeGlobals and CascadeConfig are
|
||||
* convenience definitions for the basic mode.
|
||||
* Do @em not combine them with anything.
|
||||
* @see OpenFlags
|
||||
*/
|
||||
enum OpenFlag {
|
||||
IncludeGlobals = 0x01, ///< Blend kdeglobals into the config object.
|
||||
CascadeConfig = 0x02, ///< Cascade to system-wide config files.
|
||||
|
||||
SimpleConfig = 0x00, ///< Just a single config file.
|
||||
NoCascade = IncludeGlobals, ///< Include user's globals, but omit system settings.
|
||||
NoGlobals = CascadeConfig, ///< Cascade to system settings, but omit user's globals.
|
||||
FullConfig = IncludeGlobals | CascadeConfig, ///< Fully-fledged config, including globals and cascading to system settings
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #OpenFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(OpenFlags, OpenFlag)
|
||||
|
||||
/**
|
||||
* Creates a KConfig object to manipulate a configuration file for the
|
||||
* current application.
|
||||
*
|
||||
* If an absolute path is specified for @p file, that file will be used
|
||||
* as the store for the configuration settings. If a non-absolute path
|
||||
* is provided, the file will be looked for in the standard directory
|
||||
* specified by type. If no path is provided, a default
|
||||
* configuration file will be used based on the name of the main
|
||||
* application component.
|
||||
*
|
||||
* @p mode determines whether the user or global settings will be allowed
|
||||
* to influence the values returned by this object. See OpenFlags for
|
||||
* more details.
|
||||
*
|
||||
* @note You probably want to use KSharedConfig::openConfig instead.
|
||||
*
|
||||
* @param file the name of the file. If an empty string is passed in
|
||||
* and SimpleConfig is passed in for the OpenFlags, then an in-memory
|
||||
* KConfig object is created which will not write out to file nor which
|
||||
* requires any file in the filesystem at all.
|
||||
* @param mode how global settings should affect the configuration
|
||||
* options exposed by this KConfig object
|
||||
* @param type The standard directory to look for the configuration
|
||||
* file in
|
||||
*
|
||||
* @sa KSharedConfig::openConfig(const QString&, OpenFlags, QStandardPaths::StandardLocation)
|
||||
*/
|
||||
explicit KConfig(const QString &file = QString(),
|
||||
OpenFlags mode = FullConfig,
|
||||
QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation);
|
||||
|
||||
#if KCONFIGCORE_ENABLE_DEPRECATED_SINCE(6, 3)
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Creates a KConfig object using the specified backend. If the backend can not
|
||||
* be found or loaded, then the standard configuration parser is used as a fallback.
|
||||
*
|
||||
* @param file the file to be parsed
|
||||
* @param backend the backend to load
|
||||
* @param type where to look for the file if an absolute path is not provided
|
||||
*
|
||||
* @since 4.1
|
||||
*
|
||||
* @deprecated since 6.3, use other constructor
|
||||
*/
|
||||
KCONFIGCORE_DEPRECATED_VERSION(6, 3, "Use other constructor")
|
||||
KConfig(const QString &file, const QString &backend, QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation);
|
||||
#endif
|
||||
|
||||
~KConfig() override;
|
||||
|
||||
/**
|
||||
* Returns the standard location enum passed to the constructor.
|
||||
* Used by KSharedConfig.
|
||||
* @since 5.0
|
||||
*/
|
||||
QStandardPaths::StandardLocation locationType() const;
|
||||
|
||||
/**
|
||||
* Returns the filename used to store the configuration.
|
||||
*/
|
||||
QString name() const;
|
||||
|
||||
/**
|
||||
* @return the flags this object was opened with
|
||||
* @since 5.3
|
||||
*/
|
||||
OpenFlags openFlags() const;
|
||||
|
||||
/// @reimp
|
||||
bool sync() override;
|
||||
|
||||
/// Returns true if sync has any changes to write out.
|
||||
/// @since 4.12
|
||||
bool isDirty() const;
|
||||
|
||||
/// @reimp
|
||||
void markAsClean() override;
|
||||
|
||||
/// @{ configuration object state
|
||||
/// @reimp
|
||||
AccessMode accessMode() const override;
|
||||
|
||||
/**
|
||||
* Whether the configuration can be written to.
|
||||
*
|
||||
* If @p warnUser is true and the configuration cannot be
|
||||
* written to (ie: this method returns @c false), a warning
|
||||
* message box will be shown to the user telling them to
|
||||
* contact their system administrator to get the problem fixed.
|
||||
*
|
||||
* The most likely cause for this method returning @c false
|
||||
* is that the user does not have write permission for the
|
||||
* configuration file.
|
||||
*
|
||||
* @param warnUser whether to show a warning message to the user
|
||||
* if the configuration cannot be written to
|
||||
*
|
||||
* @returns true if the configuration can be written to, false
|
||||
* if the configuration cannot be written to
|
||||
*/
|
||||
bool isConfigWritable(bool warnUser);
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* Copies all entries from this config object to a new config
|
||||
* object that will save itself to @p file.
|
||||
*
|
||||
* The configuration will not actually be saved to @p file
|
||||
* until the returned object is destroyed, or sync() is called
|
||||
* on it.
|
||||
*
|
||||
* Do not forget to delete the returned KConfig object if
|
||||
* @p config was 0.
|
||||
*
|
||||
* @param file the new config object will save itself to
|
||||
* @param config if not 0, copy to the given KConfig object rather
|
||||
* than creating a new one
|
||||
*
|
||||
* @return @p config if it was set, otherwise a new KConfig object
|
||||
*/
|
||||
KConfig *copyTo(const QString &file, KConfig *config = nullptr) const;
|
||||
|
||||
/**
|
||||
* Ensures that the configuration file contains a certain update.
|
||||
*
|
||||
* If the configuration file does not contain the update @p id
|
||||
* as contained in @p updateFile, kconf_update is run to update
|
||||
* the configuration file.
|
||||
*
|
||||
* If you install config update files with critical fixes
|
||||
* you may wish to use this method to verify that a critical
|
||||
* update has indeed been performed to catch the case where
|
||||
* a user restores an old config file from backup that has
|
||||
* not been updated yet.
|
||||
*
|
||||
* @param id the update to check
|
||||
* @param updateFile the file containing the update
|
||||
*/
|
||||
void checkUpdate(const QString &id, const QString &updateFile);
|
||||
|
||||
/**
|
||||
* Updates the state of this object to match the persistent storage.
|
||||
* Note that if this object has pending changes, this method will
|
||||
* call sync() first so as not to lose those changes.
|
||||
*/
|
||||
void reparseConfiguration();
|
||||
|
||||
/// @{ extra config files
|
||||
/**
|
||||
* Adds the list of configuration sources to the merge stack.
|
||||
*
|
||||
* Currently only files are accepted as configuration sources.
|
||||
*
|
||||
* The first entry in @p sources is treated as the most general and will
|
||||
* be overridden by the second entry. The settings in the final entry
|
||||
* in @p sources will override all the other sources provided in the list.
|
||||
*
|
||||
* The settings in @p sources will also be overridden by the sources
|
||||
* provided by any previous calls to addConfigSources().
|
||||
*
|
||||
* The settings in the global configuration sources will be overridden by
|
||||
* the sources provided to this method (@see IncludeGlobals).
|
||||
* All the sources provided to any call to this method will be overridden
|
||||
* by any files that cascade from the source provided to the constructor
|
||||
* (@see CascadeConfig), which will in turn be
|
||||
* overridden by the source provided to the constructor.
|
||||
*
|
||||
* Note that only the most specific file, ie: the file provided to the
|
||||
* constructor, will be written to by this object.
|
||||
*
|
||||
* The state is automatically updated by this method, so there is no need to call
|
||||
* reparseConfiguration().
|
||||
*
|
||||
* @param sources A list of extra config sources.
|
||||
*/
|
||||
void addConfigSources(const QStringList &sources);
|
||||
|
||||
/**
|
||||
* Returns a list of the additional configuration sources used in this object
|
||||
*/
|
||||
QStringList additionalConfigSources() const;
|
||||
|
||||
/// @}
|
||||
/// @{ locales
|
||||
/**
|
||||
* Returns the current locale.
|
||||
*/
|
||||
QString locale() const;
|
||||
/**
|
||||
* Sets the locale to @p aLocale.
|
||||
*
|
||||
* The global locale is used by default.
|
||||
*
|
||||
* @note If set to the empty string, @b no locale will be matched. This effectively disables
|
||||
* reading translated entries.
|
||||
*
|
||||
* @return @c true if locale was changed, @c false if the call had no
|
||||
* effect (eg: @p aLocale was already the current locale for this
|
||||
* object)
|
||||
*/
|
||||
bool setLocale(const QString &aLocale);
|
||||
/// @}
|
||||
|
||||
/// @{ defaults
|
||||
/**
|
||||
* When set, all readEntry calls return the system-wide (default) values
|
||||
* instead of the user's settings.
|
||||
*
|
||||
* This is off by default.
|
||||
*
|
||||
* @param b whether to read the system-wide defaults instead of the
|
||||
* user's settings
|
||||
*/
|
||||
void setReadDefaults(bool b);
|
||||
/**
|
||||
* @returns @c true if the system-wide defaults will be read instead of the
|
||||
* user's settings
|
||||
*/
|
||||
bool readDefaults() const;
|
||||
/// @}
|
||||
|
||||
/// @{ immutability
|
||||
/// @reimp
|
||||
bool isImmutable() const override;
|
||||
/// @}
|
||||
|
||||
/// @reimp
|
||||
QStringList groupList() const override;
|
||||
|
||||
/**
|
||||
* Returns a map (tree) of entries in a particular group.
|
||||
*
|
||||
* The entries are all returned as strings.
|
||||
*
|
||||
* @param aGroup The group to get entries from.
|
||||
*
|
||||
* @return A map of entries in the group specified, indexed by key.
|
||||
* The returned map may be empty if the group is empty, or not found.
|
||||
* @see QMap
|
||||
*/
|
||||
QMap<QString, QString> entryMap(const QString &aGroup = QString()) const;
|
||||
|
||||
/**
|
||||
* Sets the name of the application config file.
|
||||
* @since 5.0
|
||||
*/
|
||||
static void setMainConfigName(const QString &str);
|
||||
|
||||
/**
|
||||
* Get the name of application config file.
|
||||
* @since 5.93
|
||||
*/
|
||||
static QString mainConfigName();
|
||||
|
||||
protected:
|
||||
bool hasGroupImpl(const QString &groupName) const override;
|
||||
KConfigGroup groupImpl(const QString &groupName) override;
|
||||
const KConfigGroup groupImpl(const QString &groupName) const override;
|
||||
void deleteGroupImpl(const QString &groupName, WriteConfigFlags flags = Normal) override;
|
||||
bool isGroupImmutableImpl(const QString &groupName) const override;
|
||||
|
||||
friend class KConfigGroup;
|
||||
friend class KConfigGroupPrivate;
|
||||
friend class KSharedConfig;
|
||||
|
||||
/** Virtual hook, used to add new "virtual" functions while maintaining
|
||||
* binary compatibility. Unused in this class.
|
||||
*/
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
KConfigPrivate *const d_ptr;
|
||||
|
||||
KCONFIGCORE_NO_EXPORT explicit KConfig(KConfigPrivate &d);
|
||||
|
||||
private:
|
||||
friend class KConfigTest;
|
||||
|
||||
Q_DISABLE_COPY(KConfig)
|
||||
|
||||
Q_DECLARE_PRIVATE(KConfig)
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KConfig::OpenFlags)
|
||||
|
||||
#endif // KCONFIG_H
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIG_P_H
|
||||
#define KCONFIG_P_H
|
||||
|
||||
#include "kconfig.h"
|
||||
#include "kconfigdata_p.h"
|
||||
#include "kconfiggroup.h"
|
||||
#include "kconfigini_p.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStack>
|
||||
#include <QStringList>
|
||||
|
||||
class KConfigPrivate
|
||||
{
|
||||
friend class KConfig;
|
||||
|
||||
public:
|
||||
KConfig::OpenFlags openFlags;
|
||||
QStandardPaths::StandardLocation resourceType;
|
||||
|
||||
void changeFileName(const QString &fileName);
|
||||
|
||||
// functions for KConfigGroup
|
||||
bool canWriteEntry(const QString &group, QAnyStringView key, bool isDefault = false) const;
|
||||
QString lookupData(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags, bool *expand) const;
|
||||
QByteArray lookupData(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const;
|
||||
KEntry lookupInternalEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const;
|
||||
|
||||
void putData(const QString &groupName, const char *key, const QByteArray &value, KConfigBase::WriteConfigFlags flags, bool expand = false);
|
||||
void setEntryData(const QString &groupName, const char *key, const QByteArray &value, KEntryMap::EntryOptions flags)
|
||||
{
|
||||
if (entryMap.setEntry(groupName, key, value, flags)) {
|
||||
bDirty = true;
|
||||
}
|
||||
}
|
||||
void revertEntry(const QString &group, QAnyStringView key, KConfigBase::WriteConfigFlags flags);
|
||||
/**
|
||||
* Returns a list of keys used with entries in a particular group.
|
||||
*
|
||||
* This does not include keys of deleted entries or those with default values.
|
||||
* I.e. all the keys for which there will be also entries in KConfig::entryMap().
|
||||
*
|
||||
* @return A sorted list of keys in the group specified.
|
||||
* The returned list may be empty if the group is empty, or not found.
|
||||
*/
|
||||
QStringList usedKeyList(const QString &theGroup) const;
|
||||
QStringList groupList(const QString &groupName) const;
|
||||
// copies the entries from @p source to @p otherGroup changing all occurrences
|
||||
// of @p source with @p destination
|
||||
void copyGroup(const QString &source, const QString &destination, KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) const;
|
||||
QList<QByteArray> keyListImpl(const QString &groupName) const;
|
||||
QSet<QString> allSubGroups(const QString &parentGroupName) const;
|
||||
bool hasNonDeletedEntries(const QString &groupName) const;
|
||||
|
||||
void notifyClients(const QHash<QString, QByteArrayList> &changes, const QString &path);
|
||||
|
||||
static QString expandString(const QString &value);
|
||||
|
||||
protected:
|
||||
QExplicitlySharedDataPointer<KConfigIniBackend> mBackend;
|
||||
|
||||
KConfigPrivate(KConfig::OpenFlags flags, QStandardPaths::StandardLocation type);
|
||||
|
||||
virtual ~KConfigPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
bool bDirty : 1;
|
||||
bool bReadDefaults : 1;
|
||||
bool bFileImmutable : 1;
|
||||
bool bForceGlobal : 1;
|
||||
bool bSuppressGlobal : 1;
|
||||
|
||||
static bool mappingsRegistered;
|
||||
|
||||
KEntryMap entryMap;
|
||||
QString backendType;
|
||||
QStack<QString> extraFiles;
|
||||
|
||||
QString locale;
|
||||
QString fileName;
|
||||
QString etc_kderc;
|
||||
KConfigBase::AccessMode configState;
|
||||
|
||||
bool wantGlobals() const
|
||||
{
|
||||
return openFlags & KConfig::IncludeGlobals && !bSuppressGlobal;
|
||||
}
|
||||
bool wantDefaults() const
|
||||
{
|
||||
return openFlags & KConfig::CascadeConfig;
|
||||
}
|
||||
bool isSimple() const
|
||||
{
|
||||
return openFlags == KConfig::SimpleConfig;
|
||||
}
|
||||
bool isReadOnly() const
|
||||
{
|
||||
return configState == KConfig::ReadOnly;
|
||||
}
|
||||
|
||||
bool setLocale(const QString &aLocale);
|
||||
QStringList getGlobalFiles() const;
|
||||
void parseGlobalFiles();
|
||||
void parseConfigFiles();
|
||||
void initCustomized(KConfig *);
|
||||
bool lockLocal();
|
||||
};
|
||||
|
||||
#endif // KCONFIG_P_H
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigbase.h"
|
||||
|
||||
#include "kconfiggroup.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
bool KConfigBase::hasGroup(const QString &group) const
|
||||
{
|
||||
return hasGroupImpl(group);
|
||||
}
|
||||
|
||||
KConfigGroup KConfigBase::group(const QString &str)
|
||||
{
|
||||
return groupImpl(str);
|
||||
}
|
||||
|
||||
const KConfigGroup KConfigBase::group(const QString &s) const
|
||||
{
|
||||
return groupImpl(s);
|
||||
}
|
||||
|
||||
void KConfigBase::deleteGroup(const QString &group, WriteConfigFlags flags)
|
||||
{
|
||||
deleteGroupImpl(group, flags);
|
||||
}
|
||||
|
||||
bool KConfigBase::isGroupImmutable(const QString &aGroup) const
|
||||
{
|
||||
return isGroupImmutableImpl(aGroup);
|
||||
}
|
||||
|
||||
KConfigBase::~KConfigBase()
|
||||
{
|
||||
}
|
||||
|
||||
KConfigBase::KConfigBase()
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigBase::virtual_hook(int, void *)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGBASE_H
|
||||
#define KCONFIGBASE_H
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
#include <QStringList>
|
||||
#include <QtGlobal>
|
||||
|
||||
class KConfigGroup;
|
||||
class KConfigBasePrivate;
|
||||
|
||||
/**
|
||||
* \class KConfigBase kconfigbase.h <KConfigBase>
|
||||
* \brief Interface to interact with configuration.
|
||||
*
|
||||
* KConfigBase allows a component of an application to persists its configuration
|
||||
* without the component knowing if it is storing the configuration into a top
|
||||
* level KConfig or a KConfigGroup inside a KConfig instance.
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KConfigBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Flags to control write entry
|
||||
* @see WriteConfigFlags
|
||||
*/
|
||||
enum WriteConfigFlag {
|
||||
Persistent = 0x01,
|
||||
/**<
|
||||
* Save this entry when saving the config object.
|
||||
*/
|
||||
Global = 0x02,
|
||||
/**<
|
||||
* Save the entry to the global %KDE config file instead of the
|
||||
* application specific config file.
|
||||
*/
|
||||
Localized = 0x04,
|
||||
/**<
|
||||
* Add the locale tag to the key when writing it.
|
||||
*/
|
||||
Notify = 0x08 | Persistent,
|
||||
/**<
|
||||
* Notify remote KConfigWatchers of changes (requires DBus support)
|
||||
* Implied persistent
|
||||
* @since 5.51
|
||||
*/
|
||||
Normal = Persistent,
|
||||
/**<
|
||||
* Save the entry to the application specific config file without
|
||||
* a locale tag. This is the default.
|
||||
*/
|
||||
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #WriteConfigFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(WriteConfigFlags, WriteConfigFlag)
|
||||
|
||||
/**
|
||||
* Destructs the KConfigBase object.
|
||||
*/
|
||||
virtual ~KConfigBase();
|
||||
|
||||
/**
|
||||
* Returns a list of groups that are known about.
|
||||
*
|
||||
* @return The list of groups.
|
||||
**/
|
||||
virtual QStringList groupList() const = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the specified group is known about.
|
||||
*
|
||||
* @param group name of group to search for
|
||||
* @return true if the group exists.
|
||||
*/
|
||||
bool hasGroup(const QString &group) const;
|
||||
|
||||
/**
|
||||
* Returns an object for the named subgroup.
|
||||
*
|
||||
* @param group the group to open. Pass an empty string here to the KConfig
|
||||
* object to obtain a handle on the root group.
|
||||
* @return config group object for the given group name.
|
||||
*/
|
||||
KConfigGroup group(const QString &group);
|
||||
|
||||
/**
|
||||
* Const overload for group(const QString&)
|
||||
*/
|
||||
const KConfigGroup group(const QString &group) const;
|
||||
|
||||
/**
|
||||
* Delete @p group.
|
||||
* This marks @p group as @em deleted in the config object. This effectively
|
||||
* removes any cascaded values from config files earlier in the stack.
|
||||
*/
|
||||
void deleteGroup(const QString &group, WriteConfigFlags flags = Normal);
|
||||
|
||||
/**
|
||||
* Syncs the configuration object that this group belongs to.
|
||||
* Unrelated concurrent changes to the same file are merged and thus
|
||||
* not overwritten. Note however, that this object is @em not automatically
|
||||
* updated with those changes.
|
||||
*/
|
||||
virtual bool sync() = 0;
|
||||
|
||||
/**
|
||||
* Reset the dirty flags of all entries in the entry map, so the
|
||||
* values will not be written to disk on a later call to sync().
|
||||
*/
|
||||
virtual void markAsClean() = 0;
|
||||
|
||||
/**
|
||||
* Possible return values for accessMode().
|
||||
*/
|
||||
enum AccessMode {
|
||||
NoAccess,
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the access mode of the app-config object.
|
||||
*
|
||||
* Possible return values
|
||||
* are NoAccess (the application-specific config file could not be
|
||||
* opened neither read-write nor read-only), ReadOnly (the
|
||||
* application-specific config file is opened read-only, but not
|
||||
* read-write) and ReadWrite (the application-specific config
|
||||
* file is opened read-write).
|
||||
*
|
||||
* @return the access mode of the app-config object
|
||||
*/
|
||||
virtual AccessMode accessMode() const = 0;
|
||||
|
||||
/**
|
||||
* Checks whether this configuration object can be modified.
|
||||
* @return whether changes may be made to this configuration object.
|
||||
*/
|
||||
virtual bool isImmutable() const = 0;
|
||||
|
||||
/**
|
||||
* Can changes be made to the entries in @p group?
|
||||
*
|
||||
* @param group The group to check for immutability.
|
||||
* @return @c false if the entries in @p group can be modified, otherwise @c true
|
||||
*/
|
||||
bool isGroupImmutable(const QString &group) const;
|
||||
|
||||
protected:
|
||||
KConfigBase();
|
||||
|
||||
/// @param groupName name of group
|
||||
virtual bool hasGroupImpl(const QString &groupName) const = 0;
|
||||
/// @param groupName name of group
|
||||
virtual KConfigGroup groupImpl(const QString &groupName) = 0;
|
||||
/// @param groupName name of group
|
||||
virtual const KConfigGroup groupImpl(const QString &groupName) const = 0;
|
||||
/// @param groupName name of group
|
||||
virtual void deleteGroupImpl(const QString &groupName, WriteConfigFlags flags = Normal) = 0;
|
||||
/// @param groupName name of group
|
||||
virtual bool isGroupImmutableImpl(const QString &groupName) const = 0;
|
||||
|
||||
/** Virtual hook, used to add new "virtual" functions while maintaining
|
||||
* binary compatibility. Unused in this class.
|
||||
*/
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigBase::WriteConfigFlags)
|
||||
|
||||
#endif // KCONFIG_H
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGBASE_P_H
|
||||
#define KCONFIGBASE_P_H
|
||||
|
||||
#include <QSharedData>
|
||||
|
||||
class KConfigBasePrivate : public QSharedData
|
||||
{
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006 Thomas Braxton <brax108@cox.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIG_CONVERSION_CHECK_P_H
|
||||
#define KCONFIG_CONVERSION_CHECK_P_H
|
||||
|
||||
#include <QDate>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
|
||||
class QColor;
|
||||
class QFont;
|
||||
|
||||
namespace KConfigConversionCheck
|
||||
{
|
||||
// used to distinguish between supported/unsupported types
|
||||
struct supported {
|
||||
};
|
||||
struct unsupported {
|
||||
};
|
||||
|
||||
// traits type class to define support for constraints
|
||||
template<typename T>
|
||||
struct QVconvertible {
|
||||
typedef unsupported toQString;
|
||||
typedef unsupported toQVariant;
|
||||
};
|
||||
|
||||
// constraint classes
|
||||
template<typename T>
|
||||
struct type_toQString {
|
||||
void constraint()
|
||||
{
|
||||
supported x = y;
|
||||
Q_UNUSED(x);
|
||||
}
|
||||
typename QVconvertible<T>::toQString y;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct type_toQVariant {
|
||||
void constraint()
|
||||
{
|
||||
supported x = y;
|
||||
Q_UNUSED(x);
|
||||
}
|
||||
typename QVconvertible<T>::toQVariant y;
|
||||
};
|
||||
|
||||
// check if T is convertible to QString thru QVariant
|
||||
// if not supported can't be used in QList<T> functions
|
||||
template<typename T>
|
||||
inline void to_QString()
|
||||
{
|
||||
void (type_toQString<T>::*x)() = &type_toQString<T>::constraint;
|
||||
Q_UNUSED(x);
|
||||
}
|
||||
|
||||
// check if T is convertible to QVariant & supported in readEntry/writeEntry
|
||||
template<typename T>
|
||||
inline void to_QVariant()
|
||||
{
|
||||
void (type_toQVariant<T>::*x)() = &type_toQVariant<T>::constraint;
|
||||
Q_UNUSED(x);
|
||||
}
|
||||
|
||||
// define for all types handled in readEntry/writeEntry
|
||||
// string_support - is supported by QVariant(type).toString(),
|
||||
// can be used in QList<T> functions
|
||||
// variant_support - has a QVariant constructor
|
||||
#define QVConversions(type, string_support, variant_support) \
|
||||
template<> \
|
||||
struct QVconvertible<type> { \
|
||||
typedef string_support toQString; \
|
||||
typedef variant_support toQVariant; \
|
||||
}
|
||||
|
||||
// The only types needed here are the types handled in readEntry/writeEntry
|
||||
// the default QVconvertible will take care of the rest.
|
||||
QVConversions(bool, supported, supported);
|
||||
QVConversions(int, supported, supported);
|
||||
QVConversions(unsigned int, supported, supported);
|
||||
QVConversions(long long, supported, supported);
|
||||
QVConversions(unsigned long long, supported, supported);
|
||||
QVConversions(float, supported, supported);
|
||||
QVConversions(double, supported, supported);
|
||||
QVConversions(QString, supported, supported);
|
||||
QVConversions(QColor, unsupported, supported);
|
||||
QVConversions(QFont, supported, supported);
|
||||
QVConversions(QDateTime, unsupported, supported);
|
||||
QVConversions(QDate, unsupported, supported);
|
||||
QVConversions(QSize, unsupported, supported);
|
||||
QVConversions(QRect, unsupported, supported);
|
||||
QVConversions(QPoint, unsupported, supported);
|
||||
QVConversions(QSizeF, unsupported, supported);
|
||||
QVConversions(QRectF, unsupported, supported);
|
||||
QVConversions(QPointF, unsupported, supported);
|
||||
QVConversions(QByteArray, supported, supported);
|
||||
QVConversions(QStringList, unsupported, supported);
|
||||
QVConversions(QVariantList, unsupported, supported);
|
||||
QVConversions(QUrl, supported, supported);
|
||||
QVConversions(QList<QUrl>, unsupported, supported);
|
||||
QVConversions(QUuid, supported, supported);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999-2000 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigdata_p.h"
|
||||
|
||||
QDebug operator<<(QDebug dbg, const KEntryKey &key)
|
||||
{
|
||||
dbg.nospace() << "[" << key.mGroup << ", " << key.mKey << (key.bLocal ? " localized" : "") << (key.bDefault ? " default" : "") << (key.bRaw ? " raw" : "")
|
||||
<< "]";
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const KEntry &entry)
|
||||
{
|
||||
dbg.nospace() << "[" << entry.mValue << (entry.bDirty ? " dirty" : "") << (entry.bGlobal ? " global" : "")
|
||||
<< (entry.bOverridesGlobal ? " overrides global" : "") << (entry.bImmutable ? " immutable" : "") << (entry.bDeleted ? " deleted" : "")
|
||||
<< (entry.bReverted ? " reverted" : "") << (entry.bExpand ? " expand" : "") << "]";
|
||||
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
KEntryMapIterator KEntryMap::findExactEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
|
||||
{
|
||||
const KEntryKeyView theKey(group, key, bool(flags & SearchLocalized), bool(flags & SearchDefaults));
|
||||
return find(theKey);
|
||||
}
|
||||
|
||||
KEntryMapIterator KEntryMap::findEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags)
|
||||
{
|
||||
KEntryKeyView theKey(group, key, false, bool(flags & SearchDefaults));
|
||||
|
||||
// try the localized key first
|
||||
if (flags & SearchLocalized) {
|
||||
theKey.bLocal = true;
|
||||
|
||||
iterator it = find(theKey);
|
||||
if (it != end()) {
|
||||
return it;
|
||||
}
|
||||
|
||||
theKey.bLocal = false;
|
||||
}
|
||||
return find(theKey);
|
||||
}
|
||||
|
||||
KEntryMapConstIterator KEntryMap::constFindEntry(const QString &group, QAnyStringView key, SearchFlags flags) const
|
||||
{
|
||||
KEntryKeyView theKey(group, key, false, bool(flags & SearchDefaults));
|
||||
|
||||
// try the localized key first
|
||||
if (flags & SearchLocalized) {
|
||||
theKey.bLocal = true;
|
||||
|
||||
auto it = find(theKey);
|
||||
if (it != cend()) {
|
||||
return it;
|
||||
}
|
||||
|
||||
theKey.bLocal = false;
|
||||
}
|
||||
|
||||
return find(theKey);
|
||||
}
|
||||
|
||||
bool KEntryMap::setEntry(const QString &group, const QByteArray &key, const QByteArray &value, KEntryMap::EntryOptions options)
|
||||
{
|
||||
KEntryKey k;
|
||||
KEntry e;
|
||||
bool newKey = false;
|
||||
|
||||
const iterator it = findExactEntry(group, key, SearchFlags(options >> 16));
|
||||
|
||||
if (key.isEmpty()) { // inserting a group marker
|
||||
k.mGroup = group;
|
||||
e.bImmutable = (options & EntryImmutable);
|
||||
if (options & EntryDeleted) {
|
||||
qWarning("Internal KConfig error: cannot mark groups as deleted");
|
||||
}
|
||||
if (it == end()) {
|
||||
insert_or_assign(k, e);
|
||||
return true;
|
||||
} else if (it->second == e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
it->second = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (it != end()) {
|
||||
if (it->second.bImmutable) {
|
||||
return false; // we cannot change this entry. Inherits group immutability.
|
||||
}
|
||||
k = it->first;
|
||||
e = it->second;
|
||||
// qDebug() << "found existing entry for key" << k;
|
||||
// If overridden entry is global and not default. And it's overridden by a non global
|
||||
if (e.bGlobal && !(options & EntryGlobal) && !k.bDefault) {
|
||||
e.bOverridesGlobal = true;
|
||||
}
|
||||
} else {
|
||||
// make sure the group marker is in the map
|
||||
KEntryMap const *that = this;
|
||||
auto cit = that->constFindEntry(group);
|
||||
if (cit == cend()) {
|
||||
insert_or_assign(KEntryKey(group), KEntry());
|
||||
} else if (cit->second.bImmutable) {
|
||||
return false; // this group is immutable, so we cannot change this entry.
|
||||
}
|
||||
|
||||
k = KEntryKey(group, key);
|
||||
newKey = true;
|
||||
}
|
||||
|
||||
// set these here, since we may be changing the type of key from the one we found
|
||||
k.bLocal = (options & EntryLocalized);
|
||||
k.bDefault = (options & EntryDefault);
|
||||
k.bRaw = (options & EntryRawKey);
|
||||
|
||||
e.mValue = value;
|
||||
e.bDirty = e.bDirty || (options & EntryDirty);
|
||||
e.bNotify = e.bNotify || (options & EntryNotify);
|
||||
|
||||
e.bGlobal = (options & EntryGlobal); // we can't use || here, because changes to entries in
|
||||
// kdeglobals would be written to kdeglobals instead
|
||||
// of the local config file, regardless of the globals flag
|
||||
e.bImmutable = e.bImmutable || (options & EntryImmutable);
|
||||
if (value.isNull()) {
|
||||
e.bDeleted = e.bDeleted || (options & EntryDeleted);
|
||||
} else {
|
||||
e.bDeleted = false; // setting a value to a previously deleted entry
|
||||
}
|
||||
e.bExpand = (options & EntryExpansion);
|
||||
e.bReverted = false;
|
||||
if (options & EntryLocalized) {
|
||||
e.bLocalizedCountry = (options & EntryLocalizedCountry);
|
||||
} else {
|
||||
e.bLocalizedCountry = false;
|
||||
}
|
||||
|
||||
if (newKey) {
|
||||
// qDebug() << "inserting" << k << "=" << value;
|
||||
insert_or_assign(k, e);
|
||||
if (k.bDefault) {
|
||||
k.bDefault = false;
|
||||
// qDebug() << "also inserting" << k << "=" << value;
|
||||
insert_or_assign(k, e);
|
||||
}
|
||||
// TODO check for presence of unlocalized key
|
||||
return true;
|
||||
}
|
||||
|
||||
// KEntry e2 = it->second;
|
||||
if (options & EntryLocalized) {
|
||||
// fast exit checks for cases where the existing entry is more specific
|
||||
const KEntry &e2 = it->second;
|
||||
if (e2.bLocalizedCountry && !e.bLocalizedCountry) {
|
||||
// lang_COUNTRY > lang
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (it->second != e) {
|
||||
// qDebug() << "changing" << k << "from" << it->second.mValue << "to" << value << e;
|
||||
it->second = e;
|
||||
if (k.bDefault) {
|
||||
KEntryKey nonDefaultKey(k);
|
||||
nonDefaultKey.bDefault = false;
|
||||
insert_or_assign(nonDefaultKey, e);
|
||||
}
|
||||
if (!(options & EntryLocalized)) {
|
||||
KEntryKey theKey(group, key, true, false);
|
||||
// qDebug() << "non-localized entry, remove localized one:" << theKey;
|
||||
erase(theKey);
|
||||
if (k.bDefault) {
|
||||
theKey.bDefault = true;
|
||||
erase(theKey);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// qDebug() << k << "was already set to" << e.mValue;
|
||||
if (!(options & EntryLocalized)) {
|
||||
// qDebug() << "unchanged non-localized entry, remove localized one.";
|
||||
KEntryKey theKey(group, key, true, false);
|
||||
bool ret = false;
|
||||
iterator cit = find(theKey);
|
||||
if (cit != end()) {
|
||||
erase(cit);
|
||||
ret = true;
|
||||
}
|
||||
if (k.bDefault) {
|
||||
theKey.bDefault = true;
|
||||
iterator cit = find(theKey);
|
||||
if (cit != end()) {
|
||||
erase(cit);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// qDebug() << "localized entry, unchanged, return false";
|
||||
// When we are writing a default, we know that the non-
|
||||
// default is the same as the default, so we can simply
|
||||
// use the same branch.
|
||||
return false;
|
||||
}
|
||||
|
||||
QString KEntryMap::getEntry(const QString &group, QAnyStringView key, const QString &defaultValue, KEntryMap::SearchFlags flags, bool *expand) const
|
||||
{
|
||||
const auto it = constFindEntry(group, key, flags);
|
||||
QString theValue = defaultValue;
|
||||
|
||||
if (it != cend() && !it->second.bDeleted) {
|
||||
if (!it->second.mValue.isNull()) {
|
||||
const QByteArray data = it->second.mValue;
|
||||
theValue = QString::fromUtf8(data.constData(), data.length());
|
||||
if (expand) {
|
||||
*expand = it->second.bExpand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theValue;
|
||||
}
|
||||
|
||||
bool KEntryMap::hasEntry(const QString &group, QAnyStringView key, KEntryMap::SearchFlags flags) const
|
||||
{
|
||||
const auto it = constFindEntry(group, key, flags);
|
||||
if (it == cend()) {
|
||||
return false;
|
||||
}
|
||||
if (it->second.bDeleted) {
|
||||
return false;
|
||||
}
|
||||
if (key.isNull()) { // looking for group marker
|
||||
return it->second.mValue.isNull();
|
||||
}
|
||||
// if it->bReverted, we'll just return true; the real answer depends on lookup up with SearchDefaults, though.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KEntryMap::getEntryOption(const KEntryMapConstIterator &it, KEntryMap::EntryOption option) const
|
||||
{
|
||||
if (it == cend()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case EntryDirty:
|
||||
return it->second.bDirty;
|
||||
case EntryLocalized:
|
||||
return it->first.bLocal;
|
||||
case EntryGlobal:
|
||||
return it->second.bGlobal;
|
||||
case EntryImmutable:
|
||||
return it->second.bImmutable;
|
||||
case EntryDeleted:
|
||||
return it->second.bDeleted;
|
||||
case EntryExpansion:
|
||||
return it->second.bExpand;
|
||||
case EntryNotify:
|
||||
return it->second.bNotify;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void KEntryMap::setEntryOption(KEntryMapIterator it, KEntryMap::EntryOption option, bool bf)
|
||||
{
|
||||
if (it == end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case EntryDirty:
|
||||
it->second.bDirty = bf;
|
||||
return;
|
||||
case EntryGlobal:
|
||||
it->second.bGlobal = bf;
|
||||
return;
|
||||
case EntryImmutable:
|
||||
it->second.bImmutable = bf;
|
||||
return;
|
||||
case EntryDeleted:
|
||||
it->second.bDeleted = bf;
|
||||
return;
|
||||
case EntryExpansion:
|
||||
it->second.bExpand = bf;
|
||||
return;
|
||||
case EntryNotify:
|
||||
it->second.bNotify = bf;
|
||||
return;
|
||||
default:
|
||||
return; // fall through
|
||||
}
|
||||
}
|
||||
|
||||
bool KEntryMap::revertEntry(const QString &group, QAnyStringView key, KEntryMap::EntryOptions options, KEntryMap::SearchFlags flags)
|
||||
{
|
||||
Q_ASSERT((flags & KEntryMap::SearchDefaults) == 0);
|
||||
iterator entry = findEntry(group, key, flags);
|
||||
if (entry == end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// qDebug() << "reverting" << entry->first << " = " << entry->mValue;
|
||||
if (entry->second.bReverted) { // already done before
|
||||
return false;
|
||||
}
|
||||
|
||||
KEntryKey defaultKey(entry->first);
|
||||
defaultKey.bDefault = true;
|
||||
// qDebug() << "looking up default entry with key=" << defaultKey;
|
||||
const auto defaultEntry = find(defaultKey);
|
||||
if (defaultEntry != cend()) {
|
||||
Q_ASSERT(defaultEntry->first.bDefault);
|
||||
// qDebug() << "found, update entry";
|
||||
entry->second = defaultEntry->second; // copy default value, for subsequent lookups
|
||||
} else {
|
||||
entry->second.mValue = QByteArray();
|
||||
}
|
||||
entry->second.bNotify = entry->second.bNotify || (options & EntryNotify);
|
||||
entry->second.bDirty = true;
|
||||
entry->second.bReverted = true; // skip it when writing out to disk
|
||||
|
||||
// qDebug() << "Here's what we have now:" << *this;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999-2000 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1996-2000 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGDATA_P_H
|
||||
#define KCONFIGDATA_P_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* map/dict/list config node entry.
|
||||
* @internal
|
||||
*/
|
||||
struct KEntry {
|
||||
/** Constructor. @internal */
|
||||
KEntry()
|
||||
: mValue()
|
||||
, bDirty(false)
|
||||
, bGlobal(false)
|
||||
, bImmutable(false)
|
||||
, bDeleted(false)
|
||||
, bExpand(false)
|
||||
, bReverted(false)
|
||||
, bLocalizedCountry(false)
|
||||
, bNotify(false)
|
||||
, bOverridesGlobal(false)
|
||||
{
|
||||
}
|
||||
/** @internal */
|
||||
QByteArray mValue;
|
||||
/**
|
||||
* Must the entry be written back to disk?
|
||||
*/
|
||||
bool bDirty : 1;
|
||||
/**
|
||||
* Entry should be written to the global config file
|
||||
*/
|
||||
bool bGlobal : 1;
|
||||
/**
|
||||
* Entry can not be modified.
|
||||
*/
|
||||
bool bImmutable : 1;
|
||||
/**
|
||||
* Entry has been deleted.
|
||||
*/
|
||||
bool bDeleted : 1;
|
||||
/**
|
||||
* Whether to apply dollar expansion or not.
|
||||
*/
|
||||
bool bExpand : 1;
|
||||
/**
|
||||
* Entry has been reverted to its default value (from a more global file).
|
||||
*/
|
||||
bool bReverted : 1;
|
||||
/**
|
||||
* Entry is for a localized key. If @c false the value references just language e.g. "de",
|
||||
* if @c true the value references language and country, e.g. "de_DE".
|
||||
**/
|
||||
bool bLocalizedCountry : 1;
|
||||
|
||||
bool bNotify : 1;
|
||||
|
||||
/**
|
||||
* Entry will need to be written on a non global file even if it matches default value
|
||||
*/
|
||||
bool bOverridesGlobal : 1;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(KEntry, Q_RELOCATABLE_TYPE);
|
||||
|
||||
// These operators are used to check whether an entry which is about
|
||||
// to be written equals the previous value. As such, this intentionally
|
||||
// omits the dirty/notify flag from the comparison.
|
||||
inline bool operator==(const KEntry &k1, const KEntry &k2)
|
||||
{
|
||||
/* clang-format off */
|
||||
return k1.bGlobal == k2.bGlobal
|
||||
&& k1.bImmutable == k2.bImmutable
|
||||
&& k1.bDeleted == k2.bDeleted
|
||||
&& k1.bExpand == k2.bExpand
|
||||
&& k1.mValue == k2.mValue;
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
inline bool operator!=(const KEntry &k1, const KEntry &k2)
|
||||
{
|
||||
return !(k1 == k2);
|
||||
}
|
||||
|
||||
/**
|
||||
* key structure holding both the actual key and the group
|
||||
* to which it belongs.
|
||||
* @internal
|
||||
*/
|
||||
struct KEntryKey {
|
||||
/** Constructor. @internal */
|
||||
KEntryKey(const QString &_group = QString(), const QByteArray &_key = QByteArray(), bool isLocalized = false, bool isDefault = false)
|
||||
: mGroup(_group)
|
||||
, mKey(_key)
|
||||
, bLocal(isLocalized)
|
||||
, bDefault(isDefault)
|
||||
, bRaw(false)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* The "group" to which this EntryKey belongs
|
||||
*/
|
||||
QString mGroup;
|
||||
/**
|
||||
* The _actual_ key of the entry in question
|
||||
*/
|
||||
QByteArray mKey;
|
||||
/**
|
||||
* Entry is localised or not
|
||||
*/
|
||||
bool bLocal : 1;
|
||||
/**
|
||||
* Entry indicates if this is a default value.
|
||||
*/
|
||||
bool bDefault : 1;
|
||||
/** @internal
|
||||
* Key is a raw unprocessed key.
|
||||
* @warning this should only be set during merging, never for normal use.
|
||||
*/
|
||||
bool bRaw : 1;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(KEntryKey, Q_RELOCATABLE_TYPE);
|
||||
|
||||
/**
|
||||
* Compares two KEntryKeys (needed for std::map). The order is localized, localized-default,
|
||||
* non-localized, non-localized-default
|
||||
* @internal
|
||||
*/
|
||||
inline bool operator<(const KEntryKey &k1, const KEntryKey &k2)
|
||||
{
|
||||
int result = k1.mGroup.compare(k2.mGroup);
|
||||
if (result != 0) {
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
result = k1.mKey.compare(k2.mKey);
|
||||
if (result != 0) {
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
if (k1.bLocal != k2.bLocal) {
|
||||
return k1.bLocal;
|
||||
}
|
||||
return (!k1.bDefault && k2.bDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* Light-weight view variant of KEntryKey.
|
||||
* Used for look-up in the map.
|
||||
* @internal
|
||||
*/
|
||||
struct KEntryKeyView {
|
||||
/** Constructor. @internal */
|
||||
KEntryKeyView(QStringView _group, QAnyStringView _key, bool isLocalized = false, bool isDefault = false)
|
||||
: mGroup(_group)
|
||||
, mKey(_key)
|
||||
, bLocal(isLocalized)
|
||||
, bDefault(isDefault)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* The "group" to which this EntryKey belongs
|
||||
*/
|
||||
const QStringView mGroup;
|
||||
/**
|
||||
* The _actual_ key of the entry in question
|
||||
*/
|
||||
const QAnyStringView mKey;
|
||||
/**
|
||||
* Entry is localised or not
|
||||
*/
|
||||
bool bLocal : 1;
|
||||
/**
|
||||
* Entry indicates if this is a default value.
|
||||
*/
|
||||
bool bDefault : 1;
|
||||
};
|
||||
|
||||
template<typename TEntryKey1, typename TEntryKey2>
|
||||
bool compareEntryKeyViews(const TEntryKey1 &k1, const TEntryKey2 &k2)
|
||||
{
|
||||
int result = k1.mGroup.compare(k2.mGroup);
|
||||
if (result != 0) {
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
result = QAnyStringView::compare(k1.mKey, k2.mKey);
|
||||
if (result != 0) {
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
if (k1.bLocal != k2.bLocal) {
|
||||
return k1.bLocal;
|
||||
}
|
||||
return (!k1.bDefault && k2.bDefault);
|
||||
}
|
||||
|
||||
inline bool operator<(const KEntryKeyView &k1, const KEntryKey &k2)
|
||||
{
|
||||
return compareEntryKeyViews(k1, k2);
|
||||
}
|
||||
|
||||
inline bool operator<(const KEntryKey &k1, const KEntryKeyView &k2)
|
||||
{
|
||||
return compareEntryKeyViews(k1, k2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Struct to use as Compare type with std::map.
|
||||
* To enable usage of KEntryKeyView for look-up in the map
|
||||
* via the template find() overloads.
|
||||
* @internal
|
||||
*/
|
||||
struct KEntryKeyCompare {
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(const KEntryKey &k1, const KEntryKey &k2) const
|
||||
{
|
||||
return (k1 < k2);
|
||||
}
|
||||
|
||||
bool operator()(const KEntryKeyView &k1, const KEntryKey &k2) const
|
||||
{
|
||||
return (k1 < k2);
|
||||
}
|
||||
|
||||
bool operator()(const KEntryKey &k1, const KEntryKeyView &k2) const
|
||||
{
|
||||
return (k1 < k2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the minimum key that has @a mGroup == @p group.
|
||||
*
|
||||
* @note The returned "minimum key" is consistent with KEntryKey's operator<().
|
||||
* The return value of this function can be passed to KEntryMap::lowerBound().
|
||||
*/
|
||||
inline KEntryKeyView minimumGroupKeyView(const QString &group)
|
||||
{
|
||||
return KEntryKeyView(group, QAnyStringView{}, true, false);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const KEntryKey &key);
|
||||
QDebug operator<<(QDebug dbg, const KEntry &entry);
|
||||
|
||||
/**
|
||||
* \relates KEntry
|
||||
* type specifying a map of entries (key,value pairs).
|
||||
* The keys are actually a key in a particular config file group together
|
||||
* with the group name.
|
||||
* @internal
|
||||
*/
|
||||
class KEntryMap : public std::map<KEntryKey, KEntry, KEntryKeyCompare>
|
||||
{
|
||||
public:
|
||||
enum SearchFlag {
|
||||
SearchDefaults = 1,
|
||||
SearchLocalized = 2,
|
||||
};
|
||||
Q_DECLARE_FLAGS(SearchFlags, SearchFlag)
|
||||
|
||||
enum EntryOption {
|
||||
EntryDirty = 1,
|
||||
EntryGlobal = 2,
|
||||
EntryImmutable = 4,
|
||||
EntryDeleted = 8,
|
||||
EntryExpansion = 16,
|
||||
EntryRawKey = 32,
|
||||
EntryLocalizedCountry = 64,
|
||||
EntryNotify = 128,
|
||||
EntryDefault = (SearchDefaults << 16),
|
||||
EntryLocalized = (SearchLocalized << 16),
|
||||
};
|
||||
Q_DECLARE_FLAGS(EntryOptions, EntryOption)
|
||||
|
||||
iterator findExactEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags());
|
||||
|
||||
iterator findEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags());
|
||||
|
||||
const_iterator findEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const
|
||||
{
|
||||
return constFindEntry(group, key, flags);
|
||||
}
|
||||
|
||||
const_iterator constFindEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const;
|
||||
|
||||
/**
|
||||
* Returns true if the entry gets dirtied or false in other case
|
||||
*/
|
||||
bool setEntry(const QString &group, const QByteArray &key, const QByteArray &value, EntryOptions options);
|
||||
|
||||
void setEntry(const QString &group, const QByteArray &key, const QString &value, EntryOptions options)
|
||||
{
|
||||
setEntry(group, key, value.toUtf8(), options);
|
||||
}
|
||||
|
||||
QString getEntry(const QString &group,
|
||||
QAnyStringView key,
|
||||
const QString &defaultValue = QString(),
|
||||
SearchFlags flags = SearchFlags(),
|
||||
bool *expand = nullptr) const;
|
||||
|
||||
bool hasEntry(const QString &group, QAnyStringView key = QAnyStringView(), SearchFlags flags = SearchFlags()) const;
|
||||
|
||||
bool getEntryOption(const const_iterator &it, EntryOption option) const;
|
||||
bool getEntryOption(const QString &group, QAnyStringView key, SearchFlags flags, EntryOption option) const
|
||||
{
|
||||
return getEntryOption(findEntry(group, key, flags), option);
|
||||
}
|
||||
|
||||
void setEntryOption(iterator it, EntryOption option, bool bf);
|
||||
void setEntryOption(const QString &group, QAnyStringView key, SearchFlags flags, EntryOption option, bool bf)
|
||||
{
|
||||
setEntryOption(findEntry(group, key, flags), option, bf);
|
||||
}
|
||||
|
||||
bool revertEntry(const QString &group, QAnyStringView key, EntryOptions options, SearchFlags flags = SearchFlags());
|
||||
|
||||
template<typename ConstIteratorUser>
|
||||
void forEachEntryWhoseGroupStartsWith(const QString &groupPrefix, ConstIteratorUser callback) const
|
||||
{
|
||||
for (auto it = lower_bound(minimumGroupKeyView(groupPrefix)), end = cend(); it != end && it->first.mGroup.startsWith(groupPrefix); ++it) {
|
||||
callback(it);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ConstIteratorPredicate>
|
||||
bool anyEntryWhoseGroupStartsWith(const QString &groupPrefix, ConstIteratorPredicate predicate) const
|
||||
{
|
||||
for (auto it = lower_bound(minimumGroupKeyView(groupPrefix)), end = cend(); it != end && it->first.mGroup.startsWith(groupPrefix); ++it) {
|
||||
if (predicate(it)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ConstIteratorUser>
|
||||
void forEachEntryOfGroup(const QString &theGroup, ConstIteratorUser callback) const
|
||||
{
|
||||
const auto theEnd = cend();
|
||||
auto it = constFindEntry(theGroup);
|
||||
if (it != theEnd) {
|
||||
++it; // advance past the special group entry marker
|
||||
|
||||
for (; (it != theEnd) && (it->first.mGroup == theGroup); ++it) {
|
||||
callback(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::SearchFlags)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KEntryMap::EntryOptions)
|
||||
|
||||
/**
|
||||
* \relates KEntry
|
||||
* type for iterating over keys in a KEntryMap in sorted order.
|
||||
* @internal
|
||||
*/
|
||||
typedef KEntryMap::iterator KEntryMapIterator;
|
||||
|
||||
/**
|
||||
* \relates KEntry
|
||||
* type for iterating over keys in a KEntryMap in sorted order.
|
||||
* It is const, thus you cannot change the entries in the iterator,
|
||||
* only examine them.
|
||||
* @internal
|
||||
*/
|
||||
typedef KEntryMap::const_iterator KEntryMapConstIterator;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,810 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGGROUP_H
|
||||
#define KCONFIGGROUP_H
|
||||
|
||||
#include "kconfigbase.h"
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class KConfig;
|
||||
class KConfigGroupPrivate;
|
||||
class KSharedConfig;
|
||||
|
||||
/**
|
||||
* \class KConfigGroup kconfiggroup.h <KConfigGroup>
|
||||
*
|
||||
* A class for one specific group in a KConfig object.
|
||||
*
|
||||
* If you want to access the top-level entries of a KConfig
|
||||
* object, which are not associated with any group, use an
|
||||
* empty group name.
|
||||
*
|
||||
* A KConfigGroup will be read-only if it is constructed from a
|
||||
* const config object or from another read-only group.
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KConfigGroup : public KConfigBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs an invalid group.
|
||||
*
|
||||
* \see isValid
|
||||
*/
|
||||
KConfigGroup();
|
||||
|
||||
/**
|
||||
* Construct a config group corresponding to @p group in @p master.
|
||||
*
|
||||
* This allows the creation of subgroups by passing another
|
||||
* group as @p master.
|
||||
*
|
||||
* @param group name of group
|
||||
*/
|
||||
KConfigGroup(KConfigBase *master, const QString &group);
|
||||
|
||||
/**
|
||||
* Construct a read-only config group.
|
||||
*
|
||||
* A read-only group will silently ignore any attempts to write to it.
|
||||
*
|
||||
* This allows the creation of subgroups by passing an existing group
|
||||
* as @p master.
|
||||
*/
|
||||
KConfigGroup(const KConfigBase *master, const QString &group);
|
||||
|
||||
/** Overload for KConfigGroup(const KConfigBase*,const QString&) */
|
||||
KConfigGroup(const QExplicitlySharedDataPointer<KSharedConfig> &master, const QString &group);
|
||||
|
||||
/**
|
||||
* Creates a copy of a group.
|
||||
*/
|
||||
KConfigGroup(const KConfigGroup &);
|
||||
KConfigGroup &operator=(const KConfigGroup &);
|
||||
|
||||
~KConfigGroup() override;
|
||||
|
||||
/**
|
||||
* Whether the group is valid.
|
||||
*
|
||||
* A group is invalid if it was constructed without arguments.
|
||||
*
|
||||
* You should not call any functions on an invalid group.
|
||||
*
|
||||
* @return @c true if the group is valid, @c false if it is invalid.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* The name of this group.
|
||||
*
|
||||
* The root group is named "<default>".
|
||||
*/
|
||||
QString name() const;
|
||||
|
||||
/**
|
||||
* Check whether the containing KConfig object actually contains a
|
||||
* group with this name.
|
||||
*/
|
||||
bool exists() const;
|
||||
|
||||
/**
|
||||
* @reimp
|
||||
*
|
||||
* Syncs the parent config.
|
||||
*/
|
||||
bool sync() override;
|
||||
|
||||
/// @reimp
|
||||
void markAsClean() override;
|
||||
|
||||
/// @reimp
|
||||
AccessMode accessMode() const override;
|
||||
|
||||
/**
|
||||
* Return the config object that this group belongs to
|
||||
*/
|
||||
KConfig *config();
|
||||
/**
|
||||
* Return the config object that this group belongs to
|
||||
*/
|
||||
const KConfig *config() const;
|
||||
|
||||
/**
|
||||
* Copies the entries in this group to another configuration object
|
||||
*
|
||||
* @note @p other can be either another group or a different file.
|
||||
*
|
||||
* @param other the configuration object to copy this group's entries to
|
||||
* @param pFlags the flags to use when writing the entries to the
|
||||
* other configuration object
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
void copyTo(KConfigBase *other, WriteConfigFlags pFlags = Normal) const;
|
||||
|
||||
/**
|
||||
* Changes the configuration object that this group belongs to
|
||||
*
|
||||
* @note @p other can be another group, the top-level KConfig object or
|
||||
* a different KConfig object entirely.
|
||||
*
|
||||
* If @p parent is already the parent of this group, this method will have
|
||||
* no effect.
|
||||
*
|
||||
* @param parent the config object to place this group under
|
||||
* @param pFlags the flags to use in determining which storage source to
|
||||
* write the data to
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
void reparent(KConfigBase *parent, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Moves the key-value pairs from one config group to the other.
|
||||
* In case the entries do not exist the key is ignored.
|
||||
*
|
||||
* @since 5.88
|
||||
*/
|
||||
void moveValuesTo(const QList<const char *> &keys, KConfigGroup &other, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Moves the key-value pairs from one config group to the other.
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
void moveValuesTo(KConfigGroup &other, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Returns the group that this group belongs to
|
||||
*
|
||||
* @return the parent group, or an invalid group if this is a top-level
|
||||
* group
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
KConfigGroup parent() const;
|
||||
|
||||
/**
|
||||
* @reimp
|
||||
*/
|
||||
QStringList groupList() const override;
|
||||
|
||||
/**
|
||||
* Returns a list of keys this group contains
|
||||
*/
|
||||
QStringList keyList() const;
|
||||
|
||||
/**
|
||||
* Delete all entries in the entire group
|
||||
*
|
||||
* @param pFlags flags passed to KConfig::deleteGroup
|
||||
*
|
||||
* @see deleteEntry()
|
||||
*/
|
||||
void deleteGroup(WriteConfigFlags pFlags = Normal);
|
||||
using KConfigBase::deleteGroup;
|
||||
|
||||
/**
|
||||
* Reads the value of an entry specified by @p pKey in the current group
|
||||
*
|
||||
* This template method makes it possible to write
|
||||
* QString foo = readEntry("...", QString("default"));
|
||||
* and the same with all other types supported by QVariant.
|
||||
*
|
||||
* The return type of the method is simply the same as the type of the default value.
|
||||
*
|
||||
* @note readEntry("...", Qt::white) will not compile because Qt::white is an enum.
|
||||
* You must turn it into readEntry("...", QColor(Qt::white)).
|
||||
*
|
||||
* @note Only the following QVariant types are allowed : String,
|
||||
* StringList, List, Font, Point, PointF, Rect, RectF, Size, SizeF, Color, Int, UInt, Bool,
|
||||
* Double, LongLong, ULongLong, DateTime and Date.
|
||||
*
|
||||
* @param key The key to search for
|
||||
* @param aDefault A default value returned if the key was not found
|
||||
* @return The value for this key, or @p aDefault.
|
||||
*
|
||||
* @see writeEntry(), deleteEntry(), hasKey()
|
||||
*/
|
||||
template<typename T>
|
||||
T readEntry(const QString &key, const T &aDefault) const
|
||||
{
|
||||
return readEntry(key.toUtf8().constData(), aDefault);
|
||||
}
|
||||
/**
|
||||
* Overload for readEntry<T>(const QString&, const T&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
template<typename T>
|
||||
T readEntry(const char *key, const T &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads the value of an entry specified by @p key in the current group
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @param aDefault a default value returned if the key was not found
|
||||
* @return the value for this key, or @p aDefault if the key was not found
|
||||
*
|
||||
* @see writeEntry(), deleteEntry(), hasKey()
|
||||
*/
|
||||
QVariant readEntry(const QString &key, const QVariant &aDefault) const;
|
||||
/**
|
||||
* Overload for readEntry(const QString&, const QVariant&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QVariant readEntry(const char *key, const QVariant &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads the string value of an entry specified by @p key in the current group
|
||||
*
|
||||
* If you want to read a path, please use readPathEntry().
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @param aDefault a default value returned if the key was not found
|
||||
* @return the value for this key, or @p aDefault if the key was not found
|
||||
*
|
||||
* @see readPathEntry(), writeEntry(), deleteEntry(), hasKey()
|
||||
*/
|
||||
QString readEntry(const QString &key, const QString &aDefault) const;
|
||||
/**
|
||||
* Overload for readEntry(const QString&, const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QString readEntry(const char *key, const QString &aDefault) const;
|
||||
|
||||
/** Overload for readEntry(const QString&, const QString&) const */
|
||||
QString readEntry(const QString &key, const char *aDefault = nullptr) const;
|
||||
/**
|
||||
* Overload for readEntry(const QString&, const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QString readEntry(const char *key, const char *aDefault = nullptr) const;
|
||||
|
||||
/**
|
||||
* @copydoc readEntry(const char*, const QStringList&) const
|
||||
*
|
||||
* @warning This function doesn't convert the items returned
|
||||
* to any type. It's actually a list of QVariant::String's. If you
|
||||
* want the items converted to a specific type use
|
||||
* readEntry(const char*, const QList<T>&) const
|
||||
*/
|
||||
QVariantList readEntry(const QString &key, const QVariantList &aDefault) const;
|
||||
/**
|
||||
* Overload for readEntry(const QString&, const QVariantList&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QVariantList readEntry(const char *key, const QVariantList &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads a list of strings from the config object
|
||||
*
|
||||
* @param key The key to search for
|
||||
* @param aDefault The default value to use if the key does not exist
|
||||
* @return The list, or @p aDefault if @p key does not exist
|
||||
*
|
||||
* @see readXdgListEntry(), writeEntry(), deleteEntry(), hasKey()
|
||||
*/
|
||||
QStringList readEntry(const QString &key, const QStringList &aDefault) const;
|
||||
/**
|
||||
* Overload for readEntry(const QString&, const QStringList&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QStringList readEntry(const char *key, const QStringList &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads a list of values from the config object
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @param aDefault the default value to use if the key does not exist
|
||||
* @return the list, or @p aDefault if @p key does not exist
|
||||
*
|
||||
* @see readXdgListEntry(), writeEntry(), deleteEntry(), hasKey()
|
||||
*/
|
||||
template<typename T>
|
||||
QList<T> readEntry(const QString &key, const QList<T> &aDefault) const
|
||||
{
|
||||
return readEntry(key.toUtf8().constData(), aDefault);
|
||||
}
|
||||
/**
|
||||
* Overload for readEntry<T>(const QString&, const QList<T>&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
template<typename T>
|
||||
QList<T> readEntry(const char *key, const QList<T> &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads a list of strings from the config object with semicolons separating
|
||||
* them (i.e. following desktop entry spec separator semantics).
|
||||
*
|
||||
* @param pKey the key to search for
|
||||
* @param aDefault the default value to use if the key does not exist
|
||||
* @return the list, or @p aDefault if @p pKey does not exist
|
||||
*
|
||||
* @see readEntry(const QString&, const QStringList&) const
|
||||
*/
|
||||
QStringList readXdgListEntry(const QString &pKey, const QStringList &aDefault = QStringList()) const;
|
||||
/**
|
||||
* Overload for readXdgListEntry(const QString&, const QStringList&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QStringList readXdgListEntry(const char *key, const QStringList &aDefault = QStringList()) const;
|
||||
|
||||
/**
|
||||
* Reads a path
|
||||
*
|
||||
* Read the value of an entry specified by @p pKey in the current group
|
||||
* and interpret it as a path. This means, dollar expansion is activated
|
||||
* for this value, so that e.g. $HOME gets expanded.
|
||||
*
|
||||
* @param pKey The key to search for.
|
||||
* @param aDefault A default value returned if the key was not found.
|
||||
* @return The value for this key. Can be QString() if @p aDefault is null.
|
||||
*/
|
||||
QString readPathEntry(const QString &pKey, const QString &aDefault) const;
|
||||
/**
|
||||
* Overload for readPathEntry(const QString&, const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QString readPathEntry(const char *key, const QString &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads a list of paths
|
||||
*
|
||||
* Read the value of an entry specified by @p pKey in the current group
|
||||
* and interpret it as a list of paths. This means, dollar expansion is activated
|
||||
* for this value, so that e.g. $HOME gets expanded.
|
||||
*
|
||||
* @param pKey the key to search for
|
||||
* @param aDefault a default value returned if the key was not found
|
||||
* @return the list, or @p aDefault if the key does not exist
|
||||
*/
|
||||
QStringList readPathEntry(const QString &pKey, const QStringList &aDefault) const;
|
||||
/**
|
||||
* Overload for readPathEntry(const QString&, const QStringList&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QStringList readPathEntry(const char *key, const QStringList &aDefault) const;
|
||||
|
||||
/**
|
||||
* Reads an untranslated string entry
|
||||
*
|
||||
* You should not normally need to use this.
|
||||
*
|
||||
* @param pKey the key to search for
|
||||
* @param aDefault a default value returned if the key was not found
|
||||
* @return the value for this key, or @p aDefault if the key does not exist
|
||||
*/
|
||||
QString readEntryUntranslated(const QString &pKey, const QString &aDefault = QString()) const;
|
||||
/**
|
||||
* Overload for readEntryUntranslated(const QString&, const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
QString readEntryUntranslated(const char *key, const QString &aDefault = QString()) const;
|
||||
|
||||
/**
|
||||
* Writes a value to the configuration object.
|
||||
*
|
||||
* @param key the key to write to
|
||||
* @param value the value to write
|
||||
* @param pFlags the flags to use when writing this entry
|
||||
*
|
||||
* @see readEntry(), writeXdgListEntry(), deleteEntry()
|
||||
*/
|
||||
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const QVariant &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
void writeEntry(const QString &key, const QString &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const QString &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
void writeEntry(const QString &key, const QByteArray &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const QByteArray &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
void writeEntry(const QString &key, const char *value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
template<typename T>
|
||||
void writeEntry(const char *key, const T &value, WriteConfigFlags pFlags = Normal);
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
template<typename T>
|
||||
void writeEntry(const QString &key, const T &value, WriteConfigFlags pFlags = Normal)
|
||||
{
|
||||
writeEntry(key.toUtf8().constData(), value, pFlags);
|
||||
}
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
void writeEntry(const QString &key, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
void writeEntry(const QString &key, const QVariantList &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeEntry(const char *key, const QVariantList &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/** Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags) */
|
||||
template<typename T>
|
||||
void writeEntry(const QString &key, const QList<T> &value, WriteConfigFlags pFlags = Normal)
|
||||
{
|
||||
writeEntry(key.toUtf8().constData(), value, pFlags);
|
||||
}
|
||||
/**
|
||||
* Overload for writeEntry(const QString&, const QVariant&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
template<typename T>
|
||||
void writeEntry(const char *key, const QList<T> &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Writes a list of strings to the config object, following XDG
|
||||
* desktop entry spec separator semantics
|
||||
*
|
||||
* @param pKey the key to write to
|
||||
* @param value the list to write
|
||||
* @param pFlags the flags to use when writing this entry
|
||||
*
|
||||
* @see writeEntry(), readXdgListEntry()
|
||||
*/
|
||||
void writeXdgListEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writeXdgListEntry(const QString&, const QStringList&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writeXdgListEntry(const char *key, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Writes a file path to the configuration
|
||||
*
|
||||
* If the path is located under $HOME, the user's home directory
|
||||
* is replaced with $HOME in the persistent storage.
|
||||
* The path should therefore be read back with readPathEntry()
|
||||
*
|
||||
* @param pKey the key to write to
|
||||
* @param path the path to write
|
||||
* @param pFlags the flags to use when writing this entry
|
||||
*
|
||||
* @see writeEntry(), readPathEntry()
|
||||
*/
|
||||
void writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writePathEntry(const QString&, const QString&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writePathEntry(const char *Key, const QString &path, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Writes a list of paths to the configuration
|
||||
*
|
||||
* If any of the paths are located under $HOME, the user's home directory
|
||||
* is replaced with $HOME in the persistent storage.
|
||||
* The paths should therefore be read back with readPathEntry()
|
||||
*
|
||||
* @param pKey the key to write to
|
||||
* @param value the list to write
|
||||
* @param pFlags the flags to use when writing this entry
|
||||
*
|
||||
* @see writeEntry(), readPathEntry()
|
||||
*/
|
||||
void writePathEntry(const QString &pKey, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for writePathEntry(const QString&, const QStringList&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void writePathEntry(const char *key, const QStringList &value, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Deletes the entry specified by @p pKey in the current group
|
||||
*
|
||||
* This also hides system wide defaults.
|
||||
*
|
||||
* @param pKey the key to delete
|
||||
* @param pFlags the flags to use when deleting this entry
|
||||
*
|
||||
* @see deleteGroup(), readEntry(), writeEntry()
|
||||
*/
|
||||
void deleteEntry(const QString &pKey, WriteConfigFlags pFlags = Normal);
|
||||
/**
|
||||
* Overload for deleteEntry(const QString&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void deleteEntry(const char *key, WriteConfigFlags pFlags = Normal);
|
||||
|
||||
/**
|
||||
* Checks whether the key has an entry in this group
|
||||
*
|
||||
* Use this to determine if a key is not specified for the current
|
||||
* group (hasKey() returns false).
|
||||
*
|
||||
* If this returns @c false for a key, readEntry() (and its variants)
|
||||
* will return the default value passed to them.
|
||||
*
|
||||
* @param key the key to search for
|
||||
* @return @c true if the key is defined in this group by any of the
|
||||
* configuration sources, @c false otherwise
|
||||
*
|
||||
* @see readEntry()
|
||||
*/
|
||||
bool hasKey(const QString &key) const;
|
||||
/**
|
||||
* Overload for hasKey(const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
bool hasKey(const char *key) const;
|
||||
|
||||
/**
|
||||
* Whether this group may be changed
|
||||
*
|
||||
* @return @c false if the group may be changed, @c true otherwise
|
||||
*/
|
||||
bool isImmutable() const override;
|
||||
|
||||
/**
|
||||
* Checks if it is possible to change the given entry
|
||||
*
|
||||
* If isImmutable() returns @c true, then this method will return
|
||||
* @c true for all inputs.
|
||||
*
|
||||
* @param key the key to check
|
||||
* @return @c false if the key may be changed using this configuration
|
||||
* group object, @c true otherwise
|
||||
*/
|
||||
bool isEntryImmutable(const QString &key) const;
|
||||
/**
|
||||
* Overload for isEntryImmutable(const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
bool isEntryImmutable(const char *key) const;
|
||||
|
||||
/**
|
||||
* Reverts an entry to the default settings.
|
||||
*
|
||||
* Reverts the entry with key @p key in the current group in the
|
||||
* application specific config file to either the system wide (default)
|
||||
* value or the value specified in the global KDE config file.
|
||||
*
|
||||
* To revert entries in the global KDE config file, the global KDE config
|
||||
* file should be opened explicitly in a separate config object.
|
||||
*
|
||||
* @note This is @em not the same as deleting the key, as instead the
|
||||
* global setting will be copied to the configuration file that this
|
||||
* object manipulates.
|
||||
*
|
||||
* @param key The key of the entry to revert.
|
||||
*/
|
||||
void revertToDefault(const QString &key, WriteConfigFlags pFlag = WriteConfigFlags());
|
||||
|
||||
/**
|
||||
* Overload for revertToDefault(const QString&, WriteConfigFlags)
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
void revertToDefault(const char *key, WriteConfigFlags pFlag = WriteConfigFlags());
|
||||
|
||||
/**
|
||||
* Whether a default is specified for an entry in either the
|
||||
* system wide configuration file or the global KDE config file
|
||||
*
|
||||
* If an application computes a default value at runtime for
|
||||
* a certain entry, e.g. like:
|
||||
* \code
|
||||
* QColor computedDefault = qApp->palette().color(QPalette::Active, QPalette::Text);
|
||||
* QColor color = group.readEntry(key, computedDefault);
|
||||
* \endcode
|
||||
* then it may wish to make the following check before
|
||||
* writing back changes:
|
||||
* \code
|
||||
* if ( (value == computedDefault) && !group.hasDefault(key) )
|
||||
* group.revertToDefault(key);
|
||||
* else
|
||||
* group.writeEntry(key, value);
|
||||
* \endcode
|
||||
*
|
||||
* This ensures that as long as the entry is not modified to differ from
|
||||
* the computed default, the application will keep using the computed default
|
||||
* and will follow changes the computed default makes over time.
|
||||
*
|
||||
* @param key the key of the entry to check
|
||||
* @return @c true if the global or system settings files specify a default
|
||||
* for @p key in this group, @c false otherwise
|
||||
*/
|
||||
bool hasDefault(const QString &key) const;
|
||||
/**
|
||||
* Overload for hasDefault(const QString&) const
|
||||
* @param key name of key, encoded in UTF-8
|
||||
*/
|
||||
bool hasDefault(const char *key) const;
|
||||
|
||||
/**
|
||||
* Returns a map (tree) of entries for all entries in this group
|
||||
*
|
||||
* Only the actual entry string is returned, none of the
|
||||
* other internal data should be included.
|
||||
*
|
||||
* @return a map of entries in this group, indexed by key
|
||||
*/
|
||||
QMap<QString, QString> entryMap() const;
|
||||
|
||||
protected:
|
||||
bool hasGroupImpl(const QString &groupName) const override;
|
||||
KConfigGroup groupImpl(const QString &groupName) override;
|
||||
const KConfigGroup groupImpl(const QString &groupName) const override;
|
||||
void deleteGroupImpl(const QString &groupName, WriteConfigFlags flags) override;
|
||||
bool isGroupImmutableImpl(const QString &groupName) const override;
|
||||
|
||||
private:
|
||||
QExplicitlySharedDataPointer<KConfigGroupPrivate> d;
|
||||
|
||||
friend class KConfigGroupPrivate;
|
||||
|
||||
/**
|
||||
* Return the data in @p value converted to a QVariant
|
||||
*
|
||||
* @param pKey the name of the entry being converted, this is only used for error
|
||||
* reporting
|
||||
* @param value the UTF-8 data to be converted
|
||||
* @param aDefault the default value if @p pKey is not found
|
||||
* @return @p value converted to QVariant, or @p aDefault if @p value is invalid or cannot be converted.
|
||||
*/
|
||||
static QVariant convertToQVariant(const char *pKey, const QByteArray &value, const QVariant &aDefault);
|
||||
|
||||
KCONFIGCORE_NO_EXPORT void moveValue(const char *key, KConfigGroup &other, WriteConfigFlags pFlags);
|
||||
|
||||
// exported for usage by KServices' KService & KServiceAction
|
||||
friend class KServicePrivate; // XXX yeah, ugly^5
|
||||
friend class KServiceAction;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(KConfigGroup, Q_RELOCATABLE_TYPE);
|
||||
|
||||
#define KCONFIGGROUP_ENUMERATOR_ERROR(ENUM) "The Qt MetaObject system does not seem to know about \"" ENUM "\" please use Q_ENUM or Q_FLAG to register it."
|
||||
|
||||
/**
|
||||
* To add support for your own enums in KConfig, you can declare them with Q_ENUM()
|
||||
* in a QObject subclass (which will make moc generate the code to turn the
|
||||
* enum into a string and vice-versa), and then (in the cpp code)
|
||||
* use the macro
|
||||
* <code>KCONFIGGROUP_DECLARE_ENUM_QOBJECT(MyClass, MyEnum)</code>
|
||||
*
|
||||
*/
|
||||
#define KCONFIGGROUP_DECLARE_ENUM_QOBJECT(Class, Enum) \
|
||||
template<> \
|
||||
Class::Enum KConfigGroup::readEntry(const char *key, const Class::Enum &def) const \
|
||||
{ \
|
||||
const QMetaObject *M_obj = &Class::staticMetaObject; \
|
||||
const int M_index = M_obj->indexOfEnumerator(#Enum); \
|
||||
if (M_index == -1) \
|
||||
qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Enum)); \
|
||||
const QMetaEnum M_enum = M_obj->enumerator(M_index); \
|
||||
const QByteArray M_data = readEntry(key, QByteArray(M_enum.valueToKey(def))); \
|
||||
return static_cast<Class::Enum>(M_enum.keyToValue(M_data.constData())); \
|
||||
} \
|
||||
template<> \
|
||||
void KConfigGroup::writeEntry(const char *key, const Class::Enum &value, KConfigBase::WriteConfigFlags flags) \
|
||||
{ \
|
||||
const QMetaObject *M_obj = &Class::staticMetaObject; \
|
||||
const int M_index = M_obj->indexOfEnumerator(#Enum); \
|
||||
if (M_index == -1) \
|
||||
qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Enum)); \
|
||||
const QMetaEnum M_enum = M_obj->enumerator(M_index); \
|
||||
writeEntry(key, QByteArray(M_enum.valueToKey(value)), flags); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to KCONFIGGROUP_DECLARE_ENUM_QOBJECT but for flags declared with Q_FLAG()
|
||||
* (where multiple values can be set at the same time)
|
||||
*/
|
||||
#define KCONFIGGROUP_DECLARE_FLAGS_QOBJECT(Class, Flags) \
|
||||
template<> \
|
||||
Class::Flags KConfigGroup::readEntry(const char *key, const Class::Flags &def) const \
|
||||
{ \
|
||||
const QMetaObject *M_obj = &Class::staticMetaObject; \
|
||||
const int M_index = M_obj->indexOfEnumerator(#Flags); \
|
||||
if (M_index == -1) \
|
||||
qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Flags)); \
|
||||
const QMetaEnum M_enum = M_obj->enumerator(M_index); \
|
||||
const QByteArray M_data = readEntry(key, QByteArray(M_enum.valueToKeys(def))); \
|
||||
return static_cast<Class::Flags>(M_enum.keysToValue(M_data.constData())); \
|
||||
} \
|
||||
template<> \
|
||||
void KConfigGroup::writeEntry(const char *key, const Class::Flags &value, KConfigBase::WriteConfigFlags flags) \
|
||||
{ \
|
||||
const QMetaObject *M_obj = &Class::staticMetaObject; \
|
||||
const int M_index = M_obj->indexOfEnumerator(#Flags); \
|
||||
if (M_index == -1) \
|
||||
qFatal(KCONFIGGROUP_ENUMERATOR_ERROR(#Flags)); \
|
||||
const QMetaEnum M_enum = M_obj->enumerator(M_index); \
|
||||
writeEntry(key, QByteArray(M_enum.valueToKeys(value)), flags); \
|
||||
}
|
||||
|
||||
#include "kconfigconversioncheck_p.h"
|
||||
|
||||
template<typename T>
|
||||
T KConfigGroup::readEntry(const char *key, const T &defaultValue) const
|
||||
{
|
||||
KConfigConversionCheck::to_QVariant<T>();
|
||||
return qvariant_cast<T>(readEntry(key, QVariant::fromValue(defaultValue)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QList<T> KConfigGroup::readEntry(const char *key, const QList<T> &defaultValue) const
|
||||
{
|
||||
KConfigConversionCheck::to_QVariant<T>();
|
||||
KConfigConversionCheck::to_QString<T>();
|
||||
|
||||
QVariantList data;
|
||||
|
||||
for (const T &value : defaultValue) {
|
||||
data.append(QVariant::fromValue(value));
|
||||
}
|
||||
|
||||
QList<T> list;
|
||||
const auto variantList = readEntry<QVariantList>(key, data);
|
||||
for (const QVariant &value : variantList) {
|
||||
Q_ASSERT(value.canConvert<T>());
|
||||
list.append(qvariant_cast<T>(value));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void KConfigGroup::writeEntry(const char *key, const T &value, WriteConfigFlags pFlags)
|
||||
{
|
||||
KConfigConversionCheck::to_QVariant<T>();
|
||||
writeEntry(key, QVariant::fromValue(value), pFlags);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void KConfigGroup::writeEntry(const char *key, const QList<T> &list, WriteConfigFlags pFlags)
|
||||
{
|
||||
KConfigConversionCheck::to_QVariant<T>();
|
||||
KConfigConversionCheck::to_QString<T>();
|
||||
QVariantList data;
|
||||
for (const T &value : list) {
|
||||
data.append(QVariant::fromValue(value));
|
||||
}
|
||||
|
||||
writeEntry(key, data, pFlags);
|
||||
}
|
||||
|
||||
#endif // KCONFIGGROUP_H
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGGROUP_P_H
|
||||
#define KCONFIGGROUP_P_H
|
||||
|
||||
#include "kconfiggroup.h"
|
||||
#include <QVariant>
|
||||
|
||||
class KConfigGroup;
|
||||
|
||||
struct KConfigGroupGui {
|
||||
typedef bool (*kReadEntryGui)(const QByteArray &data, const char *key, const QVariant &input, QVariant &output);
|
||||
typedef bool (*kWriteEntryGui)(KConfigGroup *, const char *key, const QVariant &input, KConfigGroup::WriteConfigFlags flags);
|
||||
|
||||
kReadEntryGui readEntryGui;
|
||||
kWriteEntryGui writeEntryGui;
|
||||
};
|
||||
|
||||
extern KCONFIGCORE_EXPORT KConfigGroupGui _kde_internal_KConfigGroupGui;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,960 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigini_p.h"
|
||||
|
||||
#include "kconfig_core_log_settings.h"
|
||||
#include "kconfigdata_p.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QLockFile>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <unistd.h> // getuid, close
|
||||
#endif
|
||||
#include <fcntl.h> // open
|
||||
#include <sys/types.h> // uid_t
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
KCONFIGCORE_EXPORT bool kde_kiosk_exception = false; // flag to disable kiosk restrictions
|
||||
|
||||
static QByteArray lookup(QByteArrayView fragment, QHash<QByteArrayView, QByteArray> *cache)
|
||||
{
|
||||
auto it = cache->constFind(fragment);
|
||||
if (it != cache->constEnd()) {
|
||||
return it.value();
|
||||
}
|
||||
|
||||
return cache->insert(fragment, fragment.toByteArray()).value();
|
||||
}
|
||||
|
||||
QString KConfigIniBackend::warningProlog(const QFile &file, int line)
|
||||
{
|
||||
// %2 then %1 i.e. int before QString, so that the QString is last
|
||||
// This avoids a wrong substitution if the fileName itself contains %1
|
||||
return QStringLiteral("KConfigIni: In file %2, line %1:").arg(line).arg(file.fileName());
|
||||
}
|
||||
|
||||
KConfigIniBackend::KConfigIniBackend()
|
||||
: lockFile(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
KConfigIniBackend::ParseInfo KConfigIniBackend::parseConfig(const QByteArray ¤tLocale, KEntryMap &entryMap, ParseOptions options)
|
||||
{
|
||||
return parseConfig(currentLocale, entryMap, options, false);
|
||||
}
|
||||
|
||||
// merging==true is the merging that happens at the beginning of writeConfig:
|
||||
// merge changes in the on-disk file with the changes in the KConfig object.
|
||||
KConfigIniBackend::ParseInfo KConfigIniBackend::parseConfig(const QByteArray ¤tLocale, KEntryMap &entryMap, ParseOptions options, bool merging)
|
||||
{
|
||||
if (filePath().isEmpty()) {
|
||||
return ParseOk;
|
||||
}
|
||||
|
||||
QFile file(filePath());
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
return file.exists() ? ParseOpenError : ParseOk;
|
||||
}
|
||||
|
||||
QList<QString> immutableGroups;
|
||||
|
||||
bool fileOptionImmutable = false;
|
||||
bool groupOptionImmutable = false;
|
||||
bool groupSkip = false;
|
||||
|
||||
int lineNo = 0;
|
||||
// on systems using \r\n as end of line, \r will be taken care of by
|
||||
// trim() below
|
||||
QByteArray buffer = file.readAll();
|
||||
QByteArrayView contents(buffer.data(), buffer.size());
|
||||
|
||||
const int langIdx = currentLocale.indexOf('_');
|
||||
const QByteArray currentLanguage = langIdx >= 0 ? currentLocale.left(langIdx) : currentLocale;
|
||||
|
||||
QString currentGroup = QStringLiteral("<default>");
|
||||
bool bDefault = options & ParseDefaults;
|
||||
bool allowExecutableValues = options & ParseExpansions;
|
||||
|
||||
// Reduce memory overhead by making use of implicit sharing
|
||||
// This assumes that config files contain only a small amount of
|
||||
// different fragments which are repeated often.
|
||||
// This is often the case, especially sub groups will all have
|
||||
// the same list of keys and similar values as well.
|
||||
QHash<QByteArrayView, QByteArray> cache;
|
||||
cache.reserve(4096);
|
||||
|
||||
while (!contents.isEmpty()) {
|
||||
QByteArrayView line;
|
||||
if (const auto idx = contents.indexOf('\n'); idx < 0) {
|
||||
line = contents;
|
||||
contents = {};
|
||||
} else {
|
||||
line = contents.left(idx);
|
||||
contents = contents.mid(idx + 1);
|
||||
}
|
||||
line = line.trimmed();
|
||||
++lineNo;
|
||||
|
||||
// skip empty lines and lines beginning with '#'
|
||||
if (line.isEmpty() || line.at(0) == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.at(0) == '[') { // found a group
|
||||
groupOptionImmutable = fileOptionImmutable;
|
||||
|
||||
QByteArray newGroup;
|
||||
int start = 1;
|
||||
int end = 0;
|
||||
do {
|
||||
end = start;
|
||||
for (;;) {
|
||||
if (end == line.length()) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, lineNo) << "Invalid group header.";
|
||||
// XXX maybe reset the current group here?
|
||||
goto next_line;
|
||||
}
|
||||
if (line.at(end) == ']') {
|
||||
break;
|
||||
}
|
||||
++end;
|
||||
}
|
||||
/* clang-format off */
|
||||
if (end + 1 == line.length()
|
||||
&& start + 2 == end
|
||||
&& line.at(start) == '$'
|
||||
&& line.at(start + 1) == 'i') { /* clang-format on */
|
||||
if (newGroup.isEmpty()) {
|
||||
fileOptionImmutable = !kde_kiosk_exception;
|
||||
} else {
|
||||
groupOptionImmutable = !kde_kiosk_exception;
|
||||
}
|
||||
} else {
|
||||
if (!newGroup.isEmpty()) {
|
||||
newGroup += '\x1d';
|
||||
}
|
||||
QByteArrayView namePart = line.mid(start, end - start);
|
||||
printableToString(namePart, file, lineNo);
|
||||
newGroup += namePart.toByteArray();
|
||||
}
|
||||
} while ((start = end + 2) <= line.length() && line.at(end + 1) == '[');
|
||||
currentGroup = QString::fromUtf8(newGroup);
|
||||
|
||||
groupSkip = entryMap.getEntryOption(currentGroup, {}, {}, KEntryMap::EntryImmutable);
|
||||
|
||||
if (groupSkip && !bDefault) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (groupOptionImmutable)
|
||||
// Do not make the groups immutable until the entries from
|
||||
// this file have been added.
|
||||
{
|
||||
immutableGroups.append(currentGroup);
|
||||
}
|
||||
} else {
|
||||
if (groupSkip && !bDefault) {
|
||||
continue; // skip entry
|
||||
}
|
||||
|
||||
QByteArrayView aKey;
|
||||
int eqpos = line.indexOf('=');
|
||||
if (eqpos < 0) {
|
||||
aKey = line;
|
||||
line = {};
|
||||
} else {
|
||||
QByteArrayView temp = line.left(eqpos);
|
||||
aKey = temp.trimmed();
|
||||
line = line.mid(eqpos + 1);
|
||||
line = line.trimmed();
|
||||
}
|
||||
if (aKey.isEmpty()) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, lineNo) << "Invalid entry (empty key)";
|
||||
continue;
|
||||
}
|
||||
|
||||
KEntryMap::EntryOptions entryOptions = {};
|
||||
if (groupOptionImmutable) {
|
||||
entryOptions |= KEntryMap::EntryImmutable;
|
||||
}
|
||||
|
||||
QByteArrayView locale;
|
||||
int start;
|
||||
while ((start = aKey.lastIndexOf('[')) >= 0) {
|
||||
int end = aKey.indexOf(']', start);
|
||||
if (end < 0) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, lineNo) << "Invalid entry (missing ']')";
|
||||
goto next_line;
|
||||
} else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s)
|
||||
int i = start + 2;
|
||||
while (i < end) {
|
||||
switch (aKey.at(i)) {
|
||||
case 'i':
|
||||
if (!kde_kiosk_exception) {
|
||||
entryOptions |= KEntryMap::EntryImmutable;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (allowExecutableValues) {
|
||||
entryOptions |= KEntryMap::EntryExpansion;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
entryOptions |= KEntryMap::EntryDeleted;
|
||||
aKey.truncate(start);
|
||||
printableToString(aKey, file, lineNo);
|
||||
entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions);
|
||||
goto next_line;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
} else { // found a locale
|
||||
if (!locale.isNull()) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, lineNo) << "Invalid entry (second locale!?)";
|
||||
goto next_line;
|
||||
}
|
||||
|
||||
locale = aKey.mid(start + 1, end - start - 1);
|
||||
}
|
||||
aKey.truncate(start);
|
||||
}
|
||||
if (eqpos < 0) { // Do this here after [$d] was checked
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, lineNo) << "Invalid entry (missing '=')";
|
||||
continue;
|
||||
}
|
||||
printableToString(aKey, file, lineNo);
|
||||
if (!locale.isEmpty()) {
|
||||
if (locale != currentLocale && locale != currentLanguage) {
|
||||
// backward compatibility. C == en_US
|
||||
if (locale.at(0) != 'C' || currentLocale != "en_US") {
|
||||
if (merging) {
|
||||
entryOptions |= KEntryMap::EntryRawKey;
|
||||
} else {
|
||||
goto next_line; // skip this entry if we're not merging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(entryOptions & KEntryMap::EntryRawKey)) {
|
||||
printableToString(aKey, file, lineNo);
|
||||
}
|
||||
|
||||
if (options & ParseGlobal) {
|
||||
entryOptions |= KEntryMap::EntryGlobal;
|
||||
}
|
||||
if (bDefault) {
|
||||
entryOptions |= KEntryMap::EntryDefault;
|
||||
}
|
||||
if (!locale.isNull()) {
|
||||
entryOptions |= KEntryMap::EntryLocalized;
|
||||
if (locale.indexOf('_') != -1) {
|
||||
entryOptions |= KEntryMap::EntryLocalizedCountry;
|
||||
}
|
||||
}
|
||||
printableToString(line, file, lineNo);
|
||||
if (entryOptions & KEntryMap::EntryRawKey) {
|
||||
QByteArray rawKey;
|
||||
rawKey.reserve(aKey.length() + locale.length() + 2);
|
||||
rawKey.append(aKey);
|
||||
rawKey.append('[').append(locale).append(']');
|
||||
entryMap.setEntry(currentGroup, rawKey, lookup(line, &cache), entryOptions);
|
||||
} else {
|
||||
entryMap.setEntry(currentGroup, lookup(aKey, &cache), lookup(line, &cache), entryOptions);
|
||||
}
|
||||
}
|
||||
next_line:
|
||||
continue;
|
||||
}
|
||||
|
||||
// now make sure immutable groups are marked immutable
|
||||
for (const QString &group : std::as_const(immutableGroups)) {
|
||||
entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable);
|
||||
}
|
||||
|
||||
return fileOptionImmutable ? ParseImmutable : ParseOk;
|
||||
}
|
||||
|
||||
void KConfigIniBackend::writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map, bool defaultGroup, bool &firstEntry)
|
||||
{
|
||||
QString currentGroup;
|
||||
bool groupIsImmutable = false;
|
||||
for (const auto &[key, entry] : map) {
|
||||
// Either process the default group or all others
|
||||
if ((key.mGroup != QStringLiteral("<default>")) == defaultGroup) {
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
// the only thing we care about groups is, is it immutable?
|
||||
if (key.mKey.isNull()) {
|
||||
groupIsImmutable = entry.bImmutable;
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
const KEntry ¤tEntry = entry;
|
||||
if (!defaultGroup && currentGroup != key.mGroup) {
|
||||
if (!firstEntry) {
|
||||
file.putChar('\n');
|
||||
}
|
||||
currentGroup = key.mGroup;
|
||||
for (int start = 0, end;; start = end + 1) {
|
||||
file.putChar('[');
|
||||
end = currentGroup.indexOf(QLatin1Char('\x1d'), start);
|
||||
if (end < 0) {
|
||||
int cgl = currentGroup.length();
|
||||
if (currentGroup.at(start) == QLatin1Char('$') && cgl - start <= 10) {
|
||||
for (int i = start + 1; i < cgl; i++) {
|
||||
const QChar c = currentGroup.at(i);
|
||||
if (c < QLatin1Char('a') || c > QLatin1Char('z')) {
|
||||
goto nope;
|
||||
}
|
||||
}
|
||||
file.write("\\x24");
|
||||
++start;
|
||||
}
|
||||
nope:
|
||||
// TODO: make stringToPrintable also process QString, to save the conversion here and below
|
||||
file.write(stringToPrintable(QStringView(currentGroup).mid(start).toUtf8(), GroupString));
|
||||
file.putChar(']');
|
||||
if (groupIsImmutable) {
|
||||
file.write("[$i]", 4);
|
||||
}
|
||||
file.putChar('\n');
|
||||
break;
|
||||
} else {
|
||||
file.write(stringToPrintable(QStringView(currentGroup).mid(start, end - start).toUtf8(), GroupString));
|
||||
file.putChar(']');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
firstEntry = false;
|
||||
// it is data for a group
|
||||
|
||||
if (key.bRaw) { // unprocessed key with attached locale from merge
|
||||
file.write(key.mKey);
|
||||
} else {
|
||||
file.write(stringToPrintable(key.mKey, KeyString)); // Key
|
||||
if (key.bLocal && locale != "C") { // 'C' locale == untranslated
|
||||
file.putChar('[');
|
||||
file.write(locale); // locale tag
|
||||
file.putChar(']');
|
||||
}
|
||||
}
|
||||
if (currentEntry.bDeleted) {
|
||||
if (currentEntry.bImmutable) {
|
||||
file.write("[$di]", 5); // Deleted + immutable
|
||||
} else {
|
||||
file.write("[$d]", 4); // Deleted
|
||||
}
|
||||
} else {
|
||||
if (currentEntry.bImmutable || currentEntry.bExpand) {
|
||||
file.write("[$", 2);
|
||||
if (currentEntry.bImmutable) {
|
||||
file.putChar('i');
|
||||
}
|
||||
if (currentEntry.bExpand) {
|
||||
file.putChar('e');
|
||||
}
|
||||
file.putChar(']');
|
||||
}
|
||||
file.putChar('=');
|
||||
file.write(stringToPrintable(currentEntry.mValue, ValueString));
|
||||
}
|
||||
file.putChar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigIniBackend::writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map)
|
||||
{
|
||||
bool firstEntry = true;
|
||||
|
||||
// write default group
|
||||
writeEntries(locale, file, map, true, firstEntry);
|
||||
|
||||
// write all other groups
|
||||
writeEntries(locale, file, map, false, firstEntry);
|
||||
}
|
||||
|
||||
bool KConfigIniBackend::writeConfig(const QByteArray &locale, KEntryMap &entryMap, WriteOptions options)
|
||||
{
|
||||
Q_ASSERT(!filePath().isEmpty());
|
||||
|
||||
KEntryMap writeMap;
|
||||
const bool bGlobal = options & WriteGlobal;
|
||||
|
||||
// First, reparse the file on disk, to merge our changes with the ones done by other apps
|
||||
// Store the result into writeMap.
|
||||
{
|
||||
ParseOptions opts = ParseExpansions;
|
||||
if (bGlobal) {
|
||||
opts |= ParseGlobal;
|
||||
}
|
||||
ParseInfo info = parseConfig(locale, writeMap, opts, true);
|
||||
if (info != ParseOk) { // either there was an error or the file became immutable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &[key, entry] : entryMap) {
|
||||
if (!key.mKey.isEmpty() && !entry.bDirty) { // not dirty, doesn't overwrite entry in writeMap. skips default entries, too.
|
||||
continue;
|
||||
}
|
||||
|
||||
// only write entries that have the same "globality" as the file
|
||||
if (entry.bGlobal == bGlobal) {
|
||||
if (entry.bReverted && entry.bOverridesGlobal) {
|
||||
entry.bDeleted = true;
|
||||
writeMap[key] = entry;
|
||||
} else if (entry.bReverted) {
|
||||
writeMap.erase(key);
|
||||
} else if (!entry.bDeleted) {
|
||||
writeMap[key] = entry;
|
||||
} else {
|
||||
KEntryKey defaultKey = key;
|
||||
defaultKey.bDefault = true;
|
||||
if (entryMap.find(defaultKey) == entryMap.end() && !entry.bOverridesGlobal) {
|
||||
writeMap.erase(key); // remove the deleted entry if there is no default
|
||||
// qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal;
|
||||
} else {
|
||||
writeMap[key] = entry; // otherwise write an explicitly deleted entry
|
||||
// qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal;
|
||||
}
|
||||
}
|
||||
entry.bDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// now writeMap should contain only entries to be written
|
||||
// so write it out to disk
|
||||
|
||||
// check if file exists
|
||||
QFile::Permissions fileMode = filePath().startsWith(u"/etc/xdg/"_s) ? QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther //
|
||||
: QFile::ReadUser | QFile::WriteUser;
|
||||
|
||||
bool createNew = true;
|
||||
|
||||
QFileInfo fi(filePath());
|
||||
if (fi.exists()) {
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO: getuid does not exist on windows, use GetSecurityInfo and GetTokenInformation instead
|
||||
createNew = false;
|
||||
#else
|
||||
if (fi.ownerId() == ::getuid()) {
|
||||
// Preserve file mode if file exists and is owned by user.
|
||||
fileMode = fi.permissions();
|
||||
} else {
|
||||
// File is not owned by user:
|
||||
// Don't create new file but write to existing file instead.
|
||||
createNew = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (createNew) {
|
||||
QSaveFile file(filePath());
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
// HACK: when we are dealing with content:// URIs, QSaveFile has to rely on DirectWrite.
|
||||
// Otherwise this method returns a false and we're done.
|
||||
file.setDirectWriteFallback(true);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning(KCONFIG_CORE_LOG) << "Couldn't create a new file:" << filePath() << ". Error:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
qWarning(KCONFIG_CORE_LOG) << "Couldn't create a new file:" << filePath() << ". Error:" << file.errorString();
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
file.setTextModeEnabled(true); // to get eol translation
|
||||
writeEntries(locale, file, writeMap);
|
||||
|
||||
if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) {
|
||||
// File is empty and doesn't have special permissions: delete it.
|
||||
file.cancelWriting();
|
||||
|
||||
if (fi.exists()) {
|
||||
// also remove the old file in case it existed. this can happen
|
||||
// when we delete all the entries in an existing config file.
|
||||
// if we don't do this, then deletions and revertToDefault's
|
||||
// will mysteriously fail
|
||||
QFile::remove(filePath());
|
||||
}
|
||||
} else {
|
||||
// Normal case: Close the file
|
||||
if (file.commit()) {
|
||||
QFile::setPermissions(filePath(), fileMode);
|
||||
return true;
|
||||
}
|
||||
// Couldn't write. Disk full?
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Couldn't write" << filePath() << ". Disk full?";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Open existing file. *DON'T* create it if it suddenly does not exist!
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
|
||||
int fd = QT_OPEN(QFile::encodeName(filePath()).constData(), O_WRONLY | O_TRUNC);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
QFile f;
|
||||
if (!f.open(fd, QIODevice::WriteOnly)) {
|
||||
QT_CLOSE(fd);
|
||||
return false;
|
||||
}
|
||||
writeEntries(locale, f, writeMap);
|
||||
f.close();
|
||||
QT_CLOSE(fd);
|
||||
#else
|
||||
QFile f(filePath());
|
||||
// XXX This is broken - it DOES create the file if it is suddenly gone.
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
return false;
|
||||
}
|
||||
f.setTextModeEnabled(true);
|
||||
writeEntries(locale, f, writeMap);
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KConfigIniBackend::isWritable() const
|
||||
{
|
||||
const QString filePath = this->filePath();
|
||||
if (filePath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QFileInfo file(filePath);
|
||||
if (file.exists()) {
|
||||
return file.isWritable();
|
||||
}
|
||||
|
||||
// If the file does not exist, check if the deepest existing dir is writable
|
||||
QFileInfo dir(file.absolutePath());
|
||||
while (!dir.exists()) {
|
||||
QString parent = dir.absolutePath(); // Go up. Can't use cdUp() on non-existing dirs.
|
||||
if (parent == dir.filePath()) {
|
||||
// no parent
|
||||
return false;
|
||||
}
|
||||
dir.setFile(parent);
|
||||
}
|
||||
return dir.isDir() && dir.isWritable();
|
||||
}
|
||||
|
||||
QString KConfigIniBackend::nonWritableErrorMessage() const
|
||||
{
|
||||
return tr("Configuration file \"%1\" not writable.\n").arg(filePath());
|
||||
}
|
||||
|
||||
void KConfigIniBackend::createEnclosing()
|
||||
{
|
||||
const QString file = filePath();
|
||||
if (file.isEmpty()) {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
// Create the containing dir, maybe it wasn't there
|
||||
QDir().mkpath(QFileInfo(file).absolutePath());
|
||||
}
|
||||
|
||||
void KConfigIniBackend::setFilePath(const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(QDir::isAbsolutePath(path));
|
||||
|
||||
const QFileInfo info(path);
|
||||
if (info.exists()) {
|
||||
setLocalFilePath(info.canonicalFilePath());
|
||||
return;
|
||||
}
|
||||
|
||||
if (QString filePath = info.dir().canonicalPath(); !filePath.isEmpty()) {
|
||||
filePath += QLatin1Char('/') + info.fileName();
|
||||
setLocalFilePath(filePath);
|
||||
} else {
|
||||
setLocalFilePath(path);
|
||||
}
|
||||
}
|
||||
|
||||
KConfigBase::AccessMode KConfigIniBackend::accessMode() const
|
||||
{
|
||||
if (filePath().isEmpty()) {
|
||||
return KConfigBase::NoAccess;
|
||||
}
|
||||
|
||||
if (isWritable()) {
|
||||
return KConfigBase::ReadWrite;
|
||||
}
|
||||
|
||||
return KConfigBase::ReadOnly;
|
||||
}
|
||||
|
||||
bool KConfigIniBackend::lock()
|
||||
{
|
||||
Q_ASSERT(!filePath().isEmpty());
|
||||
|
||||
m_mutex.lock();
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (!lockFile) {
|
||||
// handle content Uris properly
|
||||
if (filePath().startsWith(QLatin1String("content://"))) {
|
||||
// we can't create file at an arbitrary location, so use internal storage to create one
|
||||
|
||||
// NOTE: filename can be the same, but because this lock is short lived we may never have a collision
|
||||
lockFile = new QLockFile(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/")
|
||||
+ QFileInfo(filePath()).fileName() + QLatin1String(".lock"));
|
||||
} else {
|
||||
lockFile = new QLockFile(filePath() + QLatin1String(".lock"));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (!lockFile) {
|
||||
lockFile = new QLockFile(filePath() + QLatin1String(".lock"));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!lockFile->lock()) {
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
return lockFile->isLocked();
|
||||
}
|
||||
|
||||
void KConfigIniBackend::unlock()
|
||||
{
|
||||
lockFile->unlock();
|
||||
delete lockFile;
|
||||
lockFile = nullptr;
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
bool KConfigIniBackend::isLocked() const
|
||||
{
|
||||
return lockFile && lockFile->isLocked();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// serialize an escaped byte at the end of @param data
|
||||
// @param data should have room for 4 bytes
|
||||
char *escapeByte(char *data, unsigned char s)
|
||||
{
|
||||
static const char nibbleLookup[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
*data++ = '\\';
|
||||
*data++ = 'x';
|
||||
*data++ = nibbleLookup[s >> 4];
|
||||
*data++ = nibbleLookup[s & 0x0f];
|
||||
return data;
|
||||
}
|
||||
|
||||
// Struct that represents a multi-byte UTF-8 character.
|
||||
// This struct is used to keep track of bytes that seem to be valid
|
||||
// UTF-8.
|
||||
struct Utf8Char {
|
||||
public:
|
||||
unsigned char bytes[4];
|
||||
unsigned char count;
|
||||
unsigned char charLength;
|
||||
|
||||
Utf8Char()
|
||||
{
|
||||
clear();
|
||||
charLength = 0;
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
// Add a byte to the UTF8 character.
|
||||
// When an additional byte leads to an invalid character, return false.
|
||||
bool addByte(unsigned char b)
|
||||
{
|
||||
if (count == 0) {
|
||||
if (b > 0xc1 && (b & 0xe0) == 0xc0) {
|
||||
charLength = 2;
|
||||
} else if ((b & 0xf0) == 0xe0) {
|
||||
charLength = 3;
|
||||
} else if (b < 0xf5 && (b & 0xf8) == 0xf0) {
|
||||
charLength = 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
bytes[0] = b;
|
||||
count = 1;
|
||||
} else if (count < 4 && (b & 0xc0) == 0x80) {
|
||||
if (count == 1) {
|
||||
if (charLength == 3 && bytes[0] == 0xe0 && b < 0xa0) {
|
||||
return false; // overlong 3 byte sequence
|
||||
}
|
||||
if (charLength == 4) {
|
||||
if (bytes[0] == 0xf0 && b < 0x90) {
|
||||
return false; // overlong 4 byte sequence
|
||||
}
|
||||
if (bytes[0] == 0xf4 && b > 0x8f) {
|
||||
return false; // Unicode value larger than U+10FFFF
|
||||
}
|
||||
}
|
||||
}
|
||||
bytes[count++] = b;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Return true if Utf8Char contains one valid character.
|
||||
bool isComplete() const
|
||||
{
|
||||
return count > 0 && count == charLength;
|
||||
}
|
||||
// Add the bytes in this UTF8 character in escaped form to data.
|
||||
char *escapeBytes(char *data)
|
||||
{
|
||||
for (unsigned char i = 0; i < count; ++i) {
|
||||
data = escapeByte(data, bytes[i]);
|
||||
}
|
||||
clear();
|
||||
return data;
|
||||
}
|
||||
// Add the bytes of the UTF8 character to a buffer.
|
||||
// Only call this if isComplete() returns true.
|
||||
char *writeUtf8(char *data)
|
||||
{
|
||||
for (unsigned char i = 0; i < count; ++i) {
|
||||
*data++ = bytes[i];
|
||||
}
|
||||
clear();
|
||||
return data;
|
||||
}
|
||||
// Write the bytes in the UTF8 character literally, or, if the
|
||||
// character is not complete, write the escaped bytes.
|
||||
// This is useful to handle the state that remains after handling
|
||||
// all bytes in a buffer.
|
||||
char *write(char *data)
|
||||
{
|
||||
if (isComplete()) {
|
||||
data = writeUtf8(data);
|
||||
} else {
|
||||
data = escapeBytes(data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QByteArray KConfigIniBackend::stringToPrintable(const QByteArray &aString, StringType type)
|
||||
{
|
||||
const int len = aString.size();
|
||||
if (len == 0) {
|
||||
return aString;
|
||||
}
|
||||
|
||||
QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of len*4
|
||||
result.resize(len * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences
|
||||
const char *s = aString.constData();
|
||||
int i = 0;
|
||||
char *data = result.data();
|
||||
char *start = data;
|
||||
|
||||
// Protect leading space
|
||||
if (s[0] == ' ' && type != GroupString) {
|
||||
*data++ = '\\';
|
||||
*data++ = 's';
|
||||
++i;
|
||||
}
|
||||
Utf8Char utf8;
|
||||
|
||||
for (; i < len; ++i) {
|
||||
switch (s[i]) {
|
||||
default:
|
||||
if (utf8.addByte(s[i])) {
|
||||
break;
|
||||
} else {
|
||||
data = utf8.escapeBytes(data);
|
||||
}
|
||||
// The \n, \t, \r cases (all < 32) are handled below; we can ignore them here
|
||||
if (((unsigned char)s[i]) < 32) {
|
||||
goto doEscape;
|
||||
}
|
||||
// GroupString and KeyString should be valid UTF-8, but ValueString
|
||||
// can be a bytearray with non-UTF-8 bytes that should be escaped.
|
||||
if (type == ValueString && ((unsigned char)s[i]) >= 127) {
|
||||
goto doEscape;
|
||||
}
|
||||
*data++ = s[i];
|
||||
break;
|
||||
case '\n':
|
||||
*data++ = '\\';
|
||||
*data++ = 'n';
|
||||
break;
|
||||
case '\t':
|
||||
*data++ = '\\';
|
||||
*data++ = 't';
|
||||
break;
|
||||
case '\r':
|
||||
*data++ = '\\';
|
||||
*data++ = 'r';
|
||||
break;
|
||||
case '\\':
|
||||
*data++ = '\\';
|
||||
*data++ = '\\';
|
||||
break;
|
||||
case '=':
|
||||
if (type != KeyString) {
|
||||
*data++ = s[i];
|
||||
break;
|
||||
}
|
||||
goto doEscape;
|
||||
case '[':
|
||||
case ']':
|
||||
// Above chars are OK to put in *value* strings as plaintext
|
||||
if (type == ValueString) {
|
||||
*data++ = s[i];
|
||||
break;
|
||||
}
|
||||
doEscape:
|
||||
data = escapeByte(data, s[i]);
|
||||
break;
|
||||
}
|
||||
if (utf8.isComplete()) {
|
||||
data = utf8.writeUtf8(data);
|
||||
}
|
||||
}
|
||||
data = utf8.write(data);
|
||||
*data = 0;
|
||||
result.resize(data - start);
|
||||
|
||||
// Protect trailing space
|
||||
if (result.endsWith(' ') && type != GroupString) {
|
||||
result.replace(result.length() - 1, 1, "\\s");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char KConfigIniBackend::charFromHex(const char *str, const QFile &file, int line)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ret <<= 4;
|
||||
quint8 c = quint8(str[i]);
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
ret |= c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
ret |= c - 'a' + 0x0a;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
ret |= c - 'A' + 0x0a;
|
||||
} else {
|
||||
QByteArray e(str, 2);
|
||||
e.prepend("\\x");
|
||||
qCWarning(KCONFIG_CORE_LOG) << warningProlog(file, line) << "Invalid hex character " << c << " in \\x<nn>-type escape sequence \"" << e.constData()
|
||||
<< "\".";
|
||||
return 'x';
|
||||
}
|
||||
}
|
||||
return char(ret);
|
||||
}
|
||||
|
||||
void KConfigIniBackend::printableToString(QByteArrayView &aString, const QFile &file, int line)
|
||||
{
|
||||
if (aString.isEmpty() || aString.indexOf('\\') == -1) {
|
||||
return;
|
||||
}
|
||||
aString = aString.trimmed();
|
||||
int l = aString.size();
|
||||
char *r = const_cast<char *>(aString.data());
|
||||
char *str = r;
|
||||
|
||||
for (int i = 0; i < l; i++, r++) {
|
||||
if (str[i] != '\\') {
|
||||
*r = str[i];
|
||||
} else {
|
||||
// Probable escape sequence
|
||||
++i;
|
||||
if (i >= l) { // Line ends after backslash - stop.
|
||||
*r = '\\';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (str[i]) {
|
||||
case 's':
|
||||
*r = ' ';
|
||||
break;
|
||||
case 't':
|
||||
*r = '\t';
|
||||
break;
|
||||
case 'n':
|
||||
*r = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*r = '\r';
|
||||
break;
|
||||
case '\\':
|
||||
*r = '\\';
|
||||
break;
|
||||
case ';':
|
||||
// not really an escape sequence, but allowed in .desktop files, don't strip '\;' from the string
|
||||
*r = '\\';
|
||||
++r;
|
||||
*r = ';';
|
||||
break;
|
||||
case ',':
|
||||
// not really an escape sequence, but allowed in .desktop files, don't strip '\,' from the string
|
||||
*r = '\\';
|
||||
++r;
|
||||
*r = ',';
|
||||
break;
|
||||
case 'x':
|
||||
if (i + 2 < l) {
|
||||
*r = charFromHex(str + i + 1, file, line);
|
||||
i += 2;
|
||||
} else {
|
||||
*r = 'x';
|
||||
i = l - 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*r = '\\';
|
||||
qCWarning(KCONFIG_CORE_LOG).noquote() << warningProlog(file, line) << QStringLiteral("Invalid escape sequence: «\\%1»").arg(str[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
aString.truncate(r - aString.constData());
|
||||
}
|
||||
|
||||
QString KConfigIniBackend::filePath() const
|
||||
{
|
||||
return mLocalFilePath;
|
||||
}
|
||||
|
||||
void KConfigIniBackend::setLocalFilePath(const QString &file)
|
||||
{
|
||||
mLocalFilePath = file;
|
||||
}
|
||||
|
||||
#include "moc_kconfigini_p.cpp"
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGINI_P_H
|
||||
#define KCONFIGINI_P_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QSharedData>
|
||||
#include <kconfigbase.h>
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
class QLockFile;
|
||||
class QIODevice;
|
||||
class KEntryMap;
|
||||
|
||||
class KConfigIniBackend : public QSharedData
|
||||
{
|
||||
Q_GADGET
|
||||
Q_DECLARE_TR_FUNCTIONS(KConfigIniBackend)
|
||||
|
||||
private:
|
||||
QLockFile *lockFile;
|
||||
QMutex m_mutex;
|
||||
|
||||
public:
|
||||
KConfigIniBackend();
|
||||
|
||||
/** Allows the behaviour of parseConfig() to be tuned */
|
||||
enum ParseOption {
|
||||
ParseGlobal = 1, /// entries should be marked as @em global
|
||||
ParseDefaults = 2, /// entries should be marked as @em default
|
||||
ParseExpansions = 4, /// entries are allowed to be marked as @em expandable
|
||||
};
|
||||
Q_FLAG(ParseOption)
|
||||
/// @typedef typedef QFlags<ParseOption> ParseOptions
|
||||
Q_DECLARE_FLAGS(ParseOptions, ParseOption)
|
||||
|
||||
/** Allows the behaviour of writeConfig() to be tuned */
|
||||
enum WriteOption {
|
||||
WriteGlobal = 1 /// only write entries marked as "global"
|
||||
};
|
||||
Q_FLAG(WriteOption)
|
||||
/// @typedef typedef QFlags<WriteOption> WriteOptions
|
||||
Q_DECLARE_FLAGS(WriteOptions, WriteOption)
|
||||
|
||||
/** Return value from parseConfig() */
|
||||
enum ParseInfo {
|
||||
ParseOk, /// the configuration was opened read/write
|
||||
ParseImmutable, /// the configuration is @em immutable
|
||||
ParseOpenError, /// the configuration could not be opened
|
||||
};
|
||||
|
||||
ParseInfo parseConfig(const QByteArray &locale, KEntryMap &entryMap, ParseOptions options);
|
||||
ParseInfo parseConfig(const QByteArray &locale, KEntryMap &entryMap, ParseOptions options, bool merging);
|
||||
bool writeConfig(const QByteArray &locale, KEntryMap &entryMap, WriteOptions options);
|
||||
|
||||
bool isWritable() const;
|
||||
QString nonWritableErrorMessage() const;
|
||||
KConfigBase::AccessMode accessMode() const;
|
||||
void createEnclosing();
|
||||
void setFilePath(const QString &path);
|
||||
bool lock();
|
||||
void unlock();
|
||||
bool isLocked() const;
|
||||
|
||||
/** @return the absolute path to the object */
|
||||
QString filePath() const;
|
||||
|
||||
private:
|
||||
enum StringType {
|
||||
GroupString = 0,
|
||||
KeyString = 1,
|
||||
ValueString = 2,
|
||||
};
|
||||
// Warning: this modifies data in-place. Other QByteArrayView objects referencing the same buffer
|
||||
// fragment will get their data modified too.
|
||||
static void printableToString(QByteArrayView &aString, const QFile &file, int line);
|
||||
static QByteArray stringToPrintable(const QByteArray &aString, StringType type);
|
||||
static char charFromHex(const char *str, const QFile &file, int line);
|
||||
static QString warningProlog(const QFile &file, int line);
|
||||
|
||||
void writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map);
|
||||
void writeEntries(const QByteArray &locale, QIODevice &file, const KEntryMap &map, bool defaultGroup, bool &firstEntry);
|
||||
|
||||
void setLocalFilePath(const QString &file);
|
||||
|
||||
QString mLocalFilePath;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigIniBackend::ParseOptions)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KConfigIniBackend::WriteOptions)
|
||||
|
||||
#endif // KCONFIGINI_P_H
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigwatcher.h"
|
||||
|
||||
#include "config-kconfig.h"
|
||||
#include "kconfig_core_log_settings.h"
|
||||
|
||||
#if KCONFIG_USE_DBUS
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
#include "dbussanitizer_p.h"
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHash>
|
||||
#include <QThreadStorage>
|
||||
|
||||
class KConfigWatcherPrivate
|
||||
{
|
||||
public:
|
||||
KSharedConfig::Ptr m_config;
|
||||
};
|
||||
|
||||
KConfigWatcher::Ptr KConfigWatcher::create(const KSharedConfig::Ptr &config)
|
||||
{
|
||||
static QThreadStorage<QHash<KSharedConfig *, QWeakPointer<KConfigWatcher>>> watcherList;
|
||||
|
||||
auto c = config.data();
|
||||
KConfigWatcher::Ptr watcher;
|
||||
|
||||
if (!watcherList.localData().contains(c)) {
|
||||
watcher = KConfigWatcher::Ptr(new KConfigWatcher(config));
|
||||
|
||||
watcherList.localData().insert(c, watcher.toWeakRef());
|
||||
|
||||
QObject::connect(watcher.data(), &QObject::destroyed, [c]() {
|
||||
watcherList.localData().remove(c);
|
||||
});
|
||||
}
|
||||
return watcherList.localData().value(c).toStrongRef();
|
||||
}
|
||||
|
||||
KConfigWatcher::KConfigWatcher(const KSharedConfig::Ptr &config)
|
||||
: QObject(nullptr)
|
||||
, d(new KConfigWatcherPrivate)
|
||||
{
|
||||
Q_ASSERT(config);
|
||||
d->m_config = config;
|
||||
if (config->name().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Watching absolute paths is not supported and also makes no sense.
|
||||
const bool isAbsolutePath = config->name().at(0) == QLatin1Char('/');
|
||||
if (isAbsolutePath) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Watching absolute paths is not supported" << config->name();
|
||||
return;
|
||||
}
|
||||
|
||||
#if KCONFIG_USE_DBUS
|
||||
qDBusRegisterMetaType<QByteArrayList>();
|
||||
qDBusRegisterMetaType<QHash<QString, QByteArrayList>>();
|
||||
|
||||
QStringList watchedPaths = d->m_config->additionalConfigSources();
|
||||
for (QString &file : watchedPaths) {
|
||||
file.prepend(QLatin1Char('/'));
|
||||
}
|
||||
watchedPaths.prepend(kconfigDBusSanitizePath(QLatin1Char('/') + d->m_config->name()));
|
||||
|
||||
if (d->m_config->openFlags() & KConfig::IncludeGlobals) {
|
||||
watchedPaths << QStringLiteral("/kdeglobals");
|
||||
}
|
||||
|
||||
for (const QString &path : std::as_const(watchedPaths)) {
|
||||
QDBusConnection::sessionBus().connect(QString(),
|
||||
path,
|
||||
QStringLiteral("org.kde.kconfig.notify"),
|
||||
QStringLiteral("ConfigChanged"),
|
||||
this,
|
||||
// clang-format off
|
||||
SLOT(onConfigChangeNotification(QHash<QString,QByteArrayList>))
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
KConfigWatcher::~KConfigWatcher() = default;
|
||||
|
||||
KSharedConfig::Ptr KConfigWatcher::config() const
|
||||
{
|
||||
return d->m_config;
|
||||
}
|
||||
|
||||
void KConfigWatcher::onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes)
|
||||
{
|
||||
// should we ever need it we can determine the file changed with QDbusContext::message().path(), but it doesn't seem too useful
|
||||
|
||||
d->m_config->reparseConfiguration();
|
||||
|
||||
for (auto it = changes.constBegin(); it != changes.constEnd(); it++) {
|
||||
KConfigGroup group = d->m_config->group(QString()); // top level group
|
||||
const auto parts = it.key().split(QLatin1Char('\x1d')); // magic char, see KConfig
|
||||
for (const QString &groupName : parts) {
|
||||
group = group.group(groupName);
|
||||
}
|
||||
Q_EMIT configChanged(group, it.value());
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kconfigwatcher.cpp"
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGWATCHER_H
|
||||
#define KCONFIGWATCHER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
class KConfigWatcherPrivate;
|
||||
|
||||
/**
|
||||
* \class KConfigWatcher kconfigwatcher.h <KConfigWatcher>
|
||||
*
|
||||
* Notifies when another client has updated this config file with the Notify flag set.
|
||||
* @since 5.51
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KConfigWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QSharedPointer<KConfigWatcher> Ptr;
|
||||
|
||||
/**
|
||||
* Instantiate a ConfigWatcher for a given config
|
||||
*
|
||||
* @note any additional config sources should be set before this point.
|
||||
*/
|
||||
static Ptr create(const KSharedConfig::Ptr &config);
|
||||
|
||||
~KConfigWatcher() override;
|
||||
|
||||
/**
|
||||
* Returns the config being watched
|
||||
* @since 5.66
|
||||
*/
|
||||
KSharedConfig::Ptr config() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when a config group has changed
|
||||
* The config will be reloaded before this signal is emitted
|
||||
*
|
||||
* @arg group the config group that has changed
|
||||
* @arg names a list of entries that have changed within that group (UTF-8 encoded)
|
||||
*/
|
||||
void configChanged(const KConfigGroup &group, const QByteArrayList &names);
|
||||
|
||||
private Q_SLOTS:
|
||||
KCONFIGCORE_NO_EXPORT void onConfigChangeNotification(const QHash<QString, QByteArrayList> &changes);
|
||||
|
||||
private:
|
||||
KCONFIGCORE_NO_EXPORT explicit KConfigWatcher(const KSharedConfig::Ptr &config);
|
||||
Q_DISABLE_COPY(KConfigWatcher)
|
||||
const QScopedPointer<KConfigWatcherPrivate> d;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of KOrganizer.
|
||||
SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCORECONFIGSKELETON_P_H
|
||||
#define KCORECONFIGSKELETON_P_H
|
||||
|
||||
#include "kcoreconfigskeleton.h"
|
||||
|
||||
class KCoreConfigSkeletonPrivate
|
||||
{
|
||||
public:
|
||||
KCoreConfigSkeletonPrivate()
|
||||
: mCurrentGroup(QStringLiteral("No Group"))
|
||||
, mUseDefaults(false)
|
||||
{
|
||||
}
|
||||
~KCoreConfigSkeletonPrivate()
|
||||
{
|
||||
qDeleteAll(mItems);
|
||||
}
|
||||
QString mCurrentGroup;
|
||||
|
||||
KSharedConfig::Ptr mConfig; // pointer to KConfig object
|
||||
|
||||
KConfigSkeletonItem::List mItems;
|
||||
KConfigSkeletonItem::Dict mItemDict;
|
||||
|
||||
bool mUseDefaults;
|
||||
};
|
||||
|
||||
class KConfigSkeletonItemPrivate
|
||||
{
|
||||
public:
|
||||
KConfigSkeletonItemPrivate()
|
||||
: mIsImmutable(true)
|
||||
, mWriteFlags(KConfigBase::Normal)
|
||||
{
|
||||
}
|
||||
virtual ~KConfigSkeletonItemPrivate();
|
||||
bool mIsImmutable; ///< Indicates this item is immutable
|
||||
KConfigBase::WriteConfigFlags mWriteFlags; ///< The flags to pass to calls of writeEntry() and revertToDefault()
|
||||
|
||||
QString mLabel; ///< The label for this item
|
||||
QString mToolTip; ///< The ToolTip text for this item
|
||||
QString mWhatsThis; ///< The What's This text for this item
|
||||
KConfigGroup mConfigGroup; ///< KConfigGroup, allow to read/write item in nested groups
|
||||
|
||||
// HACK: Necessary to avoid introducing new virtuals in KConfigSkeletonItem
|
||||
std::function<bool()> mIsDefaultImpl;
|
||||
std::function<bool()> mIsSaveNeededImpl;
|
||||
std::function<QVariant()> mGetDefaultImpl;
|
||||
};
|
||||
|
||||
class KPropertySkeletonItemPrivate : public KConfigSkeletonItemPrivate
|
||||
{
|
||||
public:
|
||||
KPropertySkeletonItemPrivate(QObject *object, const QByteArray &propertyName, const QVariant &defaultValue)
|
||||
: KConfigSkeletonItemPrivate()
|
||||
, mObject(object)
|
||||
, mPropertyName(propertyName)
|
||||
, mDefaultValue(defaultValue)
|
||||
, mConstDefaultValue(defaultValue)
|
||||
{
|
||||
mIsImmutable = false;
|
||||
}
|
||||
|
||||
QObject *mObject;
|
||||
const QByteArray mPropertyName;
|
||||
QVariant mDefaultValue;
|
||||
const QVariant mConstDefaultValue;
|
||||
QVariant mReference;
|
||||
QVariant mLoadedValue;
|
||||
std::function<void()> mNotifyFunction;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Pietro Iglio <iglio@kde.org>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kdesktopfile.h"
|
||||
|
||||
#include "kauthorized.h"
|
||||
#include "kconfig_core_log_settings.h"
|
||||
#include "kconfig_p.h"
|
||||
#include "kconfiggroup.h"
|
||||
#include "kconfigini_p.h"
|
||||
#include "kdesktopfileaction.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
class KDesktopFilePrivate : public KConfigPrivate
|
||||
{
|
||||
public:
|
||||
KDesktopFilePrivate(QStandardPaths::StandardLocation resourceType, const QString &fileName)
|
||||
: KConfigPrivate(KConfig::NoGlobals, resourceType)
|
||||
{
|
||||
changeFileName(fileName);
|
||||
}
|
||||
KConfigGroup desktopGroup;
|
||||
};
|
||||
|
||||
KDesktopFile::KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName)
|
||||
: KConfig(*new KDesktopFilePrivate(resourceType, fileName))
|
||||
{
|
||||
Q_D(KDesktopFile);
|
||||
reparseConfiguration();
|
||||
d->desktopGroup = KConfigGroup(this, QStringLiteral("Desktop Entry"));
|
||||
}
|
||||
|
||||
KDesktopFile::KDesktopFile(const QString &fileName)
|
||||
: KDesktopFile(QStandardPaths::ApplicationsLocation, fileName)
|
||||
{
|
||||
}
|
||||
|
||||
KDesktopFile::~KDesktopFile() = default;
|
||||
|
||||
KConfigGroup KDesktopFile::desktopGroup() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup;
|
||||
}
|
||||
|
||||
QString KDesktopFile::locateLocal(const QString &path)
|
||||
{
|
||||
static const QLatin1Char slash('/');
|
||||
|
||||
// Relative to config? (e.g. for autostart)
|
||||
const QStringList genericConfig = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation);
|
||||
// Iterate from the last item since some items may be subfolders of others.
|
||||
auto it = std::find_if(genericConfig.crbegin(), genericConfig.crend(), [&path](const QString &dir) {
|
||||
return path.startsWith(dir + slash);
|
||||
});
|
||||
if (it != genericConfig.crend()) {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + slash + QStringView(path).mid(it->size() + 1);
|
||||
}
|
||||
|
||||
QString relativePath;
|
||||
// Relative to xdg data dir? (much more common)
|
||||
const QStringList lstGenericDataLocation = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
|
||||
for (const QString &dir : lstGenericDataLocation) {
|
||||
if (path.startsWith(dir + slash)) {
|
||||
relativePath = path.mid(dir.length() + 1);
|
||||
}
|
||||
}
|
||||
if (relativePath.isEmpty()) {
|
||||
// What now? The desktop file doesn't come from XDG_DATA_DIRS. Use filename only and hope for the best.
|
||||
relativePath = path.mid(path.lastIndexOf(slash) + 1);
|
||||
}
|
||||
return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + slash + relativePath;
|
||||
}
|
||||
|
||||
bool KDesktopFile::isDesktopFile(const QString &path)
|
||||
{
|
||||
return path.endsWith(QLatin1String(".desktop"));
|
||||
}
|
||||
|
||||
bool KDesktopFile::isAuthorizedDesktopFile(const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return false; // Empty paths are not ok.
|
||||
}
|
||||
|
||||
if (QDir::isRelativePath(path)) {
|
||||
return true; // Relative paths are ok.
|
||||
}
|
||||
|
||||
const QString realPath = QFileInfo(path).canonicalFilePath();
|
||||
if (realPath.isEmpty()) {
|
||||
return false; // File doesn't exist.
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
static constexpr Qt::CaseSensitivity sensitivity = Qt::CaseSensitive;
|
||||
#else
|
||||
static constexpr Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive;
|
||||
#endif
|
||||
|
||||
// Check if the .desktop file is installed as part of KDE or XDG.
|
||||
const QStringList appsDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
|
||||
auto it = std::find_if(appsDirs.cbegin(), appsDirs.cend(), [&realPath, &path](const QString &prefix) {
|
||||
QFileInfo info(prefix);
|
||||
return info.exists() && info.isDir() && (realPath.startsWith(info.canonicalFilePath(), sensitivity) || path.startsWith(info.canonicalFilePath()));
|
||||
});
|
||||
if (it != appsDirs.cend()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString autostartDir = QStringLiteral("autostart/");
|
||||
const QStringList lstConfigPath = QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation);
|
||||
auto configIt = std::find_if(lstConfigPath.cbegin(), lstConfigPath.cend(), [&realPath, &autostartDir](const QString &xdgDataPrefix) {
|
||||
QFileInfo info(xdgDataPrefix);
|
||||
if (info.exists() && info.isDir()) {
|
||||
const QString prefix = info.canonicalFilePath();
|
||||
return realPath.startsWith(prefix + QLatin1Char('/') + autostartDir, sensitivity);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (configIt != lstConfigPath.cend()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forbid desktop files outside of standard locations if kiosk is set so
|
||||
if (!KAuthorized::authorize(QStringLiteral("run_desktop_files"))) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Access to" << path << "denied because of 'run_desktop_files' restriction.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not otherwise permitted, so only allow if the file is executable, or if
|
||||
// owned by root (uid == 0)
|
||||
QFileInfo entryInfo(path);
|
||||
if (entryInfo.isExecutable() || entryInfo.ownerId() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
qCInfo(KCONFIG_CORE_LOG) << "Access to" << path << "denied, not owned by root and executable flag not set.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QString KDesktopFile::readType() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("Type", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readIcon() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("Icon", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readName() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("Name", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readComment() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("Comment", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readGenericName() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("GenericName", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readPath() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
// NOT readPathEntry, it is not XDG-compliant: it performs
|
||||
// various expansions, like $HOME. Note that the expansion
|
||||
// behaviour still happens if the "e" flag is set, maintaining
|
||||
// backwards compatibility.
|
||||
return d->desktopGroup.readEntry("Path", QString());
|
||||
}
|
||||
|
||||
QString KDesktopFile::readUrl() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
if (hasDeviceType()) {
|
||||
return d->desktopGroup.readEntry("MountPoint", QString());
|
||||
} else {
|
||||
// NOT readPathEntry (see readPath())
|
||||
QString url = d->desktopGroup.readEntry("URL", QString());
|
||||
if (!url.isEmpty() && !QDir::isRelativePath(url)) {
|
||||
// Handle absolute paths as such (i.e. we need to escape them)
|
||||
return QUrl::fromLocalFile(url).toString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList KDesktopFile::readActions() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readXdgListEntry("Actions");
|
||||
}
|
||||
|
||||
QStringList KDesktopFile::readMimeTypes() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readXdgListEntry("MimeType");
|
||||
}
|
||||
|
||||
KConfigGroup KDesktopFile::actionGroup(const QString &group)
|
||||
{
|
||||
return KConfigGroup(this, QLatin1String("Desktop Action ") + group);
|
||||
}
|
||||
|
||||
KConfigGroup KDesktopFile::actionGroup(const QString &group) const
|
||||
{
|
||||
return const_cast<KDesktopFile *>(this)->actionGroup(group);
|
||||
}
|
||||
|
||||
bool KDesktopFile::hasActionGroup(const QString &group) const
|
||||
{
|
||||
return hasGroup(QString(QLatin1String("Desktop Action ") + group));
|
||||
}
|
||||
|
||||
bool KDesktopFile::hasLinkType() const
|
||||
{
|
||||
return readType() == QLatin1String("Link");
|
||||
}
|
||||
|
||||
bool KDesktopFile::hasApplicationType() const
|
||||
{
|
||||
return readType() == QLatin1String("Application");
|
||||
}
|
||||
|
||||
bool KDesktopFile::hasDeviceType() const
|
||||
{
|
||||
return readType() == QLatin1String("FSDevice");
|
||||
}
|
||||
|
||||
bool KDesktopFile::tryExec() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
// Test for TryExec and "X-KDE-AuthorizeAction"
|
||||
// NOT readPathEntry (see readPath())
|
||||
const QString te = d->desktopGroup.readEntry("TryExec", QString());
|
||||
if (!te.isEmpty() && QStandardPaths::findExecutable(te).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
const QStringList list = d->desktopGroup.readEntry("X-KDE-AuthorizeAction", QStringList());
|
||||
const auto isNotAuthorized = std::any_of(list.cbegin(), list.cend(), [](const QString &action) {
|
||||
return !KAuthorized::authorize(action.trimmed());
|
||||
});
|
||||
if (isNotAuthorized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See also KService::username()
|
||||
if (d->desktopGroup.readEntry("X-KDE-SubstituteUID", false)) {
|
||||
QString user = d->desktopGroup.readEntry("X-KDE-Username", QString());
|
||||
if (user.isEmpty()) {
|
||||
user = qEnvironmentVariable("ADMIN_ACCOUNT"), QStringLiteral("root");
|
||||
}
|
||||
if (!KAuthorized::authorize(QLatin1String("user/") + user)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString KDesktopFile::readDocPath() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readPathEntry("X-DocPath", QString());
|
||||
}
|
||||
|
||||
KDesktopFile *KDesktopFile::copyTo(const QString &file) const
|
||||
{
|
||||
KDesktopFile *config = new KDesktopFile(QString());
|
||||
this->KConfig::copyTo(file, config);
|
||||
return config;
|
||||
}
|
||||
|
||||
QString KDesktopFile::fileName() const
|
||||
{
|
||||
return name();
|
||||
}
|
||||
|
||||
bool KDesktopFile::noDisplay() const
|
||||
{
|
||||
Q_D(const KDesktopFile);
|
||||
return d->desktopGroup.readEntry("NoDisplay", false);
|
||||
}
|
||||
|
||||
QList<KDesktopFileAction> KDesktopFile::actions() const
|
||||
{
|
||||
QList<KDesktopFileAction> desktopFileActions;
|
||||
const QStringList actionKeys = readActions();
|
||||
for (const QString &actionKey : actionKeys) {
|
||||
const KConfigGroup grp = actionGroup(actionKey);
|
||||
desktopFileActions << KDesktopFileAction(actionKey, grp.readEntry("Name"), grp.readEntry("Icon"), grp.readEntry("Exec"), fileName());
|
||||
}
|
||||
return desktopFileActions;
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Pietro Iglio <iglio@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KDESKTOPFILE_H
|
||||
#define KDESKTOPFILE_H
|
||||
|
||||
#include <kconfig.h>
|
||||
|
||||
class KConfigGroup;
|
||||
class KDesktopFileAction;
|
||||
class KDesktopFilePrivate;
|
||||
|
||||
/**
|
||||
* \class KDesktopFile kdesktopfile.h <KDesktopFile>
|
||||
*
|
||||
* %KDE Desktop File Management.
|
||||
* This class implements %KDE's support for the freedesktop.org
|
||||
* <em>Desktop Entry Spec</em>.
|
||||
*
|
||||
* @author Pietro Iglio <iglio@kde.org>
|
||||
* @see KConfigBase KConfig
|
||||
* @see <a href="https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html">Desktop Entry Spec</a>
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KDesktopFile : public KConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructs a KDesktopFile object.
|
||||
*
|
||||
* See QStandardPaths for more information on resources.
|
||||
*
|
||||
* @param resourceType Allows you to change what sort of resource
|
||||
* to search for if @p fileName is not absolute.
|
||||
* For instance, you might want to specify GenericConfigLocation.
|
||||
* @param fileName The name or path of the desktop file. If it
|
||||
* is not absolute, it will be located
|
||||
* using the resource type @p resType.
|
||||
*/
|
||||
explicit KDesktopFile(QStandardPaths::StandardLocation resourceType, const QString &fileName);
|
||||
|
||||
/**
|
||||
* Constructs a KDesktopFile object.
|
||||
*
|
||||
* See QStandardPaths for more information on resources.
|
||||
*
|
||||
* @param fileName The name or path of the desktop file. If it
|
||||
* is not absolute, it will be located
|
||||
* using the resource type ApplicationsLocation
|
||||
*/
|
||||
explicit KDesktopFile(const QString &fileName);
|
||||
|
||||
/**
|
||||
* Destructs the KDesktopFile object.
|
||||
*
|
||||
* Writes back any changed configuration entries.
|
||||
*/
|
||||
~KDesktopFile() override;
|
||||
|
||||
/**
|
||||
* Checks whether this is really a desktop file.
|
||||
*
|
||||
* The check is performed looking at the file extension (the file is not
|
||||
* opened).
|
||||
* Currently, the only valid extension is ".desktop".
|
||||
* @param path the path of the file to check
|
||||
* @return true if the file appears to be a desktop file.
|
||||
*/
|
||||
static bool isDesktopFile(const QString &path);
|
||||
|
||||
/**
|
||||
* Checks whether the user is authorized to run this desktop file.
|
||||
* By default users are authorized to run all desktop files but
|
||||
* the KIOSK framework can be used to activate certain restrictions.
|
||||
* See README.kiosk for more information.
|
||||
*
|
||||
* Note that desktop files that are not in a standard location (as
|
||||
* specified by XDG_DATA_DIRS) must have their executable bit set
|
||||
* to be authorized, regardless of KIOSK settings, to prevent users
|
||||
* from inadvertently running trojan desktop files.
|
||||
*
|
||||
* @param path the file to check
|
||||
* @return true if the user is authorized to run the file
|
||||
*/
|
||||
static bool isAuthorizedDesktopFile(const QString &path);
|
||||
|
||||
/**
|
||||
* Returns the location where changes for the .desktop file @p path
|
||||
* should be written to.
|
||||
*/
|
||||
static QString locateLocal(const QString &path);
|
||||
|
||||
/**
|
||||
* Returns the main config group (named "Desktop Entry") in a .desktop file.
|
||||
*/
|
||||
KConfigGroup desktopGroup() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "Type=" entry.
|
||||
* @return the type or QString() if not specified
|
||||
*/
|
||||
QString readType() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "Icon=" entry.
|
||||
* @return the icon or QString() if not specified
|
||||
*/
|
||||
QString readIcon() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "Name=" entry.
|
||||
* @return the name or QString() if not specified
|
||||
*/
|
||||
QString readName() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "Comment=" entry.
|
||||
* @return the comment or QString() if not specified
|
||||
*/
|
||||
QString readComment() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "GenericName=" entry.
|
||||
* @return the generic name or QString() if not specified
|
||||
*/
|
||||
QString readGenericName() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "Path=" entry.
|
||||
* @return the path or QString() if not specified
|
||||
*/
|
||||
QString readPath() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "URL=" entry.
|
||||
* @return the URL or QString() if not specified
|
||||
*/
|
||||
QString readUrl() const;
|
||||
|
||||
/**
|
||||
* Returns a list of the "Actions=" entries.
|
||||
* @return the list of actions
|
||||
*/
|
||||
QStringList readActions() const;
|
||||
|
||||
/**
|
||||
* Returns a list of the "MimeType=" entries.
|
||||
* @return the list of mime types
|
||||
* @since 5.15
|
||||
*/
|
||||
QStringList readMimeTypes() const;
|
||||
|
||||
/**
|
||||
* Sets the desktop action group.
|
||||
* @param group the new action group
|
||||
*/
|
||||
KConfigGroup actionGroup(const QString &group);
|
||||
KConfigGroup actionGroup(const QString &group) const;
|
||||
|
||||
/**
|
||||
* Returns true if the action group exists, false otherwise
|
||||
* @param group the action group to test
|
||||
* @return true if the action group exists
|
||||
*/
|
||||
bool hasActionGroup(const QString &group) const;
|
||||
|
||||
/**
|
||||
* Checks whether there is a "Type=Link" entry.
|
||||
*
|
||||
* The link points to the "URL=" entry.
|
||||
* @return true if there is a "Type=Link" entry
|
||||
*/
|
||||
bool hasLinkType() const;
|
||||
|
||||
/**
|
||||
* Checks whether there is an entry "Type=Application".
|
||||
* @return true if there is a "Type=Application" entry
|
||||
*/
|
||||
bool hasApplicationType() const;
|
||||
|
||||
/**
|
||||
* Checks whether there is an entry "Type=FSDevice".
|
||||
* @return true if there is a "Type=FSDevice" entry
|
||||
*/
|
||||
bool hasDeviceType() const;
|
||||
|
||||
/**
|
||||
* Checks whether the TryExec field contains a binary
|
||||
* which is found on the local system.
|
||||
* @return true if TryExec contains an existing binary
|
||||
*/
|
||||
bool tryExec() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the "X-DocPath=" Or "DocPath=" entry.
|
||||
* @return The value of the "X-DocPath=" Or "DocPath=" entry.
|
||||
*/
|
||||
QString readDocPath() const;
|
||||
|
||||
/**
|
||||
* Whether the entry should be suppressed in menus.
|
||||
* This handles the NoDisplay key
|
||||
* @return true to suppress this desktop file
|
||||
* @since 4.1
|
||||
*/
|
||||
bool noDisplay() const;
|
||||
|
||||
/**
|
||||
* Copies all entries from this config object to a new
|
||||
* KDesktopFile object that will save itself to @p file.
|
||||
*
|
||||
* Actual saving to @p file happens when the returned object is
|
||||
* destructed or when sync() is called upon it.
|
||||
*
|
||||
* @param file the new KDesktopFile object it will save itself to.
|
||||
*/
|
||||
KDesktopFile *copyTo(const QString &file) const;
|
||||
|
||||
/**
|
||||
* Returns the name of the .desktop file that was used to construct this KDesktopFile.
|
||||
*/
|
||||
QString fileName() const;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
QList<KDesktopFileAction> actions() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KDesktopFile)
|
||||
|
||||
Q_DECLARE_PRIVATE(KDesktopFile)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include "kdesktopfileaction.h"
|
||||
#include <QSharedData>
|
||||
|
||||
class KDesktopFileActionPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QString m_internalActionKey;
|
||||
QString m_name;
|
||||
QString m_icon;
|
||||
QString m_exec;
|
||||
QString m_desktopFilePath;
|
||||
|
||||
KDesktopFileActionPrivate(const QString &name, const QString &text, const QString &icon, const QString &exec, const QString &desktopFilePath)
|
||||
: m_internalActionKey(name)
|
||||
, m_name(text)
|
||||
, m_icon(icon)
|
||||
, m_exec(exec)
|
||||
, m_desktopFilePath(desktopFilePath)
|
||||
{
|
||||
}
|
||||
KDesktopFileActionPrivate() = default;
|
||||
};
|
||||
|
||||
KDesktopFileAction::KDesktopFileAction(const QString &name, const QString &text, const QString &icon, const QString &exec, const QString &desktopFilePath)
|
||||
: d(new KDesktopFileActionPrivate(name, text, icon, exec, desktopFilePath))
|
||||
{
|
||||
}
|
||||
|
||||
KDesktopFileAction::KDesktopFileAction()
|
||||
: d(new KDesktopFileActionPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
KDesktopFileAction::KDesktopFileAction(const KDesktopFileAction &other) = default;
|
||||
KDesktopFileAction &KDesktopFileAction::operator=(const KDesktopFileAction &other) = default;
|
||||
KDesktopFileAction::KDesktopFileAction(KDesktopFileAction &&other) = default;
|
||||
KDesktopFileAction &KDesktopFileAction::operator=(KDesktopFileAction &&other) = default;
|
||||
KDesktopFileAction::~KDesktopFileAction() = default;
|
||||
|
||||
QString KDesktopFileAction::actionsKey() const
|
||||
{
|
||||
return d->m_internalActionKey;
|
||||
}
|
||||
|
||||
QString KDesktopFileAction::desktopFilePath() const
|
||||
{
|
||||
return d->m_desktopFilePath;
|
||||
}
|
||||
|
||||
QString KDesktopFileAction::name() const
|
||||
{
|
||||
return d->m_name;
|
||||
}
|
||||
|
||||
QString KDesktopFileAction::icon() const
|
||||
{
|
||||
return d->m_icon;
|
||||
}
|
||||
|
||||
QString KDesktopFileAction::exec() const
|
||||
{
|
||||
return d->m_exec;
|
||||
}
|
||||
|
||||
bool KDesktopFileAction::isSeparator() const
|
||||
{
|
||||
return d->m_internalActionKey == QLatin1String("_SEPARATOR_");
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KDESKTOPFILEACTION_H
|
||||
#define KDESKTOPFILEACTION_H
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
#include <QSharedDataPointer>
|
||||
#include <QString>
|
||||
|
||||
class KDesktopFileActionPrivate;
|
||||
|
||||
/**
|
||||
* Class for representing an Action of a desktop file
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KDesktopFileAction
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct an empty KDesktopFileAction. Needed so the Action can be stored in containers that require type T to be
|
||||
* default constructible (e.g. QVariant).
|
||||
*/
|
||||
explicit KDesktopFileAction();
|
||||
/**
|
||||
* Construct a KDesktopFileAction with all required properties
|
||||
*/
|
||||
explicit KDesktopFileAction(const QString &name, const QString &text, const QString &icon, const QString &exec, const QString &desktopFilePath);
|
||||
|
||||
KDesktopFileAction(const KDesktopFileAction &other);
|
||||
KDesktopFileAction &operator=(const KDesktopFileAction &other);
|
||||
KDesktopFileAction(KDesktopFileAction &&other);
|
||||
KDesktopFileAction &operator=(KDesktopFileAction &&other);
|
||||
~KDesktopFileAction();
|
||||
|
||||
/**
|
||||
* @return the action's internal name
|
||||
* For instance Actions=Setup;... and the group [Desktop Action Setup]
|
||||
* define an action with the name "Setup".
|
||||
*/
|
||||
QString actionsKey() const;
|
||||
|
||||
/**
|
||||
* @return Path of the desktop file this action was loaded from
|
||||
*/
|
||||
QString desktopFilePath() const;
|
||||
|
||||
/**
|
||||
* @return the action's Name, as defined by the Name key in the desktop action group
|
||||
*/
|
||||
QString name() const;
|
||||
|
||||
/**
|
||||
* @return the action's icon, as defined by the Icon key in the desktop action group
|
||||
*/
|
||||
QString icon() const;
|
||||
|
||||
/**
|
||||
* @return the action's exec command, as defined by the Exec key in the desktop action group
|
||||
*/
|
||||
QString exec() const;
|
||||
|
||||
/**
|
||||
* Returns whether the action is a separator.
|
||||
* This is true when the Actions key contains "_SEPARATOR_".
|
||||
*/
|
||||
bool isSeparator() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<KDesktopFileActionPrivate> d;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2000 Alex Zepeda <zipzippy@sonic.net>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "kemailsettings.h"
|
||||
|
||||
#include <kconfig.h>
|
||||
#include <kconfiggroup.h>
|
||||
|
||||
class KEMailSettingsPrivate
|
||||
{
|
||||
public:
|
||||
KEMailSettingsPrivate()
|
||||
: m_pConfig(nullptr)
|
||||
{
|
||||
}
|
||||
~KEMailSettingsPrivate()
|
||||
{
|
||||
delete m_pConfig;
|
||||
}
|
||||
KConfig *m_pConfig;
|
||||
QStringList profiles;
|
||||
QString m_sDefaultProfile, m_sCurrentProfile;
|
||||
};
|
||||
|
||||
QString KEMailSettings::defaultProfileName() const
|
||||
{
|
||||
return p->m_sDefaultProfile;
|
||||
}
|
||||
|
||||
QString KEMailSettings::getSetting(KEMailSettings::Setting s) const
|
||||
{
|
||||
KConfigGroup cg(p->m_pConfig, QLatin1String("PROFILE_") + p->m_sCurrentProfile);
|
||||
switch (s) {
|
||||
case ClientProgram: {
|
||||
return cg.readEntry("EmailClient");
|
||||
}
|
||||
case ClientTerminal: {
|
||||
return cg.readEntry("TerminalClient", QVariant(false)).toString();
|
||||
}
|
||||
case RealName: {
|
||||
return cg.readEntry("FullName");
|
||||
}
|
||||
case EmailAddress: {
|
||||
return cg.readEntry("EmailAddress");
|
||||
}
|
||||
case ReplyToAddress: {
|
||||
return cg.readEntry("ReplyAddr");
|
||||
}
|
||||
case Organization: {
|
||||
return cg.readEntry("Organization");
|
||||
}
|
||||
};
|
||||
return QString();
|
||||
}
|
||||
void KEMailSettings::setSetting(KEMailSettings::Setting s, const QString &v)
|
||||
{
|
||||
KConfigGroup cg(p->m_pConfig, QLatin1String("PROFILE_") + p->m_sCurrentProfile);
|
||||
switch (s) {
|
||||
case ClientProgram: {
|
||||
cg.writePathEntry("EmailClient", v);
|
||||
break;
|
||||
}
|
||||
case ClientTerminal: {
|
||||
cg.writeEntry("TerminalClient", (v == QLatin1String("true")));
|
||||
break;
|
||||
}
|
||||
case RealName: {
|
||||
cg.writeEntry("FullName", v);
|
||||
break;
|
||||
}
|
||||
case EmailAddress: {
|
||||
cg.writeEntry("EmailAddress", v);
|
||||
break;
|
||||
}
|
||||
case ReplyToAddress: {
|
||||
cg.writeEntry("ReplyAddr", v);
|
||||
break;
|
||||
}
|
||||
case Organization: {
|
||||
cg.writeEntry("Organization", v);
|
||||
break;
|
||||
}
|
||||
};
|
||||
cg.sync();
|
||||
}
|
||||
|
||||
void KEMailSettings::setDefault(const QString &s)
|
||||
{
|
||||
p->m_pConfig->group(QStringLiteral("Defaults")).writeEntry("Profile", s);
|
||||
p->m_pConfig->sync();
|
||||
p->m_sDefaultProfile = s;
|
||||
}
|
||||
|
||||
void KEMailSettings::setProfile(const QString &s)
|
||||
{
|
||||
const QString groupname = QLatin1String("PROFILE_") + s;
|
||||
p->m_sCurrentProfile = s;
|
||||
if (!p->m_pConfig->hasGroup(groupname)) { // Create a group if it doesn't exist
|
||||
KConfigGroup cg(p->m_pConfig, groupname);
|
||||
cg.writeEntry("ServerType", QString());
|
||||
p->profiles += s;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList KEMailSettings::profiles() const
|
||||
{
|
||||
return p->profiles;
|
||||
}
|
||||
|
||||
KEMailSettings::KEMailSettings()
|
||||
: p(new KEMailSettingsPrivate())
|
||||
{
|
||||
p->m_sCurrentProfile.clear();
|
||||
|
||||
p->m_pConfig = new KConfig(QStringLiteral("emaildefaults"));
|
||||
|
||||
const QStringList groups = p->m_pConfig->groupList();
|
||||
for (const auto &grp : groups) {
|
||||
if (grp.startsWith(QLatin1String("PROFILE_"))) {
|
||||
p->profiles += grp.mid(8 /* length of "PROFILE_" */);
|
||||
}
|
||||
}
|
||||
|
||||
KConfigGroup cg(p->m_pConfig, QStringLiteral("Defaults"));
|
||||
p->m_sDefaultProfile = cg.readEntry("Profile", tr("Default"));
|
||||
if (!p->m_sDefaultProfile.isNull()) {
|
||||
if (!p->m_pConfig->hasGroup(QLatin1String("PROFILE_") + p->m_sDefaultProfile)) {
|
||||
setDefault(tr("Default"));
|
||||
} else {
|
||||
setDefault(p->m_sDefaultProfile);
|
||||
}
|
||||
} else {
|
||||
if (!p->profiles.isEmpty()) {
|
||||
setDefault(p->profiles[0]);
|
||||
} else {
|
||||
setDefault(tr("Default"));
|
||||
}
|
||||
}
|
||||
setProfile(defaultProfileName());
|
||||
}
|
||||
|
||||
KEMailSettings::~KEMailSettings()
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2000 Alex Zepeda <zipzippy@sonic.net>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef _KEMAILSETTINGS_H
|
||||
#define _KEMAILSETTINGS_H
|
||||
|
||||
#include <QCoreApplication> // Q_DECLARE_TR_FUNCTIONS
|
||||
#include <QStringList>
|
||||
|
||||
#include <kconfigcore_export.h>
|
||||
|
||||
class KEMailSettingsPrivate;
|
||||
|
||||
/**
|
||||
* \class KEMailSettings kemailsettings.h <KEMailSettings>
|
||||
*
|
||||
* This is just a small class to facilitate accessing e-mail settings in
|
||||
* a sane way, and allowing any program to manage multiple e-mail
|
||||
* profiles effortlessly
|
||||
*
|
||||
* The default profile is automatically selected in the constructor.
|
||||
*
|
||||
* @author Alex Zepeda zipzippy@sonic.net
|
||||
**/
|
||||
class KCONFIGCORE_EXPORT KEMailSettings
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KEMailSettings)
|
||||
public:
|
||||
/**
|
||||
* The list of settings that I thought of when I wrote this
|
||||
* class.
|
||||
* @see getSetting()
|
||||
* @see setSetting()
|
||||
**/
|
||||
enum Setting {
|
||||
ClientProgram,
|
||||
ClientTerminal,
|
||||
RealName,
|
||||
EmailAddress,
|
||||
ReplyToAddress,
|
||||
Organization,
|
||||
};
|
||||
|
||||
/**
|
||||
* Default constructor, just sets things up and sets the default profile
|
||||
* as the current profile
|
||||
**/
|
||||
KEMailSettings();
|
||||
|
||||
KEMailSettings(const KEMailSettings &) = delete;
|
||||
KEMailSettings &operator=(const KEMailSettings &) = delete;
|
||||
|
||||
/**
|
||||
* Default destructor, nothing to see here.
|
||||
**/
|
||||
~KEMailSettings();
|
||||
|
||||
/**
|
||||
* List of profiles available.
|
||||
* @return the list of profiles
|
||||
**/
|
||||
QStringList profiles() const;
|
||||
|
||||
/**
|
||||
* Change the current profile.
|
||||
* @param s the name of the new profile
|
||||
**/
|
||||
void setProfile(const QString &s);
|
||||
|
||||
/**
|
||||
* Returns the name of the default profile.
|
||||
* @returns the name of the one that's currently default QString() if none
|
||||
**/
|
||||
QString defaultProfileName() const;
|
||||
|
||||
/**
|
||||
* Sets a new default.
|
||||
* @param def the new default
|
||||
**/
|
||||
void setDefault(const QString &def);
|
||||
|
||||
/**
|
||||
* Get one of the predefined "basic" settings.
|
||||
* @param s the setting to get
|
||||
* @return the value of the setting, or QString() if not
|
||||
* set
|
||||
**/
|
||||
QString getSetting(KEMailSettings::Setting s) const;
|
||||
|
||||
/**
|
||||
* Set one of the predefined "basic" settings.
|
||||
* @param s the setting to set
|
||||
* @param v the new value of the setting, or QString() to
|
||||
* unset
|
||||
**/
|
||||
void setSetting(KEMailSettings::Setting s, const QString &v);
|
||||
|
||||
private:
|
||||
KEMailSettingsPrivate *const p;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "ksharedconfig.h"
|
||||
#include "kconfig_core_log_settings.h"
|
||||
#include "kconfig_p.h"
|
||||
#include "kconfiggroup.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QThreadStorage>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
void _k_globalMainConfigSync();
|
||||
|
||||
using SharedConfigList = QList<KSharedConfig *>;
|
||||
|
||||
class GlobalSharedConfig
|
||||
{
|
||||
public:
|
||||
GlobalSharedConfig()
|
||||
: wasTestModeEnabled(false)
|
||||
{
|
||||
if (!qApp || QThread::currentThread() == qApp->thread()) {
|
||||
// We want to force the sync() before the QCoreApplication
|
||||
// instance is gone. Otherwise we trigger a QLockFile::lock()
|
||||
// after QCoreApplication is gone, calling qAppName() for a non
|
||||
// existent app...
|
||||
qAddPostRoutine(&_k_globalMainConfigSync);
|
||||
}
|
||||
// In other threads, QThreadStorage takes care of deleting the GlobalSharedConfigList when
|
||||
// the thread exits.
|
||||
}
|
||||
|
||||
SharedConfigList configList;
|
||||
// in addition to the list, we need to hold the main config,
|
||||
// so that it's not created and destroyed all the time.
|
||||
KSharedConfigPtr mainConfig;
|
||||
bool wasTestModeEnabled;
|
||||
};
|
||||
|
||||
static QThreadStorage<GlobalSharedConfig *> s_storage;
|
||||
template<typename T>
|
||||
T *perThreadGlobalStatic()
|
||||
{
|
||||
if (!s_storage.hasLocalData()) {
|
||||
s_storage.setLocalData(new T);
|
||||
}
|
||||
return s_storage.localData();
|
||||
}
|
||||
|
||||
// Q_GLOBAL_STATIC(GlobalSharedConfigList, globalSharedConfigList), but per thread:
|
||||
static GlobalSharedConfig *globalSharedConfig()
|
||||
{
|
||||
return perThreadGlobalStatic<GlobalSharedConfig>();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
[[nodiscard]] QString migrateStateRc(const QString &fileName)
|
||||
{
|
||||
// Migrate from an old legacy path to new spec compliant ~/.local/state/
|
||||
// https://gitlab.freedesktop.org/xdg/xdg-specs/-/blob/master/basedir/basedir-spec.xml
|
||||
// TODO KF7: refactor openStateConfig so it always opens from XDG_STATE_HOME instead of the legacy when on an XDG platform
|
||||
|
||||
#if !defined(Q_OS_WINDOWS) && !defined(Q_OS_ANDROID) && !defined(Q_OS_MACOS)
|
||||
if (QFileInfo(fileName).isAbsolute()) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
static auto xdgStateHome = qEnvironmentVariable("XDG_STATE_HOME", QDir::homePath() + "/.local/state"_L1);
|
||||
if (fileName.startsWith(xdgStateHome)) [[unlikely]] {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
QString newPath = xdgStateHome + "/"_L1 + fileName; // intentionally not const so it can be move returned
|
||||
QString oldPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName);
|
||||
if (oldPath.isEmpty()) { // nothing to migrate
|
||||
return newPath;
|
||||
}
|
||||
if (QFile::exists(oldPath) && QFile::exists(newPath)) {
|
||||
qCDebug(KCONFIG_CORE_LOG) << "Old staterc and new staterc found. Not migrating! Using new path" << newPath;
|
||||
return newPath;
|
||||
}
|
||||
|
||||
if (QFile::exists(newPath)) { // already migrated
|
||||
return newPath;
|
||||
}
|
||||
|
||||
// Migrate legacy files.
|
||||
// On failure we return the new path because we want higher level technology to surface the new path for read/write errors.
|
||||
if (!QDir().exists(xdgStateHome)) {
|
||||
if (!QDir().mkpath(xdgStateHome)) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Failed to make state directory" << xdgStateHome;
|
||||
return newPath;
|
||||
}
|
||||
}
|
||||
qCInfo(KCONFIG_CORE_LOG) << "Migrating old staterc" << oldPath << "->" << newPath;
|
||||
if (!QFile::rename(oldPath, newPath)) {
|
||||
qCWarning(KCONFIG_CORE_LOG) << "Failed to migrate" << oldPath << "->" << newPath;
|
||||
return newPath;
|
||||
}
|
||||
|
||||
return newPath;
|
||||
#else
|
||||
return fileName;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void _k_globalMainConfigSync()
|
||||
{
|
||||
if (KSharedConfigPtr mainConfig = globalSharedConfig()->mainConfig) {
|
||||
mainConfig->sync();
|
||||
}
|
||||
}
|
||||
|
||||
KSharedConfigPtr KSharedConfig::openConfig(const QString &_fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
|
||||
{
|
||||
QString fileName(_fileName);
|
||||
GlobalSharedConfig *global = globalSharedConfig();
|
||||
if (fileName.isEmpty() && !flags.testFlag(KConfig::SimpleConfig)) {
|
||||
// Determine the config file name that KConfig will make up (see KConfigPrivate::changeFileName)
|
||||
fileName = KConfig::mainConfigName();
|
||||
}
|
||||
|
||||
if (!global->wasTestModeEnabled && QStandardPaths::isTestModeEnabled()) {
|
||||
global->wasTestModeEnabled = true;
|
||||
global->configList.clear();
|
||||
global->mainConfig = nullptr;
|
||||
}
|
||||
|
||||
for (auto *cfg : std::as_const(global->configList)) {
|
||||
if (cfg->name() == fileName && cfg->d_ptr->openFlags == flags && cfg->locationType() == resType) {
|
||||
return KSharedConfigPtr(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
KSharedConfigPtr ptr(new KSharedConfig(fileName, flags, resType));
|
||||
|
||||
if (_fileName.isEmpty() && flags == FullConfig && resType == QStandardPaths::GenericConfigLocation) {
|
||||
global->mainConfig = ptr;
|
||||
|
||||
const bool isMainThread = !qApp || QThread::currentThread() == qApp->thread();
|
||||
static bool userWarned = false;
|
||||
if (isMainThread && !userWarned) {
|
||||
userWarned = true;
|
||||
const bool isReadOnly = qEnvironmentVariableIsEmpty("KDE_HOME_READONLY");
|
||||
if (isReadOnly && QCoreApplication::applicationName() != QLatin1String("kdialog")) {
|
||||
if (ptr->group(QStringLiteral("General")).readEntry(QStringLiteral("warn_unwritable_config"), true)) {
|
||||
ptr->isConfigWritable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
KSharedConfig::Ptr KSharedConfig::openStateConfig(const QString &_fileName)
|
||||
{
|
||||
QString fileName(_fileName);
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = QCoreApplication::applicationName() + QLatin1String("staterc");
|
||||
}
|
||||
|
||||
return openConfig(migrateStateRc(fileName),
|
||||
SimpleConfig,
|
||||
QStandardPaths::AppDataLocation /* only used on !XDG platform, on XDG we resolve an absolute path (unless there are problems) */);
|
||||
}
|
||||
|
||||
KSharedConfig::KSharedConfig(const QString &fileName, OpenFlags flags, QStandardPaths::StandardLocation resType)
|
||||
: KConfig(fileName, flags, resType)
|
||||
{
|
||||
globalSharedConfig()->configList.append(this);
|
||||
}
|
||||
|
||||
KSharedConfig::~KSharedConfig()
|
||||
{
|
||||
if (s_storage.hasLocalData()) {
|
||||
globalSharedConfig()->configList.removeAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
KConfigGroup KSharedConfig::groupImpl(const QString &groupName)
|
||||
{
|
||||
KSharedConfigPtr ptr(this);
|
||||
return KConfigGroup(ptr, groupName);
|
||||
}
|
||||
|
||||
const KConfigGroup KSharedConfig::groupImpl(const QString &groupName) const
|
||||
{
|
||||
const KSharedConfigPtr ptr(const_cast<KSharedConfig *>(this));
|
||||
return KConfigGroup(ptr, groupName);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
SPDX-FileCopyrightText: 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KSHAREDCONFIG_H
|
||||
#define KSHAREDCONFIG_H
|
||||
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <kconfig.h>
|
||||
|
||||
/**
|
||||
* \class KSharedConfig ksharedconfig.h <KSharedConfig>
|
||||
*
|
||||
* KConfig variant using shared memory
|
||||
*
|
||||
* KSharedConfig provides a shared (reference counted) variant
|
||||
* of KConfig. This allows you to use/manipulate the same configuration
|
||||
* files from different places in your code without worrying about
|
||||
* accidentally overwriting changes.
|
||||
*
|
||||
* The openConfig() method is threadsafe: every thread gets a separate repository
|
||||
* of shared KConfig objects. This means, however, that you'll be responsible for
|
||||
* synchronizing the instances of KConfig for the same filename between threads,
|
||||
* using KConfig::reparseConfiguration() after a manual change notification, just like you have
|
||||
* to do between processes.
|
||||
*/
|
||||
class KCONFIGCORE_EXPORT KSharedConfig : public KConfig, public QSharedData // krazy:exclude=dpointer (only for refcounting)
|
||||
{
|
||||
public:
|
||||
typedef QExplicitlySharedDataPointer<KSharedConfig> Ptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a KSharedConfig object to manipulate a configuration file
|
||||
*
|
||||
* If an absolute path is specified for @p fileName, that file will be used
|
||||
* as the store for the configuration settings. If a non-absolute path
|
||||
* is provided, the file will be looked for in the standard directory
|
||||
* specified by @p type. If no path is provided, a default
|
||||
* configuration file will be used based on the name of the main
|
||||
* application component.
|
||||
*
|
||||
* @p mode determines whether the user or global settings will be allowed
|
||||
* to influence the values returned by this object. See KConfig::OpenFlags for
|
||||
* more details.
|
||||
*
|
||||
* @param fileName the configuration file to open. If empty, it will be determined
|
||||
* automatically (from --config on the command line, otherwise
|
||||
* from the application name + "rc")
|
||||
* @param mode how global settings should affect the configuration
|
||||
* options exposed by this KConfig object
|
||||
* @param type The standard directory to look for the configuration
|
||||
* file in (see QStandardPaths)
|
||||
*
|
||||
* @sa KConfig
|
||||
*/
|
||||
static KSharedConfig::Ptr
|
||||
openConfig(const QString &fileName = QString(), OpenFlags mode = FullConfig, QStandardPaths::StandardLocation type = QStandardPaths::GenericConfigLocation);
|
||||
|
||||
/**
|
||||
* Creates a KSharedConfig object to manipulate a configuration file suitable
|
||||
* for storing state information. Use this for storing information that is
|
||||
* changing frequently and should not be saved by configuration backup
|
||||
* utilities.
|
||||
*
|
||||
* If an absolute path is specified for @p fileName, that file will be used
|
||||
* as the store for the configuration settings. If a non-absolute path
|
||||
* is provided, the file will be looked for in the standard data directory
|
||||
* (QStandardPaths::AppDataLocation). If no path is provided, a default
|
||||
* configuration file will be used based on the name of the main
|
||||
* application component.
|
||||
*
|
||||
* @param fileName the configuration file to open. If empty, it will be determined
|
||||
* automatically from the application name + "staterc"
|
||||
*
|
||||
* @since 5.67
|
||||
*
|
||||
* @sa KConfig
|
||||
*/
|
||||
static KSharedConfig::Ptr openStateConfig(const QString &fileName = QString());
|
||||
|
||||
~KSharedConfig() override;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KSharedConfig)
|
||||
KConfigGroup groupImpl(const QString &groupName) override;
|
||||
const KConfigGroup groupImpl(const QString &groupName) const override;
|
||||
|
||||
KCONFIGCORE_NO_EXPORT KSharedConfig(const QString &file, OpenFlags mode, QStandardPaths::StandardLocation resourceType);
|
||||
};
|
||||
|
||||
typedef KSharedConfig::Ptr KSharedConfigPtr;
|
||||
|
||||
#endif // multiple inclusion guard
|
||||
@@ -0,0 +1,84 @@
|
||||
add_library(KF6ConfigGui)
|
||||
add_library(KF6::ConfigGui ALIAS KF6ConfigGui)
|
||||
|
||||
qt_extract_metatypes(KF6ConfigGui)
|
||||
|
||||
set_target_properties(KF6ConfigGui PROPERTIES
|
||||
VERSION ${KCONFIG_VERSION}
|
||||
SOVERSION ${KCONFIG_SOVERSION}
|
||||
EXPORT_NAME ConfigGui
|
||||
)
|
||||
|
||||
ecm_create_qm_loader(KF6ConfigGui kconfig6_qt)
|
||||
|
||||
target_sources(KF6ConfigGui PRIVATE
|
||||
kconfiggui.cpp
|
||||
kconfiggroupgui.cpp
|
||||
kconfigloader.cpp
|
||||
kconfigskeleton.cpp
|
||||
kstandardshortcut.cpp
|
||||
kstandardshortcutwatcher.cpp
|
||||
kwindowconfig.cpp
|
||||
kwindowstatesaver.cpp
|
||||
kstandardactions.cpp
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6ConfigGui
|
||||
HEADER kconfig_gui_log_settings.h
|
||||
IDENTIFIER KCONFIG_GUI_LOG
|
||||
CATEGORY_NAME kf.config.gui
|
||||
DESCRIPTION "KConfig Gui"
|
||||
EXPORT KCONFIG
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6ConfigGui
|
||||
BASE_NAME KConfigGui
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME KConfig
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6ConfigGui
|
||||
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KConfig;${KDE_INSTALL_INCLUDEDIR_KF}/KConfigGui>")
|
||||
|
||||
target_link_libraries(KF6ConfigGui PUBLIC Qt6::Gui KF6::ConfigCore)
|
||||
|
||||
ecm_generate_headers(KConfigGui_HEADERS
|
||||
HEADER_NAMES
|
||||
KConfigGui
|
||||
KConfigLoader
|
||||
KConfigSkeleton
|
||||
KStandardShortcut
|
||||
KStandardShortcutWatcher
|
||||
KWindowConfig
|
||||
KWindowStateSaver
|
||||
KStandardActions
|
||||
|
||||
REQUIRED_HEADERS KConfigGui_HEADERS
|
||||
)
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_sources(KF6ConfigGui PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/kconfigguistaticinitializer.cpp>
|
||||
$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KConfigGui/kconfigguistaticinitializer.cpp>
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS KF6ConfigGui EXPORT KF6ConfigTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kconfiggui_export.h
|
||||
kstandardactions_p.h
|
||||
${KConfigGui_HEADERS}
|
||||
kconfigguistaticinitializer.cpp
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KConfigGui COMPONENT Devel
|
||||
)
|
||||
|
||||
# make available to ecm_add_qch in parent folder
|
||||
set(KConfigGui_APIDOX_SRCS ${KConfigGui_HEADERS} PARENT_SCOPE)
|
||||
set(KConfigGui_APIDOX_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfiggui_export.h"
|
||||
#include <kconfiggroup.h>
|
||||
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QFont>
|
||||
|
||||
#include <kconfiggroup_p.h>
|
||||
|
||||
/**
|
||||
* Try to read a GUI type from config group @p cg at key @p key.
|
||||
* @p input is the default value and also indicates the type to be read.
|
||||
* @p output is to be set with the value that has been read.
|
||||
*
|
||||
* @returns true if something was handled (even if output was set to clear or default)
|
||||
* or false if nothing was handled (e.g., Core type)
|
||||
*/
|
||||
static bool readEntryGui(const QByteArray &data, const char *key, const QVariant &input, QVariant &output)
|
||||
{
|
||||
const auto errString = [&]() {
|
||||
return QStringLiteral("\"%1\" - conversion from \"%3\" to %2 failed")
|
||||
.arg(QLatin1String(key), QLatin1String(input.typeName()), QLatin1String(data.constData()));
|
||||
};
|
||||
|
||||
// set in case of failure
|
||||
output = input;
|
||||
|
||||
switch (static_cast<QMetaType::Type>(input.userType())) {
|
||||
case QMetaType::QColor: {
|
||||
if (data.isEmpty() || data == "invalid") {
|
||||
output = QColor(); // return what was stored
|
||||
return true;
|
||||
} else if (data.at(0) == '#') {
|
||||
QColor col = QColor::fromString(QUtf8StringView(data.constData(), data.length()));
|
||||
if (!col.isValid()) {
|
||||
qCritical() << qPrintable(errString());
|
||||
}
|
||||
output = col;
|
||||
return true;
|
||||
} else if (!data.contains(',')) {
|
||||
QColor col = QColor::fromString(QUtf8StringView(data.constData(), data.length()));
|
||||
if (!col.isValid()) {
|
||||
qCritical() << qPrintable(errString());
|
||||
}
|
||||
output = col;
|
||||
return true;
|
||||
} else {
|
||||
const QList<QByteArray> list = data.split(',');
|
||||
const int count = list.count();
|
||||
|
||||
if (count != 3 && count != 4) {
|
||||
qCritical() //
|
||||
<< qPrintable(errString()) //
|
||||
<< qPrintable(QStringLiteral(" (wrong format: expected '%1' items, read '%2')").arg(QStringLiteral("3' or '4")).arg(count));
|
||||
return true; // return default
|
||||
}
|
||||
|
||||
int temp[4];
|
||||
// bounds check components
|
||||
for (int i = 0; i < count; i++) {
|
||||
bool ok;
|
||||
const int j = temp[i] = list.at(i).toInt(&ok);
|
||||
if (!ok) { // failed to convert to int
|
||||
qCritical() << qPrintable(errString()) << " (integer conversion failed)";
|
||||
return true; // return default
|
||||
}
|
||||
if (j < 0 || j > 255) {
|
||||
static const char *const components[] = {"red", "green", "blue", "alpha"};
|
||||
qCritical() << qPrintable(errString())
|
||||
<< qPrintable(QStringLiteral(" (bounds error: %1 component %2)")
|
||||
.arg(QLatin1String(components[i]), //
|
||||
j < 0 ? QStringLiteral("< 0") : QStringLiteral("> 255")));
|
||||
return true; // return default
|
||||
}
|
||||
}
|
||||
QColor aColor(temp[0], temp[1], temp[2]);
|
||||
if (count == 4) {
|
||||
aColor.setAlpha(temp[3]);
|
||||
}
|
||||
|
||||
if (aColor.isValid()) {
|
||||
output = aColor;
|
||||
} else {
|
||||
qCritical() << qPrintable(errString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
case QMetaType::QFont: {
|
||||
QVariant tmp = QString::fromUtf8(data.constData(), data.length());
|
||||
if (tmp.canConvert<QFont>()) {
|
||||
output = tmp;
|
||||
} else {
|
||||
qCritical() << qPrintable(errString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case QMetaType::QPixmap:
|
||||
case QMetaType::QImage:
|
||||
case QMetaType::QBrush:
|
||||
case QMetaType::QPalette:
|
||||
case QMetaType::QIcon:
|
||||
case QMetaType::QRegion:
|
||||
case QMetaType::QBitmap:
|
||||
case QMetaType::QCursor:
|
||||
case QMetaType::QSizePolicy:
|
||||
case QMetaType::QPen:
|
||||
// we may want to handle these in the future
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false; // not handled
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to write a GUI type @p prop to config group @p cg at key @p key.
|
||||
*
|
||||
* @returns true if something was handled (even if an empty value was written)
|
||||
* or false if nothing was handled (e.g., Core type)
|
||||
*/
|
||||
static bool writeEntryGui(KConfigGroup *cg, const char *key, const QVariant &prop, KConfigGroup::WriteConfigFlags pFlags)
|
||||
{
|
||||
switch (static_cast<QMetaType::Type>(prop.userType())) {
|
||||
case QMetaType::QColor: {
|
||||
const QColor rColor = prop.value<QColor>();
|
||||
|
||||
if (!rColor.isValid()) {
|
||||
cg->writeEntry(key, "invalid", pFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<int> list;
|
||||
list.insert(0, rColor.red());
|
||||
list.insert(1, rColor.green());
|
||||
list.insert(2, rColor.blue());
|
||||
if (rColor.alpha() != 255) {
|
||||
list.insert(3, rColor.alpha());
|
||||
}
|
||||
|
||||
cg->writeEntry(key, list, pFlags);
|
||||
return true;
|
||||
}
|
||||
case QMetaType::QFont: {
|
||||
QFont f = prop.value<QFont>();
|
||||
// If the styleName property is set for a QFont, using setBold(true) would
|
||||
// lead to Qt using an "emboldended"/synthetic font style instead of using
|
||||
// the bold style provided by the font itself; the latter looks much better
|
||||
// than the former. For more details see:
|
||||
// https://bugreports.qt.io/browse/QTBUG-63792
|
||||
// https://bugs.kde.org/show_bug.cgi?id=378523
|
||||
/* clang-format off */
|
||||
if (f.weight() == QFont::Normal
|
||||
&& (f.styleName() == QLatin1String("Regular")
|
||||
|| f.styleName() == QLatin1String("Normal")
|
||||
|| f.styleName() == QLatin1String("Book")
|
||||
|| f.styleName() == QLatin1String("Roman"))) { /* clang-format on */
|
||||
f.setStyleName(QString());
|
||||
}
|
||||
cg->writeEntry(key, f.toString().toUtf8(), pFlags);
|
||||
return true;
|
||||
}
|
||||
case QMetaType::QPixmap:
|
||||
case QMetaType::QImage:
|
||||
case QMetaType::QBrush:
|
||||
case QMetaType::QPalette:
|
||||
case QMetaType::QIcon:
|
||||
case QMetaType::QRegion:
|
||||
case QMetaType::QBitmap:
|
||||
case QMetaType::QCursor:
|
||||
case QMetaType::QSizePolicy:
|
||||
case QMetaType::QPen:
|
||||
// we may want to handle one of these in the future
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not static, because for static builds we use it in the kconfigguistaticinitializer.cpp file
|
||||
// Exported as we need this to force linking in consumers that read GUI types from KConfig, but
|
||||
// which are otherwise not using this library (and thus linking with --as-needed or MSVC would
|
||||
// break things)
|
||||
KCONFIGGUI_EXPORT int initKConfigGroupGui()
|
||||
{
|
||||
_kde_internal_KConfigGroupGui.readEntryGui = readEntryGui;
|
||||
_kde_internal_KConfigGroupGui.writeEntryGui = writeEntryGui;
|
||||
return 42; // because 42 is nicer than 1 or 0
|
||||
}
|
||||
|
||||
#ifdef Q_CONSTRUCTOR_FUNCTION
|
||||
Q_CONSTRUCTOR_FUNCTION(initKConfigGroupGui)
|
||||
#else
|
||||
static int dummyKConfigGroupGui = initKConfigGroupGui();
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kconfiggui.h"
|
||||
#include "kconfig_gui_log_settings.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <kconfig.h>
|
||||
|
||||
static QString configName(const QString &id, const QString &key)
|
||||
{
|
||||
return QLatin1String("session/%1_%2_%3").arg(QGuiApplication::applicationName(), id, key);
|
||||
}
|
||||
|
||||
static KConfig *s_sessionConfig = nullptr;
|
||||
|
||||
KConfig *KConfigGui::sessionConfig()
|
||||
{
|
||||
#ifdef QT_NO_SESSIONMANAGER
|
||||
qCWarning(KCONFIG_GUI_LOG) << "Qt is built without session manager support";
|
||||
#else
|
||||
if (!hasSessionConfig() && qApp->isSessionRestored()) {
|
||||
// create the default instance specific config object
|
||||
// from applications' -session command line parameter
|
||||
s_sessionConfig = new KConfig(configName(qApp->sessionId(), qApp->sessionKey()), KConfig::SimpleConfig);
|
||||
}
|
||||
#endif
|
||||
|
||||
return s_sessionConfig;
|
||||
}
|
||||
|
||||
void KConfigGui::setSessionConfig(const QString &id, const QString &key)
|
||||
{
|
||||
if (hasSessionConfig()) {
|
||||
delete s_sessionConfig;
|
||||
s_sessionConfig = nullptr;
|
||||
}
|
||||
|
||||
// create a new instance specific config object from supplied id & key
|
||||
s_sessionConfig = new KConfig(configName(id, key), KConfig::SimpleConfig);
|
||||
}
|
||||
|
||||
bool KConfigGui::hasSessionConfig()
|
||||
{
|
||||
return s_sessionConfig != nullptr;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGGUI_H
|
||||
#define KCONFIGGUI_H
|
||||
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
class KConfig;
|
||||
|
||||
/**
|
||||
* Interface-related functions.
|
||||
*/
|
||||
namespace KConfigGui
|
||||
{
|
||||
/**
|
||||
* Returns the current application session config object.
|
||||
*
|
||||
* @note If Qt is built without session manager support, i.e.
|
||||
* QT_NO_SESSIONMANAGER is defined, this by default will return
|
||||
* nullptr, unless a custom config has been set via
|
||||
* @c setSessionConfig.
|
||||
*
|
||||
* @return A pointer to the application's instance specific
|
||||
* KConfig object.
|
||||
* @see KConfig
|
||||
*/
|
||||
KCONFIGGUI_EXPORT KConfig *sessionConfig();
|
||||
|
||||
/**
|
||||
* Replaces the current application session config object.
|
||||
*
|
||||
* @param id new session id
|
||||
* @param key new session key
|
||||
*
|
||||
* @since 5.11
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void setSessionConfig(const QString &id, const QString &key);
|
||||
|
||||
/**
|
||||
* Indicates if a session config has been created for that application
|
||||
* (i.e.\ if sessionConfig() got called at least once)
|
||||
*
|
||||
* @return @c true if a sessionConfig object was created, @c false otherwise
|
||||
*/
|
||||
KCONFIGGUI_EXPORT bool hasSessionConfig();
|
||||
}
|
||||
|
||||
#endif // KCONFIGGUI_H
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
extern int initKConfigGroupGui();
|
||||
|
||||
static int myInit = initKConfigGroupGui();
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigloader.h"
|
||||
#include "kconfigloader_p.h"
|
||||
#include "kconfigloaderhandler_p.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
#include <QHash>
|
||||
#include <QUrl>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
void ConfigLoaderPrivate::parse(KConfigLoader *loader, QIODevice *xml)
|
||||
{
|
||||
clearData();
|
||||
loader->clearItems();
|
||||
|
||||
if (xml) {
|
||||
ConfigLoaderHandler handler(loader, this);
|
||||
handler.parse(xml);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigLoaderHandler::ConfigLoaderHandler(KConfigLoader *config, ConfigLoaderPrivate *d)
|
||||
: m_config(config)
|
||||
, d(d)
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
bool ConfigLoaderHandler::parse(QIODevice *input)
|
||||
{
|
||||
if (!input->open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Impossible to open device";
|
||||
return false;
|
||||
}
|
||||
QXmlStreamReader reader(input);
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
if (reader.hasError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (reader.tokenType()) {
|
||||
case QXmlStreamReader::StartElement:
|
||||
startElement(reader.name(), reader.attributes());
|
||||
break;
|
||||
case QXmlStreamReader::EndElement:
|
||||
endElement(reader.name());
|
||||
break;
|
||||
case QXmlStreamReader::Characters:
|
||||
if (!reader.isWhitespace() && !reader.text().trimmed().isEmpty()) {
|
||||
m_cdata.append(reader.text());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.isEndDocument()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool caseInsensitiveCompare(const QStringView a, const QLatin1String b)
|
||||
{
|
||||
return a.compare(b, Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
||||
void ConfigLoaderHandler::startElement(const QStringView localName, const QXmlStreamAttributes &attrs)
|
||||
{
|
||||
// qDebug() << "ConfigLoaderHandler::startElement(" << localName << qName;
|
||||
if (caseInsensitiveCompare(localName, QLatin1String("group"))) {
|
||||
QString group;
|
||||
for (const auto &attr : attrs) {
|
||||
const auto attrName = attr.name();
|
||||
if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
|
||||
// qDebug() << "set group to" << attrs.value(i);
|
||||
group = attr.value().toString();
|
||||
}
|
||||
}
|
||||
if (group.isEmpty()) {
|
||||
group = d->baseGroup;
|
||||
} else {
|
||||
d->groups.append(group);
|
||||
if (!d->baseGroup.isEmpty()) {
|
||||
group = d->baseGroup + QLatin1Char('\x1d') + group;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config) {
|
||||
m_config->setCurrentGroup(group);
|
||||
}
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
|
||||
for (const auto &attr : attrs) {
|
||||
const auto attrName = attr.name();
|
||||
if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
|
||||
m_name = attr.value().trimmed().toString();
|
||||
} else if (caseInsensitiveCompare(attrName, QLatin1String("type"))) {
|
||||
m_type = attr.value().toString().toLower();
|
||||
} else if (caseInsensitiveCompare(attrName, QLatin1String("key"))) {
|
||||
m_key = attr.value().trimmed().toString();
|
||||
}
|
||||
}
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
|
||||
m_choice.name.clear();
|
||||
m_choice.label.clear();
|
||||
m_choice.whatsThis.clear();
|
||||
for (const auto &attr : attrs) {
|
||||
const auto attrName = attr.name();
|
||||
if (caseInsensitiveCompare(attrName, QLatin1String("name"))) {
|
||||
m_choice.name = attr.value().toString();
|
||||
}
|
||||
}
|
||||
m_inChoice = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigLoaderHandler::endElement(const QStringView localName)
|
||||
{
|
||||
// qDebug() << "ConfigLoaderHandler::endElement(" << localName << qName;
|
||||
if (caseInsensitiveCompare(localName, QLatin1String("entry"))) {
|
||||
addItem();
|
||||
resetState();
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("label"))) {
|
||||
if (m_inChoice) {
|
||||
m_choice.label = std::move(m_cdata).trimmed();
|
||||
} else {
|
||||
m_label = std::move(m_cdata).trimmed();
|
||||
}
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("whatsthis"))) {
|
||||
if (m_inChoice) {
|
||||
m_choice.whatsThis = std::move(m_cdata).trimmed();
|
||||
} else {
|
||||
m_whatsThis = std::move(m_cdata).trimmed();
|
||||
}
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("default"))) {
|
||||
m_default = std::move(m_cdata).trimmed();
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("min"))) {
|
||||
m_min = m_cdata.toInt(&m_haveMin);
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("max"))) {
|
||||
m_max = m_cdata.toInt(&m_haveMax);
|
||||
} else if (caseInsensitiveCompare(localName, QLatin1String("choice"))) {
|
||||
m_enumChoices.append(m_choice);
|
||||
m_inChoice = false;
|
||||
}
|
||||
|
||||
m_cdata.clear();
|
||||
}
|
||||
|
||||
void ConfigLoaderHandler::addItem()
|
||||
{
|
||||
if (m_name.isEmpty()) {
|
||||
if (m_key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_name = m_key;
|
||||
}
|
||||
|
||||
m_name.remove(QLatin1Char(' '));
|
||||
|
||||
KConfigSkeletonItem *item = nullptr;
|
||||
|
||||
if (m_type == QLatin1String("bool")) {
|
||||
const bool defaultValue = caseInsensitiveCompare(m_default, QLatin1String("true"));
|
||||
item = m_config->addItemBool(m_name, *d->newBool(), defaultValue, m_key);
|
||||
} else if (m_type == QLatin1String("color")) {
|
||||
item = m_config->addItemColor(m_name, *d->newColor(), QColor(m_default), m_key);
|
||||
} else if (m_type == QLatin1String("datetime")) {
|
||||
item = m_config->addItemDateTime(m_name, *d->newDateTime(), QDateTime::fromString(m_default), m_key);
|
||||
} else if (m_type == QLatin1String("enum")) {
|
||||
m_key = (m_key.isEmpty()) ? m_name : m_key;
|
||||
|
||||
bool ok = false;
|
||||
int defaultValue = m_default.toInt(&ok);
|
||||
if (!ok) {
|
||||
for (int i = 0; i < m_enumChoices.size(); i++) {
|
||||
if (m_default == m_enumChoices[i].name) {
|
||||
defaultValue = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KConfigSkeleton::ItemEnum *enumItem = new KConfigSkeleton::ItemEnum(m_config->currentGroup(), m_key, *d->newInt(), m_enumChoices, defaultValue);
|
||||
m_config->addItem(enumItem, m_name);
|
||||
item = enumItem;
|
||||
} else if (m_type == QLatin1String("font")) {
|
||||
item = m_config->addItemFont(m_name, *d->newFont(), QFont(m_default), m_key);
|
||||
} else if (m_type == QLatin1String("int")) {
|
||||
KConfigSkeleton::ItemInt *intItem = m_config->addItemInt(m_name, *d->newInt(), m_default.toInt(), m_key);
|
||||
|
||||
if (m_haveMin) {
|
||||
intItem->setMinValue(m_min);
|
||||
}
|
||||
|
||||
if (m_haveMax) {
|
||||
intItem->setMaxValue(m_max);
|
||||
}
|
||||
|
||||
item = intItem;
|
||||
} else if (m_type == QLatin1String("password")) {
|
||||
item = m_config->addItemPassword(m_name, *d->newString(), m_default, m_key);
|
||||
} else if (m_type == QLatin1String("path")) {
|
||||
item = m_config->addItemPath(m_name, *d->newString(), m_default, m_key);
|
||||
} else if (m_type == QLatin1String("string")) {
|
||||
item = m_config->addItemString(m_name, *d->newString(), m_default, m_key);
|
||||
} else if (m_type == QLatin1String("stringlist")) {
|
||||
// FIXME: the split() is naive and will break on lists with ,'s in them
|
||||
// empty parts are not wanted in this case
|
||||
item = m_config->addItemStringList(m_name, *d->newStringList(), m_default.split(QLatin1Char(','), Qt::SkipEmptyParts), m_key);
|
||||
} else if (m_type == QLatin1String("uint")) {
|
||||
KConfigSkeleton::ItemUInt *uintItem = m_config->addItemUInt(m_name, *d->newUint(), m_default.toUInt(), m_key);
|
||||
if (m_haveMin) {
|
||||
uintItem->setMinValue(m_min);
|
||||
}
|
||||
if (m_haveMax) {
|
||||
uintItem->setMaxValue(m_max);
|
||||
}
|
||||
item = uintItem;
|
||||
} else if (m_type == QLatin1String("url")) {
|
||||
m_key = (m_key.isEmpty()) ? m_name : m_key;
|
||||
KConfigSkeleton::ItemUrl *urlItem = new KConfigSkeleton::ItemUrl(m_config->currentGroup(), m_key, *d->newUrl(), QUrl::fromUserInput(m_default));
|
||||
m_config->addItem(urlItem, m_name);
|
||||
item = urlItem;
|
||||
} else if (m_type == QLatin1String("double")) {
|
||||
KConfigSkeleton::ItemDouble *doubleItem = m_config->addItemDouble(m_name, *d->newDouble(), m_default.toDouble(), m_key);
|
||||
if (m_haveMin) {
|
||||
doubleItem->setMinValue(m_min);
|
||||
}
|
||||
if (m_haveMax) {
|
||||
doubleItem->setMaxValue(m_max);
|
||||
}
|
||||
item = doubleItem;
|
||||
} else if (m_type == QLatin1String("intlist")) {
|
||||
QList<int> defaultList;
|
||||
const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','), Qt::SkipEmptyParts);
|
||||
for (const QStringView tmp : tmpList) {
|
||||
defaultList.append(tmp.toInt());
|
||||
}
|
||||
item = m_config->addItemIntList(m_name, *d->newIntList(), defaultList, m_key);
|
||||
} else if (m_type == QLatin1String("longlong")) {
|
||||
KConfigSkeleton::ItemLongLong *longlongItem = m_config->addItemLongLong(m_name, *d->newLongLong(), m_default.toLongLong(), m_key);
|
||||
if (m_haveMin) {
|
||||
longlongItem->setMinValue(m_min);
|
||||
}
|
||||
if (m_haveMax) {
|
||||
longlongItem->setMaxValue(m_max);
|
||||
}
|
||||
item = longlongItem;
|
||||
/* No addItemPathList in KConfigSkeleton ?
|
||||
} else if (m_type == "PathList") {
|
||||
//FIXME: the split() is naive and will break on lists with ,'s in them
|
||||
item = m_config->addItemPathList(m_name, *d->newStringList(), m_default.split(","), m_key);
|
||||
*/
|
||||
} else if (m_type == QLatin1String("point")) {
|
||||
QPoint defaultPoint;
|
||||
const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
|
||||
if (tmpList.size() >= 2) {
|
||||
defaultPoint.setX(tmpList[0].toInt());
|
||||
defaultPoint.setY(tmpList[1].toInt());
|
||||
}
|
||||
item = m_config->addItemPoint(m_name, *d->newPoint(), defaultPoint, m_key);
|
||||
} else if (m_type == QLatin1String("pointf")) {
|
||||
QPointF defaultPointF;
|
||||
const auto tmpList = QStringView(m_default).split(u',');
|
||||
if (tmpList.size() >= 2) {
|
||||
defaultPointF.setX(tmpList[0].toDouble());
|
||||
defaultPointF.setY(tmpList[1].toDouble());
|
||||
}
|
||||
item = m_config->addItemPointF(m_name, *d->newPointF(), defaultPointF, m_key);
|
||||
} else if (m_type == QLatin1String("rect")) {
|
||||
QRect defaultRect;
|
||||
const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
|
||||
if (tmpList.size() >= 4) {
|
||||
defaultRect.setCoords(tmpList[0].toInt(), tmpList[1].toInt(), tmpList[2].toInt(), tmpList[3].toInt());
|
||||
}
|
||||
item = m_config->addItemRect(m_name, *d->newRect(), defaultRect, m_key);
|
||||
} else if (m_type == QLatin1String("rectf")) {
|
||||
QRectF defaultRectF;
|
||||
const auto tmpList = QStringView(m_default).split(u',');
|
||||
if (tmpList.size() >= 4) {
|
||||
defaultRectF.setCoords(tmpList[0].toDouble(), tmpList[1].toDouble(), tmpList[2].toDouble(), tmpList[3].toDouble());
|
||||
}
|
||||
item = m_config->addItemRectF(m_name, *d->newRectF(), defaultRectF, m_key);
|
||||
} else if (m_type == QLatin1String("size")) {
|
||||
QSize defaultSize;
|
||||
const QList<QStringView> tmpList = QStringView(m_default).split(QLatin1Char(','));
|
||||
if (tmpList.size() >= 2) {
|
||||
defaultSize.setWidth(tmpList[0].toInt());
|
||||
defaultSize.setHeight(tmpList[1].toInt());
|
||||
}
|
||||
item = m_config->addItemSize(m_name, *d->newSize(), defaultSize, m_key);
|
||||
} else if (m_type == QLatin1String("sizef")) {
|
||||
QSizeF defaultSizeF;
|
||||
const auto tmpList = QStringView(m_default).split(u',');
|
||||
if (tmpList.size() >= 2) {
|
||||
defaultSizeF.setWidth(tmpList[0].toDouble());
|
||||
defaultSizeF.setHeight(tmpList[1].toDouble());
|
||||
}
|
||||
item = m_config->addItemSizeF(m_name, *d->newSizeF(), defaultSizeF, m_key);
|
||||
} else if (m_type == QLatin1String("ulonglong")) {
|
||||
KConfigSkeleton::ItemULongLong *ulonglongItem = m_config->addItemULongLong(m_name, *d->newULongLong(), m_default.toULongLong(), m_key);
|
||||
if (m_haveMin) {
|
||||
ulonglongItem->setMinValue(m_min);
|
||||
}
|
||||
if (m_haveMax) {
|
||||
ulonglongItem->setMaxValue(m_max);
|
||||
}
|
||||
item = ulonglongItem;
|
||||
/* No addItemUrlList in KConfigSkeleton ?
|
||||
} else if (m_type == "urllist") {
|
||||
//FIXME: the split() is naive and will break on lists with ,'s in them
|
||||
QStringList tmpList = m_default.split(",");
|
||||
QList<QUrl> defaultList;
|
||||
foreach (const QString& tmp, tmpList) {
|
||||
defaultList.append(QUrl(tmp));
|
||||
}
|
||||
item = m_config->addItemUrlList(m_name, *d->newUrlList(), defaultList, m_key);*/
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item->setLabel(m_label);
|
||||
item->setWhatsThis(m_whatsThis);
|
||||
d->keysToNames.insert(item->group() + item->key(), item->name());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigLoaderHandler::resetState()
|
||||
{
|
||||
m_haveMin = false;
|
||||
m_min = 0;
|
||||
m_haveMax = false;
|
||||
m_max = 0;
|
||||
m_name.clear();
|
||||
m_type.clear();
|
||||
m_label.clear();
|
||||
m_default.clear();
|
||||
m_key.clear();
|
||||
m_whatsThis.clear();
|
||||
m_enumChoices.clear();
|
||||
m_inChoice = false;
|
||||
}
|
||||
|
||||
KConfigLoader::KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent)
|
||||
: KConfigSkeleton(configFile, parent)
|
||||
, d(new ConfigLoaderPrivate)
|
||||
{
|
||||
d->parse(this, xml);
|
||||
}
|
||||
|
||||
KConfigLoader::KConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent)
|
||||
: KConfigSkeleton(std::move(config), parent)
|
||||
, d(new ConfigLoaderPrivate)
|
||||
{
|
||||
d->parse(this, xml);
|
||||
}
|
||||
|
||||
// FIXME: obviously this is broken and should be using the group as the root,
|
||||
// but KConfigSkeleton does not currently support this. it will eventually though,
|
||||
// at which point this can be addressed properly
|
||||
KConfigLoader::KConfigLoader(const KConfigGroup &config, QIODevice *xml, QObject *parent)
|
||||
: KConfigSkeleton(KSharedConfig::openConfig(config.config()->name(), config.config()->openFlags(), config.config()->locationType()), parent)
|
||||
, d(new ConfigLoaderPrivate)
|
||||
{
|
||||
KConfigGroup group = config.parent();
|
||||
d->baseGroup = config.name();
|
||||
while (group.isValid() && group.name() != QLatin1String("<default>")) {
|
||||
d->baseGroup = group.name() + QLatin1Char('\x1d') + d->baseGroup;
|
||||
group = group.parent();
|
||||
}
|
||||
d->parse(this, xml);
|
||||
}
|
||||
|
||||
KConfigLoader::~KConfigLoader()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
KConfigSkeletonItem *KConfigLoader::findItem(const QString &group, const QString &key) const
|
||||
{
|
||||
return KConfigSkeleton::findItem(d->keysToNames[group + key]);
|
||||
}
|
||||
|
||||
KConfigSkeletonItem *KConfigLoader::findItemByName(const QString &name) const
|
||||
{
|
||||
return KConfigSkeleton::findItem(name);
|
||||
}
|
||||
|
||||
QVariant KConfigLoader::property(const QString &name) const
|
||||
{
|
||||
KConfigSkeletonItem *item = KConfigSkeleton::findItem(name);
|
||||
|
||||
if (item) {
|
||||
return item->property();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool KConfigLoader::hasGroup(const QString &group) const
|
||||
{
|
||||
return d->groups.contains(group);
|
||||
}
|
||||
|
||||
QStringList KConfigLoader::groupList() const
|
||||
{
|
||||
return d->groups;
|
||||
}
|
||||
|
||||
bool KConfigLoader::usrSave()
|
||||
{
|
||||
if (d->saveDefaults) {
|
||||
const auto listItems = items();
|
||||
for (const auto &item : listItems) {
|
||||
config()->group(item->group()).writeEntry(item->key(), "");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGLOADER_H
|
||||
#define KCONFIGLOADER_H
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
#include <kconfiggroup.h>
|
||||
#include <kconfigskeleton.h>
|
||||
#include <ksharedconfig.h>
|
||||
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
class ConfigLoaderPrivate;
|
||||
|
||||
/**
|
||||
* @class KConfigLoader kconfigloader.h <KConfigLoader>
|
||||
*
|
||||
* @short A KConfigSkeleton that populates itself based on KConfigXT XML
|
||||
*
|
||||
* This class allows one to ship an XML file and reconstitute it into a
|
||||
* KConfigSkeleton object at runtime. Common usage might look like this:
|
||||
*
|
||||
* \code
|
||||
* QFile file(xmlFilePath);
|
||||
* KConfigLoader appletConfig(configFilePath, &file);
|
||||
* \endcode
|
||||
*
|
||||
* Alternatively, any QIODevice may be used in place of QFile in the
|
||||
* example above.
|
||||
*
|
||||
* KConfigLoader is useful if it is not possible to use compiled code
|
||||
* and by that the kconfig compiler cannot be used. Common examples are
|
||||
* scripted plugins which want to provide a configuration interface.
|
||||
* With the help of KConfigLoader a dynamically loaded ui file can be
|
||||
* populated with the stored values and also stored back to the config
|
||||
* file.
|
||||
*
|
||||
* An example for populating a QDialog with a dynamically populated UI
|
||||
* with the help of a KConfigDialogManager:
|
||||
* \code
|
||||
* QDialog *dialog = new QDialog();
|
||||
* QFile xmlFile("path/to/kconfigxt.xml");
|
||||
* KConfigGroup cg = KSharedConfig::openConfig()->group(QString());
|
||||
* KConfigLoader *configLoader = new KConfigLoader(cg, &xmlFile, this);
|
||||
*
|
||||
* // load the ui file
|
||||
* QUiLoader *loader = new QUiLoader(this);
|
||||
* QFile uiFile("path/to/userinterface.ui");
|
||||
* uiFile.open(QFile::ReadOnly);
|
||||
* QWidget *customConfigForm = loader->load(&uiFile, dialog);
|
||||
* uiFile.close();
|
||||
*
|
||||
* KConfigDialogManager *manager = new KConfigDialogManager(customConfigForm, configLoader);
|
||||
* if (dialog->exec() == QDialog::Accepted) {
|
||||
* manager->updateSettings();
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Currently the following data types are supported:
|
||||
*
|
||||
* @li bools
|
||||
* @li colors
|
||||
* @li datetimes
|
||||
* @li enumerations
|
||||
* @li fonts
|
||||
* @li ints
|
||||
* @li passwords
|
||||
* @li paths
|
||||
* @li strings
|
||||
* @li stringlists
|
||||
* @li uints
|
||||
* @li urls
|
||||
* @li doubles
|
||||
* @li int lists
|
||||
* @li longlongs
|
||||
* @li path lists
|
||||
* @li points
|
||||
* @li pointfs
|
||||
* @li rects
|
||||
* @li rectfs
|
||||
* @li sizes
|
||||
* @li sizefs
|
||||
* @li ulonglongs
|
||||
* @li url lists
|
||||
**/
|
||||
class KCONFIGGUI_EXPORT KConfigLoader : public KConfigSkeleton
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a KConfigSkeleton populated using the definition found in
|
||||
* the XML data passed in.
|
||||
*
|
||||
* @param configFile path to the configuration file to use
|
||||
* @param xml the xml data; must be valid KConfigXT data
|
||||
* @param parent optional QObject parent
|
||||
**/
|
||||
KConfigLoader(const QString &configFile, QIODevice *xml, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Creates a KConfigSkeleton populated using the definition found in
|
||||
* the XML data passed in.
|
||||
*
|
||||
* @param config the configuration object to use
|
||||
* @param xml the xml data; must be valid KConfigXT data
|
||||
* @param parent optional QObject parent
|
||||
**/
|
||||
KConfigLoader(KSharedConfigPtr config, QIODevice *xml, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Creates a KConfigSkeleton populated using the definition found in
|
||||
* the XML data passed in.
|
||||
*
|
||||
* @param config the group to use as the root for configuration items
|
||||
* @param xml the xml data; must be valid KConfigXT data
|
||||
* @param parent optional QObject parent
|
||||
**/
|
||||
KConfigLoader(const KConfigGroup &config, QIODevice *xml, QObject *parent = nullptr);
|
||||
|
||||
~KConfigLoader() override;
|
||||
|
||||
/**
|
||||
* Finds the item for the given group and key.
|
||||
*
|
||||
* @param group the group in the config file to look in
|
||||
* @param key the configuration key to find
|
||||
* @return the associated KConfigSkeletonItem, or @c nullptr if none
|
||||
*/
|
||||
KConfigSkeletonItem *findItem(const QString &group, const QString &key) const;
|
||||
|
||||
/**
|
||||
* Finds an item by its name
|
||||
*/
|
||||
KConfigSkeletonItem *findItemByName(const QString &name) const;
|
||||
|
||||
/**
|
||||
* Returns the property (variantized value) of the named item
|
||||
*/
|
||||
QVariant property(const QString &name) const;
|
||||
|
||||
/**
|
||||
* Check to see if a group exists
|
||||
*
|
||||
* @param group the name of the group to check for
|
||||
* @return true if the group exists, or false if it does not
|
||||
*/
|
||||
bool hasGroup(const QString &group) const;
|
||||
|
||||
/**
|
||||
* @return the list of groups defined by the XML
|
||||
*/
|
||||
QStringList groupList() const;
|
||||
|
||||
protected:
|
||||
bool usrSave() override;
|
||||
|
||||
private:
|
||||
ConfigLoaderPrivate *const d;
|
||||
};
|
||||
|
||||
#endif // multiple inclusion guard
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGLOADER_P_H
|
||||
#define KCONFIGLOADER_P_H
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
class ConfigLoaderPrivate
|
||||
{
|
||||
public:
|
||||
ConfigLoaderPrivate()
|
||||
: saveDefaults(false)
|
||||
{
|
||||
}
|
||||
|
||||
~ConfigLoaderPrivate()
|
||||
{
|
||||
clearData();
|
||||
}
|
||||
|
||||
void clearData()
|
||||
{
|
||||
qDeleteAll(bools);
|
||||
qDeleteAll(strings);
|
||||
qDeleteAll(stringlists);
|
||||
qDeleteAll(colors);
|
||||
qDeleteAll(fonts);
|
||||
qDeleteAll(ints);
|
||||
qDeleteAll(uints);
|
||||
qDeleteAll(urls);
|
||||
qDeleteAll(dateTimes);
|
||||
qDeleteAll(doubles);
|
||||
qDeleteAll(intlists);
|
||||
qDeleteAll(longlongs);
|
||||
qDeleteAll(points);
|
||||
qDeleteAll(pointfs);
|
||||
qDeleteAll(rects);
|
||||
qDeleteAll(rectfs);
|
||||
qDeleteAll(sizes);
|
||||
qDeleteAll(sizefs);
|
||||
qDeleteAll(ulonglongs);
|
||||
qDeleteAll(urllists);
|
||||
}
|
||||
|
||||
bool *newBool()
|
||||
{
|
||||
bool *v = new bool;
|
||||
bools.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QString *newString()
|
||||
{
|
||||
QString *v = new QString;
|
||||
strings.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QStringList *newStringList()
|
||||
{
|
||||
QStringList *v = new QStringList;
|
||||
stringlists.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QColor *newColor()
|
||||
{
|
||||
QColor *v = new QColor;
|
||||
colors.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QFont *newFont()
|
||||
{
|
||||
QFont *v = new QFont;
|
||||
fonts.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
qint32 *newInt()
|
||||
{
|
||||
qint32 *v = new qint32;
|
||||
ints.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
quint32 *newUint()
|
||||
{
|
||||
quint32 *v = new quint32;
|
||||
uints.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QUrl *newUrl()
|
||||
{
|
||||
QUrl *v = new QUrl;
|
||||
urls.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QDateTime *newDateTime()
|
||||
{
|
||||
QDateTime *v = new QDateTime;
|
||||
dateTimes.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
double *newDouble()
|
||||
{
|
||||
double *v = new double;
|
||||
doubles.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QList<qint32> *newIntList()
|
||||
{
|
||||
QList<qint32> *v = new QList<qint32>;
|
||||
intlists.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
qint64 *newLongLong()
|
||||
{
|
||||
qint64 *v = new qint64;
|
||||
longlongs.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QPoint *newPoint()
|
||||
{
|
||||
QPoint *v = new QPoint;
|
||||
points.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QPointF *newPointF()
|
||||
{
|
||||
QPointF *v = new QPointF;
|
||||
pointfs.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QRect *newRect()
|
||||
{
|
||||
QRect *v = new QRect;
|
||||
rects.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QRectF *newRectF()
|
||||
{
|
||||
QRectF *v = new QRectF;
|
||||
rectfs.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QSize *newSize()
|
||||
{
|
||||
QSize *v = new QSize;
|
||||
sizes.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QSizeF *newSizeF()
|
||||
{
|
||||
QSizeF *v = new QSizeF;
|
||||
sizefs.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
quint64 *newULongLong()
|
||||
{
|
||||
quint64 *v = new quint64;
|
||||
ulonglongs.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
QList<QUrl> *newUrlList()
|
||||
{
|
||||
QList<QUrl> *v = new QList<QUrl>();
|
||||
urllists.append(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void parse(KConfigLoader *loader, QIODevice *xml);
|
||||
|
||||
/**
|
||||
* Whether or not to write out default values.
|
||||
*
|
||||
* @param writeDefaults true if defaults should be written out
|
||||
*/
|
||||
void setWriteDefaults(bool writeDefaults)
|
||||
{
|
||||
saveDefaults = writeDefaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if default values will also be written out
|
||||
*/
|
||||
bool writeDefaults() const
|
||||
{
|
||||
return saveDefaults;
|
||||
}
|
||||
|
||||
QList<bool *> bools;
|
||||
QList<QString *> strings;
|
||||
QList<QStringList *> stringlists;
|
||||
QList<QColor *> colors;
|
||||
QList<QFont *> fonts;
|
||||
QList<qint32 *> ints;
|
||||
QList<quint32 *> uints;
|
||||
QList<QUrl *> urls;
|
||||
QList<QDateTime *> dateTimes;
|
||||
QList<double *> doubles;
|
||||
QList<QList<qint32> *> intlists;
|
||||
QList<qint64 *> longlongs;
|
||||
QList<QPoint *> points;
|
||||
QList<QPointF *> pointfs;
|
||||
QList<QRect *> rects;
|
||||
QList<QRectF *> rectfs;
|
||||
QList<QSize *> sizes;
|
||||
QList<QSizeF *> sizefs;
|
||||
QList<quint64 *> ulonglongs;
|
||||
QList<QList<QUrl> *> urllists;
|
||||
QString baseGroup;
|
||||
QStringList groups;
|
||||
QHash<QString, QString> keysToNames;
|
||||
bool saveDefaults;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007-2008 Aaron Seigo <aseigo@kde.org>
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGLOADERHANDLER_P_H
|
||||
#define KCONFIGLOADERHANDLER_P_H
|
||||
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
class ConfigLoaderHandler
|
||||
{
|
||||
public:
|
||||
ConfigLoaderHandler(KConfigLoader *config, ConfigLoaderPrivate *d);
|
||||
|
||||
bool parse(QIODevice *input);
|
||||
|
||||
void startElement(const QStringView localName, const QXmlStreamAttributes &attrs);
|
||||
void endElement(const QStringView localName);
|
||||
|
||||
private:
|
||||
void addItem();
|
||||
void resetState();
|
||||
|
||||
KConfigLoader *m_config;
|
||||
ConfigLoaderPrivate *d;
|
||||
int m_min;
|
||||
int m_max;
|
||||
QString m_name;
|
||||
QString m_key;
|
||||
QString m_type;
|
||||
QString m_label;
|
||||
QString m_default;
|
||||
QString m_cdata;
|
||||
QString m_whatsThis;
|
||||
KConfigSkeleton::ItemEnum::Choice m_choice;
|
||||
QList<KConfigSkeleton::ItemEnum::Choice> m_enumChoices;
|
||||
bool m_haveMin;
|
||||
bool m_haveMax;
|
||||
bool m_inChoice;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of KOrganizer.
|
||||
SPDX-FileCopyrightText: 2000, 2001 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigskeleton.h"
|
||||
|
||||
#include <kcoreconfigskeleton_p.h>
|
||||
|
||||
KConfigSkeleton::KConfigSkeleton(const QString &configname, QObject *parent)
|
||||
: KCoreConfigSkeleton(configname, parent)
|
||||
{
|
||||
}
|
||||
|
||||
KConfigSkeleton::KConfigSkeleton(KSharedConfig::Ptr pConfig, QObject *parent)
|
||||
: KCoreConfigSkeleton(std::move(pConfig), parent)
|
||||
{
|
||||
}
|
||||
|
||||
KConfigSkeleton::ItemColor::ItemColor(const QString &_group, const QString &_key, QColor &reference, const QColor &defaultValue)
|
||||
: KConfigSkeletonGenericItem<QColor>(_group, _key, reference, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigSkeleton::ItemColor::readConfig(KConfig *config)
|
||||
{
|
||||
KConfigGroup cg = configGroup(config);
|
||||
|
||||
mReference = cg.readEntry(mKey, mDefault);
|
||||
mLoadedValue = mReference;
|
||||
|
||||
readImmutability(cg);
|
||||
}
|
||||
|
||||
void KConfigSkeleton::ItemColor::setProperty(const QVariant &p)
|
||||
{
|
||||
mReference = qvariant_cast<QColor>(p);
|
||||
}
|
||||
|
||||
bool KConfigSkeleton::ItemColor::isEqual(const QVariant &v) const
|
||||
{
|
||||
return mReference == qvariant_cast<QColor>(v);
|
||||
}
|
||||
|
||||
QVariant KConfigSkeleton::ItemColor::property() const
|
||||
{
|
||||
return QVariant(mReference);
|
||||
}
|
||||
|
||||
KConfigSkeleton::ItemFont::ItemFont(const QString &_group, const QString &_key, QFont &reference, const QFont &defaultValue)
|
||||
: KConfigSkeletonGenericItem<QFont>(_group, _key, reference, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigSkeleton::ItemFont::readConfig(KConfig *config)
|
||||
{
|
||||
KConfigGroup cg = configGroup(config);
|
||||
|
||||
mReference = cg.readEntry(mKey, mDefault);
|
||||
mLoadedValue = mReference;
|
||||
|
||||
readImmutability(cg);
|
||||
}
|
||||
|
||||
void KConfigSkeleton::ItemFont::setProperty(const QVariant &p)
|
||||
{
|
||||
mReference = qvariant_cast<QFont>(p);
|
||||
}
|
||||
|
||||
bool KConfigSkeleton::ItemFont::isEqual(const QVariant &v) const
|
||||
{
|
||||
return mReference == qvariant_cast<QFont>(v);
|
||||
}
|
||||
|
||||
QVariant KConfigSkeleton::ItemFont::property() const
|
||||
{
|
||||
return QVariant(mReference);
|
||||
}
|
||||
|
||||
KConfigSkeleton::ItemColor *KConfigSkeleton::addItemColor(const QString &name, QColor &reference, const QColor &defaultValue, const QString &key)
|
||||
{
|
||||
KConfigSkeleton::ItemColor *item;
|
||||
item = new KConfigSkeleton::ItemColor(d->mCurrentGroup, key.isNull() ? name : key, reference, defaultValue);
|
||||
addItem(item, name);
|
||||
return item;
|
||||
}
|
||||
|
||||
KConfigSkeleton::ItemFont *KConfigSkeleton::addItemFont(const QString &name, QFont &reference, const QFont &defaultValue, const QString &key)
|
||||
{
|
||||
KConfigSkeleton::ItemFont *item;
|
||||
item = new KConfigSkeleton::ItemFont(d->mCurrentGroup, key.isNull() ? name : key, reference, defaultValue);
|
||||
addItem(item, name);
|
||||
return item;
|
||||
}
|
||||
|
||||
#include "moc_kconfigskeleton.cpp"
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGSKELETON_H
|
||||
#define KCONFIGSKELETON_H
|
||||
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
#include <kcoreconfigskeleton.h>
|
||||
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
|
||||
/**
|
||||
* @class KConfigSkeleton kconfigskeleton.h <KConfigSkeleton>
|
||||
*
|
||||
* @short Class for handling preferences settings for an application.
|
||||
* @author Cornelius Schumacher
|
||||
*
|
||||
* This class extends KCoreConfigSkeleton by support for GUI types.
|
||||
*/
|
||||
class KCONFIGGUI_EXPORT KConfigSkeleton : public KCoreConfigSkeleton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Class for handling a color preferences item.
|
||||
*/
|
||||
class KCONFIGGUI_EXPORT ItemColor : public KConfigSkeletonGenericItem<QColor>
|
||||
{
|
||||
public:
|
||||
/** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */
|
||||
ItemColor(const QString &_group, const QString &_key, QColor &reference, const QColor &defaultValue = QColor(128, 128, 128));
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */
|
||||
void readConfig(KConfig *config) override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */
|
||||
void setProperty(const QVariant &p) override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) */
|
||||
bool isEqual(const QVariant &p) const override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::property() */
|
||||
QVariant property() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for handling a font preferences item.
|
||||
*/
|
||||
class KCONFIGGUI_EXPORT ItemFont : public KConfigSkeletonGenericItem<QFont>
|
||||
{
|
||||
public:
|
||||
/** @copydoc KConfigSkeletonGenericItem::KConfigSkeletonGenericItem */
|
||||
ItemFont(const QString &_group, const QString &_key, QFont &reference, const QFont &defaultValue = QFont());
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::readConfig(KConfig*) */
|
||||
void readConfig(KConfig *config) override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::setProperty(const QVariant&) */
|
||||
void setProperty(const QVariant &p) override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::isEqual(const QVariant &) */
|
||||
bool isEqual(const QVariant &p) const override;
|
||||
|
||||
/** @copydoc KConfigSkeletonItem::property() */
|
||||
QVariant property() const override;
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param configname name of config file. If no name is given, the default
|
||||
* config file as returned by KSharedConfig::openConfig() is used.
|
||||
*/
|
||||
explicit KConfigSkeleton(const QString &configname = QString(), QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param config configuration object to use.
|
||||
*/
|
||||
explicit KConfigSkeleton(KSharedConfig::Ptr config, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Register an item of type QColor.
|
||||
*
|
||||
* @param name Name used to identify this setting. Names must be unique.
|
||||
* @param reference Pointer to the variable, which is set by read()
|
||||
* calls and read by save() calls.
|
||||
* @param defaultValue Default value, which is used when the config file
|
||||
* does not yet contain the key of this item.
|
||||
* @param key Key used in config file. If @p key is a null string, @p name is used as key.
|
||||
* @return The created item
|
||||
*/
|
||||
ItemColor *addItemColor(const QString &name, QColor &reference, const QColor &defaultValue = QColor(128, 128, 128), const QString &key = QString());
|
||||
|
||||
/**
|
||||
* Register an item of type QFont.
|
||||
*
|
||||
* @param name Name used to identify this setting. Names must be unique.
|
||||
* @param reference Pointer to the variable, which is set by read()
|
||||
* calls and read by save() calls.
|
||||
* @param defaultValue Default value, which is used when the config file
|
||||
* does not yet contain the key of this item.
|
||||
* @param key Key used in config file. If @p key is a null string, @p name is used as key.
|
||||
* @return The created item
|
||||
*/
|
||||
ItemFont *addItemFont(const QString &name, QFont &reference, const QFont &defaultValue = QFont(), const QString &key = QString());
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Kurt Granroth <granroth@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kstandardactions.h"
|
||||
#include "kstandardactions_p.h"
|
||||
#include "moc_kstandardactions_p.cpp"
|
||||
|
||||
#include <KStandardShortcutWatcher>
|
||||
#include <QGuiApplication>
|
||||
|
||||
namespace KStandardActions
|
||||
{
|
||||
|
||||
QList<StandardAction> actionIds()
|
||||
{
|
||||
QList<StandardAction> result;
|
||||
|
||||
for (uint i = 0; g_rgActionInfo[i].id != ActionNone; i++) {
|
||||
result.append(g_rgActionInfo[i].id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id)
|
||||
{
|
||||
const KStandardActionsInfo *pInfo = infoPtr(id);
|
||||
return (pInfo) ? pInfo->idAccel : KStandardShortcut::AccelNone;
|
||||
}
|
||||
|
||||
QAction *_kgui_createInternal(StandardAction id, QObject *parent)
|
||||
{
|
||||
QAction *pAction = new QAction(parent);
|
||||
const KStandardActionsInfo *pInfo = infoPtr(id);
|
||||
|
||||
// qCDebug(KCONFIG_WIDGETS_LOG) << "KStandardActions::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)0) << ", " << parent << " )"; //
|
||||
// ellis
|
||||
|
||||
if (pInfo) {
|
||||
QString sLabel;
|
||||
QString iconName = pInfo->psIconName.toString();
|
||||
|
||||
switch (id) {
|
||||
case Back:
|
||||
sLabel = QCoreApplication::translate("go back", "&Back");
|
||||
if (QGuiApplication::isRightToLeft()) {
|
||||
iconName = QStringLiteral("go-next");
|
||||
}
|
||||
break;
|
||||
|
||||
case Forward:
|
||||
sLabel = QCoreApplication::translate("go forward", "&Forward");
|
||||
if (QGuiApplication::isRightToLeft()) {
|
||||
iconName = QStringLiteral("go-previous");
|
||||
}
|
||||
break;
|
||||
|
||||
case Home:
|
||||
sLabel = QCoreApplication::translate("home page", "&Home");
|
||||
break;
|
||||
case Preferences:
|
||||
case AboutApp:
|
||||
case HelpContents: {
|
||||
QString appDisplayName = QGuiApplication::applicationDisplayName();
|
||||
if (appDisplayName.isEmpty()) {
|
||||
appDisplayName = QCoreApplication::applicationName();
|
||||
}
|
||||
sLabel = QCoreApplication::translate("KStandardActions", pInfo->psLabel).arg(appDisplayName);
|
||||
} break;
|
||||
default:
|
||||
sLabel = QCoreApplication::translate("KStandardActions", pInfo->psLabel);
|
||||
}
|
||||
|
||||
if (QGuiApplication::isRightToLeft()) {
|
||||
switch (id) {
|
||||
case Prior:
|
||||
iconName = QStringLiteral("go-next-view-page");
|
||||
break;
|
||||
case Next:
|
||||
iconName = QStringLiteral("go-previous-view-page");
|
||||
break;
|
||||
case FirstPage:
|
||||
iconName = QStringLiteral("go-last-view-page");
|
||||
break;
|
||||
case LastPage:
|
||||
iconName = QStringLiteral("go-first-view-page");
|
||||
break;
|
||||
case DocumentBack:
|
||||
iconName = QStringLiteral("go-next");
|
||||
break;
|
||||
case DocumentForward:
|
||||
iconName = QStringLiteral("go-previous");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == Donate) {
|
||||
const QString currencyCode = QLocale().currencySymbol(QLocale::CurrencyIsoCode).toLower();
|
||||
if (!currencyCode.isEmpty()) {
|
||||
iconName = QStringLiteral("help-donate-%1").arg(currencyCode);
|
||||
}
|
||||
}
|
||||
|
||||
QIcon icon = iconName.isEmpty() ? QIcon() : QIcon::fromTheme(iconName);
|
||||
|
||||
if (id == AboutApp) {
|
||||
icon = qGuiApp->windowIcon();
|
||||
}
|
||||
|
||||
// Set the text before setting the MenuRole, as on OS X setText will do some heuristic role guessing.
|
||||
// This ensures user menu items get the intended role out of the list below.
|
||||
pAction->setText(sLabel);
|
||||
|
||||
switch (id) {
|
||||
case Quit:
|
||||
pAction->setMenuRole(QAction::QuitRole);
|
||||
break;
|
||||
|
||||
case Preferences:
|
||||
pAction->setMenuRole(QAction::PreferencesRole);
|
||||
break;
|
||||
|
||||
case AboutApp:
|
||||
pAction->setMenuRole(QAction::AboutRole);
|
||||
break;
|
||||
|
||||
default:
|
||||
pAction->setMenuRole(QAction::NoRole);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!QCoreApplication::translate("KStandardActions", pInfo->psToolTip).isEmpty()) {
|
||||
pAction->setToolTip(QCoreApplication::translate("KStandardActions", pInfo->psToolTip));
|
||||
}
|
||||
pAction->setIcon(icon);
|
||||
|
||||
QList<QKeySequence> cut = KStandardShortcut::shortcut(pInfo->idAccel);
|
||||
if (!cut.isEmpty()) {
|
||||
// emulate KActionCollection::setDefaultShortcuts to allow the use of "configure shortcuts"
|
||||
pAction->setShortcuts(cut);
|
||||
pAction->setProperty("defaultShortcuts", QVariant::fromValue(cut));
|
||||
}
|
||||
pAction->connect(KStandardShortcut::shortcutWatcher(),
|
||||
&KStandardShortcut::StandardShortcutWatcher::shortcutChanged,
|
||||
pAction,
|
||||
[pAction, shortcut = pInfo->idAccel](KStandardShortcut::StandardShortcut id, const QList<QKeySequence> &newShortcut) {
|
||||
if (id != shortcut) {
|
||||
return;
|
||||
}
|
||||
pAction->setShortcuts(newShortcut);
|
||||
pAction->setProperty("defaultShortcuts", QVariant::fromValue(newShortcut));
|
||||
});
|
||||
|
||||
pAction->setObjectName(pInfo->psName.toString());
|
||||
}
|
||||
|
||||
if (pAction && parent && parent->inherits("KActionCollection")) {
|
||||
QMetaObject::invokeMethod(parent, "addAction", Q_ARG(QString, pAction->objectName()), Q_ARG(QAction *, pAction));
|
||||
}
|
||||
|
||||
return pAction;
|
||||
}
|
||||
|
||||
QString name(StandardAction id)
|
||||
{
|
||||
const KStandardActionsInfo *pInfo = infoPtr(id);
|
||||
return (pInfo) ? pInfo->psName.toString() : QString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Kurt Granroth <granroth@kde.org>
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
#ifndef KGUISTANDARDACTION_H
|
||||
#define KGUISTANDARDACTION_H
|
||||
|
||||
#include <QAction>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
|
||||
#include <KStandardShortcut>
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
class QObject;
|
||||
class QAction;
|
||||
|
||||
/**
|
||||
* Convenience methods to access all standard KDE actions.
|
||||
*
|
||||
* These actions should be used instead of hardcoding menubar and
|
||||
* toolbar items. Using these actions helps your application easily
|
||||
* conform to the <a href="https://develop.kde.org/hig/">KDE Human Interface Guidelines</a>.
|
||||
*
|
||||
* All of the documentation for QAction holds for KStandardActions
|
||||
* also. When in doubt on how things work, check the QAction
|
||||
* documentation first.
|
||||
* Please note that calling any of these methods automatically adds the action
|
||||
* to the actionCollection() of the QObject given by the 'parent' parameter.
|
||||
*
|
||||
* <b>Simple Example:</b>\n
|
||||
*
|
||||
* In general, using standard actions should be a drop in replacement
|
||||
* for regular actions. For example, if you previously had:
|
||||
* @code
|
||||
* QAction *newAct = new QAction(QIcon::fromTheme("document-new"),
|
||||
* i18n("&New"),
|
||||
* this);
|
||||
* newAct->setShortcut(KStandardShortcut::shortcut(KStandardShortcut::New).constFirst());
|
||||
* connect(newAct, &QAction::triggered, this, &ClassFoo::fileNew);
|
||||
* @endcode
|
||||
*
|
||||
* You can replace it with:
|
||||
* @code
|
||||
* QAction *newAct = KStandardActions::openNew(this, &ClassFoo::fileNew, this);
|
||||
* @endcode
|
||||
*
|
||||
* Alternatively you can instantiate the action using the StandardAction enums
|
||||
* provided. This author can't think of a reason why you would want to, but, hey,
|
||||
* if you do, here's how:
|
||||
*
|
||||
* \code
|
||||
* QAction *newAct = KStandardActions::create(KStandardActions::New, this, &ClassFoo::fileNew, this);
|
||||
* \endcode
|
||||
*
|
||||
* <b>Relationship with KActionCollection from KXMLGui</b>\n
|
||||
*
|
||||
* If a KActionCollection is passed as the parent then the action will be
|
||||
* automatically added to that collection:
|
||||
* \code
|
||||
* QAction *cut = KStandardActions::cut(this, &ClassFoo::editCut, actionCollection());
|
||||
* \endcode
|
||||
*
|
||||
* Each action has a unique internal name which can be queried using the
|
||||
* name method. For example KStandardActions::name(KStandardActions::Cut)
|
||||
* would return 'edit_cut'. This name can be used as a unique identifier
|
||||
* for the actions. So if you wanted to add an existing standard action
|
||||
* to an action collection you can do so like this:
|
||||
* \code
|
||||
* QAction *cut = KStandardActions::cut(this, &ClassFoo::editCut, this);
|
||||
* actionCollection()->addAction(KStandardActions::name(KStandardActions::Cut), cut);
|
||||
* \endcode
|
||||
*
|
||||
* You can then get a pointer to the action using
|
||||
* \code
|
||||
* QAction *cutPtr = actionCollection()->action(KStandardActions::name(KStandardActions::Cut));
|
||||
* \endcode
|
||||
*
|
||||
* @author Kurt Granroth <granroth@kde.org>
|
||||
* @since 6.3
|
||||
*/
|
||||
namespace KStandardActions
|
||||
{
|
||||
/**
|
||||
* The standard menubar and toolbar actions.
|
||||
*/
|
||||
enum StandardAction {
|
||||
ActionNone,
|
||||
// File Menu
|
||||
New, ///< Create a new document or window.
|
||||
Open, ///< Open an existing file.
|
||||
OpenRecent, ///< Open a recently used document.
|
||||
Save, ///< Save the current document.
|
||||
SaveAs, ///< Save the current document under a different name.
|
||||
Revert, ///< Revert the current document to the last saved version.
|
||||
Close, ///< Close the current document.
|
||||
Print, ///< Print the current document.
|
||||
PrintPreview, ///< Show a print preview of the current document.
|
||||
Mail, ///< Send the current document by mail.
|
||||
Quit, ///< Quit the program.
|
||||
// Edit Menu
|
||||
Undo, ///< Undo the last operation.
|
||||
Redo, ///< Redo the last operation.
|
||||
Cut, ///< Cut selected area and store it in the clipboard.
|
||||
Copy, ///< Copy selected area and store it in the clipboard.
|
||||
Paste, ///< Paste the contents of clipboard at the current mouse or cursor.
|
||||
SelectAll, ///< Select all elements in the current document.
|
||||
Deselect, ///< Deselect any selected elements in the current document.
|
||||
Find, ///< Initiate a 'find' request in the current document.
|
||||
FindNext, ///< Find the next instance of a stored 'find'
|
||||
FindPrev, ///< Find a previous instance of a stored 'find'.
|
||||
Replace, ///< Find and replace matches.
|
||||
// View Menu
|
||||
ActualSize, ///< View the document at its actual size.
|
||||
FitToPage, ///< Fit the document view to the size of the current window.
|
||||
FitToWidth, ///< Fit the document view to the width of the current window.
|
||||
FitToHeight, ///< Fit the document view to the height of the current window.
|
||||
ZoomIn, ///< Zoom in the current document.
|
||||
ZoomOut, ///< Zoom out the current document.
|
||||
Zoom, ///< Select the current zoom level.
|
||||
Redisplay, ///< Redisplay or redraw the document.
|
||||
// Go Menu
|
||||
Up, ///< Move up (web style menu).
|
||||
Back, ///< Move back (web style menu).
|
||||
Forward, ///< Move forward (web style menu).
|
||||
Home, ///< Go to the "Home" position or document.
|
||||
Prior, ///< Scroll up one page.
|
||||
Next, ///< Scroll down one page.
|
||||
Goto, ///< Jump to some specific location in the document.
|
||||
GotoPage, ///< Go to a specific page.
|
||||
GotoLine, ///< Go to a specific line.
|
||||
FirstPage, ///< Jump to the first page.
|
||||
LastPage, ///< Jump to the last page.
|
||||
DocumentBack, ///< Move back (document style menu).
|
||||
DocumentForward, ///< Move forward (document style menu).
|
||||
// Bookmarks Menu
|
||||
AddBookmark, ///< Add the current page to the bookmarks tree.
|
||||
EditBookmarks, ///< Edit the application bookmarks.
|
||||
// Tools Menu
|
||||
Spelling, ///< Pop up the spell checker.
|
||||
// Settings Menu
|
||||
ShowMenubar, ///< Show/Hide the menubar.
|
||||
ShowToolbar, ///< Show/Hide the toolbar.
|
||||
ShowStatusbar, ///< Show/Hide the statusbar.
|
||||
KeyBindings, ///< Display the configure key bindings dialog.
|
||||
Preferences, ///< Display the preferences/options dialog.
|
||||
ConfigureToolbars, ///< Display the toolbar configuration dialog.
|
||||
// Help Menu
|
||||
HelpContents, ///< Display the handbook of the application.
|
||||
WhatsThis, ///< Trigger the What's This cursor.
|
||||
ReportBug, ///< Open up the Report Bug dialog.
|
||||
AboutApp, ///< Display the application's About box.
|
||||
AboutKDE, ///< Display the About KDE dialog.
|
||||
// Other standard actions
|
||||
ConfigureNotifications, ///< Display the notifications configuration dialog.
|
||||
FullScreen, ///< Switch to/from full screen mode.
|
||||
Clear, ///< Clear the content of the focus widget.
|
||||
SwitchApplicationLanguage, ///< Display the Switch Application Language dialog.
|
||||
DeleteFile, ///< Permanently deletes files or folders.
|
||||
RenameFile, ///< Renames files or folders.
|
||||
MoveToTrash, ///< Moves files or folders to the trash.
|
||||
Donate, ///< Open donation page on kde.org.
|
||||
HamburgerMenu ///< Opens a menu that substitutes the menubar.
|
||||
// To keep in sync with KConfigWidgets::KStandardAction
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QAction *_kgui_createInternal(StandardAction id, QObject *parent);
|
||||
|
||||
/**
|
||||
* Creates an action corresponding to one of the
|
||||
* KStandardActions::StandardAction actions, which is connected to the given
|
||||
* object and @p slot, and is owned by @p parent.
|
||||
*
|
||||
* If not explicitly specified, @p connectionType will be AutoConnection for all actions
|
||||
* except for ConfigureToolbars it will be QueuedConnection.
|
||||
*
|
||||
* @see create(StandardAction, const QObject *, const char *, QObject *)
|
||||
*/
|
||||
template<class Receiver, class Func>
|
||||
inline QAction *create(StandardAction id, const Receiver *recvr, Func slot, QObject *parent, std::optional<Qt::ConnectionType> connectionType = std::nullopt)
|
||||
{
|
||||
QAction *action = _kgui_createInternal(id, parent);
|
||||
// ConfigureToolbars is special because of bug #200815
|
||||
const Qt::ConnectionType defaultConnectionType = (id == ConfigureToolbars) ? Qt::QueuedConnection : Qt::AutoConnection;
|
||||
QObject::connect(action, &QAction::triggered, recvr, slot, connectionType.value_or(defaultConnectionType));
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return the internal name of a given standard action.
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QString name(StandardAction id);
|
||||
|
||||
/**
|
||||
* Returns a list of all actionIds.
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QList<StandardAction> actionIds();
|
||||
|
||||
/**
|
||||
* Returns the standardshortcut associated with @a actionId.
|
||||
*
|
||||
* @param id The identifier whose associated shortcut is wanted.
|
||||
*/
|
||||
KCONFIGGUI_EXPORT KStandardShortcut::StandardShortcut shortcutForActionId(StandardAction id);
|
||||
|
||||
// clang-format off
|
||||
#define KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(name, enumValue) \
|
||||
template<class Receiver, class Func> \
|
||||
inline QAction *name(const Receiver *recvr, Func slot, QObject *parent) \
|
||||
{ return create(enumValue, recvr, slot, parent); }
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* Create a new document or window.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(openNew, New)
|
||||
|
||||
/**
|
||||
* Open an existing file.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(open, Open)
|
||||
|
||||
/**
|
||||
* Save the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(save, Save)
|
||||
|
||||
/**
|
||||
* Save the current document under a different name.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(saveAs, SaveAs)
|
||||
|
||||
/**
|
||||
* Revert the current document to the last saved version
|
||||
* (essentially will undo all changes).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(revert, Revert)
|
||||
|
||||
/**
|
||||
* Close the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(close, Close)
|
||||
|
||||
/**
|
||||
* Print the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(print, Print)
|
||||
|
||||
/**
|
||||
* Show a print preview of the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(printPreview, PrintPreview)
|
||||
|
||||
/**
|
||||
* Mail this document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(mail, Mail)
|
||||
|
||||
/**
|
||||
* Quit the program.
|
||||
*
|
||||
* Note that you probably want to connect this action to either QWidget::close()
|
||||
* or QApplication::closeAllWindows(), but not QApplication::quit(), so that
|
||||
* KMainWindow::queryClose() is called on any open window (to warn the user
|
||||
* about unsaved changes for example).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(quit, Quit)
|
||||
|
||||
/**
|
||||
* Undo the last operation.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(undo, Undo)
|
||||
|
||||
/**
|
||||
* Redo the last operation.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(redo, Redo)
|
||||
|
||||
/**
|
||||
* Cut selected area and store it in the clipboard.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(cut, Cut)
|
||||
|
||||
/**
|
||||
* Copy the selected area into the clipboard.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(copy, Copy)
|
||||
|
||||
/**
|
||||
* Paste the contents of clipboard at the current mouse or cursor
|
||||
* position.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(paste, Paste)
|
||||
|
||||
/**
|
||||
* Clear the content of the focus widget
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(clear, Clear)
|
||||
|
||||
/**
|
||||
* Select all elements in the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(selectAll, SelectAll)
|
||||
|
||||
/**
|
||||
* Deselect any selected elements in the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(deselect, Deselect)
|
||||
|
||||
/**
|
||||
* Initiate a 'find' request in the current document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(find, Find)
|
||||
|
||||
/**
|
||||
* Find the next instance of a stored 'find'.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(findNext, FindNext)
|
||||
|
||||
/**
|
||||
* Find a previous instance of a stored 'find'.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(findPrev, FindPrev)
|
||||
|
||||
/**
|
||||
* Find and replace matches.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(replace, Replace)
|
||||
|
||||
/**
|
||||
* View the document at its actual size.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(actualSize, ActualSize)
|
||||
|
||||
/**
|
||||
* Fit the document view to the size of the current window.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(fitToPage, FitToPage)
|
||||
|
||||
/**
|
||||
* Fit the document view to the width of the current window.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(fitToWidth, FitToWidth)
|
||||
|
||||
/**
|
||||
* Fit the document view to the height of the current window.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(fitToHeight, FitToHeight)
|
||||
|
||||
/**
|
||||
* Zoom in the current document view.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(zoomIn, ZoomIn)
|
||||
|
||||
/**
|
||||
* Zoom out the current document view.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(zoomOut, ZoomOut)
|
||||
|
||||
/**
|
||||
* Select the current zoom level.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(zoom, Zoom)
|
||||
|
||||
/**
|
||||
* Redisplay or redraw the document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(redisplay, Redisplay)
|
||||
|
||||
/**
|
||||
* Move up (web style menu).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(up, Up)
|
||||
|
||||
/**
|
||||
* Move back (web style menu).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(back, Back)
|
||||
|
||||
/**
|
||||
* Move forward (web style menu).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(forward, Forward)
|
||||
|
||||
/**
|
||||
* Go to the "Home" position or document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(home, Home)
|
||||
|
||||
/**
|
||||
* Scroll up one page.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(prior, Prior)
|
||||
|
||||
/**
|
||||
* Scroll down one page.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(next, Next)
|
||||
|
||||
/**
|
||||
* Jump to some specific location in the document.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(goTo, Goto)
|
||||
|
||||
/**
|
||||
* Go to a specific page.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(gotoPage, GotoPage)
|
||||
|
||||
/**
|
||||
* Go to a specific line.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(gotoLine, GotoLine)
|
||||
|
||||
/**
|
||||
* Jump to the first page.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(firstPage, FirstPage)
|
||||
|
||||
/**
|
||||
* Jump to the last page.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(lastPage, LastPage)
|
||||
|
||||
/**
|
||||
* Move back (document style menu).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(documentBack, DocumentBack)
|
||||
|
||||
/**
|
||||
* Move forward (document style menu).
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(documentForward, DocumentForward)
|
||||
|
||||
/**
|
||||
* Add the current page to the bookmarks tree.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(addBookmark, AddBookmark)
|
||||
|
||||
/**
|
||||
* Edit the application bookmarks.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(editBookmarks, EditBookmarks)
|
||||
|
||||
/**
|
||||
* Pop up the spell checker.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(spelling, Spelling)
|
||||
|
||||
/**
|
||||
* Display the configure key bindings dialog.
|
||||
*
|
||||
* Note that you might be able to use the pre-built KXMLGUIFactory's function:
|
||||
* @code
|
||||
* KStandardActions::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
|
||||
* @endcode
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(keyBindings, KeyBindings)
|
||||
|
||||
/**
|
||||
* Display the preferences/options dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(preferences, Preferences)
|
||||
|
||||
/**
|
||||
* Display the toolbar configuration dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(configureToolbars, ConfigureToolbars)
|
||||
|
||||
/**
|
||||
* Display the notifications configuration dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(configureNotifications, ConfigureNotifications)
|
||||
|
||||
/**
|
||||
* Display the Switch Application Language dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(switchApplicationLanguage, SwitchApplicationLanguage)
|
||||
|
||||
/**
|
||||
* Display the handbook of the application.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(helpContents, HelpContents)
|
||||
|
||||
/**
|
||||
* Trigger the What's This cursor.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(whatsThis, WhatsThis)
|
||||
|
||||
/**
|
||||
* Open up the Report Bug dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(reportBug, ReportBug)
|
||||
|
||||
/**
|
||||
* Display the application's About box.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(aboutApp, AboutApp)
|
||||
|
||||
/**
|
||||
* Display the About KDE dialog.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(aboutKDE, AboutKDE)
|
||||
|
||||
/**
|
||||
* Permanently deletes files or folders.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(deleteFile, DeleteFile)
|
||||
|
||||
/**
|
||||
* Renames files or folders.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(renameFile, RenameFile)
|
||||
|
||||
/**
|
||||
* Moves files or folders to the trash.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(moveToTrash, MoveToTrash)
|
||||
|
||||
/**
|
||||
* Open donation page on kde.org.
|
||||
*/
|
||||
KGUISTANDARDACTION_WITH_NEW_STYLE_CONNECT(donate, Donate)
|
||||
}
|
||||
|
||||
#endif // KSTDACTION_H
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Kurt Granroth <granroth@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAction>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <KStandardActions>
|
||||
#include <KStandardShortcut>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace KStandardActions
|
||||
{
|
||||
|
||||
/**
|
||||
* Helper class for storing raw data in static tables which can be used for QString instance
|
||||
* creation at runtime without copying/converting to new memalloc'ed memory, as well as avoiding
|
||||
* that way storing the strings directly as QStrings resulting in non-constexpr init code on
|
||||
* library loading
|
||||
* @internal
|
||||
*/
|
||||
struct RawStringData {
|
||||
template<std::size_t StringSize>
|
||||
constexpr inline RawStringData(const char16_t (&_data)[StringSize])
|
||||
: data(_data)
|
||||
, size(std::char_traits<char16_t>::length(_data))
|
||||
{
|
||||
}
|
||||
constexpr inline RawStringData(std::nullptr_t)
|
||||
{
|
||||
}
|
||||
constexpr inline RawStringData() = default;
|
||||
|
||||
inline QString toString() const
|
||||
{
|
||||
if (!data) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return Qt::Literals::StringLiterals::operator""_s(data, size);
|
||||
}
|
||||
|
||||
private:
|
||||
const char16_t *const data = nullptr;
|
||||
const std::size_t size = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
struct KStandardActionsInfo {
|
||||
KStandardActions::StandardAction id;
|
||||
KStandardShortcut::StandardShortcut idAccel;
|
||||
const RawStringData psName;
|
||||
const char *psLabel;
|
||||
const char *psToolTip;
|
||||
const RawStringData psIconName;
|
||||
};
|
||||
// clang-format off
|
||||
static constexpr KStandardActionsInfo g_rgActionInfo[] = {
|
||||
{ New, KStandardShortcut::New, u"file_new", QT_TRANSLATE_NOOP("KStandardActions", "&New"), QT_TRANSLATE_NOOP("KStandardActions", "Create new document"), u"document-new" },
|
||||
{ Open, KStandardShortcut::Open, u"file_open", QT_TRANSLATE_NOOP("KStandardActions", "&Open…"), QT_TRANSLATE_NOOP("KStandardActions", "Open an existing document"), u"document-open" },
|
||||
{ OpenRecent, KStandardShortcut::AccelNone, u"file_open_recent", QT_TRANSLATE_NOOP("KStandardActions", "Open &Recent"), QT_TRANSLATE_NOOP("KStandardActions", "Open a document which was recently opened"), u"document-open-recent" },
|
||||
{ Save, KStandardShortcut::Save, u"file_save", QT_TRANSLATE_NOOP("KStandardActions", "&Save"), QT_TRANSLATE_NOOP("KStandardActions", "Save document"), u"document-save" },
|
||||
{ SaveAs, KStandardShortcut::SaveAs, u"file_save_as", QT_TRANSLATE_NOOP("KStandardActions", "Save &As…"), QT_TRANSLATE_NOOP("KStandardActions", "Save document under a new name"), u"document-save-as" },
|
||||
{ Revert, KStandardShortcut::Revert, u"file_revert", QT_TRANSLATE_NOOP("KStandardActions", "Re&vert"), QT_TRANSLATE_NOOP("KStandardActions", "Revert unsaved changes made to document"), u"document-revert" },
|
||||
{ Close, KStandardShortcut::Close, u"file_close", QT_TRANSLATE_NOOP("KStandardActions", "&Close"), QT_TRANSLATE_NOOP("KStandardActions", "Close document"), u"document-close" },
|
||||
{ Print, KStandardShortcut::Print, u"file_print", QT_TRANSLATE_NOOP("KStandardActions", "&Print…"), QT_TRANSLATE_NOOP("KStandardActions", "Print document"), u"document-print" },
|
||||
{ PrintPreview, KStandardShortcut::PrintPreview, u"file_print_preview", QT_TRANSLATE_NOOP("KStandardActions", "Print Previe&w"), QT_TRANSLATE_NOOP("KStandardActions", "Show a print preview of document"), u"document-print-preview" },
|
||||
{ Mail, KStandardShortcut::Mail, u"file_mail", QT_TRANSLATE_NOOP("KStandardActions", "&Mail…"), QT_TRANSLATE_NOOP("KStandardActions", "Send document by mail"), u"mail-send" },
|
||||
{ Quit, KStandardShortcut::Quit, u"file_quit", QT_TRANSLATE_NOOP("KStandardActions", "&Quit"), QT_TRANSLATE_NOOP("KStandardActions", "Quit application"), u"application-exit" },
|
||||
|
||||
{ Undo, KStandardShortcut::Undo, u"edit_undo", QT_TRANSLATE_NOOP("KStandardActions", "&Undo"), QT_TRANSLATE_NOOP("KStandardActions", "Undo last action"), u"edit-undo" },
|
||||
{ Redo, KStandardShortcut::Redo, u"edit_redo", QT_TRANSLATE_NOOP("KStandardActions", "Re&do"), QT_TRANSLATE_NOOP("KStandardActions", "Redo last undone action"), u"edit-redo" },
|
||||
{ Cut, KStandardShortcut::Cut, u"edit_cut", QT_TRANSLATE_NOOP("KStandardActions", "Cu&t"), QT_TRANSLATE_NOOP("KStandardActions", "Cut selection to clipboard"), u"edit-cut" },
|
||||
{ Copy, KStandardShortcut::Copy, u"edit_copy", QT_TRANSLATE_NOOP("KStandardActions", "&Copy"), QT_TRANSLATE_NOOP("KStandardActions", "Copy selection to clipboard"), u"edit-copy" },
|
||||
{ Paste, KStandardShortcut::Paste, u"edit_paste", QT_TRANSLATE_NOOP("KStandardActions", "&Paste"), QT_TRANSLATE_NOOP("KStandardActions", "Paste clipboard content"), u"edit-paste" },
|
||||
{ Clear, KStandardShortcut::Clear, u"edit_clear", QT_TRANSLATE_NOOP("KStandardActions", "C&lear"), {}, u"edit-clear" },
|
||||
{ SelectAll, KStandardShortcut::SelectAll, u"edit_select_all", QT_TRANSLATE_NOOP("KStandardActions", "Select &All"), {}, u"edit-select-all" },
|
||||
{ Deselect, KStandardShortcut::Deselect, u"edit_deselect", QT_TRANSLATE_NOOP("KStandardActions", "Dese&lect"), {}, u"edit-select-none" },
|
||||
{ Find, KStandardShortcut::Find, u"edit_find", QT_TRANSLATE_NOOP("KStandardActions", "&Find…"), {}, u"edit-find" },
|
||||
{ FindNext, KStandardShortcut::FindNext, u"edit_find_next", QT_TRANSLATE_NOOP("KStandardActions", "Find &Next"), {}, u"go-down-search" },
|
||||
{ FindPrev, KStandardShortcut::FindPrev, u"edit_find_prev", QT_TRANSLATE_NOOP("KStandardActions", "Find Pre&vious"), {}, u"go-up-search" },
|
||||
{ Replace, KStandardShortcut::Replace, u"edit_replace", QT_TRANSLATE_NOOP("KStandardActions", "&Replace…"), {}, u"edit-find-replace" },
|
||||
|
||||
{ ActualSize, KStandardShortcut::ActualSize, u"view_actual_size", QT_TRANSLATE_NOOP("KStandardActions", "Zoom to &Actual Size"), QT_TRANSLATE_NOOP("KStandardActions", "View document at its actual size"), u"zoom-original" },
|
||||
{ FitToPage, KStandardShortcut::FitToPage, u"view_fit_to_page", QT_TRANSLATE_NOOP("KStandardActions", "&Fit to Page"), QT_TRANSLATE_NOOP("KStandardActions", "Zoom to fit page in window"), u"zoom-fit-page" },
|
||||
{ FitToWidth, KStandardShortcut::FitToWidth, u"view_fit_to_width", QT_TRANSLATE_NOOP("KStandardActions", "Fit to Page &Width"), QT_TRANSLATE_NOOP("KStandardActions", "Zoom to fit page width in window"), u"zoom-fit-width" },
|
||||
{ FitToHeight, KStandardShortcut::FitToHeight, u"view_fit_to_height", QT_TRANSLATE_NOOP("KStandardActions", "Fit to Page &Height"), QT_TRANSLATE_NOOP("KStandardActions", "Zoom to fit page height in window"), u"zoom-fit-height" },
|
||||
{ ZoomIn, KStandardShortcut::ZoomIn, u"view_zoom_in", QT_TRANSLATE_NOOP("KStandardActions", "Zoom &In"), {}, u"zoom-in" },
|
||||
{ ZoomOut, KStandardShortcut::ZoomOut, u"view_zoom_out", QT_TRANSLATE_NOOP("KStandardActions", "Zoom &Out"), {}, u"zoom-out" },
|
||||
{ Zoom, KStandardShortcut::Zoom, u"view_zoom", QT_TRANSLATE_NOOP("KStandardActions", "&Zoom…"), QT_TRANSLATE_NOOP("KStandardActions", "Select zoom level"), u"zoom" },
|
||||
{ Redisplay, KStandardShortcut::Reload, u"view_redisplay", QT_TRANSLATE_NOOP("KStandardActions", "&Refresh"), QT_TRANSLATE_NOOP("KStandardActions", "Refresh document"), u"view-refresh" },
|
||||
|
||||
{ Up, KStandardShortcut::Up, u"go_up", QT_TRANSLATE_NOOP("KStandardActions", "&Up"), QT_TRANSLATE_NOOP("KStandardActions", "Go up"), u"go-up" },
|
||||
// The following three have special i18n() needs for sLabel
|
||||
{ Back, KStandardShortcut::Back, u"go_back", {}, {}, u"go-previous" },
|
||||
{ Forward, KStandardShortcut::Forward, u"go_forward", {}, {}, u"go-next" },
|
||||
{ Home, KStandardShortcut::Home, u"go_home", {}, {}, u"go-home" },
|
||||
{ Prior, KStandardShortcut::Prior, u"go_previous", QT_TRANSLATE_NOOP("KStandardActions", "&Previous Page"), QT_TRANSLATE_NOOP("KStandardActions", "Go to previous page"), u"go-previous-view-page" },
|
||||
{ Next, KStandardShortcut::Next, u"go_next", QT_TRANSLATE_NOOP("KStandardActions", "&Next Page"), QT_TRANSLATE_NOOP("KStandardActions", "Go to next page"), u"go-next-view-page" },
|
||||
{ Goto, KStandardShortcut::Goto, u"go_goto", QT_TRANSLATE_NOOP("KStandardActions", "&Go To…"), {}, {} },
|
||||
{ GotoPage, KStandardShortcut::GotoPage, u"go_goto_page", QT_TRANSLATE_NOOP("KStandardActions", "&Go to Page…"), {}, u"go-jump" },
|
||||
{ GotoLine, KStandardShortcut::GotoLine, u"go_goto_line", QT_TRANSLATE_NOOP("KStandardActions", "&Go to Line…"), {}, {} },
|
||||
{ FirstPage, KStandardShortcut::Begin, u"go_first", QT_TRANSLATE_NOOP("KStandardActions", "&First Page"), QT_TRANSLATE_NOOP("KStandardActions", "Go to first page"), u"go-first-view-page" },
|
||||
{ LastPage, KStandardShortcut::End, u"go_last", QT_TRANSLATE_NOOP("KStandardActions", "&Last Page"), QT_TRANSLATE_NOOP("KStandardActions", "Go to last page"), u"go-last-view-page" },
|
||||
{ DocumentBack, KStandardShortcut::DocumentBack, u"go_document_back", QT_TRANSLATE_NOOP("KStandardActions", "&Back"), QT_TRANSLATE_NOOP("KStandardActions", "Go back in document"), u"go-previous" },
|
||||
{ DocumentForward, KStandardShortcut::DocumentForward, u"go_document_forward", QT_TRANSLATE_NOOP("KStandardActions", "&Forward"), QT_TRANSLATE_NOOP("KStandardActions", "Go forward in document"), u"go-next" },
|
||||
|
||||
{ AddBookmark, KStandardShortcut::AddBookmark, u"bookmark_add", QT_TRANSLATE_NOOP("KStandardActions", "&Add Bookmark"), {}, u"bookmark-new" },
|
||||
{ EditBookmarks, KStandardShortcut::EditBookmarks, u"bookmark_edit", QT_TRANSLATE_NOOP("KStandardActions", "&Edit Bookmarks…"), {}, u"bookmarks-organize" },
|
||||
|
||||
{ Spelling, KStandardShortcut::Spelling, u"tools_spelling", QT_TRANSLATE_NOOP("KStandardActions", "&Spelling…"), QT_TRANSLATE_NOOP("KStandardActions", "Check spelling in document"), u"tools-check-spelling" },
|
||||
|
||||
{ ShowMenubar, KStandardShortcut::ShowMenubar, u"options_show_menubar", QT_TRANSLATE_NOOP("KStandardActions", "Show &Menubar"), QT_TRANSLATE_NOOP("KStandardActions", "Show or hide menubar"), u"show-menu" },
|
||||
{ ShowToolbar, KStandardShortcut::ShowToolbar, u"options_show_toolbar", QT_TRANSLATE_NOOP("KStandardActions", "Show &Toolbar"), QT_TRANSLATE_NOOP("KStandardActions", "Show or hide toolbar"), {} },
|
||||
{ ShowStatusbar, KStandardShortcut::ShowStatusbar, u"options_show_statusbar", QT_TRANSLATE_NOOP("KStandardActions", "Show St&atusbar"), QT_TRANSLATE_NOOP("KStandardActions", "Show or hide statusbar"), {} },
|
||||
{ FullScreen, KStandardShortcut::FullScreen, u"fullscreen", QT_TRANSLATE_NOOP("KStandardActions", "F&ull Screen Mode"), {}, u"view-fullscreen" },
|
||||
{ KeyBindings, KStandardShortcut::KeyBindings, u"options_configure_keybinding", QT_TRANSLATE_NOOP("KStandardActions", "Configure Keyboard S&hortcuts…"), {}, u"configure-shortcuts" },
|
||||
{ Preferences, KStandardShortcut::Preferences, u"options_configure", QT_TRANSLATE_NOOP("KStandardActions", "&Configure %1…"), {}, u"configure" },
|
||||
{ ConfigureToolbars, KStandardShortcut::ConfigureToolbars, u"options_configure_toolbars", QT_TRANSLATE_NOOP("KStandardActions", "Configure Tool&bars…"), {}, u"configure-toolbars" },
|
||||
{ ConfigureNotifications, KStandardShortcut::ConfigureNotifications, u"options_configure_notifications", QT_TRANSLATE_NOOP("KStandardActions", "Configure &Notifications…"), {}, u"preferences-desktop-notification" },
|
||||
|
||||
// the idea here is that Contents is used in menus, and Help in dialogs, so both share the same
|
||||
// shortcut
|
||||
{ HelpContents, KStandardShortcut::Help, u"help_contents", QT_TRANSLATE_NOOP("KStandardActions", "%1 &Handbook"), {}, u"help-contents" },
|
||||
{ WhatsThis, KStandardShortcut::WhatsThis, u"help_whats_this", QT_TRANSLATE_NOOP("KStandardActions", "What's &This?"), {}, u"help-contextual" },
|
||||
{ ReportBug, KStandardShortcut::ReportBug, u"help_report_bug", QT_TRANSLATE_NOOP("KStandardActions", "&Report Bug…"), {}, u"tools-report-bug" },
|
||||
{ SwitchApplicationLanguage, KStandardShortcut::SwitchApplicationLanguage, u"switch_application_language", QT_TRANSLATE_NOOP("KStandardActions", "Configure &Language…"), {}, u"preferences-desktop-locale" },
|
||||
{ AboutApp, KStandardShortcut::AccelNone, u"help_about_app", QT_TRANSLATE_NOOP("KStandardActions", "&About %1"), {}, nullptr },
|
||||
{ AboutKDE, KStandardShortcut::AccelNone, u"help_about_kde", QT_TRANSLATE_NOOP("KStandardActions", "About &KDE"), {}, u"kde" },
|
||||
{ DeleteFile, KStandardShortcut::DeleteFile, u"deletefile", QT_TRANSLATE_NOOP("KStandardActions", "&Delete"), {}, u"edit-delete" },
|
||||
{ RenameFile, KStandardShortcut::RenameFile, u"renamefile", QT_TRANSLATE_NOOP("KStandardActions", "&Rename…"), {}, u"edit-rename" },
|
||||
{ MoveToTrash, KStandardShortcut::MoveToTrash, u"movetotrash", QT_TRANSLATE_NOOP("KStandardActions", "&Move to Trash"), {}, u"trash-empty" },
|
||||
{ Donate, KStandardShortcut::Donate, u"help_donate", QT_TRANSLATE_NOOP("KStandardActions", "&Donate"), {}, u"help-donate"},
|
||||
{ HamburgerMenu, KStandardShortcut::OpenMainMenu, u"hamburger_menu", QT_TRANSLATE_NOOP("KStandardActions", "Open &Menu"), {}, u"application-menu" },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
inline const KStandardActionsInfo *infoPtr(StandardAction id)
|
||||
{
|
||||
for (const auto &action : g_rgActionInfo) {
|
||||
if (action.id == id) {
|
||||
return &action;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static inline QStringList internal_stdNames()
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
for (const auto &action : g_rgActionInfo) {
|
||||
if (!QCoreApplication::translate("KStandardActions", action.psLabel).isEmpty()) {
|
||||
if (QByteArrayView(action.psLabel).contains("%1"))
|
||||
// Prevents KLocalizedString::toString() from complaining about unsubstituted placeholder.
|
||||
{
|
||||
result.append(QCoreApplication::translate("KStandardActions", action.psLabel).arg(QString()));
|
||||
} else {
|
||||
result.append(QCoreApplication::translate("KStandardActions", action.psLabel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997 Stefan Taferner <taferner@alpin.or.at>
|
||||
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kstandardshortcut.h"
|
||||
#include "kstandardshortcutwatcher.h"
|
||||
|
||||
#include "kconfig.h"
|
||||
#include "kconfigwatcher.h"
|
||||
#include "ksharedconfig.h"
|
||||
#include <kconfiggroup.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QKeySequence>
|
||||
|
||||
namespace KStandardShortcut
|
||||
{
|
||||
struct KStandardShortcutInfo {
|
||||
//! The standard shortcut id. @see StandardShortcut
|
||||
StandardShortcut id;
|
||||
|
||||
/**
|
||||
* Unique name for the given accel. The name is used to save the user
|
||||
* settings. It's not representable. Use description for that.
|
||||
* @warning NEVER EVER CHANGE IT OR TRANSLATE IT!
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
//! Localized label for user-visible display, including translation context.
|
||||
struct {
|
||||
const char *text;
|
||||
const char *context;
|
||||
} description;
|
||||
|
||||
//! The keys for this shortcut
|
||||
int cutDefault, cutDefault2;
|
||||
|
||||
//! A shortcut that is created with @a cutDefault and @cutDefault2
|
||||
QList<QKeySequence> cut;
|
||||
|
||||
//! If this struct is initialized. If not initialized @cut is not valid
|
||||
bool isInitialized;
|
||||
|
||||
// Category of this Shortcut
|
||||
Category category;
|
||||
};
|
||||
|
||||
#define CTRL(x) QKeyCombination(Qt::CTRL | Qt::Key_##x).toCombined()
|
||||
#define SHIFT(x) QKeyCombination(Qt::SHIFT | Qt::Key_##x).toCombined()
|
||||
#define CTRLALT(x) QKeyCombination(Qt::CTRL | Qt::ALT | Qt::Key_##x).toCombined()
|
||||
#define CTRLSHIFT(x) QKeyCombination(Qt::CTRL | Qt::SHIFT | Qt::Key_##x).toCombined()
|
||||
#define ALT(x) QKeyCombination(Qt::ALT | Qt::Key_##x).toCombined()
|
||||
#define ALTSHIFT(x) QKeyCombination(Qt::ALT | Qt::SHIFT | Qt::Key_##x).toCombined()
|
||||
#define CTRLMETA(x) QKeyCombination(Qt::CTRL | Qt::META | Qt::Key_##x).toCombined()
|
||||
|
||||
/** Array of predefined KStandardShortcutInfo objects, which cover all
|
||||
the "standard" accelerators. Each enum value from StandardShortcut
|
||||
should appear in this table.
|
||||
*/
|
||||
// STUFF WILL BREAK IF YOU DON'T READ THIS!!!
|
||||
// Read the comments of the big enum in kstandardshortcut.h before you change anything!
|
||||
static KStandardShortcutInfo g_infoStandardShortcut[] = {
|
||||
// Group File,
|
||||
{AccelNone, nullptr, {nullptr, nullptr}, 0, 0, QList<QKeySequence>(), false, Category::InvalidCategory},
|
||||
{Open, "Open", QT_TRANSLATE_NOOP3("KStandardShortcut", "Open", "@action"), CTRL(O), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{New, "New", QT_TRANSLATE_NOOP3("KStandardShortcut", "New", "@action"), CTRL(N), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{Close, "Close", QT_TRANSLATE_NOOP3("KStandardShortcut", "Close", "@action"), CTRL(W), CTRL(Escape), QList<QKeySequence>(), false, Category::File},
|
||||
{Save, "Save", QT_TRANSLATE_NOOP3("KStandardShortcut", "Save", "@action"), CTRL(S), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{Print, "Print", QT_TRANSLATE_NOOP3("KStandardShortcut", "Print", "@action"), CTRL(P), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{Quit, "Quit", QT_TRANSLATE_NOOP3("KStandardShortcut", "Quit", "@action"), CTRL(Q), 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
|
||||
// Group Edit
|
||||
{Undo, "Undo", QT_TRANSLATE_NOOP3("KStandardShortcut", "Undo", "@action"), CTRL(Z), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{Redo, "Redo", QT_TRANSLATE_NOOP3("KStandardShortcut", "Redo", "@action"), CTRLSHIFT(Z), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
// Both "Cut" and "Delete" use Shift+Delete, but this is okay; see
|
||||
// https://commits.kde.org/kxmlgui/8eabbf6725386e716b7536c71a9181dfe5d959f0
|
||||
{Cut, "Cut", QT_TRANSLATE_NOOP3("KStandardShortcut", "Cut", "@action"), CTRL(X), SHIFT(Delete), QList<QKeySequence>(), false, Category::Edit},
|
||||
{Copy, "Copy", QT_TRANSLATE_NOOP3("KStandardShortcut", "Copy", "@action"), CTRL(C), CTRL(Insert), QList<QKeySequence>(), false, Category::Edit},
|
||||
{Paste, "Paste", QT_TRANSLATE_NOOP3("KStandardShortcut", "Paste", "@action"), CTRL(V), SHIFT(Insert), QList<QKeySequence>(), false, Category::Edit},
|
||||
{PasteSelection,
|
||||
"Paste Selection",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Paste Selection", "@action"),
|
||||
CTRLSHIFT(Insert),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
|
||||
{SelectAll, "SelectAll", QT_TRANSLATE_NOOP3("KStandardShortcut", "Select All", "@action"), CTRL(A), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{Deselect, "Deselect", QT_TRANSLATE_NOOP3("KStandardShortcut", "Deselect", "@action"), CTRLSHIFT(A), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{DeleteWordBack,
|
||||
"DeleteWordBack",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Delete Word Backwards", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
ALT(Backspace),
|
||||
#else
|
||||
CTRL(Backspace),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
{DeleteWordForward,
|
||||
"DeleteWordForward",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Delete Word Forward", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
ALT(Delete),
|
||||
#else
|
||||
CTRL(Delete),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
|
||||
{Find, "Find", QT_TRANSLATE_NOOP3("KStandardShortcut", "Find", "@action"), CTRL(F), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{FindNext, "FindNext", QT_TRANSLATE_NOOP3("KStandardShortcut", "Find Next", "@action"), Qt::Key_F3, 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{FindPrev, "FindPrev", QT_TRANSLATE_NOOP3("KStandardShortcut", "Find Prev", "@action"), SHIFT(F3), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{Replace, "Replace", QT_TRANSLATE_NOOP3("KStandardShortcut", "Replace", "@action"), CTRL(R), 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
|
||||
// Group Navigation
|
||||
{Home,
|
||||
"Home",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Home", "@action Go to main page"),
|
||||
ALT(Home),
|
||||
Qt::Key_HomePage,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{Begin,
|
||||
"Begin",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Begin", "@action Beginning of document"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
CTRL(Up),
|
||||
#else
|
||||
CTRL(Home),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{End,
|
||||
"End",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "End", "@action End of document"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
CTRL(Down),
|
||||
#else
|
||||
CTRL(End),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{Prior, "Prior", QT_TRANSLATE_NOOP3("KStandardShortcut", "Prior", "@action"), Qt::Key_PageUp, 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{Next,
|
||||
"Next",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Next", "@action Opposite to Prior"),
|
||||
Qt::Key_PageDown,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
|
||||
{Up, "Up", QT_TRANSLATE_NOOP3("KStandardShortcut", "Up", "@action"), ALT(Up), 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{Back, "Back", QT_TRANSLATE_NOOP3("KStandardShortcut", "Back", "@action"), ALT(Left), Qt::Key_Back, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{Forward,
|
||||
"Forward",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Forward", "@action"),
|
||||
ALT(Right),
|
||||
Qt::Key_Forward,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{Reload,
|
||||
"Reload",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Reload", "@action"),
|
||||
Qt::Key_F5,
|
||||
Qt::Key_Refresh,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
|
||||
{BeginningOfLine,
|
||||
"BeginningOfLine",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Beginning of Line", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
CTRL(Left),
|
||||
#else
|
||||
Qt::Key_Home,
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{EndOfLine,
|
||||
"EndOfLine",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "End of Line", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
CTRL(Right),
|
||||
#else
|
||||
Qt::Key_End,
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{GotoLine, "GotoLine", QT_TRANSLATE_NOOP3("KStandardShortcut", "Go to Line", "@action"), CTRL(G), 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{BackwardWord,
|
||||
"BackwardWord",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Backward Word", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
ALT(Left),
|
||||
#else
|
||||
CTRL(Left),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{ForwardWord,
|
||||
"ForwardWord",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Forward Word", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
ALT(Right),
|
||||
#else
|
||||
CTRL(Right),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
|
||||
{AddBookmark,
|
||||
"AddBookmark",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Add Bookmark", "@action"),
|
||||
CTRL(B),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{ZoomIn, "ZoomIn", QT_TRANSLATE_NOOP3("KStandardShortcut", "Zoom In", "@action"), CTRL(Plus), CTRL(Equal), QList<QKeySequence>(), false, Category::View},
|
||||
{ZoomOut, "ZoomOut", QT_TRANSLATE_NOOP3("KStandardShortcut", "Zoom Out", "@action"), CTRL(Minus), 0, QList<QKeySequence>(), false, Category::View},
|
||||
{FullScreen,
|
||||
"FullScreen",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Full Screen Mode", "@action"),
|
||||
#if defined(Q_OS_MACOS)
|
||||
CTRLMETA(F),
|
||||
#else
|
||||
CTRLSHIFT(F),
|
||||
#endif
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::View},
|
||||
|
||||
{ShowMenubar, "ShowMenubar", QT_TRANSLATE_NOOP3("KStandardShortcut", "Show Menu Bar", "@action"), CTRL(M), 0, QList<QKeySequence>(), false, Category::View},
|
||||
{TabNext,
|
||||
"Activate Next Tab",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Activate Next Tab", "@action"),
|
||||
CTRL(PageDown),
|
||||
CTRL(BracketRight),
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{TabPrev,
|
||||
"Activate Previous Tab",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Activate Previous Tab", "@action"),
|
||||
CTRL(PageUp),
|
||||
CTRL(BracketLeft),
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
|
||||
// Group Help
|
||||
{Help, "Help", QT_TRANSLATE_NOOP3("KStandardShortcut", "Help", "@action"), Qt::Key_F1, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
{WhatsThis, "WhatsThis", QT_TRANSLATE_NOOP3("KStandardShortcut", "What's This", "@action"), SHIFT(F1), 0, QList<QKeySequence>(), false, Category::Help},
|
||||
|
||||
// Group TextCompletion
|
||||
{TextCompletion,
|
||||
"TextCompletion",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Text Completion", "@action"),
|
||||
CTRL(E),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
{PrevCompletion,
|
||||
"PrevCompletion",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Previous Completion Match", "@action"),
|
||||
CTRL(Up),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
{NextCompletion,
|
||||
"NextCompletion",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Next Completion Match", "@action"),
|
||||
CTRL(Down),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
{SubstringCompletion,
|
||||
"SubstringCompletion",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Substring Completion", "@action"),
|
||||
CTRL(T),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Edit},
|
||||
|
||||
{RotateUp,
|
||||
"RotateUp",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Previous Item in List", "@action"),
|
||||
Qt::Key_Up,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{RotateDown,
|
||||
"RotateDown",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Next Item in List", "@action"),
|
||||
Qt::Key_Down,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
|
||||
{OpenRecent, "OpenRecent", QT_TRANSLATE_NOOP3("KStandardShortcut", "Open Recent", "@action"), 0, 0, QList<QKeySequence>(), false, Category::File},
|
||||
{SaveAs, "SaveAs", QT_TRANSLATE_NOOP3("KStandardShortcut", "Save As", "@action"), CTRLSHIFT(S), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{Revert, "Revert", QT_TRANSLATE_NOOP3("KStandardShortcut", "Revert", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{PrintPreview, "PrintPreview", QT_TRANSLATE_NOOP3("KStandardShortcut", "Print Preview", "@action"), 0, 0, QList<QKeySequence>(), false, Category::File},
|
||||
{Mail, "Mail", QT_TRANSLATE_NOOP3("KStandardShortcut", "Mail", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
{Clear, "Clear", QT_TRANSLATE_NOOP3("KStandardShortcut", "Clear", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{ActualSize,
|
||||
"ActualSize",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Zoom to Actual Size", "@action"),
|
||||
CTRL(0),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::View},
|
||||
{FitToPage, "FitToPage", QT_TRANSLATE_NOOP3("KStandardShortcut", "Fit To Page", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{FitToWidth, "FitToWidth", QT_TRANSLATE_NOOP3("KStandardShortcut", "Fit To Width", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{FitToHeight, "FitToHeight", QT_TRANSLATE_NOOP3("KStandardShortcut", "Fit To Height", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{Zoom, "Zoom", QT_TRANSLATE_NOOP3("KStandardShortcut", "Zoom", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{Goto, "Goto", QT_TRANSLATE_NOOP3("KStandardShortcut", "Goto", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{GotoPage, "GotoPage", QT_TRANSLATE_NOOP3("KStandardShortcut", "Goto Page", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Navigation},
|
||||
{DocumentBack,
|
||||
"DocumentBack",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Document Back", "@action"),
|
||||
ALTSHIFT(Left),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{DocumentForward,
|
||||
"DocumentForward",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Document Forward", "@action"),
|
||||
ALTSHIFT(Right),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{EditBookmarks,
|
||||
"EditBookmarks",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Edit Bookmarks", "@action"),
|
||||
0,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Navigation},
|
||||
{Spelling, "Spelling", QT_TRANSLATE_NOOP3("KStandardShortcut", "Spelling", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Edit},
|
||||
{ShowToolbar, "ShowToolbar", QT_TRANSLATE_NOOP3("KStandardShortcut", "Show Toolbar", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{ShowStatusbar, "ShowStatusbar", QT_TRANSLATE_NOOP3("KStandardShortcut", "Show Statusbar", "@action"), 0, 0, QList<QKeySequence>(), false, Category::View},
|
||||
{KeyBindings,
|
||||
"KeyBindings",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Key Bindings", "@action"),
|
||||
CTRLALT(Comma),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Settings},
|
||||
{Preferences,
|
||||
"Preferences",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Configure Application", "@action"),
|
||||
CTRLSHIFT(Comma),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Settings},
|
||||
{ConfigureToolbars,
|
||||
"ConfigureToolbars",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Configure Toolbars", "@action"),
|
||||
0,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Settings},
|
||||
{ConfigureNotifications,
|
||||
"ConfigureNotifications",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Configure Notifications", "@action"),
|
||||
0,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Settings},
|
||||
{ReportBug, "ReportBug", QT_TRANSLATE_NOOP3("KStandardShortcut", "Report Bug", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
{SwitchApplicationLanguage,
|
||||
"SwitchApplicationLanguage",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Configure Language…", "@action"),
|
||||
0,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::Settings},
|
||||
{AboutApp, "AboutApp", QT_TRANSLATE_NOOP3("KStandardShortcut", "About Application", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
{AboutKDE, "AboutKDE", QT_TRANSLATE_NOOP3("KStandardShortcut", "About KDE", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
// Both "Cut" and "Delete" use Shift+Delete, but this is okay; see
|
||||
// https://commits.kde.org/kxmlgui/8eabbf6725386e716b7536c71a9181dfe5d959f0
|
||||
{DeleteFile, "DeleteFile", QT_TRANSLATE_NOOP3("KStandardShortcut", "Delete", "@action"), SHIFT(Delete), 0, QList<QKeySequence>(), false, Category::File},
|
||||
{RenameFile, "RenameFile", QT_TRANSLATE_NOOP3("KStandardShortcut", "Rename", "@action"), Qt::Key_F2, 0, QList<QKeySequence>(), false, Category::File},
|
||||
{MoveToTrash,
|
||||
"MoveToTrash",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Move to Trash", "@action"),
|
||||
Qt::Key_Delete,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::File},
|
||||
{Donate, "Donate", QT_TRANSLATE_NOOP3("KStandardShortcut", "Donate", "@action"), 0, 0, QList<QKeySequence>(), false, Category::Help},
|
||||
{ShowHideHiddenFiles,
|
||||
"ShowHideHiddenFiles",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Show/Hide Hidden Files", "@action"),
|
||||
CTRL(H),
|
||||
ALT(Period),
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::View},
|
||||
{CreateFolder,
|
||||
"CreateFolder",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Create Folder", "@action"),
|
||||
CTRLSHIFT(N),
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::File},
|
||||
{OpenMainMenu,
|
||||
"OpenMainMenu",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Open Main Menu", "@action referring to the menu bar or a hamburger menu"),
|
||||
Qt::Key_F10,
|
||||
0,
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::View},
|
||||
{OpenContextMenu,
|
||||
"OpenContextMenu",
|
||||
QT_TRANSLATE_NOOP3("KStandardShortcut", "Open Context Menu", "@action"),
|
||||
Qt::Key_Menu,
|
||||
SHIFT(F10),
|
||||
QList<QKeySequence>(),
|
||||
false,
|
||||
Category::View},
|
||||
|
||||
// dummy entry to catch simple off-by-one errors. Insert new entries before this line.
|
||||
{AccelNone, nullptr, {nullptr, nullptr}, 0, 0, QList<QKeySequence>(), false, Category::InvalidCategory}};
|
||||
|
||||
/** Search for the KStandardShortcutInfo object associated with the given @p id.
|
||||
Return a dummy entry with no name and an empty shortcut if @p id is invalid.
|
||||
*/
|
||||
static KStandardShortcutInfo *guardedStandardShortcutInfo(StandardShortcut id)
|
||||
{
|
||||
if (id >= static_cast<int>(sizeof(g_infoStandardShortcut) / sizeof(KStandardShortcutInfo)) || id < 0) {
|
||||
qWarning() << "KStandardShortcut: id not found!";
|
||||
return &g_infoStandardShortcut[AccelNone];
|
||||
} else {
|
||||
return &g_infoStandardShortcut[id];
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize the list for duplicates. For some reason some
|
||||
// people have kdeglobals entries like
|
||||
// Close=Ctrl+W; Ctrl+Esc; Ctrl+W; Ctrl+Esc;
|
||||
// having the same shortcut more than once in the shortcut
|
||||
// declaration is clearly bogus so fix it
|
||||
static void sanitizeShortcutList(QList<QKeySequence> *list)
|
||||
{
|
||||
for (int i = 0; i < list->size(); ++i) {
|
||||
const QKeySequence &ks = list->at(i);
|
||||
int other = list->indexOf(ks, i + 1);
|
||||
while (other != -1) {
|
||||
list->removeAt(other);
|
||||
other = list->indexOf(ks, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize the accelerator @p id by checking if it is overridden
|
||||
in the configuration file (and if it isn't, use the default).
|
||||
On X11, if QApplication was initialized with GUI disabled,
|
||||
the default will always be used.
|
||||
*/
|
||||
void initialize(StandardShortcut id)
|
||||
{
|
||||
KStandardShortcutInfo *info = guardedStandardShortcutInfo(id);
|
||||
|
||||
// All three are needed.
|
||||
if (info->id != AccelNone) {
|
||||
Q_ASSERT(info->description.text);
|
||||
Q_ASSERT(info->description.context);
|
||||
Q_ASSERT(info->name);
|
||||
}
|
||||
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Shortcuts"));
|
||||
|
||||
if (cg.hasKey(info->name)) {
|
||||
QString s = cg.readEntry(info->name);
|
||||
if (s != QLatin1String("none")) {
|
||||
info->cut = QKeySequence::listFromString(s);
|
||||
sanitizeShortcutList(&info->cut);
|
||||
} else {
|
||||
info->cut = QList<QKeySequence>();
|
||||
}
|
||||
} else {
|
||||
info->cut = hardcodedDefaultShortcut(id);
|
||||
}
|
||||
|
||||
info->isInitialized = true;
|
||||
}
|
||||
|
||||
void saveShortcut(StandardShortcut id, const QList<QKeySequence> &newShortcut)
|
||||
{
|
||||
KStandardShortcutInfo *info = guardedStandardShortcutInfo(id);
|
||||
// If the action has no standard shortcut associated there is nothing to
|
||||
// save
|
||||
if (info->id == AccelNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("Shortcuts"));
|
||||
|
||||
info->cut = newShortcut;
|
||||
bool sameAsDefault = (newShortcut == hardcodedDefaultShortcut(id));
|
||||
|
||||
if (sameAsDefault) {
|
||||
// If the shortcut is the equal to the hardcoded one we remove it from
|
||||
// kdeglobal if necessary and return.
|
||||
if (cg.hasKey(info->name)) {
|
||||
cg.deleteEntry(info->name, KConfig::Global | KConfig::Persistent | KConfig::Notify);
|
||||
cg.sync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the changed shortcut to kdeglobals
|
||||
sanitizeShortcutList(&info->cut);
|
||||
cg.writeEntry(info->name, QKeySequence::listToString(info->cut), KConfig::Global | KConfig::Persistent | KConfig::Notify);
|
||||
cg.sync();
|
||||
}
|
||||
|
||||
QString name(StandardShortcut id)
|
||||
{
|
||||
return QString::fromLatin1(guardedStandardShortcutInfo(id)->name);
|
||||
}
|
||||
|
||||
QString label(StandardShortcut id)
|
||||
{
|
||||
KStandardShortcutInfo *info = guardedStandardShortcutInfo(id);
|
||||
return QCoreApplication::translate("KStandardShortcut", info->description.text, info->description.context);
|
||||
}
|
||||
|
||||
// TODO: Add psWhatsThis entry to KStandardShortcutInfo
|
||||
QString whatsThis(StandardShortcut /*id*/)
|
||||
{
|
||||
// KStandardShortcutInfo* info = guardedStandardShortcutInfo( id );
|
||||
// if( info && info->whatsThis )
|
||||
// return i18n(info->whatsThis);
|
||||
// else
|
||||
return QString();
|
||||
}
|
||||
|
||||
const QList<QKeySequence> &shortcut(StandardShortcut id)
|
||||
{
|
||||
KStandardShortcutInfo *info = guardedStandardShortcutInfo(id);
|
||||
|
||||
if (!info->isInitialized) {
|
||||
initialize(id);
|
||||
}
|
||||
|
||||
return info->cut;
|
||||
}
|
||||
|
||||
StandardShortcut find(const QKeySequence &seq)
|
||||
{
|
||||
if (!seq.isEmpty()) {
|
||||
for (const KStandardShortcutInfo &shortcutInfo : g_infoStandardShortcut) {
|
||||
const StandardShortcut id = shortcutInfo.id;
|
||||
if (id != AccelNone) {
|
||||
if (!shortcutInfo.isInitialized) {
|
||||
initialize(id);
|
||||
}
|
||||
if (shortcutInfo.cut.contains(seq)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return AccelNone;
|
||||
}
|
||||
|
||||
StandardShortcut findByName(const QString &name)
|
||||
{
|
||||
for (const KStandardShortcutInfo &shortcutInfo : g_infoStandardShortcut) {
|
||||
if (QLatin1StringView(shortcutInfo.name) == name) {
|
||||
return shortcutInfo.id;
|
||||
}
|
||||
}
|
||||
return AccelNone;
|
||||
}
|
||||
|
||||
QList<QKeySequence> hardcodedDefaultShortcut(StandardShortcut id)
|
||||
{
|
||||
QList<QKeySequence> cut;
|
||||
KStandardShortcutInfo *info = guardedStandardShortcutInfo(id);
|
||||
|
||||
if (info->cutDefault != 0) {
|
||||
cut << info->cutDefault;
|
||||
}
|
||||
|
||||
if (info->cutDefault2 != 0) {
|
||||
if (cut.isEmpty()) {
|
||||
cut << QKeySequence();
|
||||
}
|
||||
|
||||
cut << info->cutDefault2;
|
||||
}
|
||||
|
||||
return cut;
|
||||
}
|
||||
|
||||
Category category(StandardShortcut id)
|
||||
{
|
||||
return guardedStandardShortcutInfo(id)->category;
|
||||
}
|
||||
|
||||
const QList<QKeySequence> &open()
|
||||
{
|
||||
return shortcut(Open);
|
||||
}
|
||||
const QList<QKeySequence> &openNew()
|
||||
{
|
||||
return shortcut(New);
|
||||
}
|
||||
const QList<QKeySequence> &close()
|
||||
{
|
||||
return shortcut(Close);
|
||||
}
|
||||
const QList<QKeySequence> &save()
|
||||
{
|
||||
return shortcut(Save);
|
||||
}
|
||||
const QList<QKeySequence> &print()
|
||||
{
|
||||
return shortcut(Print);
|
||||
}
|
||||
const QList<QKeySequence> &quit()
|
||||
{
|
||||
return shortcut(Quit);
|
||||
}
|
||||
const QList<QKeySequence> &cut()
|
||||
{
|
||||
return shortcut(Cut);
|
||||
}
|
||||
const QList<QKeySequence> ©()
|
||||
{
|
||||
return shortcut(Copy);
|
||||
}
|
||||
const QList<QKeySequence> &paste()
|
||||
{
|
||||
return shortcut(Paste);
|
||||
}
|
||||
const QList<QKeySequence> &pasteSelection()
|
||||
{
|
||||
return shortcut(PasteSelection);
|
||||
}
|
||||
const QList<QKeySequence> &deleteWordBack()
|
||||
{
|
||||
return shortcut(DeleteWordBack);
|
||||
}
|
||||
const QList<QKeySequence> &deleteWordForward()
|
||||
{
|
||||
return shortcut(DeleteWordForward);
|
||||
}
|
||||
const QList<QKeySequence> &undo()
|
||||
{
|
||||
return shortcut(Undo);
|
||||
}
|
||||
const QList<QKeySequence> &redo()
|
||||
{
|
||||
return shortcut(Redo);
|
||||
}
|
||||
const QList<QKeySequence> &find()
|
||||
{
|
||||
return shortcut(Find);
|
||||
}
|
||||
const QList<QKeySequence> &findNext()
|
||||
{
|
||||
return shortcut(FindNext);
|
||||
}
|
||||
const QList<QKeySequence> &findPrev()
|
||||
{
|
||||
return shortcut(FindPrev);
|
||||
}
|
||||
const QList<QKeySequence> &replace()
|
||||
{
|
||||
return shortcut(Replace);
|
||||
}
|
||||
const QList<QKeySequence> &home()
|
||||
{
|
||||
return shortcut(Home);
|
||||
}
|
||||
const QList<QKeySequence> &begin()
|
||||
{
|
||||
return shortcut(Begin);
|
||||
}
|
||||
const QList<QKeySequence> &end()
|
||||
{
|
||||
return shortcut(End);
|
||||
}
|
||||
const QList<QKeySequence> &beginningOfLine()
|
||||
{
|
||||
return shortcut(BeginningOfLine);
|
||||
}
|
||||
const QList<QKeySequence> &endOfLine()
|
||||
{
|
||||
return shortcut(EndOfLine);
|
||||
}
|
||||
const QList<QKeySequence> &prior()
|
||||
{
|
||||
return shortcut(Prior);
|
||||
}
|
||||
const QList<QKeySequence> &next()
|
||||
{
|
||||
return shortcut(Next);
|
||||
}
|
||||
const QList<QKeySequence> &backwardWord()
|
||||
{
|
||||
return shortcut(BackwardWord);
|
||||
}
|
||||
const QList<QKeySequence> &forwardWord()
|
||||
{
|
||||
return shortcut(ForwardWord);
|
||||
}
|
||||
const QList<QKeySequence> &gotoLine()
|
||||
{
|
||||
return shortcut(GotoLine);
|
||||
}
|
||||
const QList<QKeySequence> &addBookmark()
|
||||
{
|
||||
return shortcut(AddBookmark);
|
||||
}
|
||||
const QList<QKeySequence> &tabNext()
|
||||
{
|
||||
return shortcut(TabNext);
|
||||
}
|
||||
const QList<QKeySequence> &tabPrev()
|
||||
{
|
||||
return shortcut(TabPrev);
|
||||
}
|
||||
const QList<QKeySequence> &fullScreen()
|
||||
{
|
||||
return shortcut(FullScreen);
|
||||
}
|
||||
const QList<QKeySequence> &zoomIn()
|
||||
{
|
||||
return shortcut(ZoomIn);
|
||||
}
|
||||
const QList<QKeySequence> &zoomOut()
|
||||
{
|
||||
return shortcut(ZoomOut);
|
||||
}
|
||||
const QList<QKeySequence> &help()
|
||||
{
|
||||
return shortcut(Help);
|
||||
}
|
||||
const QList<QKeySequence> &completion()
|
||||
{
|
||||
return shortcut(TextCompletion);
|
||||
}
|
||||
const QList<QKeySequence> &prevCompletion()
|
||||
{
|
||||
return shortcut(PrevCompletion);
|
||||
}
|
||||
const QList<QKeySequence> &nextCompletion()
|
||||
{
|
||||
return shortcut(NextCompletion);
|
||||
}
|
||||
const QList<QKeySequence> &rotateUp()
|
||||
{
|
||||
return shortcut(RotateUp);
|
||||
}
|
||||
const QList<QKeySequence> &rotateDown()
|
||||
{
|
||||
return shortcut(RotateDown);
|
||||
}
|
||||
const QList<QKeySequence> &substringCompletion()
|
||||
{
|
||||
return shortcut(SubstringCompletion);
|
||||
}
|
||||
const QList<QKeySequence> &whatsThis()
|
||||
{
|
||||
return shortcut(WhatsThis);
|
||||
}
|
||||
const QList<QKeySequence> &reload()
|
||||
{
|
||||
return shortcut(Reload);
|
||||
}
|
||||
const QList<QKeySequence> &selectAll()
|
||||
{
|
||||
return shortcut(SelectAll);
|
||||
}
|
||||
const QList<QKeySequence> &up()
|
||||
{
|
||||
return shortcut(Up);
|
||||
}
|
||||
const QList<QKeySequence> &back()
|
||||
{
|
||||
return shortcut(Back);
|
||||
}
|
||||
const QList<QKeySequence> &forward()
|
||||
{
|
||||
return shortcut(Forward);
|
||||
}
|
||||
const QList<QKeySequence> &showMenubar()
|
||||
{
|
||||
return shortcut(ShowMenubar);
|
||||
}
|
||||
const QList<QKeySequence> &deleteFile()
|
||||
{
|
||||
return shortcut(DeleteFile);
|
||||
}
|
||||
const QList<QKeySequence> &renameFile()
|
||||
{
|
||||
return shortcut(RenameFile);
|
||||
}
|
||||
const QList<QKeySequence> &createFolder()
|
||||
{
|
||||
return shortcut(CreateFolder);
|
||||
}
|
||||
const QList<QKeySequence> &moveToTrash()
|
||||
{
|
||||
return shortcut(MoveToTrash);
|
||||
}
|
||||
const QList<QKeySequence> &preferences()
|
||||
{
|
||||
return shortcut(Preferences);
|
||||
}
|
||||
const QList<QKeySequence> &showHideHiddenFiles()
|
||||
{
|
||||
return shortcut(ShowHideHiddenFiles);
|
||||
}
|
||||
const QList<QKeySequence> &openMainMenu()
|
||||
{
|
||||
return shortcut(OpenMainMenu);
|
||||
}
|
||||
const QList<QKeySequence> &openContextMenu()
|
||||
{
|
||||
return shortcut(OpenContextMenu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997 Stefan Taferner <taferner@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <hadacek@kde.org>
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KSTANDARDSHORTCUT_H
|
||||
#define KSTANDARDSHORTCUT_H
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QString>
|
||||
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
/**
|
||||
* \namespace KStandardShortcut
|
||||
* Convenient methods for access to the common accelerator keys in
|
||||
* the key configuration. These are the standard keybindings that should
|
||||
* be used in all KDE applications. They will be configurable,
|
||||
* so do not hardcode the default behavior.
|
||||
*/
|
||||
namespace KStandardShortcut
|
||||
{
|
||||
// STUFF WILL BREAK IF YOU DON'T READ THIS!!!
|
||||
/*
|
||||
*Always add new std-accels to the end of this enum, never in the middle!
|
||||
*Don't forget to add the corresponding entries in g_infoStandardShortcut[] in kstandardshortcut.cpp, too.
|
||||
*Values of elements here and positions of the corresponding entries in
|
||||
*the big array g_infoStandardShortcut[] ABSOLUTELY MUST BE THE SAME.
|
||||
* !!! !!!! !!!!! !!!!
|
||||
* !!!! !!! !!!! !!!!
|
||||
*
|
||||
* Other Rules:
|
||||
*
|
||||
* - Never change the name of an existing shortcut
|
||||
* - Never translate the name of a shortcut
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the identifier of all standard accelerators.
|
||||
*/
|
||||
enum StandardShortcut {
|
||||
// C++ requires that the value of an enum symbol be one more than the previous one.
|
||||
// This means that everything will be well-ordered from here on.
|
||||
AccelNone = 0,
|
||||
// File menu
|
||||
Open, ///< Open file.
|
||||
New, ///< Create a new document.
|
||||
Close, ///< Close current document.
|
||||
Save, ///< Save current document.
|
||||
// The Print item
|
||||
Print, ///< Print current document.
|
||||
Quit, ///< Quit the program.
|
||||
// Edit menu
|
||||
Undo, ///< Undo last operation.
|
||||
Redo, ///< Redo last operation.
|
||||
Cut, ///< Cut selected area and store it in the clipboard.
|
||||
Copy, ///< Copy selected area into the clipboard.
|
||||
Paste, ///< Paste contents of clipboard at mouse/cursor position.
|
||||
PasteSelection, ///< Paste the selection at mouse/cursor position.
|
||||
SelectAll, ///< Select all.
|
||||
Deselect, ///< Deselect any selected elements.
|
||||
DeleteWordBack, ///< Delete a word back from mouse/cursor position.
|
||||
DeleteWordForward, ///< Delete a word forward from mouse/cursor position.
|
||||
Find, ///< Initiate a 'find' request in the current document.
|
||||
FindNext, ///< Find the next instance of a stored 'find'.
|
||||
FindPrev, ///< Find a previous instance of a stored 'find'.
|
||||
Replace, ///< Find and replace matches.
|
||||
// Navigation
|
||||
Home, ///< Go to home page.
|
||||
Begin, ///< Go to beginning of the document.
|
||||
End, ///< Go to end of the document.
|
||||
Prior, ///< Scroll up one page.
|
||||
Next, ///< Scroll down one page.
|
||||
Up, ///< Up.
|
||||
Back, ///< Back.
|
||||
Forward, ///< Forward.
|
||||
Reload, ///< Reload.
|
||||
// Text Navigation
|
||||
BeginningOfLine, ///< Go to beginning of current line.
|
||||
EndOfLine, ///< Go to end of current line.
|
||||
GotoLine, ///< Go to line.
|
||||
BackwardWord, ///< BackwardWord.
|
||||
ForwardWord, ///< ForwardWord.
|
||||
// View parameters
|
||||
AddBookmark, ///< Add current page to bookmarks.
|
||||
ZoomIn, ///< Zoom in.
|
||||
ZoomOut, ///< Zoom out.
|
||||
FullScreen, ///< Full Screen mode.
|
||||
ShowMenubar, ///< Show Menu Bar.
|
||||
// Tabular navigation
|
||||
TabNext, ///< Next Tab.
|
||||
TabPrev, ///< Previous Tab.
|
||||
// Help menu
|
||||
Help, ///< Help the user in the current situation.
|
||||
WhatsThis, ///< What's This button.
|
||||
// Text completion
|
||||
TextCompletion, ///< Complete text in input widgets.
|
||||
PrevCompletion, ///< Iterate through a list when completion returns multiple items.
|
||||
NextCompletion, ///< Iterate through a list when completion returns multiple items.
|
||||
SubstringCompletion, ///< Find a string within another string or list of strings.
|
||||
RotateUp, ///< Help users iterate through a list of entries.
|
||||
RotateDown, ///< Help users iterate through a list of entries.
|
||||
OpenRecent, ///< Open a recently used document.
|
||||
SaveAs, ///< Save the current document under a different name.
|
||||
Revert, ///< Revert the current document to the last saved version.
|
||||
PrintPreview, ///< Show a print preview of the current document.
|
||||
Mail, ///< Send the current document by mail.
|
||||
Clear, ///< Clear the content of the focus widget.
|
||||
ActualSize, ///< View the document at its actual size.
|
||||
FitToPage, ///< Fit the document view to the size of the current window.
|
||||
FitToWidth, ///< Fit the document view to the width of the current window.
|
||||
FitToHeight, ///< Fit the document view to the height of the current window.
|
||||
Zoom, ///< Select the current zoom level.
|
||||
Goto, ///< Jump to some specific location in the document.
|
||||
GotoPage, ///< Go to a specific page.
|
||||
DocumentBack, ///< Move back (document style menu).
|
||||
DocumentForward, ///< Move forward (document style menu).
|
||||
EditBookmarks, ///< Edit the application bookmarks.
|
||||
Spelling, ///< Pop up the spell checker.
|
||||
ShowToolbar, ///< Show/Hide the toolbar.
|
||||
ShowStatusbar, ///< Show/Hide the statusbar.
|
||||
KeyBindings, ///< Display the configure key bindings dialog.
|
||||
Preferences, ///< Display the preferences/options dialog.
|
||||
ConfigureToolbars, ///< Display the toolbar configuration dialog.
|
||||
ConfigureNotifications, ///< Display the notifications configuration dialog.
|
||||
ReportBug, ///< Display the Report Bug dialog.
|
||||
SwitchApplicationLanguage, ///< Display the Switch Application Language dialog.
|
||||
AboutApp, ///< Display the application's About dialog.
|
||||
AboutKDE, ///< Display the About KDE dialog.
|
||||
DeleteFile, ///< Permanently delete files or folders. @since 5.25
|
||||
RenameFile, ///< Rename files or folders. @since 5.25
|
||||
MoveToTrash, ///< Move files or folders to the trash. @since 5.25
|
||||
Donate, ///< Open donation page on kde.org. @since 5.26
|
||||
ShowHideHiddenFiles, ///< Toggle showing or hiding hidden files @since 5.70
|
||||
CreateFolder, ///< Create a folder. @since 5.74
|
||||
OpenMainMenu, ///< Open a main menu like the menu bar or a hamburger menu. Necessary for accessibility. @since 6.0
|
||||
OpenContextMenu, ///< Open a context menu for the object with keyboard focus. Necessary for accessibility. @since 6.0
|
||||
// Insert new items here!
|
||||
|
||||
StandardShortcutCount, // number of standard shortcuts
|
||||
};
|
||||
|
||||
/**
|
||||
* Categories in which the standard shortcuts can be classified
|
||||
* @since 5.74
|
||||
*/
|
||||
enum class Category {
|
||||
InvalidCategory = -1,
|
||||
File,
|
||||
Edit,
|
||||
Navigation,
|
||||
View,
|
||||
Settings,
|
||||
Help,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the keybinding for @p accel.
|
||||
* On X11, if QApplication was initialized with GUI disabled, the
|
||||
* default keybinding will always be returned.
|
||||
* @param id the id of the accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &shortcut(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Returns a unique name for the given accel.
|
||||
* @param id the id of the accelerator
|
||||
* @return the unique name of the accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QString name(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Returns a localized label for user-visible display.
|
||||
* @param id the id of the accelerator
|
||||
* @return a localized label for the accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QString label(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Returns an extended WhatsThis description for the given accelerator.
|
||||
* @param id the id of the accelerator
|
||||
* @return a localized description of the accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QString whatsThis(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Return the StandardShortcut id of the standard accel action which
|
||||
* uses this key sequence, or AccelNone if none of them do.
|
||||
* This is used by class KKeyChooser.
|
||||
* @param keySeq the key sequence to search
|
||||
* @return the id of the standard accelerator, or AccelNone if there
|
||||
* is none
|
||||
*/
|
||||
KCONFIGGUI_EXPORT StandardShortcut find(const QKeySequence &keySeq);
|
||||
|
||||
/**
|
||||
* Return the StandardShortcut id of the standard accelerator action which
|
||||
* has \p name as its name, or AccelNone if none of them do.
|
||||
* @param name the name as returned by name(StandardShortcut id)
|
||||
* @return the id of the standard accelerator with the given name or AccelNone
|
||||
* if there is no such accelerator
|
||||
* @since 5.71
|
||||
*/
|
||||
KCONFIGGUI_EXPORT StandardShortcut findByName(const QString &name);
|
||||
|
||||
/**
|
||||
* Returns the hardcoded default shortcut for @p id.
|
||||
* This does not take into account the user's configuration.
|
||||
* @param id the id of the accelerator
|
||||
* @return the default shortcut of the accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT QList<QKeySequence> hardcodedDefaultShortcut(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Saves the new shortcut \a cut for standard accel \a id.
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void saveShortcut(StandardShortcut id, const QList<QKeySequence> &newShortcut);
|
||||
|
||||
/**
|
||||
* Returns the appropriate category for the given StandardShortcut \p id.
|
||||
* @since 5.73
|
||||
*/
|
||||
KCONFIGGUI_EXPORT Category category(StandardShortcut id);
|
||||
|
||||
/**
|
||||
* Open file. Default: Ctrl-o
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &open();
|
||||
|
||||
/**
|
||||
* Create a new document (or whatever). Default: Ctrl-n
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &openNew();
|
||||
|
||||
/**
|
||||
* Close current document. Default: Ctrl-w
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &close();
|
||||
|
||||
/**
|
||||
* Save current document. Default: Ctrl-s
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &save();
|
||||
|
||||
/**
|
||||
* Print current document. Default: Ctrl-p
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &print();
|
||||
|
||||
/**
|
||||
* Quit the program. Default: Ctrl-q
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &quit();
|
||||
|
||||
/**
|
||||
* Undo last operation. Default: Ctrl-z
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &undo();
|
||||
|
||||
/**
|
||||
* Redo last operation. Default: Shift-Ctrl-z
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &redo();
|
||||
|
||||
/**
|
||||
* Cut selected area and store it in the clipboard. Default: Ctrl-x
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &cut();
|
||||
|
||||
/**
|
||||
* Copy selected area into the clipboard. Default: Ctrl-c
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> ©();
|
||||
|
||||
/**
|
||||
* Paste contents of clipboard at mouse/cursor position. Default: Ctrl-v
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &paste();
|
||||
|
||||
/**
|
||||
* Paste the selection at mouse/cursor position. Default: Ctrl-Shift-Insert
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &pasteSelection();
|
||||
|
||||
/**
|
||||
* Select all. Default: Ctrl-A
|
||||
* @return the shortcut of the standard accelerator
|
||||
**/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &selectAll();
|
||||
|
||||
/**
|
||||
* Delete a word back from mouse/cursor position. Default: Ctrl-Backspace
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &deleteWordBack();
|
||||
|
||||
/**
|
||||
* Delete a word forward from mouse/cursor position. Default: Ctrl-Delete
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &deleteWordForward();
|
||||
|
||||
/**
|
||||
* Initiate a 'find' request in the current document. Default: Ctrl-f
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &find();
|
||||
|
||||
/**
|
||||
* Find the next instance of a stored 'find' Default: F3
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &findNext();
|
||||
|
||||
/**
|
||||
* Find a previous instance of a stored 'find'. Default: Shift-F3
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &findPrev();
|
||||
|
||||
/**
|
||||
* Find and replace matches. Default: Ctrl-r
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &replace();
|
||||
|
||||
/**
|
||||
* Zoom in. Default: Ctrl-Plus
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &zoomIn();
|
||||
|
||||
/**
|
||||
* Zoom out. Default: Ctrl-Minus
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &zoomOut();
|
||||
|
||||
/**
|
||||
* Go to home page. Default: Alt-Home
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &home();
|
||||
|
||||
/**
|
||||
* Go to beginning of the document. Default: Ctrl-Home
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &begin();
|
||||
|
||||
/**
|
||||
* Go to end of the document. Default: Ctrl-End
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &end();
|
||||
|
||||
/**
|
||||
* Go to beginning of current line. Default: Home
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &beginningOfLine();
|
||||
|
||||
/**
|
||||
* Go to end of current line. Default: End
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &endOfLine();
|
||||
|
||||
/**
|
||||
* Scroll up one page. Default: Prior
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &prior();
|
||||
|
||||
/**
|
||||
* Scroll down one page. Default: Next
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &next();
|
||||
|
||||
/**
|
||||
* Go to line. Default: Ctrl+G
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &gotoLine();
|
||||
|
||||
/**
|
||||
* Add current page to bookmarks. Default: Ctrl+B
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &addBookmark();
|
||||
|
||||
/**
|
||||
* Next Tab. Default: Ctrl-<
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &tabNext();
|
||||
|
||||
/**
|
||||
* Previous Tab. Default: Ctrl->
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &tabPrev();
|
||||
|
||||
/**
|
||||
* Full Screen Mode. Default: Ctrl+Shift+F
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &fullScreen();
|
||||
|
||||
/**
|
||||
* Help the user in the current situation. Default: F1
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &help();
|
||||
|
||||
/**
|
||||
* Complete text in input widgets. Default Ctrl+E
|
||||
* @return the shortcut of the standard accelerator
|
||||
**/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &completion();
|
||||
|
||||
/**
|
||||
* Iterate through a list when completion returns
|
||||
* multiple items. Default: Ctrl+Up
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &prevCompletion();
|
||||
|
||||
/**
|
||||
* Iterate through a list when completion returns
|
||||
* multiple items. Default: Ctrl+Down
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &nextCompletion();
|
||||
|
||||
/**
|
||||
* Find a string within another string or list of strings.
|
||||
* Default: Ctrl-T
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &substringCompletion();
|
||||
|
||||
/**
|
||||
* Help users iterate through a list of entries. Default: Up
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &rotateUp();
|
||||
|
||||
/**
|
||||
* Help users iterate through a list of entries. Default: Down
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &rotateDown();
|
||||
|
||||
/**
|
||||
* What's This button. Default: Shift+F1
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &whatsThis();
|
||||
|
||||
/**
|
||||
* Reload. Default: F5
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &reload();
|
||||
|
||||
/**
|
||||
* Up. Default: Alt+Up
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &up();
|
||||
|
||||
/**
|
||||
* Back. Default: Alt+Left
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &back();
|
||||
|
||||
/**
|
||||
* Forward. Default: ALT+Right
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &forward();
|
||||
|
||||
/**
|
||||
* BackwardWord. Default: Ctrl+Left
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &backwardWord();
|
||||
|
||||
/**
|
||||
* ForwardWord. Default: Ctrl+Right
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &forwardWord();
|
||||
|
||||
/**
|
||||
* Show Menu Bar. Default: Ctrl-M
|
||||
* @return the shortcut of the standard accelerator
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &showMenubar();
|
||||
|
||||
/**
|
||||
* Permanently delete files or folders. Default: Shift+Delete
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.25
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &deleteFile();
|
||||
|
||||
/**
|
||||
* Rename files or folders. Default: F2
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.25
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &renameFile();
|
||||
|
||||
/**
|
||||
* Create a folder. Default: Ctrl+Shift+N
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.74
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &createFolder();
|
||||
|
||||
/**
|
||||
* Moves files or folders to the trash. Default: Delete
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.25
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &moveToTrash();
|
||||
|
||||
/**
|
||||
* Opens the app's settings window. Default: Ctrl+Shift+Comma
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.64
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &preferences();
|
||||
|
||||
/**
|
||||
* Shows or hides hidden files. Defaults: Ctrl+H, Alt+.
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 5.70
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &showHideHiddenFiles();
|
||||
|
||||
/**
|
||||
* Open a main menu like the menu bar or a hamburger menu.
|
||||
* Necessary for accessibility. Default: F10
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 6.0
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &openMainMenu();
|
||||
|
||||
/**
|
||||
* Open a context menu for the object with keyboard focus.
|
||||
* Necessary for accessibility. Default: Menu, Shift+F10
|
||||
* @return the shortcut of the standard accelerator
|
||||
* @since 6.0
|
||||
*/
|
||||
KCONFIGGUI_EXPORT const QList<QKeySequence> &openContextMenu();
|
||||
}
|
||||
|
||||
#endif // KSTANDARDSHORTCUT_H
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997 Stefan Taferner <taferner@alpin.or.at>
|
||||
SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
|
||||
SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <ellis@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KSTANDARDSHORTCUT_P_H
|
||||
#define KSTANDARDSHORTCUT_P_H
|
||||
|
||||
#include "kstandardshortcut.h"
|
||||
|
||||
namespace KStandardShortcut
|
||||
{
|
||||
void initialize(StandardShortcut id);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kstandardshortcutwatcher.h"
|
||||
|
||||
#include "kconfigwatcher.h"
|
||||
#include "kstandardshortcut_p.h"
|
||||
|
||||
namespace KStandardShortcut
|
||||
{
|
||||
class StandardShortcutWatcherPrivate
|
||||
{
|
||||
public:
|
||||
KConfigWatcher::Ptr watcher = KConfigWatcher::create(KSharedConfig::openConfig());
|
||||
};
|
||||
|
||||
StandardShortcutWatcher::StandardShortcutWatcher(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(std::make_unique<StandardShortcutWatcherPrivate>())
|
||||
{
|
||||
connect(d->watcher.get(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &keys) {
|
||||
if (group.name() != QStringLiteral("Shortcuts")) {
|
||||
return;
|
||||
}
|
||||
for (const auto &key : keys) {
|
||||
const StandardShortcut shortcut = KStandardShortcut::findByName(QString::fromUtf8(key));
|
||||
if (shortcut != KStandardShortcut::AccelNone) {
|
||||
initialize(shortcut);
|
||||
Q_EMIT shortcutChanged(shortcut, KStandardShortcut::shortcut(shortcut));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
StandardShortcutWatcher::~StandardShortcutWatcher() = default;
|
||||
|
||||
StandardShortcutWatcher *shortcutWatcher()
|
||||
{
|
||||
static StandardShortcutWatcher watcher;
|
||||
return &watcher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_kstandardshortcutwatcher.cpp"
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KSTANDARDSHORTCUTWATCHER_H
|
||||
#define KSTANDARDSHORTCUTWATCHER_H
|
||||
|
||||
#include "kstandardshortcut.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
namespace KStandardShortcut
|
||||
{
|
||||
class StandardShortcutWatcherPrivate;
|
||||
|
||||
/**
|
||||
* Watches for changes made to standard shortcuts and notifies about those changes.
|
||||
* @see KStandardShortcut::shortcutWatcher
|
||||
* @since 5.91
|
||||
*/
|
||||
class KCONFIGGUI_EXPORT StandardShortcutWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~StandardShortcutWatcher();
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* The standardshortcut @p id was changed to @p shortcut
|
||||
*/
|
||||
void shortcutChanged(KStandardShortcut::StandardShortcut id, const QList<QKeySequence> &shortcut);
|
||||
|
||||
private:
|
||||
KCONFIGGUI_NO_EXPORT explicit StandardShortcutWatcher(QObject *parent = nullptr);
|
||||
|
||||
friend KCONFIGGUI_EXPORT StandardShortcutWatcher *shortcutWatcher();
|
||||
std::unique_ptr<StandardShortcutWatcherPrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the global KStandardShortcutWatcher instance of this program.
|
||||
* In addition to the notifying about changes it also keeps the information returned by the
|
||||
* functions in @p KStandardShortcut up to date.
|
||||
* The object is created by the first call to this function.
|
||||
* @since 5.91
|
||||
*/
|
||||
KCONFIGGUI_EXPORT StandardShortcutWatcher *shortcutWatcher();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2012 Benjamin Port <benjamin.port@ben2367.fr>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kwindowconfig.h"
|
||||
#include "ksharedconfig.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
static const char s_initialSizePropertyName[] = "_kconfig_initial_size";
|
||||
static const char s_initialScreenSizePropertyName[] = "_kconfig_initial_screen_size";
|
||||
|
||||
// Convenience function to get a space-separated list of all connected screens
|
||||
static QString allConnectedScreens()
|
||||
{
|
||||
QStringList names;
|
||||
const auto screens = QGuiApplication::screens();
|
||||
names.reserve(screens.length());
|
||||
for (auto screen : screens) {
|
||||
names << screen->name();
|
||||
}
|
||||
// A string including the connector names is used in the config file key for
|
||||
// storing per-screen-arrangement size and position data, which means we
|
||||
// need this string to be consistent for the same screen arrangement. But
|
||||
// connector order is non-deterministic. We need to sort the list to keep a
|
||||
// consistent order and avoid losing multi-screen size and position data.
|
||||
names.sort();
|
||||
return names.join(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
// Convenience function to return screen by its name from window screen siblings
|
||||
// returns current window screen if not found
|
||||
static QScreen *findScreenByName(const QWindow *window, const QString screenName)
|
||||
{
|
||||
if (screenName == window->screen()->name()) {
|
||||
return window->screen();
|
||||
}
|
||||
for (QScreen *s : window->screen()->virtualSiblings()) {
|
||||
if (s->name() == screenName) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return window->screen();
|
||||
}
|
||||
|
||||
// Convenience function to get an appropriate config file key under which to
|
||||
// save window size, position, or maximization information.
|
||||
static QString configFileString(const QString &key)
|
||||
{
|
||||
QString returnString;
|
||||
const int numberOfScreens = QGuiApplication::screens().length();
|
||||
|
||||
if (numberOfScreens == 1) {
|
||||
// For single-screen setups, we save data on a per-resolution basis.
|
||||
const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
|
||||
returnString = QStringLiteral("%1x%2 screen: %3").arg(QString::number(screenGeometry.width()), QString::number(screenGeometry.height()), key);
|
||||
} else {
|
||||
// For multi-screen setups, we save data based on the number of screens.
|
||||
// Distinguishing individual screens based on their names is unreliable
|
||||
// due to name strings being inherently volatile.
|
||||
returnString = QStringLiteral("%1 screens: %2").arg(QString::number(numberOfScreens), key);
|
||||
}
|
||||
return returnString;
|
||||
}
|
||||
|
||||
// Convenience function for "window is maximized" string
|
||||
static QString screenMaximizedString()
|
||||
{
|
||||
return configFileString(QStringLiteral("Window-Maximized"));
|
||||
}
|
||||
// Convenience function for window width string
|
||||
static QString windowWidthString()
|
||||
{
|
||||
return configFileString(QStringLiteral("Width"));
|
||||
}
|
||||
// Convenience function for window height string
|
||||
static QString windowHeightString()
|
||||
{
|
||||
return configFileString(QStringLiteral("Height"));
|
||||
}
|
||||
// Convenience function for window X position string
|
||||
static QString windowXPositionString()
|
||||
{
|
||||
return configFileString(QStringLiteral("XPosition"));
|
||||
}
|
||||
// Convenience function for window Y position string
|
||||
static QString windowYPositionString()
|
||||
{
|
||||
return configFileString(QStringLiteral("YPosition"));
|
||||
}
|
||||
static QString windowScreenPositionString()
|
||||
{
|
||||
return QStringLiteral("%1").arg(allConnectedScreens());
|
||||
}
|
||||
|
||||
void KWindowConfig::saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options)
|
||||
{
|
||||
// QWindow::screen() shouldn't return null, but it sometimes does due to bugs.
|
||||
if (!window || !window->screen()) {
|
||||
return;
|
||||
}
|
||||
const QScreen *screen = window->screen();
|
||||
|
||||
const QSize sizeToSave = window->size();
|
||||
const bool isMaximized = window->windowState() & Qt::WindowMaximized;
|
||||
|
||||
// Save size only if window is not maximized
|
||||
if (!isMaximized) {
|
||||
const QSize defaultSize(window->property(s_initialSizePropertyName).toSize());
|
||||
const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize());
|
||||
const bool sizeValid = defaultSize.isValid() && defaultScreenSize.isValid();
|
||||
if (!sizeValid || (defaultSize != sizeToSave || defaultScreenSize != screen->geometry().size())) {
|
||||
config.writeEntry(windowWidthString(), sizeToSave.width(), options);
|
||||
config.writeEntry(windowHeightString(), sizeToSave.height(), options);
|
||||
// Don't keep the maximized string in the file since the window is
|
||||
// no longer maximized at this point
|
||||
config.deleteEntry(screenMaximizedString());
|
||||
}
|
||||
// Revert width and height to default if they are same as defaults
|
||||
else {
|
||||
config.revertToDefault(windowWidthString());
|
||||
config.revertToDefault(windowHeightString());
|
||||
}
|
||||
}
|
||||
if ((isMaximized == false) && !config.hasDefault(screenMaximizedString())) {
|
||||
config.revertToDefault(screenMaximizedString());
|
||||
} else {
|
||||
config.writeEntry(screenMaximizedString(), isMaximized, options);
|
||||
}
|
||||
}
|
||||
|
||||
bool KWindowConfig::hasSavedWindowSize(KConfigGroup &config)
|
||||
{
|
||||
return config.hasKey(windowWidthString()) || config.hasKey(windowHeightString()) || config.hasKey(screenMaximizedString());
|
||||
}
|
||||
|
||||
void KWindowConfig::restoreWindowSize(QWindow *window, const KConfigGroup &config)
|
||||
{
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString screenName = config.readEntry(windowScreenPositionString(), window->screen()->name());
|
||||
|
||||
const int width = config.readEntry(windowWidthString(), -1);
|
||||
const int height = config.readEntry(windowHeightString(), -1);
|
||||
const bool isMaximized = config.readEntry(configFileString(QStringLiteral("Window-Maximized")), false);
|
||||
|
||||
// Check default size
|
||||
const QSize defaultSize(window->property(s_initialSizePropertyName).toSize());
|
||||
const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize());
|
||||
if (!defaultSize.isValid() || !defaultScreenSize.isValid()) {
|
||||
const QScreen *screen = findScreenByName(window, screenName);
|
||||
window->setProperty(s_initialSizePropertyName, window->size());
|
||||
window->setProperty(s_initialScreenSizePropertyName, screen->geometry().size());
|
||||
}
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
window->resize(width, height);
|
||||
}
|
||||
|
||||
if (isMaximized) {
|
||||
window->setWindowState(Qt::WindowMaximized);
|
||||
}
|
||||
}
|
||||
|
||||
void KWindowConfig::saveWindowPosition(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options)
|
||||
{
|
||||
// On Wayland, the compositor is solely responsible for window positioning,
|
||||
// So this needs to be a no-op
|
||||
if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the window is maximized, saving the position will only serve to mis-position
|
||||
// it once de-maximized, so let's not do that
|
||||
if (window->windowState() & Qt::WindowMaximized) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.writeEntry(windowXPositionString(), window->x(), options);
|
||||
config.writeEntry(windowYPositionString(), window->y(), options);
|
||||
config.writeEntry(windowScreenPositionString(), window->screen()->name(), options);
|
||||
}
|
||||
|
||||
bool KWindowConfig::hasSavedWindowPosition(KConfigGroup &config)
|
||||
{
|
||||
// Window position save/restore features outside of the compositor are not
|
||||
// supported on Wayland
|
||||
if (QGuiApplication::platformName() == QLatin1String{"wayland"}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return config.hasKey(windowXPositionString()) || config.hasKey(windowYPositionString()) || config.hasKey(windowScreenPositionString());
|
||||
}
|
||||
|
||||
void KWindowConfig::restoreWindowPosition(QWindow *window, const KConfigGroup &config)
|
||||
{
|
||||
// On Wayland, the compositor is solely responsible for window positioning,
|
||||
// So this needs to be a no-op
|
||||
if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isMaximized = config.readEntry(configFileString(QStringLiteral("Window-Maximized")), false);
|
||||
|
||||
// Don't need to restore position if the window was maximized
|
||||
if (isMaximized) {
|
||||
window->setWindowState(Qt::WindowMaximized);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move window to proper screen
|
||||
const QScreen *screen = window->screen();
|
||||
const QString screenName = config.readEntry(windowScreenPositionString(), screen->name());
|
||||
if (screenName != screen->name()) {
|
||||
QScreen *screenConf = findScreenByName(window, screenName);
|
||||
window->setScreen(screenConf);
|
||||
restoreWindowScreenPosition(window, screenConf, config);
|
||||
return;
|
||||
}
|
||||
restoreWindowScreenPosition(window, screen, config);
|
||||
}
|
||||
|
||||
void KWindowConfig::restoreWindowScreenPosition(QWindow *window, const QScreen *screen, const KConfigGroup &config)
|
||||
{
|
||||
Q_UNUSED(screen);
|
||||
const int xPos = config.readEntry(windowXPositionString(), -1);
|
||||
const int yPos = config.readEntry(windowYPositionString(), -1);
|
||||
|
||||
if (xPos == -1 || yPos == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->setX(xPos);
|
||||
window->setY(yPos);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2012 Benjamin Port <benjamin.port@ben2367.fr>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KWINDOWCONFIG_H
|
||||
#define KWINDOWCONFIG_H
|
||||
|
||||
#include <kconfiggroup.h>
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
class QWindow;
|
||||
class QScreen;
|
||||
|
||||
/**
|
||||
* Save and load window sizes into a config
|
||||
*/
|
||||
namespace KWindowConfig
|
||||
{
|
||||
/**
|
||||
* Saves the window's size dependent on the screen dimension either to the
|
||||
* global or application config file.
|
||||
*
|
||||
* @note the group must be set before calling
|
||||
*
|
||||
* @param window The window to save size.
|
||||
* @param config The config group to read from.
|
||||
* @param options passed to KConfigGroup::writeEntry()
|
||||
* @since 5.0
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options = KConfigGroup::Normal);
|
||||
|
||||
/**
|
||||
* Returns whether a given KConfig group has any saved window size data.
|
||||
*
|
||||
* @param config The config group to read from.
|
||||
* @since 6.0
|
||||
*/
|
||||
KCONFIGGUI_EXPORT bool hasSavedWindowSize(KConfigGroup &config);
|
||||
|
||||
/**
|
||||
* Restores the dialog's size from the configuration according to
|
||||
* the screen size.
|
||||
*
|
||||
* If you're calling this from a constructor (for a mainwindow or dialog, for instance)
|
||||
* you should first call winId() so that a QWindow is created, then you can call windowHandle()
|
||||
* to pass to this method.
|
||||
*
|
||||
* Example code:
|
||||
* @code
|
||||
* create(); // ensure there's a window created
|
||||
* const QSize availableSize = windowHandle()->screen()->availableSize();
|
||||
* windowHandle()->resize(availableSize.width() * 0.7, availableSize.height() * 0.5); // default size
|
||||
* KWindowConfig::restoreWindowSize(windowHandle(), KSharedConfig::openConfig()->group("MyDialog"));
|
||||
* resize(windowHandle()->size()); // workaround for QTBUG-40584
|
||||
* @endcode
|
||||
*
|
||||
* @note the group must be set before calling
|
||||
*
|
||||
* @param window The window to restore size.
|
||||
* @param config The config group to read from.
|
||||
* @since 5.0.
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void restoreWindowSize(QWindow *window, const KConfigGroup &config);
|
||||
|
||||
/**
|
||||
* Saves the window's position either to the global or application config file.
|
||||
* This function has no effect on Wayland, where the compositor is responsible
|
||||
* for window positioning.
|
||||
*
|
||||
* @note the group must be set before calling
|
||||
*
|
||||
* @param window The window whose position to save.
|
||||
* @param config The config group to read from.
|
||||
* @param options passed to KConfigGroup::writeEntry()
|
||||
* @since 5.74
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void saveWindowPosition(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options = KConfigGroup::Normal);
|
||||
|
||||
/**
|
||||
* Returns whether a given KConfig group has any saved window position data.
|
||||
*
|
||||
* @note: always returns false on Wayland where saving and restoring window
|
||||
* position data is not supported.
|
||||
*
|
||||
* @param config The config group to read from.
|
||||
* @since 6.0
|
||||
*/
|
||||
KCONFIGGUI_EXPORT bool hasSavedWindowPosition(KConfigGroup &config);
|
||||
/**
|
||||
* Restores the window's screen position from the configuration and calls restoreWindowScreenPosition.
|
||||
* This function has no effect on Wayland, where the compositor is responsible
|
||||
* for window positioning.
|
||||
*
|
||||
* @note the group must be set before calling
|
||||
*
|
||||
* @param window The window whose position to restore.
|
||||
* @param config The config group to read from.
|
||||
* @since 5.74
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void restoreWindowPosition(QWindow *window, const KConfigGroup &config);
|
||||
|
||||
/**
|
||||
* Restores the window's position on provided screen from the configuration.
|
||||
* This function has no effect on Wayland, where the compositor is responsible
|
||||
* for window positioning.
|
||||
*
|
||||
* @note the group must be set before calling
|
||||
*
|
||||
* @param window The window whose position to restore.
|
||||
* @param screen Screen on which window should be placed.
|
||||
* @param config The config group to read from.
|
||||
* @since 5.99
|
||||
*/
|
||||
KCONFIGGUI_EXPORT void restoreWindowScreenPosition(QWindow *window, const QScreen *screen, const KConfigGroup &config);
|
||||
}
|
||||
#endif // KWINDOWCONFIG_H
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kwindowstatesaver.h"
|
||||
#include "ksharedconfig.h"
|
||||
#include "kwindowconfig.h"
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
class KWindowStateSaverPrivate
|
||||
{
|
||||
public:
|
||||
QWindow *window = nullptr;
|
||||
KConfigGroup configGroup;
|
||||
std::function<QWindow *()> windowHandleCallback;
|
||||
int timerId = 0;
|
||||
|
||||
void init(KWindowStateSaver *q);
|
||||
void initWidget(QObject *widget, KWindowStateSaver *q);
|
||||
};
|
||||
|
||||
void KWindowStateSaverPrivate::init(KWindowStateSaver *q)
|
||||
{
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
KWindowConfig::restoreWindowSize(window, configGroup);
|
||||
KWindowConfig::restoreWindowPosition(window, configGroup);
|
||||
|
||||
const auto saveSize = [q, this]() {
|
||||
KWindowConfig::saveWindowSize(window, configGroup);
|
||||
if (!timerId) {
|
||||
timerId = q->startTimer(std::chrono::seconds(30));
|
||||
}
|
||||
};
|
||||
const auto savePosition = [q, this]() {
|
||||
KWindowConfig::saveWindowPosition(window, configGroup);
|
||||
if (!timerId) {
|
||||
timerId = q->startTimer(std::chrono::seconds(30));
|
||||
}
|
||||
};
|
||||
|
||||
QObject::connect(window, &QWindow::widthChanged, q, saveSize);
|
||||
QObject::connect(window, &QWindow::heightChanged, q, saveSize);
|
||||
QObject::connect(window, &QWindow::xChanged, q, savePosition);
|
||||
QObject::connect(window, &QWindow::yChanged, q, savePosition);
|
||||
}
|
||||
|
||||
void KWindowStateSaverPrivate::initWidget(QObject *widget, KWindowStateSaver *q)
|
||||
{
|
||||
if (!window && windowHandleCallback) {
|
||||
window = windowHandleCallback();
|
||||
}
|
||||
if (window) {
|
||||
init(q);
|
||||
} else {
|
||||
widget->installEventFilter(q);
|
||||
}
|
||||
}
|
||||
|
||||
KWindowStateSaver::KWindowStateSaver(QWindow *window, const KConfigGroup &configGroup)
|
||||
: QObject(window)
|
||||
, d(new KWindowStateSaverPrivate)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
d->window = window;
|
||||
d->configGroup = configGroup;
|
||||
d->init(this);
|
||||
}
|
||||
|
||||
KWindowStateSaver::KWindowStateSaver(QWindow *window, const QString &configGroupName)
|
||||
: QObject(window)
|
||||
, d(new KWindowStateSaverPrivate)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
d->window = window;
|
||||
d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName);
|
||||
d->init(this);
|
||||
}
|
||||
|
||||
KWindowStateSaver::~KWindowStateSaver()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KWindowStateSaver::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
killTimer(event->timerId());
|
||||
d->configGroup.sync();
|
||||
d->timerId = 0;
|
||||
}
|
||||
|
||||
bool KWindowStateSaver::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
// QEvent::PlatformSurface would give us a valid window, but if there are
|
||||
// intial resizings (explicitly or via layout constraints) those would then
|
||||
// already overwrite our restored values. So wait until all that is done
|
||||
// and only restore afterwards.
|
||||
if (event->type() == QEvent::ShowToParent && !d->window) {
|
||||
watched->removeEventFilter(this);
|
||||
d->window = d->windowHandleCallback();
|
||||
d->init(this);
|
||||
}
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void KWindowStateSaver::initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const KConfigGroup &configGroup)
|
||||
{
|
||||
d = new KWindowStateSaverPrivate;
|
||||
d->windowHandleCallback = windowHandleCallback;
|
||||
d->configGroup = configGroup;
|
||||
d->initWidget(widget, this);
|
||||
}
|
||||
|
||||
void KWindowStateSaver::initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const QString &configGroupName)
|
||||
{
|
||||
d = new KWindowStateSaverPrivate;
|
||||
d->windowHandleCallback = windowHandleCallback;
|
||||
d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName);
|
||||
d->initWidget(widget, this);
|
||||
}
|
||||
|
||||
#include "moc_kwindowstatesaver.cpp"
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KWINDOWSTATESAVER_H
|
||||
#define KWINDOWSTATESAVER_H
|
||||
|
||||
#include <kconfiggroup.h>
|
||||
#include <kconfiggui_export.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QWindow;
|
||||
class KWindowStateSaverPrivate;
|
||||
|
||||
/**
|
||||
* Saves and restores a window size and (when possible) position.
|
||||
*
|
||||
* This is useful for retrofitting persisting window geometry on existing windows or dialogs,
|
||||
* without having to modify those classes themselves, or having to inherit from them.
|
||||
* For this, create a new instance of KWindowStateSaver for every window that should have it's
|
||||
* state persisted, and pass it the window or widget as well as the config group the state
|
||||
* should be stored in. The KWindowStateSaver will restore an existing state and then monitor
|
||||
* the window for subsequent changes to persist. It will delete itself once the window is
|
||||
* deleted.
|
||||
*
|
||||
* @code
|
||||
* QPrintPreviewDialog dlg = ...
|
||||
* new KWindowStateSaver(&dlg, "printPreviewDialogState");
|
||||
* ...
|
||||
* dlg.exec();
|
||||
* @endcode
|
||||
*
|
||||
* Note that freshly created top-level QWidgets (such as the dialog in the above example)
|
||||
* do not have an associated QWindow yet (ie. windowHandle() return @c nullptr). KWindowStateSaver
|
||||
* supports this with its QWidget constructors which will monitor the widget for having
|
||||
* its associated QWindow created before continuing with that.
|
||||
*
|
||||
* When implementing your own windows/dialogs, using KWindowConfig directly can be an
|
||||
* alternative.
|
||||
*
|
||||
* @see KWindowConfig
|
||||
* @since 5.92
|
||||
*/
|
||||
class KCONFIGGUI_EXPORT KWindowStateSaver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Create a new window state saver for @p window.
|
||||
* @param configGroup A KConfigGroup that holds the window state.
|
||||
*/
|
||||
explicit KWindowStateSaver(QWindow *window, const KConfigGroup &configGroup);
|
||||
/**
|
||||
* Create a new window state saver for @p window.
|
||||
* @param configGroupName The name of a KConfigGroup in the default state
|
||||
* configuration (see KSharedConfig::openStateConfig) that holds the window state.
|
||||
*/
|
||||
explicit KWindowStateSaver(QWindow *window, const QString &configGroupName);
|
||||
|
||||
/**
|
||||
* Create a new window state saver for @p widget.
|
||||
* Use this for widgets that aren't shown yet and would still return @c nullptr from windowHandle().
|
||||
* @param configGroup A KConfigGroup that holds the window state.
|
||||
*/
|
||||
template<typename Widget>
|
||||
explicit inline KWindowStateSaver(Widget *widget, const KConfigGroup &configGroup);
|
||||
/**
|
||||
* Create a new window state saver for @p widget.
|
||||
* Use this for widgets that aren't shown yet and would still return @c nullptr from windowHandle().
|
||||
* @param configGroupName The name of a KConfigGroup in the default state
|
||||
* configuration (see KSharedConfig::openStateConfig) that holds the window state.
|
||||
*/
|
||||
template<typename Widget>
|
||||
explicit inline KWindowStateSaver(Widget *widget, const QString &configGroupName);
|
||||
|
||||
~KWindowStateSaver();
|
||||
|
||||
private:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
// API used by template code, so technically part of the ABI
|
||||
void initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const KConfigGroup &configGroup);
|
||||
void initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const QString &configGroupName);
|
||||
|
||||
// cannot use std::unique_ptr due to the template ctors
|
||||
// not seeing the full private class
|
||||
KWindowStateSaverPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
template<typename Widget>
|
||||
KWindowStateSaver::KWindowStateSaver(Widget *widget, const KConfigGroup &configGroup)
|
||||
: QObject(widget)
|
||||
{
|
||||
initWidget(
|
||||
widget,
|
||||
[widget]() {
|
||||
return widget->windowHandle();
|
||||
},
|
||||
configGroup);
|
||||
}
|
||||
|
||||
template<typename Widget>
|
||||
KWindowStateSaver::KWindowStateSaver(Widget *widget, const QString &configGroupName)
|
||||
: QObject(widget)
|
||||
{
|
||||
initWidget(
|
||||
widget,
|
||||
[widget]() {
|
||||
return widget->windowHandle();
|
||||
},
|
||||
configGroupName);
|
||||
}
|
||||
|
||||
#endif // KWINDOWSTATESAVER_H
|
||||
@@ -0,0 +1,20 @@
|
||||
add_executable(kconf_update kconf_update.cpp)
|
||||
add_executable(KF6::kconf_update ALIAS kconf_update)
|
||||
|
||||
target_compile_definitions(kconf_update PRIVATE -DCMAKE_INSTALL_FULL_LIBDIR="${CMAKE_INSTALL_FULL_LIBDIR}")
|
||||
ecm_qt_declare_logging_category(kconf_update
|
||||
HEADER kconf_update_debug.h
|
||||
IDENTIFIER KCONF_UPDATE_LOG
|
||||
CATEGORY_NAME kf.config.kconf_update
|
||||
OLD_CATEGORY_NAMES kf5.kconfig.update
|
||||
DESCRIPTION "kconf_update"
|
||||
EXPORT KCONFIG
|
||||
)
|
||||
|
||||
target_link_libraries(kconf_update Qt6::Core KF6::ConfigCore)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
ecm_mark_nongui_executable(kconf_update)
|
||||
|
||||
# Although this is mostly an internal binary (hence installing it in
|
||||
# KF6_LIBEXEC_INSTALL_DIR), it is used by kded, and so we export its location
|
||||
install(TARGETS kconf_update EXPORT KF6ConfigCompilerTargets DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF})
|
||||
@@ -0,0 +1,31 @@
|
||||
/** @mainpage ./kconf_update
|
||||
|
||||
kconf_update is a tool designed to update config files. Over time applications
|
||||
sometimes need to rearrange the way configuration options are stored. Since
|
||||
such an update shouldn't influence the configuration options that the user
|
||||
has selected, the application must take care that the options stored in the
|
||||
old way will still be honored.
|
||||
|
||||
What used to happen is that the application looks up both the old and the
|
||||
new configuration option and then decides which one to use. This method has
|
||||
several drawbacks:
|
||||
- The application may need to read more configuration files than strictly
|
||||
needed, resulting in a slower startup.
|
||||
- The application becomes bigger with code that will only be used once.
|
||||
|
||||
kconf_update addresses these problems by offering a framework to update
|
||||
configuration files without adding code to the application itself.
|
||||
|
||||
See the <a href="https://commits.kde.org/kconfig?path=src/kconf_update/README.kconf_update">README file</a> for more information.
|
||||
|
||||
@authors
|
||||
Waldo Bastian \<bastian@kde.org\>
|
||||
|
||||
@maintainers
|
||||
[Unknown/None]
|
||||
|
||||
@licenses
|
||||
@lgpl
|
||||
|
||||
*/
|
||||
// vim:ts=4:sw=4:expandtab:filetype=doxygen
|
||||
@@ -0,0 +1 @@
|
||||
https://techbase.kde.org/Development/Tools/Using_kconf_update
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kconfig_version.h"
|
||||
#include <cstdlib>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDate>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextStream>
|
||||
#include <QUrl>
|
||||
|
||||
#include <kconfig.h>
|
||||
#include <kconfiggroup.h>
|
||||
|
||||
#include <QCommandLineOption>
|
||||
#include <QCommandLineParser>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "kconf_update_debug.h"
|
||||
|
||||
// Convenience wrapper around qCDebug to prefix the output with metadata of
|
||||
// the file.
|
||||
#define qCDebugFile(CATEGORY) qCDebug(CATEGORY) << m_currentFilename << ':' << m_lineCount << ":'" << m_line << "': "
|
||||
|
||||
class KonfUpdate
|
||||
{
|
||||
public:
|
||||
KonfUpdate(QCommandLineParser *parser);
|
||||
~KonfUpdate();
|
||||
|
||||
KonfUpdate(const KonfUpdate &) = delete;
|
||||
KonfUpdate &operator=(const KonfUpdate &) = delete;
|
||||
|
||||
QStringList findUpdateFiles(bool dirtyOnly);
|
||||
|
||||
bool updateFile(const QString &filename);
|
||||
|
||||
void gotId(const QString &_id);
|
||||
void gotScript(const QString &_script);
|
||||
|
||||
protected:
|
||||
/** kconf_updaterc */
|
||||
KConfig *m_config;
|
||||
QString m_currentFilename;
|
||||
bool m_skip = false;
|
||||
bool m_bTestMode;
|
||||
bool m_bDebugOutput;
|
||||
QString m_id;
|
||||
|
||||
bool m_bUseConfigInfo = false;
|
||||
QStringList m_arguments;
|
||||
QTextStream *m_textStream;
|
||||
QFile *m_file;
|
||||
QString m_line;
|
||||
int m_lineCount;
|
||||
};
|
||||
|
||||
KonfUpdate::KonfUpdate(QCommandLineParser *parser)
|
||||
: m_textStream(nullptr)
|
||||
, m_file(nullptr)
|
||||
, m_lineCount(-1)
|
||||
{
|
||||
bool updateAll = false;
|
||||
|
||||
m_config = new KConfig(QStringLiteral("kconf_updaterc"));
|
||||
KConfigGroup cg(m_config, QString());
|
||||
|
||||
QStringList updateFiles;
|
||||
|
||||
m_bDebugOutput = parser->isSet(QStringLiteral("debug"));
|
||||
if (m_bDebugOutput) {
|
||||
// The only way to enable debug reliably is through a filter rule.
|
||||
// The category itself is const, so we can't just go around changing
|
||||
// its mode. This can however be overridden by the environment, so
|
||||
// we'll want to have a fallback warning if debug is not enabled
|
||||
// after setting the filter.
|
||||
QLoggingCategory::setFilterRules(QLatin1String("%1.debug=true").arg(QLatin1String{KCONF_UPDATE_LOG().categoryName()}));
|
||||
qDebug() << "Automatically enabled the debug logging category" << KCONF_UPDATE_LOG().categoryName();
|
||||
if (!KCONF_UPDATE_LOG().isDebugEnabled()) {
|
||||
qWarning("The debug logging category %s needs to be enabled manually to get debug output", KCONF_UPDATE_LOG().categoryName());
|
||||
}
|
||||
}
|
||||
|
||||
m_bTestMode = parser->isSet(QStringLiteral("testmode"));
|
||||
if (m_bTestMode) {
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
}
|
||||
|
||||
if (parser->isSet(QStringLiteral("check"))) {
|
||||
m_bUseConfigInfo = true;
|
||||
const QString file =
|
||||
QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String{"kconf_update/"} + parser->value(QStringLiteral("check")));
|
||||
if (file.isEmpty()) {
|
||||
qWarning("File '%s' not found.", parser->value(QStringLiteral("check")).toLocal8Bit().data());
|
||||
qCDebug(KCONF_UPDATE_LOG) << "File" << parser->value(QStringLiteral("check")) << "passed on command line not found";
|
||||
return;
|
||||
}
|
||||
updateFiles.append(file);
|
||||
} else if (!parser->positionalArguments().isEmpty()) {
|
||||
updateFiles += parser->positionalArguments();
|
||||
} else if (m_bTestMode) {
|
||||
qWarning("Test mode enabled, but no files given.");
|
||||
return;
|
||||
} else {
|
||||
if (cg.readEntry("autoUpdateDisabled", false)) {
|
||||
return;
|
||||
}
|
||||
updateFiles = findUpdateFiles(true);
|
||||
updateAll = true;
|
||||
}
|
||||
|
||||
for (const QString &file : std::as_const(updateFiles)) {
|
||||
updateFile(file);
|
||||
}
|
||||
|
||||
if (updateAll && !cg.readEntry("updateInfoAdded", false)) {
|
||||
cg.writeEntry("updateInfoAdded", true);
|
||||
updateFiles = findUpdateFiles(false);
|
||||
}
|
||||
}
|
||||
|
||||
KonfUpdate::~KonfUpdate()
|
||||
{
|
||||
delete m_config;
|
||||
delete m_file;
|
||||
delete m_textStream;
|
||||
}
|
||||
|
||||
QStringList KonfUpdate::findUpdateFiles(bool dirtyOnly)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kconf_update"), QStandardPaths::LocateDirectory);
|
||||
for (const QString &d : dirs) {
|
||||
const QDir dir(d);
|
||||
|
||||
const QStringList fileNames = dir.entryList(QStringList(QStringLiteral("*.upd")));
|
||||
for (const QString &fileName : fileNames) {
|
||||
const QString file = dir.filePath(fileName);
|
||||
QFileInfo info(file);
|
||||
|
||||
KConfigGroup cg(m_config, fileName);
|
||||
const qint64 ctime = cg.readEntry("ctime", 0);
|
||||
const qint64 mtime = cg.readEntry("mtime", 0);
|
||||
const QString done = cg.readEntry("done", QString());
|
||||
if (!dirtyOnly //
|
||||
|| (ctime != 0 && ctime != info.birthTime().toSecsSinceEpoch()) //
|
||||
|| mtime != info.lastModified().toSecsSinceEpoch() //
|
||||
|| (mtime != 0 && done.isEmpty())) {
|
||||
result.append(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntax:
|
||||
* # Comment
|
||||
* Id=id
|
||||
* ScriptArguments=arguments
|
||||
* Script=scriptfile[,interpreter]
|
||||
**/
|
||||
bool KonfUpdate::updateFile(const QString &filename)
|
||||
{
|
||||
m_currentFilename = filename;
|
||||
const int i = m_currentFilename.lastIndexOf(QLatin1Char{'/'});
|
||||
if (i != -1) {
|
||||
m_currentFilename = m_currentFilename.mid(i + 1);
|
||||
}
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Could not open update-file '%s'.", qUtf8Printable(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
qCDebug(KCONF_UPDATE_LOG) << "Checking update-file" << filename << "for new updates";
|
||||
|
||||
QTextStream ts(&file);
|
||||
ts.setEncoding(QStringConverter::Encoding::Latin1);
|
||||
m_lineCount = 0;
|
||||
bool foundVersion = false;
|
||||
while (!ts.atEnd()) {
|
||||
m_line = ts.readLine().trimmed();
|
||||
const QLatin1String versionPrefix("Version=");
|
||||
if (m_line.startsWith(versionPrefix)) {
|
||||
if (m_line.mid(versionPrefix.length()) == QLatin1Char('6')) {
|
||||
foundVersion = true;
|
||||
continue;
|
||||
} else {
|
||||
qWarning(KCONF_UPDATE_LOG).noquote() << filename << "defined" << m_line << "but Version=6 was expected";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++m_lineCount;
|
||||
if (m_line.isEmpty() || (m_line[0] == QLatin1Char('#'))) {
|
||||
continue;
|
||||
}
|
||||
if (m_line.startsWith(QLatin1String("Id="))) {
|
||||
if (!foundVersion) {
|
||||
qCDebug(KCONF_UPDATE_LOG, "Missing 'Version=6', file '%s' will be skipped.", qUtf8Printable(filename));
|
||||
break;
|
||||
}
|
||||
gotId(m_line.mid(3));
|
||||
} else if (m_skip) {
|
||||
continue;
|
||||
} else if (m_line.startsWith(QLatin1String("Script="))) {
|
||||
gotScript(m_line.mid(7));
|
||||
m_arguments.clear();
|
||||
} else if (m_line.startsWith(QLatin1String("ScriptArguments="))) {
|
||||
const QString argLine = m_line.mid(16);
|
||||
m_arguments = QProcess::splitCommand(argLine);
|
||||
} else {
|
||||
qCDebugFile(KCONF_UPDATE_LOG) << "Parse error";
|
||||
}
|
||||
}
|
||||
// Flush.
|
||||
gotId(QString());
|
||||
|
||||
// Remember that this file was updated:
|
||||
if (!m_bTestMode) {
|
||||
QFileInfo info(filename);
|
||||
KConfigGroup cg(m_config, m_currentFilename);
|
||||
if (info.birthTime().isValid()) {
|
||||
cg.writeEntry("ctime", info.birthTime().toSecsSinceEpoch());
|
||||
}
|
||||
cg.writeEntry("mtime", info.lastModified().toSecsSinceEpoch());
|
||||
cg.sync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KonfUpdate::gotId(const QString &_id)
|
||||
{
|
||||
// Remember that the last update group has been done:
|
||||
if (!m_id.isEmpty() && !m_skip && !m_bTestMode) {
|
||||
KConfigGroup cg(m_config, m_currentFilename);
|
||||
|
||||
QStringList ids = cg.readEntry("done", QStringList());
|
||||
if (!ids.contains(m_id)) {
|
||||
ids.append(m_id);
|
||||
cg.writeEntry("done", ids);
|
||||
cg.sync();
|
||||
}
|
||||
}
|
||||
|
||||
if (_id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether this update group needs to be done:
|
||||
KConfigGroup cg(m_config, m_currentFilename);
|
||||
QStringList ids = cg.readEntry("done", QStringList());
|
||||
if (ids.contains(_id) && !m_bUseConfigInfo) {
|
||||
// qDebug("Id '%s' was already in done-list", _id.toLatin1().constData());
|
||||
m_skip = true;
|
||||
return;
|
||||
}
|
||||
m_skip = false;
|
||||
m_id = _id;
|
||||
if (m_bUseConfigInfo) {
|
||||
qCDebug(KCONF_UPDATE_LOG) << m_currentFilename << ": Checking update" << _id;
|
||||
} else {
|
||||
qCDebug(KCONF_UPDATE_LOG) << m_currentFilename << ": Found new update" << _id;
|
||||
}
|
||||
}
|
||||
|
||||
void KonfUpdate::gotScript(const QString &_script)
|
||||
{
|
||||
QString script;
|
||||
QString interpreter;
|
||||
const int i = _script.indexOf(QLatin1Char{','});
|
||||
if (i == -1) {
|
||||
script = _script.trimmed();
|
||||
} else {
|
||||
script = _script.left(i).trimmed();
|
||||
interpreter = _script.mid(i + 1).trimmed();
|
||||
}
|
||||
|
||||
if (script.isEmpty()) {
|
||||
qCDebugFile(KCONF_UPDATE_LOG) << "Script fails to specify filename";
|
||||
m_skip = true;
|
||||
return;
|
||||
}
|
||||
|
||||
QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kconf_update/") + script);
|
||||
if (path.isEmpty()) {
|
||||
if (interpreter.isEmpty()) {
|
||||
path = QStringLiteral("%1/kconf_update_bin/%2").arg(QStringLiteral(CMAKE_INSTALL_FULL_LIBDIR), script);
|
||||
if (!QFile::exists(path)) {
|
||||
path = QStandardPaths::findExecutable(script);
|
||||
}
|
||||
}
|
||||
|
||||
if (path.isEmpty()) {
|
||||
qCDebugFile(KCONF_UPDATE_LOG) << "Script" << script << "not found";
|
||||
m_skip = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_arguments.isEmpty()) {
|
||||
qCDebug(KCONF_UPDATE_LOG) << m_currentFilename << ": Running script" << script << "with arguments" << m_arguments;
|
||||
} else {
|
||||
qCDebug(KCONF_UPDATE_LOG) << m_currentFilename << ": Running script" << script;
|
||||
}
|
||||
|
||||
QStringList args;
|
||||
QString cmd;
|
||||
if (interpreter.isEmpty()) {
|
||||
cmd = path;
|
||||
} else {
|
||||
QString interpreterPath = QStandardPaths::findExecutable(interpreter);
|
||||
if (interpreterPath.isEmpty()) {
|
||||
qCDebugFile(KCONF_UPDATE_LOG) << "Cannot find interpreter" << interpreter;
|
||||
m_skip = true;
|
||||
return;
|
||||
}
|
||||
cmd = interpreterPath;
|
||||
args << path;
|
||||
}
|
||||
|
||||
args += m_arguments;
|
||||
|
||||
int result;
|
||||
qCDebug(KCONF_UPDATE_LOG) << "About to run" << cmd;
|
||||
if (m_bDebugOutput) {
|
||||
QFile scriptFile(path);
|
||||
if (scriptFile.open(QIODevice::ReadOnly)) {
|
||||
qCDebug(KCONF_UPDATE_LOG) << "Script contents is:\n" << scriptFile.readAll();
|
||||
}
|
||||
}
|
||||
QProcess proc;
|
||||
proc.start(cmd, args);
|
||||
if (!proc.waitForFinished(60000)) {
|
||||
qCDebugFile(KCONF_UPDATE_LOG) << "update script did not terminate within 60 seconds:" << cmd;
|
||||
m_skip = true;
|
||||
return;
|
||||
}
|
||||
result = proc.exitCode();
|
||||
proc.close();
|
||||
|
||||
if (result != EXIT_SUCCESS) {
|
||||
qCDebug(KCONF_UPDATE_LOG) << m_currentFilename << ": !! An error occurred while running" << cmd;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(KCONF_UPDATE_LOG) << "Successfully ran" << cmd;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.addVersionOption();
|
||||
parser.setApplicationDescription(QCoreApplication::translate("main", "KDE Tool for updating user configuration files"));
|
||||
parser.addHelpOption();
|
||||
parser.addOption(QCommandLineOption(QStringList{QStringLiteral("debug")}, QCoreApplication::translate("main", "Keep output results from scripts")));
|
||||
parser.addOption(QCommandLineOption(
|
||||
QStringList{QStringLiteral("testmode")},
|
||||
QCoreApplication::translate("main", "For unit tests only: do not write the done entries, so that with every re-run, the scripts are executed again")));
|
||||
parser.addOption(QCommandLineOption(QStringList{QStringLiteral("check")},
|
||||
QCoreApplication::translate("main", "Check whether config file itself requires updating"),
|
||||
QStringLiteral("update-file")));
|
||||
parser.addPositionalArgument(QStringLiteral("files"),
|
||||
QCoreApplication::translate("main", "File(s) to read update instructions from"),
|
||||
QStringLiteral("[files...]"));
|
||||
|
||||
// TODO aboutData.addAuthor(ki18n("Waldo Bastian"), KLocalizedString(), "bastian@kde.org");
|
||||
|
||||
parser.process(app);
|
||||
KonfUpdate konfUpdate(&parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
add_executable(kconfig_compiler)
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
if(BUILD_TESTING)
|
||||
message(WARNING "Testing should be disabled on cross-compilation")
|
||||
endif()
|
||||
else()
|
||||
add_executable(KF6::kconfig_compiler ALIAS kconfig_compiler)
|
||||
endif()
|
||||
|
||||
target_sources(kconfig_compiler PRIVATE
|
||||
KConfigParameters.cpp
|
||||
KConfigCodeGeneratorBase.cpp
|
||||
KConfigHeaderGenerator.cpp
|
||||
KConfigSourceGenerator.cpp
|
||||
KConfigXmlParser.cpp
|
||||
kconfig_compiler.cpp
|
||||
)
|
||||
|
||||
set_target_properties(kconfig_compiler PROPERTIES
|
||||
OUTPUT_NAME "kconfig_compiler_kf6"
|
||||
)
|
||||
|
||||
target_link_libraries(kconfig_compiler Qt6::Xml)
|
||||
|
||||
ecm_mark_nongui_executable(kconfig_compiler)
|
||||
|
||||
install(TARGETS kconfig_compiler EXPORT KF6ConfigCompilerTargets DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF})
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QLatin1Char>
|
||||
|
||||
#include <QDebug>
|
||||
#include <ostream>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
KConfigCodeGeneratorBase::KConfigCodeGeneratorBase(const QString &inputFile,
|
||||
const QString &baseDir,
|
||||
const QString &fileName,
|
||||
const KConfigParameters ¶meters,
|
||||
ParseResult &parseResult)
|
||||
: parseResult(parseResult)
|
||||
, m_inputFile(inputFile)
|
||||
, m_baseDir(baseDir)
|
||||
, m_fileName(fileName)
|
||||
, m_cfg(parameters)
|
||||
{
|
||||
m_file.setFileName(m_fileName);
|
||||
if (!m_file.open(QIODevice::WriteOnly)) {
|
||||
std::cerr << "Can not open '" << qPrintable(m_fileName) << "for writing." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
m_stream.setDevice(&m_file);
|
||||
|
||||
if (m_cfg.staticAccessors) {
|
||||
m_this = QStringLiteral("self()->");
|
||||
} else {
|
||||
m_const = QStringLiteral(" const");
|
||||
}
|
||||
}
|
||||
|
||||
KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::save()
|
||||
{
|
||||
m_file.close();
|
||||
}
|
||||
|
||||
// TODO: Remove this weird logic and adapt the testcases
|
||||
void KConfigCodeGeneratorBase::indent()
|
||||
{
|
||||
if (m_indentLevel >= 4) {
|
||||
m_indentLevel += 2;
|
||||
} else {
|
||||
m_indentLevel += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::unindent()
|
||||
{
|
||||
if (m_indentLevel > 4) {
|
||||
m_indentLevel -= 2;
|
||||
} else {
|
||||
m_indentLevel -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
QString KConfigCodeGeneratorBase::whitespace() const
|
||||
{
|
||||
QString spaces;
|
||||
for (int i = 0; i < m_indentLevel; i++) {
|
||||
spaces.append(QLatin1Char(' '));
|
||||
}
|
||||
return spaces;
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::startScope()
|
||||
{
|
||||
m_stream << whitespace() << QLatin1Char('{');
|
||||
m_stream << '\n';
|
||||
indent();
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer)
|
||||
{
|
||||
unindent();
|
||||
m_stream << whitespace() << QLatin1Char('}');
|
||||
if (finalizer == ScopeFinalizer::Semicolon) {
|
||||
m_stream << ';';
|
||||
}
|
||||
m_stream << '\n';
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::start()
|
||||
{
|
||||
const QString m_fileName = QFileInfo(m_inputFile).fileName();
|
||||
m_stream << "// This file is generated by kconfig_compiler_kf6 from " << m_fileName << ".kcfg"
|
||||
<< ".\n";
|
||||
m_stream << "// All changes you do to this file will be lost.\n";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList)
|
||||
{
|
||||
for (const auto &include : headerList) {
|
||||
if (include.startsWith(QLatin1Char('"'))) {
|
||||
m_stream << "#include " << include << '\n';
|
||||
} else {
|
||||
m_stream << "#include <" << include << ">\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adds as many 'namespace foo {' lines to p_out as
|
||||
// there are namespaces in p_ns
|
||||
void KConfigCodeGeneratorBase::beginNamespaces()
|
||||
{
|
||||
if (!m_cfg.nameSpace.isEmpty()) {
|
||||
const auto nameSpaceList = m_cfg.nameSpace.split(QStringLiteral("::"));
|
||||
for (const QString &ns : nameSpaceList) {
|
||||
m_stream << "namespace " << ns << " {\n";
|
||||
}
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// adds as many '}' lines to p_out as
|
||||
// there are namespaces in p_ns
|
||||
void KConfigCodeGeneratorBase::endNamespaces()
|
||||
{
|
||||
if (!m_cfg.nameSpace.isEmpty()) {
|
||||
m_stream << '\n';
|
||||
const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1;
|
||||
for (int i = 0; i < namespaceCount; ++i) {
|
||||
m_stream << "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns the member accessor implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
QString n = e->name;
|
||||
QString t = e->type;
|
||||
bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum");
|
||||
|
||||
out << "return ";
|
||||
if (useEnumType) {
|
||||
out << "static_cast<" << enumType(e, globalEnums) << ">(";
|
||||
}
|
||||
out << m_this << varPath(n, m_cfg);
|
||||
if (!e->param.isEmpty()) {
|
||||
out << "[i]";
|
||||
}
|
||||
if (useEnumType) {
|
||||
out << ")";
|
||||
}
|
||||
out << ";\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::memberImmutableBody(const CfgEntry *e, bool globalEnums)
|
||||
{
|
||||
stream() << whitespace() << "return " << m_this << "isImmutable( QStringLiteral( \"";
|
||||
if (!e->param.isEmpty()) {
|
||||
stream() << QString(e->paramName).replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("%1")) << "\" ).arg( ";
|
||||
if (e->paramType == QLatin1String("Enum")) {
|
||||
stream() << "QLatin1String( ";
|
||||
|
||||
if (globalEnums) {
|
||||
stream() << enumName(e->param) << "ToString[i]";
|
||||
} else {
|
||||
stream() << enumName(e->param) << "::enumToString[i]";
|
||||
}
|
||||
|
||||
stream() << " )";
|
||||
} else {
|
||||
stream() << "i";
|
||||
}
|
||||
stream() << " )";
|
||||
} else {
|
||||
stream() << e->name << "\" )";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression)
|
||||
{
|
||||
const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
|
||||
|
||||
m_stream << whitespace() << "if (";
|
||||
if (hasBody) {
|
||||
m_stream << "v != " << varExpression << " && ";
|
||||
}
|
||||
|
||||
const auto immutablefunction = immutableFunction(e->name, m_cfg.dpointer ? m_cfg.className : QString{});
|
||||
m_stream << "!" << m_this << immutablefunction << "(";
|
||||
if (!e->param.isEmpty()) {
|
||||
m_stream << " i ";
|
||||
}
|
||||
m_stream << "))";
|
||||
}
|
||||
|
||||
void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e)
|
||||
{
|
||||
// HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation.
|
||||
if (!e->min.isEmpty()) {
|
||||
if (e->min != QLatin1String("0") || !isUnsigned(e->type)) { // skip writing "if uint<0" (#187579)
|
||||
m_stream << whitespace() << "if (v < " << e->min << ")\n";
|
||||
m_stream << whitespace() << "{\n";
|
||||
m_stream << whitespace();
|
||||
addDebugMethod(m_stream, m_cfg, e->name);
|
||||
m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n";
|
||||
m_stream << whitespace() << " v = " << e->min << ";\n";
|
||||
m_stream << whitespace() << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!e->max.isEmpty()) {
|
||||
m_stream << '\n';
|
||||
m_stream << whitespace() << "if (v > " << e->max << ")\n";
|
||||
m_stream << whitespace() << "{\n";
|
||||
m_stream << whitespace();
|
||||
addDebugMethod(m_stream, m_cfg, e->name);
|
||||
m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n";
|
||||
m_stream << whitespace() << " v = " << e->max << ";\n";
|
||||
m_stream << whitespace() << "}\n\n";
|
||||
}
|
||||
|
||||
const QString varExpression = m_this + varPath(e->name, m_cfg) + (e->param.isEmpty() ? QString{} : QStringLiteral("[i]"));
|
||||
|
||||
// TODO: Remove this `hasBody` logic, always use an '{' for the if.
|
||||
const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties;
|
||||
|
||||
// m_this call creates an `if (someTest ...) that's just to long to throw over the code.
|
||||
createIfSetLogic(e, varExpression);
|
||||
m_stream << (hasBody ? " {" : "") << '\n';
|
||||
m_stream << whitespace() << " " << varExpression << " = v;\n";
|
||||
|
||||
const auto listSignal = e->signalList;
|
||||
for (const Signal &signal : std::as_const(listSignal)) {
|
||||
if (signal.modify) {
|
||||
m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();\n";
|
||||
} else {
|
||||
m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) << ".insert(" << signalEnumName(signal.name) << ");\n";
|
||||
}
|
||||
}
|
||||
if (hasBody) {
|
||||
m_stream << whitespace() << "}\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGCODEGENERATORBASE_H
|
||||
#define KCONFIGCODEGENERATORBASE_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
class CfgEntry;
|
||||
struct ParseResult;
|
||||
|
||||
/* This class manages the base of writing a C - Based code */
|
||||
class KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
enum ScopeFinalizer {
|
||||
None,
|
||||
Semicolon,
|
||||
};
|
||||
|
||||
KConfigCodeGeneratorBase(const QString &inputFileName, // The kcfg file
|
||||
const QString &baseDir, // where we should store the generated file
|
||||
const QString &fileName, // the name of the generated file
|
||||
const KConfigParameters ¶meters, // parameters passed to the generator
|
||||
ParseResult &parseResult // The pre processed configuration entries
|
||||
);
|
||||
virtual ~KConfigCodeGeneratorBase();
|
||||
|
||||
// iterates over the header list adding an #include directive.
|
||||
void addHeaders(const QStringList &header);
|
||||
|
||||
// Create all the namespace indentation levels based on the parsed result and parameters */
|
||||
void beginNamespaces();
|
||||
|
||||
// Closes all the namespaces adding lines with single '}'
|
||||
void endNamespaces();
|
||||
|
||||
// Add the correct amount of whitespace in the code.
|
||||
QString whitespace() const;
|
||||
|
||||
// start a block scope `{` and increase indentation level.
|
||||
void endScope(ScopeFinalizer finalizer = None);
|
||||
|
||||
// end a block scope `}` and decrease indentation level.
|
||||
void startScope();
|
||||
|
||||
// start writing to the output file
|
||||
virtual void start();
|
||||
|
||||
// save the result on the disk
|
||||
void save();
|
||||
|
||||
// Code Implementations
|
||||
// Implements the `Get` methods for the CfgEntry
|
||||
// TODO: write to the stream directly without returning a QString.
|
||||
QString memberAccessorBody(const CfgEntry *e, bool globalEnums) const;
|
||||
|
||||
// Implements the is<Param>Immutable for the CfgEntry
|
||||
void memberImmutableBody(const CfgEntry *e, bool globalEnums);
|
||||
|
||||
// Implements the `Set` methods for the CfgEntry
|
||||
void memberMutatorBody(const CfgEntry *e);
|
||||
|
||||
// This is the code that creates the logic for the Setter / Mutator.
|
||||
// It *just* creates the if test, no body. The reason is that just
|
||||
// the if test was more than 20 lines of code and hard to understand
|
||||
// what was happening in a bigger function.
|
||||
void createIfSetLogic(const CfgEntry *e, const QString &varExpression);
|
||||
|
||||
protected:
|
||||
/* advance the number of spaces for the indentation level */
|
||||
void indent();
|
||||
|
||||
/* reduce the number of spaces for the indentation level */
|
||||
void unindent();
|
||||
|
||||
QString inputFile() const
|
||||
{
|
||||
return m_inputFile;
|
||||
}
|
||||
QString fileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
QString baseDir() const
|
||||
{
|
||||
return m_baseDir;
|
||||
}
|
||||
QString This() const
|
||||
{
|
||||
return m_this;
|
||||
}
|
||||
QString Const() const
|
||||
{
|
||||
return m_const;
|
||||
}
|
||||
KConfigParameters cfg() const
|
||||
{
|
||||
return m_cfg;
|
||||
}
|
||||
|
||||
// Can't be const.
|
||||
QTextStream &stream()
|
||||
{
|
||||
return m_stream;
|
||||
}
|
||||
|
||||
// HACK: This needs to be accessible because the HeaderGenerator actually modifies
|
||||
// it while running. Considering that this is a the result of the xml Parse, and not
|
||||
// the result of generating code, I consider this to be quite wrong - but moving the
|
||||
// changes away from the header generator only created more problems and I have to
|
||||
// investigate more.
|
||||
ParseResult &parseResult; // the result of the parsed kcfg file
|
||||
|
||||
private:
|
||||
QString m_inputFile; // the base file name, input file is based on this.
|
||||
|
||||
QString m_baseDir; // Where we are going to save the file
|
||||
QString m_fileName; // The file name
|
||||
|
||||
KConfigParameters m_cfg; // The parameters passed via the kcfgc file
|
||||
QTextStream m_stream; // the stream that operates in the file to write data.
|
||||
QFile m_file; // The file handler.
|
||||
|
||||
// Special access to `this->` and `const` thru the code.
|
||||
QString m_this;
|
||||
QString m_const;
|
||||
int m_indentLevel = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGCOMMONSTRUCTS_H
|
||||
#define KCONFIGCOMMONSTRUCTS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
struct Param {
|
||||
QString name;
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct Signal {
|
||||
QString name;
|
||||
QString label;
|
||||
QList<Param> arguments;
|
||||
bool modify = false;
|
||||
};
|
||||
|
||||
class CfgEntry
|
||||
{
|
||||
public:
|
||||
struct Choice {
|
||||
QString name;
|
||||
QString context;
|
||||
QString label;
|
||||
QString toolTip;
|
||||
QString whatsThis;
|
||||
QString val;
|
||||
|
||||
QString value() const
|
||||
{
|
||||
return !val.isEmpty() ? val : name;
|
||||
}
|
||||
};
|
||||
class Choices
|
||||
{
|
||||
public:
|
||||
Choices()
|
||||
{
|
||||
}
|
||||
Choices(const QList<Choice> &d, const QString &n, const QString &p)
|
||||
: prefix(p)
|
||||
, choices(d)
|
||||
, mName(n)
|
||||
{
|
||||
int i = n.indexOf(QLatin1String("::"));
|
||||
if (i >= 0) {
|
||||
mExternalQual = n.left(i + 2);
|
||||
}
|
||||
}
|
||||
QString prefix;
|
||||
QList<Choice> choices;
|
||||
const QString &name() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
const QString &externalQualifier() const
|
||||
{
|
||||
return mExternalQual;
|
||||
}
|
||||
bool external() const
|
||||
{
|
||||
return !mExternalQual.isEmpty();
|
||||
}
|
||||
|
||||
private:
|
||||
QString mName;
|
||||
QString mExternalQual;
|
||||
};
|
||||
|
||||
public:
|
||||
QString group;
|
||||
QString parentGroup;
|
||||
QString type;
|
||||
QString key;
|
||||
QString name;
|
||||
QString labelContext;
|
||||
QString label;
|
||||
QString toolTipContext;
|
||||
QString toolTip;
|
||||
QString whatsThisContext;
|
||||
QString whatsThis;
|
||||
QString code;
|
||||
QString defaultValue;
|
||||
QString param;
|
||||
QString paramName;
|
||||
QString paramType;
|
||||
Choices choices;
|
||||
QList<Signal> signalList;
|
||||
QStringList paramValues;
|
||||
QStringList paramDefaultValues;
|
||||
int paramMax;
|
||||
bool hidden;
|
||||
QString min;
|
||||
QString max;
|
||||
};
|
||||
|
||||
struct ParseResult {
|
||||
QString cfgFileName;
|
||||
bool cfgFileNameArg = false;
|
||||
bool cfgStateConfig = false;
|
||||
QList<Param> parameters;
|
||||
QList<Signal> signalList;
|
||||
QStringList includes;
|
||||
QList<CfgEntry *> entries;
|
||||
bool hasNonModifySignals = false;
|
||||
};
|
||||
|
||||
// TODO: Move those methods
|
||||
QString enumName(const QString &n);
|
||||
QString enumName(const QString &n, const CfgEntry::Choices &c);
|
||||
QString param(const QString &t);
|
||||
QString cppType(const QString &t);
|
||||
QString itemType(const QString &type);
|
||||
QString changeSignalName(const QString &n);
|
||||
|
||||
QString enumType(const CfgEntry *e, bool globalEnums);
|
||||
|
||||
QString getDefaultFunction(const QString &n, const QString &className = QString());
|
||||
QString setFunction(const QString &n, const QString &className = QString());
|
||||
QString getFunction(const QString &n, const QString &className = QString());
|
||||
QString immutableFunction(const QString &n, const QString &className = QString());
|
||||
|
||||
QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
QString signalEnumName(const QString &signalName);
|
||||
|
||||
bool isUnsigned(const QString &type);
|
||||
|
||||
// returns the name of an member variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString varName(const QString &n, const KConfigParameters &cfg);
|
||||
|
||||
QString varPath(const QString &n, const KConfigParameters &cfg);
|
||||
|
||||
// returns the name of an item variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString itemVar(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
// returns the name of the local inner item if there is one
|
||||
// (before wrapping with KConfigCompilerSignallingItem)
|
||||
// Otherwise return itemVar()
|
||||
QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString itemPath(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString filenameOnly(const QString &path);
|
||||
|
||||
QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg);
|
||||
|
||||
QString translatedString(const KConfigParameters &cfg,
|
||||
const QString &string,
|
||||
const QString &context = QString(),
|
||||
const QString ¶m = QString(),
|
||||
const QString ¶mValue = QString());
|
||||
|
||||
// TODO: Sanitize those functions.
|
||||
QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m = QString());
|
||||
|
||||
QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m = QString());
|
||||
|
||||
QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr = QString(), const QString &i = QString());
|
||||
|
||||
QString paramString(const QString &s, const CfgEntry *e, int i);
|
||||
QString paramString(const QString &group, const QList<Param> ¶meters);
|
||||
|
||||
QString defaultValue(const QString &t);
|
||||
QString memberGetDefaultBody(const CfgEntry *e);
|
||||
QString literalString(const QString &s);
|
||||
QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c);
|
||||
|
||||
void addQuotes(QString &s);
|
||||
void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigHeaderGenerator.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
#include <iostream>
|
||||
|
||||
KConfigHeaderGenerator::KConfigHeaderGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result)
|
||||
: KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::start()
|
||||
{
|
||||
KConfigCodeGeneratorBase::start();
|
||||
startHeaderGuards();
|
||||
createHeaders();
|
||||
|
||||
beginNamespaces();
|
||||
|
||||
createForwardDeclarations();
|
||||
|
||||
doClassDefinition();
|
||||
|
||||
endNamespaces();
|
||||
endHeaderGuards();
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::doClassDefinition()
|
||||
{
|
||||
stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << '\n';
|
||||
startScope();
|
||||
|
||||
// Add Q_OBJECT macro if the config need signals.
|
||||
if (!parseResult.signalList.isEmpty() || cfg().generateProperties) {
|
||||
stream() << " Q_OBJECT\n";
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
stream() << " QML_ELEMENT\n";
|
||||
|
||||
if (cfg().singleton) {
|
||||
stream() << " QML_SINGLETON\n";
|
||||
}
|
||||
}
|
||||
stream() << " public:\n";
|
||||
implementEnums();
|
||||
createConstructor();
|
||||
createDestructor();
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString returnType = (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) ? enumType(entry, cfg().globalEnums) : cppType(entry->type);
|
||||
|
||||
createSetters(entry);
|
||||
createProperties(entry, returnType);
|
||||
createImmutableProperty(entry);
|
||||
createGetters(entry, returnType);
|
||||
createImmutableGetters(entry);
|
||||
createDefaultValueMember(entry);
|
||||
createItemAcessors(entry, returnType);
|
||||
}
|
||||
|
||||
createSignals();
|
||||
stream() << " protected:\n";
|
||||
createSingleton();
|
||||
|
||||
// TODO: Move those to functions too.
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << whitespace() << "bool usrSave() override;\n";
|
||||
}
|
||||
|
||||
// Member variables
|
||||
if (!cfg().memberVariables.isEmpty() //
|
||||
&& cfg().memberVariables != QLatin1String("private") //
|
||||
&& cfg().memberVariables != QLatin1String("dpointer")) {
|
||||
stream() << " " << cfg().memberVariables << ":\n";
|
||||
}
|
||||
|
||||
// Class Parameters
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";\n";
|
||||
}
|
||||
|
||||
createNonDPointerHelpers();
|
||||
createDPointer();
|
||||
|
||||
if (cfg().customAddons) {
|
||||
stream() << whitespace() << "// Include custom additions\n";
|
||||
stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << "\"\n";
|
||||
}
|
||||
|
||||
endScope(ScopeFinalizer::Semicolon);
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createHeaders()
|
||||
{
|
||||
addHeaders(cfg().headerIncludes);
|
||||
if (cfg().headerIncludes.size()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
if (!cfg().singleton && parseResult.parameters.isEmpty()) {
|
||||
addHeaders({QStringLiteral("qglobal.h")});
|
||||
}
|
||||
|
||||
if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) {
|
||||
addHeaders({QStringLiteral("kcoreconfigskeleton.h")});
|
||||
} else {
|
||||
addHeaders({QStringLiteral("kconfigskeleton.h")});
|
||||
}
|
||||
|
||||
addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")});
|
||||
if (!cfg().dpointer && parseResult.hasNonModifySignals) {
|
||||
addHeaders({QStringLiteral("QSet")});
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
addHeaders({QStringLiteral("qqmlintegration.h")});
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
|
||||
addHeaders(parseResult.includes);
|
||||
if (parseResult.includes.size()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::startHeaderGuards()
|
||||
{
|
||||
const bool hasNamespace = !cfg().nameSpace.isEmpty();
|
||||
const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper();
|
||||
const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QString{};
|
||||
const QString defineName = namespaceStr + cfg().className.toUpper() + QStringLiteral("_H");
|
||||
|
||||
stream() << "#ifndef " << defineName << '\n';
|
||||
stream() << "#define " << defineName << '\n';
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::endHeaderGuards()
|
||||
{
|
||||
stream() << '\n';
|
||||
stream() << "#endif";
|
||||
stream() << '\n';
|
||||
// HACK: Original files ended with two last newlines, add them.
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices)
|
||||
{
|
||||
const QList<CfgEntry::Choice> chlist = choices.choices;
|
||||
|
||||
if (chlist.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList values;
|
||||
for (const auto &choice : std::as_const(chlist)) {
|
||||
values.append(choices.prefix + choice.name);
|
||||
}
|
||||
|
||||
if (choices.name().isEmpty()) {
|
||||
if (cfg().globalEnums) {
|
||||
stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
if (cfg().generateProperties) {
|
||||
stream() << whitespace() << "Q_ENUM(" << enumName(entry->name, entry->choices) << ")\n";
|
||||
}
|
||||
} else {
|
||||
// Create an automatically named enum
|
||||
stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " public:\n";
|
||||
stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
|
||||
stream() << whitespace() << "};\n";
|
||||
}
|
||||
} else if (!choices.external()) {
|
||||
// Create a named enum
|
||||
stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values)
|
||||
{
|
||||
if (values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg().globalEnums) {
|
||||
// ### FIXME!!
|
||||
// make the following string table an index-based string search!
|
||||
// ###
|
||||
stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };\n";
|
||||
stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];\n";
|
||||
} else {
|
||||
stream() << whitespace() << "class " << enumName(entry->param) << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " public:\n";
|
||||
stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n";
|
||||
stream() << whitespace() << " static const char* const enumToString[];\n";
|
||||
stream() << whitespace() << "};\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::implementEnums()
|
||||
{
|
||||
if (!parseResult.entries.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const CfgEntry::Choices &choices = entry->choices;
|
||||
const QStringList values = entry->paramValues;
|
||||
|
||||
implementChoiceEnums(entry, choices);
|
||||
implementValueEnums(entry, values);
|
||||
}
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSignals()
|
||||
{
|
||||
// Signal definition.
|
||||
if (parseResult.signalList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << "\n enum {\n";
|
||||
|
||||
// HACK: Use C-Style for add a comma in all but the last element,
|
||||
// just to make the source generated code equal to the old one.
|
||||
// When we are sure, revert this to a range-based-for and just add
|
||||
// a last comma, as it's valid c++.
|
||||
for (int i = 0, end = parseResult.signalList.size(); i < end; i++) {
|
||||
auto signal = parseResult.signalList.at(i);
|
||||
stream() << whitespace() << " " << signalEnumName(signal.name) << " = " << (i + 1);
|
||||
if (i != end - 1) {
|
||||
stream() << ",\n";
|
||||
}
|
||||
}
|
||||
stream() << '\n';
|
||||
stream() << whitespace() << "};\n\n";
|
||||
|
||||
stream() << " Q_SIGNALS:";
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
stream() << '\n';
|
||||
if (!signal.label.isEmpty()) {
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " " << signal.label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
}
|
||||
stream() << whitespace() << "void " << signal.name << "(";
|
||||
|
||||
auto it = signal.arguments.cbegin();
|
||||
const auto itEnd = signal.arguments.cend();
|
||||
while (it != itEnd) {
|
||||
Param argument = *it;
|
||||
QString type = param(argument.type);
|
||||
if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->name == argument.name) {
|
||||
type = enumType(entry, cfg().globalEnums);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream() << type << " " << argument.name;
|
||||
if (++it != itEnd) {
|
||||
stream() << ", ";
|
||||
}
|
||||
}
|
||||
stream() << ");\n";
|
||||
}
|
||||
stream() << '\n';
|
||||
|
||||
stream() << " private:\n";
|
||||
stream() << whitespace() << "void itemChanged(quint64 signalFlag);\n";
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDPointer()
|
||||
{
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// use a private class for both member variables and items
|
||||
stream() << " private:\n";
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << "static ";
|
||||
}
|
||||
stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << ";\n";
|
||||
}
|
||||
}
|
||||
stream() << whitespace() << "" << cfg().className << "Private *d;\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createConstructor()
|
||||
{
|
||||
if (cfg().singleton) {
|
||||
stream() << whitespace() << "static " << cfg().className << " *self();\n";
|
||||
|
||||
if (cfg().qmlRegistration) {
|
||||
stream() << whitespace() << "static " << cfg().className << " *create(QQmlEngine *, QJSEngine *);\n";
|
||||
}
|
||||
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << whitespace() << "static void instance(const QString& cfgfilename);\n";
|
||||
stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "" << cfg().className << "(";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (cfg().forceStringFilename) {
|
||||
stream() << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", ");
|
||||
} else if (parseResult.cfgStateConfig) {
|
||||
stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openStateConfig()" : ", ");
|
||||
} else {
|
||||
stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", ");
|
||||
}
|
||||
}
|
||||
if (cfg().forceStringFilename && parseResult.cfgStateConfig) {
|
||||
std::cerr << "One can not use ForceStringFilename and use the stateConfig attribute, consider "
|
||||
"removing the ForceStringFilename kcfgc option if you want to use state data"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stream() << ",";
|
||||
}
|
||||
|
||||
stream() << " " << param(parameter.type) << " " << parameter.name;
|
||||
}
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " QObject *parent = nullptr";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDestructor()
|
||||
{
|
||||
stream() << whitespace() << "~" << cfg().className << "() override;\n\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createForwardDeclarations()
|
||||
{
|
||||
// Private class declaration
|
||||
if (cfg().dpointer) {
|
||||
stream() << "class " << cfg().className << "Private;\n\n";
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
stream() << "class QQmlEngine;\n";
|
||||
stream() << "class QJSEngine;\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
if (!cfg().generateProperties) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name);
|
||||
stream() << " READ " << getFunction(entry->name);
|
||||
|
||||
if (cfg().allMutators || cfg().mutators.contains(entry->name)) {
|
||||
const QString signal = changeSignalName(entry->name);
|
||||
stream() << " WRITE " << setFunction(entry->name);
|
||||
stream() << " NOTIFY " << signal;
|
||||
|
||||
// If we have the modified signal, we'll also need
|
||||
// the changed signal as well
|
||||
Signal s;
|
||||
s.name = signal;
|
||||
s.modify = true;
|
||||
parseResult.signalList.append(s);
|
||||
} else {
|
||||
stream() << " CONSTANT";
|
||||
}
|
||||
stream() << ")\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createImmutableProperty(const CfgEntry *entry)
|
||||
{
|
||||
if (!cfg().generateProperties) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace();
|
||||
stream() << "Q_PROPERTY(bool " << immutableFunction(entry->name);
|
||||
stream() << " READ " << immutableFunction(entry->name);
|
||||
stream() << " CONSTANT)\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSetters(const CfgEntry *entry)
|
||||
{
|
||||
// Manipulator
|
||||
if (!cfg().allMutators && !cfg().mutators.contains(entry->name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Set " << entry->label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
|
||||
stream() << whitespace() << "void " << setFunction(entry->name) << "( ";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << cppType(entry->paramType) << " i, ";
|
||||
}
|
||||
|
||||
stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") ? enumType(entry, cfg().globalEnums) : param(entry->type));
|
||||
|
||||
stream() << " v )";
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
memberMutatorBody(entry);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
// Accessor
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get " << entry->label << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
stream() << returnType;
|
||||
stream() << " " << getFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const();
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
stream() << whitespace() << memberAccessorBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createImmutableGetters(const CfgEntry *entry)
|
||||
{
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Is " << entry->label << " Immutable\n";
|
||||
stream() << whitespace() << "*/\n";
|
||||
// Immutable
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
stream() << "bool " << immutableFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const();
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
memberImmutableBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
} else {
|
||||
stream() << ";\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString &returnType)
|
||||
{
|
||||
Q_UNUSED(returnType)
|
||||
|
||||
// Item accessor
|
||||
if (!cfg().itemAccessors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString declType = entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
|
||||
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get Item object corresponding to " << entry->name << "()" << '\n';
|
||||
stream() << whitespace() << "*/\n";
|
||||
stream() << whitespace() << declType << " *" << getFunction(entry->name) << "Item(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")";
|
||||
if (!cfg().dpointer) {
|
||||
stream() << '\n';
|
||||
startScope();
|
||||
stream() << whitespace() << itemAccessorBody(entry, cfg());
|
||||
endScope();
|
||||
} else {
|
||||
stream() << ";\n";
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createDefaultValueMember(const CfgEntry *entry)
|
||||
{
|
||||
// Default value Accessor
|
||||
if (!((cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) && !entry->defaultValue.isEmpty())) {
|
||||
return;
|
||||
}
|
||||
stream() << whitespace() << "/**\n";
|
||||
stream() << whitespace() << " Get " << entry->label << " default value\n";
|
||||
stream() << whitespace() << "*/\n";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << whitespace() << "static\n";
|
||||
}
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << cppType(entry->type);
|
||||
}
|
||||
stream() << " " << getDefaultFunction(entry->name) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
stream() << whitespace() << "{\n";
|
||||
stream() << whitespace() << " return ";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << "static_cast<" << enumType(entry, cfg().globalEnums) << ">(";
|
||||
}
|
||||
stream() << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " i ";
|
||||
}
|
||||
stream() << ")";
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << ")";
|
||||
}
|
||||
stream() << ";\n";
|
||||
stream() << whitespace() << "}\n";
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createSingleton()
|
||||
{
|
||||
// Private constructor for singleton
|
||||
if (!cfg().singleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << whitespace() << "" << cfg().className << "(";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << "KSharedConfig::Ptr config";
|
||||
}
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << ", ";
|
||||
}
|
||||
stream() << "QObject *parent = nullptr";
|
||||
}
|
||||
stream() << ");\n";
|
||||
stream() << whitespace() << "friend class " << cfg().className << "Helper;\n\n";
|
||||
}
|
||||
|
||||
void KConfigHeaderGenerator::createNonDPointerHelpers()
|
||||
{
|
||||
if (cfg().memberVariables == QLatin1String("dpointer")) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString group;
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->group != group) {
|
||||
group = entry->group;
|
||||
stream() << '\n';
|
||||
stream() << whitespace() << "// " << group << '\n';
|
||||
}
|
||||
stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
|
||||
if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) {
|
||||
stream() << whitespace() << "";
|
||||
if (cfg().staticAccessors) {
|
||||
stream() << "static ";
|
||||
}
|
||||
stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
stream() << "\n private:\n";
|
||||
if (cfg().itemAccessors) {
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString declType =
|
||||
entry->signalList.isEmpty() ? QStringLiteral("Item") + itemType(entry->type) : QStringLiteral("KConfigCompilerSignallingItem");
|
||||
stream() << whitespace() << declType << " *" << itemVar(entry, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << whitespace() << "QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGHEADERGENERATOR_H
|
||||
#define KCONFIGHEADERGENERATOR_H
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class KConfigParameters;
|
||||
class CfgEntry;
|
||||
class QTextStream;
|
||||
struct ParseResult;
|
||||
|
||||
class KConfigHeaderGenerator : public KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
KConfigHeaderGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters ¶meters, ParseResult &parseResult);
|
||||
|
||||
void start() override;
|
||||
|
||||
private:
|
||||
void startHeaderGuards();
|
||||
void endHeaderGuards();
|
||||
|
||||
void implementEnums();
|
||||
void implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices);
|
||||
void implementValueEnums(const CfgEntry *entry, const QStringList &values);
|
||||
|
||||
void doClassDefinition();
|
||||
void createHeaders();
|
||||
void createDPointer();
|
||||
void createNonDPointerHelpers();
|
||||
|
||||
void createConstructor();
|
||||
void createDestructor();
|
||||
void createForwardDeclarations();
|
||||
void createSingleton();
|
||||
void createSignals();
|
||||
|
||||
void createSetters(const CfgEntry *entry);
|
||||
void createItemAcessors(const CfgEntry *entry, const QString &returnType);
|
||||
void createGetters(const CfgEntry *entry, const QString &returnType);
|
||||
void createImmutableGetters(const CfgEntry *entry);
|
||||
void createProperties(const CfgEntry *entry, const QString &returnType);
|
||||
void createImmutableProperty(const CfgEntry *entry);
|
||||
void createDefaultValueMember(const CfgEntry *entry);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of the KDE libraries.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
KConfigParameters::KConfigParameters(const QString &codegenFilename)
|
||||
{
|
||||
if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) {
|
||||
std::cerr << "Codegen options file must have extension .kcfgc" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
baseName = QFileInfo(codegenFilename).fileName();
|
||||
baseName = baseName.left(baseName.length() - 6);
|
||||
|
||||
// Configure the compiler with some settings
|
||||
QSettings codegenConfig(codegenFilename, QSettings::IniFormat);
|
||||
|
||||
nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString();
|
||||
className = codegenConfig.value(QStringLiteral("ClassName")).toString();
|
||||
if (className.isEmpty()) {
|
||||
std::cerr << "Class name missing" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
inherits = codegenConfig.value(QStringLiteral("Inherits")).toString();
|
||||
if (inherits.isEmpty()) {
|
||||
inherits = QStringLiteral("KConfigSkeleton");
|
||||
}
|
||||
visibility = codegenConfig.value(QStringLiteral("Visibility")).toString();
|
||||
if (!visibility.isEmpty()) {
|
||||
visibility += QLatin1Char(' ');
|
||||
}
|
||||
parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool();
|
||||
forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool();
|
||||
singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool();
|
||||
staticAccessors = singleton;
|
||||
customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool();
|
||||
memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString();
|
||||
dpointer = (memberVariables == QLatin1String("dpointer"));
|
||||
headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList();
|
||||
sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList();
|
||||
mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList();
|
||||
allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true")));
|
||||
itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool();
|
||||
setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool();
|
||||
defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList();
|
||||
allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true"));
|
||||
notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList();
|
||||
allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true")));
|
||||
globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool();
|
||||
useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool();
|
||||
const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower();
|
||||
generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool();
|
||||
if (trString == QLatin1String("kde")) {
|
||||
translationSystem = KdeTranslation;
|
||||
translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString();
|
||||
} else {
|
||||
if (!trString.isEmpty() && trString != QLatin1String("qt")) {
|
||||
std::cerr << "Unknown translation system, falling back to Qt tr()" << std::endl;
|
||||
}
|
||||
translationSystem = QtTranslation;
|
||||
}
|
||||
qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString();
|
||||
headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString();
|
||||
qmlRegistration = codegenConfig.value(QStringLiteral("QmlRegistration")).toBool();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGXTPARAMETERS_H
|
||||
#define KCONFIGXTPARAMETERS_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
Configuration Compiler Configuration
|
||||
*/
|
||||
class KConfigParameters
|
||||
{
|
||||
public:
|
||||
KConfigParameters(const QString &codegenFilename);
|
||||
|
||||
public:
|
||||
enum TranslationSystem {
|
||||
QtTranslation,
|
||||
KdeTranslation,
|
||||
};
|
||||
|
||||
// These are read from the .kcfgc configuration file
|
||||
QString nameSpace; // The namespace for the class to be generated
|
||||
QString className; // The class name to be generated
|
||||
QString inherits; // The class the generated class inherits (if empty, from KConfigSkeleton)
|
||||
QString visibility;
|
||||
bool parentInConstructor; // The class has the optional parent parameter in its constructor
|
||||
bool forceStringFilename;
|
||||
bool singleton; // The class will be a singleton
|
||||
bool staticAccessors; // provide or not static accessors
|
||||
bool customAddons;
|
||||
QString memberVariables;
|
||||
QStringList headerIncludes;
|
||||
QStringList sourceIncludes;
|
||||
QStringList mutators;
|
||||
QStringList defaultGetters;
|
||||
QStringList notifiers;
|
||||
QString qCategoryLoggingName;
|
||||
QString headerExtension;
|
||||
bool allMutators;
|
||||
bool setUserTexts;
|
||||
bool allDefaultGetters;
|
||||
bool dpointer;
|
||||
bool globalEnums;
|
||||
bool useEnumTypes;
|
||||
bool itemAccessors;
|
||||
bool allNotifiers;
|
||||
TranslationSystem translationSystem;
|
||||
QString translationDomain;
|
||||
bool generateProperties;
|
||||
QString baseName;
|
||||
bool qmlRegistration;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigSourceGenerator.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
KConfigSourceGenerator::KConfigSourceGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &parseResult)
|
||||
: KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1String(".cpp"), cfg, parseResult)
|
||||
{
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::start()
|
||||
{
|
||||
KConfigCodeGeneratorBase::start();
|
||||
stream() << '\n';
|
||||
createHeaders();
|
||||
|
||||
if (!cfg().nameSpace.isEmpty()) {
|
||||
stream() << "using namespace " << cfg().nameSpace << ";";
|
||||
stream() << "\n\n";
|
||||
}
|
||||
|
||||
createPrivateDPointerImplementation();
|
||||
createSingletonImplementation();
|
||||
createPreamble();
|
||||
doConstructor();
|
||||
doGetterSetterDPointerMode();
|
||||
createDefaultValueGetterSetter();
|
||||
createDestructor();
|
||||
createNonModifyingSignalsHelper();
|
||||
createSignalFlagsHandler();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createHeaders()
|
||||
{
|
||||
QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension;
|
||||
|
||||
// TODO: Make addQuotes return a string instead of replacing it inplace.
|
||||
addQuotes(headerName);
|
||||
|
||||
addHeaders({headerName});
|
||||
stream() << '\n';
|
||||
|
||||
addHeaders(cfg().sourceIncludes);
|
||||
if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) {
|
||||
addHeaders({QStringLiteral("klocalizedstring.h")});
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
// Header required by singleton implementation
|
||||
if (cfg().singleton) {
|
||||
addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")});
|
||||
|
||||
// HACK: Add single line to fix test.
|
||||
if (cfg().singleton && parseResult.cfgFileNameArg) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg().singleton && parseResult.cfgFileNameArg) {
|
||||
addHeaders({QStringLiteral("QDebug")});
|
||||
}
|
||||
|
||||
if (cfg().dpointer && parseResult.hasNonModifySignals) {
|
||||
addHeaders({QStringLiteral("QSet")});
|
||||
}
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
addHeaders({QStringLiteral("QQmlEngine")});
|
||||
}
|
||||
|
||||
if (cfg().singleton) {
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createPrivateDPointerImplementation()
|
||||
{
|
||||
// private class implementation
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString group;
|
||||
beginNamespaces();
|
||||
stream() << "class " << cfg().className << "Private\n";
|
||||
stream() << "{\n";
|
||||
stream() << " public:\n";
|
||||
|
||||
// Create Members
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->group != group) {
|
||||
group = entry->group;
|
||||
stream() << '\n';
|
||||
stream() << " // " << group << '\n';
|
||||
}
|
||||
stream() << " " << cppType(entry->type) << " " << varName(entry->name, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
stream() << "\n // items\n";
|
||||
|
||||
// Create Items.
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
const QString declType = entry->signalList.isEmpty() ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(entry->type))
|
||||
: QStringLiteral("KConfigCompilerSignallingItem");
|
||||
|
||||
stream() << " " << declType << " *" << itemVar(entry, cfg());
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1);
|
||||
}
|
||||
stream() << ";\n";
|
||||
}
|
||||
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << " QSet<quint64> " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n";
|
||||
}
|
||||
|
||||
stream() << "};\n\n";
|
||||
endNamespaces();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSingletonImplementation()
|
||||
{
|
||||
// Singleton implementation
|
||||
if (!cfg().singleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginNamespaces();
|
||||
stream() << "class " << cfg().className << "Helper\n";
|
||||
stream() << '{' << '\n';
|
||||
stream() << " public:\n";
|
||||
stream() << " " << cfg().className << "Helper() : q(nullptr) {}\n";
|
||||
stream() << " ~" << cfg().className << "Helper() { delete q; q = nullptr; }\n";
|
||||
stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;\n";
|
||||
stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;\n";
|
||||
stream() << " " << cfg().className << " *q;\n";
|
||||
stream() << "};\n";
|
||||
endNamespaces();
|
||||
|
||||
stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")\n";
|
||||
|
||||
stream() << cfg().className << " *" << cfg().className << "::self()\n";
|
||||
stream() << "{\n";
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
stream() << " if (!s_global" << cfg().className << "()->q)\n";
|
||||
stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");\n";
|
||||
} else {
|
||||
stream() << " if (!s_global" << cfg().className << "()->q) {\n";
|
||||
stream() << " new " << cfg().className << ';' << '\n';
|
||||
stream() << " s_global" << cfg().className << "()->q->read();\n";
|
||||
stream() << " }\n\n";
|
||||
}
|
||||
stream() << " return s_global" << cfg().className << "()->q;\n";
|
||||
stream() << "}\n\n";
|
||||
|
||||
if (cfg().qmlRegistration && cfg().singleton) {
|
||||
stream() << cfg().className << " *" << cfg().className << "::create(QQmlEngine *, QJSEngine *)\n";
|
||||
stream() << "{\n";
|
||||
stream() << " QQmlEngine::setObjectOwnership(self(), QQmlEngine::CppOwnership);\n";
|
||||
stream() << " return self();\n";
|
||||
stream() << "}\n\n";
|
||||
}
|
||||
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
auto instance = [this](const QString &type, const QString &arg, bool isString) {
|
||||
stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")\n";
|
||||
stream() << "{\n";
|
||||
stream() << " if (s_global" << cfg().className << "()->q) {\n";
|
||||
stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";\n";
|
||||
stream() << " return;\n";
|
||||
stream() << " }\n";
|
||||
stream() << " new " << cfg().className << "(";
|
||||
if (parseResult.cfgStateConfig) {
|
||||
stream() << "KSharedConfig::openStateConfig(" << arg << ")";
|
||||
} else if (isString) {
|
||||
stream() << "KSharedConfig::openConfig(" << arg << ")";
|
||||
} else {
|
||||
stream() << "std::move(" << arg << ")";
|
||||
}
|
||||
stream() << ");\n";
|
||||
stream() << " s_global" << cfg().className << "()->q->read();\n";
|
||||
stream() << "}\n\n";
|
||||
};
|
||||
instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true);
|
||||
instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createPreamble()
|
||||
{
|
||||
QString cppPreamble;
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
if (entry->paramValues.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(entry->param);
|
||||
cppPreamble += cfg().globalEnums
|
||||
? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n")
|
||||
: QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n");
|
||||
}
|
||||
|
||||
if (!cppPreamble.isEmpty()) {
|
||||
stream() << cppPreamble << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createConstructorParameterList()
|
||||
{
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (!cfg().forceStringFilename) {
|
||||
stream() << " KSharedConfig::Ptr config";
|
||||
} else {
|
||||
stream() << " const QString& config";
|
||||
}
|
||||
stream() << (parseResult.parameters.isEmpty() ? "" : ",");
|
||||
}
|
||||
|
||||
for (auto it = parseResult.parameters.cbegin(); it != parseResult.parameters.cend(); ++it) {
|
||||
if (it != parseResult.parameters.cbegin()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " " << param((*it).type) << " " << (*it).name;
|
||||
}
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) {
|
||||
stream() << ",";
|
||||
}
|
||||
stream() << " QObject *parent";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createParentConstructorCall()
|
||||
{
|
||||
stream() << cfg().inherits << "(";
|
||||
if (parseResult.cfgStateConfig) {
|
||||
stream() << " KSharedConfig::openStateConfig(QStringLiteral( \"" << parseResult.cfgFileName << "\") ";
|
||||
} else if (!parseResult.cfgFileName.isEmpty()) {
|
||||
stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" ";
|
||||
}
|
||||
if (parseResult.cfgFileNameArg) {
|
||||
if (!cfg().forceStringFilename) {
|
||||
stream() << " std::move( config ) ";
|
||||
} else {
|
||||
stream() << " config ";
|
||||
}
|
||||
}
|
||||
if (!parseResult.cfgFileName.isEmpty()) {
|
||||
stream() << ") ";
|
||||
}
|
||||
stream() << ")\n";
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createInitializerList()
|
||||
{
|
||||
for (const auto ¶meter : std::as_const(parseResult.parameters)) {
|
||||
stream() << " , mParam" << parameter.name << "(" << parameter.name << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createEnums(const CfgEntry *entry)
|
||||
{
|
||||
if (entry->type != QLatin1String("Enum")) {
|
||||
return;
|
||||
}
|
||||
stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";\n";
|
||||
|
||||
for (const auto &choice : std::as_const(entry->choices.choices)) {
|
||||
stream() << " {\n";
|
||||
stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;\n";
|
||||
stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");\n";
|
||||
if (cfg().setUserTexts) {
|
||||
if (!choice.label.isEmpty()) {
|
||||
stream() << " choice.label = " << translatedString(cfg(), choice.label, choice.context) << ";\n";
|
||||
}
|
||||
if (!choice.toolTip.isEmpty()) {
|
||||
stream() << " choice.toolTip = " << translatedString(cfg(), choice.toolTip, choice.context) << ";\n";
|
||||
}
|
||||
if (!choice.whatsThis.isEmpty()) {
|
||||
stream() << " choice.whatsThis = " << translatedString(cfg(), choice.whatsThis, choice.context) << ";\n";
|
||||
}
|
||||
}
|
||||
stream() << " values" << entry->name << ".append( choice );\n";
|
||||
stream() << " }\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key)
|
||||
{
|
||||
const QString itemVarStr = itemPath(entry, cfg());
|
||||
const QString innerItemVarStr = innerItemVar(entry, cfg());
|
||||
if (!entry->signalList.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, key, entry->defaultValue, cfg()) << '\n';
|
||||
}
|
||||
|
||||
stream() << " " << itemVarStr << " = " << newItem(entry, key, entry->defaultValue, cfg()) << '\n';
|
||||
|
||||
if (!entry->min.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
|
||||
}
|
||||
|
||||
if (!entry->max.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
|
||||
}
|
||||
|
||||
if (cfg().setUserTexts) {
|
||||
stream() << userTextsFunctions(entry, cfg());
|
||||
}
|
||||
|
||||
if (cfg().allNotifiers || cfg().notifiers.contains(entry->name)) {
|
||||
stream() << " " << itemVarStr << "->setWriteFlags(KConfigBase::Notify);\n";
|
||||
}
|
||||
|
||||
for (const CfgEntry::Choice &choice : std::as_const(entry->choices.choices)) {
|
||||
if (!choice.val.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
|
||||
<< "\" ));\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry->parentGroup.isEmpty()) {
|
||||
stream() << " " << itemVarStr << "->setGroup(cg" << QString(entry->group).remove(QRegularExpression(QStringLiteral("\\W"))) << ");\n";
|
||||
}
|
||||
|
||||
stream() << " addItem( " << itemVarStr;
|
||||
QString quotedName = entry->name;
|
||||
addQuotes(quotedName);
|
||||
if (quotedName != key) {
|
||||
stream() << ", QStringLiteral( \"" << entry->name << "\" )";
|
||||
}
|
||||
stream() << " );\n";
|
||||
}
|
||||
|
||||
// TODO : Some compiler option won't work or generate bogus settings file.
|
||||
// * Does not manage properly Notifiers=true kcfgc option for parameterized entries :
|
||||
// ** KConfigCompilerSignallingItem generated with wrong userData parameter (4th one).
|
||||
// ** setWriteFlags() is missing.
|
||||
// * Q_PROPERTY signal won't work
|
||||
void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key)
|
||||
{
|
||||
for (int i = 0; i <= entry->paramMax; i++) {
|
||||
const QString argBracket = QStringLiteral("[%1]").arg(i);
|
||||
const QString innerItemVarStr = innerItemVar(entry, cfg()) + argBracket;
|
||||
|
||||
const QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i]
|
||||
: !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i)
|
||||
: defaultValue(entry->type);
|
||||
|
||||
if (!entry->signalList.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << " = " << newInnerItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n';
|
||||
}
|
||||
|
||||
const QString itemVarStr = itemPath(entry, cfg()) + argBracket;
|
||||
|
||||
stream() << " " << itemVarStr << " = " << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), argBracket) << '\n';
|
||||
|
||||
if (!entry->min.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMinValue(" << entry->min << ");\n";
|
||||
}
|
||||
if (!entry->max.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setMaxValue(" << entry->max << ");\n";
|
||||
}
|
||||
|
||||
for (const CfgEntry::Choice &choice : std::as_const(entry->choices.choices)) {
|
||||
if (!choice.val.isEmpty()) {
|
||||
stream() << " " << innerItemVarStr << "->setValueForChoice(QStringLiteral( \"" << choice.name << "\" ), QStringLiteral( \"" << choice.val
|
||||
<< "\" ));\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg().setUserTexts) {
|
||||
stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName);
|
||||
}
|
||||
|
||||
// Make mutators for enum parameters work by adding them with $(..) replaced by the
|
||||
// param name. The check for isImmutable in the set* functions doesn't have the param
|
||||
// name available, just the corresponding enum value (int), so we need to store the
|
||||
// param names in a separate static list!.
|
||||
const bool isEnum = entry->paramType == QLatin1String("Enum");
|
||||
const QString arg = isEnum ? entry->paramValues[i] : QString::number(i);
|
||||
|
||||
QString paramName = entry->paramName;
|
||||
|
||||
stream() << " addItem( " << itemVarStr << ", QStringLiteral( \"";
|
||||
stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg(arg);
|
||||
stream() << "\" ) );\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry)
|
||||
{
|
||||
if (entry->group == mCurrentGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases.
|
||||
static bool first = true;
|
||||
if (!entry->group.isEmpty()) {
|
||||
if (!first) {
|
||||
stream() << '\n';
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
mCurrentGroup = entry->group;
|
||||
|
||||
if (!entry->parentGroup.isEmpty()) {
|
||||
QString parentGroup = QString(entry->parentGroup).remove(QRegularExpression(QStringLiteral("\\W")));
|
||||
if (!mConfigGroupList.contains(parentGroup)) {
|
||||
stream() << " KConfigGroup cg" << parentGroup << "(this->config(), " << paramString(entry->parentGroup, parseResult.parameters) << ");\n";
|
||||
mConfigGroupList << parentGroup;
|
||||
}
|
||||
QString currentGroup = QString(mCurrentGroup).remove(QRegularExpression(QStringLiteral("\\W")));
|
||||
if (!mConfigGroupList.contains(currentGroup)) {
|
||||
stream() << " KConfigGroup cg" << currentGroup << " = cg" << QString(entry->parentGroup).remove(QRegularExpression(QStringLiteral("\\W")))
|
||||
<< ".group(" << paramString(mCurrentGroup, parseResult.parameters) << ");\n";
|
||||
mConfigGroupList << currentGroup;
|
||||
}
|
||||
} else {
|
||||
stream() << " setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );";
|
||||
stream() << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::doConstructor()
|
||||
{
|
||||
// Constructor
|
||||
stream() << cfg().className << "::" << cfg().className << "(";
|
||||
createConstructorParameterList();
|
||||
stream() << " )\n";
|
||||
stream() << " : ";
|
||||
createParentConstructorCall();
|
||||
createInitializerList();
|
||||
|
||||
stream() << "{\n";
|
||||
|
||||
if (cfg().parentInConstructor) {
|
||||
stream() << " setParent(parent);\n";
|
||||
}
|
||||
|
||||
if (cfg().dpointer) {
|
||||
stream() << " d = new " << cfg().className << "Private;\n";
|
||||
}
|
||||
|
||||
// Needed in case the singleton class is used as baseclass for
|
||||
// another singleton.
|
||||
if (cfg().singleton) {
|
||||
stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);\n";
|
||||
stream() << " s_global" << cfg().className << "()->q = this;\n";
|
||||
}
|
||||
|
||||
if (!parseResult.signalList.isEmpty()) {
|
||||
// this cast to base-class pointer-to-member is valid C++
|
||||
// https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/
|
||||
stream() << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction ="
|
||||
<< " static_cast<KConfigCompilerSignallingItem::NotifyFunction>(&" << cfg().className << "::itemChanged);\n";
|
||||
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
handleCurrentGroupChange(entry);
|
||||
|
||||
const QString key = paramString(entry->key, parseResult.parameters);
|
||||
if (!entry->code.isEmpty()) {
|
||||
stream() << entry->code << '\n';
|
||||
}
|
||||
createEnums(entry);
|
||||
|
||||
stream() << itemDeclaration(entry, cfg());
|
||||
|
||||
if (entry->param.isEmpty()) {
|
||||
createNormalEntry(entry, key);
|
||||
} else {
|
||||
createIndexedEntry(entry, key);
|
||||
}
|
||||
}
|
||||
|
||||
stream() << "}\n\n";
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Accessor
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << cppType(entry->type);
|
||||
}
|
||||
|
||||
stream() << " " << getFunction(entry->name, cfg().className) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
startScope();
|
||||
// HACK: Fix memberAccessorBody
|
||||
stream() << " " << memberAccessorBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createImmutableGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
stream() << whitespace() << "";
|
||||
stream() << "bool "
|
||||
<< " " << immutableFunction(entry->name, cfg().className) << "(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
startScope();
|
||||
memberImmutableBody(entry, cfg().globalEnums);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Manipulator
|
||||
if (!(cfg().allMutators || cfg().mutators.contains(entry->name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << "void " << setFunction(entry->name, cfg().className) << "( ";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << cppType(entry->paramType) << " i, ";
|
||||
}
|
||||
|
||||
if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) {
|
||||
stream() << enumType(entry, cfg().globalEnums);
|
||||
} else {
|
||||
stream() << param(entry->type);
|
||||
}
|
||||
stream() << " v )\n";
|
||||
|
||||
// function body inline only if not using dpointer
|
||||
// for BC mode
|
||||
startScope();
|
||||
memberMutatorBody(entry);
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry)
|
||||
{
|
||||
// Item accessor
|
||||
if (!cfg().itemAccessors) {
|
||||
return;
|
||||
}
|
||||
stream() << '\n';
|
||||
stream() << cfg().inherits << "::Item" << itemType(entry->type) << " *" << getFunction(entry->name, cfg().className) << "Item(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")\n";
|
||||
startScope();
|
||||
stream() << " " << itemAccessorBody(entry, cfg());
|
||||
endScope();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::doGetterSetterDPointerMode()
|
||||
{
|
||||
if (!cfg().dpointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// setters and getters go in Cpp if in dpointer mode
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
createSetterDPointerMode(entry);
|
||||
createGetterDPointerMode(entry);
|
||||
createImmutableGetterDPointerMode(entry);
|
||||
createItemGetterDPointerMode(entry);
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createDefaultValueGetterSetter()
|
||||
{
|
||||
// default value getters always go in Cpp
|
||||
for (const auto *entry : std::as_const(parseResult.entries)) {
|
||||
QString n = entry->name;
|
||||
QString t = entry->type;
|
||||
|
||||
// Default value Accessor, as "helper" function
|
||||
if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) {
|
||||
stream() << cppType(t) << " " << getDefaultFunction(n, cfg().className) << "_helper(";
|
||||
if (!entry->param.isEmpty()) {
|
||||
stream() << " " << cppType(entry->paramType) << " i ";
|
||||
}
|
||||
stream() << ")" << Const() << '\n';
|
||||
startScope();
|
||||
stream() << memberGetDefaultBody(entry) << '\n';
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createDestructor()
|
||||
{
|
||||
stream() << cfg().className << "::~" << cfg().className << "()\n";
|
||||
startScope();
|
||||
if (cfg().dpointer) {
|
||||
stream() << " delete d;\n";
|
||||
}
|
||||
if (cfg().singleton) {
|
||||
const QString qgs = QLatin1String("s_global") + cfg().className;
|
||||
stream() << " if (" << qgs << ".exists() && !" << qgs << ".isDestroyed()) {\n";
|
||||
stream() << " " << qgs << "()->q = nullptr;\n";
|
||||
stream() << " }\n";
|
||||
}
|
||||
endScope();
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createNonModifyingSignalsHelper()
|
||||
{
|
||||
if (!parseResult.hasNonModifySignals) {
|
||||
return;
|
||||
}
|
||||
stream() << "bool " << cfg().className << "::"
|
||||
<< "usrSave()\n";
|
||||
startScope();
|
||||
stream() << " const bool res = " << cfg().inherits << "::usrSave();\n";
|
||||
stream() << " if (!res) return false;\n\n";
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
if (signal.modify) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stream() << " if (" << varPath(QStringLiteral("settingsChanged"), cfg()) << ".contains(" << signalEnumName(signal.name) << "))\n";
|
||||
stream() << " Q_EMIT " << signal.name << "(";
|
||||
auto it = signal.arguments.cbegin();
|
||||
const auto itEnd = signal.arguments.cend();
|
||||
while (it != itEnd) {
|
||||
Param argument = *it;
|
||||
bool cast = false;
|
||||
if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) {
|
||||
for (int i = 0, end = parseResult.entries.count(); i < end; ++i) {
|
||||
if (parseResult.entries.at(i)->name == argument.name) {
|
||||
stream() << "static_cast<" << enumType(parseResult.entries.at(i), cfg().globalEnums) << ">(";
|
||||
cast = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stream() << varPath(argument.name, cfg());
|
||||
if (cast) {
|
||||
stream() << ")";
|
||||
}
|
||||
if (++it != itEnd) {
|
||||
stream() << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
stream() << ");\n";
|
||||
}
|
||||
|
||||
stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << ".clear();\n";
|
||||
stream() << " return true;\n";
|
||||
endScope();
|
||||
}
|
||||
|
||||
void KConfigSourceGenerator::createSignalFlagsHandler()
|
||||
{
|
||||
if (parseResult.signalList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream() << '\n';
|
||||
stream() << "void " << cfg().className << "::"
|
||||
<< "itemChanged(quint64 signalFlag) {\n";
|
||||
if (parseResult.hasNonModifySignals) {
|
||||
stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << ".insert(signalFlag);\n";
|
||||
}
|
||||
|
||||
if (!parseResult.signalList.isEmpty()) {
|
||||
stream() << '\n';
|
||||
}
|
||||
|
||||
bool modifySignalsWritten = false;
|
||||
for (const Signal &signal : std::as_const(parseResult.signalList)) {
|
||||
if (signal.modify) {
|
||||
if (!modifySignalsWritten) {
|
||||
stream() << " switch (signalFlag) {\n";
|
||||
modifySignalsWritten = true;
|
||||
}
|
||||
stream() << " case " << signalEnumName(signal.name) << ":\n";
|
||||
stream() << " Q_EMIT " << signal.name << "();\n";
|
||||
stream() << " break;\n";
|
||||
}
|
||||
}
|
||||
if (modifySignalsWritten) {
|
||||
stream() << " }\n";
|
||||
}
|
||||
|
||||
stream() << "}\n";
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGSOURCEGENERATOR_H
|
||||
#define KCONFIGSOURCEGENERATOR_H
|
||||
|
||||
#include "KConfigCodeGeneratorBase.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class KConfigParameters;
|
||||
class CfgEntry;
|
||||
class QTextStream;
|
||||
struct ParseResult;
|
||||
|
||||
class KConfigSourceGenerator : public KConfigCodeGeneratorBase
|
||||
{
|
||||
public:
|
||||
KConfigSourceGenerator(const QString &inputFile, const QString &baseDir, const KConfigParameters ¶meters, ParseResult &parseResult);
|
||||
|
||||
void start() override;
|
||||
|
||||
private:
|
||||
// Those are fairly self contained functions.
|
||||
void createHeaders();
|
||||
void createPrivateDPointerImplementation();
|
||||
void createSingletonImplementation();
|
||||
void createPreamble();
|
||||
void createDestructor();
|
||||
void createConstructorParameterList();
|
||||
void createParentConstructorCall();
|
||||
void createInitializerList();
|
||||
void createDefaultValueGetterSetter();
|
||||
void createNonModifyingSignalsHelper();
|
||||
void createSignalFlagsHandler();
|
||||
|
||||
// Constructor related methods
|
||||
// the `do` methods have related helper functions that are only related
|
||||
// to it. So we can break the function into many smaller ones and create
|
||||
// logic inside of the `do` function.
|
||||
void doConstructor();
|
||||
void createEnums(const CfgEntry *entry);
|
||||
void createNormalEntry(const CfgEntry *entry, const QString &key);
|
||||
void createIndexedEntry(const CfgEntry *entry, const QString &key);
|
||||
void handleCurrentGroupChange(const CfgEntry *entry);
|
||||
|
||||
void doGetterSetterDPointerMode();
|
||||
void createGetterDPointerMode(const CfgEntry *entry);
|
||||
void createImmutableGetterDPointerMode(const CfgEntry *entry);
|
||||
void createSetterDPointerMode(const CfgEntry *entry);
|
||||
void createItemGetterDPointerMode(const CfgEntry *entry);
|
||||
|
||||
private:
|
||||
QString mCurrentGroup;
|
||||
QStringList mConfigGroupList; // keeps track of generated KConfigGroup;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "KConfigXmlParser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDomAttr>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <iostream>
|
||||
// TODO: Move preprocessDefault to Header / CPP implementation.
|
||||
// it makes no sense for a parser to process those values and generate code.
|
||||
|
||||
static void preProcessDefault(QString &defaultValue,
|
||||
const QString &name,
|
||||
const QString &type,
|
||||
const CfgEntry::Choices &cfgChoices,
|
||||
QString &code,
|
||||
const KConfigParameters &cfg)
|
||||
{
|
||||
if (type == QLatin1String("String") && !defaultValue.isEmpty()) {
|
||||
defaultValue = literalString(defaultValue);
|
||||
|
||||
} else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) {
|
||||
defaultValue = literalString(defaultValue);
|
||||
} else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) {
|
||||
// Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did.
|
||||
defaultValue = QLatin1String("QUrl::fromUserInput( %1)").arg(literalString(defaultValue));
|
||||
} else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) {
|
||||
QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
|
||||
if (!code.isEmpty()) {
|
||||
cpp << '\n';
|
||||
}
|
||||
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << " QList<QUrl> default" << name << ";\n";
|
||||
} else {
|
||||
cpp << " QStringList default" << name << ";\n";
|
||||
}
|
||||
const QStringList defaults = defaultValue.split(QLatin1Char(','));
|
||||
for (const auto &val : defaults) {
|
||||
cpp << " default" << name << ".append( ";
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << "QUrl::fromUserInput(";
|
||||
}
|
||||
cpp << "QString::fromUtf8( \"" << val << "\" ) ";
|
||||
if (type == QLatin1String("UrlList")) {
|
||||
cpp << ") ";
|
||||
}
|
||||
cpp << ");\n";
|
||||
}
|
||||
defaultValue = QLatin1String("default") + name;
|
||||
|
||||
} else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) {
|
||||
static const QRegularExpression colorRe(QRegularExpression::anchoredPattern(QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?")));
|
||||
|
||||
if (colorRe.match(defaultValue).hasMatch()) {
|
||||
defaultValue = QLatin1String("QColor( %1 )").arg(defaultValue);
|
||||
} else {
|
||||
defaultValue = QLatin1String("QColor( \"%1\" )").arg(defaultValue);
|
||||
}
|
||||
|
||||
} else if (type == QLatin1String("Enum")) {
|
||||
for (const auto &choice : cfgChoices.choices) {
|
||||
if (choice.name == defaultValue) {
|
||||
if (cfg.globalEnums && cfgChoices.name().isEmpty()) {
|
||||
defaultValue.prepend(cfgChoices.prefix);
|
||||
} else {
|
||||
defaultValue.prepend(enumTypeQualifier(name, cfgChoices) + cfgChoices.prefix);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type == QLatin1String("IntList")) {
|
||||
QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append);
|
||||
if (!code.isEmpty()) {
|
||||
cpp << '\n';
|
||||
}
|
||||
|
||||
cpp << " QList<int> default" << name << ";\n";
|
||||
if (!defaultValue.isEmpty()) {
|
||||
const QStringList defaults = defaultValue.split(QLatin1Char(','));
|
||||
for (const auto &defaultVal : defaults) {
|
||||
cpp << " default" << name << ".append( " << defaultVal << " );\n";
|
||||
}
|
||||
}
|
||||
defaultValue = QLatin1String("default") + name;
|
||||
}
|
||||
}
|
||||
|
||||
static QString dumpNode(const QDomNode &node)
|
||||
{
|
||||
QString msg;
|
||||
QTextStream s(&msg, QIODevice::WriteOnly);
|
||||
node.save(s, 0);
|
||||
|
||||
msg = msg.simplified();
|
||||
if (msg.length() > 40) {
|
||||
return msg.left(37) + QLatin1String("...");
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e)
|
||||
{
|
||||
readEntry.param = e.attribute(QStringLiteral("name"));
|
||||
readEntry.paramType = e.attribute(QStringLiteral("type"));
|
||||
|
||||
if (readEntry.param.isEmpty()) {
|
||||
std::cerr << "Parameter must have a name: " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (readEntry.paramType.isEmpty()) {
|
||||
std::cerr << "Parameter must have a type: " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) {
|
||||
bool ok;
|
||||
readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok);
|
||||
if (!ok) {
|
||||
std::cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << qPrintable(dumpNode(e)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
} else if (readEntry.paramType == QLatin1String("Enum")) {
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("values")) {
|
||||
for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) {
|
||||
if (e3.tagName() == QLatin1String("value")) {
|
||||
readEntry.paramValues.append(e3.text());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (readEntry.paramValues.isEmpty()) {
|
||||
std::cerr << "No values specified for parameter '" << qPrintable(readEntry.param) << "'." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
readEntry.paramMax = readEntry.paramValues.count() - 1;
|
||||
} else {
|
||||
std::cerr << "Parameter '" << qPrintable(readEntry.param) << "' has type " << qPrintable(readEntry.paramType)
|
||||
<< " but must be of type int, uint or Enum." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
Q_UNUSED(readEntry)
|
||||
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
if (e.attribute(QStringLiteral("param")).isEmpty()) {
|
||||
if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e)
|
||||
{
|
||||
QList<CfgEntry::Choice> chlist;
|
||||
const auto choiceNameRegex = QRegularExpression(QStringLiteral("\\w+"));
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() != QLatin1String("choice")) {
|
||||
continue;
|
||||
}
|
||||
CfgEntry::Choice choice;
|
||||
choice.name = e2.attribute(QStringLiteral("name"));
|
||||
if (choice.name.isEmpty()) {
|
||||
std::cerr << "Tag <choice> requires attribute 'name'." << std::endl;
|
||||
} else if (!choiceNameRegex.match(choice.name).hasMatch()) {
|
||||
std::cerr << "Tag <choice> attribute 'name' must be compatible with Enum naming. name was '" << qPrintable(choice.name)
|
||||
<< "'. You can use attribute 'value' to pass any string as the choice value." << std::endl;
|
||||
}
|
||||
choice.val = e2.attribute(QStringLiteral("value"));
|
||||
for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) {
|
||||
if (e3.tagName() == QLatin1String("label")) {
|
||||
choice.label = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
if (e3.tagName() == QLatin1String("tooltip")) {
|
||||
choice.toolTip = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
if (e3.tagName() == QLatin1String("whatsthis")) {
|
||||
choice.whatsThis = e3.text();
|
||||
choice.context = e3.attribute(QStringLiteral("context"));
|
||||
}
|
||||
}
|
||||
chlist.append(choice);
|
||||
}
|
||||
|
||||
QString name = e.attribute(QStringLiteral("name"));
|
||||
QString prefix = e.attribute(QStringLiteral("prefix"));
|
||||
|
||||
readEntry.choices = CfgEntry::Choices(chlist, name, prefix);
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
QString tag = e.tagName();
|
||||
if (tag == QLatin1String("label")) {
|
||||
readEntry.label = e.text();
|
||||
readEntry.labelContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("tooltip")) {
|
||||
readEntry.toolTip = e.text();
|
||||
readEntry.toolTipContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("whatsthis")) {
|
||||
readEntry.whatsThis = e.text();
|
||||
readEntry.whatsThisContext = e.attribute(QStringLiteral("context"));
|
||||
} else if (tag == QLatin1String("min")) {
|
||||
readEntry.min = e.text();
|
||||
} else if (tag == QLatin1String("max")) {
|
||||
readEntry.max = e.text();
|
||||
} else if (tag == QLatin1String("code")) {
|
||||
readEntry.code = e.text();
|
||||
} else if (tag == QLatin1String("parameter")) {
|
||||
readParameterFromEntry(readEntry, e);
|
||||
} else if (tag == QLatin1String("default")) {
|
||||
if (e.attribute(QStringLiteral("param")).isEmpty()) {
|
||||
readEntry.defaultValue = e.text();
|
||||
}
|
||||
} else if (tag == QLatin1String("choices")) {
|
||||
readChoicesFromEntry(readEntry, e);
|
||||
} else if (tag == QLatin1String("emit")) {
|
||||
Signal signal;
|
||||
signal.name = e.attribute(QStringLiteral("signal"));
|
||||
readEntry.signalList.append(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::createChangedSignal(CfgEntry &readEntry)
|
||||
{
|
||||
if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) {
|
||||
Signal s;
|
||||
s.name = changeSignalName(readEntry.name);
|
||||
s.modify = true;
|
||||
readEntry.signalList.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
bool nameIsEmpty = readEntry.name.isEmpty();
|
||||
if (nameIsEmpty && readEntry.key.isEmpty()) {
|
||||
std::cerr << "Entry must have a name or a key: " << qPrintable(dumpNode(element)) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (readEntry.key.isEmpty()) {
|
||||
readEntry.key = readEntry.name;
|
||||
}
|
||||
|
||||
if (nameIsEmpty) {
|
||||
readEntry.name = readEntry.key;
|
||||
readEntry.name.remove(QLatin1Char(' '));
|
||||
} else if (readEntry.name.contains(QLatin1Char(' '))) {
|
||||
std::cout << "Entry '" << qPrintable(readEntry.name) << "' contains spaces! <name> elements can not contain spaces!" << std::endl;
|
||||
readEntry.name.remove(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
if (readEntry.name.contains(QStringLiteral("$("))) {
|
||||
if (readEntry.param.isEmpty()) {
|
||||
std::cerr << "Name may not be parameterized: " << qPrintable(readEntry.name) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (!readEntry.param.isEmpty()) {
|
||||
std::cerr << "Name must contain '$(" << qPrintable(readEntry.param) << ")': " << qPrintable(readEntry.name) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element)
|
||||
{
|
||||
if (readEntry.param.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Adjust name
|
||||
readEntry.paramName = readEntry.name;
|
||||
|
||||
readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')'));
|
||||
// Lookup defaults for indexed entries
|
||||
for (int i = 0; i <= readEntry.paramMax; i++) {
|
||||
readEntry.paramDefaultValues.append(QString());
|
||||
}
|
||||
|
||||
for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
|
||||
QString tag = e.tagName();
|
||||
if (tag != QLatin1String("default")) {
|
||||
continue;
|
||||
}
|
||||
QString index = e.attribute(QStringLiteral("param"));
|
||||
if (index.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int i = index.toInt(&ok);
|
||||
if (!ok) {
|
||||
i = readEntry.paramValues.indexOf(index);
|
||||
if (i == -1) {
|
||||
std::cerr << "Index '" << qPrintable(index) << "' for default value is unknown." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((i < 0) || (i > readEntry.paramMax)) {
|
||||
std::cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QString tmpDefaultValue = e.text();
|
||||
|
||||
if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) {
|
||||
preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg);
|
||||
}
|
||||
|
||||
readEntry.paramDefaultValues[i] = tmpDefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
CfgEntry *KConfigXmlParser::parseEntry(const QString &group, const QString &parentGroup, const QDomElement &element)
|
||||
{
|
||||
CfgEntry readEntry;
|
||||
readEntry.type = element.attribute(QStringLiteral("type"));
|
||||
readEntry.name = element.attribute(QStringLiteral("name"));
|
||||
readEntry.key = element.attribute(QStringLiteral("key"));
|
||||
readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");
|
||||
;
|
||||
readEntry.group = group;
|
||||
readEntry.parentGroup = parentGroup;
|
||||
|
||||
const bool nameIsEmpty = readEntry.name.isEmpty();
|
||||
|
||||
readGroupElements(readEntry, element);
|
||||
|
||||
validateNameAndKey(readEntry, element);
|
||||
|
||||
if (readEntry.label.isEmpty()) {
|
||||
readEntry.label = readEntry.key;
|
||||
}
|
||||
|
||||
if (readEntry.type.isEmpty()) {
|
||||
readEntry.type = QStringLiteral("String"); // XXX : implicit type might be bad
|
||||
}
|
||||
|
||||
readParamDefaultValues(readEntry, element);
|
||||
|
||||
if (!mValidNameRegexp.match(readEntry.name).hasMatch()) {
|
||||
if (nameIsEmpty) {
|
||||
std::cerr << "The key '" << qPrintable(readEntry.key)
|
||||
<< "' can not be used as name for the entry because "
|
||||
"it is not a valid name. You need to specify a valid name for this entry."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not a valid name for an entry." << std::endl;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (mAllNames.contains(readEntry.name)) {
|
||||
if (nameIsEmpty) {
|
||||
std::cerr << "The key '" << qPrintable(readEntry.key)
|
||||
<< "' can not be used as name for the entry because "
|
||||
"it does not result in a unique name. You need to specify a unique name for this entry."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not unique." << std::endl;
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mAllNames.append(readEntry.name);
|
||||
|
||||
if (!hasDefaultCode(readEntry, element)) {
|
||||
// TODO: Move all the options to CfgEntry.
|
||||
preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg);
|
||||
}
|
||||
|
||||
// TODO: Try to Just return the CfgEntry we populated instead of
|
||||
// creating another one to fill the code.
|
||||
CfgEntry *result = new CfgEntry();
|
||||
result->group = readEntry.group;
|
||||
result->parentGroup = readEntry.parentGroup;
|
||||
result->type = readEntry.type;
|
||||
result->key = readEntry.key;
|
||||
result->name = readEntry.name;
|
||||
result->labelContext = readEntry.labelContext;
|
||||
result->label = readEntry.label;
|
||||
result->toolTipContext = readEntry.toolTipContext;
|
||||
result->toolTip = readEntry.toolTip;
|
||||
result->whatsThisContext = readEntry.whatsThisContext;
|
||||
result->whatsThis = readEntry.whatsThis;
|
||||
result->code = readEntry.code;
|
||||
result->defaultValue = readEntry.defaultValue;
|
||||
result->choices = readEntry.choices;
|
||||
result->signalList = readEntry.signalList;
|
||||
result->hidden = readEntry.hidden;
|
||||
|
||||
if (!readEntry.param.isEmpty()) {
|
||||
result->param = readEntry.param;
|
||||
result->paramName = readEntry.paramName;
|
||||
result->paramType = readEntry.paramType;
|
||||
result->paramValues = readEntry.paramValues;
|
||||
result->paramDefaultValues = readEntry.paramDefaultValues;
|
||||
result->paramMax = readEntry.paramMax;
|
||||
}
|
||||
result->min = readEntry.min;
|
||||
result->max = readEntry.max;
|
||||
createChangedSignal(*result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Change the name of the config variable.
|
||||
KConfigXmlParser::KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName)
|
||||
: cfg(cfg)
|
||||
, mInputFileName(inputFileName)
|
||||
{
|
||||
mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*")));
|
||||
}
|
||||
|
||||
void KConfigXmlParser::start()
|
||||
{
|
||||
QFile input(mInputFileName);
|
||||
if (!input.open(QIODevice::ReadOnly)) {
|
||||
qFatal("Could not open input file: %s", qUtf8Printable(mInputFileName));
|
||||
}
|
||||
QDomDocument doc;
|
||||
const QDomDocument::ParseResult parseResult = doc.setContent(&input);
|
||||
if (!parseResult) {
|
||||
std::cerr << "Unable to load document." << std::endl;
|
||||
std::cerr << "Parse error in " << qPrintable(mInputFileName) << ", line " << parseResult.errorLine << ", col " << parseResult.errorColumn << ": "
|
||||
<< qPrintable(parseResult.errorMessage) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QDomElement cfgElement = doc.documentElement();
|
||||
if (cfgElement.isNull()) {
|
||||
std::cerr << "No document in kcfg file" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) {
|
||||
QString tag = element.tagName();
|
||||
|
||||
if (tag == QLatin1String("include")) {
|
||||
readIncludeTag(element);
|
||||
} else if (tag == QLatin1String("kcfgfile")) {
|
||||
readKcfgfileTag(element);
|
||||
} else if (tag == QLatin1String("group")) {
|
||||
readGroupTag(element);
|
||||
} else if (tag == QLatin1String("signal")) {
|
||||
readSignalTag(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult KConfigXmlParser::getParseResult() const
|
||||
{
|
||||
return mParseResult;
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readIncludeTag(const QDomElement &e)
|
||||
{
|
||||
QString includeFile = e.text();
|
||||
if (!includeFile.isEmpty()) {
|
||||
mParseResult.includes.append(includeFile);
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readGroupTag(const QDomElement &e)
|
||||
{
|
||||
QString group = e.attribute(QStringLiteral("name"));
|
||||
if (group.isEmpty()) {
|
||||
std::cerr << "Group without name" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const QString parentGroup = e.attribute(QStringLiteral("parentGroupName"));
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() != QLatin1String("entry")) {
|
||||
continue;
|
||||
}
|
||||
CfgEntry *entry = parseEntry(group, parentGroup, e2);
|
||||
if (entry) {
|
||||
mParseResult.entries.append(entry);
|
||||
} else {
|
||||
std::cerr << "Can not parse entry." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readKcfgfileTag(const QDomElement &e)
|
||||
{
|
||||
mParseResult.cfgFileName = e.attribute(QStringLiteral("name"));
|
||||
mParseResult.cfgStateConfig = e.attribute(QStringLiteral("stateConfig")).toLower() == QLatin1String("true");
|
||||
mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true");
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("parameter")) {
|
||||
Param p;
|
||||
p.name = e2.attribute(QStringLiteral("name"));
|
||||
p.type = e2.attribute(QStringLiteral("type"));
|
||||
if (p.type.isEmpty()) {
|
||||
p.type = QStringLiteral("String");
|
||||
}
|
||||
mParseResult.parameters.append(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigXmlParser::readSignalTag(const QDomElement &e)
|
||||
{
|
||||
QString signalName = e.attribute(QStringLiteral("name"));
|
||||
if (signalName.isEmpty()) {
|
||||
std::cerr << "Signal without name." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
Signal theSignal;
|
||||
theSignal.name = signalName;
|
||||
|
||||
for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) {
|
||||
if (e2.tagName() == QLatin1String("argument")) {
|
||||
Param argument;
|
||||
argument.type = e2.attribute(QStringLiteral("type"));
|
||||
if (argument.type.isEmpty()) {
|
||||
std::cerr << "Signal argument without type." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
argument.name = e2.text();
|
||||
theSignal.arguments.append(argument);
|
||||
} else if (e2.tagName() == QLatin1String("label")) {
|
||||
theSignal.label = e2.text();
|
||||
}
|
||||
}
|
||||
|
||||
mParseResult.signalList.append(theSignal);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Tomaz Cananbrava <tcanabrava@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGXMLPARSER_H
|
||||
#define KCONFIGXMLPARSER_H
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigParameters.h"
|
||||
|
||||
/* This parses the contents of a Xml file into a ParseResult Structure,
|
||||
* It also fails hard:
|
||||
* If start() succeeds, you can use the result,
|
||||
* if start() fails, the program aborts with an error message so there's
|
||||
* no possibility of generating incorrect code information.
|
||||
*/
|
||||
class KConfigXmlParser
|
||||
{
|
||||
public:
|
||||
KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName);
|
||||
|
||||
// Start the parser and reads the contents of the inputFileName into the ParseResult Structure
|
||||
void start();
|
||||
|
||||
// Get the result of the parse
|
||||
ParseResult getParseResult() const;
|
||||
|
||||
private:
|
||||
// creates a `somethingChanged` signal for every property
|
||||
void createChangedSignal(CfgEntry &readEntry);
|
||||
|
||||
void validateNameAndKey(CfgEntry &readEntry, const QDomElement &element);
|
||||
|
||||
// TODO: Use std::optional and CfgEntry (without heap allocation) for this function
|
||||
// *or* fail hard if the parse fails.
|
||||
CfgEntry *parseEntry(const QString &group, const QString &parentGroup, const QDomElement &element);
|
||||
|
||||
// Steps
|
||||
void readIncludeTag(const QDomElement &element);
|
||||
void readGroupTag(const QDomElement &element);
|
||||
void readKcfgfileTag(const QDomElement &element);
|
||||
void readSignalTag(const QDomElement &element);
|
||||
|
||||
// Those are the Entries in the Xml, that represent a parameter within the <group> </group> tag.
|
||||
void readParameterFromEntry(CfgEntry &entry, const QDomElement &element);
|
||||
bool hasDefaultCode(CfgEntry &entry, const QDomElement &element);
|
||||
void readChoicesFromEntry(CfgEntry &entry, const QDomElement &element);
|
||||
void readGroupElements(CfgEntry &entry, const QDomElement &element);
|
||||
void readParamDefaultValues(CfgEntry &entry, const QDomElement &element);
|
||||
|
||||
private:
|
||||
ParseResult mParseResult;
|
||||
KConfigParameters cfg;
|
||||
QString mInputFileName;
|
||||
QStringList mAllNames;
|
||||
QRegularExpression mValidNameRegexp;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,560 @@
|
||||
/**
|
||||
\page kconfig_compiler The KDE Configuration Compiler
|
||||
|
||||
kconfig_compiler generates C++ source code from an XML file containing
|
||||
information about configuration options (.kcfg) and a file that provides
|
||||
the code generation options (.kcfgc) The generated class is based on
|
||||
KConfigSkeleton and provides an API for the application to access its
|
||||
configuration data.
|
||||
|
||||
The generated C++ source code is output to a .h and a .cpp file, whose base
|
||||
name is the same as that of the .kcfgc file.
|
||||
|
||||
\section kcfg_format The .kcfg XML file format: description of the configuration skeleton
|
||||
|
||||
The structure of the .kcfg file is described by its DTD kcfg.xsd.
|
||||
|
||||
The \<kcfgfile\> tag may contain either the "name" attribute, which should be the name
|
||||
of the configuration file described, or the "arg" attribute, which, if set to
|
||||
"true", will allow you to pass the KSharedConfig::Ptr object to use.
|
||||
|
||||
If neither "name" nor "arg" is set, the default configuration file
|
||||
("\<appname\>rc") will be used.
|
||||
|
||||
The \<include\> tags are optional and may contain C++ header files that
|
||||
are needed to compile the code needed to compute default values. To generate
|
||||
a \#include "..." statement instead of \#include \<...\>, enclose the header
|
||||
file name in double quotes (e.g. \<include\>"header.h"\</include\>).
|
||||
|
||||
The remaining entries in the XML file are grouped by the tag \<group\>
|
||||
which describes the corresponding group in the configuration file.
|
||||
|
||||
The individual entries must have at least a name or a key. The key is used
|
||||
as the key in the config file, while the name is used to create accessor and
|
||||
modifier functions. If \<key\> is given, but not \<name\>, the name is
|
||||
constructed by removing all spaces from \<key\>. If \<name\> is given, but
|
||||
not \<key\>, the key is the same as \<name\>.
|
||||
|
||||
An entry must also have a type. The list of allowable types is
|
||||
specified in the DTD and loosely follows the list of types supported
|
||||
by the QVariant with exception of the clearly binary types
|
||||
(e.g. Pixmap, Image...) which are not supported. Besides those basic
|
||||
types the following special types are supported:
|
||||
|
||||
<dl>
|
||||
<dt>Path</dt>
|
||||
<dd>
|
||||
This is a string that is specially treated as a file-path.
|
||||
In particular paths in the home directory are prefixed with $HOME in
|
||||
when being stored in the configuration file.
|
||||
</dd>
|
||||
|
||||
<dt>Enum</dt>
|
||||
<dd>
|
||||
This indicates an enumeration. The possible enum values and optional
|
||||
enum name should be provided via the \<choices\> tag. Enum values are
|
||||
accessed as integers by the application but stored as strings in the
|
||||
configuration file. This makes it possible to add more values at a later
|
||||
date without breaking compatibility.
|
||||
</dd>
|
||||
|
||||
<dt>IntList</dt>
|
||||
<dd>
|
||||
This indicates a list of integers. This information is provided
|
||||
to the application as QList<int>. Useful for storing QSplitter
|
||||
geometries.
|
||||
</dd>
|
||||
|
||||
<dt>Color</dt>
|
||||
<dd>
|
||||
Isn't a special type but has special input. It is generated as QColor.
|
||||
Any valid input to QColor(QString) can be used (hex or SVG keyword notation)
|
||||
as well as a special format r,g,b,a where the a denotes the alpha channel and
|
||||
may be omitted.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
An entry can optionally have a default value which is used as default when
|
||||
the value isn't specified in any config file. Default values are interpreted
|
||||
as literal constant values. If a default value needs to be computed
|
||||
or if it needs to be obtained from a function call, the \<default\> tag
|
||||
should contain the code="true" attribute. The contents of the \<default\>
|
||||
tag is then considered to be a C++ expression. Note that in this case you
|
||||
might have to add an \<include\> tag as described above, or a
|
||||
SourceIncludeFiles entry in the .kcfgc file as described below, so that the
|
||||
code which computes the default value can be compiled.
|
||||
|
||||
Additional code for computing default values can be provided outside any
|
||||
entry definition via the \<code\> tag. The contents of the \<code\> tag is
|
||||
inserted as-is. A typical use for this is to compute a common default value
|
||||
which can then be referenced by multiple entries that follow.
|
||||
|
||||
\section kcfgc_format The .kcfgc file format: code generation options
|
||||
|
||||
The options for generating the C++ sources are read from the file with the
|
||||
extension .kcfgc. To generate a class add the corresponding kcfgc file to the
|
||||
SOURCES line in the Makefile.am.
|
||||
|
||||
The following options are read from the kcfgc file:
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>File=\<string\></dt>
|
||||
<dd>
|
||||
Default: \<programname\>.kcfg \n
|
||||
Name of kcfg file containing the options the class is generated for
|
||||
<!-- what? -->
|
||||
</dd>
|
||||
|
||||
<dt>HeaderExtension=\<string\></dt>
|
||||
<dd>
|
||||
Default: h \n
|
||||
Since KF 5.57 \n
|
||||
Extension to use for the name of the generated C++ header files.
|
||||
</dd>
|
||||
|
||||
<dt>NameSpace=\<string\></dt>
|
||||
<dd>
|
||||
Optional namespace for generated class
|
||||
</dd>
|
||||
|
||||
<dt>ClassName=\<string\></dt>
|
||||
<dd>
|
||||
Name of generated class (required)
|
||||
</dd>
|
||||
|
||||
<dt>Inherits=\<string\></dt>
|
||||
<dd>
|
||||
Default: KConfigSkeleton \n
|
||||
Class the generated class inherits from.
|
||||
|
||||
This class must inherit KConfigSkeleton and must provide a default
|
||||
constructor (kcfgfile not specified), a constructor taking a
|
||||
QString argument (kcfgfile with "name" attribute) and a constructor
|
||||
taking a KSharedConfig::Ptr as argument (kcfgfile with "arg" attribute).
|
||||
Please refer to the documentation of KConfigSkeleton.
|
||||
</dd>
|
||||
|
||||
<dt>Visibility=\<string\></dt>
|
||||
<dd>
|
||||
Inserts visibility directive (for example KDE_EXPORT) between
|
||||
"class" keyword and class name in header file
|
||||
</dd>
|
||||
|
||||
<dt>Singleton=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Generated class is a singleton.
|
||||
</dd>
|
||||
|
||||
<dt>CustomAdditions=\<bool\></dt>
|
||||
<dd>
|
||||
<!-- what? -->
|
||||
</dd>
|
||||
|
||||
<dt>MemberVariables=\<string\></dt>
|
||||
<dd>
|
||||
Values: public, protected, private, dpointer \n
|
||||
Default: private \n
|
||||
C++ access modifier used for member variables holding the configuration
|
||||
values
|
||||
</dd>
|
||||
|
||||
<dt>IncludeFiles=\<string\>, \<string\> ...</dt>
|
||||
<dd>
|
||||
Names of files to be included in the header of the generated class.
|
||||
|
||||
Enclose a file name in (escaped) double quotes to generate
|
||||
\#include "..." instead of \#include \<...\>.
|
||||
</dd>
|
||||
|
||||
<dt>SourceIncludeFiles=\<string\>, \<string\> ...</dt>
|
||||
<dd>
|
||||
Names of files to be included in the source file of the generated class.
|
||||
|
||||
Enclose a file name in (escaped) double quotes to generate
|
||||
\#include "..." instead of \#include \<...\>.
|
||||
</dd>
|
||||
|
||||
<dt>Mutators=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, mutator functions for all configuration options are generated.
|
||||
If false, no mutator functions are generated. If a list is provided,
|
||||
mutator functions are generated for the options that are listed.
|
||||
</dd>
|
||||
|
||||
<dt>Notifiers=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, entries will be written with the <b>Notify</b> flag enabled,
|
||||
so changes can be detected using <b>KConfigWatcher</b>.
|
||||
If a list is provided, the options that are listed are written with
|
||||
said flag.
|
||||
</dd>
|
||||
|
||||
<dt>DefaultValueGetters=\<value\></dt>
|
||||
<dd>
|
||||
Values: true, false or a comma separated list of options \n
|
||||
Default: false \n
|
||||
If true, functions to return the default value of all configuration options
|
||||
are generated. If false, no default value functions are generated. If a list
|
||||
is provided, default value functions are generated for the options that are listed.
|
||||
</dd>
|
||||
|
||||
<dt>ItemAccessors=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Generate accessor functions for the KConfigSkeletonItem objects
|
||||
corresponding to the configuration options. If <b>SetUserTexts</b> is set,
|
||||
<b>ItemAccessors</b> also has to be set.
|
||||
</dd>
|
||||
|
||||
<dt>SetUserTexts=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
Set the label and whatthis texts of the items from the kcfg file.
|
||||
If <b>SetUserTexts</b> is set, <b>ItemAccessors</b> also has to be set.
|
||||
</dd>
|
||||
|
||||
<dt>GlobalEnums=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true all choices of Enum items will be created in the global
|
||||
scope of the generated class. If set to false, each Enum item whose enum is not
|
||||
explicitly named will get its own namespace for its choices.
|
||||
</dd>
|
||||
|
||||
<dt>UseEnumTypes=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, all Enum items whose enums are named will use enum types for
|
||||
the return value of accessor functions and for the parameter of mutator
|
||||
functions. This eliminates the need to cast accessor return values to the enum
|
||||
type if you want to use the enum type in your own code. If set to false,
|
||||
accessor return values and mutator parameters will be of type int.
|
||||
</dd>
|
||||
|
||||
<dt>ForceStringFilename=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, forces the first parameter of the generated class
|
||||
to be a QString when using an argument for the filename.
|
||||
This is useful to specify at runtime the filename of the configuration class.
|
||||
</dd>
|
||||
|
||||
<dt>GenerateProperties=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, a Q_PROPERTY will be generated for each member variable holding a
|
||||
configuration value and the Q_OBJECT macro will be added to the generated class.
|
||||
Note that you will also need to pass the GENERATE_MOC option to the
|
||||
kconfig_add_kcfg_files macro.
|
||||
</dd>
|
||||
|
||||
|
||||
<dt>ParentInConstructor=\<bool\></dt>
|
||||
<dd>
|
||||
Default: false \n
|
||||
If set to true, the generated constructor will take an additional QObject*
|
||||
parameter that will be used as the object's parent.
|
||||
This is useful when working with KQuickAddons::ManagedConfigModule
|
||||
to set it as the parent of the generated class to allow automatic
|
||||
settings discovery and handle the deallocation.
|
||||
Note this parameter is incompatible with <b>Singleton</b> set to true.
|
||||
</dd>
|
||||
|
||||
<dt>TranslationSystem=\<string\></dt>
|
||||
<dd>
|
||||
Default: qt \n
|
||||
Set the translation system for label, tooltip and whatsthis text in generated KConfigSkeleton.
|
||||
Set the value to <b>kde</b> to use the KDE Framework translation system, see KI18n.
|
||||
</dd>
|
||||
|
||||
<dt>TranslationDomain=\<value\></dt>
|
||||
<dd>
|
||||
When <b>TranslationSystem=kde</b> is set, allow to specify the domain in which to look for translations.
|
||||
</dd>
|
||||
|
||||
<dt>QmlRegistration=\<bool></dt>
|
||||
<dd>
|
||||
Default: false\n
|
||||
If set to true the generated code will use the QML_ELEMENT macro to register the type to QML.
|
||||
If Singleton is also true it will be registered as a QML singleton using QML_SINGLETON
|
||||
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
\section entry_options Advanced entry options
|
||||
|
||||
There are several possibilities to parameterize entries.
|
||||
|
||||
\subsection parametrized_entries Parameterized entries
|
||||
|
||||
An entry can be parameterized using a fixed range parameter specified with
|
||||
the \<parameter\> tag. Such parameter can either be an Enum or an int. An Enum
|
||||
parameter should specify the possible enumeration values with the \<choices\>
|
||||
tag. An int parameter should specify its maximum value. Its minimum value
|
||||
is always 0.
|
||||
|
||||
A parameterized entry is expanded to a number of entries, one for each
|
||||
value in the parameter range. The name and key should contain a reference
|
||||
to the parameter in the form of $(parameter-name). When expanding the entries
|
||||
the $(parameter-name) part is replaced with the value of the parameter.
|
||||
In the case of an Enum parameter it is replaced with the name of the
|
||||
enumuration value. In the case of an int parameter it is replaced with
|
||||
the numeric value of the parameter.
|
||||
|
||||
Parameterized entries all share the same default value unless different
|
||||
default values have been specified for specific parameter values.
|
||||
This can be done with the param= attribute of the \<default\>. When a
|
||||
param attribute is specified the default value only applies to that
|
||||
particular parameter value.
|
||||
|
||||
Example 1:
|
||||
\code{.xml}
|
||||
<entry name="Color$(ColorIndex)" type="Color" key="color_$(ColorIndex)">
|
||||
<parameter name="ColorIndex" type="Int" max="3"/>
|
||||
<default param="0">#ff0000</default>
|
||||
<default param="1">#00ff00</default>
|
||||
<default param="2">#0000ff</default>
|
||||
<default param="3">#ffff00</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The above describes 4 color configuration entries with the following defaults:
|
||||
|
||||
\verbatim
|
||||
color_0=#ff0000
|
||||
color_1=#00ff00
|
||||
color_2=#0000ff
|
||||
color_3=#ffff00
|
||||
\endverbatim
|
||||
|
||||
The configuration options will be accessible to the application via
|
||||
a QColor color(int ColorIndex) and a
|
||||
void setColor(int ColorIndex, const QColor &v) function.
|
||||
|
||||
Example 2:
|
||||
\code{.xml}
|
||||
<entry name="Sound$(SoundEvent)" type="String" key="sound_$(SoundEvent)">
|
||||
<parameter name="SoundEvent" type="Enum">
|
||||
<values>
|
||||
<value>Explosion</value>
|
||||
<value>Crash</value>
|
||||
<value>Missile</value>
|
||||
</values>
|
||||
</parameter>
|
||||
<default param="Explosion">boom.wav</default>
|
||||
<default param="Crash">crash.wav</default>
|
||||
<default param="Missile">missile.wav</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The above describes 3 string configuration entries with the following defaults:
|
||||
|
||||
\verbatim
|
||||
sound_Explosion=boom.wav
|
||||
sound_Crash=crash.wav
|
||||
sound_Missile=missile.wav
|
||||
\endverbatim
|
||||
|
||||
The configuration options will be accessible to the application via
|
||||
a QString sound(int SoundEvent) and a
|
||||
void setSound(int SoundEvent, const QString &v) function.
|
||||
|
||||
\subsection parametrized_groups Parameterized groups
|
||||
|
||||
A group name can be parametrized using a parameter given to the KConfigSkeleton
|
||||
instance (which means this feature cannot be used with singleton classes).
|
||||
|
||||
Example 1:
|
||||
\code{.xml}
|
||||
<kcfgfile name="testrc">
|
||||
<parameter name="groupname"/>
|
||||
</kcfgfile>
|
||||
<group name="$(groupname)">
|
||||
<entry key="Text" type="string">
|
||||
</entry>
|
||||
</group>
|
||||
\endcode
|
||||
|
||||
In this case passing "Group2" as the 'groupname' parameter to the generated class
|
||||
will make it use group "Group2" for the entry "Text".
|
||||
|
||||
By setting the stateConfig attribute of kcfgfile to "true", KSharedConfig::openStateConfig is used.
|
||||
This should be used when one stores volatile data, like window sizes or autocompletion texts.
|
||||
It is recommended to have at least two separate kcfg files for the different kinds of data.
|
||||
NOTE: This option is ignored when ForceStringFilename is set.
|
||||
|
||||
\subsection enums Enums
|
||||
|
||||
By default, if <b>GlobalEnums</b> is set to false, a separate named enum will be generated
|
||||
for each Enum entry. Since each enum is defined in a little enclosing class of its own,
|
||||
this allows the same Enum value names to be used in different enums. For example, the
|
||||
.kcfg entry
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices>
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont" value="Don't"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
will generate this public class containing the enum definition, inside the generated class:
|
||||
|
||||
\code{.cpp}
|
||||
class EnumKeepData
|
||||
{
|
||||
public:
|
||||
enum type { Do, Dont, COUNT };
|
||||
};
|
||||
\endcode
|
||||
|
||||
Since 5.68, if present the <b>value</b> attribute will be used as the choice value written to the backend
|
||||
instead of the <b>name</b>, allowing to write text incompatible with enum naming.
|
||||
|
||||
Alternatively, if <b>GlobalEnums</b> is set to true, all Enum items are defined as
|
||||
unnamed enums in the global scope of the generated class. In this case, all Enum values
|
||||
must have different names to avoid clashes. However, you can use a 'prefix' argument
|
||||
in \<choices\> to prevent duplicate enum member names clashing. Using this, the Enum value
|
||||
names are prefixed in code with the string you specify. For example, if <b>GlobalEnums</b>
|
||||
is set to true, the .kcfg entry
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices prefix="Keep_">
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont" value="Don't"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
will generate config file entries of "KeepData=Do" and "KeepData=Dont", but the enum
|
||||
will be declared
|
||||
|
||||
\code{.cpp}
|
||||
enum { Keep_Do, Keep_Dont };
|
||||
\endcode
|
||||
|
||||
It is possible to specify your own name for a generated enum, by including a
|
||||
'name' parameter in \<choices\>. Just like unnamed enums, this enum will be defined in
|
||||
the global scope of the generated class (without any enclosing class of its own).
|
||||
Therefore the names of Enum values must be unique across both unnamed enums (if
|
||||
<b>GlobalEnums</b> is set to true) and all specifically named enums.
|
||||
|
||||
An example of a specifically named enum:
|
||||
|
||||
\code{.xml}
|
||||
<entry name="KeepData" type="Enum">
|
||||
<choices name="Types">
|
||||
<choice name="Do"/>
|
||||
<choice name="Dont"/>
|
||||
</choices>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
which results in the following enum declaration, inside the generated class:
|
||||
|
||||
\code{.cpp}
|
||||
enum Types { Do, Dont };
|
||||
\endcode
|
||||
|
||||
It is also possible to specify the use of enums external to the generated class, by
|
||||
including the string "::" in the enum name - just ensure that it is sufficiently
|
||||
qualified to be unambiguous in use. To specify use of an unnamed enum, append a
|
||||
trailing "::". For example, to use the enum 'myEnum' defined in class ClassA, use
|
||||
either of
|
||||
|
||||
\code{.xml}
|
||||
<choices name="ClassA::myEnum">
|
||||
<choices name="::ClassA::myEnum">
|
||||
\endcode
|
||||
|
||||
To specify an unnamed enum in namespace ProgSpace, use
|
||||
|
||||
\code{.xml}
|
||||
<choices name="ProgSpace::">
|
||||
\endcode
|
||||
|
||||
To specify a top-level unnamed enum, use
|
||||
|
||||
\code{.xml}
|
||||
<choices name="::">
|
||||
\endcode
|
||||
|
||||
To specify the top-level enum 'anotherEnum', use
|
||||
|
||||
\code{.xml}
|
||||
|
||||
<choices name="::anotherEnum">
|
||||
\endcode
|
||||
|
||||
|
||||
\subsection notify_signals Notify signals
|
||||
|
||||
An entry can emit a signal when it gets changed. First of all, you must
|
||||
define a list of signals for the configuration class. The signal's name may be
|
||||
any legal identifier you wish. The \<argument\> tag allows you to specify arguments
|
||||
for the emitted signal. It supports all types as defined in
|
||||
the KConfigXT DTD. The argument value must specify the name, without spaces, of one
|
||||
of the entries defined in the .kcfg file.
|
||||
A signal definition can also contain a \<label\> tag which will be
|
||||
the documentation line in the generated file.
|
||||
|
||||
\code{.xml}
|
||||
<signal name="emoticonSettingsChanged" />
|
||||
|
||||
<signal name="styleChanged">
|
||||
<label>Tell when a complete style change.</label>
|
||||
<argument type="String">stylePath</argument>
|
||||
<argument type="String">StyleCSSVariant</argument>
|
||||
</signal>
|
||||
\endcode
|
||||
|
||||
After defining the signals, you must tell which signal to emit for the entry.
|
||||
A signal can be emitted by multiple entries. Also, you don't need to specify the arguments
|
||||
for a signal, the signal name will suffice.
|
||||
|
||||
\code{.xml}
|
||||
<entry key="stylePath" type="String">
|
||||
<label>Absolute path to a directory containing a Adium/Kopete chat window style.</label>
|
||||
<emit signal="styleChanged" />
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
The signal will be emitted when save() is called.
|
||||
|
||||
@warning
|
||||
You must not call save() in direct response to any notify signal.
|
||||
If you need to call save(),
|
||||
use a QueuedConnection to listen to the notify signal.
|
||||
|
||||
You can also listen to the generic configChanged() signal from KConfigSkeleton to
|
||||
notify your application about configuration changes.
|
||||
In that case you may also call save() in direct response.
|
||||
|
||||
Note that you will also need to pass the GENERATE_MOC option to the kconfig_add_kcfg_files macro.
|
||||
|
||||
\subsection translation_context Translation context
|
||||
|
||||
In the kcfg file you can specify the translation's context for \<tooltip\>, \<whatsthis\> and \<label\> element for an entry.
|
||||
|
||||
\code{.xml}
|
||||
<entry type="Bool" key="Auto Save">
|
||||
<label>Enable automatic saving of calendar</label>
|
||||
<whatsthis context="@info:whatsthis">Enable automatic saving of calendars to have calendars saved automatically.</whatsthis>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
\endcode
|
||||
|
||||
For more information on translation context and how to write good translatable text,
|
||||
please refer to https://api.kde.org/frameworks/ki18n/html/prg_guide.html
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
if ( @ARGV < 1 ) {
|
||||
print STDERR "Missing arg: filename\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$file = $ARGV[0];
|
||||
|
||||
$file =~ /^(.*)\.[^\.]*$/;
|
||||
$filebase = $1;
|
||||
|
||||
$header_extension = @ARGV > 1 ? $ARGV[1] : "h";
|
||||
$source_extension = @ARGV > 2 ? $ARGV[2] : "cpp";
|
||||
$file_h = "$filebase.$header_extension";
|
||||
$file_cpp = "$filebase.$source_extension";
|
||||
|
||||
$kcfgc = $file . "c";
|
||||
|
||||
$cmd = "./kconfig_compiler_kf6 $file $kcfgc";
|
||||
|
||||
#print "CMD $cmd\n";
|
||||
|
||||
if ( system( $cmd ) != 0 ) {
|
||||
print STDERR "Unable to run kconfig_compiler_kf6\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
checkfile( $file_h );
|
||||
checkfile( $file_cpp );
|
||||
|
||||
exit 0;
|
||||
|
||||
sub checkfile()
|
||||
{
|
||||
my $file = shift;
|
||||
|
||||
$file =~ /\/([^\/]*)$/;
|
||||
my $filename = $1;
|
||||
|
||||
print "Checking '$filename':\n";
|
||||
|
||||
my @ref;
|
||||
if ( !open( REF, "$file.ref" ) ) {
|
||||
print STDERR "Unable to open $file.ref\n";
|
||||
exit 1;
|
||||
}
|
||||
while( <REF> ) {
|
||||
push @ref, $_;
|
||||
}
|
||||
close REF;
|
||||
|
||||
if ( !open( READ, $filename ) ) {
|
||||
print STDERR "Unable to open $filename\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$error = 0;
|
||||
$i = 0;
|
||||
$line = 1;
|
||||
while( <READ> ) {
|
||||
$out = $_;
|
||||
$ref = @ref[$i++];
|
||||
|
||||
if ( $out ne $ref ) {
|
||||
$error++;
|
||||
print " Line $line: Expected : $ref";
|
||||
print " Line $line: Compiler output : $out";
|
||||
}
|
||||
|
||||
$line++;
|
||||
}
|
||||
|
||||
close READ;
|
||||
|
||||
if ( $error > 0 ) {
|
||||
print "\n FAILED: $error errors found.\n";
|
||||
if ( $error > 5 ) {
|
||||
system( "diff -u $file.ref $filename" );
|
||||
}
|
||||
exit 1;
|
||||
} else {
|
||||
print " OK\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- kcfg XSD v1.0 -->
|
||||
<xsd:schema
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:kcfg="http://www.kde.org/standards/kcfg/1.0"
|
||||
targetNamespace="http://www.kde.org/standards/kcfg/1.0"
|
||||
version="1.0"
|
||||
elementFormDefault="qualified" >
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2004 Frans Englich <frans.englich@telia.com>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
|
||||
Permission to use, copy, modify and distribute this DTD
|
||||
and its accompanying documentation for any purpose and without fee
|
||||
is hereby granted in perpetuity, provided that the above copyright
|
||||
notice and this paragraph appear in all copies. The copyright
|
||||
holders make no representation about the suitability of the DTD for
|
||||
any purpose. It is provided "as is" without expressed or implied
|
||||
warranty.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
A Schema for KDE's KConfigXT XML format. It is similar to the DTD
|
||||
found at:
|
||||
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.dtd
|
||||
|
||||
Documents valid against the Schema version are backwards compatible
|
||||
to the DTD. Validating against the Schema instead of the DTD is
|
||||
recommended, since the former provides better validation.
|
||||
|
||||
A document instance of this Schema should have a declaration
|
||||
looking like this:
|
||||
|
||||
<![CDATA[
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<!-- the content -->
|
||||
</kcfg>
|
||||
|
||||
]]>
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="kcfg">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="include" minOccurs="0" maxOccurs="unbounded" type="xsd:string"/>
|
||||
<xsd:element name="kcfgfile" minOccurs="0" maxOccurs="1" >
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="parameter" type="kcfg:parameter" minOccurs="0" maxOccurs="unbounded" />
|
||||
<!-- FIXME: Are really unbounded occurrences of parameter allowed? -->
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="optional"/>
|
||||
<xsd:attribute name="stateConfig" type="xsd:boolean" use="optional"/>
|
||||
<xsd:attribute name="arg" type="xsd:boolean" use="optional"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="signal" type="kcfg:signal" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xsd:element name="group" maxOccurs="unbounded" >
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="entry" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="parameter" minOccurs="0" type="kcfg:parameter"/>
|
||||
<xsd:element name="label" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="whatsthis" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="tooltip" minOccurs="0" type="kcfg:translatableString"/>
|
||||
<xsd:element name="choices" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="choice" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:all>
|
||||
<xsd:element minOccurs="0" name="label" type="kcfg:translatableString"/>
|
||||
<xsd:element minOccurs="0" name="whatsthis" type="kcfg:translatableString"/>
|
||||
<xsd:element minOccurs="0" name="tooltip" type="kcfg:translatableString"/>
|
||||
</xsd:all>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="value" use="optional" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="prefix" use="optional" type="xsd:string"/>
|
||||
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="code" minOccurs="0" type="kcfg:code"/>
|
||||
|
||||
<xsd:element name="default" maxOccurs="unbounded" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="optional" name="code" type="xsd:boolean"/>
|
||||
<xsd:attribute use="optional" name="param" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="min" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="code" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="max" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="code" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="emit" minOccurs="0">
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="signal" use="required" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="key" use="optional" type="xsd:string"/>
|
||||
<xsd:attribute name="hidden" use="optional" type="xsd:boolean"/>
|
||||
<xsd:attribute name="type" type="kcfg:datatype"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="parentGroupName" use="optional" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="datatype">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="String"/>
|
||||
<xsd:enumeration value="StringList"/>
|
||||
<xsd:enumeration value="Font"/>
|
||||
<xsd:enumeration value="Rect"/>
|
||||
<xsd:enumeration value="RectF"/>
|
||||
<xsd:enumeration value="Size"/>
|
||||
<xsd:enumeration value="SizeF"/>
|
||||
<xsd:enumeration value="Color"/>
|
||||
<xsd:enumeration value="Point"/>
|
||||
<xsd:enumeration value="PointF"/>
|
||||
<xsd:enumeration value="Int"/>
|
||||
<xsd:enumeration value="UInt"/>
|
||||
<xsd:enumeration value="Bool"/>
|
||||
<xsd:enumeration value="Double"/>
|
||||
<xsd:enumeration value="DateTime"/>
|
||||
<xsd:enumeration value="LongLong"/>
|
||||
<xsd:enumeration value="ULongLong"/>
|
||||
<xsd:enumeration value="IntList"/>
|
||||
<xsd:enumeration value="Enum"/>
|
||||
<xsd:enumeration value="Path"/>
|
||||
<xsd:enumeration value="PathList"/>
|
||||
<xsd:enumeration value="Password"/>
|
||||
<xsd:enumeration value="Url"/>
|
||||
<xsd:enumeration value="UrlList"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="parameter">
|
||||
<xsd:sequence>
|
||||
<xsd:element minOccurs="0" name="values">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" maxOccurs="unbounded" type="xsd:string"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" use="optional" type="kcfg:datatype" />
|
||||
<xsd:attribute name="max" use="optional" type="xsd:positiveInteger"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="code">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string"/>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="signal">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="label" minOccurs="0" type="xsd:string"/>
|
||||
<xsd:element name="argument" maxOccurs="unbounded" minOccurs="0" >
|
||||
<xsd:complexType>
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="required" name="type" type="kcfg:datatype"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="translatableString">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute use="optional" name="context" type="xsd:string"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,809 @@
|
||||
/*
|
||||
This file is part of KDE.
|
||||
|
||||
SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
|
||||
SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QCommandLineOption>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QDomAttr>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../core/kconfig_version.h"
|
||||
#include "KConfigCommonStructs.h"
|
||||
#include "KConfigHeaderGenerator.h"
|
||||
#include "KConfigParameters.h"
|
||||
#include "KConfigSourceGenerator.h"
|
||||
#include "KConfigXmlParser.h"
|
||||
|
||||
QString varName(const QString &n, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (!cfg.dpointer) {
|
||||
result = QChar::fromLatin1('m') + n;
|
||||
result[1] = result.at(1).toUpper();
|
||||
} else {
|
||||
result = n;
|
||||
result[0] = result.at(0).toLower();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString varPath(const QString &n, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (cfg.dpointer) {
|
||||
result = QLatin1String{"d->"} + varName(n, cfg);
|
||||
} else {
|
||||
result = varName(n, cfg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumName(const QString &n)
|
||||
{
|
||||
QString result = QLatin1String("Enum") + n;
|
||||
result[4] = result.at(4).toUpper();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumName(const QString &n, const CfgEntry::Choices &c)
|
||||
{
|
||||
QString result = c.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + n;
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumType(const CfgEntry *e, bool globalEnums)
|
||||
{
|
||||
QString result = e->choices.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + e->name;
|
||||
if (!globalEnums) {
|
||||
result += QLatin1String("::type");
|
||||
}
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
|
||||
{
|
||||
QString result = c.name();
|
||||
if (result.isEmpty()) {
|
||||
result = QLatin1String("Enum") + n + QLatin1String("::");
|
||||
result[4] = result.at(4).toUpper();
|
||||
} else if (c.external()) {
|
||||
result = c.externalQualifier();
|
||||
} else {
|
||||
result.clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString setFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("set") + n;
|
||||
result[3] = result.at(3).toUpper();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result = className + QLatin1String("::") + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString changeSignalName(const QString &n)
|
||||
{
|
||||
return n + QLatin1String{"Changed"};
|
||||
}
|
||||
|
||||
QString getDefaultFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("default%1Value").arg(n);
|
||||
result[7] = result.at(7).toUpper();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString getFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = n;
|
||||
result[0] = result.at(0).toLower();
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString immutableFunction(const QString &n, const QString &className)
|
||||
{
|
||||
QString result = QLatin1String("is") + n;
|
||||
result[2] = result.at(2).toUpper();
|
||||
result += QLatin1String{"Immutable"};
|
||||
|
||||
if (!className.isEmpty()) {
|
||||
result.prepend(className + QLatin1String("::"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void addQuotes(QString &s)
|
||||
{
|
||||
if (!s.startsWith(QLatin1Char('"'))) {
|
||||
s.prepend(QLatin1Char('"'));
|
||||
}
|
||||
if (!s.endsWith(QLatin1Char('"'))) {
|
||||
s.append(QLatin1Char('"'));
|
||||
}
|
||||
}
|
||||
|
||||
static QString quoteString(const QString &s)
|
||||
{
|
||||
QString r = s;
|
||||
r.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
|
||||
r.replace(QLatin1Char('\"'), QLatin1String("\\\""));
|
||||
r.remove(QLatin1Char('\r'));
|
||||
r.replace(QLatin1Char('\n'), QLatin1String("\\n\"\n\""));
|
||||
return QLatin1Char('\"') + r + QLatin1Char('\"');
|
||||
}
|
||||
|
||||
QString literalString(const QString &str)
|
||||
{
|
||||
const bool isAscii = std::none_of(str.cbegin(), str.cend(), [](const QChar ch) {
|
||||
return ch.unicode() > 127;
|
||||
});
|
||||
|
||||
if (isAscii) {
|
||||
return QLatin1String("QStringLiteral( %1 )").arg(quoteString(str));
|
||||
} else {
|
||||
return QLatin1String("QString::fromUtf8( %1 )").arg(quoteString(str));
|
||||
}
|
||||
}
|
||||
|
||||
QString signalEnumName(const QString &signalName)
|
||||
{
|
||||
QString result;
|
||||
result = QLatin1String("signal") + signalName;
|
||||
result[6] = result.at(6).toUpper();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isUnsigned(const QString &type)
|
||||
{
|
||||
return type == QLatin1String("UInt") || type == QLatin1String("ULongLong");
|
||||
}
|
||||
|
||||
/**
|
||||
Return parameter declaration for given type.
|
||||
*/
|
||||
QString param(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("const QStringList &");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("const QFont &");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("const QRect &");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("const QRectF &");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("const QSize &");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("const QSizeF &");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("const QColor &");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("const QPoint &");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("const QPointF &");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("uint");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("bool");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("double");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("const QDateTime &");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("qint64");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("quint64");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("const QList<int> &");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("const QStringList &");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("const QString &");
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("const QUrl &");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("const QList<QUrl> &");
|
||||
} else {
|
||||
std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Actual C++ storage type for given type.
|
||||
*/
|
||||
QString cppType(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("QStringList");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("QFont");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("QRect");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("QRectF");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("QSize");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("QSizeF");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("QColor");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("QPoint");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("QPointF");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("uint");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("bool");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("double");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("QDateTime");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("qint64");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("quint64");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("QList<int>");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("int");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("QStringList");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("QString");
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("QUrl");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("QList<QUrl>");
|
||||
} else {
|
||||
std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
QString defaultValue(const QString &t)
|
||||
{
|
||||
const QString type = t.toLower();
|
||||
if (type == QLatin1String("string")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("stringlist")) {
|
||||
return QStringLiteral("QStringList()");
|
||||
} else if (type == QLatin1String("font")) {
|
||||
return QStringLiteral("QFont()");
|
||||
} else if (type == QLatin1String("rect")) {
|
||||
return QStringLiteral("QRect()");
|
||||
} else if (type == QLatin1String("rectf")) {
|
||||
return QStringLiteral("QRectF()");
|
||||
} else if (type == QLatin1String("size")) {
|
||||
return QStringLiteral("QSize()");
|
||||
} else if (type == QLatin1String("sizef")) {
|
||||
return QStringLiteral("QSizeF()");
|
||||
} else if (type == QLatin1String("color")) {
|
||||
return QStringLiteral("QColor(128, 128, 128)");
|
||||
} else if (type == QLatin1String("point")) {
|
||||
return QStringLiteral("QPoint()");
|
||||
} else if (type == QLatin1String("pointf")) {
|
||||
return QStringLiteral("QPointF()");
|
||||
} else if (type == QLatin1String("int")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("uint")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("bool")) {
|
||||
return QStringLiteral("false");
|
||||
} else if (type == QLatin1String("double")) {
|
||||
return QStringLiteral("0.0");
|
||||
} else if (type == QLatin1String("datetime")) {
|
||||
return QStringLiteral("QDateTime()");
|
||||
} else if (type == QLatin1String("longlong")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("ulonglong")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("intlist")) {
|
||||
return QStringLiteral("QList<int>()");
|
||||
} else if (type == QLatin1String("enum")) {
|
||||
return QStringLiteral("0");
|
||||
} else if (type == QLatin1String("path")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("pathlist")) {
|
||||
return QStringLiteral("QStringList()");
|
||||
} else if (type == QLatin1String("password")) {
|
||||
return QStringLiteral("\"\""); // Use empty string, not null string!
|
||||
} else if (type == QLatin1String("url")) {
|
||||
return QStringLiteral("QUrl()");
|
||||
} else if (type == QLatin1String("urllist")) {
|
||||
return QStringLiteral("QList<QUrl>()");
|
||||
} else {
|
||||
std::cerr << "Error, kconfig_compiler_kf6 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
|
||||
return QStringLiteral("QString"); // For now, but an assert would be better
|
||||
}
|
||||
}
|
||||
|
||||
QString itemType(const QString &type)
|
||||
{
|
||||
if (type.isEmpty()) {
|
||||
return QString{};
|
||||
}
|
||||
|
||||
QString str = type;
|
||||
str[0] = str.at(0).toUpper();
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
if (e->name.isEmpty()) {
|
||||
return QString{};
|
||||
}
|
||||
|
||||
const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(e->type);
|
||||
|
||||
QString fCap = e->name;
|
||||
fCap[0] = fCap.at(0).toUpper();
|
||||
const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(e->paramMax + 1)) : QString();
|
||||
QString result;
|
||||
|
||||
if (!cfg.itemAccessors && !cfg.dpointer) {
|
||||
result += QLatin1String{" "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
|
||||
result += QLatin1String(" *item%1;\n").arg(fCap + argSuffix);
|
||||
}
|
||||
|
||||
if (!e->signalList.isEmpty()) {
|
||||
result += QLatin1String(" %1 *%2;\n").arg(type, innerItemVar(e, cfg) + argSuffix);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the name of an item variable
|
||||
// use itemPath to know the full path
|
||||
// like using d-> in case of dpointer
|
||||
QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
if (cfg.itemAccessors) {
|
||||
if (!cfg.dpointer) {
|
||||
result = QLatin1String("m%1Item").arg(e->name);
|
||||
result[1] = result.at(1).toUpper();
|
||||
} else {
|
||||
result = e->name + QLatin1String{"Item"};
|
||||
result[0] = result.at(0).toLower();
|
||||
}
|
||||
} else {
|
||||
result = QLatin1String{"item"} + e->name;
|
||||
result[4] = result.at(4).toUpper();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the name of the local inner item if there is one
|
||||
// (before wrapping with KConfigCompilerSignallingItem)
|
||||
// Otherwise return itemVar()
|
||||
QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
if (e->signalList.isEmpty()) {
|
||||
return itemPath(e, cfg);
|
||||
}
|
||||
|
||||
QString result = QLatin1String{"innerItem"} + e->name;
|
||||
result[9] = result.at(9).toUpper();
|
||||
return result;
|
||||
}
|
||||
|
||||
QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
|
||||
}
|
||||
|
||||
QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m)
|
||||
{
|
||||
QString str = QLatin1String("new %1::Item%2").arg(cfg.inherits, itemType(entry->type));
|
||||
str += QLatin1String("( currentGroup(), %1, %2").arg(key, varPath(entry->name, cfg) + param);
|
||||
|
||||
if (entry->type == QLatin1String("Enum")) {
|
||||
str += QLatin1String{", values"} + entry->name;
|
||||
}
|
||||
if (!defaultValue.isEmpty()) {
|
||||
str += QLatin1String(", ") + defaultValue;
|
||||
}
|
||||
str += QLatin1String(" );");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString ¶m)
|
||||
{
|
||||
const QList<Signal> sigs = entry->signalList;
|
||||
if (sigs.isEmpty()) {
|
||||
return newInnerItem(entry, key, defaultValue, cfg, param);
|
||||
}
|
||||
|
||||
QString str;
|
||||
str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(innerItemVar(entry, cfg) + param);
|
||||
// Append the signal flags
|
||||
const int listSize = sigs.size();
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
if (i != 0) {
|
||||
str += QLatin1String(" | ");
|
||||
}
|
||||
str += signalEnumName(sigs[i].name);
|
||||
}
|
||||
str += QLatin1String(");");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
QString paramString(const QString &s, const CfgEntry *e, int i)
|
||||
{
|
||||
QString result = s;
|
||||
const QString needle = QLatin1String("$(%1)").arg(e->param);
|
||||
if (result.contains(needle)) {
|
||||
const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
|
||||
|
||||
result.replace(needle, tmp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString paramString(const QString &group, const QList<Param> ¶meters)
|
||||
{
|
||||
QString paramString = group;
|
||||
QString arguments;
|
||||
int i = 1;
|
||||
bool firstArg = true;
|
||||
for (const auto ¶m : parameters) {
|
||||
const QString paramName = param.name;
|
||||
const QString str = QLatin1String("$(%1)").arg(paramName);
|
||||
if (paramString.contains(str)) {
|
||||
const QString tmp = QStringLiteral("%%1").arg(i++);
|
||||
paramString.replace(str, tmp);
|
||||
|
||||
if (firstArg) {
|
||||
arguments += QLatin1String{".arg( "};
|
||||
firstArg = false;
|
||||
}
|
||||
|
||||
arguments += QLatin1String("mParam%1, ").arg(paramName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
// Remove the last ", "
|
||||
arguments.chop(2);
|
||||
|
||||
// Close the ".arg( "
|
||||
arguments += QLatin1String{" )"};
|
||||
} else {
|
||||
return QLatin1String("QStringLiteral( \"%1\" )").arg(group);
|
||||
}
|
||||
|
||||
return QLatin1String("QStringLiteral( \"%1\" )%2").arg(paramString, arguments);
|
||||
}
|
||||
|
||||
QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue)
|
||||
{
|
||||
QString result;
|
||||
|
||||
switch (cfg.translationSystem) {
|
||||
case KConfigParameters::QtTranslation:
|
||||
if (!context.isEmpty()) {
|
||||
result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(context);
|
||||
} else {
|
||||
result += QLatin1String{"QCoreApplication::translate(\""};
|
||||
}
|
||||
result += QLatin1String("%1\", ").arg(cfg.className);
|
||||
break;
|
||||
|
||||
case KConfigParameters::KdeTranslation:
|
||||
if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
|
||||
result += QLatin1String("i18ndc(%1, %2, ").arg(quoteString(cfg.translationDomain), quoteString(context));
|
||||
} else if (!cfg.translationDomain.isEmpty()) {
|
||||
result += QLatin1String("i18nd(%1, ").arg(quoteString(cfg.translationDomain));
|
||||
} else if (!context.isEmpty()) {
|
||||
result += QLatin1String("i18nc(%1, ").arg(quoteString(context));
|
||||
} else {
|
||||
result += QLatin1String{"i18n("};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!param.isEmpty()) {
|
||||
QString resolvedString = string;
|
||||
resolvedString.replace(QLatin1String("$(%1)").arg(param), paramValue);
|
||||
result += quoteString(resolvedString);
|
||||
} else {
|
||||
result += quoteString(string);
|
||||
}
|
||||
|
||||
result += QLatin1Char{')'};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* int i is the value of the parameter */
|
||||
QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
|
||||
{
|
||||
QString txt;
|
||||
if (itemVarStr.isNull()) {
|
||||
itemVarStr = itemPath(e, cfg);
|
||||
}
|
||||
if (!e->label.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setLabel( %2 );\n").arg(itemVarStr, translatedString(cfg, e->label, e->labelContext, e->param, i));
|
||||
}
|
||||
if (!e->toolTip.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setToolTip( %2 );\n").arg(itemVarStr, translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i));
|
||||
}
|
||||
if (!e->whatsThis.isEmpty()) {
|
||||
txt += QLatin1String(" %1->setWhatsThis( %2 );\n").arg(itemVarStr, translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i));
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
// returns the member mutator implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
// TODO: Fix add Debug Method, it should also take the debug string.
|
||||
void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
|
||||
{
|
||||
if (cfg.qCategoryLoggingName.isEmpty()) {
|
||||
out << " qDebug() << \"" << setFunction(n);
|
||||
} else {
|
||||
out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the member get default implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString memberGetDefaultBody(const CfgEntry *e)
|
||||
{
|
||||
QString result = e->code;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
out << '\n';
|
||||
|
||||
if (!e->param.isEmpty()) {
|
||||
out << " switch (i) {\n";
|
||||
for (int i = 0; i <= e->paramMax; ++i) {
|
||||
if (!e->paramDefaultValues[i].isEmpty()) {
|
||||
out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
|
||||
}
|
||||
}
|
||||
QString defaultValue = e->defaultValue;
|
||||
|
||||
out << " default:\n";
|
||||
out << " return " << defaultValue.replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("i")) << ";\n";
|
||||
out << " }\n";
|
||||
} else {
|
||||
out << " return " << e->defaultValue << ';';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns the item accessor implementation
|
||||
// which should go in the h file if inline
|
||||
// or the cpp file if not inline
|
||||
QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
|
||||
out << "return " << itemPath(e, cfg);
|
||||
if (!e->param.isEmpty()) {
|
||||
out << "[i]";
|
||||
}
|
||||
out << ";\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// indents text adding X spaces per line
|
||||
QString indent(QString text, int spaces)
|
||||
{
|
||||
QString result;
|
||||
QTextStream out(&result, QIODevice::WriteOnly);
|
||||
QTextStream in(&text, QIODevice::ReadOnly);
|
||||
QString currLine;
|
||||
while (!in.atEnd()) {
|
||||
currLine = in.readLine();
|
||||
if (!currLine.isEmpty()) {
|
||||
for (int i = 0; i < spaces; ++i) {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
out << currLine << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
|
||||
{
|
||||
Q_UNUSED(parser)
|
||||
|
||||
if (cfg.className.isEmpty()) {
|
||||
std::cerr << "Class name missing" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg.singleton && !parseResult.parameters.isEmpty()) {
|
||||
std::cerr << "Singleton class can not have parameters" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
|
||||
std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: For some reason some configuration files prefer to have *no* entries
|
||||
* at all in it, and the generated code is mostly bogus as KConfigXT will not
|
||||
* handle save / load / properties, etc, nothing.
|
||||
*
|
||||
* The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
|
||||
* project.
|
||||
* I think we should remove the possibility of creating configuration classes from configuration
|
||||
* files that don't really have configuration in it. but I'm changing this right now to allow
|
||||
* kdevelop files to pass.
|
||||
*
|
||||
* Remove for KDE 6
|
||||
* (to make things more interesting, it failed in a code that's never used within KDevelop... )
|
||||
*/
|
||||
if (parseResult.entries.isEmpty()) {
|
||||
std::cerr << "No entries." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName(QStringLiteral("kconfig_compiler"));
|
||||
app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
|
||||
|
||||
QString inputFilename;
|
||||
QString codegenFilename;
|
||||
|
||||
QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
|
||||
QCoreApplication::translate("main", "Directory to generate files in [.]"),
|
||||
QCoreApplication::translate("main", "directory"),
|
||||
QStringLiteral("."));
|
||||
|
||||
QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
|
||||
QCoreApplication::translate("main", "Display software license."));
|
||||
|
||||
QCommandLineParser parser;
|
||||
|
||||
parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
|
||||
parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
|
||||
|
||||
parser.addOption(targetDirectoryOption);
|
||||
parser.addOption(licenseOption);
|
||||
|
||||
parser.addVersionOption();
|
||||
parser.addHelpOption();
|
||||
parser.process(app);
|
||||
|
||||
if (parser.isSet(licenseOption)) {
|
||||
std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
|
||||
std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
|
||||
std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
|
||||
std::cout << "You may redistribute copies of this program" << std::endl;
|
||||
std::cout << "under the terms of the GNU Library Public License." << std::endl;
|
||||
std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.count() < 2) {
|
||||
std::cerr << "Too few arguments." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.count() > 2) {
|
||||
std::cerr << "Too many arguments." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
inputFilename = args.at(0);
|
||||
codegenFilename = args.at(1);
|
||||
|
||||
// TODO: Transform baseDir into a helper.
|
||||
QString baseDir = parser.value(targetDirectoryOption);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
|
||||
#else
|
||||
if (!baseDir.endsWith(QLatin1Char{'/'})) {
|
||||
#endif
|
||||
baseDir.append(QLatin1Char{'/'});
|
||||
}
|
||||
|
||||
KConfigParameters cfg(codegenFilename);
|
||||
|
||||
KConfigXmlParser xmlParser(cfg, inputFilename);
|
||||
|
||||
// The Xml Parser aborts in the case of an error, so if we get
|
||||
// to parseResult, we have a working Xml file.
|
||||
xmlParser.start();
|
||||
|
||||
ParseResult parseResult = xmlParser.getParseResult();
|
||||
|
||||
if (hasErrors(xmlParser, parseResult, cfg)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Move this to somewhere saner.
|
||||
for (const auto &signal : std::as_const(parseResult.signalList)) {
|
||||
parseResult.hasNonModifySignals |= !signal.modify;
|
||||
}
|
||||
|
||||
// remove '.kcfg' from the name.
|
||||
const QString baseName = inputFilename.mid(0, inputFilename.size() - 5);
|
||||
KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
|
||||
headerGenerator.start();
|
||||
headerGenerator.save();
|
||||
|
||||
KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
|
||||
sourceGenerator.start();
|
||||
sourceGenerator.save();
|
||||
|
||||
qDeleteAll(parseResult.entries);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
add_executable(kreadconfig6 kreadconfig.cpp)
|
||||
ecm_mark_nongui_executable(kreadconfig6)
|
||||
|
||||
target_link_libraries(kreadconfig6 KF6::ConfigCore)
|
||||
|
||||
install(TARGETS kreadconfig6 ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
########### next target ###############
|
||||
|
||||
add_executable(kwriteconfig6 kwriteconfig.cpp)
|
||||
ecm_mark_nongui_executable(kwriteconfig6)
|
||||
|
||||
target_link_libraries(kwriteconfig6 KF6::ConfigCore)
|
||||
|
||||
install(TARGETS kwriteconfig6 ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Read KConfig() entries - for use in shell scripts.
|
||||
|
||||
SPDX-FileCopyrightText: 2001 Red Hat, Inc.
|
||||
SPDX-FileContributor: Programmed by Bernhard Rosenkraenzer <bero@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/*
|
||||
* If --type is specified as bool, the return value is 0 if the value
|
||||
* is set, 1 if it isn't set. There is no output.
|
||||
*
|
||||
* If --type is specified as num, the return value matches the value
|
||||
* of the key. There is no output.
|
||||
*
|
||||
* If --type is not set, the value of the key is simply printed to stdout.
|
||||
*
|
||||
* Usage examples:
|
||||
* if kreadconfig6 --group KDE --key macStyle --type bool; then
|
||||
* echo "We're using Mac-Style menus."
|
||||
* else
|
||||
* echo "We're using normal menus."
|
||||
* fi
|
||||
*
|
||||
* TRASH=`kreadconfig6 --group Paths --key Trash`
|
||||
* if test -n "$TRASH"; then
|
||||
* mv someFile "$TRASH"
|
||||
* else
|
||||
* rm someFile
|
||||
* fi
|
||||
*/
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
#include <QCommandLineParser>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.addOption(
|
||||
QCommandLineOption(QStringLiteral("file"), QCoreApplication::translate("main", "Use <file> instead of global config"), QStringLiteral("file")));
|
||||
parser.addOption(
|
||||
QCommandLineOption(QStringLiteral("group"),
|
||||
QCoreApplication::translate("main", "Group to look in. Use \"<default>\" for the root group, or use repeatedly for nested groups."),
|
||||
QStringLiteral("group"),
|
||||
QStringLiteral("KDE")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("key"), QCoreApplication::translate("main", "Key to look for"), QStringLiteral("key")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("default"), QCoreApplication::translate("main", "Default value"), QStringLiteral("value")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("type"), QCoreApplication::translate("main", "Type of variable"), QStringLiteral("type")));
|
||||
|
||||
parser.process(app);
|
||||
|
||||
const QStringList groups = parser.values(QStringLiteral("group"));
|
||||
QString key = parser.value(QStringLiteral("key"));
|
||||
QString file = parser.value(QStringLiteral("file"));
|
||||
QString dflt = parser.value(QStringLiteral("default"));
|
||||
QString type = parser.value(QStringLiteral("type")).toLower();
|
||||
|
||||
if (key.isNull() || !parser.positionalArguments().isEmpty()) {
|
||||
parser.showHelp(1);
|
||||
}
|
||||
|
||||
KSharedConfig::openConfig();
|
||||
|
||||
KConfig *konfig;
|
||||
bool configMustDeleted = false;
|
||||
if (file.isEmpty()) {
|
||||
konfig = KSharedConfig::openConfig().data();
|
||||
} else {
|
||||
konfig = new KConfig(file, KConfig::NoGlobals);
|
||||
configMustDeleted = true;
|
||||
}
|
||||
KConfigGroup cfgGroup = konfig->group(QString());
|
||||
for (const QString &grp : groups) {
|
||||
if (grp.isEmpty()) {
|
||||
fprintf(stderr,
|
||||
"%s: %s\n",
|
||||
qPrintable(QCoreApplication::applicationName()),
|
||||
qPrintable(QCoreApplication::translate("main", "Group name cannot be empty, use \"<default>\" for the root group")));
|
||||
if (configMustDeleted) {
|
||||
delete konfig;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
cfgGroup = cfgGroup.group(grp);
|
||||
}
|
||||
|
||||
if (type == QLatin1String{"bool"}) {
|
||||
dflt = dflt.toLower();
|
||||
bool def = (dflt == QLatin1String{"true"} || dflt == QLatin1String{"on"} || dflt == QLatin1String{"yes"} || dflt == QLatin1String{"1"});
|
||||
bool retValue = !cfgGroup.readEntry(key, def);
|
||||
if (configMustDeleted) {
|
||||
delete konfig;
|
||||
}
|
||||
return retValue;
|
||||
} else if (type == QLatin1String{"num"} || type == QLatin1String{"int"}) {
|
||||
int retValue = cfgGroup.readEntry(key, dflt.toInt());
|
||||
if (configMustDeleted) {
|
||||
delete konfig;
|
||||
}
|
||||
return retValue;
|
||||
} else if (type == QLatin1String{"path"}) {
|
||||
fprintf(stdout, "%s\n", cfgGroup.readPathEntry(key, dflt).toLocal8Bit().data());
|
||||
if (configMustDeleted) {
|
||||
delete konfig;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
/* Assume it's a string... */
|
||||
fprintf(stdout, "%s\n", cfgGroup.readEntry(key, dflt).toLocal8Bit().data());
|
||||
if (configMustDeleted) {
|
||||
delete konfig;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Write KConfig() entries - for use in shell scripts.
|
||||
|
||||
SPDX-FileCopyrightText: 2001 Red Hat , Inc.
|
||||
SPDX-FileCopyrightText: 2001 Luís Pedro Coelho <luis_pedro@netcabo.pt>
|
||||
|
||||
Programmed by Luís Pedro Coelho <luis_pedro@netcabo.pt>
|
||||
based on kreadconfig by Bernhard Rosenkraenzer <bero@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.addOption(
|
||||
QCommandLineOption(QStringLiteral("file"), QCoreApplication::translate("main", "Use <file> instead of global config"), QStringLiteral("file")));
|
||||
parser.addOption(
|
||||
QCommandLineOption(QStringLiteral("group"),
|
||||
QCoreApplication::translate("main", "Group to look in. Use \"<default>\" for the root group, or use repeatedly for nested groups."),
|
||||
QStringLiteral("group"),
|
||||
QStringLiteral("KDE")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("key"), QCoreApplication::translate("main", "Key to look for"), QStringLiteral("key")));
|
||||
parser.addOption(
|
||||
QCommandLineOption(QStringLiteral("type"),
|
||||
QCoreApplication::translate("main", "Type of variable. Use \"bool\" for a boolean, otherwise it is treated as a string"),
|
||||
QStringLiteral("type")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("delete"), QCoreApplication::translate("main", "Delete the designated key if enabled")));
|
||||
parser.addPositionalArgument(QStringLiteral("value"), QCoreApplication::translate("main", "The value to write. Mandatory, on a shell use '' for empty"));
|
||||
|
||||
parser.process(app);
|
||||
|
||||
const QStringList groups = parser.values(QStringLiteral("group"));
|
||||
QString key = parser.value(QStringLiteral("key"));
|
||||
QString file = parser.value(QStringLiteral("file"));
|
||||
QString type = parser.value(QStringLiteral("type")).toLower();
|
||||
bool del = parser.isSet(QStringLiteral("delete"));
|
||||
|
||||
QString value;
|
||||
if (del) {
|
||||
value = QString{};
|
||||
} else if (parser.positionalArguments().isEmpty()) {
|
||||
parser.showHelp(1);
|
||||
} else {
|
||||
value = parser.positionalArguments().at(0);
|
||||
}
|
||||
|
||||
KConfig *konfig;
|
||||
if (file.isEmpty()) {
|
||||
konfig = new KConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
|
||||
} else {
|
||||
konfig = new KConfig(file, KConfig::NoGlobals);
|
||||
}
|
||||
|
||||
KConfigGroup cfgGroup = konfig->group(QString());
|
||||
for (const QString &grp : groups) {
|
||||
if (grp.isEmpty()) {
|
||||
fprintf(stderr,
|
||||
"%s: %s\n",
|
||||
qPrintable(QCoreApplication::applicationName()),
|
||||
qPrintable(QCoreApplication::translate("main", "Group name cannot be empty, use \"<default>\" for the root group")));
|
||||
return 2;
|
||||
}
|
||||
cfgGroup = cfgGroup.group(grp);
|
||||
}
|
||||
|
||||
if (konfig->accessMode() != KConfig::ReadWrite || cfgGroup.isEntryImmutable(key)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (del) {
|
||||
cfgGroup.deleteEntry(key);
|
||||
} else if (type == QLatin1String{"bool"}) {
|
||||
// For symmetry with kreadconfig we accept a wider range of values as true than Qt
|
||||
/* clang-format off */
|
||||
bool boolvalue = value == QLatin1String{"true"}
|
||||
|| value == QLatin1String{"on"}
|
||||
|| value == QLatin1String{"yes"}
|
||||
|| value == QLatin1String{"1"}; /* clang-format on */
|
||||
cfgGroup.writeEntry(key, boolvalue);
|
||||
} else if (type == QLatin1String{"path"}) {
|
||||
cfgGroup.writePathEntry(key, value);
|
||||
} else {
|
||||
cfgGroup.writeEntry(key, value);
|
||||
}
|
||||
konfig->sync();
|
||||
delete konfig;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
# SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
add_library(KF6ConfigQml)
|
||||
add_library(KF6::ConfigQml ALIAS KF6ConfigQml)
|
||||
|
||||
ecm_add_qml_module(KF6ConfigQml URI org.kde.config GENERATE_PLUGIN_SOURCE)
|
||||
|
||||
set_target_properties(KF6ConfigQml PROPERTIES
|
||||
VERSION ${KCONFIG_VERSION}
|
||||
SOVERSION ${KCONFIG_SOVERSION}
|
||||
EXPORT_NAME ConfigQml
|
||||
)
|
||||
|
||||
target_sources(KF6ConfigQml PRIVATE
|
||||
kconfigpropertymap.cpp
|
||||
kwindowstatesaverquick.cpp
|
||||
types.cpp
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6ConfigQml
|
||||
HEADER kconfig_qml_log_settings.h
|
||||
IDENTIFIER KCONFIG_QML_LOG
|
||||
CATEGORY_NAME kf.config.qml
|
||||
DESCRIPTION "KConfig QML"
|
||||
EXPORT KCONFIG
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6ConfigQml
|
||||
BASE_NAME KConfigQml
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME KConfig
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_link_libraries(KF6ConfigQml
|
||||
PUBLIC
|
||||
KF6::ConfigCore # KCoreConfigSkeleton, in ConfigPropertyMap
|
||||
KF6::ConfigGui # KWindowStateSaver
|
||||
Qt6::Qml
|
||||
PRIVATE
|
||||
Qt6::Quick
|
||||
)
|
||||
|
||||
ecm_generate_headers(KConfigQml_HEADERS
|
||||
HEADER_NAMES
|
||||
KConfigPropertyMap
|
||||
|
||||
REQUIRED_HEADERS KConfigQml_HEADERS
|
||||
)
|
||||
target_include_directories(KF6ConfigQml
|
||||
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KConfig;${KDE_INSTALL_INCLUDEDIR_KF}/KConfigQml>")
|
||||
|
||||
install(TARGETS KF6ConfigQml EXPORT KF6ConfigTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kconfigqml_export.h
|
||||
${KConfigQml_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KConfigQml COMPONENT Devel
|
||||
)
|
||||
|
||||
ecm_finalize_qml_module(KF6ConfigQml DESTINATION ${KDE_INSTALL_QMLDIR} EXPORT KF6ConfigTargets)
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <notmart@gmail.com>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kconfigpropertymap.h"
|
||||
|
||||
#include <KCoreConfigSkeleton>
|
||||
#include <QJSValue>
|
||||
#include <QPointer>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class KConfigPropertyMapPrivate
|
||||
{
|
||||
public:
|
||||
KConfigPropertyMapPrivate(KConfigPropertyMap *map)
|
||||
: q(map)
|
||||
{
|
||||
}
|
||||
|
||||
enum LoadConfigOption {
|
||||
DontEmitValueChanged,
|
||||
EmitValueChanged,
|
||||
};
|
||||
|
||||
void loadConfig(LoadConfigOption option);
|
||||
void writeConfig();
|
||||
void writeConfigValue(const QString &key, const QVariant &value);
|
||||
|
||||
KConfigPropertyMap *q;
|
||||
QPointer<KCoreConfigSkeleton> config;
|
||||
bool updatingConfigValue = false;
|
||||
bool notify = false;
|
||||
};
|
||||
|
||||
KConfigPropertyMap::KConfigPropertyMap(KCoreConfigSkeleton *config, QObject *parent)
|
||||
: QQmlPropertyMap(this, parent)
|
||||
, d(new KConfigPropertyMapPrivate(this))
|
||||
{
|
||||
Q_ASSERT(config);
|
||||
d->config = config;
|
||||
|
||||
// Reload the config only if the change signal has *not* been emitted by ourselves updating the config
|
||||
connect(config, &KCoreConfigSkeleton::configChanged, this, [this]() {
|
||||
if (!d->updatingConfigValue) {
|
||||
d->loadConfig(KConfigPropertyMapPrivate::EmitValueChanged);
|
||||
}
|
||||
});
|
||||
connect(this, &KConfigPropertyMap::valueChanged, this, [this](const QString &key, const QVariant &value) {
|
||||
d->writeConfigValue(key, value);
|
||||
});
|
||||
|
||||
d->loadConfig(KConfigPropertyMapPrivate::DontEmitValueChanged);
|
||||
}
|
||||
|
||||
KConfigPropertyMap::~KConfigPropertyMap() = default;
|
||||
|
||||
bool KConfigPropertyMap::isNotify() const
|
||||
{
|
||||
return d->notify;
|
||||
}
|
||||
|
||||
void KConfigPropertyMap::setNotify(bool notify)
|
||||
{
|
||||
d->notify = notify;
|
||||
}
|
||||
|
||||
void KConfigPropertyMap::writeConfig()
|
||||
{
|
||||
d->writeConfig();
|
||||
}
|
||||
|
||||
QVariant KConfigPropertyMap::updateValue(const QString &key, const QVariant &input)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
if (input.userType() == qMetaTypeId<QJSValue>()) {
|
||||
return input.value<QJSValue>().toVariant();
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
bool KConfigPropertyMap::isImmutable(const QString &key) const
|
||||
{
|
||||
KConfigSkeletonItem *item = d->config.data()->findItem(key);
|
||||
if (item) {
|
||||
return item->isImmutable();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KConfigPropertyMapPrivate::loadConfig(KConfigPropertyMapPrivate::LoadConfigOption option)
|
||||
{
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &items = config.data()->items();
|
||||
for (KConfigSkeletonItem *item : items) {
|
||||
q->insert(item->key() + QStringLiteral("Default"), item->getDefault());
|
||||
q->insert(item->key(), item->property());
|
||||
if (option == EmitValueChanged) {
|
||||
Q_EMIT q->valueChanged(item->key(), item->property());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KConfigPropertyMapPrivate::writeConfig()
|
||||
{
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto lstItems = config.data()->items();
|
||||
for (KConfigSkeletonItem *item : lstItems) {
|
||||
item->setWriteFlags(notify ? KConfigBase::Notify : KConfigBase::Normal);
|
||||
item->setProperty(q->value(item->key()));
|
||||
}
|
||||
// Internally sync the config. This way we ensure the config file is written, even if the process crashed
|
||||
config.data()->save();
|
||||
}
|
||||
|
||||
void KConfigPropertyMapPrivate::writeConfigValue(const QString &key, const QVariant &value)
|
||||
{
|
||||
KConfigSkeletonItem *item = config.data()->findItem(key);
|
||||
if (item) {
|
||||
updatingConfigValue = true;
|
||||
item->setWriteFlags(notify ? KConfigBase::Notify : KConfigBase::Normal);
|
||||
item->setProperty(value);
|
||||
updatingConfigValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kconfigpropertymap.cpp"
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Marco Martin <notmart@gmail.com>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCONFIGPROPERTYMAP_H
|
||||
#define KCONFIGPROPERTYMAP_H
|
||||
|
||||
#include <QQmlPropertyMap>
|
||||
#include <memory>
|
||||
#include <qqmlregistration.h>
|
||||
|
||||
class KCoreConfigSkeleton;
|
||||
|
||||
#include <kconfigqml_export.h>
|
||||
|
||||
class KConfigPropertyMapPrivate;
|
||||
|
||||
/**
|
||||
* @class KConfigPropertyMap configpropertymap.h ConfigPropertyMap
|
||||
*
|
||||
* An object that (optionally) automatically saves changes in a
|
||||
* property map to a configuration object (e.g. a KConfig file).
|
||||
* @since 5.89
|
||||
*/
|
||||
class KCONFIGQML_EXPORT KConfigPropertyMap : public QQmlPropertyMap
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
public:
|
||||
KConfigPropertyMap(KCoreConfigSkeleton *config, QObject *parent = nullptr);
|
||||
~KConfigPropertyMap() override;
|
||||
|
||||
/**
|
||||
* Whether notifications on config changes are enabled. Disabled by default.
|
||||
* @see KConfigBase::Notify
|
||||
* @return true if writes send (dbus) notifications
|
||||
*/
|
||||
bool isNotify() const;
|
||||
|
||||
/**
|
||||
* Enable or disable notifications on config changes.
|
||||
* @see KConfigBase::Notify
|
||||
* @param notify whether to send notifications
|
||||
*/
|
||||
void setNotify(bool notify);
|
||||
|
||||
/**
|
||||
* @brief Whether the value at the given key is immutable
|
||||
*
|
||||
* @return true if the value is immutable, false if it isn't or it doesn't exist
|
||||
*/
|
||||
Q_INVOKABLE bool isImmutable(const QString &key) const;
|
||||
|
||||
/**
|
||||
* Saves the state of the property map on disk.
|
||||
*/
|
||||
Q_INVOKABLE void writeConfig();
|
||||
|
||||
protected:
|
||||
QVariant updateValue(const QString &key, const QVariant &input) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KConfigPropertyMapPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "kwindowstatesaverquick.h"
|
||||
#include "kconfig_qml_log_settings.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
|
||||
#include <KWindowStateSaver>
|
||||
|
||||
void KWindowStateSaverQuick::classBegin()
|
||||
{
|
||||
}
|
||||
|
||||
void KWindowStateSaverQuick::componentComplete()
|
||||
{
|
||||
const auto parentItem = qobject_cast<QQuickItem *>(parent());
|
||||
if (!parentItem) {
|
||||
qCWarning(KCONFIG_QML_LOG) << "WindowStateSaver requires a parent item";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto window = qobject_cast<QWindow *>(parentItem->window());
|
||||
if (!window) {
|
||||
qCWarning(KCONFIG_QML_LOG) << "WindowStateSaver requires the parent to be a type that inherits QWindow";
|
||||
return;
|
||||
}
|
||||
|
||||
new KWindowStateSaver(window, m_configGroupName);
|
||||
|
||||
// To work around oddities in QtQuick window visibility handling.
|
||||
// If we do not set the window visible now, then our window state is
|
||||
// overwritten during QQuickWindowQmlImpl::setWindowVisibility() because
|
||||
// QQuickWindow is AutomaticVisibility by default.
|
||||
if (window->windowState() == Qt::WindowMaximized) {
|
||||
window->setVisibility(QWindow::Visibility::Maximized);
|
||||
}
|
||||
}
|
||||
|
||||
void KWindowStateSaverQuick::setConfigGroupName(const QString &name)
|
||||
{
|
||||
if (m_configGroupName != name) {
|
||||
m_configGroupName = name;
|
||||
Q_EMIT configGroupNameChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString KWindowStateSaverQuick::configGroupName() const
|
||||
{
|
||||
return m_configGroupName;
|
||||
}
|
||||
|
||||
#include "moc_kwindowstatesaverquick.cpp"
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#ifndef KWINDOWSTATESAVER_QUICK_H
|
||||
#define KWINDOWSTATESAVER_QUICK_H
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
/**
|
||||
* @brief Creates a @c KWindowStateSaver in QML, and assigns it to the window it's parented to.
|
||||
*
|
||||
* Functions exactly as KWindowStateSaver in C++, as it's a small wrapper around it.
|
||||
*
|
||||
* @code
|
||||
* import org.kde.config as KConfig
|
||||
*
|
||||
* Kirigami.ApplicationWindow {
|
||||
* id: root
|
||||
*
|
||||
* title: i18n("My Window")
|
||||
*
|
||||
* KConfig.WindowStateSaver {
|
||||
* configGroupName: "Main"
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
* @since 6.5
|
||||
*
|
||||
* @sa KWindowStateSaver
|
||||
*/
|
||||
class KWindowStateSaverQuick : public QObject, public QQmlParserStatus
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_NAMED_ELEMENT(WindowStateSaver)
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
|
||||
Q_PROPERTY(QString configGroupName READ configGroupName WRITE setConfigGroupName NOTIFY configGroupNameChanged REQUIRED)
|
||||
|
||||
public:
|
||||
void classBegin() override;
|
||||
void componentComplete() override;
|
||||
|
||||
void setConfigGroupName(const QString &name);
|
||||
QString configGroupName() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void configGroupNameChanged();
|
||||
|
||||
private:
|
||||
QString m_configGroupName;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "moc_types.cpp"
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#ifndef KCONFIGTYPES_H
|
||||
#define KCONFIGTYPES_H
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <kauthorized.h>
|
||||
#include <kcoreconfigskeleton.h>
|
||||
|
||||
struct KAuthorizedForeign {
|
||||
Q_GADGET
|
||||
QML_NAMED_ELEMENT(KAuthorized)
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(KAuthorized)
|
||||
};
|
||||
|
||||
struct KCoreConfigSkeletonForeign {
|
||||
Q_GADGET
|
||||
QML_ANONYMOUS
|
||||
QML_FOREIGN(KCoreConfigSkeleton)
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user