diff --git a/kcms/fonts/fonts.cpp b/kcms/fonts/fonts.cpp index 4d9183733..02e31fea7 100644 --- a/kcms/fonts/fonts.cpp +++ b/kcms/fonts/fonts.cpp @@ -1,711 +1,776 @@ /* 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_subPixelOptionsModel(new QStandardItemModel(this)) , m_hintingOptionsModel(new QStandardItemModel(this)) { - for (int t = KXftConfig::SubPixel::NotSet; t <= KXftConfig::SubPixel::Vbgr; ++t) { + 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); } - for (int s = KXftConfig::Hint::NotSet; s <= KXftConfig::Hint::Full; ++s) { + 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_excludeFrom = from; - m_excludeTo = to; + m_state.excludeFrom = from; + m_state.excludeTo = to; setExclude(true); } else { - m_excludeFrom = 8; - m_excludeTo = 15; + m_state.excludeFrom = 8; + m_state.excludeTo = 15; setExclude(false); } - m_excludeFromOriginal = m_excludeFrom; - m_excludeToOriginal = m_excludeTo; + m_originalState.exclude = m_state.exclude; + m_originalState.excludeFrom = m_state.excludeFrom; + m_originalState.excludeTo = m_state.excludeTo; excludeToChanged(); excludeFromChanged(); KXftConfig::SubPixel::Type spType; - xft.getSubPixelType(spType); + if (!xft.getSubPixelType(spType) || KXftConfig::SubPixel::NotSet == spType) { + spType = KXftConfig::SubPixel::None; + } - setSubPixelCurrentIndex(spType); - m_subPixelCurrentIndexOriginal = spType; + setSubPixel(spType); + m_originalState.subPixel = spType; + m_state.subPixelHasLocalConfig = xft.subPixelTypeHasLocalConfig(); KXftConfig::Hint::Style hStyle; if (!xft.getHintStyle(hStyle) || KXftConfig::Hint::NotSet == hStyle) { - KConfig kglobals("kdeglobals", KConfig::NoGlobals); - - hStyle = KXftConfig::Hint::NotSet; - xft.setHintStyle(hStyle); - KConfigGroup(&kglobals, "General").writeEntry("XftHintStyle", KXftConfig::toStr(hStyle)); - kglobals.sync(); - runRdb(KRdbExportXftSettings | KRdbExportGtkTheme); + hStyle = KXftConfig::Hint::Medium; } - setHintingCurrentIndex(hStyle); - m_hintingCurrentIndexOriginal = hStyle; + 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_dpiOriginal = 0; + m_originalState.dpi = 0; } else { - m_dpiOriginal = dpicfg; + m_originalState.dpi = dpicfg; }; setDpi(dpicfg); KSharedConfig::Ptr config = KSharedConfig::openConfig("kdeglobals"); KConfigGroup cg(config, "General"); if (cfgfonts.readEntry("dontChangeAASettings", true)) { - setAntiAliasing(1); //AASystem + setAntiAliasing(true); //AASystem + m_state.antiAliasingHasLocalConfig = false; } else if (cg.readEntry("XftAntialias", true)) { - setAntiAliasing(0); //AAEnabled + setAntiAliasing(true); //AAEnabled + m_state.antiAliasingHasLocalConfig = xft.antiAliasingHasLocalConfig(); } else { - setAntiAliasing(2); //AADisabled + setAntiAliasing(false); //AADisabled + m_state.antiAliasingHasLocalConfig = xft.antiAliasingHasLocalConfig(); } - m_antiAliasingOriginal = m_antiAliasing; + 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_exclude) { - xft.setExcludeRange(m_excludeFrom, m_excludeTo); + 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_subPixelCurrentIndex; + KXftConfig::SubPixel::Type spType = (KXftConfig::SubPixel::Type)m_state.subPixel; - xft.setSubPixelType(spType); + 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_hintingCurrentIndex; + KXftConfig::Hint::Style hStyle = (KXftConfig::Hint::Style)m_state.hinting; - xft.setHintStyle(hStyle); + 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_dpi); + cfgfonts.writeEntry("forceFontDPIWayland", m_state.dpi); } else { - cfgfonts.writeEntry("forceFontDPI", m_dpi); + 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_dpi == 0 && m_dpiOriginal != 0 && !KWindowSystem::isPlatformWayland()) { + 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) && HAVE_X11 - if (mod || (m_antiAliasing != m_antiAliasingOriginal) || m_dpi != m_dpiOriginal) { +#if defined(HAVE_FONTCONFIG) && defined (HAVE_X11) + if (mod || (m_state.antiAliasing != m_originalState.antiAliasing) || m_state.dpi != m_originalState.dpi) { KMessageBox::information(nullptr, i18n( "

Some changes such as anti-aliasing or DPI will only affect newly started applications.

" ), i18n("Font Settings Changed"), "FontSettingsChanged"); - m_antiAliasingOriginal = m_antiAliasing; - m_dpiOriginal = m_dpi; + m_originalState.antiAliasing = m_state.antiAliasing; + m_originalState.dpi = m_state.dpi; } #else #if HAVE_X11 - if (m_dpi != m_dpiOriginal) { + if (m_state.dpi != m_originalState.dpi) { KMessageBox::information(0, i18n( "

Some changes such as DPI will only affect newly started applications.

" ), i18n("Font Settings Changed"), "FontSettingsChanged"); - m_dpiOriginal = m_dpi; + m_originalState.dpi = m_state.dpi; } #endif #endif - m_excludeToOriginal = m_excludeTo; - m_excludeFromOriginal = m_excludeFrom; + m_originalState.excludeTo = m_state.excludeTo; + m_originalState.excludeFrom = m_state.excludeFrom; - m_subPixelCurrentIndexOriginal = m_subPixelCurrentIndex; - m_hintingCurrentIndexOriginal = m_hintingCurrentIndex; + m_originalState.subPixel = m_state.subPixel; + m_originalState.hinting = m_state.hinting; return mod; } void FontAASettings::defaults() { setExcludeTo(15); setExcludeFrom(8); - setAntiAliasing(1); - m_antiAliasingOriginal = m_antiAliasing; + setAntiAliasing(true); + m_originalState.antiAliasing = m_state.antiAliasing; + m_state.antiAliasingHasLocalConfig = false; setDpi(0); - setSubPixelCurrentIndex(KXftConfig::SubPixel::NotSet); - setHintingCurrentIndex(KXftConfig::Hint::NotSet); + setSubPixel(KXftConfig::SubPixel::None); + m_state.subPixelHasLocalConfig = false; + setHinting(KXftConfig::Hint::Medium); + m_state.hintingHasLocalConfig = false; } #endif void FontAASettings::setExclude(bool exclude) { - if (exclude == m_exclude) { + if (exclude == m_state.exclude) { return; } - m_exclude = exclude; + m_state.exclude = exclude; emit excludeChanged(); } bool FontAASettings::exclude() const { - return m_exclude; + return m_state.exclude; } void FontAASettings::setExcludeTo(const int &excludeTo) { - if (m_excludeTo == excludeTo) { + if (m_state.excludeTo == excludeTo) { return; } - m_excludeTo = excludeTo; + m_state.excludeTo = excludeTo; emit excludeToChanged(); } int FontAASettings::excludeTo() const { - return m_excludeTo; + return m_state.excludeTo; } void FontAASettings::setExcludeFrom(const int &excludeTo) { - if (m_excludeFrom == excludeTo) { + if (m_state.excludeFrom == excludeTo) { return; } - m_excludeFrom = excludeTo; + m_state.excludeFrom = excludeTo; emit excludeToChanged(); } int FontAASettings::excludeFrom() const { - return m_excludeFrom; + return m_state.excludeFrom; } -void FontAASettings::setAntiAliasing(const int &antiAliasing) +void FontAASettings::setAntiAliasing(bool antiAliasing) { - if (m_antiAliasing == antiAliasing) { + if (m_state.antiAliasing == antiAliasing) { return; } - m_antiAliasing = antiAliasing; + m_state.antiAliasing = antiAliasing; emit aliasingChanged(); } -int FontAASettings::antiAliasing() const +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_antiAliasing; + 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_dpi == dpi) { + if (m_state.dpi == dpi) { return; } - m_dpi = dpi; + m_state.dpi = dpi; emit dpiChanged(); } int FontAASettings::dpi() const { - return m_dpi; + return m_state.dpi; } -void FontAASettings::setSubPixelCurrentIndex(int idx) +void FontAASettings::setSubPixel(int idx) { - if (m_subPixelCurrentIndex == idx) { + if (m_state.subPixel == idx) { return; } - m_subPixelCurrentIndex = idx; + m_state.subPixel = idx; emit subPixelCurrentIndexChanged(); } +void FontAASettings::setSubPixelCurrentIndex(int idx) +{ + setSubPixel(KXftConfig::SubPixel::None + idx); +} + int FontAASettings::subPixelCurrentIndex() { - return m_subPixelCurrentIndex; + return m_state.subPixel - KXftConfig::SubPixel::None; } -void FontAASettings::setHintingCurrentIndex(int idx) +void FontAASettings::setHinting(int idx) { - if (m_hintingCurrentIndex == idx) { + if (m_state.hinting == idx) { return; } - m_hintingCurrentIndex = idx; + m_state.hinting = idx; emit hintingCurrentIndexChanged(); } +void FontAASettings::setHintingCurrentIndex(int idx) +{ + setHinting(KXftConfig::Hint::None + idx); +} + int FontAASettings::hintingCurrentIndex() { - return m_hintingCurrentIndex; + return m_state.hinting - KXftConfig::Hint::None; } bool FontAASettings::needsSave() const { - return m_excludeTo != m_excludeToOriginal - || m_excludeFrom != m_excludeFromOriginal - || m_antiAliasing != m_antiAliasingOriginal - || m_dpi != m_dpiOriginal - || m_subPixelCurrentIndex != m_subPixelCurrentIndexOriginal - || m_hintingCurrentIndex != m_hintingCurrentIndexOriginal; + 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::AASetting)m_fontAASettings->antiAliasing(); + 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/fonts/fonts.h b/kcms/fonts/fonts.h index d760e5244..d660eae11 100644 --- a/kcms/fonts/fonts.h +++ b/kcms/fonts/fonts.h @@ -1,196 +1,208 @@ /* 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. */ #ifndef FONTS_H #define FONTS_H #include #include #include #include #include "kxftconfig.h" class FontAASettings : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel *subPixelOptionsModel READ subPixelOptionsModel CONSTANT) Q_PROPERTY(int subPixelCurrentIndex READ subPixelCurrentIndex WRITE setSubPixelCurrentIndex NOTIFY subPixelCurrentIndexChanged) Q_PROPERTY(QAbstractItemModel *hintingOptionsModel READ hintingOptionsModel CONSTANT) Q_PROPERTY(int hintingCurrentIndex READ hintingCurrentIndex WRITE setHintingCurrentIndex NOTIFY hintingCurrentIndexChanged) Q_PROPERTY(bool exclude READ exclude WRITE setExclude NOTIFY excludeChanged) Q_PROPERTY(int excludeTo READ excludeTo WRITE setExcludeTo NOTIFY excludeToChanged) Q_PROPERTY(int excludeFrom READ excludeFrom WRITE setExcludeFrom NOTIFY excludeFromChanged) - Q_PROPERTY(int antiAliasing READ antiAliasing WRITE setAntiAliasing NOTIFY aliasingChanged) + Q_PROPERTY(bool antiAliasing READ antiAliasing WRITE setAntiAliasing NOTIFY aliasingChanged) Q_PROPERTY(int dpi READ dpi WRITE setDpi NOTIFY dpiChanged) + struct State + { + bool exclude; + int excludeFrom; + int excludeTo; + int antiAliasing; + bool antiAliasingHasLocalConfig; + bool subPixelHasLocalConfig; + bool hintingHasLocalConfig; + int dpi; + int subPixel; + int hinting; + + bool operator==(const State& other) const; + bool operator!=(const State& other) const; + }; + public: enum AASetting { AAEnabled, AASystem, AADisabled }; #if defined(HAVE_FONTCONFIG) && HAVE_X11 FontAASettings(QObject *parent); bool save(KXftConfig::AntiAliasing::State aaState); void load(); void defaults(); void setAntiAliasingState(KXftConfig::AntiAliasing::State aaState); QAbstractItemModel* subPixelOptionsModel() { return m_subPixelOptionsModel; } QAbstractItemModel* hintingOptionsModel() { return m_hintingOptionsModel; } void setExclude(bool exclude); bool exclude() const; void setExcludeTo(const int &excludeTo); int excludeTo() const; void setExcludeFrom(const int &excludeTo); int excludeFrom() const; - void setAntiAliasing(const int& antiAliasing); - int antiAliasing() const; + void setAntiAliasing(bool antiAliasing); + bool antiAliasing() const; + + bool antiAliasingNeedsSave() const; + bool subPixelNeedsSave() const; + bool hintingNeedsSave() const; void setDpi(const int &dpi); int dpi() const; int subPixelCurrentIndex(); void setSubPixelCurrentIndex(int idx); + void setSubPixel(int idx); int hintingCurrentIndex(); void setHintingCurrentIndex(int idx); + void setHinting(int idx); bool needsSave() const; #endif Q_SIGNALS: void excludeChanged(); void excludeToChanged(); void excludeFromChanged(); void antiAliasingChanged(); void aliasingChanged(); void dpiChanged(); void subPixelCurrentIndexChanged(); void hintingCurrentIndexChanged(); #if defined(HAVE_FONTCONFIG) && HAVE_X11 private: - int m_excludeTo; - int m_excludeToOriginal; - int m_excludeFrom; - int m_excludeFromOriginal; - int m_antiAliasing; - int m_antiAliasingOriginal; - int m_dpi; - int m_dpiOriginal; - int m_subPixelCurrentIndex = 0; - int m_subPixelCurrentIndexOriginal; - int m_hintingCurrentIndex = 0; - int m_hintingCurrentIndexOriginal; + State m_state; + State m_originalState; QStandardItemModel *m_subPixelOptionsModel; QStandardItemModel *m_hintingOptionsModel; - bool m_exclude = false; #endif }; /** * The Desktop/fonts tab in kcontrol. */ class KFonts : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QFont generalFont READ generalFont WRITE setGeneralFont NOTIFY generalFontChanged) Q_PROPERTY(QFont fixedWidthFont READ fixedWidthFont WRITE setFixedWidthFont NOTIFY fixedWidthFontChanged) Q_PROPERTY(QFont smallFont READ smallFont WRITE setSmallFont NOTIFY smallFontChanged) Q_PROPERTY(QFont toolbarFont READ toolbarFont WRITE setToolbarFont NOTIFY toolbarFontChanged) Q_PROPERTY(QFont menuFont READ menuFont WRITE setMenuFont NOTIFY menuFontChanged) Q_PROPERTY(QFont windowTitleFont READ windowTitleFont WRITE setWindowTitleFont NOTIFY windowTitleFontChanged) Q_PROPERTY(QObject *fontAASettings READ fontAASettings CONSTANT) public: KFonts(QObject *parent, const QVariantList &); ~KFonts() override; void setGeneralFont(const QFont &font); QFont generalFont() const; void setFixedWidthFont(const QFont &font); QFont fixedWidthFont() const; void setSmallFont(const QFont &font); QFont smallFont() const; void setToolbarFont(const QFont &font); QFont toolbarFont() const; void setMenuFont(const QFont &font); QFont menuFont() const; void setWindowTitleFont(const QFont &font); QFont windowTitleFont() const; QObject* fontAASettings() { return m_fontAASettings; } public Q_SLOTS: void load() override; void save() override; void defaults() override; Q_INVOKABLE void adjustAllFonts(); Q_SIGNALS: void fontsHaveChanged(); void generalFontChanged(); void fixedWidthFontChanged(); void smallFontChanged(); void toolbarFontChanged(); void menuFontChanged(); void windowTitleFontChanged(); private: void updateNeedsSave(); QFont applyFontDiff(const QFont &fnt, const QFont &newFont, int fontDiffFlags); QFont m_defaultFont; QFont m_generalFont; QFont m_fixedWidthFont; QFont m_smallFont; QFont m_toolbarFont; QFont m_menuFont; QFont m_windowTitleFont; QFont m_defaultFontOriginal; QFont m_generalFontOriginal; QFont m_fixedWidthFontOriginal; QFont m_smallFontOriginal; QFont m_toolbarFontOriginal; QFont m_menuFontOriginal; QFont m_windowTitleFontOriginal; FontAASettings *m_fontAASettings; }; #endif diff --git a/kcms/fonts/kxftconfig.cpp b/kcms/fonts/kxftconfig.cpp index cd4423557..866fa036a 100644 --- a/kcms/fonts/kxftconfig.cpp +++ b/kcms/fonts/kxftconfig.cpp @@ -1,827 +1,941 @@ /* Copyright (c) 2002 Craig Drummond 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 "kxftconfig.h" #ifdef HAVE_FONTCONFIG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; static int point2Pixel(double point) { return (int)(((point * QX11Info::appDpiY()) / 72.0) + 0.5); } static int pixel2Point(double pixel) { return (int)(((pixel * 72.0) / (double)QX11Info::appDpiY()) + 0.5); } static bool equal(double d1, double d2) { return (fabs(d1 - d2) < 0.0001); } static QString dirSyntax(const QString &d) { if (!d.isNull()) { QString ds(d); ds.replace("//", "/"); int slashPos = ds.lastIndexOf('/'); if (slashPos != (((int)ds.length()) - 1)) { ds.append('/'); } return ds; } return d; } inline bool fExists(const QString &p) { return QFileInfo(p).isFile(); } inline bool dWritable(const QString &p) { QFileInfo info(p); return info.isDir() && info.isWritable(); } static QString getDir(const QString &f) { QString d(f); int slashPos = d.lastIndexOf('/'); if (-1 != slashPos) { d.remove(slashPos + 1, d.length()); } return dirSyntax(d); } static QDateTime getTimeStamp(const QString &item) { return QFileInfo(item).lastModified(); } -// -// Obtain location of config file to use. -QString getConfigFile() -{ - FcStrList *list = FcConfigGetConfigFiles(FcConfigGetCurrent()); - QStringList files; - FcChar8 *file; - QString home(dirSyntax(QDir::homePath())); - - while ((file = FcStrListNext(list))) { - QString f((const char *)file); - - if (fExists(f) && 0 == f.indexOf(home)) { - files.append(f); - } - } - FcStrListDone(list); - - // - // Go through list of files, looking for the preferred one... - if (!files.isEmpty()) { - QStringList::const_iterator it(files.begin()), - end(files.end()); - - for (; it != end; ++it) - if (-1 != (*it).indexOf(QRegExp("/\\.?fonts\\.conf$"))) { - return *it; - } - return files.front(); // Just return the 1st one... - } else { // Hmmm... no known files? - if (FcGetVersion() >= 21000) { - QString targetPath(KGlobal::dirs()->localxdgconfdir() + "fontconfig"); - QDir target(targetPath); - if (!target.exists()) { - target.mkpath(targetPath); - } - return targetPath + "/fonts.conf"; - } else { - return home + "/.fonts.conf"; - } - } -} - static QString getEntry(QDomElement element, const char *type, unsigned int numAttributes, ...) { if (numAttributes == element.attributes().length()) { va_list args; unsigned int arg; bool ok = true; va_start(args, numAttributes); for (arg = 0; arg < numAttributes && ok; ++arg) { const char *attr = va_arg(args, const char *); const char *val = va_arg(args, const char *); if (!attr || !val || val != element.attribute(attr)) { ok = false; } } va_end(args); if (ok) { QDomNode n = element.firstChild(); if (!n.isNull()) { QDomElement e = n.toElement(); if (!e.isNull() && type == e.tagName()) { return e.text(); } } } } return QString(); } static KXftConfig::SubPixel::Type strToType(const char *str) { if (0 == strcmp(str, "rgb")) { return KXftConfig::SubPixel::Rgb; } else if (0 == strcmp(str, "bgr")) { return KXftConfig::SubPixel::Bgr; } else if (0 == strcmp(str, "vrgb")) { return KXftConfig::SubPixel::Vrgb; } else if (0 == strcmp(str, "vbgr")) { return KXftConfig::SubPixel::Vbgr; } else if (0 == strcmp(str, "none")) { return KXftConfig::SubPixel::None; } else { return KXftConfig::SubPixel::NotSet; } } static KXftConfig::Hint::Style strToStyle(const char *str) { if (0 == strcmp(str, "hintslight")) { return KXftConfig::Hint::Slight; } else if (0 == strcmp(str, "hintmedium")) { return KXftConfig::Hint::Medium; } else if (0 == strcmp(str, "hintfull")) { return KXftConfig::Hint::Full; } else { return KXftConfig::Hint::None; } } KXftConfig::KXftConfig() : m_doc("fontconfig") , m_file(getConfigFile()) { qDebug() << "Using fontconfig file:" << m_file; reset(); } KXftConfig::~KXftConfig() { } +// +// Obtain location of config file to use. +QString KXftConfig::getConfigFile() +{ + FcStrList *list = FcConfigGetConfigFiles(FcConfigGetCurrent()); + QStringList localFiles; + FcChar8 *file; + QString home(dirSyntax(QDir::homePath())); + + m_globalFiles.clear(); + + while ((file = FcStrListNext(list))) { + + QString f((const char *)file); + + if (fExists(f) && 0 == f.indexOf(home)) { + localFiles.append(f); + } else { + m_globalFiles.append(f); + } + } + FcStrListDone(list); + + // + // Go through list of localFiles, looking for the preferred one... + if (localFiles.count()) { + QStringList::const_iterator it(localFiles.begin()), + end(localFiles.end()); + + for (; it != end; ++it) + if (-1 != (*it).indexOf(QRegExp("/\\.?fonts\\.conf$"))) { + return *it; + } + return localFiles.front(); // Just return the 1st one... + } else { // Hmmm... no known localFiles? + if (FcGetVersion() >= 21000) { + QString targetPath(KGlobal::dirs()->localxdgconfdir() + "fontconfig"); + QDir target(targetPath); + if (!target.exists()) { + target.mkpath(targetPath); + } + return targetPath + "/fonts.conf"; + } else { + return home + "/.fonts.conf"; + } + } +} + bool KXftConfig::reset() { bool ok = false; m_madeChanges = false; m_hint.reset(); m_hinting.reset(); m_excludeRange.reset(); m_excludePixelRange.reset(); m_subPixel.reset(); m_antiAliasing.reset(); + m_antiAliasingHasLocalConfig = false; + m_subPixelHasLocalConfig = false; + m_hintHasLocalConfig = false; + + QStringList::const_iterator it(m_globalFiles.begin()), + end(m_globalFiles.end()); + for (; it != end; ++it) { + ok |= parseConfigFile(*it); + } - QFile f(m_file); + AntiAliasing globalAntialiasing; + globalAntialiasing.state = m_antiAliasing.state; + SubPixel globalSubPixel; + globalSubPixel.type = m_subPixel.type; + Hint globalHint; + globalHint.style = m_hint.style; + Exclude globalExcludeRange; + globalExcludeRange.from = m_excludeRange.from; + globalExcludeRange.to = m_excludePixelRange.to; + Exclude globalExcludePixelRange; + globalExcludePixelRange.from = m_excludePixelRange.from; + globalExcludePixelRange.to = m_excludePixelRange.to; + Hinting globalHinting; + globalHinting.set = m_hinting.set; - if (f.open(QIODevice::ReadOnly)) { - m_time = getTimeStamp(m_file); - ok = true; - m_doc.clear(); + m_antiAliasing.reset(); + m_subPixel.reset(); + m_hint.reset(); + m_hinting.reset(); + m_excludeRange.reset(); + m_excludePixelRange.reset(); - if (m_doc.setContent(&f)) { - readContents(); - } - f.close(); + ok |= parseConfigFile(m_file); + + if (m_antiAliasing.node.isNull()) { + m_antiAliasing = globalAntialiasing; } else { - ok = !fExists(m_file) && dWritable(getDir(m_file)); + m_antiAliasingHasLocalConfig = true; } - if (m_doc.documentElement().isNull()) { - m_doc.appendChild(m_doc.createElement("fontconfig")); + if (m_subPixel.node.isNull()) { + m_subPixel = globalSubPixel; + } else { + m_subPixelHasLocalConfig = true; } - if (ok) { - // - // Check exclude range values - i.e. size and pixel size... - // If "size" range is set, ensure "pixelsize" matches... - if (!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) { - double pFrom = (double)point2Pixel(m_excludeRange.from), - pTo = (double)point2Pixel(m_excludeRange.to); + if (m_hint.node.isNull()) { + m_hint = globalHint; + } else { + m_hintHasLocalConfig = true; + } - if (!equal(pFrom, m_excludePixelRange.from) || !equal(pTo, m_excludePixelRange.to)) { - m_excludePixelRange.from = pFrom; - m_excludePixelRange.to = pTo; - m_madeChanges = true; - } - } else if (!equal(0, m_excludePixelRange.from) || !equal(0, m_excludePixelRange.to)) { - // "pixelsize" set, but not "size" !!! - m_excludeRange.from = (int)pixel2Point(m_excludePixelRange.from); - m_excludeRange.to = (int)pixel2Point(m_excludePixelRange.to); - m_madeChanges = true; - } + if (m_hinting.node.isNull()) { + m_hinting = globalHinting; + } + if (m_excludeRange.node.isNull()) { + m_excludeRange = globalExcludeRange; + } + if (m_excludePixelRange.node.isNull()) { + m_excludePixelRange = globalExcludePixelRange; } return ok; } bool KXftConfig::apply() { bool ok = true; if (m_madeChanges) { // // Check if file has been written since we last read it. If it has, then re-read and add any // of our changes... if (fExists(m_file) && getTimeStamp(m_file) != m_time) { KXftConfig newConfig; newConfig.setExcludeRange(m_excludeRange.from, m_excludeRange.to); newConfig.setSubPixelType(m_subPixel.type); newConfig.setHintStyle(m_hint.style); newConfig.setAntiAliasing(m_antiAliasing.state); ok = newConfig.changed() ? newConfig.apply() : true; if (ok) { reset(); } else { m_time = getTimeStamp(m_file); } } else { // Ensure these are always equal... m_excludePixelRange.from = (int)point2Pixel(m_excludeRange.from); m_excludePixelRange.to = (int)point2Pixel(m_excludeRange.to); FcAtomic *atomic = FcAtomicCreate((const unsigned char *)(QFile::encodeName(m_file).data())); ok = false; if (atomic) { if (FcAtomicLock(atomic)) { FILE *f = fopen((char *)FcAtomicNewFile(atomic), "w"); if (f) { applySubPixelType(); applyHintStyle(); applyAntiAliasing(); applyExcludeRange(false); applyExcludeRange(true); // // Check document syntax... static const char qtXmlHeader[] = ""; static const char xmlHeader[] = ""; static const char qtDocTypeLine[] = ""; static const char docTypeLine[] = ""; QString str(m_doc.toString()); int idx; if (0 != str.indexOf("= 0 || to >= 0) && foundFalse) { m_excludeRange.from = from < to ? from : to; m_excludeRange.to = from < to ? to : from; m_excludeRange.node = n; } else if ((pixelFrom >= 0 || pixelTo >= 0) && foundFalse) { m_excludePixelRange.from = pixelFrom < pixelTo ? pixelFrom : pixelTo; m_excludePixelRange.to = pixelFrom < pixelTo ? pixelTo : pixelFrom; m_excludePixelRange.node = n; } } break; default: break; } } } n = n.nextSibling(); } } void KXftConfig::applySubPixelType() { if (SubPixel::NotSet == m_subPixel.type) { if (!m_subPixel.node.isNull()) { m_doc.documentElement().removeChild(m_subPixel.node); m_subPixel.node.clear(); } } else { QDomElement matchNode = m_doc.createElement("match"); QDomElement typeNode = m_doc.createElement("const"); QDomElement editNode = m_doc.createElement("edit"); QDomText typeText = m_doc.createTextNode(toStr(m_subPixel.type)); matchNode.setAttribute("target", "font"); editNode.setAttribute("mode", "assign"); editNode.setAttribute("name", "rgba"); editNode.appendChild(typeNode); typeNode.appendChild(typeText); matchNode.appendChild(editNode); if (m_subPixel.node.isNull()) { m_doc.documentElement().appendChild(matchNode); } else { m_doc.documentElement().replaceChild(matchNode, m_subPixel.node); } m_subPixel.node = matchNode; } } void KXftConfig::applyHintStyle() { applyHinting(); if (Hint::NotSet == m_hint.style) { if (!m_hint.node.isNull()) { m_doc.documentElement().removeChild(m_hint.node); m_hint.node.clear(); } if (!m_hinting.node.isNull()) { m_doc.documentElement().removeChild(m_hinting.node); m_hinting.node.clear(); } } else { QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("const"), editNode = m_doc.createElement("edit"); QDomText typeText = m_doc.createTextNode(toStr(m_hint.style)); matchNode.setAttribute("target", "font"); editNode.setAttribute("mode", "assign"); editNode.setAttribute("name", "hintstyle"); editNode.appendChild(typeNode); typeNode.appendChild(typeText); matchNode.appendChild(editNode); if (m_hint.node.isNull()) { m_doc.documentElement().appendChild(matchNode); } else { m_doc.documentElement().replaceChild(matchNode, m_hint.node); } m_hint.node = matchNode; } } void KXftConfig::applyHinting() { QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("bool"), editNode = m_doc.createElement("edit"); QDomText typeText = m_doc.createTextNode(m_hinting.set ? "true" : "false"); matchNode.setAttribute("target", "font"); editNode.setAttribute("mode", "assign"); editNode.setAttribute("name", "hinting"); editNode.appendChild(typeNode); typeNode.appendChild(typeText); matchNode.appendChild(editNode); if (m_hinting.node.isNull()) { m_doc.documentElement().appendChild(matchNode); } else { m_doc.documentElement().replaceChild(matchNode, m_hinting.node); } m_hinting.node = matchNode; } void KXftConfig::applyExcludeRange(bool pixel) { Exclude &range = pixel ? m_excludePixelRange : m_excludeRange; if (equal(range.from, 0) && equal(range.to, 0)) { if (!range.node.isNull()) { m_doc.documentElement().removeChild(range.node); range.node.clear(); } } else { QString fromString, toString; fromString.setNum(range.from); toString.setNum(range.to); QDomElement matchNode = m_doc.createElement("match"), fromTestNode = m_doc.createElement("test"), fromNode = m_doc.createElement("double"), toTestNode = m_doc.createElement("test"), toNode = m_doc.createElement("double"), editNode = m_doc.createElement("edit"), boolNode = m_doc.createElement("bool"); QDomText fromText = m_doc.createTextNode(fromString), toText = m_doc.createTextNode(toString), boolText = m_doc.createTextNode("false"); matchNode.setAttribute("target", "font"); // CPD: Is target "font" or "pattern" ???? fromTestNode.setAttribute("qual", "any"); fromTestNode.setAttribute("name", pixel ? "pixelsize" : "size"); fromTestNode.setAttribute("compare", "more_eq"); fromTestNode.appendChild(fromNode); fromNode.appendChild(fromText); toTestNode.setAttribute("qual", "any"); toTestNode.setAttribute("name", pixel ? "pixelsize" : "size"); toTestNode.setAttribute("compare", "less_eq"); toTestNode.appendChild(toNode); toNode.appendChild(toText); editNode.setAttribute("mode", "assign"); editNode.setAttribute("name", "antialias"); editNode.appendChild(boolNode); boolNode.appendChild(boolText); matchNode.appendChild(fromTestNode); matchNode.appendChild(toTestNode); matchNode.appendChild(editNode); if (!m_antiAliasing.node.isNull()) { m_doc.documentElement().removeChild(range.node); } m_doc.documentElement().appendChild(matchNode); range.node = matchNode; } } +bool KXftConfig::antiAliasingHasLocalConfig() const +{ + return m_antiAliasingHasLocalConfig; +} + KXftConfig::AntiAliasing::State KXftConfig::getAntiAliasing() const { return m_antiAliasing.state; } void KXftConfig::setAntiAliasing(AntiAliasing::State state) { if (state != m_antiAliasing.state) { m_antiAliasing.state = state; m_madeChanges = true; } } void KXftConfig::applyAntiAliasing() { if (AntiAliasing::NotSet == m_antiAliasing.state) { if (!m_antiAliasing.node.isNull()) { m_doc.documentElement().removeChild(m_antiAliasing.node); m_antiAliasing.node.clear(); } } else { QDomElement matchNode = m_doc.createElement("match"); QDomElement typeNode = m_doc.createElement("bool"); QDomElement editNode = m_doc.createElement("edit"); QDomText typeText = m_doc.createTextNode(m_antiAliasing.state == AntiAliasing::Enabled ? "true" : "false"); matchNode.setAttribute("target", "font"); editNode.setAttribute("mode", "assign"); editNode.setAttribute("name", "antialias"); editNode.appendChild(typeNode); typeNode.appendChild(typeText); matchNode.appendChild(editNode); if (!m_antiAliasing.node.isNull()) { m_doc.documentElement().removeChild(m_antiAliasing.node); } m_doc.documentElement().appendChild(matchNode); m_antiAliasing.node = matchNode; } } // KXftConfig only parses one config file, user's .fonts.conf usually. // If that one doesn't exist, then KXftConfig doesn't know if antialiasing // is enabled or not. So try to find out the default value from the default font. // Maybe there's a better way *shrug*. bool KXftConfig::aliasingEnabled() { FcPattern *pattern = FcPatternCreate(); FcConfigSubstitute(nullptr, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result; FcPattern *f = FcFontMatch(nullptr, pattern, &result); FcBool antialiased = FcTrue; FcPatternGetBool(f, FC_ANTIALIAS, 0, &antialiased); FcPatternDestroy(f); FcPatternDestroy(pattern); return antialiased == FcTrue; } #endif diff --git a/kcms/fonts/kxftconfig.h b/kcms/fonts/kxftconfig.h index cc2846ce3..5b42ac1d6 100644 --- a/kcms/fonts/kxftconfig.h +++ b/kcms/fonts/kxftconfig.h @@ -1,194 +1,205 @@ #ifndef __KXFTCONFIG_H__ #define __KXFTCONFIG_H__ /* Copyright (c) 2002 Craig Drummond 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 #ifdef HAVE_FONTCONFIG #include #include +#include class KXftConfig { public: struct Item { Item(QDomNode &n) : node(n), toBeRemoved(false) {} Item() : toBeRemoved(false) {} virtual void reset() { node.clear(); toBeRemoved = false; } bool added() { return node.isNull(); } QDomNode node; virtual ~Item() {} bool toBeRemoved; }; struct SubPixel : public Item { enum Type { NotSet, None, Rgb, Bgr, Vrgb, Vbgr }; SubPixel(Type t, QDomNode &n) : Item(n), type(t) {} SubPixel(Type t = NotSet) : type(t) {} void reset() override { Item::reset(); type = NotSet; } Type type; }; struct Exclude : public Item { Exclude(double f, double t, QDomNode &n) : Item(n), from(f), to(t) {} Exclude(double f = 0, double t = 0) : from(f), to(t) {} void reset() override { Item::reset(); from = to = 0; } double from, to; }; struct Hint : public Item { enum Style { NotSet, None, Slight, Medium, Full }; Hint(Style s, QDomNode &n) : Item(n), style(s) {} Hint(Style s = NotSet) : style(s) {} void reset() override { Item::reset(); style = NotSet; } Style style; }; struct Hinting : public Item { Hinting(bool s, QDomNode &n) : Item(n), set(s) {} Hinting(bool s = true) : set(s) {} void reset() override { Item::reset(); set = true; } bool set; }; struct AntiAliasing : public Item { enum State { NotSet, Enabled, Disabled }; AntiAliasing(State s, QDomNode &n) : Item(n), state(s) {} AntiAliasing(State s = NotSet) : state(s) {} void reset() override { Item::reset(); state = NotSet; } enum State state; }; public: explicit KXftConfig(); virtual ~KXftConfig(); bool reset(); bool apply(); bool getSubPixelType(SubPixel::Type &type); void setSubPixelType(SubPixel::Type type); // SubPixel::None => turn off sub-pixel rendering bool getExcludeRange(double &from, double &to); void setExcludeRange(double from, double to); // from:0, to:0 => turn off exclude range bool getHintStyle(Hint::Style &style); void setHintStyle(Hint::Style style); void setAntiAliasing(AntiAliasing::State state); AntiAliasing::State getAntiAliasing() const; + bool antiAliasingHasLocalConfig() const; + bool subPixelTypeHasLocalConfig() const; + bool hintStyleHasLocalConfig() const; bool changed() { return m_madeChanges; } static QString description(SubPixel::Type t); static const char *toStr(SubPixel::Type t); static QString description(Hint::Style s); static const char *toStr(Hint::Style s); bool aliasingEnabled(); private: + bool parseConfigFile(const QString& filename); void readContents(); void applySubPixelType(); void applyHintStyle(); void applyAntiAliasing(); void setHinting(bool set); void applyHinting(); void applyExcludeRange(bool pixel); + QString getConfigFile(); private: + QStringList m_globalFiles; + SubPixel m_subPixel; Exclude m_excludeRange, m_excludePixelRange; Hint m_hint; Hinting m_hinting; AntiAliasing m_antiAliasing; + bool m_antiAliasingHasLocalConfig; + bool m_subPixelHasLocalConfig; + bool m_hintHasLocalConfig; QDomDocument m_doc; QString m_file; bool m_madeChanges; QDateTime m_time; }; #endif #endif diff --git a/kcms/fonts/package/contents/ui/main.qml b/kcms/fonts/package/contents/ui/main.qml index 408baf5ef..a0c09174b 100644 --- a/kcms/fonts/package/contents/ui/main.qml +++ b/kcms/fonts/package/contents/ui/main.qml @@ -1,252 +1,246 @@ /* Copyright (c) 2015 Antonis Tsiapaliokas Copyright (c) 2017 Marco Martin 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. */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.0 as QtControls import QtQuick.Dialogs 1.2 as QtDialogs import org.kde.kirigami 2.3 as Kirigami import org.kde.kcm 1.1 as KCM KCM.SimpleKCM { id: root KCM.ConfigModule.quickHelp: i18n("This module lets you configure the system fonts.") Kirigami.FormLayout { id: formLayout - readonly property int maxImplicitWidth: Math.max(adjustAllFontsButton.implicitWidth, Math.max(antiAliasingComboBox.implicitWidth, Math.max(excludeField.implicitWidth, Math.max(subPixelCombo.implicitWidth, hintingCombo.implicitWidth)))) + readonly property int maxImplicitWidth: Math.max(adjustAllFontsButton.implicitWidth, excludeField.implicitWidth, subPixelCombo.implicitWidth, hintingCombo.implicitWidth) QtControls.Button { id: adjustAllFontsButton Layout.preferredWidth: formLayout.maxImplicitWidth icon.name: "font-select-symbolic" text: i18n("&Adjust All Fonts...") onClicked: kcm.adjustAllFonts(); } FontWidget { id: generalFontWidget label: i18n("General:") category: "generalFont" font: kcm.generalFont } FontWidget { label: i18n("Fixed width:") category: "fixedWidthFont" font: kcm.fixedWidthFont } FontWidget { label: i18n("Small:") category: "smallFont" font: kcm.smallFont } FontWidget { label: i18n("Toolbar:") category: "toolbarFont" font: kcm.toolbarFont } FontWidget { label: i18n("Menu:") category: "menuFont" font: kcm.menuFont } FontWidget { label: i18n("Window title:") category: "windowTitleFont" font: kcm.windowTitleFont } Kirigami.Separator { Kirigami.FormData.isSection: true } - QtControls.ComboBox { - id: antiAliasingComboBox - Layout.preferredWidth: formLayout.maxImplicitWidth - Kirigami.FormData.label: i18nc("Used as a noun, and precedes a combobox full of options", "Anti-aliasing:") - - model: [i18n("Enabled"), i18n("Vendor default"), i18n("Disabled")] - - currentIndex: kcm.fontAASettings.antiAliasing - onCurrentIndexChanged: kcm.fontAASettings.antiAliasing = antiAliasingComboBox.currentIndex + QtControls.CheckBox { + id: antiAliasingCheckBox + checked: kcm.fontAASettings.antiAliasing + onCheckedChanged: kcm.fontAASettings.antiAliasing = checked + Kirigami.FormData.label: i18n("Anti-Aliasing:") + text: i18n("Enable") + Layout.fillWidth: true } QtControls.CheckBox { id: excludeCheckBox checked: kcm.fontAASettings.exclude onCheckedChanged: kcm.fontAASettings.exclude = checked; text: i18n("Exclude range from anti-aliasing") Layout.fillWidth: true - enabled: antiAliasingComboBox.currentIndex == 0 + enabled: antiAliasingCheckBox.checked } RowLayout { id: excludeField - enabled: antiAliasingComboBox.currentIndex == 0 - - Item { - width: units.largeSpacing - } - + Layout.preferredWidth: formLayout.maxImplicitWidth + enabled: antiAliasingCheckBox.checked QtControls.SpinBox { id: excludeFromSpinBox stepSize: 1 onValueChanged: kcm.fontAASettings.excludeFrom = value textFromValue: function(value, locale) { return i18n("%1 pt", value)} enabled: excludeCheckBox.checked } QtControls.Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter text: i18n("to") enabled: excludeCheckBox.checked } QtControls.SpinBox { id: excludeToSpinBox stepSize: 1 onValueChanged: kcm.fontAASettings.excludeTo = value textFromValue: function(value, locale) { return i18n("%1 pt", value)} enabled: excludeCheckBox.checked } Connections { target: kcm.fontAASettings onExcludeFromChanged: excludeFromSpinBox.value = kcm.fontAASettings.excludeFrom; onExcludeToChanged: excludeToSpinBox.value = kcm.fontAASettings.excludeTo; } } QtControls.ComboBox { id: subPixelCombo Layout.preferredWidth: formLayout.maxImplicitWidth Kirigami.FormData.label: i18nc("Used as a noun, and precedes a combobox full of options", "Sub-pixel rendering:") currentIndex: kcm.fontAASettings.subPixelCurrentIndex onCurrentIndexChanged: kcm.fontAASettings.subPixelCurrentIndex = currentIndex; model: kcm.fontAASettings.subPixelOptionsModel textRole: "display" - enabled: antiAliasingComboBox.currentIndex == 0 + enabled: antiAliasingCheckBox.checked popup.height: popup.implicitHeight delegate: QtControls.ItemDelegate { id: subPixelDelegate onWidthChanged: { subPixelCombo.popup.width = Math.max(subPixelCombo.popup.width, width) } contentItem: ColumnLayout { id: subPixelLayout Kirigami.Heading { id: subPixelComboText text: model.display level: 5 } Image { id: subPixelComboImage source: "image://preview/" + model.index + "_" + kcm.fontAASettings.hintingCurrentIndex + ".png" // Setting sourceSize here is necessary as a workaround for QTBUG-38127 // // With this bug, images requested from a QQuickImageProvider have an incorrect scale with devicePixelRatio != 1 when sourceSize is not set. // // TODO: Check if QTBUG-38127 is fixed and remove the next two lines. sourceSize.width: 1 sourceSize.height: 1 } } } } QtControls.ComboBox { id: hintingCombo Layout.preferredWidth: formLayout.maxImplicitWidth Kirigami.FormData.label: i18nc("Used as a noun, and precedes a combobox full of options", "Hinting:") currentIndex: kcm.fontAASettings.hintingCurrentIndex onCurrentTextChanged: kcm.fontAASettings.hintingCurrentIndex = currentIndex; model: kcm.fontAASettings.hintingOptionsModel textRole: "display" - enabled: antiAliasingComboBox.currentIndex == 0 + enabled: antiAliasingCheckBox.checked popup.height: popup.implicitHeight delegate: QtControls.ItemDelegate { id: hintingDelegate onWidthChanged: { hintingCombo.popup.width = Math.max(hintingCombo.popup.width, width) } contentItem: ColumnLayout { id: hintingLayout Kirigami.Heading { id: hintingComboText text: model.display level: 5 } Image { id: hintingComboImage source: "image://preview/" + kcm.fontAASettings.subPixelCurrentIndex + "_" + model.index + ".png" // Setting sourceSize here is necessary as a workaround for QTBUG-38127 // // With this bug, images requested from a QQuickImageProvider have an incorrect scale with devicePixelRatio != 1 when sourceSize is not set. // // TODO: Check if QTBUG-38127 is fixed and remove the next two lines. sourceSize.width: 1 sourceSize.height: 1 } } } } RowLayout { QtControls.CheckBox { id: dpiCheckBox checked: kcm.fontAASettings.dpi !== 0 text: i18n("Force font DPI:") } QtControls.SpinBox { id: dpiSpinBox stepSize: 24 editable: true enabled: dpiCheckBox.checked value: enabled ? kcm.fontAASettings.dpi : 96 Binding { target: kcm property: "fontAASettings.dpi" value: dpiSpinBox.enabled ? dpiSpinBox.value : 0 } to: 1000 from: 1 } } QtDialogs.FontDialog { id: fontDialog title: i18n("Select Font") modality: Qt.WindowModal property string currentCategory property bool adjustAllFonts: false onAccepted: { if (adjustAllFonts) { kcm.adjustAllFonts(font); } else { kcm[currentCategory] = font; } } } } } diff --git a/kcms/fonts/previewimageprovider.cpp b/kcms/fonts/previewimageprovider.cpp index 78eb5cb17..a769e7130 100644 --- a/kcms/fonts/previewimageprovider.cpp +++ b/kcms/fonts/previewimageprovider.cpp @@ -1,133 +1,136 @@ /* Copyright (c) 2018 Julian Wolff 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 #include #include #include #include "kxftconfig.h" #include "previewimageprovider.h" #include "previewrenderengine.h" QImage combineImages(const QList& images, const QColor& bgnd, int spacing=0) { int width = 0; int height = 0; QImage::Format format = QImage::Format_Invalid; int devicePixelRatio = 1; for(const auto& image : images) { if(width < image.width()) { width = image.width(); } height += image.height() + spacing; format = image.format(); devicePixelRatio = image.devicePixelRatio(); } height -= spacing; // To correctly align the image pixels on a high dpi display, // the image dimensions need to be a multiple of devicePixelRatio width = (width + devicePixelRatio - 1) / devicePixelRatio * devicePixelRatio; height = (height + devicePixelRatio - 1) / devicePixelRatio * devicePixelRatio; QImage combinedImage(width, height, format); combinedImage.setDevicePixelRatio(devicePixelRatio); combinedImage.fill(bgnd); int offset = 0; QPainter p(&combinedImage); for(const auto& image : images) { p.drawImage(0, offset, image); offset += (image.height() + spacing) / devicePixelRatio; } return combinedImage; } PreviewImageProvider::PreviewImageProvider(const QFont& font) : QQuickImageProvider(QQuickImageProvider::Image) , m_font(font) { } QImage PreviewImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { if (!KWindowSystem::isPlatformX11()) { return QImage(); } int subPixelIndex = 0; int hintingIndex = 0; const auto idpart = id.splitRef(QLatin1Char('.'))[0]; const auto sections = idpart.split(QLatin1Char('_')); if (sections.size() >= 2) { - subPixelIndex = sections[0].toInt(); - hintingIndex = sections[1].toInt(); + subPixelIndex = sections[0].toInt() + KXftConfig::SubPixel::None; + hintingIndex = sections[1].toInt() + KXftConfig::Hint::None; + } else { + return QImage(); } KXftConfig xft; - + KXftConfig::AntiAliasing::State oldAntialiasing = xft.getAntiAliasing(); - double oldStart,oldEnd; + double oldStart = 0; + double oldEnd = 0; xft.getExcludeRange(oldStart, oldEnd); - KXftConfig::SubPixel::Type oldSubPixelType; + KXftConfig::SubPixel::Type oldSubPixelType = KXftConfig::SubPixel::NotSet; xft.getSubPixelType(oldSubPixelType); - KXftConfig::Hint::Style oldHintStyle; + KXftConfig::Hint::Style oldHintStyle = KXftConfig::Hint::NotSet; xft.getHintStyle(oldHintStyle); xft.setAntiAliasing(KXftConfig::AntiAliasing::Enabled); xft.setExcludeRange(0, 0); KXftConfig::SubPixel::Type subPixelType = (KXftConfig::SubPixel::Type)subPixelIndex; xft.setSubPixelType(subPixelType); KXftConfig::Hint::Style hintStyle = (KXftConfig::Hint::Style)hintingIndex; xft.setHintStyle(hintStyle); xft.apply(); QColor text(QApplication::palette().color(QPalette::Text)); QColor bgnd(QApplication::palette().color(QPalette::Window)); PreviewRenderEngine eng(true); QList lines; lines << eng.drawAutoSize(m_font, text, bgnd, eng.getDefaultPreviewString()); QImage img = combineImages(lines, bgnd, lines[0].height()*.25); xft.setAntiAliasing(oldAntialiasing); xft.setExcludeRange(oldStart, oldEnd); xft.setSubPixelType(oldSubPixelType); xft.setHintStyle(oldHintStyle); xft.apply(); *size = img.size(); return img; }