OSDN Git Service

Version 0.5
[fontmanager/fontmanager.git] / fontconfigmanager.cpp
index d03ba9c..dce59b3 100644 (file)
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2011 Takumi Asaki
+** Copyright (C) 2012 Takumi Asaki
 ** All rights reserved.
 ** Contact: Takumi Asaki (takumi.asaki@gmail.com)
 **
 
 #include "fontconfigmanager.h"
 
+#include "fontconfigdefs.h"
 #include "installedfontinfo.h"
 #include "fontsconfigproperties.h"
 
+#include "fontsconf.h"
+
 #include <QProcess>
 #include <QBuffer>
 #include <QFile>
 #include <QXmlStreamReader>
 #include <QVariant>
+#include <QFontInfo>
+#include <QFontMetrics>
+#include <QFontDatabase>
+#include <QUrl>
+#include <QTextStream>
+
+#undef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
 
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
 #include <QDebug>
+#endif
 
 #define FCBIN_PATH "/usr/bin/"
 #define FCCACHE_COMMAND "fc-cache"
 #define FCLIST_COMMAND "fc-list"
 #define FCLIST_OPTION "-v"
 
-enum FCState {
-    Initialized, Ready, WaitFamilyForAlias, WaitPreferForAlias, ReadingPrefer,
-    WaitTestForMatch, ReadingTest, WaitEditForMatch, WaitBoolForEdit, WaitEndMatch,
-    InvalidConfig,
-    UnknownState
-};
-
-static const char *configKeys[] = { "embeddedbitmap:true", "embeddedbitmap:false", "hinting:true", "hinting:false", 0 };
-
 FontConfigManager::FontConfigManager(QObject *parent) :
-    QObject(parent), mLang("en"), mHasUnknownConfig(false), mFontsConfModified(false)
+    QObject(parent), mLang("en"), mLocalFontsConf(0)
 {
-    clearFontProperties();
+    connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(localFontsConfExistsChanged()));
+    //    connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(fontsConfUpdated()));
+
+    mLocalFontsConf = new FontsConf(this);
+    mSystemLocalConf = new FontsConf(this);
+
+    QStringList cjkConflicts;
+    cjkConflicts << "ja" << "ko" << "zh-cn" << "zh-hk" << "zh-mo" << "zh-sg" << "zh-tw";
+    mConflictFonts << cjkConflicts;
+
+    mSystemLocalConf->load("/etc/fonts/local.conf");
+
 }
 
 QString FontConfigManager::currentLanguage() const
@@ -103,29 +117,28 @@ void FontConfigManager::setLocalFontsConfPath(const QString &path)
     }
 }
 
-bool FontConfigManager::hasUnknownConfig() const
+bool FontConfigManager::localFontsConfExists() const
 {
-    return mHasUnknownConfig;
+    return QFile::exists(mLocalFontsConfPath);
 }
 
-bool FontConfigManager::fontsConfModified() const
-{
-    return mFontsConfModified;
-}
-
-QStringList FontConfigManager::families() const
+bool FontConfigManager::hasUnknownConfig() const
 {
-    return mFamilies;
+    if (!mLocalFontsConf)
+        return false;
+    return mLocalFontsConf->hasUnknownConfig();
 }
 
-QStringList FontConfigManager::systemFamilies() const
+bool FontConfigManager::fontsConfModified() const
 {
-    return mSystemFamilies;
+    if (!mLocalFontsConf)
+        return false;
+    return mLocalFontsConf->modified();
 }
 
-QStringList FontConfigManager::localFamilies() const
+bool FontConfigManager::isEmptyLocalFontsConf() const
 {
-    return mLocalFamilies;
+    return !mLocalFontsConf || mLocalFontsConf->isEmpty();
 }
 
 QStringList FontConfigManager::fontCount(const QString &fontpath) const
@@ -138,12 +151,16 @@ QStringList FontConfigManager::fontCount(const QString &fontpath) const
     return fontList;
 }
 
-InstalledFontInfo *FontConfigManager::fontInfo(const QString &family) const
+InstalledFontInfo *FontConfigManager::fontInfo(const QString &family, const QString &fullname) const
 {
     InstalledFontInfo *info = 0;
     foreach (info, mFcListInfo) {
-        if (info->family().contains(family))
-            return info;
+        if (info->family().contains(family)) {
+            if (fullname.isEmpty())
+                return info;
+            if (info->fullname().contains(fullname))
+                return info;
+        }
     }
     return 0;
 }
