You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kcontrol/fonts/kxftconfig.cpp

1545 lines
46 KiB

/*
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 <tqregexp.h>
#include <tqfile.h>
#include <tqpaintdevice.h>
#include <klocale.h>
#include <klargefile.h>
#include <tqdir.h>
#include <tqsettings.h>
#include <tqfont.h>
#ifdef HAVE_FONTCONFIG
#include <stdarg.h>
#include <stdio.h>
#include <fontconfig/fontconfig.h>
#include <kdebug.h>
#endif
using namespace std;
TQString KXftConfig::contractHome(TQString path)
{
if (!path.isEmpty() && '/'==path[0])
{
TQString home(TQDir::homeDirPath());
if(path.startsWith(home))
{
unsigned int len = home.length();
if(path.length() == len || path[len] == '/')
return path.replace(0, len, TQString::fromLatin1("~"));
}
}
return path;
}
TQString KXftConfig::expandHome(TQString path)
{
if(!path.isEmpty() && '~'==path[0])
return 1==path.length() ? TQDir::homeDirPath() : path.replace(0, 1, TQDir::homeDirPath());
return path;
}
static int point2Pixel(double point)
{
return (int)(((point*TQPaintDevice::x11AppDpiY())/72.0)+0.5);
}
static int pixel2Point(double pixel)
{
return (int)(((pixel*72.0)/(double)TQPaintDevice::x11AppDpiY())+0.5);
}
static bool equal(double d1, double d2)
{
return (fabs(d1 - d2) < 0.0001);
}
static TQString dirSyntax(const TQString &d)
{
if(!d.isNull())
{
TQString ds(d);
ds.replace("//", "/");
int slashPos=ds.findRev('/');
if(slashPos!=(((int)ds.length())-1))
ds.append('/');
return ds;
}
return d;
}
static TQString xDirSyntax(const TQString &d)
{
if(!d.isNull())
{
TQString ds(d);
int slashPos=ds.findRev('/');
if(slashPos==(((int)ds.length())-1))
ds.remove(slashPos, 1);
return ds;
}
return d;
}
static bool check(const TQString &path, unsigned int fmt, bool checkW=false)
{
KDE_struct_stat info;
TQCString pathC(TQFile::encodeName(path));
return 0==KDE_lstat(pathC, &info) && (info.st_mode&S_IFMT)==fmt && (!checkW || 0==::access(pathC, W_OK));
}
inline bool fExists(const TQString &p)
{
return check(p, S_IFREG, false);
}
inline bool dWritable(const TQString &p)
{
return check(p, S_IFDIR, true);
}
inline bool dExists(const TQString &p)
{
return check(p, S_IFDIR, false);
}
static TQString getDir(const TQString &f)
{
TQString d(f);
int slashPos=d.findRev('/');
if(-1!=slashPos)
d.remove(slashPos+1, d.length());
return dirSyntax(d);
}
static time_t getTimeStamp(const TQString &item)
{
KDE_struct_stat info;
return !item.isNull() && 0==KDE_lstat(TQFile::encodeName(item), &info) ? info.st_mtime : 0;
}
#ifdef HAVE_FONTCONFIG
inline TQString fileSyntax(const TQString &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
//
TQString getConfigFile(bool system)
{
#if (FC_VERSION>=20300)
static const char * constKdeRootFcFile="00kde.conf";
#endif
FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent());
TQStringList files;
FcChar8 *file;
TQString home(dirSyntax(TQDir::homeDirPath()));
while((file=FcStrListNext(list)))
{
TQString 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(TQRegExp("/conf\\.d/?$")) || -1!=f.find(TQRegExp("/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())
{
TQStringList::Iterator it(files.begin()),
end(files.end());
for(; it!=end; ++it)
if(-1!=(*it).find(TQRegExp(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 TQString getEntry(TQDomElement 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)
{
TQDomNode n=element.firstChild();
if(!n.isNull())
{
TQDomElement e = n.toElement();
if(!e.isNull() && type==e.tagName())
return e.text();
}
}
}
return TQString::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(TQPtrList<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(TQPtrList<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 TQString defaultPath("/usr/X11R6/lib/X11/XftConfig");
static const TQString defaultUserFile(".xftconfig");
static const char * constSymEnc="\"glyphs-fontspecific\"";
static const TQString constConfigFiles[]=
{
defaultPath,
"/etc/X11/XftConfig",
TQString::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= TQString(TQDir::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
TQFile 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
TQFile 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);
TQStringList list;
TQStringList::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 *)(TQFile::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\">";
TQString 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(TQFile::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 TQString &d)
{
TQString dir(dirSyntax(d));
if(dExists(dir) && !hasDir(dir))
addItem(m_dirs, dir);
}
void KXftConfig::removeDir(const TQString &d)
{
TQString dir(dirSyntax(d));
removeItem(m_dirs, dir);
}
TQString 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
TQString 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 TQString &d)
{
TQString 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(TQPtrList<ListItem> &list, const TQString &i)
{
ListItem *item;
for(item=list.first(); item; item=list.next())
if(item->str==i)
break;
return item;
}
void KXftConfig::clearList(TQPtrList<ListItem> &list)
{
ListItem *item;
for(item=list.first(); item; item=list.next())
removeItem(list, item);
}
TQStringList KXftConfig::getList(TQPtrList<ListItem> &list)
{
TQStringList res;
ListItem *item;
for(item=list.first(); item; item=list.next())
if(!item->toBeRemoved)
res.append(item->str);
return res;
}
void KXftConfig::addItem(TQPtrList<ListItem> &list, const TQString &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(TQPtrList<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
TQDomNode n = m_doc.documentElement().firstChild();
while(!n.isNull())
{
TQDomElement 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())
{
TQString str;
switch(e.childNodes().count())
{
case 1:
if(m_required&SubPixelType && "font"==e.attribute("target"))
{
TQDomElement 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;
TQDomNode en=e.firstChild();
TQString family;
double from=-1.0,
to=-1.0,
pixelFrom=-1.0,
pixelTo=-1.0;
while(!en.isNull())
{
TQDomElement 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())
{
TQDomElement newNode = m_doc.createElement("dir");
TQDomText 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()
{
TQDomElement matchNode = m_doc.createElement("match"),
typeNode = m_doc.createElement("const"),
editNode = m_doc.createElement("edit");
TQDomText 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
{
TQDomElement matchNode = m_doc.createElement("match"),
typeNode = m_doc.createElement("const"),
editNode = m_doc.createElement("edit");
TQDomText 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()
{
TQDomElement matchNode = m_doc.createElement("match"),
typeNode = m_doc.createElement("bool"),
editNode = m_doc.createElement("edit");
TQDomText 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
{
TQString fromString,
toString;
fromString.setNum(range.from);
toString.setNum(range.to);
TQDomElement 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");
TQDomText 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(TQPtrList<ListItem> &list)
{
ListItem *item;
TQDomElement 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 TQString &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 TQString &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()
{
TQDomElement matchNode = m_doc.createElement("match"),
typeNode = m_doc.createElement("bool"),
editNode = m_doc.createElement("edit");
TQDomText 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 )
{
TQSettings().writeEntry("/qt/useXft", set);
if (set)
TQSettings().writeEntry("/qt/enableXft", set);
}
bool KXftConfig::getAntiAliasing() const
{
return TQSettings().readBoolEntry("/qt/useXft");
}
#endif