diff --git a/kcms/colors/CMakeLists.txt b/kcms/colors/CMakeLists.txt index 00f1d925f..9af35c071 100644 --- a/kcms/colors/CMakeLists.txt +++ b/kcms/colors/CMakeLists.txt @@ -1,94 +1,43 @@ # KI18N Translation Domain for this library -add_definitions(-DTRANSLATION_DOMAIN=\"kcmcolors\") - -set(scheme_editor_SRCS - kcolorschemeeditor.cpp - scmeditordialog.cpp - scmeditoroptions.cpp - scmeditorcolors.cpp - scmeditoreffects.cpp - previewwidget.cpp - setpreviewwidget.cpp - colorscm.cpp - ../krdb/krdb.cpp - ) - -set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) - -ki18n_wrap_ui(scheme_editor_SRCS - colorsettings.ui - scmeditordialog.ui - scmeditoroptions.ui - scmeditorcolors.ui - scmeditoreffects.ui - preview.ui - setpreview.ui - ) - -qt5_add_dbus_interface(scheme_editor_SRCS ${klauncher_xml} klauncher_iface) - -add_executable(kcolorschemeeditor ${scheme_editor_SRCS}) - -target_link_libraries(kcolorschemeeditor - KF5::KCMUtils - KF5::GuiAddons - KF5::I18n - KF5::KIOCore - KF5::CoreAddons - KF5::NewStuff - KF5::WindowSystem - ) -if(X11_FOUND) - target_link_libraries(kcolorschemeeditor ${X11_LIBRARIES} Qt5::X11Extras) -endif() -install(TARGETS kcolorschemeeditor DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) -install(FILES org.kde.kcolorschemeeditor.desktop DESTINATION ${KDE_INSTALL_APPDIR}) - -# ---------------- +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_colors\") set(kcm_colors_SRCS ../krdb/krdb.cpp - colorscm.cpp - scmeditordialog.cpp - scmeditoroptions.cpp - scmeditorcolors.cpp - scmeditoreffects.cpp - previewwidget.cpp - setpreviewwidget.cpp - ) - + colors.cpp +) -qt5_add_dbus_interface(kcm_colors_SRCS ${klauncher_xml} klauncher_iface) - -ki18n_wrap_ui(kcm_colors_SRCS - colorsettings.ui - scmeditordialog.ui - scmeditoroptions.ui - scmeditorcolors.ui - scmeditoreffects.ui - preview.ui - setpreview.ui) +# needed for krdb +qt5_add_dbus_interface(kcm_colors_SRCS ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml klauncher_iface) add_library(kcm_colors MODULE ${kcm_colors_SRCS}) -add_dependencies(kcm_colors kcolorschemeeditor) target_link_libraries(kcm_colors + Qt5::DBus KF5::KCMUtils + KF5::CoreAddons + KF5::Declarative KF5::GuiAddons KF5::I18n KF5::KIOCore - KF5::CoreAddons - Qt5::DBus + KF5::KIOWidgets KF5::NewStuff - KF5::WindowSystem) + KF5::QuickAddons + KF5::WindowSystem +) if(X11_FOUND) target_link_libraries(kcm_colors ${X11_LIBRARIES} Qt5::X11Extras) endif() -install(TARGETS kcm_colors DESTINATION ${KDE_INSTALL_PLUGINDIR}) -install( FILES colors.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) -install( FILES colorschemes.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} ) +kcoreaddons_desktop_to_json(kcm_colors "kcm_colors.desktop") + +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_CONFDIR}) + +kpackage_install_package(package kcm_colors kcms) # built-in color schemes file(GLOB schemefiles schemes/*.colors) install( FILES ${schemefiles} DESTINATION ${KDE_INSTALL_DATADIR}/color-schemes ) + +add_subdirectory(editor) diff --git a/kcms/colors/Messages.sh b/kcms/colors/Messages.sh index 71fb50bec..9f8d15c7b 100644 --- a/kcms/colors/Messages.sh +++ b/kcms/colors/Messages.sh @@ -1,4 +1,4 @@ #! /usr/bin/env bash $EXTRACTRC *.ui >> rc.cpp -$XGETTEXT *.cpp -o $podir/kcmcolors.pot +$XGETTEXT `find . -name "*.cpp" -o -name "*.qml"` -o $podir/kcm_colors.pot rm -f rc.cpp diff --git a/kcms/colors/TODO b/kcms/colors/TODO deleted file mode 100644 index a0695b9ff..000000000 --- a/kcms/colors/TODO +++ /dev/null @@ -1,3 +0,0 @@ -query kwin for what WM colors should be shown for configuring(?) -options (i.e. "things on the first tab") not saved in schemes (in kdeglobals only) -sort schemes list, 'current' at 0, 'default' at 1, everything else sorted (possibly kde3-style, user schemes on top?) diff --git a/kcms/colors/colors.cpp b/kcms/colors/colors.cpp new file mode 100644 index 000000000..a1a63538d --- /dev/null +++ b/kcms/colors/colors.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Jeremy Whiting + * Copyright (C) 2016 Olivier Churlaud + * Copyright (C) 2019 Kai Uwe Broulik + * + * 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 . + */ + +#include "colors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../krdb/krdb.h" + +static const QString s_defaultColorSchemeName = QStringLiteral("Breeze"); + +K_PLUGIN_FACTORY_WITH_JSON(KCMColorsFactory, "kcm_colors.json", registerPlugin();) + +KCMColors::KCMColors(QObject *parent, const QVariantList &args) + : KQuickAddons::ConfigModule(parent, args) + , m_config(KSharedConfig::openConfig(QStringLiteral("kdeglobals"))) +{ + qmlRegisterType(); + + KAboutData *about = new KAboutData(QStringLiteral("kcm_colors"), i18n("Choose the color scheme"), + QStringLiteral("2.0"), QString(), KAboutLicense::GPL); + about->addAuthor(i18n("Kai Uwe Broulik"), QString(), QStringLiteral("kde@privat.broulik.de")); + setAboutData(about); + + m_model = new QStandardItemModel(this); + m_model->setItemRoleNames({ + {Qt::DisplayRole, QByteArrayLiteral("display")}, + {SchemeNameRole, QByteArrayLiteral("schemeName")}, + {PaletteRole, QByteArrayLiteral("palette")}, + {RemovableRole, QByteArrayLiteral("removable")}, + {PendingDeletionRole, QByteArrayLiteral("pendingDeletion")} + }); +} + +KCMColors::~KCMColors() +{ + m_config->markAsClean(); +} + +QStandardItemModel *KCMColors::colorsModel() const +{ + return m_model; +} + +QString KCMColors::selectedScheme() const +{ + return m_selectedScheme; +} + +void KCMColors::setSelectedScheme(const QString &scheme) +{ + if (m_selectedScheme == scheme) { + return; + } + + const bool firstTime = m_selectedScheme.isNull(); + m_selectedScheme = scheme; + emit selectedSchemeChanged(); + emit selectedSchemeIndexChanged(); + + if (!firstTime) { + setNeedsSave(true); + m_selectedSchemeDirty = true; + } +} + +int KCMColors::selectedSchemeIndex() const +{ + return indexOfScheme(m_selectedScheme); +} + +int KCMColors::indexOfScheme(const QString &schemeName) const +{ + const auto results = m_model->match(m_model->index(0, 0), SchemeNameRole, schemeName); + if (results.count() == 1) { + return results.first().row(); + } + + return -1; +} + +bool KCMColors::downloadingFile() const +{ + return m_tempCopyJob; +} + +void KCMColors::setPendingDeletion(int index, bool pending) +{ + QModelIndex idx = m_model->index(index, 0); + + m_model->setData(idx, pending, PendingDeletionRole); + + if (pending && selectedSchemeIndex() == index) { + // move to the next non-pending theme + const auto nonPending = m_model->match(idx, PendingDeletionRole, false); + setSelectedScheme(nonPending.first().data(SchemeNameRole).toString()); + } + + setNeedsSave(true); +} + +void KCMColors::loadModel() +{ + m_model->clear(); + + QStringList schemeFiles; + + const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); + for (const QString &dir : schemeDirs) { + const QStringList fileNames = QDir(dir).entryList(QStringList{QStringLiteral("*.colors")}); + for (const QString &file : fileNames) { + const QString suffixedFileName = QStringLiteral("color-schemes/") + file; + // can't use QSet because of the transform below (passing const QString as this argument discards qualifiers) + if (!schemeFiles.contains(suffixedFileName)) { + schemeFiles.append(suffixedFileName); + } + } + } + + std::transform(schemeFiles.begin(), schemeFiles.end(), schemeFiles.begin(), [](const QString &item) { + return QStandardPaths::locate(QStandardPaths::GenericDataLocation, item); + }); + + for (const QString &schemeFile : schemeFiles) { + const QFileInfo fi(schemeFile); + const QString baseName = fi.baseName(); + + KSharedConfigPtr config = KSharedConfig::openConfig(schemeFile, KConfig::SimpleConfig); + KConfigGroup group(config, "General"); + const QString name = group.readEntry("Name", baseName); + + QStandardItem *item = new QStandardItem(name); + item->setData(baseName, SchemeNameRole); + item->setData(fi.isWritable(), RemovableRole); + item->setData(false, PendingDeletionRole); + + item->setData(KColorScheme::createApplicationPalette(config), PaletteRole); + + m_model->appendRow(item); + } + + m_model->sort(0 /*column*/); +} + +void KCMColors::getNewStuff(QQuickItem *ctx) +{ + if (!m_newStuffDialog) { + m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("colorschemes.knsrc")); + m_newStuffDialog.data()->setWindowTitle(i18n("Download New Color Schemes")); + m_newStuffDialog->setWindowModality(Qt::WindowModal); + m_newStuffDialog->winId(); // so it creates the windowHandle(); + + connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, [this] { + loadModel(); + + const auto newEntries = m_newStuffDialog->installedEntries(); + // If one new theme was installed, select the first color file in it + if (newEntries.count() == 1) { + QStringList installedThemes; + + const QString suffix = QStringLiteral(".colors"); + + for (const QString &path : newEntries.first().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()); + + setSelectedScheme(installedThemes.constFirst()); + } + } + }); + } + + if (ctx && ctx->window()) { + m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); + } + + m_newStuffDialog.data()->show(); +} + +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(); + + loadModel(); + + const auto results = m_model->match(m_model->index(0, 0), SchemeNameRole, newName); + if (!results.isEmpty()) { + setSelectedScheme(newName); + } + + emit showSuccessMessage(i18n("Color scheme installed successfully.")); +} + +void KCMColors::editScheme(int index, QQuickItem *ctx) +{ + if (m_editDialogProcess) { + return; + } + + QModelIndex idx = m_model->index(index, 0); + + m_editDialogProcess = new QProcess(this); + connect(m_editDialogProcess, QOverload::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()) { + loadModel(); // would be cool to just reload/add the changed/new ones + + setSelectedScheme(savedThemes.last()); + } + + m_editDialogProcess->deleteLater(); + m_editDialogProcess = nullptr; + }); + + QStringList args; + args << idx.data(KCMColors::SchemeNameRole).toString(); + if (idx.data(KCMColors::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); +} + +void KCMColors::load() +{ + loadModel(); + + m_config->markAsClean(); + m_config->reparseConfiguration(); + + KConfigGroup group(m_config, "General"); + const QString schemeName = group.readEntry("ColorScheme", s_defaultColorSchemeName); + + // If the scheme named in kdeglobals doesn't exist, show a warning and use default scheme + if (indexOfScheme(schemeName) == -1) { + setSelectedScheme(s_defaultColorSchemeName); + emit showSchemeNotInstalledWarning(schemeName); + } else { + setSelectedScheme(schemeName); + } + + { + KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); + group = KConfigGroup(&cfg, "X11"); + m_applyToAlien = group.readEntry("exportKDEColors", true); + } +} + +void KCMColors::save() +{ + if (m_selectedSchemeDirty) { + saveColors(); + } + + processPendingDeletions(); + + setNeedsSave(false); +} + +void KCMColors::saveColors() +{ + KConfigGroup grp(m_config, "General"); + grp.writeEntry("ColorScheme", m_selectedScheme); + + const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, + QStringLiteral("color-schemes/%1.colors").arg(m_selectedScheme)); + + KSharedConfigPtr config = KSharedConfig::openConfig(path); + + const QStringList colorSetGroupList{ + QStringLiteral("Colors:View"), + QStringLiteral("Colors:Window"), + QStringLiteral("Colors:Button"), + QStringLiteral("Colors:Selection"), + QStringLiteral("Colors:Tooltip"), + QStringLiteral("Colors:Complementary") + }; + + const QList 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) + }; + + 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"); + + const QStringList colorItemListWM{ + QStringLiteral("activeBackground"), + QStringLiteral("activeForeground"), + QStringLiteral("inactiveBackground"), + QStringLiteral("inactiveForeground"), + QStringLiteral("activeBlend"), + QStringLiteral("inactiveBlend") + }; + + const QVector defaultWMColors{ + QColor(71,80,87), + QColor(239,240,241), + QColor(239,240,241), + QColor(189,195,199), + QColor(255,255,255), + QColor(75,71,67) + }; + + 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"), + QStringLiteral("ContrastAmount") + }; + + for (const QString &groupName : groupNameList) { + KConfigGroup groupEffectOut(m_config, groupName); + KConfigGroup groupEffectTheme(config, groupName); + + for (const QString &effect : effectList) { + groupEffectOut.writeEntry(effect, groupEffectTheme.readEntry(effect)); + } + } + + m_config->sync(); + + runRdb(KRdbExportQtColors | KRdbExportGtkTheme | (m_applyToAlien ? KRdbExportColors : 0)); + + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KGlobalSettings"), + QStringLiteral("org.kde.KGlobalSettings"), + QStringLiteral("notifyChange")); + message.setArguments({ + 0, //previous KGlobalSettings::PaletteChanged. This is now private API in khintsettings + 0 //unused in palette changed but needed for the DBus signature + }); + QDBusConnection::sessionBus().send(message); + + if (KWindowSystem::isPlatformX11()) { + // Send signal to all kwin instances + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); + QDBusConnection::sessionBus().send(message); + } + + m_selectedSchemeDirty = false; +} + +void KCMColors::processPendingDeletions() +{ + const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true, -1 /*all*/); + QVector persistentPendingDeletions; + // turn into persistent model index so we can delete as we go + std::transform(pendingDeletions.begin(), pendingDeletions.end(), + std::back_inserter(persistentPendingDeletions), [](const QModelIndex &idx) { + return QPersistentModelIndex(idx); + }); + + for (const QPersistentModelIndex &idx : persistentPendingDeletions) { + const QString schemeName = idx.data(SchemeNameRole).toString(); + + Q_ASSERT(schemeName != m_selectedScheme); + + const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, + QStringLiteral("color-schemes/%1.colors").arg(schemeName)); + + auto *job = KIO::del(QUrl::fromLocalFile(path), KIO::HideProgressInfo); + // needs to block for it to work on "OK" where the dialog (kcmshell) closes + job->exec(); + } + + // remove them in a separate loop after all the delete jobs for a smoother animation + for (const QPersistentModelIndex &idx : persistentPendingDeletions) { + m_model->removeRow(idx.row()); + } +} + +void KCMColors::defaults() +{ + setSelectedScheme(s_defaultColorSchemeName); + + setNeedsSave(true); +} + +#include "colors.moc" diff --git a/kcms/colors/colors.h b/kcms/colors/colors.h new file mode 100644 index 000000000..c4f0cbeaa --- /dev/null +++ b/kcms/colors/colors.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Kai Uwe Broulik + * + * 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 . + */ + +#pragma once + +#include +#include + +#include + +#include + +#include + +class QStandardItemModel; +class QProcess; +class QTemporaryFile; + +namespace KIO +{ +class FileCopyJob; +} + +class KCMColors : public KQuickAddons::ConfigModule +{ + Q_OBJECT + + Q_PROPERTY(QStandardItemModel *colorsModel READ colorsModel CONSTANT) + Q_PROPERTY(QString selectedScheme READ selectedScheme WRITE setSelectedScheme NOTIFY selectedSchemeChanged) + Q_PROPERTY(int selectedSchemeIndex READ selectedSchemeIndex NOTIFY selectedSchemeIndexChanged) + Q_PROPERTY(bool downloadingFile READ downloadingFile NOTIFY downloadingFileChanged) + +public: + KCMColors(QObject *parent, const QVariantList &args); + ~KCMColors() override; + + enum Roles { + SchemeNameRole = Qt::UserRole + 1, + PaletteRole, + RemovableRole, + PendingDeletionRole + }; + + QStandardItemModel *colorsModel() const; + + QString selectedScheme() const; + void setSelectedScheme(const QString &scheme); + + int selectedSchemeIndex() const; + + bool downloadingFile() const; + + Q_INVOKABLE void getNewStuff(QQuickItem *ctx); + Q_INVOKABLE void installSchemeFromFile(const QUrl &url); + + Q_INVOKABLE void setPendingDeletion(int index, bool pending); + + Q_INVOKABLE void editScheme(int index, QQuickItem *ctx); + +public Q_SLOTS: + void load() override; + void save() override; + void defaults() override; + +Q_SIGNALS: + void selectedSchemeChanged(); + void selectedSchemeIndexChanged(); + void downloadingFileChanged(); + + void showSuccessMessage(const QString &message); + void showErrorMessage(const QString &message); + + void showSchemeNotInstalledWarning(const QString &schemeName); + +private: + void loadModel(); + + void saveColors(); + void processPendingDeletions(); + + int indexOfScheme(const QString &schemeName) const; + + void installSchemeFile(const QString &path); + + QStandardItemModel *m_model; + + QString m_selectedScheme; + bool m_selectedSchemeDirty = false; + + bool m_applyToAlien = true; + + QPointer m_newStuffDialog; + + QProcess *m_editDialogProcess = nullptr; + + KSharedConfigPtr m_config; + + QScopedPointer m_tempInstallFile; + QPointer m_tempCopyJob; + +}; diff --git a/kcms/colors/colorscm.cpp b/kcms/colors/colorscm.cpp deleted file mode 100644 index 4107172c4..000000000 --- a/kcms/colors/colorscm.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/* KDE Display color scheme setup module - * Copyright (C) 2007 Matthew Woehlke - * Copyright (C) 2007 Jeremy Whiting - * Copyright (C) 2016 Olivier Churlaud - * - * 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) any later version. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "colorscm.h" - -#include "../krdb/krdb.h" -#include "scmeditordialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -K_PLUGIN_FACTORY( KolorFactory, registerPlugin(); ) -K_EXPORT_PLUGIN( KolorFactory("kcmcolors") ) - -KColorCm::KColorCm(QWidget *parent, const QVariantList &) - : KCModule( parent ), - m_dontLoadSelectedScheme(false), - m_previousSchemeItem(nullptr) -{ - KAboutData* about = new KAboutData( - QStringLiteral("kcmcolors"), i18n("Colors"), QStringLiteral("1.0"), QString(), - KAboutLicense::GPL, - i18n("(c) 2007 Matthew Woehlke") - ); - about->addAuthor( i18n("Matthew Woehlke"), QString(), - QStringLiteral("mw_triad@users.sourceforge.net") ); - about->addAuthor( i18n("Jeremy Whiting"), QString(), QStringLiteral("jpwhiting@kde.org")); - setAboutData( about ); - - m_config = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); - - setupUi(this); - connect(applyToAlien, &QCheckBox::toggled, [=](){ emit changed(true); }); - connect(schemeList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this, SLOT(loadScheme(QListWidgetItem*,QListWidgetItem*))); - schemeKnsButton->setIcon( QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")) ); -} - -KColorCm::~KColorCm() -{ - m_config->markAsClean(); -} - -void KColorCm::populateSchemeList() -{ - // clear the list in case this is being called from reset button click - schemeList->clear(); - - // add entries - QIcon icon; - - QStringList schemeFiles; - const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); - Q_FOREACH (const QString &dir, schemeDirs) - { - const QStringList fileNames = QDir(dir).entryList(QStringList()<setData(Qt::UserRole, info.baseName()); - schemeList->addItem(newItem); - } - schemeList->sortItems(); - - // add default entry (do this here so that the current and default entry appear at the top) - m_config->setReadDefaults(true); - icon = createSchemePreviewIcon(m_config); - schemeList->insertItem(0, new QListWidgetItem(icon, i18nc("Default color scheme", "Default"))); - m_config->setReadDefaults(false); - - // add current scheme entry - icon = createSchemePreviewIcon(m_config); - QListWidgetItem *currentitem = new QListWidgetItem(icon, i18nc("Current color scheme", "Current")); - schemeList->insertItem(0, currentitem); -} - - -void KColorCm::loadScheme(KSharedConfigPtr config) // const QString &path) -{ - schemePreview->setPalette(config); - updateConfig(config); -} - -void KColorCm::selectPreviousSchemeAgain() -{ - m_dontLoadSelectedScheme = true; - schemeList->setCurrentItem(m_previousSchemeItem); - m_dontLoadSelectedScheme = false; -} - -void KColorCm::loadScheme(QListWidgetItem *currentItem, QListWidgetItem *previousItem) -{ - m_previousSchemeItem = previousItem; - - if (m_dontLoadSelectedScheme) - { - qDebug() << "dontload"; - return; - } - - if (currentItem != nullptr) - { - // load it - const QString name = currentItem->text(); - m_currentColorScheme = name; - const QString fileBaseName = currentItem->data(Qt::UserRole).toString(); - if (name == i18nc("Default color scheme", "Default")) - { - schemeRemoveButton->setEnabled(false); - - KSharedConfigPtr config = m_config; - config->setReadDefaults(true); - loadScheme(config); - config->setReadDefaults(false); - schemeEditButton->setEnabled(true); - // load the default scheme - emit changed(true); - } - else if (name == i18nc("Current color scheme", "Current")) - { - loadInternal(); - m_dontLoadSelectedScheme = true; - schemeList->setCurrentItem(schemeList->item(0)); - m_dontLoadSelectedScheme = false; - schemeRemoveButton->setEnabled(false); - schemeEditButton->setEnabled(true); - } - else - { - const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - "color-schemes/" + fileBaseName + ".colors"); - - const int permissions = QFile(path).permissions(); - const bool canWrite = (permissions & QFile::WriteUser); - qDebug() << "checking permissions of " << path; - schemeRemoveButton->setEnabled(canWrite); - schemeEditButton->setEnabled(true); - - KSharedConfigPtr config = KSharedConfig::openConfig(path); - loadScheme(config); - - emit changed(true); - } - } else { - schemeEditButton->setEnabled(false); - schemeRemoveButton->setEnabled(false); - } -} - -void KColorCm::on_schemeRemoveButton_clicked() -{ - if (schemeList->currentItem() != nullptr) - { - const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - "color-schemes/" + schemeList->currentItem()->data(Qt::UserRole).toString() + - ".colors"); - KIO::DeleteJob *job = KIO::del(QUrl::fromLocalFile(path)); - job->uiDelegate()->setParent(this); - if (job->exec()) - { - delete schemeList->takeItem(schemeList->currentRow()); - } - else - { - // deletion failed, so show an error message - KMessageBox::error(this, i18n("You do not have permission to delete that scheme"), i18n("Error")); - } - } -} - -void KColorCm::on_schemeImportButton_clicked() -{ - // get the path to the scheme to import - QUrl url = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, i18n("Import Color Scheme"))); - - if(!url.isValid()) - { - return; - } - // TODO: possibly untar or uncompress it - // open it - - // load the scheme - KSharedConfigPtr config = KSharedConfig::openConfig(url.path()); - - if (config->groupList().contains(QStringLiteral("Color Scheme"))) - { - KMessageBox::sorry(this, - i18n("The scheme you have selected appears to be a KDE3 scheme.\n\n" - "This is not supported anymore."), - i18n("Notice")); - return; - } - - // Do not overwrite another scheme - KConfigGroup group(config, "General"); - QString name = group.readEntry("Name"); - - int increment = 0; - QString newName = name; - QString testpath; - do - { - if (increment) { - newName = name + QString::number(increment); - } - testpath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - QStringLiteral("color-schemes/") + newName + QStringLiteral(".colors")); - increment++; - } while (!testpath.isEmpty()); - - QString newpath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) - + QStringLiteral("/color-schemes/"); - QDir dir; - dir.mkpath(newpath); - newpath += newName + QStringLiteral(".colors"); - QFile::copy(url.toLocalFile(), newpath); - - // Update name - KSharedConfigPtr config2 = KSharedConfig::openConfig(newpath); - KConfigGroup group2(config2, "General"); - group2.writeEntry("Name", newName); - config2->sync(); - - this->populateSchemeList(); - QList itemList = schemeList->findItems(newName, Qt::MatchExactly); - if (!itemList.isEmpty()) - { - schemeList->setCurrentItem(itemList.first()); - } -} - -void KColorCm::on_schemeKnsButton_clicked() -{ - KNS3::DownloadDialog dialog(QStringLiteral("colorschemes.knsrc"), this); - dialog.exec(); - if ( ! dialog.changedEntries().isEmpty() ) - { - populateSchemeList(); - } -} - -QPixmap KColorCm::createSchemePreviewIcon(const KSharedConfigPtr &config) -{ - const uchar bits1[] = { 0xff, 0xff, 0xff, 0x2c, 0x16, 0x0b }; - const uchar bits2[] = { 0x68, 0x34, 0x1a, 0xff, 0xff, 0xff }; - const QSize bitsSize(24,2); - const QBitmap b1 = QBitmap::fromData(bitsSize, bits1); - const QBitmap b2 = QBitmap::fromData(bitsSize, bits2); - - QPixmap pixmap(23, 16); - pixmap.fill(Qt::black); // ### use some color other than black for borders? - - QPainter p(&pixmap); - - KConfigGroup group(config, "WM"); - // NOTE: keep this in sync with kdelibs/kdeui/kernel/kglobalsettings.cpp - QColor activeBackground = group.readEntry("activeBackground", QColor(48, 174, 232)); - QColor activeForeground = group.readEntry("activeForeground", QColor(255, 255, 255)); - QColor inactiveBackground = group.readEntry("inactiveBackground", QColor(224, 223, 222)); - QColor inactiveForeground = group.readEntry("inactiveForeground", QColor(75, 71, 67)); - - KColorScheme windowScheme(QPalette::Active, KColorScheme::Window, config); - p.fillRect( 1, 1, 7, 7, windowScheme.background()); - p.fillRect( 2, 2, 5, 2, QBrush(windowScheme.foreground().color(), b1)); - - KColorScheme buttonScheme(QPalette::Active, KColorScheme::Button, config); - p.fillRect( 8, 1, 7, 7, buttonScheme.background()); - p.fillRect( 9, 2, 5, 2, QBrush(buttonScheme.foreground().color(), b1)); - - p.fillRect(15, 1, 7, 7, activeBackground); - p.fillRect(16, 2, 5, 2, QBrush(activeForeground, b1)); - - KColorScheme viewScheme(QPalette::Active, KColorScheme::View, config); - p.fillRect( 1, 8, 7, 7, viewScheme.background()); - p.fillRect( 2, 12, 5, 2, QBrush(viewScheme.foreground().color(), b2)); - - KColorScheme selectionScheme(QPalette::Active, KColorScheme::Selection, config); - p.fillRect( 8, 8, 7, 7, selectionScheme.background()); - p.fillRect( 9, 12, 5, 2, QBrush(selectionScheme.foreground().color(), b2)); - - p.fillRect(15, 8, 7, 7, inactiveBackground); - p.fillRect(16, 12, 5, 2, QBrush(inactiveForeground, b2)); - - p.end(); - - return pixmap; -} - -void KColorCm::load() -{ - loadInternal(); - - // get colorscheme name from global settings - KConfigGroup group(m_config, "General"); - m_currentColorScheme = group.readEntry("ColorScheme"); - - QList itemList = schemeList->findItems(m_currentColorScheme, Qt::MatchExactly); - if(!itemList.isEmpty()) // "Default" is already selected, so don't handle the case that itemList is empty - schemeList->setCurrentItem(itemList.at(0)); - - KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); - group = KConfigGroup(&cfg, "X11"); - - applyToAlien->blockSignals(true); // don't emit SIGNAL(toggled(bool)) which would call SLOT(emitChanged()) - applyToAlien->setChecked(group.readEntry("exportKDEColors", true)); - applyToAlien->blockSignals(false); -} - - -void KColorCm::loadInternal() -{ - // clean the config, in case we have changed the in-memory kconfig - m_config->markAsClean(); - m_config->reparseConfiguration(); - - // fill in the color scheme list - populateSchemeList(); - - schemePreview->setPalette(m_config); - emit changed(false); -} - -void KColorCm::save() -{ - m_config->sync(); - - KConfig cfg(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); - KConfigGroup displayGroup(&cfg, "X11"); - - displayGroup.writeEntry("exportKDEColors", applyToAlien->isChecked()); - - cfg.sync(); - - runRdb(KRdbExportQtColors | KRdbExportGtkTheme | ( applyToAlien->isChecked() ? KRdbExportColors : 0 ) ); - - QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange") ); - QList args; - args.append(0);//previous KGlobalSettings::PaletteChanged. This is now private API in khintsettings - args.append(0);//unused in palette changed but needed for the DBus signature - message.setArguments(args); - QDBusConnection::sessionBus().send(message); - if (qApp->platformName() == QStringLiteral("xcb")) { - // Send signal to all kwin instances - QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); - QDBusConnection::sessionBus().send(message); - } - - emit changed(false); -} - -void KColorCm::defaults() -{ - // Switch to default scheme - for(int i = 0; i < schemeList->count(); ++i) { - QListWidgetItem *item = schemeList->item(i); - if(item->text() == i18nc("Default color scheme", "Default")) { - // If editing the default scheme, force a reload, else select the default scheme - if(schemeList->currentItem() == item) - loadScheme(item, item); - else - schemeList->setCurrentItem(item); - m_currentColorScheme = item->text(); - break; - } - } - - KCModule::defaults(); - emit changed(true); -} - -void KColorCm::on_schemeEditButton_clicked() -{ - QListWidgetItem *currentItem = schemeList->currentItem(); - if (!currentItem) { - return; - } - const QString fileBaseName = currentItem->data(Qt::UserRole).toString(); - const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - "color-schemes/" + fileBaseName + ".colors"); - - SchemeEditorDialog* dialog; - - //Current scheme - if (schemeList->currentItem()->text() == i18nc("Default color scheme", "Default")) { - dialog = new SchemeEditorDialog(m_config, this); - //Default scheme - } else if (schemeList->currentItem()->text() == i18nc("Current color scheme", "Current")) { - KSharedConfigPtr config = m_config; - config->setReadDefaults(true); - dialog = new SchemeEditorDialog(config, this); - } else { - dialog = new SchemeEditorDialog(path, this); - } - - dialog->setAttribute(Qt::WA_DeleteOnClose); // avoid mem-leak - dialog->setModal(true); - dialog->show(); - connect(dialog, &SchemeEditorDialog::accepted, [=](){ this->populateSchemeList(); }); - connect(dialog, &SchemeEditorDialog::rejected, [=](){ this->populateSchemeList(); }); - connect(dialog, &SchemeEditorDialog::applied, [=](){ this->populateSchemeList(); }); -} - -void KColorCm::updateConfig(KSharedConfigPtr config) -{ - // store colorscheme name in global settings - KConfigGroup groupOut(m_config, "General"); - groupOut.writeEntry("ColorScheme", m_currentColorScheme); - - QStringList colorItemList; - colorItemList << QStringLiteral("BackgroundNormal") - << QStringLiteral("BackgroundAlternate") - << QStringLiteral("ForegroundNormal") - << QStringLiteral("ForegroundInactive") - << QStringLiteral("ForegroundActive") - << QStringLiteral("ForegroundLink") - << QStringLiteral("ForegroundVisited") - << QStringLiteral("ForegroundNegative") - << QStringLiteral("ForegroundNeutral") - << QStringLiteral("ForegroundPositive") - << QStringLiteral("DecorationFocus") - << QStringLiteral("DecorationHover"); - - QStringList colorSetGroupList; - colorSetGroupList << QStringLiteral("Colors:View") - << QStringLiteral("Colors:Window") - << QStringLiteral("Colors:Button") - << QStringLiteral("Colors:Selection") - << QStringLiteral("Colors:Tooltip") - << QStringLiteral("Colors:Complementary"); - - QList colorSchemes; - - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::View, config)); - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Window, config)); - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Button, config)); - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Selection, config)); - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Tooltip, config)); - colorSchemes.append(KColorScheme(QPalette::Active, KColorScheme::Complementary, 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"); - - QStringList colorItemListWM; - colorItemListWM << QStringLiteral("activeBackground") - << QStringLiteral("activeForeground") - << QStringLiteral("inactiveBackground") - << QStringLiteral("inactiveForeground") - << QStringLiteral("activeBlend") - << QStringLiteral("inactiveBlend"); - - QVector defaultWMColors; - defaultWMColors << QColor(71,80,87) - << QColor(239,240,241) - << QColor(239,240,241) - << QColor(189,195,199) - << QColor(255,255,255) - << QColor(75,71,67); - - int i = 0; - for (const QString &coloritem : colorItemListWM) - { - groupWMOut.writeEntry(coloritem, groupWMTheme.readEntry(coloritem, defaultWMColors.value(i))); - ++i; - } - - QStringList groupNameList; - groupNameList << QStringLiteral("ColorEffects:Inactive") << QStringLiteral("ColorEffects:Disabled"); - - QStringList effectList; - effectList << QStringLiteral("Enable") - << QStringLiteral("ChangeSelectionColor") - << QStringLiteral("IntensityEffect") - << QStringLiteral("IntensityAmount") - << QStringLiteral("ColorEffect") - << QStringLiteral("ColorAmount") - << QStringLiteral("Color") - << QStringLiteral("ContrastEffect") - << QStringLiteral("ContrastAmount"); - - for (const QString &groupName : groupNameList) - { - - KConfigGroup groupEffectOut(m_config, groupName); - KConfigGroup groupEffectTheme(config, groupName); - - for (const QString &effect : effectList) { - groupEffectOut.writeEntry(effect, groupEffectTheme.readEntry(effect)); - } - } -} -#include "colorscm.moc" diff --git a/kcms/colors/colorscm.h b/kcms/colors/colorscm.h deleted file mode 100644 index a1a6d21ba..000000000 --- a/kcms/colors/colorscm.h +++ /dev/null @@ -1,141 +0,0 @@ -/* KDE Display color scheme setup module - * Copyright (C) 2007 Matthew Woehlke - * Copyright (C) 2007 Jeremy Whiting - * Copyright (C) 2016 Olivier Churlaud - * - * 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) any later version. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __COLORSCM_H__ -#define __COLORSCM_H__ - -#include - -#include "ui_colorsettings.h" - -class QStackedWidget; -class QListWidgetItem; - -class KColorButton; - - -/** - * The Desktop/Colors tab in kcontrol. - */ -class KColorCm : public KCModule, public Ui::colorSettings -{ - Q_OBJECT - -public: - KColorCm(QWidget *parent, const QVariantList &); - ~KColorCm() override; - -public Q_SLOTS: - - /** - * load the settings from the config - */ - void load() override; - - /** - * save the current settings - */ - void save() override; - - /** - * sets the configuration to sensible default values. - */ - void defaults() override; - - /** - * Update all keys of the Global config with the theme ones. - */ - void updateConfig(KSharedConfigPtr config); - -private Q_SLOTS: - - - /** - * slot called when the schemeList selection changes. - * - * It loads the scheme with loadScheme(KSharedConfigPtr), which updates the - * config (updateConfig()) - */ - void loadScheme(QListWidgetItem *currentItem, QListWidgetItem *previousItem); - - /** - * Reselect the previously selected scheme in schemeList without loading it - */ - void selectPreviousSchemeAgain(); - - /** - * Slot called when the remove button is clicked - */ - void on_schemeRemoveButton_clicked(); - - /** - * Slot called when the import button is clicked - */ - void on_schemeImportButton_clicked(); - - /** - * slot called when the get new schemes button is clicked - */ - void on_schemeKnsButton_clicked(); - - /** - * Slot called when the Edit button is clicked. - * - * It opens a dialog for the edition/creation. - */ - void on_schemeEditButton_clicked(); -private: - - /** - * Create a preview icon of a color scheme - */ - static QPixmap createSchemePreviewIcon(const KSharedConfigPtr &config); - - /** - * Load from global. - */ - void loadInternal(); - - /** - * load a scheme from a config file at a given path and updates the - * config (updateConfig()) - */ - void loadScheme(KSharedConfigPtr config); - - /** - * Populate the schemeList with color schemes found on the system - */ - void populateSchemeList(); - - - QString m_currentColorScheme; - - // The global config - KSharedConfigPtr m_config; - - // don't (re)load the scheme, only select it in schemeList - bool m_dontLoadSelectedScheme; - - // the item previously selected in schemeList - QListWidgetItem *m_previousSchemeItem; -}; - -#endif diff --git a/kcms/colors/editor/CMakeLists.txt b/kcms/colors/editor/CMakeLists.txt new file mode 100644 index 000000000..6f456dde8 --- /dev/null +++ b/kcms/colors/editor/CMakeLists.txt @@ -0,0 +1,33 @@ +set(scheme_editor_SRCS + kcolorschemeeditor.cpp + scmeditordialog.cpp + scmeditoroptions.cpp + scmeditorcolors.cpp + scmeditoreffects.cpp + previewwidget.cpp + setpreviewwidget.cpp +) + +ki18n_wrap_ui(scheme_editor_SRCS + colorsettings.ui + scmeditordialog.ui + scmeditoroptions.ui + scmeditorcolors.ui + scmeditoreffects.ui + preview.ui + setpreview.ui +) + +add_executable(kcolorschemeeditor ${scheme_editor_SRCS}) + +target_link_libraries(kcolorschemeeditor + KF5::ConfigWidgets + KF5::GuiAddons + KF5::I18n + KF5::CoreAddons + KF5::NewStuff + KF5::WindowSystem +) + +install(TARGETS kcolorschemeeditor DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES org.kde.kcolorschemeeditor.desktop DESTINATION ${KDE_INSTALL_APPDIR}) diff --git a/kcms/colors/colorsettings.ui b/kcms/colors/editor/colorsettings.ui similarity index 100% rename from kcms/colors/colorsettings.ui rename to kcms/colors/editor/colorsettings.ui diff --git a/kcms/colors/kcolorschemeeditor.cpp b/kcms/colors/editor/kcolorschemeeditor.cpp similarity index 71% rename from kcms/colors/kcolorschemeeditor.cpp rename to kcms/colors/editor/kcolorschemeeditor.cpp index da45ae699..8482c3494 100644 --- a/kcms/colors/kcolorschemeeditor.cpp +++ b/kcms/colors/editor/kcolorschemeeditor.cpp @@ -1,74 +1,98 @@ /* KDE Display scheme editor * Copyright (C) 2016 Olivier Churlaud * * 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) any later version. * * 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "scmeditordialog.h" #include #include #include #include +#include int main(int argc, char* argv[]) { - QApplication app(argc, argv); KAboutData aboutData( QStringLiteral("kcolorschemeeditor"), i18n("KColorSchemeEditor"), QStringLiteral("0.1"), i18n("Utility to edit and create color schemes"), KAboutLicense::GPL_V3 ); aboutData.addAuthor(i18n("Olivier Churlaud"), i18n("Utility creation"), QStringLiteral("olivier@churlaud.com")); aboutData.addAuthor(i18n("Jeremy Whiting"), i18n("KCM code (reused in here)"), QStringLiteral("jpwhiting@kde.org")); aboutData.addAuthor(i18n("Matthew Woehlke"), i18n("KCM code (reused in here)"), QStringLiteral("mw_triad@users.sourceforge.net")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.addPositionalArgument("theme", i18n("Scheme to edit or to use as a base."), QStringLiteral("kcolorschemeeditor ThemeName")); + + QCommandLineOption overwriteOption(QStringLiteral("overwrite"), i18n("Overwrite edited theme when saving")); + parser.addOption(overwriteOption); + + QCommandLineOption attachOption(QStringLiteral("attach"), i18n("Makes the dialog transient for another application window specified by handle"), QStringLiteral("handle")); + parser.addOption(attachOption); + aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); const QStringList args = parser.positionalArguments(); QString path = ""; if (args.count() == 1) { const QString fileBaseName(args.at(0)); path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "color-schemes/" + fileBaseName + ".colors"); } if (path.isEmpty()) { QTextStream out(stderr); out << i18n("Scheme not found, falling back to current one.\n"); } SchemeEditorDialog dialog(path); + dialog.setOverwriteOnSave(parser.isSet(overwriteOption)); + + // FIXME doesn't work :( + const QString attachHandle = parser.value(attachOption); + if (!attachHandle.isEmpty()) { + // TODO wayland: once we have foreign surface support + const QString x11Prefix = QStringLiteral("x11:"); + + if (attachHandle.startsWith(x11Prefix)) { + bool ok = false; + WId winId = attachHandle.mid(x11Prefix.length()).toLong(&ok, 0); + if (ok) { + dialog.setModal(true); + KWindowSystem::setMainWindow(&dialog, winId); + } + } + } dialog.show(); return app.exec(); } diff --git a/kcms/colors/org.kde.kcolorschemeeditor.desktop b/kcms/colors/editor/org.kde.kcolorschemeeditor.desktop similarity index 100% rename from kcms/colors/org.kde.kcolorschemeeditor.desktop rename to kcms/colors/editor/org.kde.kcolorschemeeditor.desktop diff --git a/kcms/colors/preview.ui b/kcms/colors/editor/preview.ui similarity index 100% rename from kcms/colors/preview.ui rename to kcms/colors/editor/preview.ui diff --git a/kcms/colors/previewwidget.cpp b/kcms/colors/editor/previewwidget.cpp similarity index 100% rename from kcms/colors/previewwidget.cpp rename to kcms/colors/editor/previewwidget.cpp diff --git a/kcms/colors/previewwidget.h b/kcms/colors/editor/previewwidget.h similarity index 100% rename from kcms/colors/previewwidget.h rename to kcms/colors/editor/previewwidget.h diff --git a/kcms/colors/scmeditorcolors.cpp b/kcms/colors/editor/scmeditorcolors.cpp similarity index 100% rename from kcms/colors/scmeditorcolors.cpp rename to kcms/colors/editor/scmeditorcolors.cpp diff --git a/kcms/colors/scmeditorcolors.h b/kcms/colors/editor/scmeditorcolors.h similarity index 100% rename from kcms/colors/scmeditorcolors.h rename to kcms/colors/editor/scmeditorcolors.h diff --git a/kcms/colors/scmeditorcolors.ui b/kcms/colors/editor/scmeditorcolors.ui similarity index 100% rename from kcms/colors/scmeditorcolors.ui rename to kcms/colors/editor/scmeditorcolors.ui diff --git a/kcms/colors/scmeditordialog.cpp b/kcms/colors/editor/scmeditordialog.cpp similarity index 85% rename from kcms/colors/scmeditordialog.cpp rename to kcms/colors/editor/scmeditordialog.cpp index 4bc42d676..f286c0ede 100644 --- a/kcms/colors/scmeditordialog.cpp +++ b/kcms/colors/editor/scmeditordialog.cpp @@ -1,272 +1,272 @@ /* ColorEdit widget for KDE Display color scheme setup module * Copyright (C) 2016 Olivier Churlaud * * 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) any later version. * * 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "scmeditordialog.h" #include "scmeditoroptions.h" #include "scmeditorcolors.h" #include "scmeditoreffects.h" -#include "colorscm.h" #include #include #include #include #include #include #include #include +#include #include -SchemeEditorDialog::SchemeEditorDialog(KSharedConfigPtr config, KColorCm *parent) - : QDialog( parent ) - , m_disableUpdates(false) - , m_unsavedChanges(false) - , m_kcm(parent) +SchemeEditorDialog::SchemeEditorDialog(KSharedConfigPtr config, QWidget *parent) + : QDialog(parent) { m_config = config; init(); } -SchemeEditorDialog::SchemeEditorDialog(const QString &path, KColorCm *parent) - : QDialog( parent ) +SchemeEditorDialog::SchemeEditorDialog(const QString &path, QWidget *parent) + : QDialog(parent) , m_filePath(path) - , m_disableUpdates(false) - , m_unsavedChanges(false) - , m_kcm(parent) { m_config = KSharedConfig::openConfig(path); m_schemeName = KConfigGroup(m_config, "General").readEntry("Name"); this->setWindowTitle(m_schemeName); init(); } +bool SchemeEditorDialog::overwriteOnSave() const +{ + return m_overwriteOnSave; +} + +void SchemeEditorDialog::setOverwriteOnSave(bool overwrite) +{ + m_overwriteOnSave = overwrite; + + buttonBox->button(QDialogButtonBox::Apply)->setVisible(overwrite); + buttonBox->button(QDialogButtonBox::Save)->setVisible(!overwrite); +} + void SchemeEditorDialog::init() { setupUi(this); - //we are in standalone appication mode? don't show the apply button - if (!m_kcm) { - buttonBox->button(QDialogButtonBox::Apply)->setVisible(false); - } - schemeKnsUploadButton->setIcon( QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")) ); m_optionTab = new SchemeEditorOptions(m_config); m_colorTab = new SchemeEditorColors(m_config); m_disabledTab = new SchemeEditorEffects(m_config, QPalette::Disabled); m_inactiveTab = new SchemeEditorEffects(m_config, QPalette::Inactive); tabWidget->addTab(m_optionTab, i18n("Options")); tabWidget->addTab(m_colorTab, i18n("Colors")); tabWidget->addTab(m_disabledTab, i18n("Disabled")); tabWidget->setCurrentWidget(m_colorTab); connect(m_optionTab, &SchemeEditorOptions::changed, this, &SchemeEditorDialog::updateTabs); connect(m_colorTab, &SchemeEditorColors::changed, this, &SchemeEditorDialog::updateTabs); connect(m_disabledTab, &SchemeEditorEffects::changed, this, &SchemeEditorDialog::updateTabs); connect(m_inactiveTab, &SchemeEditorEffects::changed, this, &SchemeEditorDialog::updateTabs); + // In overwrite mode we use "Apply", in regular mode "Save" button + buttonBox->button(QDialogButtonBox::Apply)->setVisible(false); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); + buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false); updateTabs(); } void SchemeEditorDialog::on_schemeKnsUploadButton_clicked() { if (m_unsavedChanges) { KMessageBox::ButtonCode reallyUpload = KMessageBox::questionYesNo( this, i18n("This colour scheme was not saved. Continue?"), i18n("Do you really want to upload?")); if (reallyUpload == KMessageBox::No) { return; } } // find path const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/") + m_schemeName + QStringLiteral(".colors")); if (path.isEmpty() ) // if the color scheme file wasn't found { qDebug() << "path for color scheme " << m_schemeName << " couldn't be found"; return; } // upload KNS3::UploadDialog dialog(QStringLiteral("colorschemes.knsrc"), this); dialog.setUploadFile(QUrl::fromLocalFile(path) ); dialog.exec(); } void SchemeEditorDialog::on_buttonBox_clicked(QAbstractButton *button) { if (buttonBox->standardButton(button) == QDialogButtonBox::Reset) { m_config->markAsClean(); m_config->reparseConfiguration(); updateTabs(); setUnsavedChanges(false); } - else if (buttonBox->standardButton(button) == QDialogButtonBox::Save) + else if (buttonBox->standardButton(button) == QDialogButtonBox::Save + || buttonBox->standardButton(button) == QDialogButtonBox::Apply) { saveScheme(); } - else if (buttonBox->standardButton(button) == QDialogButtonBox::Apply) - { - applyScheme(); - emit applied(); - } else if (buttonBox->standardButton(button) == QDialogButtonBox::Close) { if (m_unsavedChanges) { KMessageBox::ButtonCode ans = KMessageBox::questionYesNo( this, i18n("You have unsaved changes. Do you really want to quit?"), i18n("Unsaved changes")); if (ans == KMessageBox::No) { return; } } m_config->markAsClean(); m_config->reparseConfiguration(); this->accept(); } } -void SchemeEditorDialog::applyScheme() -{ - if (!m_kcm) { - return; - } - - m_kcm->updateConfig(m_config); - m_kcm->save(); -} - void SchemeEditorDialog::saveScheme() { + QString name = m_schemeName; + // prompt for the name to save as - bool ok; - QString schemeName = KConfigGroup(m_config, "General").readEntry("Name"); - QString name = QInputDialog::getText(this, i18n("Save Color Scheme"), - i18n("&Enter a name for the color scheme:"), QLineEdit::Normal, m_schemeName, &ok); - if (!ok) { - return; + if (!m_overwriteOnSave) { + bool ok; + name = QInputDialog::getText(this, i18n("Save Color Scheme"), + i18n("&Enter a name for the color scheme:"), QLineEdit::Normal, m_schemeName, &ok); + if (!ok) { + return; + } } QString filename = name; filename.remove(QLatin1Char('\'')); // So Foo's does not become FooS QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)")); int offset; while ((offset = fixer.indexIn(filename)) >= 0) filename.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); filename.replace(0, 1, filename.at(0).toUpper()); // check if that name is already in the list const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/") + filename + QStringLiteral(".colors")); QFile file(path); const int permissions = file.permissions(); const bool canWrite = (permissions & QFile::WriteUser); // or if we can overwrite it if it exists if (path.isEmpty() || !file.exists() || canWrite) { - if(canWrite){ + if(canWrite && !m_overwriteOnSave){ int ret = KMessageBox::questionYesNo(this, i18n("A color scheme with that name already exists.\nDo you want to overwrite it?"), i18n("Save Color Scheme"), KStandardGuiItem::overwrite(), KStandardGuiItem::cancel()); //on don't overwrite, call again the function if(ret == KMessageBox::No){ this->saveScheme(); return; } } // go ahead and save it QString newpath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/color-schemes/"; QDir dir; dir.mkpath(newpath); newpath += filename + ".colors"; KConfig *config = m_config->copyTo(newpath); m_config->markAsClean(); m_config->reparseConfiguration(); KConfigGroup group(config, "General"); group.writeEntry("Name", name); // sync it and delete pointer config->sync(); delete config; // reopen and update window m_config = KSharedConfig::openConfig(newpath); m_schemeName = name; setWindowTitle(name); setUnsavedChanges(false); + + QTextStream out(stdout); + out << filename << endl; } else if (!canWrite && file.exists()) { KMessageBox::error(this, i18n("You do not have permission to overwrite that scheme"), i18n("Error")); } } void SchemeEditorDialog::updateTabs(bool madeByUser) { if (madeByUser) { setUnsavedChanges(true); } KConfigGroup group(m_config, "ColorEffects:Inactive"); bool hideInactiveTab = group.readEntry("Enable", QVariant(true)).toBool(); if ( hideInactiveTab ) { tabWidget->addTab(m_inactiveTab, i18n("Inactive")); } else { tabWidget->removeTab(tabWidget->indexOf(m_inactiveTab)); } m_optionTab->updateValues(); m_colorTab->updateValues(); m_inactiveTab->updateValues(); m_disabledTab->updateValues(); } void SchemeEditorDialog::setUnsavedChanges(bool changes) { m_unsavedChanges = changes; if (changes) { buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true); } else { buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); + buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false); } } diff --git a/kcms/colors/scmeditordialog.h b/kcms/colors/editor/scmeditordialog.h similarity index 84% rename from kcms/colors/scmeditordialog.h rename to kcms/colors/editor/scmeditordialog.h index 5d46fa23b..28abc0122 100644 --- a/kcms/colors/scmeditordialog.h +++ b/kcms/colors/editor/scmeditordialog.h @@ -1,78 +1,79 @@ /* ColorEdit widget for KDE Display color scheme setup module * Copyright (C) 2016 Olivier Churlaud * * 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) any later version. * * 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __SCMEDITORDIALOG_H__ #define __SCMEDITORDIALOG_H__ #include #include #include #include #include #include "ui_scmeditordialog.h" class SchemeEditorOptions; class SchemeEditorColors; class SchemeEditorEffects; -class KColorCm; class SchemeEditorDialog : public QDialog, public Ui::ScmEditorDialog { Q_OBJECT public: - SchemeEditorDialog(const QString &path, KColorCm *parent = nullptr); - SchemeEditorDialog(KSharedConfigPtr config, KColorCm *parent = nullptr); + SchemeEditorDialog(const QString &path, QWidget *parent = nullptr); + SchemeEditorDialog(KSharedConfigPtr config, QWidget *parent = nullptr); + + bool overwriteOnSave() const; + void setOverwriteOnSave(bool overwrite); Q_SIGNALS: void changed(bool); - void applied(); private Q_SLOTS: /** slot called when the upload scheme button is clicked */ void on_schemeKnsUploadButton_clicked(); void on_buttonBox_clicked(QAbstractButton *button); void updateTabs(bool byUser=false); private: void init(); - void applyScheme(); /** save the current scheme */ void saveScheme(); void setUnsavedChanges(bool changes); const QString m_filePath; QString m_schemeName; KSharedConfigPtr m_config; - bool m_disableUpdates; - bool m_unsavedChanges; + bool m_disableUpdates = false; + bool m_unsavedChanges = false; SchemeEditorOptions *m_optionTab; SchemeEditorColors *m_colorTab; SchemeEditorEffects *m_disabledTab; SchemeEditorEffects *m_inactiveTab; - KColorCm *m_kcm; + + bool m_overwriteOnSave = false; }; #endif diff --git a/kcms/colors/scmeditordialog.ui b/kcms/colors/editor/scmeditordialog.ui similarity index 100% rename from kcms/colors/scmeditordialog.ui rename to kcms/colors/editor/scmeditordialog.ui diff --git a/kcms/colors/scmeditoreffects.cpp b/kcms/colors/editor/scmeditoreffects.cpp similarity index 100% rename from kcms/colors/scmeditoreffects.cpp rename to kcms/colors/editor/scmeditoreffects.cpp diff --git a/kcms/colors/scmeditoreffects.h b/kcms/colors/editor/scmeditoreffects.h similarity index 100% rename from kcms/colors/scmeditoreffects.h rename to kcms/colors/editor/scmeditoreffects.h diff --git a/kcms/colors/scmeditoreffects.ui b/kcms/colors/editor/scmeditoreffects.ui similarity index 100% rename from kcms/colors/scmeditoreffects.ui rename to kcms/colors/editor/scmeditoreffects.ui diff --git a/kcms/colors/scmeditoroptions.cpp b/kcms/colors/editor/scmeditoroptions.cpp similarity index 100% rename from kcms/colors/scmeditoroptions.cpp rename to kcms/colors/editor/scmeditoroptions.cpp diff --git a/kcms/colors/scmeditoroptions.h b/kcms/colors/editor/scmeditoroptions.h similarity index 100% rename from kcms/colors/scmeditoroptions.h rename to kcms/colors/editor/scmeditoroptions.h diff --git a/kcms/colors/scmeditoroptions.ui b/kcms/colors/editor/scmeditoroptions.ui similarity index 100% rename from kcms/colors/scmeditoroptions.ui rename to kcms/colors/editor/scmeditoroptions.ui diff --git a/kcms/colors/setpreview.ui b/kcms/colors/editor/setpreview.ui similarity index 100% rename from kcms/colors/setpreview.ui rename to kcms/colors/editor/setpreview.ui diff --git a/kcms/colors/setpreviewwidget.cpp b/kcms/colors/editor/setpreviewwidget.cpp similarity index 100% rename from kcms/colors/setpreviewwidget.cpp rename to kcms/colors/editor/setpreviewwidget.cpp diff --git a/kcms/colors/setpreviewwidget.h b/kcms/colors/editor/setpreviewwidget.h similarity index 100% rename from kcms/colors/setpreviewwidget.h rename to kcms/colors/editor/setpreviewwidget.h diff --git a/kcms/colors/colors.desktop b/kcms/colors/kcm_colors.desktop similarity index 81% rename from kcms/colors/colors.desktop rename to kcms/colors/kcm_colors.desktop index a0a68f7c9..84098801c 100644 --- a/kcms/colors/colors.desktop +++ b/kcms/colors/kcm_colors.desktop @@ -1,194 +1,155 @@ [Desktop Entry] -Exec=kcmshell5 colors +Exec=kcmshell5 kcm_colors Icon=preferences-desktop-color Type=Service X-KDE-ServiceTypes=KCModule X-DocPath=kcontrol/colors/index.html X-KDE-Library=kcm_colors X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=appearance X-KDE-Weight=60 Name=Colors Name[af]=Kleure Name[ar]=الألوان Name[be]=Колеры Name[be@latin]=Kolery Name[bg]=Цветове Name[bn]=রং Name[bn_IN]=রং Name[br]=Livioù Name[bs]=Boje Name[ca]=Colors Name[ca@valencia]=Colors Name[cs]=Barvy Name[csb]=Farwë Name[cy]=Lliwiau Name[da]=Farver Name[de]=Farben Name[el]=Χρώματα Name[en_GB]=Colours Name[eo]=Koloroj Name[es]=Colores Name[et]=Värvid Name[eu]=Koloreak Name[fa]=رنگها Name[fi]=Värit Name[fr]=Couleurs Name[fy]=Kleuren Name[ga]=Dathanna Name[gl]=Cores Name[gu]=રંગો Name[he]=צבעים Name[hi]=रंग Name[hne]=रंग Name[hr]=Boje Name[hsb]=Barby Name[hu]=Színek Name[ia]=Colores Name[id]=Warna Name[is]=Litir Name[it]=Colori Name[ja]=色 Name[ka]=ცვეტები Name[kk]=Түстер Name[km]=ពណ៌ Name[kn]=ಬಣ್ಣಗಳು Name[ko]=색상 Name[ku]=Reng Name[lt]=Spalvos Name[lv]=Krāsas Name[mai]=रँग Name[mk]=Бои Name[ml]=നിറങ്ങള്‍ Name[mr]=रंग Name[ms]=Warna Name[nb]=Farger Name[nds]=Klören Name[ne]=रङ Name[nl]=Kleuren Name[nn]=Fargar Name[oc]=Colors Name[or]=ରଙ୍ଗ Name[pa]=ਰੰਗ Name[pl]=Kolory Name[pt]=Cores Name[pt_BR]=Cores Name[ro]=Culori Name[ru]=Цвета Name[se]=Ivnnit Name[si]=වර්‍ණ Name[sk]=Farby Name[sl]=Barve Name[sr]=Боје Name[sr@ijekavian]=Боје Name[sr@ijekavianlatin]=Boje Name[sr@latin]=Boje Name[sv]=Färger Name[ta]=வண்ணங்கள் Name[te]=రంగులు Name[tg]=Рангҳо Name[th]=สี Name[tr]=Renkler Name[ug]=رەڭلەر Name[uk]=Кольори Name[uz]=Ranglar Name[uz@cyrillic]=Ранглар Name[vi]=Màu sắc Name[wa]=Coleurs Name[xh]=Imibala Name[x-test]=xxColorsxx Name[zh_CN]=色彩 Name[zh_TW]=顏色 -Comment=Application Color Scheme -Comment[ca]=Esquema de color de les aplicacions -Comment[ca@valencia]=Esquema de color de les aplicacions -Comment[cs]=Barevný motiv aplikací -Comment[da]=Farvetema til program -Comment[de]=Farbschema für Anwendungen -Comment[el]=Θέμα χρωμάτων εφαρμογής -Comment[en_GB]=Application Colour Scheme -Comment[es]=Esquema de color de las aplicaciones -Comment[eu]=Aplikazioen kolore-eskema -Comment[fi]=Ohjelmien väriteema -Comment[fr]=Thème de couleur des applications -Comment[gl]=Esquema de cores do aplicativo -Comment[he]=ערכות צבעים של יישומים -Comment[hu]=Alkalmazások színsémái -Comment[id]=Skema Warna Aplikasi -Comment[it]=Schema di colore delle applicazioni -Comment[ja]=アプリケーションカラースキーム -Comment[ko]=프로그램 색 배열 -Comment[lt]=Programos spalvų derinys -Comment[nb]=Fargeoppsett for program -Comment[nl]=Toepassing Kleurenschema -Comment[nn]=Fargeoppsett for program -Comment[pa]=ਐਪਲੀਕੇਸ਼ਨ ਰੰਗ ਸਕੀਮ -Comment[pl]=Zestaw kolorów programów -Comment[pt]=Esquema de Cores da Aplicação -Comment[pt_BR]=Esquema de cores do aplicativo -Comment[ru]=Цветовая схема для приложений -Comment[sk]=Farebná schéma aplikácie -Comment[sl]=Barvna shema programov -Comment[sr]=Шема боја у програмима -Comment[sr@ijekavian]=Шема боја у програмима -Comment[sr@ijekavianlatin]=Šema boja u programima -Comment[sr@latin]=Šema boja u programima -Comment[sv]=Programmets färgschema -Comment[tr]=Uygulama Renk Şeması -Comment[uk]=Схема кольорів вікон програм -Comment[x-test]=xxApplication Color Schemexx -Comment[zh_CN]=应用程序配色方案 -Comment[zh_TW]=應用程式色彩機制 +Comment=Choose the color scheme X-KDE-Keywords=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme X-KDE-Keywords[ar]=لون,ألوان,مخطّط,مخطط,تباين,ألوان الودجات,مخطّط ألوان,مخطط ألوان,نمط اللون,سمة اللون X-KDE-Keywords[bs]=boje,boje,tema,kontrast,dodatak bojama,tema u boji,stil boja,tema boja X-KDE-Keywords[ca]=colors,esquema,contrast,colors d'estris,Esquema de color,estil de color, tema de color X-KDE-Keywords[ca@valencia]=colors,esquema,contrast,colors d'estris,Esquema de color,estil de color, tema de color X-KDE-Keywords[da]=farver,tema,kontrast,widget-farver,farvetema,farvestil,farveskema X-KDE-Keywords[de]=Farben,Schema,Kontrast,Farbschema,Elemente X-KDE-Keywords[el]=χρώματα,χρώματα,σχήμα,αντίθεση,χρώματα γραφικών συστατικών,χρωματικό σχήμα,χρωματικό στιλ,θέμα χρώματος X-KDE-Keywords[en_GB]=colours,scheme,contrast,Widget colours,Colour Scheme,colour style,colour theme X-KDE-Keywords[es]=colores,esquema,contraste,colores de elementos gráficos,Esquema de color,estilo de color,tema de color X-KDE-Keywords[et]=värv,värvid,skeem,kontrast,vidina värvid,värviskeem,värvistiil,värviteema X-KDE-Keywords[eu]=kolore,koloreak,eskema,kontraste,trepetaren koloreak,kolore-eskema,kolore-estilo, kolorearen gai X-KDE-Keywords[fi]=värit,teema,kontrasti,käyttöliittymäelementtien värit,elementtien värit,väriteema,värityyli X-KDE-Keywords[fr]=couleurs, couleurs, schéma, contraste, couleur des composants graphiques, schéma de couleur, style de couleur, thème de couleur X-KDE-Keywords[ga]=dathanna,scéim,codarsnacht,dathanna Giuirléidí,Scéim Datha,téama datha X-KDE-Keywords[gl]=cores,esquema,contraste,cores do trebello,esquema de cores, tema de cores X-KDE-Keywords[he]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,צבעים,ערכת נושא,צבע X-KDE-Keywords[hu]=színek,színek,séma,kontraszt,Grafikai elemek színei,Színséma,színstílus,színtéma X-KDE-Keywords[ia]=colores,colores,schema,contrasto,colores de Widget,Schema de Color,stilo de color, thema de color X-KDE-Keywords[id]=warna,warna,skema,kontras,Widget warna,Skema Warna,gaya warna,tema warna X-KDE-Keywords[it]=colori,schema,contrasto,colore degli oggetti,schema di colore,stile colore,tema colore X-KDE-Keywords[kk]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme X-KDE-Keywords[km]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme X-KDE-Keywords[ko]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,색,색 배열,고대비 X-KDE-Keywords[mr]=रंग, रंग, योजना, कॉन्ट्रास्ट, विजेट रंग, रंगयोजना, रंगप्रकार, रंगशैली X-KDE-Keywords[nb]=farger,oppsett,kontrast,elementfarger,fargeoppsett,fargestil,fargetema X-KDE-Keywords[nds]=Klöör,Klören,Schema,Kontrast,Lüttprogramm-Klören,Klöörschema,Klöörstil,Klöörmuster X-KDE-Keywords[nl]=colors,colours, kleuren,scheme,schema,contrast,Widget colors,Widgetkleuren,Color Scheme,kleurschema,kleurstijl,kleurthema X-KDE-Keywords[nn]=fargar,oppsett,kontrast,elementfargar,fargeoppsett,fargestil,fargetema X-KDE-Keywords[pa]=ਰੰਗ,ਸਕੀਮ,ਕਨਟਰਾਸਟ,ਵਿਜੈਟ ਰੰਗ,ਰੰਗ ਸਕੀ,ਰੰਗ ਸਟਾਈਲ,ਰੰਗ ਥੀਮ X-KDE-Keywords[pl]=kolory,schemat,kontrast,kolory elementów interfejsu,zestaw kolorów,styl kolorów,motyw kolorów X-KDE-Keywords[pt]=cores,esquema,contraste,cores dos elementos,esquema de cores,estilo de cores,tema de cores X-KDE-Keywords[pt_BR]=cor,cores,esquema,contraste,Cores do widget,Esquema de cores,estilo de cores,tema de cores X-KDE-Keywords[ru]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,цвет,цвета,схема,контраст,цвета виджета,цветовая схема,цветовой стиль,цветовая тема X-KDE-Keywords[sk]=farba,farby,schéma,kontrast,farby widgetov,Farebná schéma,štýl farieb,téma farieb X-KDE-Keywords[sl]=barve,shema,tema,kontrast,barve gradnikov,barvna shema,barvna tema,barvni slog X-KDE-Keywords[sr]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,боје,шема,контраст,боје виџета,шема боја,стил боја,тема боја X-KDE-Keywords[sr@ijekavian]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,боје,шема,контраст,боје виџета,шема боја,стил боја,тема боја X-KDE-Keywords[sr@ijekavianlatin]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,boje,šema,kontrast,boje vidžeta,šema boja,stil boja,tema boja X-KDE-Keywords[sr@latin]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,boje,šema,kontrast,boje vidžeta,šema boja,stil boja,tema boja X-KDE-Keywords[sv]=färger,schema,kontrast,Komponentfärger,Färgschema,färgstil,färgtema X-KDE-Keywords[tr]=renkler,renk,şema,karşıtlık,kontrast,Gereç renkleri,RenkŞeması,renk biçemi,renk teması,renk biçimi X-KDE-Keywords[uk]=кольори,кольори,схема,контраст,кольори віджетів,схема кольорів,стиль кольорів,тема кольорів,colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme X-KDE-Keywords[x-test]=xxcolorsxx,xxcoloursxx,xxschemexx,xxcontrastxx,xxWidget colorsxx,xxColor Schemexx,xxcolor stylexx,xxcolor themexx X-KDE-Keywords[zh_CN]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme,颜色,配色方案,对比度,部件颜色,颜色方案,颜色风格,颜色主题 X-KDE-Keywords[zh_TW]=colors,colours,scheme,contrast,Widget colors,Color Scheme,color style,color theme Categories=Qt;KDE;X-KDE-settings-looknfeel; diff --git a/kcms/colors/package/contents/ui/main.qml b/kcms/colors/package/contents/ui/main.qml new file mode 100644 index 000000000..6fe2633af --- /dev/null +++ b/kcms/colors/package/contents/ui/main.qml @@ -0,0 +1,280 @@ +/* + * Copyright 2018 Kai Uwe Broulik + * + * 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 . + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 +import QtQuick.Dialogs 1.0 as QtDialogs +import QtQuick.Controls 2.3 as QtControls +import org.kde.kirigami 2.4 as Kirigami +import org.kde.kconfig 1.0 // for KAuthorized +import org.kde.kcm 1.1 as KCM + +KCM.GridViewKCM { + KCM.ConfigModule.quickHelp: i18n("This module lets you choose the color scheme.") + + view.model: kcm.colorsModel + view.currentIndex: kcm.selectedSchemeIndex + + enabled: !kcm.downloadingFile + + Component.onCompleted: { + // The thumbnails are a bit more elaborate and need more room, especially when translated + view.implicitCellWidth = Kirigami.Units.gridUnit * 13; + } + + DropArea { + anchors.fill: parent + onEntered: { + if (!drag.hasUrls) { + drag.accepted = false; + } + } + onDropped: { + infoLabel.visible = false; + kcm.installSchemeFromFile(drop.urls[0]); + } + } + + // putting the InlineMessage as header item causes it to show up initially despite visible false + header: ColumnLayout { + Kirigami.InlineMessage { + id: notInstalledWarning + Layout.fillWidth: true + + type: Kirigami.MessageType.Warning + showCloseButton: true + visible: false + + Connections { + target: kcm + onShowSchemeNotInstalledWarning: { + notInstalledWarning.text = i18n("The color scheme '%1' is not installed. Selecting the default theme instead.", schemeName) + notInstalledWarning.visible = true; + } + } + } + } + + view.remove: Transition { + ParallelAnimation { + NumberAnimation { property: "scale"; to: 0.5; duration: Kirigami.Units.longDuration } + NumberAnimation { property: "opacity"; to: 0.0; duration: Kirigami.Units.longDuration } + } + } + + view.removeDisplaced: Transition { + SequentialAnimation { + // wait for the "remove" animation to finish + PauseAnimation { duration: Kirigami.Units.longDuration } + NumberAnimation { properties: "x,y"; duration: Kirigami.Units.longDuration } + } + } + + view.delegate: KCM.GridDelegate { + id: delegate + + text: model.display + + thumbnailAvailable: true + thumbnail: Rectangle { + anchors.fill: parent + + opacity: model.pendingDeletion ? 0.3 : 1 + Behavior on opacity { + NumberAnimation { duration: Kirigami.Units.longDuration } + } + + color: model.palette.window + + Kirigami.Theme.highlightColor: model.palette.highlight + Kirigami.Theme.highlightedTextColor: model.palette.highlightedText + Kirigami.Theme.linkColor: model.palette.link + Kirigami.Theme.textColor: model.palette.text + + ColumnLayout { + anchors { + fill: parent + margins: Kirigami.Units.smallSpacing + } + + RowLayout { + Layout.fillWidth: true + + QtControls.Label { + Layout.fillWidth: true + Layout.fillHeight: true + verticalAlignment: Text.AlignVCenter + text: i18n("Window text") + elide: Text.ElideRight + color: model.palette.windowText + } + + QtControls.Button { + Layout.alignment: Qt.AlignBottom + text: i18n("Button") + Kirigami.Theme.backgroundColor: model.palette.button + Kirigami.Theme.textColor: model.palette.buttonText + activeFocusOnTab: false + } + } + + QtControls.Frame { + Layout.fillWidth: true + Layout.fillHeight: true + Kirigami.Theme.backgroundColor: model.palette.base + // FIXME Make Frame still visible but without any inner padding + padding: 1 + activeFocusOnTab: false + + Column { + id: listPreviewColumn + + readonly property string demoText: " %2 %4" + .arg(i18nc("Hyperlink", "link")) + .arg(model.palette.linkVisited) + .arg(i18nc("Visited hyperlink", "visited")) + + width: parent.width + + QtControls.ItemDelegate { + width: parent.width + text: i18n("Normal text") + listPreviewColumn.demoText + activeFocusOnTab: false + } + + QtControls.ItemDelegate { + width: parent.width + highlighted: true + // TODO use proper highlighted link color + text: i18n("Highlighted text") + listPreviewColumn.demoText + activeFocusOnTab: false + } + + QtControls.ItemDelegate { + width: parent.width + enabled: false + text: i18n("Disabled text") + listPreviewColumn.demoText + activeFocusOnTab: false + } + } + } + } + + // Make the preview non-clickable but still reacting to hover + MouseArea { + anchors.fill: parent + onClicked: delegate.clicked() + onDoubleClicked: delegate.doubleClicked() + } + } + + actions: [ + Kirigami.Action { + iconName: "document-edit" + tooltip: i18n("Edit Color Scheme") + enabled: !model.pendingDeletion + onTriggered: kcm.editScheme(model.index, parent) + }, + Kirigami.Action { + iconName: "edit-delete" + tooltip: i18n("Remove Color Scheme") + enabled: model.removable + visible: !model.pendingDeletion + onTriggered: kcm.setPendingDeletion(model.index, true) + }, + Kirigami.Action { + iconName: "edit-undo" + tooltip: i18n("Restore Color Scheme") + visible: model.pendingDeletion + onTriggered: kcm.setPendingDeletion(model.index, false) + } + ] + onClicked: { + kcm.selectedScheme = model.schemeName; + view.forceActiveFocus(); + } + onDoubleClicked: { + kcm.save(); + } + } + + footer: ColumnLayout { + Kirigami.InlineMessage { + id: infoLabel + Layout.fillWidth: true + + showCloseButton: true + + Connections { + target: kcm + onShowSuccessMessage: { + infoLabel.type = Kirigami.MessageType.Positive; + infoLabel.text = message; + infoLabel.visible = true; + // Avoid dual message widgets + notInstalledWarning.visible = false; + } + onShowErrorMessage: { + infoLabel.type = Kirigami.MessageType.Error; + infoLabel.text = message; + infoLabel.visible = true; + notInstalledWarning.visible = false; + } + } + } + + RowLayout { + Layout.alignment: Qt.AlignRight + + QtControls.Button { + text: i18n("Install from File...") + icon.name: "document-import" + onClicked: fileDialogLoader.active = true + } + + QtControls.Button { + text: i18n("Get New Color Schemes...") + icon.name: "get-hot-new-stuff" + onClicked: kcm.getNewStuff(this) + visible: KAuthorized.authorize("ghns") + } + } + } + + Loader { + id: fileDialogLoader + active: false + sourceComponent: QtDialogs.FileDialog { + title: i18n("Open Color Scheme") + folder: shortcuts.home + nameFilters: [ i18n("Color Scheme Files (*.colors)") ] + Component.onCompleted: open() + onAccepted: { + infoLabel.visible = false; + kcm.installSchemeFromFile(fileUrls[0]) + fileDialogLoader.active = false + } + onRejected: { + fileDialogLoader.active = false + } + } + } +} diff --git a/kcms/colors/package/metadata.desktop b/kcms/colors/package/metadata.desktop new file mode 100644 index 000000000..50bf1b7fe --- /dev/null +++ b/kcms/colors/package/metadata.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Name=Colors +Comment=Choose the color scheme + +Icon=preferences-desktop-color +Type=Service +X-KDE-PluginInfo-Author=Kai Uwe Broulik +X-KDE-PluginInfo-Email=kde@privat.broulik.de +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Name=kcm_colors +X-KDE-PluginInfo-Version= +X-KDE-PluginInfo-Website= +X-KDE-ServiceTypes=Plasma/Generic +X-Plasma-API=declarativeappletscript + +X-Plasma-MainScript=ui/main.qml