@@ -162,49 +179,62 @@ int FontConfigManager::count() const
 
 void FontConfigManager::appendFontProperty(FontsConfigProperties *prop, const QStringList &familyList)
 {
-//    qDebug() << "appendFontProperty(" << prop->family() << "," << familyList << ")";
+    if (!mLocalFontsConf)
+        return;
 
-    QStringList preferedFamilies(mPreferFontListMap.keys());
+    QStringList preferedFamilies(mLocalFontsConf->preferFamily());
+    preferedFamilies << prop->preferFamilies();
+    preferedFamilies.removeDuplicates();
     foreach (const QString &family, preferedFamilies) {
-        QStringList preferList(mPreferFontListMap.value(family, QStringList()));
-        QStringList origList(preferList);
         if (prop->preferFamilies().contains(family)) {
-            preferList << prop->family();
-            foreach (const QString &f, familyList) {
-                if (f != prop->family())
-                    preferList << f;
-            }
+            if (familyList.isEmpty())
+                mLocalFontsConf->appendPreferFamilyFor(family, prop->family());
+            else
+                foreach (const QString &f, familyList)
+                    mLocalFontsConf->appendPreferFamilyFor(family, f);
         } else {
-            preferList.removeOne(prop->family());
-            foreach (const QString &f, familyList) {
-                preferList.removeOne(f);
-            }
+            if (familyList.isEmpty())
+                mLocalFontsConf->removePreferFamilyFor(family, prop->family());
+            else
+                foreach (const QString &f, familyList)
+                    mLocalFontsConf->removePreferFamilyFor(family, f);
         }
-        preferList.removeDuplicates();
-//        qDebug() << prop->family() << prop->preferFamilies() << origList << preferList;
-        if (preferList != origList) {
-            mFontsConfModified = true;
-            mPreferFontListMap.insert(family, preferList);
+    }
+
+    QStringList keys = FontsConf::configKeys();
+    foreach (const QString &key, keys) {
+        if (prop->configValue(key) == FontsConfigProperties::Default) {
+            if (familyList.isEmpty())
+                mLocalFontsConf->unsetMatchEditValueFor(key, prop->family());
+            else
+                foreach (const QString &f, familyList)
+                    mLocalFontsConf->unsetMatchEditValueFor(key, f);
+        } else {
+            if (familyList.isEmpty())
+                mLocalFontsConf->setMatchEditValueFor(key, prop->family(), (prop->configValue(key) == FontsConfigProperties::True));
+            else
+                foreach (const QString &f, familyList)
+                    mLocalFontsConf->setMatchEditValueFor(key, f, (prop->configValue(key) == FontsConfigProperties::True));
         }
     }
-    appendFontPropertyBool("embeddedbitmap", prop->embeddedBitmap(), prop, familyList);
-    appendFontPropertyBool("hinting", prop->embeddedBitmap(), prop, familyList);
 
-    emit configHasUninstalledFontsChanged();
+    emit fontsConfUpdated();
 }
 
 void FontConfigManager::appendFontProperty(InstalledFontInfo *fontInfo)
 {
-    appendFontProperty(fontInfo->fontProperty(), fontInfo->family());
+    QStringList familyList;
+    familyList << fontInfo->enfamily();
+    appendFontProperty(fontInfo->fontProperty(), familyList);
 }
 
 FontsConfigProperties *FontConfigManager::fontProperty(const QString &family, const QStringList &familyList) const
 {
     FontsConfigProperties *prop = new FontsConfigProperties(family);
     QStringList families;
-    QStringList preferedFamilies = mPreferFontListMap.keys();
+    QStringList preferedFamilies = mLocalFontsConf->preferFamily();
     foreach (const QString &key, preferedFamilies) {
-        families = mPreferFontListMap.value(key);
+        families = mLocalFontsConf->preferFamilyFor(key);
         if (families.contains(family))
             prop->addPreferFamily(key);
         foreach (const QString &f, familyList) {
@@ -213,15 +243,21 @@ FontsConfigProperties *FontConfigManager::fontProperty(const QString &family, co
         }
     }
 
-    if (fontPropertyBoolValue("embeddedbitmap", true, family, familyList))
-        prop->setEmbeddedBitmap(FontsConfigProperties::True);
-    if (fontPropertyBoolValue("embeddedbitmap", false, family, familyList))
-        prop->setEmbeddedBitmap(FontsConfigProperties::False);
-
-    if (fontPropertyBoolValue("hinting", true, family, familyList))
-        prop->setHinting(FontsConfigProperties::True);
-    if (fontPropertyBoolValue("hinting", false, family, familyList))
-        prop->setHinting(FontsConfigProperties::False);
+    QStringList keys = FontsConf::configKeys();
+    foreach (const QString &key, keys) {
+        QString val = mLocalFontsConf->matchEditValueFor(key, prop->family());
+        if (!val.isEmpty()) {
+            prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
+            continue;
+        }
+        foreach (const QString &f, familyList) {
+            mLocalFontsConf->matchEditValueFor(key, f);
+            if (!val.isEmpty()) {
+                prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
+                break;
+            }
+        }
+    }
 
     return prop;
 }
@@ -250,27 +286,51 @@ QString FontConfigManager::localFontsConf() const
     return buf;
 }
 
-bool FontConfigManager::configHasUninstalledFonts() const
+QStringList FontConfigManager::preferFamilyFor(const QString &family) const
 {
-    QStringList localfamilies;
-    foreach (InstalledFontInfo *info, mFcListInfo) {
-        localfamilies << info->family();
-    }
-    localfamilies.removeDuplicates();
-    QStringList preferedFamilies = mPreferFontListMap.keys();
-    foreach (const QString &key, preferedFamilies) {
-        QStringList families = mPreferFontListMap.value(key);
-        foreach (const QString &f, families) {
-            if (!localfamilies.contains(f)) {
-                return true;
+    return mLocalFontsConf->preferFamilyFor(family);
+}
+
+QStringList FontConfigManager::acceptFamilyFor(const QString &family) const
+{
+    return mLocalFontsConf->acceptFamilyFor(family);
+}
+
+QStringList FontConfigManager::prependFamilyFor(const QString &family) const
+{
+    return mLocalFontsConf->prependFamilyFor(family);
+}
+
+QStringList FontConfigManager::appendFamilyFor(const QString &family) const
+{
+    return mLocalFontsConf->appendFamilyFor(family);
+}
+
+bool FontConfigManager::isConflict(const QString &family, const QStringList &lang) const
+{
+    InstalledFontInfo *info = fontInfo(family);
+    if (!info)
+        return false;
+
+    QList<int> confListIndex;
+    for (int i = 0; i < mConflictFonts.count(); i++) {
+        const QStringList list(mConflictFonts.at(i));
+        foreach (const QString &f, list) {
+            if (lang.contains(f)) {
+                if (!confListIndex.contains(i))
+                    confListIndex << i;
+                break;
             }
         }
     }
 
-    for (int i = 0; configKeys[i]; i++) {
-        QStringList families = mMatchFontListMap.value(configKeys[i]);
-        foreach (const QString &f, families) {
-            if (!localfamilies.contains(f)) {
+    if (confListIndex.isEmpty())
+        return false;
+
+    foreach (int i, confListIndex) {
+        QStringList confList = mConflictFonts.at(confListIndex.at(i));
+        foreach (const QString &f, confList) {
+            if (info->lang().contains(f)) {
                 return true;
             }
         }
@@ -279,91 +339,289 @@ bool FontConfigManager::configHasUninstalledFonts() const
     return false;
 }
 
-void FontConfigManager::removeUninstalledFontsFromConfig()
+QStringList FontConfigManager::systemPreferFamilyFor(const QString &family) const
+{
+    return mSystemLocalConf->preferFamilyFor(family);
+}
+
+void FontConfigManager::importSystemSettings(const QString &family)
 {
-    QStringList localfamilies;
+    QStringList systemFamilyList = systemPreferFamilyFor(family);
+
+    QStringList installedFontLang;
     foreach (InstalledFontInfo *info, mFcListInfo) {
-        localfamilies << info->family();
+        if (info->systemFont())
+            continue;
+        installedFontLang << info->lang();
     }
-    localfamilies.removeDuplicates();
-    bool modified = false;
-    QStringList preferedFamilies = mPreferFontListMap.keys();
-    foreach (const QString &key, preferedFamilies) {
-        bool m = false;
-        QStringList families = mPreferFontListMap.value(key);
-        foreach (const QString &f, families) {
-            if (!localfamilies.contains(f)) {
-                families.removeOne(f);
-                modified = true;
-                m = true;
-            }
-        }
-        if (m)
-            mPreferFontListMap.insert(key, families);
+    installedFontLang.sort();
+    installedFontLang.removeDuplicates();
+
+    emit startUpdateFontsConfig();
+    foreach (const QString &f, systemFamilyList) {
+        InstalledFontInfo *info = fontInfo(f);
+        if (!info)
+            continue;
+
+        if (isConflict(f, installedFontLang))
+            mLocalFontsConf->appendAcceptFamilyFor(family, f);
+        else
+            mLocalFontsConf->appendPrependFamilyFor(family, f);
+    }
+    emit endUpdateFontsConfig();
+
+    if (mLocalFontsConf->modified()) {
+        emit fontsConfUpdated();
     }
+}
+
+void FontConfigManager::createRecommendedSettings()
+{
+    QList<InstalledFontInfo*> sansSerifFonts;
+    QList<InstalledFontInfo*> serifFonts;
+    QList<InstalledFontInfo*> monospaceFonts;
+    QList<InstalledFontInfo*> monospaceSansSerifFonts;
+    QList<InstalledFontInfo*> monospaceSerifFonts;
+    QList<InstalledFontInfo*> unknownFonts;
+
+    foreach (InstalledFontInfo *info, mFcListInfo) {
+        if (info->systemFont())
+            continue;
+        if (maybeMonospaceFont(info)) {
+            if (maybeSansSerifFont(info))
+                monospaceSansSerifFonts << info;
+            else if (maybeSerifFont(info))
+                monospaceSerifFonts << info;
+            else
+                monospaceFonts << info;
+        } else if (maybeSansSerifFont(info))
+            sansSerifFonts << info;
+        else if (maybeSerifFont(info))
+            serifFonts << info;
+        else
+            unknownFonts << info;
+    }
+
+    emit startUpdateFontsConfig();
+    resetFontsConf();
+
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+    QStringList f;
+    foreach (InstalledFontInfo *info, monospaceFonts)
+        f << info->localefamily();
+    qDebug() << "Mono:" << f;
+    f.clear();
+    foreach (InstalledFontInfo *info, monospaceSansSerifFonts)
+        f << info->localefamily();
+    qDebug() << "Mono(Sans Serif):" << f;
+    f.clear();
+    foreach (InstalledFontInfo *info, monospaceSerifFonts)
+        f << info->localefamily();
+    qDebug() << "Mono(Serif):" << f;
+    f.clear();
+    foreach (InstalledFontInfo *info, sansSerifFonts)
+        f << info->localefamily();
+    qDebug() << "Sans Serif:" << f;
+    f.clear();
+    foreach (InstalledFontInfo *info, serifFonts)
+        f << info->localefamily();
+    qDebug() << "Serif:" << f;
+    f.clear();
+    foreach (InstalledFontInfo *info, unknownFonts)
+        f << info->localefamily();
+    qDebug() << "Unknown:" << f;
+#endif
+
+    if (monospaceFonts.count())
+        foreach (InstalledFontInfo *info, monospaceFonts) {
+            addPreferFamily(MONOSPACE_DEF, info->enfamily());
+        }
+    else if (monospaceSansSerifFonts.count())
+        foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
+            addPreferFamily(MONOSPACE_DEF, info->enfamily());
+        }
+    else if (monospaceSerifFonts.count())
+        foreach (InstalledFontInfo *info, monospaceSerifFonts) {
+            addPreferFamily(MONOSPACE_DEF, info->enfamily());
+        }
+
+    if (sansSerifFonts.count())
+        foreach (InstalledFontInfo *info, sansSerifFonts) {
+            addPreferFamily(SANSSERIF_DEF, info->enfamily());
+            addPreferFamily(SYSTEM_DEF, info->enfamily());
+        }
+    else if (monospaceSansSerifFonts.count())
+        foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
+            addPreferFamily(SANSSERIF_DEF, info->enfamily());
+            addPreferFamily(SYSTEM_DEF, info->enfamily());
+        }
+    else if (unknownFonts.count())
+        foreach (InstalledFontInfo *info, unknownFonts) {
+            addPreferFamily(SANSSERIF_DEF, info->enfamily());
+            addPreferFamily(SYSTEM_DEF, info->enfamily());
+        }
+    else if (monospaceFonts.count())
+        foreach (InstalledFontInfo *info, monospaceFonts) {
+            addPreferFamily(SANSSERIF_DEF, info->enfamily());
+            addPreferFamily(SYSTEM_DEF, info->enfamily());
+        }
+
+    if (serifFonts.count())
+        foreach (InstalledFontInfo *info, serifFonts) {
+            addPreferFamily(SERIF_DEF, info->enfamily());
+        }
+    else if (monospaceSerifFonts.count())
+        foreach (InstalledFontInfo *info, monospaceSerifFonts) {
+            addPreferFamily(SANSSERIF_DEF, info->enfamily());
+        }
+    else if (unknownFonts.count())
+        foreach (InstalledFontInfo *info, unknownFonts) {
+            addPreferFamily(SERIF_DEF, info->enfamily());
+        }
+    else if (monospaceFonts.count())
+        foreach (InstalledFontInfo *info, monospaceFonts) {
+            addPreferFamily(SERIF_DEF, info->enfamily());
+        }
 
-    for (int i = 0; configKeys[i]; i++) {
-        bool m = false;
-        QStringList families = mMatchFontListMap.value(configKeys[i]);
-        foreach (const QString &f, families) {
-            if (!localfamilies.contains(f)) {
-                families.removeOne(f);
-                modified = true;
-                m = true;
+    foreach (const QString &f, FontsConf::genericFamilies())
+        importSystemSettings(f);
+    emit endUpdateFontsConfig();
+
+    emit fontsConfUpdated();
+}
+
+QStringList FontConfigManager::installableFamily(const QString &family, bool localOnly)
+{
+    QStringList installedFont;
+    installedFont << prependFamilyFor(family);
+    installedFont << appendFamilyFor(family);
+    installedFont << preferFamilyFor(family);
+    installedFont << acceptFamilyFor(family);
+
+    QStringList familyList;
+    foreach (InstalledFontInfo *info, mFcListInfo) {
+        if (localOnly && info->systemFont())
+            continue;
+        bool check = false;
+        foreach (const QString &f, info->family()) {
+            if (installedFont.contains(f)) {
+                check = true;
+                break;
             }
         }
-        if (m)
-            mMatchFontListMap.insert(configKeys[i], families);
+        if (!check)
+            familyList << info->localefamily();
+    }
+    familyList.sort();
+    familyList.removeDuplicates();
+
+    return familyList;
+}
+
+QString FontConfigManager::localeFamily(const QString &family) const
+{
+    if (mEnLocaleFontMap.contains(family))
+        return mEnLocaleFontMap.value(family);
+    return family;
+}
+
+bool FontConfigManager::maybeSansSerifFont(InstalledFontInfo *info) const
+{
+    foreach (const QString &f, info->family()) {
+        if (f.contains("gothic", Qt::CaseInsensitive) ||
+                f.contains("arial", Qt::CaseInsensitive) ||
+                f.contains("helvetica", Qt::CaseInsensitive) ||
+                f.contains("verdana", Qt::CaseInsensitive) ||
+                f.contains("sans", Qt::CaseInsensitive))
+            return true;
     }
+    return false;
+}
 
-    if (modified) {
-        mFontsConfModified = true;
-        emit configHasUninstalledFontsChanged();
+bool FontConfigManager::maybeSerifFont(InstalledFontInfo *info) const
+{
+    foreach (const QString &f, info->family()) {
+        if (f.contains("mincho", Qt::CaseInsensitive) ||
+                f.contains("times", Qt::CaseInsensitive) ||
+                (f.contains("serif", Qt::CaseInsensitive) && !f.contains("sans", Qt::CaseInsensitive)))
+            return true;
     }
+    return false;
 }
 
-void FontConfigManager::appendFontPropertyBool(const QString &config, FontsConfigProperties::ConfigValue value, FontsConfigProperties *prop, const QStringList &familyList)
+#define DEFAULT_POINTSIZE 24
+
+bool FontConfigManager::maybeMonospaceFont(InstalledFontInfo *info) const
 {
-    static const char *boolValues[] = { "true", "false", 0 };
-    for (int i = 0; boolValues[i]; i++) {
-        QString key = config + QLatin1String(":") + boolValues[i];
-        QStringList matchedList(mMatchFontListMap.value(key, QStringList()));
-        QStringList origList(matchedList);
-        if ((value == FontsConfigProperties::True && i == 0) || (value == FontsConfigProperties::False && i == 1)) {
-            matchedList << prop->family();
-            foreach (const QString &f, familyList) {
-                if (f != prop->family())
-                    matchedList << f;
+    int fontId = -1;
+    QFont font = font4info(info, DEFAULT_POINTSIZE);
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+    qDebug() << "0 - FontConfigManager::maybeMonospaceFont(" << info->enfamily() << info->enstyle() << point << ")" << font.family() << font.exactMatch();
+#endif
+    if (!font.exactMatch() || font.family() != info->enfamily()) {
+        fontId = QFontDatabase::addApplicationFont(info->file());
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+        qDebug() << "\tfontId:" << fontId << info->file();
+#endif
+        if (fontId >= 0) {
+            font = font4info(info, DEFAULT_POINTSIZE);
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+            qDebug() << "1 - FontConfigManager::maybeMonospaceFont(" << info->enfamily() << info->enstyle() << point << ")" << font.family() << font.exactMatch();
+#endif
+            if (!font.exactMatch() || font.family() != info->enfamily()) {
+                font = QFont(info->enfamily());
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+                qDebug() << "2 - FontConfigManager::maybeMonospaceFont(" << info->enfamily() << info->enstyle() << point << ")" << font.family() << font.exactMatch();
+#endif
             }
+        }
+    }
+    bool isFixed = false;
+    if (font.exactMatch()) {
+        QFontInfo fi(font);
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+        qDebug() << "\tfixedPitch:" << fi.fixedPitch();
+#endif
+        if (fi.fixedPitch()) {
+            isFixed = true;
         } else {
-            matchedList.removeOne(prop->family());
-            foreach (const QString &f, familyList) {
-                matchedList.removeOne(f);
+            QFontMetrics fm(font);
+            int w = fm.width(QLatin1Char('A'));
+#ifdef FONTMANAGER_DEBUG_RECOMMENDED_SETTINGS
+            qDebug() << "\twidth:" << w << fm.width(QLatin1Char('i')) << fm.width(QLatin1Char('X'));
+#endif
+            if (fm.width(QLatin1Char('i')) == w && fm.width(QLatin1Char('X')) == w) {
+                isFixed = true;
             }
         }
-        matchedList.removeDuplicates();
-        if (matchedList != origList) {
-            mFontsConfModified = true;
-            mMatchFontListMap.insert(key, matchedList);
-        }
     }
-}
-
-bool FontConfigManager::fontPropertyBoolValue(const QString &config, bool value, const QString &family, const QStringList &familyList) const
-{
-    QStringList families = mMatchFontListMap.value(config + (value ? ":true" : ":false"));
-    if (families.contains(family))
+    if (fontId >= 0)
+        QFontDatabase::removeApplicationFont(fontId);
+    if (isFixed)
         return true;
-    foreach (const QString &f, familyList) {
-        if (families.contains(f))
+    foreach (const QString &f, info->family()) {
+        if (f.contains("courier", Qt::CaseInsensitive) ||
+                f.contains("mono", Qt::CaseInsensitive))
             return true;
     }
     return false;
 }
 
+QFont FontConfigManager::font4info(InstalledFontInfo *info, int pointSize) const
+{
+    QFontDatabase fdb;
+    int point = pointSize;
+    if (!fdb.isScalable(info->enfamily(), info->enstyle())) {
+        QList<int> points = fdb.pointSizes(info->enfamily(), info->enstyle());
+        if (points.count() > 0)
+            point = points.first();
+    }
+    QFont font = fdb.font(info->enfamily(), info->enstyle(), point);
+    return font;
+}
+
 void FontConfigManager::runFcCache()
 {
-//    qDebug("runFcCache()");
     QProcess *proc = new QProcess(this);
     connect(proc, SIGNAL(finished(int)), SIGNAL(fcCacheFinished()));
     connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
@@ -372,7 +630,6 @@ void FontConfigManager::runFcCache()
 
 void FontConfigManager::readFcList()
 {
-//    qDebug("FontConfigManager::readFcList()");
     QProcess *proc = new QProcess(this);
     proc->start(FCBIN_PATH FCLIST_COMMAND, QStringList() << FCLIST_OPTION);
     if (!proc->waitForStarted())
@@ -383,9 +640,12 @@ void FontConfigManager::readFcList()
 
     qDeleteAll(mFcListInfo);
     mFcListInfo.clear();
+    mEnLocaleFontMap.clear();
 
     QByteArray buf = proc->readAllStandardOutput();
     QByteArray errbuf = proc->readAllStandardError();
+    if (!errbuf.isEmpty())
+        qWarning("%s", errbuf.constData());
     Q_ASSERT(errbuf.isEmpty());
 
     static QByteArray emptyLine("\n");
@@ -399,11 +659,12 @@ void FontConfigManager::readFcList()
             if (!fibuf.isEmpty()) {
                 InstalledFontInfo *info = new InstalledFontInfo(fibuf, mLocalFontPath, this);
                 info->setLocale(mLang);
-                if (info->isValid())
+                if (info->isValid()) {
                     mFcListInfo.append(info);
-                else
+                    mEnLocaleFontMap.insert(info->enfamily(), info->localefamily());
+                    info->setFontProperty(fontProperty(info));
+                } else
                     delete info;
-                info->setFontProperty(fontProperty(info));
                 fibuf.clear();
             }
         } else {
@@ -413,295 +674,133 @@ void FontConfigManager::readFcList()
     Q_ASSERT(fibuf.isEmpty());
     buffer.close();
 
-    QStringList f;
-    QStringList sf;
-    QStringList lf;
-    foreach (InstalledFontInfo *info, mFcListInfo) {
-        QString family(info->localefamily());
-        f << family;
-        if (info->systemFont()) {
-            sf << family;
-        } else {
-            lf << family;
-        }
-    }
-    f.sort();
-    sf.sort();
-    lf.sort();
-    if (f != mFamilies) {
-        mFamilies = f;
-        emit familiesChanged();
-    }
-    if (sf != mSystemFamilies) {
-        mSystemFamilies = sf;
-        emit systemFamiliesChanged();
-    }
-    if (lf != mLocalFamilies) {
-        mLocalFamilies = lf;
-        emit localFamiliesChanged();
-    }
+    qSort(mFcListInfo.begin(), mFcListInfo.end(), InstalledFontInfo::compare);
 
-    emit configHasUninstalledFontsChanged();
+    emit fontListUpdated();
 
     delete proc;
 }
 
 void FontConfigManager::readFontsConf()
 {
-    QFile fp(mLocalFontsConfPath);
-    if (!fp.exists())
-        return;
-    fp.open(QIODevice::ReadOnly);
-    if (!fp.isOpen())
-        return;
-
-    clearFontProperties();
-
-    QXmlStreamReader xml(&fp);
-
-    FCState state = Initialized;
-
-    QString currentConig;
-    int depth = 0;
-    bool hasUnknownConfig = false;
-    QString aliasFamily;
-    QString matchTarget;
-    QStringList familyList;
-    while (!xml.atEnd()) {
-        QXmlStreamReader::TokenType token = xml.readNext();
-        if (token == QXmlStreamReader::DTD) {
-            if (xml.dtdName() != "fontconfig" || xml.dtdSystemId() != "fonts.dtd") {
-                state = InvalidConfig;
-            }
-        } else if (token == QXmlStreamReader::StartElement) {
-            depth++;
-            QString elemName(xml.name().toString());
-            switch (state) {
-            case Initialized:
-                if (elemName == "fontconfig")
-                    state = Ready;
-                else
-                    state = InvalidConfig;
-                break;
-
-            case Ready:
-                currentConig = elemName;
-                if (elemName == "alias")
-                    state = WaitFamilyForAlias;
-                else if (elemName == "match" && xml.attributes().value("target") == "font")
-                    state = WaitTestForMatch;
-                else
-                    state = UnknownState;
-                break;
-
-            case WaitFamilyForAlias:
-                if (elemName == "family") {
-                    QXmlStreamReader::TokenType t = xml.readNext();
-                    Q_ASSERT(t == QXmlStreamReader::Characters);
-                    aliasFamily = xml.text().toString();
-                    QStringList preferList(mPreferFontListMap.value(aliasFamily, QStringList()));
-                    preferList.clear();
-                    mPreferFontListMap.insert(aliasFamily, preferList);
-                    state = WaitPreferForAlias;
-                } else
-                    state = UnknownState;
-                break;
-
-            case WaitPreferForAlias:
-                if (elemName == "prefer")
-                    state = ReadingPrefer;
-                else
-                    state = UnknownState;
-                break;
-
-            case ReadingPrefer:
-                if (elemName == "family") {
-                    QXmlStreamReader::TokenType t = xml.readNext();
-                    Q_ASSERT(t == QXmlStreamReader::Characters);
-                    QString family(xml.text().toString());
-
-                    QStringList preferList(mPreferFontListMap.value(aliasFamily, QStringList()));
-                    preferList << family;
-                    mPreferFontListMap.insert(aliasFamily, preferList);
-                } else
-                    state = UnknownState;
-                break;
-
-            case WaitTestForMatch:
-                if (elemName == "test" && xml.attributes().value("name") == "family" && xml.attributes().value("compare") == "eq") {
-                    familyList.clear();
-                    state = ReadingTest;
-                } else
-                    state = UnknownState;
-                break;
-
-            case ReadingTest:
-                if (elemName == "string") {
-                    QXmlStreamReader::TokenType t = xml.readNext();
-                    Q_ASSERT(t == QXmlStreamReader::Characters);
-                    QString family(xml.text().toString());
-                    familyList << family;
-                } else
-                    state = UnknownState;
-                break;
-
-            case WaitEditForMatch:
-                state = UnknownState;
-                if (elemName == "edit" && xml.attributes().hasAttribute("name") && xml.attributes().value("mode") == "assign") {
-                    matchTarget = xml.attributes().value("name").toString();
-                    if (matchTarget == "embeddedbitmap" || matchTarget == "hinting")
-                        state = WaitBoolForEdit;
-                }
-                break;
-
-            case WaitBoolForEdit:
-                if (elemName == "bool") {
-                    QXmlStreamReader::TokenType t = xml.readNext();
-                    Q_ASSERT(t == QXmlStreamReader::Characters);
-                    QString boolVal(xml.text().toString());
-                    QString key(matchTarget + ":" + boolVal);
-                    mMatchFontListMap.insert(key, familyList);
-                    state = WaitEndMatch;
-                } else
-                    state = UnknownState;
-                break;
-
-            default:
-                ;
-            }
-
-        } else if (token == QXmlStreamReader::EndElement) {
-            depth--;
-            QString elemName(xml.name().toString());
-
-            switch (state) {
-            case ReadingPrefer:
-                if (elemName == "alias")
-                    state = Ready;
-                break;
-
-            case ReadingTest:
-                if (elemName == "test")
-                    state = WaitEditForMatch;
-                break;
-
-            case WaitEndMatch:
-                if (elemName == "match")
-                    state = Ready;
-                break;
+    mLocalFontsConf->load(mLocalFontsConfPath);
+    emit fontsConfUpdated();
+}
 
-            case UnknownState:
-                if (elemName == currentConig && depth == 1)
-                    state = Ready;
-                break;
+void FontConfigManager::saveFontsConf()
+{
+    if (!mLocalFontsConf || mLocalFontsConfPath.isEmpty())
+        return;
 
-            default:
-                ;
-            }
+    bool check = localFontsConfExists();
+    if (isEmptyLocalFontsConf()) {
+        if (check) {
+            QFile::remove(mLocalFontsConfPath);
+            emit localFontsConfExistsChanged();
         }
-        if (state == InvalidConfig)
-            break;
-        else if (state == UnknownState)
-            hasUnknownConfig = true;
+    } else {
+        mLocalFontsConf->save(mLocalFontsConfPath);
+        if (!check)
+            emit localFontsConfExistsChanged();
     }
-
-    if (state == InvalidConfig)
-        emit warning(tr("%1 is invali.!  Could not parse it.").arg(mLocalFontsConfPath));
-    else if (hasUnknownConfig)
-        emit warning(tr("%1 contains unsupported config(s).  It will be overwritten.\nAre you sure?").arg(mLocalFontsConfPath));
-
-    mHasUnknownConfig = hasUnknownConfig;
-    mFontsConfModified = false;
-    emit configHasUninstalledFontsChanged();
 }
 
-static void writeBoolConfig(QXmlStreamWriter &xml, const QString &configName, bool configValue, const QStringList &families)
+void FontConfigManager::backupFontsConf(const QString &filepath)
 {
-    if (families.isEmpty())
+    if (filepath.isEmpty() || isEmptyLocalFontsConf())
         return;
+    mLocalFontsConf->save(filepath);
+}
 
-    xml.writeStartElement("match");
-    xml.writeAttribute("target", "font");
-    xml.writeStartElement("test");
-    xml.writeAttribute("name", "family");
-    xml.writeAttribute("compare", "eq");
-    foreach (const QString &family, families) {
-        xml.writeTextElement("string", family);
+void FontConfigManager::restoreFontsConf(const QString &filepath)
+{
+    if (filepath.isEmpty() || !QFile::exists(filepath))
+        return;
+    FontsConf *restoredConf = new FontsConf(this);
+    restoredConf->load(filepath);
+    if (restoredConf->isValid()) {
+        mLocalFontsConf->copy(restoredConf);
+        emit fontsConfUpdated();
+        delete restoredConf;
     }
-    xml.writeEndElement();
-    xml.writeStartElement("edit");
-    xml.writeAttribute("name", configName);
-    xml.writeAttribute("mode", "assign");
-    xml.writeTextElement("bool", configValue ? "true" : "false");
-    xml.writeEndElement();
-    xml.writeEndElement();
-
 }
 
-void FontConfigManager::saveFontsConf()
+void FontConfigManager::resetFontsConf()
 {
-    QByteArray buf;
-
-    if (!mFontsConfModified)
+    if (!mLocalFontsConf)
         return;
+    mLocalFontsConf->initFontsConf();
+    emit fontsConfUpdated();
+}
 
-    QXmlStreamWriter xml(&buf);
-    xml.setAutoFormatting(true);
+void FontConfigManager::addPreferFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->appendPreferFamilyFor(family, value);
+    emit fontsConfUpdated();
+}
 
-    xml.writeStartDocument();
+void FontConfigManager::addAcceptFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->appendAcceptFamilyFor(family, value);
+    emit fontsConfUpdated();
+}
 
-    xml.writeDTD("<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>");
+void FontConfigManager::addPrependFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->appendPrependFamilyFor(family, value);
+    emit fontsConfUpdated();
+}
 
-    xml.writeStartElement("fontconfig");
+void FontConfigManager::addAppendFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->appendAppendFamilyFor(family, value);
+    emit fontsConfUpdated();
+}
 
-    QStringList preferedFamilies = mPreferFontListMap.keys();
-    foreach (const QString &family, preferedFamilies) {
-        QStringList preferFamilies = mPreferFontListMap.value(family);
-
-        if (!preferFamilies.isEmpty()) {
-            xml.writeStartElement("alias");
-            xml.writeTextElement("family", family);
-            xml.writeStartElement("prefer");
-            foreach (const QString &family, preferFamilies) {
-                xml.writeTextElement("family", family);
-            }
-            xml.writeEndElement();
-            xml.writeEndElement();
-        }
-    }
+void FontConfigManager::insertPreferFamily(const QString &family, const QString &value, int index)
+{
+    mLocalFontsConf->insertPreferFamilyFor(family, value, index);
+    emit fontsConfUpdated();
+}
 
-    QStringList familyList;
+void FontConfigManager::insertAcceptFamily(const QString &family, const QString &value, int index)
+{
+    mLocalFontsConf->insertAcceptFamilyFor(family, value, index);
+    emit fontsConfUpdated();
+}
 
-    familyList = mMatchFontListMap.value("embeddedbitmap:true");
-    writeBoolConfig(xml, "embeddedbitmap", true, familyList);
-    familyList = mMatchFontListMap.value("embeddedbitmap:false");
-    writeBoolConfig(xml, "embeddedbitmap", false, familyList);
+void FontConfigManager::insertPrependFamily(const QString &family, const QString &value, int index)
+{
+    mLocalFontsConf->insertPrependFamilyFor(family, value, index);
+    emit fontsConfUpdated();
+}
 
-    familyList = mMatchFontListMap.value("hinting:true");
-    writeBoolConfig(xml, "hinting", true, familyList);
-    familyList = mMatchFontListMap.value("hinting:false");
-    writeBoolConfig(xml, "hinting", false, familyList);
+void FontConfigManager::insertAppendFamily(const QString &family, const QString &value, int index)
+{
+    mLocalFontsConf->insertAppendFamilyFor(family, value, index);
+    emit fontsConfUpdated();
+}
 
-    xml.writeEndElement();
+void FontConfigManager::removePreferFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->removePreferFamilyFor(family, value);
+    emit fontsConfUpdated();
+}
 
-    QFile fp(mLocalFontsConfPath);
-    if (!fp.open(QIODevice::WriteOnly)) {
-        emit warning(tr("Could not write to %1").arg(mLocalFontsConfPath));
-        return;
-    }
-    fp.write(buf);
-    fp.close();
-    mHasUnknownConfig = false;
-    mFontsConfModified = false;
+void FontConfigManager::removeAcceptFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->removeAcceptFamilyFor(family, value);
+    emit fontsConfUpdated();
 }
 
-void FontConfigManager::clearFontProperties()
+void FontConfigManager::removePrependFamily(const QString &family, const QString &value)
 {
-    static const char *genericFamilies[] = { "sans-serif", "serif", "monospace", 0 };
-    mPreferFontListMap.clear();
-    for (int i = 0; genericFamilies[i]; i++) {
-        mPreferFontListMap.insert(genericFamilies[i], QStringList());
-    }
-    mMatchFontListMap.clear();
+    mLocalFontsConf->removePrependFamilyFor(family, value);
+    emit fontsConfUpdated();
 }
 
+void FontConfigManager::removeAppendFamily(const QString &family, const QString &value)
+{
+    mLocalFontsConf->removeAppendFamilyFor(family, value);
+    emit fontsConfUpdated();
+}