diff options
Diffstat (limited to 'kcontrol/fonts/kxftconfig.cpp')
-rw-r--r-- | kcontrol/fonts/kxftconfig.cpp | 1544 |
1 files changed, 1544 insertions, 0 deletions
diff --git a/kcontrol/fonts/kxftconfig.cpp b/kcontrol/fonts/kxftconfig.cpp new file mode 100644 index 000000000..833e31118 --- /dev/null +++ b/kcontrol/fonts/kxftconfig.cpp @@ -0,0 +1,1544 @@ +/* + Copyright (c) 2002 Craig Drummond <craig@kde.org> + + 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" +#include <math.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <qregexp.h> +#include <qfile.h> +#include <qpaintdevice.h> +#include <klocale.h> +#include <klargefile.h> +#include <qdir.h> +#include <qsettings.h> +#include <qfont.h> + +#ifdef HAVE_FONTCONFIG +#include <stdarg.h> +#include <stdio.h> +#include <fontconfig/fontconfig.h> +#include <kdebug.h> +#endif + +using namespace std; + +QString KXftConfig::contractHome(QString path) +{ + if (!path.isEmpty() && '/'==path[0]) + { + QString home(QDir::homeDirPath()); + + if(path.startsWith(home)) + { + unsigned int len = home.length(); + + if(path.length() == len || path[len] == '/') + return path.replace(0, len, QString::fromLatin1("~")); + } + } + + return path; +} + +QString KXftConfig::expandHome(QString path) +{ + if(!path.isEmpty() && '~'==path[0]) + return 1==path.length() ? QDir::homeDirPath() : path.replace(0, 1, QDir::homeDirPath()); + + return path; +} + +static int point2Pixel(double point) +{ + return (int)(((point*QPaintDevice::x11AppDpiY())/72.0)+0.5); +} + +static int pixel2Point(double pixel) +{ + return (int)(((pixel*72.0)/(double)QPaintDevice::x11AppDpiY())+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.findRev('/'); + + if(slashPos!=(((int)ds.length())-1)) + ds.append('/'); + + return ds; + } + + return d; +} + +static QString xDirSyntax(const QString &d) +{ + if(!d.isNull()) + { + QString ds(d); + int slashPos=ds.findRev('/'); + + if(slashPos==(((int)ds.length())-1)) + ds.remove(slashPos, 1); + return ds; + } + + return d; +} + +static bool check(const QString &path, unsigned int fmt, bool checkW=false) +{ + KDE_struct_stat info; + QCString pathC(QFile::encodeName(path)); + + return 0==KDE_lstat(pathC, &info) && (info.st_mode&S_IFMT)==fmt && (!checkW || 0==::access(pathC, W_OK)); +} + +inline bool fExists(const QString &p) +{ + return check(p, S_IFREG, false); +} + +inline bool dWritable(const QString &p) +{ + return check(p, S_IFDIR, true); +} + +inline bool dExists(const QString &p) +{ + return check(p, S_IFDIR, false); +} + +static QString getDir(const QString &f) +{ + QString d(f); + + int slashPos=d.findRev('/'); + + if(-1!=slashPos) + d.remove(slashPos+1, d.length()); + + return dirSyntax(d); +} + +static time_t getTimeStamp(const QString &item) +{ + KDE_struct_stat info; + + return !item.isNull() && 0==KDE_lstat(QFile::encodeName(item), &info) ? info.st_mtime : 0; +} + +#ifdef HAVE_FONTCONFIG + +inline QString fileSyntax(const QString &f) { return xDirSyntax(f); } +// +// Obtain location of config file to use. +// +// For system, prefer the following: +// +// <...>/config.d/00kde.conf = preferred method from FontConfig >= 2.3 +// <...>/local.conf +// +// Non-system, prefer: +// +// $HOME/<...>/.fonts.conf +// $HOME/<...>/fonts.conf +// +QString getConfigFile(bool system) +{ +#if (FC_VERSION>=20300) + static const char * constKdeRootFcFile="00kde.conf"; +#endif + + FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent()); + QStringList files; + FcChar8 *file; + QString home(dirSyntax(QDir::homeDirPath())); + + while((file=FcStrListNext(list))) + { + QString f((const char *)file); + + if(fExists(f)) + { + if(system || 0==fileSyntax(f).find(home)) // For nonsystem, only consider file within $HOME + files.append(f); + } +#if (FC_VERSION>=20300) + if(system && dExists(f) && (-1!=f.find(QRegExp("/conf\\.d/?$")) || -1!=f.find(QRegExp("/conf\\.d?$"))) ) + return dirSyntax(f)+constKdeRootFcFile; // This ones good enough for me! +#endif + } + + // + // Go through list of files, looking for the preferred one... + if(files.count()) + { + QStringList::Iterator it(files.begin()), + end(files.end()); + + for(; it!=end; ++it) + if(-1!=(*it).find(QRegExp(system ? "/local\\.conf$" : "/\\.?fonts\\.conf$"))) + return *it; + return files.front(); // Just return the 1st one... + } + else + return system ? "/etc/fonts/local.conf" : fileSyntax(home+"/.fonts.conf"); // Hmmm... no known files? +} + +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::null; +} + +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 + return KXftConfig::SubPixel::None; +} + +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; +} + +#else +static bool strToType(const char *str, KXftConfig::SubPixel::Type &type) +{ + if(0==memcmp(str, "rgb", 3)) + type=KXftConfig::SubPixel::Rgb; + else if(0==memcmp(str, "bgr", 3)) + type=KXftConfig::SubPixel::Bgr; + else if(0==memcmp(str, "vrgb", 4)) + type=KXftConfig::SubPixel::Vrgb; + else if(0==memcmp(str, "vbgr", 4)) + type=KXftConfig::SubPixel::Vbgr; + else if(0==memcmp(str, "none", 4)) + type=KXftConfig::SubPixel::None; + else + return false; + return true; +} + +static inline bool isWhiteSpace(char c) +{ + return c==' ' || c=='\n' || c== '\t'; +} + +static bool ok(char *data, char *entry) +{ + char *e=entry; + + for(;;) + { + e--; + if(e==data || *e=='\n') + return true; + else + if(!isWhiteSpace(*e)) + return false; + } + return false; +} + +static char * getKey(char *data, const char *key) +{ + char *entry, + *start=data; + + while(start &&start && (entry=strstr(start, key))) + if(entry==data || ok(data, entry) && isWhiteSpace(entry[strlen(key)])) + return entry; + else + start=entry+strlen(key); + + return NULL; +} + +static bool skipToken(char **ptr, const char *token) +{ + while(isWhiteSpace(**ptr)) + (*ptr)++; + if(0!=memcmp(*ptr, token, strlen(token))) + return false; + (*ptr)+=strlen(token); + return true; +} + +static bool readNum(char **ptr, double *num) +{ + static const int constMaxNumLen=64; + + char n[constMaxNumLen+1]; + bool foundNum=false, + foundPoint=false, + foundE=false; + int numChars=0; + + while(isWhiteSpace(**ptr)) + (*ptr)++; + + while(numChars<constMaxNumLen && (isdigit(**ptr) || + ('.'==**ptr && foundNum && !foundPoint && !foundE) || ('e'==**ptr && foundNum && !foundE))) + { + n[numChars++]=**ptr; + if('.'==**ptr) + foundPoint=true; + else if('e'==**ptr) + foundE=true; + else + foundNum=true; + (*ptr)++; + } + + if(numChars) + { + n[numChars]='\0'; + *num=atof(n); + return true; + } + + return false; +} + +static KXftConfig::ListItem * getFirstItem(QPtrList<KXftConfig::ListItem> &list) +{ + KXftConfig::ListItem *cur; + + for(cur=list.first(); cur; cur=list.next()) + if(!cur->added()) + return cur; + return NULL; +} +#endif + +static KXftConfig::ListItem * getLastItem(QPtrList<KXftConfig::ListItem> &list) +{ + KXftConfig::ListItem *cur; + + for(cur=list.last(); cur; cur=list.prev()) + if(!cur->added()) + return cur; + return NULL; +} + +#ifndef HAVE_FONTCONFIG +static const QString defaultPath("/usr/X11R6/lib/X11/XftConfig"); +static const QString defaultUserFile(".xftconfig"); +static const char * constSymEnc="\"glyphs-fontspecific\""; + +static const QString constConfigFiles[]= +{ + defaultPath, + + "/etc/X11/XftConfig", + QString::null +}; +#endif + +KXftConfig::KXftConfig(int required, bool system) + : m_required(required), +#ifdef HAVE_FONTCONFIG + m_doc("fontconfig"), +#else + m_size(0), + m_data(NULL), +#endif + m_system(system) +{ +#ifdef HAVE_FONTCONFIG + m_file=getConfigFile(system); + kdDebug(1208) << "Using fontconfig file:" << m_file << endl; + m_antiAliasing = aliasingEnabled(); +#else + if(system) + { + int f; + + for(f=0; !constConfigFiles[f].isNull(); ++f) + if(fExists(constConfigFiles[f])) + m_file=constConfigFiles[f]; + + if(m_file.isNull()) + m_file=defaultPath; + } + else + m_file= QString(QDir::homeDirPath()+"/"+defaultUserFile); +#endif +#ifndef HAVE_FONTCONFIG + m_symbolFamilies.setAutoDelete(true); +#endif + m_dirs.setAutoDelete(true); + reset(); +} + +KXftConfig::~KXftConfig() +{ +#ifndef HAVE_FONTCONFIG + delete [] m_data; + m_data=NULL; +#endif +} + +bool KXftConfig::reset() +{ + bool ok=false; + + m_madeChanges=false; +#ifdef HAVE_FONTCONFIG + m_hint.reset(); + m_hinting.reset(); +#else + m_symbolFamilies.clear(); +#endif + m_dirs.clear(); + m_excludeRange.reset(); + m_excludePixelRange.reset(); + m_subPixel.reset(); + +#ifdef HAVE_FONTCONFIG + QFile f(m_file); + + if(f.open(IO_ReadOnly)) + { + m_time=getTimeStamp(m_file); + ok=true; + m_doc.clear(); + + if(m_doc.setContent(&f)) + readContents(); + f.close(); + } + else + ok=!fExists(m_file) && dWritable(getDir(m_file)); + + if(m_doc.documentElement().isNull()) + m_doc.appendChild(m_doc.createElement("fontconfig")); +#else + QFile f(m_file); + + m_size=0; + delete [] m_data; + m_data=NULL; + + if(f.open(IO_Raw|IO_ReadOnly)) + { + m_time=getTimeStamp(m_file); + m_size=f.size(); + ok=true; + + if(m_size>0) + { + m_data=new char [m_size+1]; + + if(m_data) + { + f.readBlock(m_data, m_size); + m_data[m_size]='\0'; + readContents(); + } + } + f.close(); + } + else + ok=!fExists(m_file) && dWritable(getDir(m_file)); +#endif + + if(ok && m_required&ExcludeRange) + { + // + // Check exclude range values - i.e. size and pixel size... + if(!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) // If "size" range is set, ensure "pixelsize" matches... + { + double pFrom=(double)point2Pixel(m_excludeRange.from), + pTo=(double)point2Pixel(m_excludeRange.to); + + if(!equal(pFrom, m_excludePixelRange.from) || !equal(pTo, m_excludePixelRange.to)) + { + m_excludePixelRange.from=pFrom; + m_excludePixelRange.to=pTo; + m_madeChanges=true; + apply(); + } + } + 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; + apply(); + } + } + + 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(m_required, m_system); + QStringList list; + QStringList::Iterator it; + + if(m_required&Dirs) + { + list=getDirs(); + for(it=list.begin(); it!=list.end(); ++it) + newConfig.addDir(*it); + } + + if(m_required&ExcludeRange) + newConfig.setExcludeRange(m_excludeRange.from, m_excludeRange.to); + if(m_required&SubPixelType) + newConfig.setSubPixelType(m_subPixel.type); +#ifdef HAVE_FONTCONFIG + if(m_required&HintStyle) + newConfig.setHintStyle(m_hint.style); + if(m_required&AntiAlias) + newConfig.setAntiAliasing(m_antiAliasing.set); +#else + if(m_required&SymbolFamilies) + { + list=getSymbolFamilies(); + for(it=list.begin(); it!=list.end(); ++it) + newConfig.addSymbolFamily(*it); + } +#endif + + ok=newConfig.changed() ? newConfig.apply() : true; + if(ok) + reset(); + else + m_time=getTimeStamp(m_file); + } + else + { + if(m_required&ExcludeRange) + { + // Ensure these are always equal... + m_excludePixelRange.from=(int)point2Pixel(m_excludeRange.from); + m_excludePixelRange.to=(int)point2Pixel(m_excludeRange.to); + } + +#ifdef HAVE_FONTCONFIG + FcAtomic *atomic=FcAtomicCreate((const unsigned char *)((const char *)(QFile::encodeName(m_file)))); + + ok=false; + if(atomic) + { + if(FcAtomicLock(atomic)) + { + FILE *f=fopen((char *)FcAtomicNewFile(atomic), "w"); + + if(f) + { + if(m_required&Dirs) + { + applyDirs(); + removeItems(m_dirs); + } + if(m_required&SubPixelType) + applySubPixelType(); + if(m_required&HintStyle) + applyHintStyle(); + if(m_required&AntiAlias) + applyAntiAliasing(); + if(m_required&ExcludeRange) + { + applyExcludeRange(false); + applyExcludeRange(true); + } + + // + // Check document syntax... + const char qtXmlHeader[] = "<?xml version = '1.0'?>"; + const char xmlHeader[] = "<?xml version=\"1.0\"?>"; + const char qtDocTypeLine[] = "<!DOCTYPE fontconfig>"; + const char docTypeLine[] = "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">"; + + QString str(m_doc.toString()); + int idx; + + if(0!=str.find("<?xml")) + str.insert(0, xmlHeader); + else if(0==str.find(qtXmlHeader)) + str.replace(0, strlen(qtXmlHeader), xmlHeader); + + if(-1!=(idx=str.find(qtDocTypeLine))) + str.replace(idx, strlen(qtDocTypeLine), docTypeLine); + + // + // Write to file... + fputs(str.utf8(), f); + fclose(f); + + if(FcAtomicReplaceOrig(atomic)) + { + ok=true; + reset(); // Re-read contents.. + } + else + FcAtomicDeleteNew(atomic); + } + FcAtomicUnlock(atomic); + } + FcAtomicDestroy(atomic); + } +#else + std::ofstream f(QFile::encodeName(m_file)); + + if(f) + { + ListItem *ldi=m_required&Dirs ? getLastItem(m_dirs) : NULL, + *lfi=m_required&SymbolFamilies ? getLastItem(m_symbolFamilies) : NULL; + char *pos=m_data; + bool finished=false, + pixel=false; + + while(!finished) + { + int type=0; + ListItem *fdi=NULL, + *ffi=NULL; + Item *first=NULL; + + if(m_required&Dirs && NULL!=(fdi=getFirstItem(m_dirs)) && (NULL==first || fdi->start < first->start)) + { + first=fdi; + type=Dirs; + } + if(m_required&SymbolFamilies && NULL!=(ffi=getFirstItem(m_symbolFamilies)) && (NULL==first || ffi->start < first->start)) + { + first=ffi; + type=SymbolFamilies; + } + if(m_required&SubPixelType && NULL!=m_subPixel.start && (NULL==first || m_subPixel.start < first->start)) + { + first=&m_subPixel; + type=SubPixelType; + } + if(m_required&ExcludeRange) + if(NULL!=m_excludeRange.start && (NULL==first || m_excludeRange.start < first->start)) + { + first=&m_excludeRange; + type=ExcludeRange; + pixel=false; + } + else if(NULL!=m_excludePixelRange.start && (NULL==first || m_excludePixelRange.start < first->start)) + { + first=&m_excludePixelRange; + type=ExcludeRange; + pixel=true; + } + + if(first && first->start!=pos) + f.write(pos, first->start-pos); + + if(0!=type) + pos=first->end+1; + + switch(type) + { + case Dirs: + if(!first->toBeRemoved) + outputDir(f, fdi->str); + m_dirs.remove(fdi); + if(fdi==ldi) + outputNewDirs(f); + break; + case SymbolFamilies: + if(!first->toBeRemoved) + outputSymbolFamily(f, ffi->str); + m_symbolFamilies.remove(ffi); + if(ffi==lfi) + outputNewSymbolFamilies(f); + break; + case SubPixelType: + if(!first->toBeRemoved) + outputSubPixelType(f, false); + m_subPixel.start=NULL; + break; + case ExcludeRange: + if(!first->toBeRemoved) + outputExcludeRange(f, false, pixel); + m_excludeRange.start=NULL; + break; + case 0: // 0 => All read in entries written... + if(m_size && (pos < m_data+m_size)) + f.write(pos, (m_data+m_size)-pos); + default: + finished=true; + break; + } + }; + + outputNewDirs(f); + outputNewSymbolFamilies(f); + outputSubPixelType(f, true); + outputExcludeRange(f, true, false); + outputExcludeRange(f, true, true); + f.close(); + reset(); // Re-read contents... + } + else + ok=false; +#endif + } + } + + return ok; +} + +bool KXftConfig::getSubPixelType(SubPixel::Type &type) +{ + type=m_subPixel.type; + return SubPixel::None!=m_subPixel.type; +} + +void KXftConfig::setSubPixelType(SubPixel::Type type) +{ + if(type!=m_subPixel.type) + { + m_subPixel.type=type; + m_madeChanges=true; + } +} + +#ifdef HAVE_FONTCONFIG +bool KXftConfig::getHintStyle(Hint::Style &style) +{ + if(Hint::NotSet!=m_hint.style && !m_hint.toBeRemoved) + { + style=m_hint.style; + return true; + } + else + return false; +} + +void KXftConfig::setHintStyle(Hint::Style style) +{ + if((Hint::NotSet==style && Hint::NotSet!=m_hint.style && !m_hint.toBeRemoved) || + (Hint::NotSet!=style && (style!=m_hint.style || m_hint.toBeRemoved)) ) + { + m_hint.toBeRemoved=(Hint::NotSet==style); + m_hint.style=style; + m_madeChanges=true; + } + + if(Hint::NotSet!=style) + setHinting(Hint::None!=m_hint.style); +} + +void KXftConfig::setHinting(bool set) +{ + if(set!=m_hinting.set) + { + m_hinting.set=set; + m_madeChanges=true; + } +} +#endif + +bool KXftConfig::getExcludeRange(double &from, double &to) +{ + if(!equal(0, m_excludeRange.from) || !equal(0,m_excludeRange.to)) + { + from=m_excludeRange.from; + to=m_excludeRange.to; + return true; + } + else + return false; +} + + +void KXftConfig::setExcludeRange(double from, double to) +{ + double f=from<to ? from : to, + t=from<to ? to : from; + + if(!equal(f, m_excludeRange.from) || !equal(t,m_excludeRange.to)) + { + m_excludeRange.from=f; + m_excludeRange.to=t; + m_madeChanges=true; + } +} + +void KXftConfig::addDir(const QString &d) +{ + QString dir(dirSyntax(d)); + + if(dExists(dir) && !hasDir(dir)) + addItem(m_dirs, dir); +} + +void KXftConfig::removeDir(const QString &d) +{ + QString dir(dirSyntax(d)); + + removeItem(m_dirs, dir); +} + +QString KXftConfig::description(SubPixel::Type t) +{ + switch(t) + { + default: + case SubPixel::None: + return i18n("None"); + case SubPixel::Rgb: + return i18n("RGB"); + case SubPixel::Bgr: + return i18n("BGR"); + case SubPixel::Vrgb: + return i18n("Vertical RGB"); + case SubPixel::Vbgr: + return i18n("Vertical BGR"); + } +} + +const char * KXftConfig::toStr(SubPixel::Type t) +{ + switch(t) + { + default: + case SubPixel::None: + return "none"; + case SubPixel::Rgb: + return "rgb"; + case SubPixel::Bgr: + return "bgr"; + case SubPixel::Vrgb: + return "vrgb"; + case SubPixel::Vbgr: + return "vbgr"; + } +} + +#ifdef HAVE_FONTCONFIG +QString KXftConfig::description(Hint::Style s) +{ + switch(s) + { + default: + case Hint::Medium: + return i18n("Medium"); + case Hint::NotSet: + return ""; + case Hint::None: + return i18n("None"); + case Hint::Slight: + return i18n("Slight"); + case Hint::Full: + return i18n("Full"); + } +} + +const char * KXftConfig::toStr(Hint::Style s) +{ + switch(s) + { + default: + case Hint::Medium: + return "hintmedium"; + case Hint::None: + return "hintnone"; + case Hint::Slight: + return "hintslight"; + case Hint::Full: + return "hintfull"; + } +} +#endif + +bool KXftConfig::hasDir(const QString &d) +{ + QString dir(dirSyntax(d)); + +#ifdef HAVE_FONTCONFIG + ListItem *item; + + for(item=m_dirs.first(); item; item=m_dirs.next()) + if(0==dir.find(item->str)) + return true; + + return false; +#else + return NULL!=findItem(m_dirs, dir); +#endif +} + +KXftConfig::ListItem * KXftConfig::findItem(QPtrList<ListItem> &list, const QString &i) +{ + ListItem *item; + + for(item=list.first(); item; item=list.next()) + if(item->str==i) + break; + + return item; +} + +void KXftConfig::clearList(QPtrList<ListItem> &list) +{ + ListItem *item; + + for(item=list.first(); item; item=list.next()) + removeItem(list, item); +} + +QStringList KXftConfig::getList(QPtrList<ListItem> &list) +{ + QStringList res; + ListItem *item; + + for(item=list.first(); item; item=list.next()) + if(!item->toBeRemoved) + res.append(item->str); + + return res; +} + +void KXftConfig::addItem(QPtrList<ListItem> &list, const QString &i) +{ + ListItem *item=findItem(list, i); + + if(!item) + { + list.append(new ListItem(i +#ifndef HAVE_FONTCONFIG + , NULL, NULL +#endif + )); + m_madeChanges=true; + } + else + item->toBeRemoved=false; +} + +void KXftConfig::removeItem(QPtrList<ListItem> &list, ListItem *item) +{ + if(item) + { + if(item->added()) + list.remove(item); + else + item->toBeRemoved=true; + m_madeChanges=true; + } +} + +void KXftConfig::readContents() +{ +#ifdef HAVE_FONTCONFIG + QDomNode n = m_doc.documentElement().firstChild(); + + while(!n.isNull()) + { + QDomElement e = n.toElement(); + + if(!e.isNull()) + if("dir"==e.tagName()) + { + if(m_required&Dirs) + m_dirs.append(new ListItem(expandHome(dirSyntax(e.text())), n)); + } + else if("match"==e.tagName()) + { + QString str; + + switch(e.childNodes().count()) + { + case 1: + if(m_required&SubPixelType && "font"==e.attribute("target")) + { + QDomElement ene=e.firstChild().toElement(); + + if(!ene.isNull() && "edit"==ene.tagName()) + if(!(str=getEntry(ene, "const", 2, "name", "rgba", "mode", "assign")).isNull()) + { + m_subPixel.node=n; + m_subPixel.type=strToType(str.latin1()); + } + else if(!(str=getEntry(ene, "const", 2, "name", "hintstyle", "mode", "assign")).isNull()) + { + m_hint.node=n; + m_hint.style=strToStyle(str.latin1()); + } + else if(!(str=getEntry(ene, "bool", 2, "name", "hinting", "mode", "assign")).isNull()) + { + m_hinting.node=n; + m_hinting.set=str.lower()!="false"; + } + else if(!(str=getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")).isNull()) + { + m_antiAliasing.node=n; + m_antiAliasing.set=str.lower()!="false"; + } + } + break; + case 3: + if(m_required&ExcludeRange && "font"==e.attribute("target")) // CPD: Is target "font" or "pattern" ???? + { + bool foundFalse=false; + QDomNode en=e.firstChild(); + QString family; + double from=-1.0, + to=-1.0, + pixelFrom=-1.0, + pixelTo=-1.0; + + while(!en.isNull()) + { + QDomElement ene=en.toElement(); + + if(!ene.isNull()) + if("test"==ene.tagName()) + { + // kcmfonts used to write incorrectly more or less instead of more_eq and less_eq, so read both, + // first the old (wrong) one then the right one + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more")).isNull()) + from=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more_eq")).isNull()) + from=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less")).isNull()) + to=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less_eq")).isNull()) + to=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more")).isNull()) + pixelFrom=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more_eq")).isNull()) + pixelFrom=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less")).isNull()) + pixelTo=str.toDouble(); + if(!(str=getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less_eq")).isNull()) + pixelTo=str.toDouble(); + } + else if("edit"==ene.tagName() && "false"==getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")) + foundFalse=true; + + en=en.nextSibling(); + } + + if((from>=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(); + } +#else + static const int constMaxDataLen=2048; + + char *from=NULL, + *ptr=m_data, + *eostr=NULL, + data[constMaxDataLen]; + + if(m_required&Dirs) + while((ptr=getKey(ptr, "dir"))) + { + from=ptr; + ptr+=4; + while(isWhiteSpace(*ptr)) + ptr++; + + if(*ptr=='\"') + { + ptr++; + if(NULL!=(eostr=strchr(ptr, '\"')) && eostr-ptr<constMaxDataLen) + { + memcpy(data, ptr, eostr-ptr); + data[eostr-ptr]='\0'; + if(NULL==strchr(data, '\n')) + { + ptr=eostr+1; + + while(*ptr!='\n' && *ptr!='\0' && isWhiteSpace(*ptr)) + ptr++; + m_dirs.append(new ListItem(expandHome(dirSyntax(data)), from, ptr)); + } + } + } + } + + if(m_required&SymbolFamilies || m_required&SubPixelType || m_required&ExcludeRange) + { + double efrom, + eto; + + ptr=m_data; + + while((ptr=getKey(ptr, "match"))) + { + from=ptr; + ptr+=6; + if((m_required&SymbolFamilies || m_required&ExcludeRange) && skipToken(&ptr, "any")) + { + if(m_required&SymbolFamilies && skipToken(&ptr, "family") && skipToken(&ptr, "==")) + { + while(isWhiteSpace(*ptr)) + ptr++; + if(*ptr=='\"') + { + ptr++; + if(NULL!=(eostr=strchr(ptr, '\"')) && eostr-ptr<constMaxDataLen) + { + memcpy(data, ptr, eostr-ptr); + data[eostr-ptr]='\0'; + if(NULL==strchr(data, '\n')) + { + ptr=eostr+1; + + if(skipToken(&ptr, "edit") && skipToken(&ptr, "encoding") && skipToken(&ptr, "=") && + skipToken(&ptr, constSymEnc) && skipToken(&ptr, ";")) + { + while(*ptr!='\n' && *ptr!='\0' && isWhiteSpace(*ptr)) + ptr++; + + m_symbolFamilies.append(new ListItem(data, from, ptr)); + } + } + } + } + } + else if(m_required&ExcludeRange && skipToken(&ptr, "size") && (skipToken(&ptr, ">")||skipToken(&ptr, "<")) && + readNum(&ptr, &efrom) && skipToken(&ptr, "any") && skipToken(&ptr, "size") && + (skipToken(&ptr, "<")||skipToken(&ptr, ">")) && readNum(&ptr, &eto) && skipToken(&ptr, "edit") && + skipToken(&ptr, "antialias") && skipToken(&ptr, "=") && skipToken(&ptr, "false") && skipToken(&ptr, ";")) + { + while(*ptr!='\n' && *ptr!='\0' && isWhiteSpace(*ptr)) + ptr++; + m_excludeRange.from=efrom<eto ? efrom : eto; + m_excludeRange.to=efrom<eto ? eto : efrom; + m_excludeRange.start=from; + m_excludeRange.end=ptr; + } + else if(m_required&ExcludeRange && skipToken(&ptr, "pixelsize") && (skipToken(&ptr, ">")||skipToken(&ptr, "<")) && + readNum(&ptr, &efrom) && skipToken(&ptr, "any") && skipToken(&ptr, "pixelsize") && + (skipToken(&ptr, "<")||skipToken(&ptr, ">")) && readNum(&ptr, &eto) && skipToken(&ptr, "edit") && + skipToken(&ptr, "antialias") && skipToken(&ptr, "=") && skipToken(&ptr, "false") && skipToken(&ptr, ";")) + { + while(*ptr!='\n' && *ptr!='\0' && isWhiteSpace(*ptr)) + ptr++; + m_excludePixelRange.from=efrom<eto ? efrom : eto; + m_excludePixelRange.to=efrom<eto ? eto : efrom; + m_excludePixelRange.start=from; + m_excludePixelRange.end=ptr; + } + } + else if(m_required&SubPixelType && skipToken(&ptr, "edit") && skipToken(&ptr, "rgba") && skipToken(&ptr, "=")) + { + SubPixel::Type type=SubPixel::None; + + while(isWhiteSpace(*ptr)) + ptr++; + + if(!strToType(ptr, type)) + continue; + + ptr+=SubPixel::Rgb==type || SubPixel::Bgr==type ? 3 : 4; + + if(skipToken(&ptr, ";")) + { + while(*ptr!='\n' && *ptr!='\0' && isWhiteSpace(*ptr)) + ptr++; + m_subPixel.type=type; + m_subPixel.start=from; + m_subPixel.end=ptr; + } + } + } + } +#endif +} + +#ifdef HAVE_FONTCONFIG +void KXftConfig::applyDirs() +{ + ListItem *item, + *last=getLastItem(m_dirs); + + for(item=m_dirs.first(); item; item=m_dirs.next()) + if(!item->toBeRemoved && item->node.isNull()) + { + QDomElement newNode = m_doc.createElement("dir"); + QDomText text = m_doc.createTextNode(contractHome(xDirSyntax(item->str))); + + newNode.appendChild(text); + + if(last) + m_doc.documentElement().insertAfter(newNode, last->node); + else + m_doc.documentElement().appendChild(newNode); + } +} + +void KXftConfig::applySubPixelType() +{ + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("const"), + 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 || m_hint.toBeRemoved) + { + if(!m_hint.node.isNull()) + { + m_doc.documentElement().removeChild(m_hint.node); + m_hint.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; + } +} + +void KXftConfig::removeItems(QPtrList<ListItem> &list) +{ + ListItem *item; + QDomElement docElem = m_doc.documentElement(); + + for(item=list.first(); item; item=list.next()) + if(item->toBeRemoved && !item->node.isNull()) + docElem.removeChild(item->node); +} +#else +void KXftConfig::outputDir(std::ofstream &f, const QString &str) +{ + f << "dir \"" << contractHome(xDirSyntax(str)).local8Bit() << "\"" << endl; +} + +void KXftConfig::outputNewDirs(std::ofstream &f) +{ + ListItem *item; + + for(item=m_dirs.first(); item; item=m_dirs.next()) + if(!item->toBeRemoved && NULL==item->start) + outputDir(f, item->str); + m_dirs.clear(); +} + +void KXftConfig::outputSymbolFamily(std::ofstream &f, const QString &str) +{ + f << "match any family == \"" << str.local8Bit() << "\" edit encoding = " << constSymEnc << ';' << endl; +} + +void KXftConfig::outputNewSymbolFamilies(std::ofstream &f) +{ + ListItem *item; + + for(item=m_symbolFamilies.first(); item; item=m_symbolFamilies.next()) + if(!item->toBeRemoved && NULL==item->start) + outputSymbolFamily(f, item->str); + m_symbolFamilies.clear(); +} + +void KXftConfig::outputSubPixelType(std::ofstream &f, bool ifNew) +{ + if((ifNew && NULL==m_subPixel.end) || (!ifNew && NULL!=m_subPixel.end)) + f << "match edit rgba = " << toStr(m_subPixel.type) << ';' << endl; +} + +void KXftConfig::outputExcludeRange(std::ofstream &f, bool ifNew, bool pixel) +{ + Exclude &range=pixel ? m_excludePixelRange : m_excludeRange; + + if(((ifNew && NULL==range.end) || (!ifNew && NULL!=range.end)) && + (!equal(range.from,0) || !equal(range.to,0))) + { + if(pixel) + f << "match any pixelsize > "; + else + f << "match any size > "; + + f << range.from; + if(pixel) + f << " any pixelsize < "; + else + f << " any size < "; + f << range.to << " edit antialias = false;" << endl; +} +} + +#endif + +#ifdef HAVE_FONTCONFIG +bool KXftConfig::getAntiAliasing() const +{ + return m_antiAliasing.set; +} + +void KXftConfig::setAntiAliasing( bool set ) +{ + if ((set && !m_antiAliasing.set) || (!set && m_antiAliasing.set)) + { + m_antiAliasing.set = set; + m_madeChanges = true; + } +} + +void KXftConfig::applyAntiAliasing() +{ + QDomElement matchNode = m_doc.createElement("match"), + typeNode = m_doc.createElement("bool"), + editNode = m_doc.createElement("edit"); + QDomText typeText = m_doc.createTextNode(m_antiAliasing.set ? "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(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + FcResult result; + FcPattern *f = FcFontMatch( 0, pattern, &result ); + FcBool antialiased = FcTrue; + FcPatternGetBool( f, FC_ANTIALIAS, 0, &antialiased ); + FcPatternDestroy( f ); + FcPatternDestroy( pattern ); + return antialiased == FcTrue; +} + +#else + +void KXftConfig::setAntiAliasing( bool set ) +{ + QSettings().writeEntry("/qt/useXft", set); + if (set) + QSettings().writeEntry("/qt/enableXft", set); +} + +bool KXftConfig::getAntiAliasing() const +{ + return QSettings().readBoolEntry("/qt/useXft"); +} + + +#endif |