Commit 7d5806ff authored by Bhushan Shah's avatar Bhushan Shah 📱 Committed by Bhushan Shah
Browse files

kcms: move some of the KCMS to plasma-workspace

We currently need the lookandfeel and style kcm in plasma-workspace, but
appearantly they depend on the krdb so best solution is to move all kcms
together

They are taken from plasma-desktop at the commit :
8447c08e878ccbde9c5516ba8a912993ea199cb9
parent f1bee4f4
......@@ -25,6 +25,7 @@ include(FeatureSummary)
include(ECMOptionalAddSubdirectory)
include(ECMQtDeclareLoggingCategory)
include(ECMQueryQmake)
include(ECMInstallIcons)
include(KDEPackageAppTemplates)
include(KDEClangFormat)
......@@ -87,6 +88,14 @@ set_package_properties(ZLIB PROPERTIES DESCRIPTION "Support for gzip compressed
TYPE REQUIRED
)
find_package(Fontconfig)
set_package_properties(Fontconfig PROPERTIES DESCRIPTION "Font access configuration library"
URL "https://www.freedesktop.org/wiki/Software/fontconfig"
TYPE OPTIONAL
PURPOSE "Needed to build font configuration and installation tools"
)
find_package(X11)
set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
URL "https://www.x.org"
......@@ -102,7 +111,7 @@ find_package(PlasmaWaylandProtocols REQUIRED)
find_package(Wayland REQUIRED COMPONENTS Client)
if(X11_FOUND)
find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR)
find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR IMAGE)
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
if(NOT X11_SM_FOUND)
message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n")
......
......@@ -155,3 +155,5 @@
/** place where plasma-frameworks things are installed */
#define PLASMA_RELATIVE_DATA_INSTALL_DIR "@PLASMA_RELATIVE_DATA_INSTALL_DIR@"
#define WORKSPACE_VERSION_STRING "${PROJECT_VERSION}"
......@@ -5,3 +5,18 @@ add_subdirectory(translations)
if(KUserFeedback_FOUND)
add_subdirectory(feedback)
endif()
add_subdirectory(style)
add_subdirectory(krdb)
add_subdirectory(lookandfeel)
add_subdirectory(colors)
if(X11_Xcursor_FOUND)
add_subdirectory(cursortheme)
endif()
set(libkxftconfig_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/fonts/kxftconfig.cpp )
if(FONTCONFIG_FOUND AND FREETYPE_FOUND)
add_subdirectory( kfontinst )
add_subdirectory( fonts )
endif()
# KI18N Translation Domain for this library
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_colors\")
set(kcm_colors_SRCS
../krdb/krdb.cpp
colors.cpp
colorsdata.cpp
colorsmodel.cpp
filterproxymodel.cpp
)
# needed for krdb
kconfig_add_kcfg_files(kcm_colors_SRCS colorssettings.kcfgc GENERATE_MOC)
add_library(kcm_colors MODULE ${kcm_colors_SRCS})
target_link_libraries(kcm_colors
Qt5::DBus
KF5::KCMUtils
KF5::CoreAddons
KF5::Declarative
KF5::GuiAddons
KF5::I18n
KF5::KIOCore
KF5::KIOWidgets
KF5::NewStuffCore
KF5::QuickAddons
KF5::WindowSystem
PW::KWorkspace
)
if(X11_FOUND)
target_link_libraries(kcm_colors ${X11_LIBRARIES} Qt5::X11Extras)
endif()
kcoreaddons_desktop_to_json(kcm_colors "kcm_colors.desktop")
install(FILES colorssettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(FILES kcm_colors.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(TARGETS kcm_colors DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install(FILES colorschemes.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR})
kpackage_install_package(package kcm_colors kcms)
add_subdirectory(editor)
#! /usr/bin/env bash
$EXTRACTRC `find . -name "*.ui" -o -name "*.kcfg"` >> rc.cpp
$XGETTEXT `find . -name "*.cpp" -o -name "*.qml"` -o $podir/kcm_colors.pot
rm -f rc.cpp
There are two sets of preview strings in the color kcm, with special translation needs. In general, length of the translations, and "gist"-level clarity is more important than precise translation. It should be easy to identify what color role a string represents, but there are size considerations that should be taken into account. The tooltips should be translated normally.
When translating the color kcm, it is helpful to understand the intended meaning of the various color roles in order to chose the best possible translation if more than one would be possible. Hopefully the sets (view, window, button, selection, tooltip) are obvious, as well as normal background/foreground. The others are as follows:
Alternate [Background]: used in lists when every other row uses a different color; "different".
Inactive: comments, something which is old, unimportant/uninteresting/secondary text
Active: e.g. something new, something active (currently engaged in activity), something requesting attention, a hovered hyperlink
Link: hyperlink, somewhere the user can go, new history
Visited: visited hyperlinks, somewhere the user has been, old history
Negative: "bad", untrusted, unreliable, a mistake, an error the user made, an error that occurred
Neutral: warning, encrypted, anything "between" negative and positive
Positive: success, completion messages, trusted content
For Link, although other uses are possible, translate as referring to hyperlinks to minimize user confusion.
The "generic" preview uses these strings (all with disambiguation "color-kcm-preview"):
- Window Text
- Push Button
- Normal Text
- Selected Text
- link
- visited
- a
- i
- !
- =
- +
The two-word strings may be translated normally, though it is suggested to omit the translation of "Text" if the strings would be excessively (around 20+ characters) long and doing so does not cause confusion (i.e. substantially change the conveyed meaning). The "link" and "visited" strings should be translated according to the notes above, ideally keeping the length to four to eight characters; abbreviation (to not less than four characters if possible) is acceptable as the tooltips will provide more complete descriptions.
The remaining one-character strings represent (respectively) the text roles active (a), inactive (i), negative (!), neutral (=) and positive (+). These should be translated as one- or two-character strings which uniquely correspond to the aforementioned text roles, ideally taking the first letter of the corresponding translation, or maintaining the symbols (!, =, +) as-is if culturally appropriate.
Ideographic languages (e.g. Japanese, Mandarin, etc.) where the single-character strings can be replaced with one or two characters representing the full word should do so. These should also, if needed, consider a phonetic representation or relevant phrase for the longer elements, to keep the translations close to the same number of characters as the English.
The "set" preview uses these strings (all with disambiguation "color-kcm-set-preview"):
- normal
- link
- visited
- active
- inactive
- alternate
- negative
- neutral
- positive
- hover
- focus
These live in a preview widget that is two rows tall by nine items wide, and is often the determining factor in the minimum width of the dialog. For this reason, it is important to keep the strings SHORT. To ensure each text block is of sufficient size to provide an adequate preview, each should be at least a few characters long, but please try to limit them to no more than eight or ten characters on average. Abbreviation is fine and encouraged if necessary to achieve this average. In particular, PLEASE avoid adding the translated equivalent of " text" to these strings if at all possible; the tooltips will disambiguate if required. If these objectives cannot be achieved via direct translation, semantic translation (e.g. "negative" -> i18n("oops!"), "neutral" -> i18n("meh")) would be better. (Ideographic languages, which may need to ADD text to achieve a minimum length, should ignore the previous two sentences.) Note also that "hover" and "focus" are actually "decoration" roles, so e.g. "hover text" is incorrect (should be "hover decoration").
/*
* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
* Copyright (C) 2007 Jeremy Whiting <jpwhiting@kde.org>
* Copyright (C) 2016 Olivier Churlaud <olivier@churlaud.com>
* Copyright (C) 2019 Kai Uwe Broulik <kde@privat.broulik.de>
* Copyright (c) 2019 Cyril Rossi <cyril.rossi@enioka.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "colors.h"
#include <QDBusConnection>
#include <QDBusMessage>
#include <QFileInfo>
#include <QGuiApplication>
#include <QProcess>
#include <QStandardItemModel>
#include <QStandardPaths>
#include <QQuickItem>
#include <QQuickRenderControl>
#include <QQuickWindow>
#include <KAboutData>
#include <KColorUtils>
#include <KColorScheme>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KPluginFactory>
#include <KWindowSystem>
#include <KIO/FileCopyJob>
#include <KIO/DeleteJob>
#include <KIO/JobUiDelegate>
#include <knewstuffcore_version.h>
#if KNEWSTUFFCORE_VERSION_MAJOR==5 && KNEWSTUFFCORE_VERSION_MINOR>=68
#include <KNSCore/EntryWrapper>
#endif
#include <algorithm>
#include "../krdb/krdb.h"
#include "colorsmodel.h"
#include "filterproxymodel.h"
#include "colorssettings.h"
#include "colorsdata.h"
K_PLUGIN_FACTORY_WITH_JSON(KCMColorsFactory, "kcm_colors.json", registerPlugin<KCMColors>();registerPlugin<ColorsData>();)
KCMColors::KCMColors(QObject *parent, const QVariantList &args)
: KQuickAddons::ManagedConfigModule(parent, args)
, m_model(new ColorsModel(this))
, m_filteredModel(new FilterProxyModel(this))
, m_data(new ColorsData(this))
, m_config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")))
{
qmlRegisterUncreatableType<KCMColors>("org.kde.private.kcms.colors", 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM"));
qmlRegisterType<ColorsModel>();
qmlRegisterType<FilterProxyModel>();
qmlRegisterType<ColorsSettings>();
KAboutData *about = new KAboutData(QStringLiteral("kcm_colors"), i18n("Colors"),
QStringLiteral("2.0"), QString(), KAboutLicense::GPL);
about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de"));
setAboutData(about);
connect(m_model, &ColorsModel::pendingDeletionsChanged, this, &KCMColors::settingsChanged);
connect(m_model, &ColorsModel::selectedSchemeChanged, this, [this](const QString &scheme) {
m_selectedSchemeDirty = true;
colorsSettings()->setColorScheme(scheme);
});
connect(colorsSettings(), &ColorsSettings::colorSchemeChanged, this, [this] {
m_model->setSelectedScheme(colorsSettings()->colorScheme());
});
connect(m_model, &ColorsModel::selectedSchemeChanged, m_filteredModel, &FilterProxyModel::setSelectedScheme);
m_filteredModel->setSourceModel(m_model);
}
KCMColors::~KCMColors()
{
m_config->markAsClean();
}
ColorsModel *KCMColors::model() const
{
return m_model;
}
FilterProxyModel *KCMColors::filteredModel() const
{
return m_filteredModel;
}
ColorsSettings *KCMColors::colorsSettings() const
{
return m_data->settings();
}
bool KCMColors::downloadingFile() const
{
return m_tempCopyJob;
}
void KCMColors::reloadModel(const QQmlListReference &changedEntries)
{
m_model->load();
#if KNEWSTUFFCORE_VERSION_MAJOR==5 && KNEWSTUFFCORE_VERSION_MINOR>=68
// If a new theme was installed, select the first color file in it
if (changedEntries.count() > 0) {
QStringList installedThemes;
const QString suffix = QStringLiteral(".colors");
for (int i = 0; i < changedEntries.count(); ++i) {
KNSCore::EntryWrapper* entry = qobject_cast<KNSCore::EntryWrapper*>(changedEntries.at(i));
if (entry && entry->entry().status() == KNS3::Entry::Installed) {
for (const QString &path : entry->entry().installedFiles()) {
const QString fileName = path.section(QLatin1Char('/'), -1, -1);
const int suffixPos = fileName.indexOf(suffix);
if (suffixPos != fileName.length() - suffix.length()) {
continue;
}
installedThemes.append(fileName.left(suffixPos));
}
if (!installedThemes.isEmpty()) {
// The list is sorted by (potentially translated) name
// but that would require us parse every file, so this should be close enough
std::sort(installedThemes.begin(), installedThemes.end());
m_model->setSelectedScheme(installedThemes.constFirst());
}
// Only do this for the first newly installed theme we find
break;
}
}
}
#endif
}
void KCMColors::installSchemeFromFile(const QUrl &url)
{
if (url.isLocalFile()) {
installSchemeFile(url.toLocalFile());
return;
}
if (m_tempCopyJob) {
return;
}
m_tempInstallFile.reset(new QTemporaryFile());
if (!m_tempInstallFile->open()) {
emit showErrorMessage(i18n("Unable to create a temporary file."));
m_tempInstallFile.reset();
return;
}
// Ideally we copied the file into the proper location right away but
// (for some reason) we determine the file name from the "Name" inside the file
m_tempCopyJob = KIO::file_copy(url, QUrl::fromLocalFile(m_tempInstallFile->fileName()),
-1, KIO::Overwrite);
m_tempCopyJob->uiDelegate()->setAutoErrorHandlingEnabled(true);
emit downloadingFileChanged();
connect(m_tempCopyJob, &KIO::FileCopyJob::result, this, [this, url](KJob *job) {
if (job->error() != KJob::NoError) {
emit showErrorMessage(i18n("Unable to download the color scheme: %1", job->errorText()));
return;
}
installSchemeFile(m_tempInstallFile->fileName());
m_tempInstallFile.reset();
});
connect(m_tempCopyJob, &QObject::destroyed, this, &KCMColors::downloadingFileChanged);
}
void KCMColors::installSchemeFile(const QString &path)
{
KSharedConfigPtr config = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
KConfigGroup group(config, "General");
const QString name = group.readEntry("Name");
if (name.isEmpty()) {
emit showErrorMessage(i18n("This file is not a color scheme file."));
return;
}
// Do not overwrite another scheme
int increment = 0;
QString newName = name;
QString testpath;
do {
if (increment) {
newName = name + QString::number(increment);
}
testpath = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QStringLiteral("color-schemes/%1.colors").arg(newName));
increment++;
} while (!testpath.isEmpty());
QString newPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/color-schemes/");
if (!QDir().mkpath(newPath)) {
emit showErrorMessage(i18n("Failed to create 'color-scheme' data folder."));
return;
}
newPath += newName + QLatin1String(".colors");
if (!QFile::copy(path, newPath)) {
emit showErrorMessage(i18n("Failed to copy color scheme into 'color-scheme' data folder."));
return;
}
// Update name
KSharedConfigPtr config2 = KSharedConfig::openConfig(newPath, KConfig::SimpleConfig);
KConfigGroup group2(config2, "General");
group2.writeEntry("Name", newName);
config2->sync();
m_model->load();
const auto results = m_model->match(m_model->index(0, 0), ColorsModel::SchemeNameRole, newName);
if (!results.isEmpty()) {
m_model->setSelectedScheme(newName);
}
emit showSuccessMessage(i18n("Color scheme installed successfully."));
}
void KCMColors::editScheme(const QString &schemeName, QQuickItem *ctx)
{
if (m_editDialogProcess) {
return;
}
QModelIndex idx = m_model->index(m_model->indexOfScheme(schemeName), 0);
m_editDialogProcess = new QProcess(this);
connect(m_editDialogProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
[this](int exitCode, QProcess::ExitStatus exitStatus) {
Q_UNUSED(exitCode);
Q_UNUSED(exitStatus);
const auto savedThemes = QString::fromUtf8(m_editDialogProcess->readAllStandardOutput()).split(QLatin1Char('\n'), QString::SkipEmptyParts);
if (!savedThemes.isEmpty()) {
m_model->load(); // would be cool to just reload/add the changed/new ones
// If the currently active scheme was edited, consider settings dirty even if the scheme itself didn't change
if (savedThemes.contains(colorsSettings()->colorScheme())) {
m_activeSchemeEdited = true;
settingsChanged();
}
m_model->setSelectedScheme(savedThemes.last());
}
m_editDialogProcess->deleteLater();
m_editDialogProcess = nullptr;
});
QStringList args;
args << idx.data(ColorsModel::SchemeNameRole).toString();
if (idx.data(ColorsModel::RemovableRole).toBool()) {
args << QStringLiteral("--overwrite");
}
if (ctx && ctx->window()) {
// QQuickWidget, used for embedding QML KCMs, renders everything into an offscreen window
// Qt is able to resolve this on its own when setting transient parents in-process.
// However, since we pass the ID to an external process which has no idea of this
// we need to resolve the actual window we end up showing in.
if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(ctx->window())) {
if (KWindowSystem::isPlatformX11()) {
// TODO wayland: once we have foreign surface support
args << QStringLiteral("--attach") << (QStringLiteral("x11:") + QString::number(actualWindow->winId()));
}
}
}
m_editDialogProcess->start(QStringLiteral("kcolorschemeeditor"), args);
}
bool KCMColors::isSaveNeeded() const
{
return m_activeSchemeEdited || !m_model->match(m_model->index(0, 0), ColorsModel::PendingDeletionRole, true).isEmpty();
}
void KCMColors::load()
{
ManagedConfigModule::load();
m_model->load();
m_config->markAsClean();
m_config->reparseConfiguration();
const QString schemeName = colorsSettings()->colorScheme();
// If the scheme named in kdeglobals doesn't exist, show a warning and use default scheme
if (m_model->indexOfScheme(schemeName) == -1) {
m_model->setSelectedScheme(colorsSettings()->defaultColorSchemeValue());
// These are normally synced but initially the model doesn't emit a change to avoid the
// Apply button from being enabled without any user interaction. Sync manually here.
m_filteredModel->setSelectedScheme(colorsSettings()->defaultColorSchemeValue());
emit showSchemeNotInstalledWarning(schemeName);
} else {
m_model->setSelectedScheme(schemeName);
m_filteredModel->setSelectedScheme(schemeName);
}
{
KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals);
KConfigGroup group(m_config, "General");
group = KConfigGroup(&cfg, "X11");
m_applyToAlien = group.readEntry("exportKDEColors", true);
}
// If need save is true at the end of load() function, it will stay disabled forever.
// setSelectedScheme() call due to unexisting scheme name in kdeglobals will trigger a need to save.
// this following call ensure the apply button will work properly.
setNeedsSave(false);
}
void KCMColors::save()
{
// We need to save the colors change first, to avoid a situation,
// when we announced that the color scheme has changed, but
// the colors themselves in the color scheme have not yet
if (m_selectedSchemeDirty || m_activeSchemeEdited) {
saveColors();
}
ManagedConfigModule::save();
m_activeSchemeEdited = false;
processPendingDeletions();
}
void KCMColors::saveColors()
{
const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
QStringLiteral("color-schemes/%1.colors").arg(m_model->selectedScheme()));
// Using KConfig::SimpleConfig because otherwise Header colors won't be
// rewritten when a new color scheme is loaded.
KSharedConfigPtr config = KSharedConfig::openConfig(path, KConfig::SimpleConfig);
const QStringList colorSetGroupList{
QStringLiteral("Colors:View"),
QStringLiteral("Colors:Window"),
QStringLiteral("Colors:Button"),
QStringLiteral("Colors:Selection"),
QStringLiteral("Colors:Tooltip"),
QStringLiteral("Colors:Complementary"),
QStringLiteral("Colors:Header")
};
const QList<KColorScheme> colorSchemes{
KColorScheme(QPalette::Active, KColorScheme::View, config),
KColorScheme(QPalette::Active, KColorScheme::Window, config),
KColorScheme(QPalette::Active, KColorScheme::Button, config),
KColorScheme(QPalette::Active, KColorScheme::Selection, config),
KColorScheme(QPalette::Active, KColorScheme::Tooltip, config),
KColorScheme(QPalette::Active, KColorScheme::Complementary, config),
KColorScheme(QPalette::Active, KColorScheme::Header, config)
};
for (int i = 0; i < colorSchemes.length(); ++i) {
KConfigGroup group(m_config, colorSetGroupList.value(i));
group.writeEntry("BackgroundNormal", colorSchemes[i].background(KColorScheme::NormalBackground).color());
group.writeEntry("BackgroundAlternate", colorSchemes[i].background(KColorScheme::AlternateBackground).color());
group.writeEntry("ForegroundNormal", colorSchemes[i].foreground(KColorScheme::NormalText).color());
group.writeEntry("ForegroundInactive", colorSchemes[i].foreground(KColorScheme::InactiveText).color());
group.writeEntry("ForegroundActive", colorSchemes[i].foreground(KColorScheme::ActiveText).color());
group.writeEntry("ForegroundLink", colorSchemes[i].foreground(KColorScheme::LinkText).color());
group.writeEntry("ForegroundVisited", colorSchemes[i].foreground(KColorScheme::VisitedText).color());
group.writeEntry("ForegroundNegative", colorSchemes[i].foreground(KColorScheme::NegativeText).color());
group.writeEntry("ForegroundNeutral", colorSchemes[i].foreground(KColorScheme::NeutralText).color());
group.writeEntry("ForegroundPositive", colorSchemes[i].foreground(KColorScheme::PositiveText).color());
group.writeEntry("DecorationFocus", colorSchemes[i].decoration(KColorScheme::FocusColor).color());
group.writeEntry("DecorationHover", colorSchemes[i].decoration(KColorScheme::HoverColor).color());
}
KConfigGroup groupWMTheme(config, "WM");
KConfigGroup groupWMOut(m_config, "WM");
KColorScheme inactiveHeaderColorScheme(QPalette::Inactive, KColorScheme::Header, config);
const QStringList colorItemListWM{
QStringLiteral("activeBackground"),
QStringLiteral("activeForeground"),
QStringLiteral("inactiveBackground"),
QStringLiteral("inactiveForeground"),
QStringLiteral("activeBlend"),
QStringLiteral("inactiveBlend")
};
const QVector<QColor> defaultWMColors{
colorSchemes[KColorScheme::Header].background().color(),
colorSchemes[KColorScheme::Header].foreground().color(),
inactiveHeaderColorScheme.background().color(),
inactiveHeaderColorScheme.foreground().color(),
colorSchemes[KColorScheme::Header].background().color(),
inactiveHeaderColorScheme.background().color()
};
int i = 0;
for (const QString &coloritem : colorItemListWM) {
groupWMOut.writeEntry(coloritem, groupWMTheme.readEntry(coloritem, defaultWMColors.value(i)));
++i;
}
const QStringList groupNameList{
QStringLiteral("ColorEffects:Inactive"),
QStringLiteral("ColorEffects:Disabled")
};
const QStringList effectList{
QStringLiteral("Enable"),
QStringLiteral("ChangeSelectionColor"),
QStringLiteral("IntensityEffect"),
QStringLiteral("IntensityAmount"),
QStringLiteral("ColorEffect"),
QStringLiteral("ColorAmount"),
QStringLiteral("Color"),
QStringLiteral("ContrastEffect"),