forked from Qortal/Brooklyn
890 lines
30 KiB
C++
890 lines
30 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "FontInst.h"
|
|
#include "Fc.h"
|
|
#include "Misc.h"
|
|
#include "Utils.h"
|
|
#include "WritingSystems.h"
|
|
#include "fontinstadaptor.h"
|
|
#include "kfontinst_debug.h"
|
|
|
|
#include <KAuth>
|
|
#include <QDebug>
|
|
#include <QTimer>
|
|
#include <fontconfig/fontconfig.h>
|
|
#include <kio/global.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
namespace KFI
|
|
{
|
|
static void decompose(const QString &name, QString &family, QString &style)
|
|
{
|
|
int commaPos = name.lastIndexOf(',');
|
|
|
|
family = -1 == commaPos ? name : name.left(commaPos);
|
|
style = -1 == commaPos ? KFI_WEIGHT_REGULAR : name.mid(commaPos + 2);
|
|
}
|
|
|
|
static bool isSystem = false;
|
|
static Folder theFolders[FontInst::FOLDER_COUNT];
|
|
static const int constSystemReconfigured = -1;
|
|
static const int constConnectionsTimeout = 30 * 1000;
|
|
static const int constFontListTimeout = 10 * 1000;
|
|
|
|
typedef void (*SignalHandler)(int);
|
|
|
|
static void registerSignalHandler(SignalHandler handler)
|
|
{
|
|
if (!handler) {
|
|
handler = SIG_DFL;
|
|
}
|
|
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
|
|
#ifdef SIGSEGV
|
|
signal(SIGSEGV, handler);
|
|
sigaddset(&mask, SIGSEGV);
|
|
#endif
|
|
#ifdef SIGFPE
|
|
signal(SIGFPE, handler);
|
|
sigaddset(&mask, SIGFPE);
|
|
#endif
|
|
#ifdef SIGILL
|
|
signal(SIGILL, handler);
|
|
sigaddset(&mask, SIGILL);
|
|
#endif
|
|
#ifdef SIGABRT
|
|
signal(SIGABRT, handler);
|
|
sigaddset(&mask, SIGABRT);
|
|
#endif
|
|
|
|
sigprocmask(SIG_UNBLOCK, &mask, nullptr);
|
|
}
|
|
|
|
void signalHander(int)
|
|
{
|
|
static bool inHandler = false;
|
|
|
|
if (!inHandler) {
|
|
inHandler = true;
|
|
theFolders[isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER].saveDisabled();
|
|
inHandler = false;
|
|
}
|
|
}
|
|
|
|
FontInst::FontInst()
|
|
{
|
|
isSystem = Misc::root();
|
|
registerTypes();
|
|
|
|
new FontinstAdaptor(this);
|
|
QDBusConnection bus = QDBusConnection::sessionBus();
|
|
|
|
// qDebug() << "Connecting to session bus";
|
|
if (!bus.registerService(OrgKdeFontinstInterface::staticInterfaceName())) {
|
|
// qDebug() << "Failed to register service!";
|
|
::exit(-1);
|
|
}
|
|
if (!bus.registerObject(FONTINST_PATH, this)) {
|
|
// qDebug() << "Failed to register object!";
|
|
::exit(-1);
|
|
}
|
|
|
|
registerSignalHandler(signalHander);
|
|
m_connectionsTimer = new QTimer(this);
|
|
m_fontListTimer = new QTimer(this);
|
|
connect(m_connectionsTimer, &QTimer::timeout, this, &FontInst::connectionsTimeout);
|
|
connect(m_fontListTimer, &QTimer::timeout, this, &FontInst::fontListTimeout);
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
theFolders[i].init(FOLDER_SYS == i, isSystem);
|
|
}
|
|
|
|
updateFontList(false);
|
|
}
|
|
|
|
FontInst::~FontInst()
|
|
{
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
theFolders[i].saveDisabled();
|
|
}
|
|
}
|
|
|
|
void FontInst::list(int folders, int pid)
|
|
{
|
|
// qDebug() << folders << pid;
|
|
|
|
m_connections.insert(pid);
|
|
updateFontList(false);
|
|
QList<KFI::Families> fonts;
|
|
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
if (0 == folders || folders & (1 << i)) {
|
|
fonts += theFolders[i].list();
|
|
}
|
|
}
|
|
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
Q_EMIT fontList(pid, fonts);
|
|
}
|
|
|
|
void FontInst::statFont(const QString &name, int folders, int pid)
|
|
{
|
|
// qDebug() << name << folders << pid;
|
|
|
|
bool checkSystem = 0 == folders || folders & SYS_MASK || isSystem, checkUser = 0 == folders || (folders & USR_MASK && !isSystem);
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
|
|
m_connections.insert(pid);
|
|
if ((checkSystem && findFont(name, FOLDER_SYS, fam, st)) || (checkUser && findFont(name, FOLDER_USER, fam, st, !checkSystem))) {
|
|
Family rv((*fam).name());
|
|
rv.add(*st);
|
|
// qDebug() << "Found font, Q_EMIT details...";
|
|
Q_EMIT fontStat(pid, rv);
|
|
} else {
|
|
// qDebug() << "Font not found, Q_EMIT empty details...";
|
|
Q_EMIT fontStat(pid, Family(name));
|
|
}
|
|
}
|
|
|
|
void FontInst::install(const QString &file, bool createAfm, bool toSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << file << createAfm << toSystem << pid << checkConfig;
|
|
|
|
m_connections.insert(pid);
|
|
|
|
if (checkConfig) {
|
|
updateFontList();
|
|
}
|
|
|
|
EFolder folder = isSystem || toSystem ? FOLDER_SYS : FOLDER_USER;
|
|
Family font;
|
|
Utils::EFileType type = Utils::check(file, font);
|
|
|
|
int result = Utils::FILE_BITMAP == type && !FC::bitmapsEnabled() ? STATUS_BITMAPS_DISABLED : Utils::FILE_INVALID == type ? STATUS_NOT_FONT_FILE : STATUS_OK;
|
|
|
|
if (STATUS_OK == result) {
|
|
if (Utils::FILE_AFM != type && Utils::FILE_PFM != type) {
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT) && STATUS_OK == result; ++i) {
|
|
if (theFolders[i].contains(font.name(), (*font.styles().begin()).value())) {
|
|
result = STATUS_ALREADY_INSTALLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
QString name(Misc::modifyName(Misc::getFile(file))), destFolder(Misc::getDestFolder(theFolders[folder].location(), name));
|
|
|
|
result = Utils::FILE_AFM != type && Utils::FILE_PFM != type && Misc::fExists(destFolder + name) ? (int)KIO::ERR_FILE_ALREADY_EXIST : (int)STATUS_OK;
|
|
if (STATUS_OK == result) {
|
|
if (toSystem && !isSystem) {
|
|
// qDebug() << "Send request to system helper" << file << destFolder << name;
|
|
QVariantMap args;
|
|
args["method"] = "install";
|
|
args["file"] = file;
|
|
args["name"] = name;
|
|
args["destFolder"] = destFolder;
|
|
args["createAfm"] = createAfm;
|
|
args["type"] = (int)type;
|
|
result = performAction(args);
|
|
} else {
|
|
if (!Misc::dExists(destFolder)) {
|
|
result = Misc::createDir(destFolder) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
result = QFile::copy(file, destFolder + name) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
Misc::setFilePerms(QFile::encodeName(destFolder + name));
|
|
if ((Utils::FILE_SCALABLE == type || Utils::FILE_PFM == type) && createAfm) {
|
|
Utils::createAfm(destFolder + name, type);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (STATUS_OK == result && Utils::FILE_AFM != type && Utils::FILE_PFM != type) {
|
|
StyleCont::ConstIterator st(font.styles().begin());
|
|
FileCont::ConstIterator f((*st).files().begin());
|
|
File df(destFolder + name, (*f).foundry(), (*f).index());
|
|
|
|
(*st).clearFiles();
|
|
(*st).add(df);
|
|
theFolders[folder].add(font);
|
|
theFolders[folder].addModifiedDir(destFolder);
|
|
Q_EMIT fontsAdded(Families(font, FOLDER_SYS == folder));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Q_EMIT status(pid, result);
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
}
|
|
|
|
void FontInst::uninstall(const QString &family, quint32 style, bool fromSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << family << style << fromSystem << pid << checkConfig;
|
|
|
|
m_connections.insert(pid);
|
|
|
|
if (checkConfig) {
|
|
updateFontList();
|
|
}
|
|
|
|
EFolder folder = isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER;
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
|
|
|
|
if (STATUS_OK == result) {
|
|
Family del((*fam).name());
|
|
Style s((*st).value(), (*st).scalable(), (*st).writingSystems());
|
|
FileCont files((*st).files());
|
|
FileCont::ConstIterator it(files.begin()), end(files.end());
|
|
|
|
if (fromSystem && !isSystem) {
|
|
QStringList fileList;
|
|
bool wasDisabled(false);
|
|
|
|
for (; it != end; ++it) {
|
|
fileList.append((*it).path());
|
|
theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*it).path()));
|
|
if (!wasDisabled && Misc::isHidden(Misc::getFile((*it).path()))) {
|
|
wasDisabled = true;
|
|
}
|
|
}
|
|
QVariantMap args;
|
|
args["method"] = "uninstall";
|
|
args["files"] = fileList;
|
|
result = performAction(args);
|
|
|
|
if (STATUS_OK == result) {
|
|
FileCont empty;
|
|
s.setFiles(files);
|
|
(*st).setFiles(empty);
|
|
if (wasDisabled) {
|
|
theFolders[FOLDER_SYS].setDisabledDirty();
|
|
}
|
|
}
|
|
} else {
|
|
for (; it != end; ++it) {
|
|
if (!Misc::fExists((*it).path()) || QFile::remove((*it).path())) {
|
|
// Also remove any AFM or PFM files...
|
|
QStringList other;
|
|
Misc::getAssociatedFiles((*it).path(), other);
|
|
QStringList::ConstIterator oit(other.constBegin()), oend(other.constEnd());
|
|
for (; oit != oend; ++oit) {
|
|
QFile::remove(*oit);
|
|
}
|
|
|
|
theFolders[folder].addModifiedDir(Misc::getDir((*it).path()));
|
|
(*st).remove(*it);
|
|
s.add(*it);
|
|
if (!theFolders[folder].disabledDirty() && Misc::isHidden(Misc::getFile((*it).path()))) {
|
|
theFolders[folder].setDisabledDirty();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
if ((*st).files().isEmpty()) {
|
|
(*fam).remove(*st);
|
|
if ((*fam).styles().isEmpty()) {
|
|
theFolders[folder].removeFont(*fam);
|
|
}
|
|
} else {
|
|
result = STATUS_PARTIAL_DELETE;
|
|
}
|
|
del.add(s);
|
|
}
|
|
Q_EMIT fontsRemoved(Families(del, FOLDER_SYS == folder));
|
|
}
|
|
// qDebug() << "status" << result;
|
|
Q_EMIT status(pid, result);
|
|
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
}
|
|
|
|
void FontInst::uninstall(const QString &name, bool fromSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << name << fromSystem << pid << checkConfig;
|
|
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
if (findFont(name, fromSystem || isSystem ? FOLDER_SYS : FOLDER_USER, fam, st)) {
|
|
uninstall((*fam).name(), (*st).value(), fromSystem, pid, checkConfig);
|
|
} else {
|
|
Q_EMIT status(pid, KIO::ERR_DOES_NOT_EXIST);
|
|
}
|
|
}
|
|
|
|
void FontInst::move(const QString &family, quint32 style, bool toSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << family << style << toSystem << pid << checkConfig;
|
|
|
|
m_connections.insert(pid);
|
|
if (checkConfig) {
|
|
updateFontList();
|
|
}
|
|
|
|
if (isSystem) {
|
|
Q_EMIT status(pid, KIO::ERR_UNSUPPORTED_ACTION);
|
|
} else {
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
EFolder from = toSystem ? FOLDER_USER : FOLDER_SYS, to = toSystem ? FOLDER_SYS : FOLDER_USER;
|
|
|
|
if (findFont(family, style, from, fam, st)) {
|
|
FileCont::ConstIterator it((*st).files().begin()), end((*st).files().end());
|
|
QStringList files;
|
|
|
|
for (; it != end; ++it) {
|
|
files.append((*it).path());
|
|
theFolders[from].addModifiedDir(Misc::getDir((*it).path()));
|
|
// Actual 'to' folder does not really matter, as we only want to call fc-cache
|
|
// ...actual folders only matter for xreating fonts.dir, etc, and we wont be doing this...
|
|
theFolders[to].addModifiedDir(theFolders[to].location());
|
|
}
|
|
|
|
QVariantMap args;
|
|
args["method"] = "move";
|
|
args["files"] = files;
|
|
args["toSystem"] = toSystem;
|
|
args["dest"] = theFolders[to].location();
|
|
args["uid"] = getuid();
|
|
args["gid"] = getgid();
|
|
int result = performAction(args);
|
|
|
|
if (STATUS_OK == result) {
|
|
updateFontList();
|
|
}
|
|
Q_EMIT status(pid, result);
|
|
} else {
|
|
// qDebug() << "does not exist";
|
|
Q_EMIT status(pid, KIO::ERR_DOES_NOT_EXIST);
|
|
}
|
|
}
|
|
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
}
|
|
|
|
static bool renameFontFile(const QString &from, const QString &to, int uid = -1, int gid = -1)
|
|
{
|
|
if (!QFile::rename(from, to)) {
|
|
return false;
|
|
}
|
|
|
|
QByteArray dest(QFile::encodeName(to));
|
|
Misc::setFilePerms(dest);
|
|
if (-1 != uid && -1 != gid) {
|
|
::chown(dest.data(), uid, gid);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FontInst::enable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << family << style << inSystem << pid << checkConfig;
|
|
toggle(true, family, style, inSystem, pid, checkConfig);
|
|
}
|
|
|
|
void FontInst::disable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << family << style << inSystem << pid << checkConfig;
|
|
toggle(false, family, style, inSystem, pid, checkConfig);
|
|
}
|
|
|
|
void FontInst::removeFile(const QString &family, quint32 style, const QString &file, bool fromSystem, int pid, bool checkConfig)
|
|
{
|
|
// qDebug() << family << style << file << fromSystem << pid << checkConfig;
|
|
|
|
m_connections.insert(pid);
|
|
|
|
if (checkConfig) {
|
|
updateFontList();
|
|
}
|
|
|
|
// First find the family/style
|
|
EFolder folder = isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER;
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
|
|
|
|
if (STATUS_OK == result) {
|
|
// Family/style found - now check that the requested file is *within* the same folder as one
|
|
// of the files linked to this font...
|
|
FileCont files((*st).files());
|
|
FileCont::ConstIterator it(files.begin()), end(files.end());
|
|
QString dir(Misc::getDir(file));
|
|
|
|
result = KIO::ERR_DOES_NOT_EXIST;
|
|
for (; it != end && STATUS_OK != result; ++it) {
|
|
if (Misc::getDir((*it).path()) == dir) {
|
|
result = STATUS_OK;
|
|
}
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
// OK, found folder - so can now proceed to delete the file...
|
|
if (fromSystem && !isSystem) {
|
|
QVariantMap args;
|
|
args["method"] = "removeFile";
|
|
args["file"] = file;
|
|
result = performAction(args);
|
|
} else {
|
|
result = Misc::fExists(file) ? QFile::remove(file) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED : (int)KIO::ERR_DOES_NOT_EXIST;
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
theFolders[folder].addModifiedDir(dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
Q_EMIT status(pid, result);
|
|
}
|
|
|
|
void FontInst::reconfigure(int pid, bool force)
|
|
{
|
|
// qDebug() << pid << force;
|
|
bool sysModified(theFolders[FOLDER_SYS].isModified());
|
|
|
|
saveDisabled();
|
|
|
|
// qDebug() << theFolders[FOLDER_USER].isModified() << sysModified;
|
|
if (!isSystem && (force || theFolders[FOLDER_USER].isModified())) {
|
|
theFolders[FOLDER_USER].configure(force);
|
|
}
|
|
|
|
if (sysModified) {
|
|
if (isSystem) {
|
|
theFolders[FOLDER_SYS].configure();
|
|
} else {
|
|
QVariantMap args;
|
|
args["method"] = "reconfigure";
|
|
performAction(args);
|
|
theFolders[FOLDER_SYS].clearModified();
|
|
}
|
|
}
|
|
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
|
|
updateFontList();
|
|
Q_EMIT status(pid, isSystem ? constSystemReconfigured : STATUS_OK);
|
|
}
|
|
|
|
QString FontInst::folderName(bool sys)
|
|
{
|
|
return theFolders[sys || isSystem ? FOLDER_SYS : FOLDER_USER].location();
|
|
}
|
|
|
|
void FontInst::saveDisabled()
|
|
{
|
|
if (isSystem) {
|
|
theFolders[FOLDER_SYS].saveDisabled();
|
|
} else {
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
if (FOLDER_SYS == i && !isSystem) {
|
|
if (theFolders[i].disabledDirty()) {
|
|
QVariantMap args;
|
|
args["method"] = "saveDisabled";
|
|
performAction(args);
|
|
theFolders[i].saveDisabled();
|
|
}
|
|
} else {
|
|
theFolders[i].saveDisabled();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontInst::connectionsTimeout()
|
|
{
|
|
bool canExit(true);
|
|
|
|
// qDebug() << "exiting";
|
|
checkConnections();
|
|
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
if (theFolders[i].disabledDirty()) {
|
|
canExit = false;
|
|
}
|
|
theFolders[i].saveDisabled();
|
|
}
|
|
|
|
if (0 == m_connections.count()) {
|
|
if (canExit) {
|
|
qApp->exit(0);
|
|
} else { // Try again later...
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontInst::fontListTimeout()
|
|
{
|
|
updateFontList(true);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
}
|
|
|
|
void FontInst::updateFontList(bool emitChanges)
|
|
{
|
|
// For some reason just the "!FcConfigUptoDate(0)" check does not always work :-(
|
|
FcBool fcModified = !FcConfigUptoDate(nullptr);
|
|
|
|
if (fcModified || theFolders[FOLDER_SYS].fonts().isEmpty() || (!isSystem && theFolders[FOLDER_USER].fonts().isEmpty())
|
|
|| theFolders[FOLDER_SYS].disabledDirty() || (!isSystem && theFolders[FOLDER_USER].disabledDirty())) {
|
|
// qDebug() << "Need to refresh font lists";
|
|
if (fcModified) {
|
|
// qDebug() << "Re-init FC";
|
|
if (!FcInitReinitialize()) {
|
|
// qDebug() << "Re-init failed????";
|
|
}
|
|
}
|
|
|
|
Folder::Flat old[FOLDER_COUNT];
|
|
|
|
if (emitChanges) {
|
|
// qDebug() << "Flatten existing font lists";
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
old[i] = theFolders[i].flatten();
|
|
}
|
|
}
|
|
|
|
saveDisabled();
|
|
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
theFolders[i].clearFonts();
|
|
}
|
|
|
|
// qDebug() << "update list of fonts";
|
|
|
|
FcPattern *pat = FcPatternCreate();
|
|
FcObjectSet *os = FcObjectSetBuild(FC_FILE,
|
|
FC_FAMILY,
|
|
FC_FAMILYLANG,
|
|
FC_WEIGHT,
|
|
FC_LANG,
|
|
FC_CHARSET,
|
|
FC_SCALABLE,
|
|
#ifndef KFI_FC_NO_WIDTHS
|
|
FC_WIDTH,
|
|
#endif
|
|
FC_SLANT,
|
|
FC_INDEX,
|
|
FC_FOUNDRY,
|
|
(void *)nullptr);
|
|
|
|
FcFontSet *list = FcFontList(nullptr, pat, os);
|
|
|
|
FcPatternDestroy(pat);
|
|
FcObjectSetDestroy(os);
|
|
|
|
theFolders[FOLDER_SYS].loadDisabled();
|
|
if (!isSystem) {
|
|
theFolders[FOLDER_USER].loadDisabled();
|
|
}
|
|
|
|
if (list) {
|
|
QString home(Misc::dirSyntax(QDir::homePath()));
|
|
|
|
for (int i = 0; i < list->nfont; i++) {
|
|
QString fileName(Misc::fileSyntax(FC::getFcString(list->fonts[i], FC_FILE)));
|
|
|
|
if (!fileName.isEmpty() && Misc::fExists(fileName)) // && 0!=fileName.indexOf(constDefomaLocation))
|
|
{
|
|
QString family, foundry;
|
|
quint32 styleVal;
|
|
int index;
|
|
qulonglong writingSystems(WritingSystems::instance()->get(list->fonts[i]));
|
|
FcBool scalable = FcFalse;
|
|
|
|
if (FcResultMatch != FcPatternGetBool(list->fonts[i], FC_SCALABLE, 0, &scalable)) {
|
|
scalable = FcFalse;
|
|
}
|
|
|
|
FC::getDetails(list->fonts[i], family, styleVal, index, foundry);
|
|
FamilyCont::ConstIterator fam = theFolders[isSystem || 0 != fileName.indexOf(home) ? FOLDER_SYS : FOLDER_USER].addFont(Family(family));
|
|
StyleCont::ConstIterator style = (*fam).add(Style(styleVal));
|
|
FileCont::ConstIterator file = (*style).add(File(fileName, foundry, index));
|
|
Q_UNUSED(file);
|
|
|
|
(*style).setWritingSystems((*style).writingSystems() | writingSystems);
|
|
if (scalable) {
|
|
(*style).setScalable();
|
|
}
|
|
}
|
|
}
|
|
|
|
FcFontSetDestroy(list);
|
|
}
|
|
|
|
if (emitChanges) {
|
|
// qDebug() << "Look for differences";
|
|
for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
|
|
// qDebug() << "Flatten, and take copies...";
|
|
Folder::Flat newList = theFolders[i].flatten(), onlyNew = newList;
|
|
|
|
// qDebug() << "Determine differences...";
|
|
onlyNew.subtract(old[i]);
|
|
old[i].subtract(newList);
|
|
|
|
// qDebug() << "Emit changes...";
|
|
Families families = onlyNew.build(isSystem || i == FOLDER_SYS);
|
|
|
|
if (!families.items.isEmpty()) {
|
|
Q_EMIT fontsAdded(families);
|
|
}
|
|
|
|
families = old[i].build(isSystem || i == FOLDER_SYS);
|
|
if (!families.items.isEmpty()) {
|
|
Q_EMIT fontsRemoved(families);
|
|
}
|
|
}
|
|
}
|
|
// qDebug() << "updated list of fonts";
|
|
}
|
|
}
|
|
|
|
void FontInst::toggle(bool enable, const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
|
|
{
|
|
m_connections.insert(pid);
|
|
|
|
if (checkConfig) {
|
|
updateFontList();
|
|
}
|
|
|
|
EFolder folder = isSystem || inSystem ? FOLDER_SYS : FOLDER_USER;
|
|
FamilyCont::ConstIterator fam;
|
|
StyleCont::ConstIterator st;
|
|
int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
|
|
|
|
if (STATUS_OK == result) {
|
|
FileCont files((*st).files()), toggledFiles;
|
|
FileCont::ConstIterator it(files.begin()), end(files.end());
|
|
QHash<File, QString> movedFonts;
|
|
QHash<QString, QString> movedAssoc;
|
|
QSet<QString> modifiedDirs;
|
|
|
|
for (; it != end && STATUS_OK == result; ++it) {
|
|
QString to = Misc::getDir((*it).path()) + QString(enable ? Misc::unhide(Misc::getFile((*it).path())) : Misc::hide(Misc::getFile((*it).path())));
|
|
if (to != (*it).path()) {
|
|
// qDebug() << "MOVE:" << (*it).path() << " to " << to;
|
|
// If the font is a system font, and we're not root, then just go through the actions here - so
|
|
// that we can build the list of changes that would happen...
|
|
if ((inSystem && !isSystem) || renameFontFile((*it).path(), to)) {
|
|
modifiedDirs.insert(Misc::getDir(enable ? to : (*it).path()));
|
|
toggledFiles.insert(File(to, (*it).foundry(), (*it).index()));
|
|
// Now try to move an associated AFM or PFM files...
|
|
QStringList assoc;
|
|
|
|
movedFonts[*it] = to;
|
|
Misc::getAssociatedFiles((*it).path(), assoc);
|
|
|
|
QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd());
|
|
|
|
for (; ait != aend && STATUS_OK == result; ++ait) {
|
|
to = Misc::getDir(*ait) + QString(enable ? Misc::unhide(Misc::getFile(*ait)) : Misc::hide(Misc::getFile(*ait)));
|
|
|
|
if (to != *ait) {
|
|
if ((inSystem && !isSystem) || renameFontFile(*ait, to)) {
|
|
movedAssoc[*ait] = to;
|
|
} else {
|
|
result = KIO::ERR_WRITE_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result = KIO::ERR_WRITE_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inSystem && !isSystem) {
|
|
Family toggleFam((*fam).name());
|
|
|
|
toggleFam.add(*st);
|
|
QVariantMap args;
|
|
args["method"] = "toggle";
|
|
QString xml;
|
|
QTextStream str(&xml);
|
|
toggleFam.toXml(false, str);
|
|
args["xml"] = xml;
|
|
args["enable"] = enable;
|
|
result = performAction(args);
|
|
}
|
|
|
|
if (STATUS_OK == result) {
|
|
Family addFam((*fam).name()), delFam((*fam).name());
|
|
Style addStyle((*st).value(), (*st).scalable(), (*st).writingSystems()), delStyle((*st).value(), (*st).scalable(), (*st).writingSystems());
|
|
|
|
addStyle.setFiles(toggledFiles);
|
|
addFam.add(addStyle);
|
|
delStyle.setFiles(files);
|
|
delFam.add(delStyle);
|
|
(*st).setFiles(toggledFiles);
|
|
|
|
theFolders[folder].addModifiedDirs(modifiedDirs);
|
|
Q_EMIT fontsAdded(Families(addFam, FOLDER_SYS == folder));
|
|
Q_EMIT fontsRemoved(Families(delFam, FOLDER_SYS == folder));
|
|
|
|
theFolders[folder].setDisabledDirty();
|
|
} else // un-move fonts!
|
|
{
|
|
QHash<File, QString>::ConstIterator fit(movedFonts.constBegin()), fend(movedFonts.constEnd());
|
|
QHash<QString, QString>::ConstIterator ait(movedAssoc.constBegin()), aend(movedAssoc.constEnd());
|
|
|
|
for (; fit != fend; ++fit) {
|
|
renameFontFile(fit.value(), fit.key().path());
|
|
}
|
|
for (; ait != aend; ++ait) {
|
|
renameFontFile(ait.value(), ait.key());
|
|
}
|
|
}
|
|
}
|
|
Q_EMIT status(pid, result);
|
|
|
|
m_connectionsTimer->start(constConnectionsTimeout);
|
|
m_fontListTimer->start(constFontListTimeout);
|
|
}
|
|
|
|
void FontInst::addModifedSysFolders(const Family &family)
|
|
{
|
|
StyleCont::ConstIterator style(family.styles().begin()), styleEnd(family.styles().end());
|
|
|
|
for (; style != styleEnd; ++style) {
|
|
FileCont::ConstIterator file((*style).files().begin()), fileEnd((*style).files().end());
|
|
|
|
for (; file != fileEnd; ++file) {
|
|
theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*file).path()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FontInst::checkConnections()
|
|
{
|
|
QSet<int>::ConstIterator it(m_connections.begin()), end(m_connections.end());
|
|
QSet<int> remove;
|
|
|
|
for (; it != end; ++it) {
|
|
if (0 != kill(*it, 0)) {
|
|
remove.insert(*it);
|
|
}
|
|
}
|
|
m_connections.subtract(remove);
|
|
}
|
|
|
|
bool FontInst::findFontReal(const QString &family, const QString &style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st)
|
|
{
|
|
Family f(family);
|
|
fam = theFolders[folder].fonts().find(f);
|
|
if (theFolders[folder].fonts().end() == fam) {
|
|
return false;
|
|
}
|
|
|
|
StyleCont::ConstIterator end((*fam).styles().end());
|
|
for (st = (*fam).styles().begin(); st != end; ++st) {
|
|
if (FC::createStyleName((*st).value()) == style) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FontInst::findFont(const QString &font, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList)
|
|
{
|
|
QString family, style;
|
|
|
|
decompose(font, family, style);
|
|
|
|
if (!findFontReal(family, style, folder, fam, st)) {
|
|
if (updateList) {
|
|
// Not found, so refresh font list and try again...
|
|
updateFontList();
|
|
return findFontReal(family, style, folder, fam, st);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FontInst::findFontReal(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st)
|
|
{
|
|
fam = theFolders[folder].fonts().find(Family(family));
|
|
|
|
if (theFolders[folder].fonts().end() == fam) {
|
|
return false;
|
|
} else {
|
|
st = (*fam).styles().find(style);
|
|
|
|
return (*fam).styles().end() != st;
|
|
}
|
|
}
|
|
|
|
bool FontInst::findFont(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList)
|
|
{
|
|
if (!findFontReal(family, style, folder, fam, st)) {
|
|
if (updateList) {
|
|
// Not found, so refresh font list and try again...
|
|
updateFontList();
|
|
return findFontReal(family, style, folder, fam, st);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int FontInst::performAction(const QVariantMap &args)
|
|
{
|
|
KAuth::Action action("org.kde.fontinst.manage");
|
|
|
|
action.setHelperId("org.kde.fontinst");
|
|
action.setArguments(args);
|
|
// qDebug() << "Call " << args["method"].toString() << " on helper";
|
|
m_fontListTimer->stop();
|
|
m_connectionsTimer->stop();
|
|
|
|
KAuth::ExecuteJob *j = action.execute();
|
|
j->exec();
|
|
if (j->error()) {
|
|
qCWarning(KFONTINST_DEBUG) << "kauth action failed" << j->errorString() << j->errorText();
|
|
// error is a KAuth::ActionReply::Error rest of this code expects KIO error codes which are extended by EStatus
|
|
switch (j->error()) {
|
|
case KAuth::ActionReply::Error::UserCancelledError:
|
|
return KIO::ERR_USER_CANCELED;
|
|
case KAuth::ActionReply::Error::AuthorizationDeniedError:
|
|
/*fall through*/
|
|
case KAuth::ActionReply::Error::NoSuchActionError:
|
|
return KIO::ERR_CANNOT_AUTHENTICATE;
|
|
default:
|
|
return KIO::ERR_INTERNAL;
|
|
}
|
|
return KIO::ERR_INTERNAL;
|
|
}
|
|
// qDebug() << "Success!";
|
|
return STATUS_OK;
|
|
}
|
|
|
|
}
|