Advance Wayland and KDE package bring-up

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,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()
+6
View File
@@ -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 &currentLocale, 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 &currentLocale, 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 &currentEntry = 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> &copy()
{
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> &copy();
/**
* 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})
@@ -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 &parameters,
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 &parameters, // 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 &param = QString(),
const QString &paramValue = QString());
// TODO: Sanitize those functions.
QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param = QString());
QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param = 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> &parameters);
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 &parameter : 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 &parameter : 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 &parameters, 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 &parameter : 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 &parameters, 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 &lt;schumacher@kde.org&gt;
SPDX-FileCopyrightText: 2003 Waldo Bastian &lt;bastian@kde.org&gt;
SPDX-FileCopyrightText: 2003 Zack Rusin &lt;zack@kde.org&gt;
SPDX-FileCopyrightText: 2004 Frans Englich &lt;frans.englich@telia.com&gt;
SPDX-FileCopyrightText: 2006 Michaël Larouche &lt;michael.larouche@kdemail.net&gt;
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 &param)
{
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 &param)
{
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> &parameters)
{
QString paramString = group;
QString arguments;
int i = 1;
bool firstArg = true;
for (const auto &param : 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 &param, const QString &paramValue)
{
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