diff --git a/kcms/desktoptheme/kcm.cpp b/kcms/desktoptheme/kcm.cpp index 10baa37c2..79483b192 100644 --- a/kcms/desktoptheme/kcm.cpp +++ b/kcms/desktoptheme/kcm.cpp @@ -1,372 +1,368 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa Copyright (c) 2016 David Rosca Copyright (c) 2018 Kai Uwe Broulik This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(KCM_DESKTOP_THEME, "kcm_desktoptheme") K_PLUGIN_FACTORY_WITH_JSON(KCMDesktopThemeFactory, "kcm_desktoptheme.json", registerPlugin();) KCMDesktopTheme::KCMDesktopTheme(QObject *parent, const QVariantList &args) : KQuickAddons::ConfigModule(parent, args) , m_defaultTheme(new Plasma::Theme(this)) , m_haveThemeExplorerInstalled(false) { - //This flag seems to be needed in order for QQuickWidget to work - //see https://bugreports.qt-project.org/browse/QTBUG-40765 - //also, it seems to work only if set in the kcm, not in the systemsettings' main - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_desktoptheme"), i18n("Plasma Style"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("David Rosca"), QString(), QStringLiteral("nowrep@gmail.com")); setAboutData(about); setButtons(Apply | Default | Help); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = QByteArrayLiteral("pluginName"); roles[ThemeNameRole] = QByteArrayLiteral("themeName"); roles[DescriptionRole] = QByteArrayLiteral("description"); roles[IsLocalRole] = QByteArrayLiteral("isLocal"); roles[PendingDeletionRole] = QByteArrayLiteral("pendingDeletion"); m_model->setItemRoleNames(roles); m_haveThemeExplorerInstalled = !QStandardPaths::findExecutable(QStringLiteral("plasmathemeexplorer")).isEmpty(); } KCMDesktopTheme::~KCMDesktopTheme() { delete m_defaultTheme; } QStandardItemModel *KCMDesktopTheme::desktopThemeModel() const { return m_model; } QString KCMDesktopTheme::selectedPlugin() const { return m_selectedPlugin; } void KCMDesktopTheme::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } m_selectedPlugin = plugin; emit selectedPluginChanged(m_selectedPlugin); emit selectedPluginIndexChanged(); updateNeedsSave(); } int KCMDesktopTheme::selectedPluginIndex() const { const auto results = m_model->match(m_model->index(0, 0), PluginNameRole, m_selectedPlugin); if (results.count() == 1) { return results.first().row(); } return -1; } bool KCMDesktopTheme::downloadingFile() const { return m_tempCopyJob; } void KCMDesktopTheme::setPendingDeletion(int index, bool pending) { QModelIndex idx = m_model->index(index, 0); m_model->setData(idx, pending, PendingDeletionRole); if (pending && selectedPluginIndex() == index) { // move to the next non-pending theme const auto nonPending = m_model->match(idx, PendingDeletionRole, false); setSelectedPlugin(nonPending.first().data(PluginNameRole).toString()); } updateNeedsSave(); } void KCMDesktopTheme::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("plasma-themes.knsrc")); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Plasma Styles")); m_newStuffDialog->setWindowModality(Qt::WindowModal); m_newStuffDialog->winId(); // so it creates the windowHandle(); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMDesktopTheme::load); } if (ctx && ctx->window()) { m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); } m_newStuffDialog.data()->show(); } void KCMDesktopTheme::installThemeFromFile(const QUrl &url) { if (url.isLocalFile()) { installTheme(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; } 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 theme: %1", job->errorText())); return; } installTheme(m_tempInstallFile->fileName()); m_tempInstallFile.reset(); }); connect(m_tempCopyJob, &QObject::destroyed, this, &KCMDesktopTheme::downloadingFileChanged); } void KCMDesktopTheme::installTheme(const QString &path) { qCDebug(KCM_DESKTOP_THEME) << "Installing ... " << path; const QString program = QStringLiteral("kpackagetool5"); const QStringList arguments = { QStringLiteral("--type"), QStringLiteral("Plasma/Theme"), QStringLiteral("--install"), path}; qCDebug(KCM_DESKTOP_THEME) << program << arguments.join(QStringLiteral(" ")); QProcess *myProcess = new QProcess(this); connect(myProcess, static_cast(&QProcess::finished), this, [this, myProcess](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { emit showSuccessMessage(i18n("Theme installed successfully.")); load(); } else { Q_EMIT showErrorMessage(i18n("Theme installation failed.")); } }); connect(myProcess, static_cast(&QProcess::error), this, [this](QProcess::ProcessError e) { qCWarning(KCM_DESKTOP_THEME) << "Theme installation failed: " << e; Q_EMIT showErrorMessage(i18n("Theme installation failed.")); }); myProcess->start(program, arguments); } void KCMDesktopTheme::applyPlasmaTheme(QQuickItem *item, const QString &themeName) { if (!item) { return; } Plasma::Theme *theme = m_themes[themeName]; if (!theme) { theme = new Plasma::Theme(themeName, this); m_themes[themeName] = theme; } Q_FOREACH (Plasma::Svg *svg, item->findChildren()) { svg->setTheme(theme); svg->setUsingRenderingCache(false); } } void KCMDesktopTheme::load() { m_pendingRemoval.clear(); // Get all desktop themes QStringList themes; const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/desktoptheme"), QStandardPaths::LocateDirectory); Q_FOREACH (const QString &ppath, packs) { const QDir cd(ppath); const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot); Q_FOREACH (const QString &pack, entries) { const QString _metadata = ppath + QLatin1Char('/') + pack + QStringLiteral("/metadata.desktop"); if (QFile::exists(_metadata)) { themes << _metadata; } } } m_model->clear(); Q_FOREACH (const QString &theme, themes) { int themeSepIndex = theme.lastIndexOf(QLatin1Char('/'), -1); const QString themeRoot = theme.left(themeSepIndex); int themeNameSepIndex = themeRoot.lastIndexOf(QLatin1Char('/'), -1); const QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); KDesktopFile df(theme); if (df.noDisplay()) { continue; } QString name = df.readName(); if (name.isEmpty()) { name = packageName; } const bool isLocal = QFileInfo(theme).isWritable(); if (m_model->findItems(packageName).isEmpty()) { QStandardItem *item = new QStandardItem; item->setText(packageName); item->setData(packageName, PluginNameRole); item->setData(name, ThemeNameRole); item->setData(df.readComment(), DescriptionRole); item->setData(isLocal, IsLocalRole); item->setData(false, PendingDeletionRole); m_model->appendRow(item); } } m_model->setSortRole(ThemeNameRole); // FIXME the model should really be just using Qt::DisplayRole m_model->sort(0 /*column*/); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "Theme"); setSelectedPlugin(cg.readEntry("name", m_defaultTheme->themeName())); emit selectedPluginIndexChanged(); updateNeedsSave(); } void KCMDesktopTheme::save() { if (m_defaultTheme->themeName() != m_selectedPlugin) { m_defaultTheme->setThemeName(m_selectedPlugin); } processPendingDeletions(); updateNeedsSave(); } void KCMDesktopTheme::defaults() { setSelectedPlugin(QStringLiteral("default")); // can this be done more elegantly? const auto pendingDeletions = m_model->match(m_model->index(0, 0), PendingDeletionRole, true); for (const QModelIndex &idx : pendingDeletions) { m_model->setData(idx, false, PendingDeletionRole); } } bool KCMDesktopTheme::canEditThemes() const { return m_haveThemeExplorerInstalled; } void KCMDesktopTheme::editTheme(const QString &theme) { QProcess::startDetached(QStringLiteral("plasmathemeexplorer -t ") % theme); } void KCMDesktopTheme::updateNeedsSave() { setNeedsSave(!m_model->match(m_model->index(0, 0), PendingDeletionRole, true).isEmpty() || m_selectedPlugin != m_defaultTheme->themeName()); } void KCMDesktopTheme::processPendingDeletions() { const QString program = QStringLiteral("plasmapkg2"); 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 pluginName = idx.data(PluginNameRole).toString(); const QString displayName = idx.data(Qt::DisplayRole).toString(); Q_ASSERT(pluginName != m_selectedPlugin); const QStringList arguments = {QStringLiteral("-t"), QStringLiteral("theme"), QStringLiteral("-r"), pluginName}; QProcess *process = new QProcess(this); connect(process, static_cast(&QProcess::finished), this, [this, process, idx, pluginName, displayName](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { m_model->removeRow(idx.row()); } else { emit showErrorMessage(i18n("Removing theme failed: %1", QString::fromLocal8Bit(process->readAllStandardOutput().trimmed()))); m_model->setData(idx, false, PendingDeletionRole); } process->deleteLater(); }); process->start(program, arguments); process->waitForFinished(); // needed so it deletes fine when "OK" is clicked and the dialog destroyed } } #include "kcm.moc" diff --git a/kcms/fonts/fonts.cpp b/kcms/fonts/fonts.cpp index a353de0b5..a9fcbdf8a 100644 --- a/kcms/fonts/fonts.cpp +++ b/kcms/fonts/fonts.cpp @@ -1,772 +1,771 @@ /* Copyright 1997 Mark Donohoe Copyright 1999 Lars Knoll Copyright 2000 Rik Hemsley Copyright 2015 Antonis Tsiapaliokas Copyright 2017 Marco Martin Ported to kcontrol2 by Geert Jansen. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fonts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krdb/krdb.h" #include "previewimageprovider.h" /**** DLL Interface ****/ K_PLUGIN_FACTORY_WITH_JSON(KFontsFactory, "kcm_fonts.json", registerPlugin();) //from KFontRequester // Determine if the font with given properties is available on the system, // otherwise find and return the best fitting combination. static QFont nearestExistingFont(const QFont &font) { QFontDatabase dbase; // Initialize font data according to given font object. QString family = font.family(); QString style = dbase.styleString(font); qreal size = font.pointSizeF(); // Check if the family exists. const QStringList families = dbase.families(); if (!families.contains(family)) { // Chose another family. family = QFontInfo(font).family(); // the nearest match if (!families.contains(family)) { family = families.count() ? families.at(0) : QStringLiteral("fixed"); } } // Check if the family has the requested style. // Easiest by piping it through font selection in the database. QString retStyle = dbase.styleString(dbase.font(family, style, 10)); style = retStyle; // Check if the family has the requested size. // Only for bitmap fonts. if (!dbase.isSmoothlyScalable(family, style)) { QList sizes = dbase.smoothSizes(family, style); if (!sizes.contains(size)) { // Find nearest available size. int mindiff = 1000; int refsize = size; Q_FOREACH (int lsize, sizes) { int diff = qAbs(refsize - lsize); if (mindiff > diff) { mindiff = diff; size = lsize; } } } } // Select the font with confirmed properties. QFont result = dbase.font(family, style, int(size)); if (dbase.isSmoothlyScalable(family, style) && result.pointSize() == floor(size)) { result.setPointSizeF(size); } return result; } /**** FontAASettings ****/ #if defined(HAVE_FONTCONFIG) && HAVE_X11 FontAASettings::FontAASettings(QObject *parent) : QObject(parent) , m_state() , m_originalState() , m_subPixelOptionsModel(new QStandardItemModel(this)) , m_hintingOptionsModel(new QStandardItemModel(this)) { m_state.subPixel = KXftConfig::SubPixel::None; for (int t = KXftConfig::SubPixel::None; t <= KXftConfig::SubPixel::Vbgr; ++t) { QStandardItem *item = new QStandardItem(KXftConfig::description((KXftConfig::SubPixel::Type)t)); m_subPixelOptionsModel->appendRow(item); } m_state.hinting = KXftConfig::Hint::None; for (int s = KXftConfig::Hint::None; s <= KXftConfig::Hint::Full; ++s) { QStandardItem * item = new QStandardItem(KXftConfig::description((KXftConfig::Hint::Style)s)); m_hintingOptionsModel->appendRow(item); } } void FontAASettings::load() { double from, to; KXftConfig xft; if (xft.getExcludeRange(from, to)) { m_state.excludeFrom = from; m_state.excludeTo = to; setExclude(true); } else { m_state.excludeFrom = 8; m_state.excludeTo = 15; setExclude(false); } m_originalState.exclude = m_state.exclude; m_originalState.excludeFrom = m_state.excludeFrom; m_originalState.excludeTo = m_state.excludeTo; excludeToChanged(); excludeFromChanged(); KXftConfig::SubPixel::Type spType; if (!xft.getSubPixelType(spType) || KXftConfig::SubPixel::NotSet == spType) { spType = KXftConfig::SubPixel::Rgb; } setSubPixel(spType); m_originalState.subPixel = spType; m_state.subPixelHasLocalConfig = xft.subPixelTypeHasLocalConfig(); KXftConfig::Hint::Style hStyle; if (!xft.getHintStyle(hStyle) || KXftConfig::Hint::NotSet == hStyle) { hStyle = KXftConfig::Hint::Slight; } setHinting(hStyle); m_originalState.hinting = hStyle; m_state.hintingHasLocalConfig = xft.hintStyleHasLocalConfig(); KConfig _cfgfonts("kcmfonts"); KConfigGroup cfgfonts(&_cfgfonts, "General"); int dpicfg; if (KWindowSystem::isPlatformWayland()) { dpicfg = cfgfonts.readEntry("forceFontDPIWayland", 0); } else { dpicfg = cfgfonts.readEntry("forceFontDPI", 0); } if (dpicfg <= 0) { m_originalState.dpi = 0; } else { m_originalState.dpi = dpicfg; }; setDpi(dpicfg); KSharedConfig::Ptr config = KSharedConfig::openConfig("kdeglobals"); KConfigGroup cg(config, "General"); if (cfgfonts.readEntry("dontChangeAASettings", true)) { setAntiAliasing(true); //AASystem m_state.antiAliasingHasLocalConfig = false; } else if (cg.readEntry("XftAntialias", true)) { setAntiAliasing(true); //AAEnabled m_state.antiAliasingHasLocalConfig = xft.antiAliasingHasLocalConfig(); } else { setAntiAliasing(false); //AADisabled m_state.antiAliasingHasLocalConfig = xft.antiAliasingHasLocalConfig(); } m_originalState.antiAliasing = m_state.antiAliasing; m_originalState.antiAliasingHasLocalConfig = m_state.antiAliasingHasLocalConfig; } bool FontAASettings::save(KXftConfig::AntiAliasing::State aaState) { KXftConfig xft; KConfig kglobals("kdeglobals", KConfig::NoGlobals); KConfigGroup grp(&kglobals, "General"); xft.setAntiAliasing(aaState); if (m_state.exclude) { xft.setExcludeRange(m_state.excludeFrom, m_state.excludeTo); } else { xft.setExcludeRange(0, 0); } KXftConfig::SubPixel::Type spType = (KXftConfig::SubPixel::Type)m_state.subPixel; if (subPixelNeedsSave()) { xft.setSubPixelType(spType); } else { xft.setSubPixelType(KXftConfig::SubPixel::NotSet); } grp.writeEntry("XftSubPixel", KXftConfig::toStr(spType)); if (aaState == KXftConfig::AntiAliasing::NotSet) { grp.revertToDefault("XftAntialias"); } else { grp.writeEntry("XftAntialias", aaState == KXftConfig::AntiAliasing::Enabled); } bool mod = false; KXftConfig::Hint::Style hStyle = (KXftConfig::Hint::Style)m_state.hinting; if (hintingNeedsSave()) { xft.setHintStyle(hStyle); } else { xft.setHintStyle(KXftConfig::Hint::NotSet); } QString hs(KXftConfig::toStr(hStyle)); if (hs != grp.readEntry("XftHintStyle")) { if (KXftConfig::Hint::NotSet == hStyle) { grp.revertToDefault("XftHintStyle"); } else { grp.writeEntry("XftHintStyle", hs); } } mod = true; kglobals.sync(); if (!mod) { mod = xft.changed(); } xft.apply(); KConfig _cfgfonts("kcmfonts"); KConfigGroup cfgfonts(&_cfgfonts, "General"); if (KWindowSystem::isPlatformWayland()) { cfgfonts.writeEntry("forceFontDPIWayland", m_state.dpi); } else { cfgfonts.writeEntry("forceFontDPI", m_state.dpi); } cfgfonts.sync(); #if HAVE_X11 // if the setting is reset in the module, remove the dpi value, // otherwise don't explicitly remove it and leave any possible system-wide value if (m_state.dpi == 0 && m_originalState.dpi != 0 && !KWindowSystem::isPlatformWayland()) { QProcess proc; proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start("xrdb", QStringList() << "-quiet" << "-remove" << "-nocpp"); if (proc.waitForStarted()) { proc.write(QByteArray("Xft.dpi\n")); proc.closeWriteChannel(); proc.waitForFinished(); } } #endif QApplication::processEvents(); // Process font change ourselves // Don't overwrite global settings unless explicitly asked for - e.g. the system // fontconfig setup may be much more complex than this module can provide. // TODO: With AASystem the changes already made by this module should be reverted somehow. #if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) if (mod || (m_state.antiAliasing != m_originalState.antiAliasing) || m_state.dpi != m_originalState.dpi) { m_originalState.antiAliasing = m_state.antiAliasing; m_originalState.dpi = m_state.dpi; emit aliasingChangeApplied(); } #else #if HAVE_X11 if (m_state.dpi != m_originalState.dpi) { m_originalState.dpi = m_state.dpi; } #endif #endif m_originalState.exclude = m_state.exclude; m_originalState.excludeTo = m_state.excludeTo; m_originalState.excludeFrom = m_state.excludeFrom; m_originalState.subPixel = m_state.subPixel; m_originalState.hinting = m_state.hinting; return mod; } void FontAASettings::defaults() { setExcludeTo(15); setExcludeFrom(8); setAntiAliasing(true); m_originalState.antiAliasing = m_state.antiAliasing; m_state.antiAliasingHasLocalConfig = false; setDpi(0); setSubPixel(KXftConfig::SubPixel::Rgb); m_state.subPixelHasLocalConfig = false; setHinting(KXftConfig::Hint::Slight); m_state.hintingHasLocalConfig = false; } #endif void FontAASettings::setExclude(bool exclude) { if (exclude == m_state.exclude) { return; } m_state.exclude = exclude; emit excludeChanged(); } bool FontAASettings::exclude() const { return m_state.exclude; } void FontAASettings::setExcludeTo(const int &excludeTo) { if (m_state.excludeTo == excludeTo) { return; } m_state.excludeTo = excludeTo; emit excludeToChanged(); } int FontAASettings::excludeTo() const { return m_state.excludeTo; } void FontAASettings::setExcludeFrom(const int &excludeTo) { if (m_state.excludeFrom == excludeTo) { return; } m_state.excludeFrom = excludeTo; emit excludeToChanged(); } int FontAASettings::excludeFrom() const { return m_state.excludeFrom; } void FontAASettings::setAntiAliasing(bool antiAliasing) { if (m_state.antiAliasing == antiAliasing) { return; } m_state.antiAliasing = antiAliasing; emit aliasingChanged(); } bool FontAASettings::antiAliasing() const { return m_state.antiAliasing; } bool FontAASettings::antiAliasingNeedsSave() const { return m_state.antiAliasingHasLocalConfig || (m_state.antiAliasing != m_originalState.antiAliasing); } bool FontAASettings::subPixelNeedsSave() const { return m_state.subPixelHasLocalConfig || (m_state.subPixel != m_originalState.subPixel); } bool FontAASettings::hintingNeedsSave() const { return m_state.hintingHasLocalConfig || (m_state.hinting != m_originalState.hinting); } void FontAASettings::setDpi(const int &dpi) { if (m_state.dpi == dpi) { return; } m_state.dpi = dpi; emit dpiChanged(); } int FontAASettings::dpi() const { return m_state.dpi; } void FontAASettings::setSubPixel(int idx) { if (m_state.subPixel == idx) { return; } m_state.subPixel = idx; emit subPixelCurrentIndexChanged(); } void FontAASettings::setSubPixelCurrentIndex(int idx) { setSubPixel(KXftConfig::SubPixel::None + idx); } int FontAASettings::subPixelCurrentIndex() { return m_state.subPixel - KXftConfig::SubPixel::None; } void FontAASettings::setHinting(int idx) { if (m_state.hinting == idx) { return; } m_state.hinting = idx; emit hintingCurrentIndexChanged(); } void FontAASettings::setHintingCurrentIndex(int idx) { setHinting(KXftConfig::Hint::None + idx); } int FontAASettings::hintingCurrentIndex() { return m_state.hinting - KXftConfig::Hint::None; } bool FontAASettings::needsSave() const { return m_state != m_originalState; } /**** KFonts ****/ KFonts::KFonts(QObject *parent, const QVariantList &args) : KQuickAddons::ConfigModule(parent, args) , m_fontAASettings(new FontAASettings(this)) { - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); KAboutData* about = new KAboutData("kcm_fonts", i18n("Fonts"), "0.1", QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Antonis Tsiapaliokas"), QString(), "antonis.tsiapaliokas@kde.org"); setAboutData(about); qmlRegisterType(); setButtons(Apply | Default | Help); auto updateState = [this]() { setNeedsSave(m_fontAASettings->needsSave()); }; connect(m_fontAASettings, &FontAASettings::subPixelCurrentIndexChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::hintingCurrentIndexChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::excludeChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::excludeFromChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::excludeToChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::antiAliasingChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::aliasingChanged, this, updateState); connect(m_fontAASettings, &FontAASettings::dpiChanged, this, updateState); } KFonts::~KFonts() { } void KFonts::defaults() { #ifdef Q_OS_MAC setGeneralFont(QFont("Lucida Grande", 13)); setMenuFont(QFont("Lucida Grande", 13)); setFixedWidthFont(QFont("Monaco", 10)); setToolbarFont(QFont("Lucida Grande", 11)); setSmallFont(QFont("Lucida Grande", 9)); setWindowTitleFont(QFont("Lucida Grande", 14)); #else setGeneralFont(QFont("Noto Sans", 10)); setMenuFont(QFont("Noto Sans", 10)); setFixedWidthFont(QFont("Hack", 9)); setToolbarFont(QFont("Noto Sans", 10)); setSmallFont(QFont("Noto Sans", 8)); setWindowTitleFont(QFont("Noto Sans", 10)); #endif m_fontAASettings->defaults(); } void KFonts::load() { KSharedConfig::Ptr config = KSharedConfig::openConfig("kdeglobals"); KConfigGroup cg(config, "General"); m_generalFont = m_generalFontOriginal = nearestExistingFont(cg.readEntry("font", m_defaultFont)); m_fixedWidthFont = m_fixedWidthFontOriginal = nearestExistingFont(cg.readEntry("fixed", QFont("Hack", 9))); m_smallFont = m_smallFontOriginal = nearestExistingFont(cg.readEntry("smallestReadableFont", m_defaultFont)); m_toolbarFont = m_toolbarFontOriginal = nearestExistingFont(cg.readEntry("toolBarFont", m_defaultFont)); m_menuFont = m_menuFontOriginal = nearestExistingFont(cg.readEntry("menuFont", m_defaultFont)); cg = KConfigGroup(config, "WM"); m_windowTitleFont = m_windowTitleFontOriginal = nearestExistingFont(cg.readEntry("activeFont", m_defaultFont)); engine()->addImageProvider("preview", new PreviewImageProvider(generalFont())); emit generalFontChanged(); emit fixedWidthFontChanged(); emit smallFontChanged(); emit toolbarFontChanged(); emit menuFontChanged(); emit windowTitleFontChanged(); m_fontAASettings->load(); setNeedsSave(false); } void KFonts::save() { KSharedConfig::Ptr config = KSharedConfig::openConfig("kdeglobals"); KConfigGroup cg(config, "General"); cg.writeEntry("font", m_generalFont.toString()); cg.writeEntry("fixed", m_fixedWidthFont.toString()); cg.writeEntry("smallestReadableFont", m_smallFont.toString()); cg.writeEntry("toolBarFont", m_toolbarFont.toString()); cg.writeEntry("menuFont", m_menuFont.toString()); cg.sync(); cg = KConfigGroup(config, "WM"); cg.writeEntry("activeFont", m_windowTitleFont.toString()); cg.sync(); m_defaultFontOriginal = m_defaultFont; m_generalFontOriginal = m_generalFont; m_fixedWidthFontOriginal = m_fixedWidthFont; m_smallFontOriginal = m_smallFont; m_toolbarFontOriginal = m_toolbarFont; m_menuFontOriginal = m_menuFont; m_windowTitleFontOriginal = m_windowTitleFont; KConfig _cfgfonts("kcmfonts"); KConfigGroup cfgfonts(&_cfgfonts, "General"); FontAASettings::AASetting aaSetting = FontAASettings::AASystem; if (m_fontAASettings->antiAliasingNeedsSave()) { aaSetting = m_fontAASettings->antiAliasing() ? FontAASettings::AAEnabled : FontAASettings::AADisabled; } cfgfonts.writeEntry("dontChangeAASettings", aaSetting == FontAASettings::AASystem); if (aaSetting == FontAASettings::AAEnabled) { m_fontAASettings->save(KXftConfig::AntiAliasing::Enabled); } else if (aaSetting == FontAASettings::AADisabled) { m_fontAASettings->save(KXftConfig::AntiAliasing::Disabled); } else { m_fontAASettings->save(KXftConfig::AntiAliasing::NotSet); } KGlobalSettings::self()->emitChange(KGlobalSettings::FontChanged); runRdb(KRdbExportXftSettings | KRdbExportGtkTheme); emit fontsHaveChanged(); setNeedsSave(false); } void KFonts::updateNeedsSave() { setNeedsSave(m_defaultFontOriginal != m_defaultFont || m_generalFontOriginal != m_generalFont || m_fixedWidthFontOriginal != m_fixedWidthFont || m_smallFontOriginal != m_smallFont || m_toolbarFontOriginal != m_toolbarFont || m_menuFontOriginal != m_menuFont || m_windowTitleFontOriginal != m_windowTitleFont || m_fontAASettings->needsSave()); } void KFonts::setGeneralFont(const QFont &font) { if (m_generalFont == font) { return; } m_generalFont = font; emit generalFontChanged(); updateNeedsSave(); } QFont KFonts::generalFont() const { return m_generalFont; } void KFonts::setFixedWidthFont(const QFont &font) { if (m_fixedWidthFont == font) { return; } m_fixedWidthFont = font; emit fixedWidthFontChanged(); updateNeedsSave(); } QFont KFonts::fixedWidthFont() const { return m_fixedWidthFont; } void KFonts::setSmallFont(const QFont &font) { if (m_smallFont == font) { return; } m_smallFont = font; emit smallFontChanged(); updateNeedsSave(); } QFont KFonts::smallFont() const { return m_smallFont; } void KFonts::setToolbarFont(const QFont &font) { if (m_toolbarFont == font) { return; } m_toolbarFont = font; emit toolbarFontChanged(); updateNeedsSave(); } QFont KFonts::toolbarFont() const { return m_toolbarFont; } void KFonts::setMenuFont(const QFont &font) { if (m_menuFont == font) { return; } m_menuFont = font; emit menuFontChanged(); updateNeedsSave(); } QFont KFonts::menuFont() const { return m_menuFont; } void KFonts::setWindowTitleFont(const QFont &font) { if (m_windowTitleFont == font) { return; } m_windowTitleFont = font; emit windowTitleFontChanged(); updateNeedsSave(); } QFont KFonts::windowTitleFont() const { return m_windowTitleFont; } void KFonts::adjustAllFonts() { QFont font = m_generalFont; KFontChooser::FontDiffFlags fontDiffFlags; int ret = KFontDialog::getFontDiff(font, fontDiffFlags, KFontChooser::NoDisplayFlags); if (ret == KDialog::Accepted && fontDiffFlags) { setGeneralFont(applyFontDiff(m_generalFont, font, fontDiffFlags)); setMenuFont(applyFontDiff(m_menuFont, font, fontDiffFlags)); { const QFont adjustedFont = applyFontDiff(m_fixedWidthFont, font, fontDiffFlags); if (QFontInfo(adjustedFont).fixedPitch()) { setFixedWidthFont(adjustedFont); } } setToolbarFont(applyFontDiff(m_toolbarFont, font, fontDiffFlags)); setSmallFont(applyFontDiff(m_smallFont, font, fontDiffFlags)); setWindowTitleFont(applyFontDiff(m_windowTitleFont, font, fontDiffFlags)); } } QFont KFonts::applyFontDiff(const QFont &fnt, const QFont &newFont, int fontDiffFlags) { QFont font(fnt); if (fontDiffFlags & KFontChooser::FontDiffSize) { font.setPointSizeF(newFont.pointSizeF()); } if ((fontDiffFlags & KFontChooser::FontDiffFamily)) { font.setFamily(newFont.family()); } if (fontDiffFlags & KFontChooser::FontDiffStyle) { font.setWeight(newFont.weight()); font.setStyle(newFont.style()); font.setUnderline(newFont.underline()); font.setStyleName(newFont.styleName()); } return font; } bool FontAASettings::State::operator==(const State& other) const { if ( exclude != other.exclude || antiAliasing != other.antiAliasing || dpi != other.dpi || subPixel != other.subPixel || hinting != other.hinting ) { return false; } if (exclude && (excludeFrom != other.excludeFrom || excludeTo != other.excludeTo)) { return false; } return true; } bool FontAASettings::State::operator!=(const State& other) const { return !(*this == other); } #include "fonts.moc" diff --git a/kcms/lookandfeel/kcm.cpp b/kcms/lookandfeel/kcm.cpp index 738623d58..9fc78e487 100644 --- a/kcms/lookandfeel/kcm.cpp +++ b/kcms/lookandfeel/kcm.cpp @@ -1,834 +1,830 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include "../krdb/krdb.h" #include "../cursortheme/xcursor/xcursortheme.h" #include "config-kcm.h" #include "config-workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XFIXES # include #endif KCMLookandFeel::KCMLookandFeel(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) , m_config(QStringLiteral("kdeglobals")) , m_configGroup(m_config.group("KDE")) , m_applyColors(true) , m_applyWidgetStyle(true) , m_applyIcons(true) , m_applyPlasmaTheme(true) , m_applyCursors(true) , m_applyWindowSwitcher(true) , m_applyDesktopSwitcher(true) , m_resetDefaultLayout(false) , m_applyWindowDecoration(true) { - //This flag seems to be needed in order for QQuickWidget to work - //see https://bugreports.qt-project.org/browse/QTBUG-40765 - //also, it seems to work only if set in the kcm, not in the systemsettings' main - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); qmlRegisterType(); qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_lookandfeel"), i18n("Global Theme"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); setAboutData(about); setButtons(Apply | Default); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = "pluginName"; roles[DescriptionRole] = "description"; roles[ScreenhotRole] = "screenshot"; roles[FullScreenPreviewRole] = "fullScreenPreview"; roles[HasSplashRole] = "hasSplash"; roles[HasLockScreenRole] = "hasLockScreen"; roles[HasRunCommandRole] = "hasRunCommand"; roles[HasLogoutRole] = "hasLogout"; roles[HasColorsRole] = "hasColors"; roles[HasWidgetStyleRole] = "hasWidgetStyle"; roles[HasIconsRole] = "hasIcons"; roles[HasPlasmaThemeRole] = "hasPlasmaTheme"; roles[HasCursorsRole] = "hasCursors"; roles[HasWindowSwitcherRole] = "hasWindowSwitcher"; roles[HasDesktopSwitcherRole] = "hasDesktopSwitcher"; m_model->setItemRoleNames(roles); loadModel(); } KCMLookandFeel::~KCMLookandFeel() { } void KCMLookandFeel::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("lookandfeel.knsrc") ); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Global Themes")); m_newStuffDialog->setWindowModality(Qt::WindowModal); m_newStuffDialog->winId(); // so it creates the windowHandle(); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMLookandFeel::loadModel); } if (ctx && ctx->window()) { m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); } m_newStuffDialog.data()->show(); } QStandardItemModel *KCMLookandFeel::lookAndFeelModel() { return m_model; } QString KCMLookandFeel::selectedPlugin() const { return m_selectedPlugin; } void KCMLookandFeel::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } const bool firstTime = m_selectedPlugin.isNull(); m_selectedPlugin = plugin; emit selectedPluginChanged(); emit selectedPluginIndexChanged(); if (!firstTime) { setNeedsSave(true); } } int KCMLookandFeel::selectedPluginIndex() const { for (int i = 0; i < m_model->rowCount(); ++i) { if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { return i; } } return -1; } QList KCMLookandFeel::availablePackages(const QStringList &components) { QList packages; QStringList paths; const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); paths.reserve(dataPaths.count()); for (const QString &path : dataPaths) { QDir dir(path + QStringLiteral("/plasma/look-and-feel")); paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } for (const QString &path : paths) { Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); pkg.setPath(path); pkg.setFallbackPackage(Plasma::Package()); if (components.isEmpty()) { packages << pkg; } else { for (const auto &component : components) { if (!pkg.filePath(component.toUtf8()).isEmpty()) { packages << pkg; break; } } } } return packages; } void KCMLookandFeel::loadModel() { m_model->clear(); const QList pkgs = availablePackages({"defaults", "layouts"}); for (const Plasma::Package &pkg : pkgs) { if (!pkg.metadata().isValid()) { continue; } QStandardItem* row = new QStandardItem(pkg.metadata().name()); row->setData(pkg.metadata().pluginName(), PluginNameRole); row->setData(pkg.metadata().comment(), DescriptionRole); row->setData(pkg.filePath("preview"), ScreenhotRole); row->setData(pkg.filePath("fullscreenpreview"), FullScreenPreviewRole); //What the package provides row->setData(!pkg.filePath("splashmainscript").isEmpty(), HasSplashRole); row->setData(!pkg.filePath("lockscreenmainscript").isEmpty(), HasLockScreenRole); row->setData(!pkg.filePath("runcommandmainscript").isEmpty(), HasRunCommandRole); row->setData(!pkg.filePath("logoutmainscript").isEmpty(), HasLogoutRole); if (!pkg.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); bool hasColors = !cg.readEntry("ColorScheme", QString()).isEmpty(); if (!hasColors) { hasColors = !pkg.filePath("colors").isEmpty(); } row->setData(hasColors, HasColorsRole); cg = KConfigGroup(&cg, "KDE"); row->setData(!cg.readEntry("widgetStyle", QString()).isEmpty(), HasWidgetStyleRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Icons"); row->setData(!cg.readEntry("Theme", QString()).isEmpty(), HasIconsRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Theme"); row->setData(!cg.readEntry("name", QString()).isEmpty(), HasPlasmaThemeRole); cg = KConfigGroup(conf, "kcminputrc"); cg = KConfigGroup(&cg, "Mouse"); row->setData(!cg.readEntry("cursorTheme", QString()).isEmpty(), HasCursorsRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "WindowSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasWindowSwitcherRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "DesktopSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasDesktopSwitcherRole); } m_model->appendRow(row); } m_model->sort(0 /*column*/); emit selectedPluginIndexChanged(); } void KCMLookandFeel::load() { m_package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { m_package.setPath(packageName); } if (!m_package.metadata().isValid()) { return; } setSelectedPlugin(m_package.metadata().pluginName()); setNeedsSave(false); } void KCMLookandFeel::save() { Plasma::Package package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); package.setPath(m_selectedPlugin); if (!package.isValid()) { return; } m_configGroup.writeEntry("LookAndFeelPackage", m_selectedPlugin); if (m_resetDefaultLayout) { QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), QStringLiteral("/PlasmaShell"), QStringLiteral("org.kde.PlasmaShell"), QStringLiteral("loadLookAndFeelDefaultLayout")); QList args; args << m_selectedPlugin; message.setArguments(args); QDBusConnection::sessionBus().call(message, QDBus::NoBlock); } if (!package.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "KDE"); if (m_applyWidgetStyle) { setWidgetStyle(cg.readEntry("widgetStyle", QString())); } if (m_applyColors) { QString colorsFile = package.filePath("colors"); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); QString colorScheme = cg.readEntry("ColorScheme", QString()); if (!colorsFile.isEmpty()) { if (!colorScheme.isEmpty()) { setColors(colorScheme, colorsFile); } else { setColors(package.metadata().name(), colorsFile); } } else if (!colorScheme.isEmpty()) { colorScheme.remove(QLatin1Char('\'')); // So Foo's does not become FooS QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)")); int offset; while ((offset = fixer.indexIn(colorScheme)) >= 0) { colorScheme.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); } colorScheme.replace(0, 1, colorScheme.at(0).toUpper()); //NOTE: why this loop trough all the scheme files? //the scheme theme name is an heuristic, there is no plugin metadata whatsoever. //is based on the file name stripped from weird characters or the //eventual id- prefix store.kde.org puts, so we can just find a //theme that ends as the specified name bool schemeFound = false; const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); for (const QString &dir : schemeDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList()<emitChange(KGlobalSettings::StyleChanged); } void KCMLookandFeel::setColors(const QString &scheme, const QString &colorFile) { if (scheme.isEmpty() && colorFile.isEmpty()) { return; } KSharedConfigPtr conf = KSharedConfig::openConfig(colorFile, KSharedConfig::CascadeConfig); foreach (const QString &grp, conf->groupList()) { KConfigGroup cg(conf, grp); KConfigGroup cg2(&m_config, grp); cg.copyTo(&cg2); } KConfigGroup configGroup(&m_config, "General"); configGroup.writeEntry("ColorScheme", scheme); configGroup.sync(); KGlobalSettings::self()->emitChange(KGlobalSettings::PaletteChanged); } void KCMLookandFeel::setIcons(const QString &theme) { if (theme.isEmpty()) { return; } KConfigGroup cg(&m_config, "Icons"); cg.writeEntry("Theme", theme); cg.sync(); for (int i=0; i < KIconLoader::LastGroup; i++) { KIconLoader::emitChange(KIconLoader::Group(i)); } } void KCMLookandFeel::setPlasmaTheme(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("plasmarc")); KConfigGroup cg(&config, "Theme"); cg.writeEntry("name", theme); cg.sync(); } void KCMLookandFeel::setCursorTheme(const QString themeName) { //TODO: use pieces of cursor kcm when moved to plasma-desktop if (themeName.isEmpty()) { return; } KConfig config(QStringLiteral("kcminputrc")); KConfigGroup cg(&config, "Mouse"); cg.writeEntry("cursorTheme", themeName); cg.sync(); // Require the Xcursor version that shipped with X11R6.9 or greater, since // in previous versions the Xfixes code wasn't enabled due to a bug in the // build system (freedesktop bug #975). #if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105 const int cursorSize = cg.readEntry("cursorSize", 0); QDir themeDir = cursorThemeDir(themeName, 0); if (!themeDir.exists()) { return; } XCursorTheme theme(themeDir); if (!CursorTheme::haveXfixes()) { return; } // Set up the proper launch environment for newly started apps OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QDBusConnection::sessionBus()); klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), themeName); // Update the Xcursor X resources runRdb(0); // Notify all applications that the cursor theme has changed KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged); // Reload the standard cursors QStringList names; // Qt cursors names << QStringLiteral("left_ptr") << QStringLiteral("up_arrow") << QStringLiteral("cross") << QStringLiteral("wait") << QStringLiteral("left_ptr_watch") << QStringLiteral("ibeam") << QStringLiteral("size_ver") << QStringLiteral("size_hor") << QStringLiteral("size_bdiag") << QStringLiteral("size_fdiag") << QStringLiteral("size_all") << QStringLiteral("split_v") << QStringLiteral("split_h") << QStringLiteral("pointing_hand") << QStringLiteral("openhand") << QStringLiteral("closedhand") << QStringLiteral("forbidden") << QStringLiteral("whats_this") << QStringLiteral("copy") << QStringLiteral("move") << QStringLiteral("link"); // X core cursors names << QStringLiteral("X_cursor") << QStringLiteral("right_ptr") << QStringLiteral("hand1") << QStringLiteral("hand2") << QStringLiteral("watch") << QStringLiteral("xterm") << QStringLiteral("crosshair") << QStringLiteral("left_ptr_watch") << QStringLiteral("center_ptr") << QStringLiteral("sb_h_double_arrow") << QStringLiteral("sb_v_double_arrow") << QStringLiteral("fleur") << QStringLiteral("top_left_corner") << QStringLiteral("top_side") << QStringLiteral("top_right_corner") << QStringLiteral("right_side") << QStringLiteral("bottom_right_corner") << QStringLiteral("bottom_side") << QStringLiteral("bottom_left_corner") << QStringLiteral("left_side") << QStringLiteral("question_arrow") << QStringLiteral("pirate"); foreach (const QString &name, names) { XFixesChangeCursorByName(QX11Info::display(), theme.loadCursor(name, cursorSize), QFile::encodeName(name)); } #else KMessageBox::information(this, i18n("You have to restart the Plasma session for these changes to take effect."), i18n("Cursor Settings Changed"), "CursorSettingsChanged"); #endif } QDir KCMLookandFeel::cursorThemeDir(const QString &theme, const int depth) { // Prevent infinite recursion if (depth > 10) { return QDir(); } // Search each icon theme directory for 'theme' foreach (const QString &baseDir, cursorSearchPaths()) { QDir dir(baseDir); if (!dir.exists() || !dir.cd(theme)) { continue; } // If there's a cursors subdir, we'll assume this is a cursor theme if (dir.exists(QStringLiteral("cursors"))) { return dir; } // If the theme doesn't have an index.theme file, it can't inherit any themes. if (!dir.exists(QStringLiteral("index.theme"))) { continue; } // Open the index.theme file, so we can get the list of inherited themes KConfig config(dir.path() + QStringLiteral("/index.theme"), KConfig::NoGlobals); KConfigGroup cg(&config, "Icon Theme"); // Recurse through the list of inherited themes, to check if one of them // is a cursor theme. QStringList inherits = cg.readEntry("Inherits", QStringList()); foreach (const QString &inherit, inherits) { // Avoid possible DoS if (inherit == theme) { continue; } if (cursorThemeDir(inherit, depth + 1).exists()) { return dir; } } } return QDir(); } const QStringList KCMLookandFeel::cursorSearchPaths() { if (!m_cursorSearchPaths.isEmpty()) return m_cursorSearchPaths; #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 // These are the default paths Xcursor will scan for cursor themes QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); // If XCURSOR_PATH is set, use that instead of the default path char *xcursorPath = std::getenv("XCURSOR_PATH"); if (xcursorPath) path = xcursorPath; #else // Get the search path from Xcursor QString path = XcursorLibraryPath(); #endif // Separate the paths m_cursorSearchPaths = path.split(QLatin1Char(':'), QString::SkipEmptyParts); // Remove duplicates QMutableStringListIterator i(m_cursorSearchPaths); while (i.hasNext()) { const QString path = i.next(); QMutableStringListIterator j(i); while (j.hasNext()) if (j.next() == path) j.remove(); } // Expand all occurrences of ~/ to the home dir m_cursorSearchPaths.replaceInStrings(QRegExp(QStringLiteral("^~\\/")), QDir::home().path() + QLatin1Char('/')); return m_cursorSearchPaths; } void KCMLookandFeel::setSplashScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("ksplashrc")); KConfigGroup cg(&config, "KSplash"); cg.writeEntry("Theme", theme); //TODO: a way to set none as spash in the l&f cg.writeEntry("Engine", "KSplashQML"); cg.sync(); } void KCMLookandFeel::setLockScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kscreenlockerrc")); KConfigGroup cg(&config, "Greeter"); cg.writeEntry("Theme", theme); cg.sync(); } void KCMLookandFeel::setWindowSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("LayoutName", theme); cg.sync(); } void KCMLookandFeel::setDesktopSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("DesktopLayout", theme); cg.writeEntry("DesktopListLayout", theme); cg.sync(); } void KCMLookandFeel::setWindowDecoration(const QString &library, const QString &theme) { if (library.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "org.kde.kdecoration2"); cg.writeEntry("library", library); cg.writeEntry("theme", theme); cg.sync(); // Reload KWin. QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); QDBusConnection::sessionBus().send(message); } void KCMLookandFeel::setApplyColors(bool apply) { if (m_applyColors == apply) { return; } m_applyColors = apply; emit applyColorsChanged(); } bool KCMLookandFeel::applyColors() const { return m_applyColors; } void KCMLookandFeel::setApplyWidgetStyle(bool apply) { if (m_applyWidgetStyle == apply) { return; } m_applyWidgetStyle = apply; emit applyWidgetStyleChanged(); } bool KCMLookandFeel::applyWidgetStyle() const { return m_applyWidgetStyle; } void KCMLookandFeel::setApplyIcons(bool apply) { if (m_applyIcons == apply) { return; } m_applyIcons = apply; emit applyIconsChanged(); } bool KCMLookandFeel::applyIcons() const { return m_applyIcons; } void KCMLookandFeel::setApplyPlasmaTheme(bool apply) { if (m_applyPlasmaTheme == apply) { return; } m_applyPlasmaTheme = apply; emit applyPlasmaThemeChanged(); } bool KCMLookandFeel::applyPlasmaTheme() const { return m_applyPlasmaTheme; } void KCMLookandFeel::setApplyWindowSwitcher(bool apply) { if (m_applyWindowSwitcher == apply) { return; } m_applyWindowSwitcher = apply; emit applyWindowSwitcherChanged(); } bool KCMLookandFeel::applyWindowSwitcher() const { return m_applyWindowSwitcher; } void KCMLookandFeel::setApplyDesktopSwitcher(bool apply) { if (m_applyDesktopSwitcher == apply) { return; } m_applyDesktopSwitcher = apply; emit applyDesktopSwitcherChanged(); } bool KCMLookandFeel::applyDesktopSwitcher() const { return m_applyDesktopSwitcher; } void KCMLookandFeel::setResetDefaultLayout(bool reset) { if (m_resetDefaultLayout == reset) { return; } m_resetDefaultLayout = reset; emit resetDefaultLayoutChanged(); if (reset) { setNeedsSave(true); } } bool KCMLookandFeel::resetDefaultLayout() const { return m_resetDefaultLayout; }