diff --git a/kcms/kfontinst/kcmfontinst/FontsPackage.cpp b/kcms/kfontinst/kcmfontinst/FontsPackage.cpp index e590eaf76..01af2c82e 100644 --- a/kcms/kfontinst/kcmfontinst/FontsPackage.cpp +++ b/kcms/kfontinst/kcmfontinst/FontsPackage.cpp @@ -1,97 +1,97 @@ /* * KFontInst - KDE Font Installer * * Copyright 2009 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include "FontsPackage.h" #include "KfiConstants.h" #include "Misc.h" namespace KFI { namespace FontsPackage { QSet extract(const QString &fileName, QTemporaryDir **tempDir) { QSet urls; if(!tempDir) return urls; KZip zip(fileName); if(zip.open(QIODevice::ReadOnly)) { const KArchiveDirectory *zipDir=zip.directory(); if(zipDir) { QStringList fonts(zipDir->entries()); if(fonts.count()) { QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); for(; it!=end; ++it) { const KArchiveEntry *entry=zipDir->entry(*it); if(entry && entry->isFile()) { if(!(*tempDir)) { (*tempDir)=new QTemporaryDir(QDir::tempPath() + "/" KFI_TMP_DIR_PREFIX); (*tempDir)->setAutoRemove(true); } ((KArchiveFile *)entry)->copyTo((*tempDir)->path()); QString name(entry->name()); // // Cant install hidden fonts, therefore need to // unhide 1st! if(Misc::isHidden(name)) { - ::rename(QFile::encodeName((*tempDir)->path()+QLatin1Char('/')+name).data(), - QFile::encodeName((*tempDir)->path()+QLatin1Char('/')+name.mid(1)).data()); + ::rename(QFile::encodeName((*tempDir)->filePath(name)).data(), + QFile::encodeName((*tempDir)->filePath(name.mid(1))).data()); name=name.mid(1); } - urls.insert(QUrl((*tempDir)->path()+QLatin1Char('/')+name)); + urls.insert(QUrl((*tempDir)->filePath(name))); } } } } } return urls; } } } diff --git a/kcms/kfontinst/kcmfontinst/JobRunner.cpp b/kcms/kfontinst/kcmfontinst/JobRunner.cpp index e3760d98f..66b6101f5 100644 --- a/kcms/kfontinst/kcmfontinst/JobRunner.cpp +++ b/kcms/kfontinst/kcmfontinst/JobRunner.cpp @@ -1,807 +1,807 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2007 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "JobRunner.h" #include "KfiConstants.h" #include "Misc.h" #include "Fc.h" #include "ActionLabel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config-fontinst.h" #define CFG_GROUP "Runner Dialog" #define CFG_DONT_SHOW_FINISHED_MSG "DontShowFinishedMsg" namespace KFI { Q_GLOBAL_STATIC(FontInstInterface, theInterface) FontInstInterface * CJobRunner::dbus() { return theInterface; } QString CJobRunner::folderName(bool sys) { if(!theInterface) return QString(); QDBusPendingReply reply=theInterface->folderName(sys); reply.waitForFinished(); return reply.isError() ? QString() : reply.argumentAt<0>(); } void CJobRunner::startDbusService() { if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(OrgKdeFontinstInterface::staticInterfaceName())) { const QString fontinst = QStringLiteral(KFONTINST_LIB_EXEC_DIR"/fontinst"); qDebug() << "Service " << OrgKdeFontinstInterface::staticInterfaceName() << " not registered, starting" << fontinst; QProcess::startDetached(fontinst, QStringList()); } } static const int constDownloadFailed=-1; static const int constInterfaceCheck=5*1000; static void decode(const QUrl &url, Misc::TFont &font, bool &system) { font=FC::decode(url); QUrlQuery query(url); system = (query.hasQueryItem("sys") && query.queryItemValue("sys") == QStringLiteral("true")); } QUrl CJobRunner::encode(const QString &family, quint32 style, bool system) { QUrl url(FC::encode(family, style)); url.addQueryItem("sys", system ? "true" : "false"); return url; } enum EPages { PAGE_PROGRESS, PAGE_SKIP, PAGE_ERROR, PAGE_CANCEL, PAGE_COMPLETE }; enum Response { RESP_CONTINUE, RESP_AUTO, RESP_CANCEL }; static void addIcon(QGridLayout *layout, QFrame *page, const char *iconName, int iconSize) { QLabel *icon=new QLabel(page); icon->setPixmap(QIcon::fromTheme(iconName).pixmap(iconSize)); icon->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); layout->addWidget(icon, 0, 0); } CJobRunner::CJobRunner(QWidget *parent, int xid) : QDialog(parent), itsIt(itsUrls.end()), itsEnd(itsIt), itsAutoSkip(false), itsCancelClicked(false), itsModified(false), itsTempDir(0L) { setModal(true); if(NULL==parent && 0!=xid) XSetTransientForHint(QX11Info::display(), winId(), xid); itsButtonBox = new QDialogButtonBox; connect(itsButtonBox, &QDialogButtonBox::clicked, this, &CJobRunner::slotButtonClicked); itsSkipButton = new QPushButton(i18n("Skip")); itsButtonBox->addButton(itsSkipButton, QDialogButtonBox::ActionRole); itsSkipButton->hide(); itsAutoSkipButton = new QPushButton(i18n("AutoSkip")); itsButtonBox->addButton(itsAutoSkipButton, QDialogButtonBox::ActionRole); itsAutoSkipButton->hide(); itsStack = new QStackedWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(itsStack); mainLayout->addWidget(itsButtonBox); QStyleOption option; option.initFrom(this); int iconSize=style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this); QFrame *page = new QFrame(itsStack); QGridLayout *layout=new QGridLayout(page); itsStatusLabel=new QLabel(page); itsProgress=new QProgressBar(page); // itsStatusLabel->setWordWrap(true); layout->addWidget(itsActionLabel = new CActionLabel(this), 0, 0, 2, 1); layout->addWidget(itsStatusLabel, 0, 1); layout->addWidget(itsProgress, 1, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 2, 0); itsStack->insertWidget(PAGE_PROGRESS, page); page=new QFrame(itsStack); layout=new QGridLayout(page); itsSkipLabel=new QLabel(page); itsSkipLabel->setWordWrap(true); addIcon(layout, page, "dialog-error", iconSize); layout->addWidget(itsSkipLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_SKIP, page); page=new QFrame(itsStack); layout=new QGridLayout(page); itsErrorLabel=new QLabel(page); itsErrorLabel->setWordWrap(true); addIcon(layout, page, "dialog-error", iconSize); layout->addWidget(itsErrorLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_ERROR, page); page=new QFrame(itsStack); layout=new QGridLayout(page); QLabel *cancelLabel=new QLabel(i18n("

Cancel?

Are you sure you wish to cancel?

"), page); cancelLabel->setWordWrap(true); addIcon(layout, page, "dialog-warning", iconSize); layout->addWidget(cancelLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsStack->insertWidget(PAGE_CANCEL, page); if(KSharedConfig::openConfig(KFI_UI_CFG_FILE)->group(CFG_GROUP).readEntry(CFG_DONT_SHOW_FINISHED_MSG, false)) itsDontShowFinishedMsg=0L; else { page=new QFrame(itsStack); layout=new QGridLayout(page); QLabel *finishedLabel=new QLabel(i18n("

Finished

" "

Please note that any open applications will need to be " "restarted in order for any changes to be noticed.

"), page); finishedLabel->setWordWrap(true); addIcon(layout, page, "dialog-information", iconSize); layout->addWidget(finishedLabel, 0, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); itsDontShowFinishedMsg = new QCheckBox(i18n("Do not show this message again"), page); itsDontShowFinishedMsg->setChecked(false); layout->addItem(new QSpacerItem(0, layout->spacing(), QSizePolicy::Fixed, QSizePolicy::Fixed), 2, 0); layout->addWidget(itsDontShowFinishedMsg, 3, 1); layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 4, 0); itsStack->insertWidget(PAGE_COMPLETE, page); } QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(dbusServiceOwnerChanged(QString,QString,QString))); connect(dbus(), SIGNAL(status(int,int)), SLOT(dbusStatus(int,int))); setMinimumSize(420, 160); } CJobRunner::~CJobRunner() { delete itsTempDir; } void CJobRunner::getAssociatedUrls(const QUrl &url, QList &list, bool afmAndPfm, QWidget *widget) { QString ext(url.path()); int dotPos(ext.lastIndexOf('.')); bool check(false); if(-1==dotPos) // Hmm, no extension - check anyway... check=true; else // Cool, got an extension - see if it is a Type1 font... { ext=ext.mid(dotPos+1); check=0==ext.compare("pfa", Qt::CaseInsensitive) || 0==ext.compare("pfb", Qt::CaseInsensitive); } if(check) { const char *afm[]={"afm", "AFM", "Afm", NULL}, *pfm[]={"pfm", "PFM", "Pfm", NULL}; bool gotAfm(false), localFile(url.isLocalFile()); int e; for(e=0; afm[e]; ++e) { QUrl statUrl(url); statUrl.setPath(Misc::changeExt(url.path(), afm[e])); bool urlExists = false; if (localFile) { urlExists = Misc::fExists(statUrl.toLocalFile()); } else { auto job = KIO::stat(statUrl); KJobWidgets::setWindow(job, widget); job->exec(); urlExists = !job->error(); } if (urlExists) { list.append(statUrl); gotAfm=true; break; } } if(afmAndPfm || !gotAfm) for(e=0; pfm[e]; ++e) { QUrl statUrl(url); statUrl.setPath(Misc::changeExt(url.path(), pfm[e])); bool urlExists = false; if (localFile) { urlExists = Misc::fExists(statUrl.toLocalFile()); } else { auto job = KIO::stat(statUrl); KJobWidgets::setWindow(job, widget); job->exec(); urlExists = !job->error(); } if (urlExists) { list.append(statUrl); break; } } } } static void addEnableActions(CJobRunner::ItemList &urls) { CJobRunner::ItemList modified; CJobRunner::ItemList::ConstIterator it(urls.constBegin()), end(urls.constEnd()); for(; it!=end; ++it) { if((*it).isDisabled) { CJobRunner::Item item(*it); item.fileName=QLatin1String("--"); modified.append(item); } modified.append(*it); } urls=modified; } int CJobRunner::exec(ECommand cmd, const ItemList &urls, bool destIsSystem) { itsAutoSkip=itsCancelClicked=itsModified=false; switch(cmd) { case CMD_INSTALL: setWindowTitle(i18n("Installing")); break; case CMD_DELETE: setWindowTitle(i18n("Uninstalling")); break; case CMD_ENABLE: setWindowTitle(i18n("Enabling")); break; case CMD_MOVE: setWindowTitle(i18n("Moving")); break; case CMD_UPDATE: setWindowTitle(i18n("Updating")); itsModified=true; break; case CMD_REMOVE_FILE: setWindowTitle(i18n("Removing")); break; default: case CMD_DISABLE: setWindowTitle(i18n("Disabling")); } itsDestIsSystem=destIsSystem; itsUrls=urls; if(CMD_INSTALL==cmd) qSort(itsUrls.begin(), itsUrls.end()); // Sort list of fonts so that we have type1 fonts followed by their metrics... else if(CMD_MOVE==cmd) addEnableActions(itsUrls); itsIt=itsUrls.constBegin(); itsEnd=itsUrls.constEnd(); itsPrev=itsEnd; itsProgress->setValue(0); itsProgress->setRange(0, itsUrls.count()+1); itsProgress->show(); itsCmd=cmd; itsCurrentFile=QString(); itsStatusLabel->setText(QString()); setPage(PAGE_PROGRESS); QTimer::singleShot(0, this, SLOT(doNext())); QTimer::singleShot(constInterfaceCheck, this, SLOT(checkInterface())); itsActionLabel->startAnimation(); int rv=QDialog::exec(); if(itsTempDir) { delete itsTempDir; itsTempDir=0L; } return rv; } void CJobRunner::doNext() { if(itsIt==itsEnd/* || CMD_UPDATE==itsCmd*/) { if(itsModified) { // Force reconfig if command was already set to update... dbus()->reconfigure(getpid(), CMD_UPDATE==itsCmd); itsCmd=CMD_UPDATE; itsStatusLabel->setText(i18n("Updating font configuration. Please wait...")); itsProgress->setValue(itsProgress->maximum()); emit configuring(); } else { itsActionLabel->stopAnimation(); if(PAGE_ERROR!=itsStack->currentIndex()) reject(); } } else { Misc::TFont font; bool system; switch(itsCmd) { case CMD_INSTALL: { itsCurrentFile=fileName((*itsIt)); if(itsCurrentFile.isEmpty()) // Failed to download... dbusStatus(getpid(), constDownloadFailed); else { // Create AFM if this is a PFM, and the previous was not the AFM for this font... bool createAfm=Item::TYPE1_PFM==(*itsIt).type && (itsPrev==itsEnd || (*itsIt).fileName!=(*itsPrev).fileName || Item::TYPE1_AFM!=(*itsPrev).type); dbus()->install(itsCurrentFile, createAfm, itsDestIsSystem, getpid(), false); } break; } case CMD_DELETE: decode(*itsIt, font, system); dbus()->uninstall(font.family, font.styleInfo, system, getpid(), false); break; case CMD_ENABLE: decode(*itsIt, font, system); dbus()->enable(font.family, font.styleInfo, system, getpid(), false); break; case CMD_DISABLE: decode(*itsIt, font, system); dbus()->disable(font.family, font.styleInfo, system, getpid(), false); break; case CMD_MOVE: decode(*itsIt, font, system); // To 'Move' a disabled font, we first need to enable it. To accomplish this, JobRunner creates a 'fake' entry // with the filename "--" if((*itsIt).fileName==QLatin1String("--")) { setWindowTitle(i18n("Enabling")); dbus()->enable(font.family, font.styleInfo, system, getpid(), false); } else { if(itsPrev!=itsEnd && (*itsPrev).fileName==QLatin1String("--")) setWindowTitle(i18n("Moving")); dbus()->move(font.family, font.styleInfo, itsDestIsSystem, getpid(), false); } break; case CMD_REMOVE_FILE: decode(*itsIt, font, system); dbus()->removeFile(font.family, font.styleInfo, (*itsIt).fileName, system, getpid(), false); break; default: break; } itsStatusLabel->setText(CMD_INSTALL==itsCmd ? (*itsIt).url() : FC::createName(FC::decode(*itsIt))); itsProgress->setValue(itsProgress->value()+1); // Keep copy of this iterator - so that can check whether AFM should be created. itsPrev=itsIt; } } void CJobRunner::checkInterface() { if(itsIt==itsUrls.constBegin() && !FontInst::isStarted(dbus())) { setPage(PAGE_ERROR, i18n("Unable to start backend.")); itsActionLabel->stopAnimation(); itsIt=itsEnd; } } void CJobRunner::dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to) { if(to.isEmpty() && !from.isEmpty() && name==QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()) && itsIt!=itsEnd) { setPage(PAGE_ERROR, i18n("Backend died, but has been restarted. Please try again.")); itsActionLabel->stopAnimation(); itsIt=itsEnd; } } void CJobRunner::dbusStatus(int pid, int status) { if(pid!=getpid()) return; if(CMD_UPDATE==itsCmd) { setPage(PAGE_COMPLETE); return; } itsLastDBusStatus=status; if(itsCancelClicked) { itsActionLabel->stopAnimation(); setPage(PAGE_CANCEL); return; /* if(RESP_CANCEL==itsResponse) itsIt=itsEnd; itsCancelClicked=false; setPage(PAGE_PROGRESS); itsActionLabel->startAnimation(); */ } // itsIt will equal itsEnd if user decided to cancel the current op if(itsIt==itsEnd) { doNext(); } else if (0==status) { itsModified=true; ++itsIt; doNext(); } else { bool cont(itsAutoSkip && itsUrls.count()>1); QString currentName((*itsIt).fileName); if(!cont) { itsActionLabel->stopAnimation(); if(FontInst::STATUS_SERVICE_DIED==status) { setPage(PAGE_ERROR, errorString(status)); itsIt=itsEnd; } else { ItemList::ConstIterator lastPartOfCurrent(itsIt), next(itsIt==itsEnd ? itsEnd : itsIt+1); // If we're installing a Type1 font, and its already installed - then we need to skip past AFM/PFM if(next!=itsEnd && Item::TYPE1_FONT==(*itsIt).type && (*next).fileName==currentName && (Item::TYPE1_AFM==(*next).type || Item::TYPE1_PFM==(*next).type)) { next++; if(next!=itsEnd && (*next).fileName==currentName && (Item::TYPE1_AFM==(*next).type || Item::TYPE1_PFM==(*next).type)) next++; } if(1==itsUrls.count() || next==itsEnd) setPage(PAGE_ERROR, errorString(status)); else { setPage(PAGE_SKIP, errorString(status)); return; } } } contineuToNext(cont); } } void CJobRunner::contineuToNext(bool cont) { itsActionLabel->startAnimation(); if(cont) { if(CMD_INSTALL==itsCmd && Item::TYPE1_FONT==(*itsIt).type) // Did we error on a pfa/pfb? if so, exclude the afm/pfm... { QString currentName((*itsIt).fileName); ++itsIt; // Skip afm/pfm if(itsIt!=itsEnd && (*itsIt).fileName==currentName && (Item::TYPE1_AFM==(*itsIt).type || Item::TYPE1_PFM==(*itsIt).type)) ++itsIt; // Skip pfm/afm if(itsIt!=itsEnd && (*itsIt).fileName==currentName && (Item::TYPE1_AFM==(*itsIt).type || Item::TYPE1_PFM==(*itsIt).type)) ++itsIt; } else ++itsIt; } else { itsUrls.empty(); itsIt=itsEnd=itsUrls.constEnd(); } doNext(); } void CJobRunner::slotButtonClicked(QAbstractButton *button) { switch(itsStack->currentIndex()) { case PAGE_PROGRESS: if(itsIt!=itsEnd) itsCancelClicked=true; break; case PAGE_SKIP: setPage(PAGE_PROGRESS); if (button == itsSkipButton) { contineuToNext(true); } else if (button == itsAutoSkipButton) { itsAutoSkip=true; contineuToNext(true); } else { contineuToNext(false); } break; case PAGE_CANCEL: if(button == itsButtonBox->button(QDialogButtonBox::Yes)) itsIt=itsEnd; itsCancelClicked=false; setPage(PAGE_PROGRESS); itsActionLabel->startAnimation(); // Now continue... dbusStatus(getpid(), itsLastDBusStatus); break; case PAGE_COMPLETE: if(itsDontShowFinishedMsg) { KConfigGroup grp(KSharedConfig::openConfig(KFI_UI_CFG_FILE)->group(CFG_GROUP)); grp.writeEntry(CFG_DONT_SHOW_FINISHED_MSG, itsDontShowFinishedMsg->isChecked()); } case PAGE_ERROR: QDialog::accept(); break; } } void CJobRunner::closeEvent(QCloseEvent *e) { if(PAGE_COMPLETE!=itsStack->currentIndex()) { e->ignore(); slotButtonClicked(PAGE_CANCEL==itsStack->currentIndex() ? itsButtonBox->button(QDialogButtonBox::No) : itsButtonBox->button(QDialogButtonBox::Cancel)); } } void CJobRunner::setPage(int page, const QString &msg) { itsStack->setCurrentIndex(page); switch(page) { case PAGE_PROGRESS: itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_SKIP: itsSkipLabel->setText(i18n("

Error

")+QLatin1String("

")+msg+QLatin1String("

")); itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->show(); itsAutoSkipButton->show(); break; case PAGE_ERROR: itsErrorLabel->setText(i18n("

Error

")+QLatin1String("

")+msg+QLatin1String("

")); itsButtonBox->setStandardButtons(QDialogButtonBox::Cancel); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_CANCEL: itsButtonBox->setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No); itsSkipButton->hide(); itsAutoSkipButton->hide(); break; case PAGE_COMPLETE: if(!itsDontShowFinishedMsg || itsDontShowFinishedMsg->isChecked()) { QDialog::accept(); } else { itsButtonBox->setStandardButtons(QDialogButtonBox::Close); itsSkipButton->hide(); itsAutoSkipButton->hide(); } break; } } QString CJobRunner::fileName(const QUrl &url) { if(url.isLocalFile()) return url.toLocalFile(); else { auto job = KIO::mostLocalUrl(url); job->exec(); QUrl local = job->mostLocalUrl(); if(local.isLocalFile()) return local.toLocalFile(); // Yipee! no need to download!! else { // Need to do actual download... if(!itsTempDir) { itsTempDir=new QTemporaryDir(QDir::tempPath() + "/fontinst"); itsTempDir->setAutoRemove(true); } - QString tempName(itsTempDir->path()+QLatin1Char('/')+Misc::getFile(url.path())); + QString tempName(itsTempDir->filePath(Misc::getFile(url.path()))); auto job = KIO::file_copy(url, QUrl::fromLocalFile(tempName), -1, KIO::Overwrite); if (job->exec()) return tempName; else return QString(); } } } QString CJobRunner::errorString(int value) const { Misc::TFont font(FC::decode(*itsIt)); QString urlStr; if(CMD_REMOVE_FILE==itsCmd) urlStr=(*itsIt).fileName; else if(font.family.isEmpty()) urlStr=(*itsIt).url(); else urlStr=FC::createName(font.family, font.styleInfo); switch(value) { case constDownloadFailed: return i18n("Failed to download %1", urlStr); case FontInst::STATUS_SERVICE_DIED: return i18n("System backend died. Please try again.
%1", urlStr); case FontInst::STATUS_BITMAPS_DISABLED: return i18n("%1 is a bitmap font, and these have been disabled on your system.", urlStr); case FontInst::STATUS_ALREADY_INSTALLED: return i18n("%1 contains the font %2, which is already installed on your system.", urlStr, FC::getName(itsCurrentFile)); case FontInst::STATUS_NOT_FONT_FILE: return i18n("%1 is not a font.", urlStr); case FontInst::STATUS_PARTIAL_DELETE: return i18n("Could not remove all files associated with %1", urlStr); case FontInst::STATUS_NO_SYS_CONNECTION: return i18n("Failed to start the system daemon.
%1", urlStr); case KIO::ERR_FILE_ALREADY_EXIST: { QString name(Misc::modifyName(Misc::getFile((*itsIt).fileName))), destFolder(Misc::getDestFolder(folderName(itsDestIsSystem), name)); return i18n("%1 already exists.", destFolder+name); } case KIO::ERR_DOES_NOT_EXIST: return i18n("%1 does not exist.", urlStr); case KIO::ERR_WRITE_ACCESS_DENIED: return i18n("Permission denied.
%1", urlStr); case KIO::ERR_UNSUPPORTED_ACTION: return i18n("Unsupported action.
%1", urlStr); case KIO::ERR_COULD_NOT_AUTHENTICATE: return i18n("Authentication failed.
%1", urlStr); default: return i18n("Unexpected error while processing: %1", urlStr); } } CJobRunner::Item::Item(const QUrl &u, const QString &n, bool dis) : QUrl(u), name(n), fileName(Misc::getFile(u.path())), isDisabled(dis) { type=Misc::checkExt(fileName, "pfa") || Misc::checkExt(fileName, "pfb") ? TYPE1_FONT : Misc::checkExt(fileName, "afm") ? TYPE1_AFM : Misc::checkExt(fileName, "pfm") ? TYPE1_PFM : OTHER_FONT; if(OTHER_FONT!=type) { int pos(fileName.lastIndexOf('.')); if(-1!=pos) fileName=fileName.left(pos); } } CJobRunner::Item::Item(const QString &file, const QString &family, quint32 style, bool system) : QUrl(CJobRunner::encode(family, style, system)), fileName(file), type(OTHER_FONT) { } bool CJobRunner::Item::operator<(const Item &o) const { int nameComp(fileName.compare(o.fileName)); return nameComp<0 || (0==nameComp && type * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KioFonts.h" #include "KfiConstants.h" #include "FontInstInterface.h" #include "FontInst.h" #include "Fc.h" #include "Misc.h" #include "XmlStrings.h" #include "Family.h" #include "Style.h" #include "File.h" #include "debug.h" #define MAX_IPC_SIZE (1024*32) #define KFI_DBUG qCDebug(KCM_KFONTINST_KIO) << '(' << time(NULL) << ')' static const int constReconfigTimeout = 10; extern "C" { Q_DECL_EXPORT int kdemain(int argc, char **argv) { if (argc != 4) { fprintf(stderr, "Usage: kio_" KFI_KIO_FONTS_PROTOCOL " protocol domain-socket1 domain-socket2\n"); exit(-1); } QCoreApplication app(argc, argv); QCoreApplication::setApplicationName("kio_" KFI_KIO_FONTS_PROTOCOL); KFI::CKioFonts slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } } namespace KFI { inline bool isSysFolder(const QString &folder) { return i18n(KFI_KIO_FONTS_SYS)==folder || KFI_KIO_FONTS_SYS==folder; } inline bool isUserFolder(const QString &folder) { return i18n(KFI_KIO_FONTS_USER)==folder || KFI_KIO_FONTS_USER==folder; } static CKioFonts::EFolder getFolder(const QStringList &list) { if(list.size()>0) { QString folder=list[0]; if(isSysFolder(folder)) return CKioFonts::FOLDER_SYS; else if(isUserFolder(folder)) return CKioFonts::FOLDER_USER; return CKioFonts::FOLDER_UNKNOWN; } return CKioFonts::FOLDER_ROOT; } static int getSize(const QString &file) { QT_STATBUF buff; QByteArray f(QFile::encodeName(file)); if(-1!=QT_LSTAT(f.constData(), &buff)) { if (S_ISLNK(buff.st_mode)) { char buffer2[1000]; int n=readlink(f.constData(), buffer2, 999); if(n!= -1) buffer2[n]='\0'; if(-1==QT_STAT(f.constData(), &buff)) return -1; } return buff.st_size; } return -1; } static bool writeAll(int fd, const char *buf, size_t len) { while(len>0) { ssize_t written=write(fd, buf, len); if (written<0 && EINTR!=errno) return false; buf+=written; len-=written; } return true; } static bool isScalable(const QString &str) { return Misc::checkExt(str, "ttf") || Misc::checkExt(str, "otf") || Misc::checkExt(str, "ttc") || Misc::checkExt(str, "pfa") || Misc::checkExt(str, "pfb"); } static const char * const constExtensions[]= {".ttf", KFI_FONTS_PACKAGE, ".otf", ".pfa", ".pfb", ".ttc", ".pcf", ".pcf.gz", ".bdf", ".bdf.gz", NULL }; static QString removeKnownExtension(const QUrl &url) { QString fname(url.fileName()); int pos; for(int i=0; constExtensions[i]; ++i) if(-1!=(pos=fname.lastIndexOf(QString::fromLatin1(constExtensions[i]), -1, Qt::CaseInsensitive))) return fname.left(pos); return fname; } CKioFonts::CKioFonts(const QByteArray &pool, const QByteArray &app) : KIO::SlaveBase(KFI_KIO_FONTS_PROTOCOL, pool, app) , itsInterface(new FontInstInterface()) , itsTempDir(0L) { KFI_DBUG; } CKioFonts::~CKioFonts() { KFI_DBUG; delete itsInterface; delete itsTempDir; } void CKioFonts::listDir(const QUrl &url) { KFI_DBUG << url; QStringList pathList(url.adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); EFolder folder=Misc::root() ? FOLDER_SYS : getFolder(pathList); KIO::UDSEntry entry; int size=0; switch(folder) { case FOLDER_ROOT: KFI_DBUG << "List root folder"; size=2; totalSize(2); createUDSEntry(entry, FOLDER_SYS); listEntry(entry); createUDSEntry(entry, FOLDER_USER); listEntry(entry); break; case FOLDER_SYS: case FOLDER_USER: size=listFolder(entry, folder); break; default: break; } if(FOLDER_UNKNOWN!=folder) { finished(); } else error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } void CKioFonts::put(const QUrl &url, int /*permissions*/, KIO::JobFlags /*flags*/) { KFI_DBUG << url; QStringList pathList(url.adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); EFolder folder(getFolder(pathList)); if(!Misc::root() && FOLDER_ROOT==folder) error(KIO::ERR_SLAVE_DEFINED, i18n("Can only install fonts to either \"%1\" or \"%2\".", i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS))); else if(Misc::isPackage(url.fileName())) error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot install a fonts package directly.\n" "Please extract %1, and install the components individually.", url.toDisplayString())); else { if(!itsTempDir) { itsTempDir=new QTemporaryDir(QDir::tempPath() + QString::fromLatin1("/kio_fonts_")+QString::number(getpid())); itsTempDir->setAutoRemove(true); } - QString tempFile(itsTempDir->path()+QLatin1Char('/')+url.fileName()); + QString tempFile(itsTempDir->filePath(url.fileName())); QFile dest(tempFile); if (dest.open(QIODevice::WriteOnly)) { int result; // Loop until we got 0 (end of data) do { QByteArray buffer; dataReq(); // Request for data result = readData(buffer); if(result > 0 && !writeAll(dest.handle(), buffer.constData(), buffer.size())) { if(ENOSPC==errno) // disk full { error(KIO::ERR_DISK_FULL, dest.fileName()); result = -2; // means: remove dest file } else { error(KIO::ERR_COULD_NOT_WRITE, dest.fileName()); result = -1; } } } while(result>0); if (result<0) { dest.close(); ::exit(255); } handleResp(itsInterface->install(tempFile, Misc::root() || FOLDER_SYS==folder), url.fileName(), tempFile, FOLDER_SYS==folder); QFile::remove(tempFile); } else error(EACCES==errno ? KIO::ERR_WRITE_ACCESS_DENIED : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.fileName()); } } void CKioFonts::get(const QUrl &url) { KFI_DBUG << url; QStringList pathList(url.adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); EFolder folder(getFolder(pathList)); Family family(getFont(url, folder)); if(!family.name().isEmpty() && 1==family.styles().count()) { StyleCont::ConstIterator style(family.styles().begin()); FileCont::ConstIterator it((*style).files().begin()), end((*style).files().end()); // // The thumbnail job always donwloads non-local files to /tmp/... and passes this file name to // the thumbnail creator. However, in the case of fonts which are split among many files, this // wont work. Therefore, when the thumbnail code asks for the font to donwload, just return // the family and style info for enabled fonts, and the filename for disabled fonts. This way // the font-thumbnail creator can read this and just ask Xft/fontconfig for the font data. if("1"==metaData("thumbnail")) { QByteArray array; QTextStream stream(&array, QIODevice::WriteOnly); emit mimeType("text/plain"); bool hidden(true); for(; it!=end && hidden; ++it) if(!Misc::isHidden(Misc::getFile((*it).path()))) hidden=false; if(hidden) { // // OK, its a disabled font - if possible try to return the location of the font file // itself. bool found=false; it=(*style).files().begin(); end=(*style).files().end(); for(; it!=end && hidden; ++it) if(isScalable((*it).path())) { KFI_DBUG << "hasMetaData(\"thumbnail\"), so return FILE: " << (*it).path() << " / " << (*it).index(); stream << KFI_PATH_KEY << (*it).path() << endl << KFI_FACE_KEY << (*it).index() << endl; found=true; break; } if(!found) { KFI_DBUG << "hasMetaData(\"thumbnail\"), so return Url: " << url; stream << url.toDisplayString(); } } else { KFI_DBUG << "hasMetaData(\"thumbnail\"), so return DETAILS: " << family.name() << " / " << (*style).value(); stream << KFI_NAME_KEY << family.name() << endl << KFI_STYLE_KEY << (*style).value() << endl; } totalSize(array.size()); data(array); processedSize(array.size()); data(QByteArray()); processedSize(array.size()); finished(); KFI_DBUG << "Finished thumbnail..."; return; } QSet files; QString realPath; QT_STATBUF buff; bool multiple=false; for(; it!=end; ++it) { QStringList assoc; files.insert((*it).path()); Misc::getAssociatedFiles((*it).path(), assoc); QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd()); for(; ait!=aend; ++ait) files.insert(*ait); } if(1==files.count()) realPath=(*files.begin()); else // Font is made up of multiple files - so create .zip of them all! { QTemporaryFile tmpFile; if(tmpFile.open()) { KZip zip(tmpFile.fileName()); tmpFile.setAutoRemove(false); realPath=tmpFile.fileName(); if(zip.open(QIODevice::WriteOnly)) { QMap map=Misc::getFontFileMap(files); QMap::ConstIterator it(map.constBegin()), end(map.constEnd()); for(; it!=end; ++it) zip.addLocalFile(it.value(), it.key()); multiple=true; zip.close(); } } } QByteArray realPathC(QFile::encodeName(realPath)); KFI_DBUG << "real: " << realPathC; if (-2==QT_STAT(realPathC.constData(), &buff)) error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); else if (S_ISDIR(buff.st_mode)) error(KIO::ERR_IS_DIRECTORY, url.toDisplayString()); else if (!S_ISREG(buff.st_mode)) error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); else { int fd = QT_OPEN(realPathC.constData(), O_RDONLY); if (fd < 0) error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); else { // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). // This code can be optimized by using QFileInfo instead of buff above // and passing it to mimeTypeForFile() instead of realPath. QMimeDatabase db; emit mimeType(db.mimeTypeForFile(realPath).name()); totalSize(buff.st_size); KIO::filesize_t processed=0; char buffer[MAX_IPC_SIZE]; QByteArray array; while(1) { int n=::read(fd, buffer, MAX_IPC_SIZE); if (-1==n) { if (EINTR==errno) continue; error(KIO::ERR_COULD_NOT_READ, url.toDisplayString()); ::close(fd); if(multiple) ::unlink(realPathC); return; } if (0==n) break; // Finished array=array.fromRawData(buffer, n); data(array); array.clear(); processed+=n; processedSize(processed); } data(QByteArray()); ::close(fd); processedSize(buff.st_size); finished(); } } if(multiple) ::unlink(realPathC); } else error(KIO::ERR_COULD_NOT_READ, url.toDisplayString()); } void CKioFonts::copy(const QUrl &, const QUrl &, int, KIO::JobFlags) { error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot copy fonts")); } void CKioFonts::rename(const QUrl &, const QUrl &, KIO::JobFlags) { error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot move fonts")); } void CKioFonts::del(const QUrl &url, bool isFile) { KFI_DBUG << url; QStringList pathList(url.adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); EFolder folder(getFolder(pathList)); QString name(removeKnownExtension(url)); if(!isFile) error(KIO::ERR_SLAVE_DEFINED, i18n("Only fonts may be deleted.")); else if(!Misc::root() && FOLDER_ROOT==folder) error(KIO::ERR_SLAVE_DEFINED, i18n("Can only remove fonts from either \"%1\" or \"%2\".", i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS))); else if(!name.isEmpty()) handleResp(itsInterface->uninstall(name, Misc::root() || FOLDER_SYS==folder), name); else error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } void CKioFonts::statFont(const QUrl &url) { KFI_DBUG << url; QStringList pathList(url.adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); EFolder folder=getFolder(pathList); KIO::UDSEntry entry; bool ok=true; switch(pathList.count()) { case 0: createUDSEntry(entry, FOLDER_ROOT); break; case 1: if(Misc::root()) ok=createStatEntry(entry, url, FOLDER_SYS); else if(FOLDER_SYS==folder || FOLDER_USER==folder) createUDSEntry(entry, folder); else { error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".", i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS))); return; } break; default: ok=createStatEntry(entry, url, folder); } if(ok) { statEntry(entry); finished(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); return; } } void CKioFonts::special(const QByteArray &a) { if(a.size()) error(KIO::ERR_UNSUPPORTED_ACTION, i18n("No special methods supported.")); else { setTimeoutSpecialCommand(-1); itsInterface->reconfigure(); } } int CKioFonts::listFolder(KIO::UDSEntry &entry, EFolder folder) { KFI_DBUG << folder; int styleCount(0); KFI::Families families(itsInterface->list(FOLDER_SYS==folder)); FamilyCont::ConstIterator family(families.items.begin()), end(families.items.end()); KFI_DBUG << "Num families:" << families.items.count(); for(; family!=end; ++family) { StyleCont::ConstIterator styleIt((*family).styles().begin()), styleEnd((*family).styles().end()); styleCount+=(*family).styles().count(); for(; styleIt!=styleEnd; ++styleIt) { createUDSEntry(entry, folder, *family, *styleIt); listEntry(entry); } } totalSize(styleCount); return styleCount; } QString CKioFonts::getUserName(uid_t uid) { if (!itsUserCache.contains(uid)) { struct passwd *user = getpwuid(uid); if(user) itsUserCache.insert(uid, QString::fromLatin1(user->pw_name)); else return QString::number(uid); } return itsUserCache[uid]; } QString CKioFonts::getGroupName(gid_t gid) { if (!itsGroupCache.contains(gid)) { struct group *grp = getgrgid(gid); if(grp) itsGroupCache.insert(gid, QString::fromLatin1(grp->gr_name)); else return QString::number(gid); } return itsGroupCache[gid]; } bool CKioFonts::createStatEntry(KIO::UDSEntry &entry, const QUrl &url, EFolder folder) { Family fam(getFont(url, folder)); if(!fam.name().isEmpty() && 1==fam.styles().count()) { createUDSEntry(entry, folder, fam, *fam.styles().begin()); return true; } return false; } void CKioFonts::createUDSEntry(KIO::UDSEntry &entry, EFolder folder) { KFI_DBUG << QString(FOLDER_SYS==folder ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER)); entry.clear(); entry.insert(KIO::UDSEntry::UDS_NAME, FOLDER_ROOT==folder || Misc::root() ? i18n("Fonts") : FOLDER_SYS==folder ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER)); entry.insert(KIO::UDSEntry::UDS_ACCESS, !Misc::root() && FOLDER_SYS==folder ? 0444 : 0744); entry.insert(KIO::UDSEntry::UDS_USER, Misc::root() || FOLDER_SYS==folder ? QString::fromLatin1("root") : getUserName(getuid())); entry.insert(KIO::UDSEntry::UDS_GROUP, Misc::root() || FOLDER_SYS==folder ? QString::fromLatin1("root") : getGroupName(getgid())); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory")); } bool CKioFonts::createUDSEntry(KIO::UDSEntry &entry, EFolder folder, const Family &family, const Style &style) { int size=0; QString name(FC::createName(family.name(), style.value())); FileCont::ConstIterator file(style.files().begin()), fileEnd(style.files().end()); QList files; bool hidden=true, haveExtraFiles=false; KFI_DBUG << name; for(; file!=fileEnd; ++file) { size+=getSize((*file).path()); // TODO: Make scalable a property of the file? // Then isScalable() is not needed!!! if(isScalable((*file).path())) files.prepend(*file); else files.append(*file); if(hidden && !Misc::isHidden(Misc::getFile((*file).path()))) hidden=false; QStringList assoc; Misc::getAssociatedFiles((*file).path(), assoc); QStringList::ConstIterator oit(assoc.constBegin()), oend(assoc.constEnd()); if(!haveExtraFiles && !assoc.isEmpty()) haveExtraFiles=true; for(; oit!=oend; ++oit) size+=getSize(*oit); } entry.clear(); entry.insert(KIO::UDSEntry::UDS_NAME, name); entry.insert(KIO::UDSEntry::UDS_SIZE, size); entry.insert(UDS_EXTRA_FC_STYLE, style.value()); QList::ConstIterator it(files.constBegin()), end(files.constEnd()); for(; it!=end; ++it) { QByteArray cPath(QFile::encodeName((*it).path())); QT_STATBUF buff; if(-1!=QT_LSTAT(cPath, &buff)) { QString fileName(Misc::getFile((*it).path())), mt; int dotPos(fileName.lastIndexOf('.')); QString extension(-1==dotPos ? QString() : fileName.mid(dotPos)); if(QString::fromLatin1(".gz")==extension) { dotPos=fileName.lastIndexOf('.', dotPos-1); extension=-1==dotPos ? QString() : fileName.mid(dotPos); } if(QString::fromLatin1(".ttf")==extension || QString::fromLatin1(".ttc")==extension) mt="application/x-font-ttf"; else if(QString::fromLatin1(".otf")==extension) mt="application/x-font-otf"; else if(QString::fromLatin1(".pfa")==extension || QString::fromLatin1(".pfb")==extension) mt="application/x-font-type1"; else if(QString::fromLatin1(".pcf.gz")==extension || QString::fromLatin1(".pcf")==extension) mt="application/x-font-pcf"; else if(QString::fromLatin1(".bdf.gz")==extension || QString::fromLatin1(".bdf")==extension) mt="application/x-font-bdf"; else { // File extension check failed, use QMimeDatabase to read contents... QMimeDatabase db; QMimeType mime = db.mimeTypeForFile((*it).path()); QStringList patterns = mime.globPatterns(); mt = mime.name(); if(patterns.size()>0) extension=(*patterns.begin()).remove("*"); } entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode&S_IFMT); entry.insert(KIO::UDSEntry::UDS_ACCESS, buff.st_mode&07777); entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime); entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime); entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid)); entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid)); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, mt); if(hidden) { entry.insert(KIO::UDSEntry::UDS_HIDDEN, 1); entry.insert(UDS_EXTRA_FILE_NAME, (*it).path()); entry.insert(UDS_EXTRA_FILE_FACE, (*it).path()); } QString path(QString::fromLatin1("/")); if(!Misc::root()) { path+=FOLDER_SYS==folder ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER); path+=QString::fromLatin1("/"); } path+=name; if(files.count()>1 || haveExtraFiles) path+=QString::fromLatin1(KFI_FONTS_PACKAGE); else path+=extension; QUrl url(QUrl::fromLocalFile(path)); url.setScheme(KFI_KIO_FONTS_PROTOCOL); entry.insert(KIO::UDSEntry::UDS_URL, url.url()); return true; } } return false; } Family CKioFonts::getFont(const QUrl &url, EFolder folder) { QString name(removeKnownExtension(url)); KFI_DBUG << url << name; return itsInterface->statFont(name, FOLDER_SYS==folder); } void CKioFonts::handleResp(int resp, const QString &file, const QString &tempFile, bool destIsSystem) { switch(resp) { case FontInst::STATUS_NO_SYS_CONNECTION: error(KIO::ERR_SLAVE_DEFINED, i18n("Failed to start the system daemon")); break; case FontInst::STATUS_SERVICE_DIED: error(KIO::ERR_SLAVE_DEFINED, i18n("Backend died")); break; case FontInst::STATUS_BITMAPS_DISABLED: error(KIO::ERR_SLAVE_DEFINED, i18n("%1 is a bitmap font, and these have been disabled on your system.", file)); break; case FontInst::STATUS_ALREADY_INSTALLED: error(KIO::ERR_SLAVE_DEFINED, i18n("%1 contains the font %2, which is already installed on your system.", file, FC::getName(tempFile))); break; case FontInst::STATUS_NOT_FONT_FILE: error(KIO::ERR_SLAVE_DEFINED, i18n("%1 is not a font.", file)); break; case FontInst::STATUS_PARTIAL_DELETE: error(KIO::ERR_SLAVE_DEFINED, i18n("Could not remove all files associated with %1", file)); break; case KIO::ERR_FILE_ALREADY_EXIST: { QString name(Misc::modifyName(file)), destFolder(Misc::getDestFolder(itsInterface->folderName(destIsSystem), name)); error(KIO::ERR_SLAVE_DEFINED, i18n("%1 already exists.", destFolder+name)); break; } case FontInst::STATUS_OK: finished(); break; default: error(resp, file); } if(FontInst::STATUS_OK==resp) setTimeoutSpecialCommand(constReconfigTimeout); } } diff --git a/kcms/kfontinst/thumbnail/FontThumbnail.cpp b/kcms/kfontinst/thumbnail/FontThumbnail.cpp index 412272dd7..55a0d0158 100644 --- a/kcms/kfontinst/thumbnail/FontThumbnail.cpp +++ b/kcms/kfontinst/thumbnail/FontThumbnail.cpp @@ -1,125 +1,125 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2007 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "FontThumbnail.h" #include "KfiConstants.h" #include "FcEngine.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #define KFI_DBUG qCDebug(KCM_KFONTINST_THUMBNAIL) extern "C" { Q_DECL_EXPORT ThumbCreator *new_creator() { return new KFI::CFontThumbnail; } } namespace KFI { CFontThumbnail::CFontThumbnail() { } bool CFontThumbnail::create(const QString &path, int width, int height, QImage &img) { QString realPath(path); QTemporaryDir *tempDir = 0; KFI_DBUG << "Create font thumbnail for:" << path << endl; // Is this a appliaction/vnd.kde.fontspackage file? If so, extract 1 scalable font... QMimeDatabase db; if (Misc::isPackage(path) || "application/zip" == db.mimeTypeForFile(path, QMimeDatabase::MatchContent).name()) { KZip zip(path); if(zip.open(QIODevice::ReadOnly)) { const KArchiveDirectory *zipDir=zip.directory(); if(zipDir) { QStringList fonts(zipDir->entries()); if(fonts.count()) { QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); for(; it!=end; ++it) { const KArchiveEntry *entry=zipDir->entry(*it); if(entry && entry->isFile()) { delete tempDir; tempDir=new QTemporaryDir(QDir::tempPath() + "/" KFI_TMP_DIR_PREFIX); tempDir->setAutoRemove(true); ((KArchiveFile *)entry)->copyTo(tempDir->path()); - QString mime(db.mimeTypeForFile(tempDir->path()+QLatin1Char('/')+entry->name()).name()); + QString mime(db.mimeTypeForFile(tempDir->filePath(entry->name())).name()); if(mime=="font/ttf" || mime=="font/otf" || mime=="application/x-font-ttf" || mime=="application/x-font-otf" || mime=="application/x-font-type1") { - realPath=tempDir->path()+QLatin1Char('/')+entry->name(); + realPath=tempDir->filePath(entry->name()); break; } else - ::unlink(QFile::encodeName(tempDir->path()+QLatin1Char('/')+entry->name()).data()); + ::unlink(QFile::encodeName(tempDir->filePath(entry->name())).data()); } } } } } } QColor bgnd(Qt::black); bgnd.setAlpha(0); img=itsEngine.draw(realPath, KFI_NO_STYLE_INFO, 0, QApplication::palette().text().color(), bgnd, width, height, true); delete tempDir; return !img.isNull(); } ThumbCreator::Flags CFontThumbnail::flags() const { return None; } } diff --git a/kcms/kfontinst/viewpart/FontViewPart.cpp b/kcms/kfontinst/viewpart/FontViewPart.cpp index 7138e9398..03ea59e83 100644 --- a/kcms/kfontinst/viewpart/FontViewPart.cpp +++ b/kcms/kfontinst/viewpart/FontViewPart.cpp @@ -1,578 +1,578 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2007 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "FontViewPart.h" #include "Misc.h" #include "KfiConstants.h" #include "FcEngine.h" #include "PreviewSelectAction.h" #include "FontInstInterface.h" #include "FontInst.h" #include "config-workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include "config-fontinst.h" // Enable the following to allow printing of non-installed fonts. Does not seem to work :-( //#define KFI_PRINT_APP_FONTS namespace KFI { static QString getFamily(const QString &font) { int commaPos=font.lastIndexOf(','); return -1==commaPos ? font : font.left(commaPos); } K_PLUGIN_FACTORY(CFontViewPartFactory, registerPlugin();) K_EXPORT_PLUGIN(CFontViewPartFactory("kfontview")) CFontViewPart::CFontViewPart(QWidget *parentWidget, QObject *parent, const QList &) : KParts::ReadOnlyPart(parent), itsConfig(KSharedConfig::openConfig()), itsProc(NULL), itsTempDir(NULL), itsInterface(new FontInstInterface()), itsOpening(false) { // create browser extension (for printing when embedded into browser) itsExtension = new BrowserExtension(this); itsFrame=new QFrame(parentWidget); QFrame *previewFrame=new QFrame(itsFrame); QWidget *controls=new QWidget(itsFrame); // QGroupBox *metaBox=new QGroupBox(i18n("Information:"), controls); itsFaceWidget=new QWidget(controls); QBoxLayout *mainLayout=new QBoxLayout(QBoxLayout::TopToBottom, itsFrame); QBoxLayout *previewLayout=new QBoxLayout(QBoxLayout::LeftToRight, previewFrame), *controlsLayout=new QBoxLayout(QBoxLayout::LeftToRight, controls), *faceLayout=new QBoxLayout(QBoxLayout::LeftToRight, itsFaceWidget); // QBoxLayout *metaLayout=new QBoxLayout(QBoxLayout::LeftToRight, metaBox); // itsMetaLabel=new QLabel(metaBox); // itsMetaLabel->setAlignment(Qt::AlignTop); // metaLayout->addWidget(itsMetaLabel); previewLayout->setMargin(0); previewLayout->setSpacing(0); faceLayout->setMargin(0); controlsLayout->setMargin(0); previewLayout->setSpacing(0); itsFrame->setFrameShape(QFrame::NoFrame); itsFrame->setFocusPolicy(Qt::ClickFocus); previewFrame->setFrameShape(QFrame::StyledPanel); previewFrame->setFrameShadow(QFrame::Sunken); KAboutData aboutData(KFI_NAME, i18n("FontViewPart"), WORKSPACE_VERSION_STRING); setComponentData(aboutData); itsPreview=new CFontPreview(previewFrame); itsPreview->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); itsFaceLabel=new QLabel(i18n("Show Face:"), itsFaceWidget); itsFaceSelector=new QSpinBox(itsFaceWidget); itsFaceSelector->setValue(1); itsInstallButton=new QPushButton(i18n("Install..."), controls); itsInstallButton->setEnabled(false); previewLayout->addWidget(itsPreview); faceLayout->addWidget(itsFaceLabel); faceLayout->addWidget(itsFaceSelector); faceLayout->addItem(new QSpacerItem(faceLayout->spacing(), 0, QSizePolicy::Fixed, QSizePolicy::Fixed)); itsFaceWidget->hide(); itsPreview->engine()->readConfig(*itsConfig); //controlsLayout->addWidget(metaBox); //controlsLayout->addStretch(2); controlsLayout->addWidget(itsFaceWidget); controlsLayout->addStretch(1); controlsLayout->addWidget(itsInstallButton); mainLayout->addWidget(previewFrame); mainLayout->addWidget(controls); connect(itsPreview, SIGNAL(status(bool)), SLOT(previewStatus(bool))); connect(itsInstallButton, SIGNAL(clicked()), SLOT(install())); connect(itsFaceSelector, SIGNAL(valueChanged(int)), SLOT(showFace(int))); itsChangeTextAction=actionCollection()->addAction("changeText"); itsChangeTextAction->setIcon(QIcon::fromTheme("edit-rename")); itsChangeTextAction->setText(i18n("Change Text...")); connect(itsChangeTextAction, SIGNAL(triggered(bool)), SLOT(changeText())); CPreviewSelectAction *displayTypeAction=new CPreviewSelectAction(this, CPreviewSelectAction::BlocksAndScripts); actionCollection()->addAction("displayType", displayTypeAction); connect(displayTypeAction, SIGNAL(range(QList)), SLOT(displayType(QList))); QAction *zoomIn=actionCollection()->addAction(KStandardAction::ZoomIn, itsPreview, SLOT(zoomIn())), *zoomOut=actionCollection()->addAction(KStandardAction::ZoomOut, itsPreview, SLOT(zoomOut())); connect(itsPreview, SIGNAL(atMax(bool)), zoomIn, SLOT(setDisabled(bool))); connect(itsPreview, SIGNAL(atMin(bool)), zoomOut, SLOT(setDisabled(bool))); setXMLFile("kfontviewpart.rc"); setWidget(itsFrame); itsExtension->enablePrint(false); FontInst::registerTypes(); connect(itsInterface, SIGNAL(status(int,int)), SLOT(dbusStatus(int,int))); connect(itsInterface, SIGNAL(fontStat(int,KFI::Family)), SLOT(fontStat(int,KFI::Family))); } CFontViewPart::~CFontViewPart() { delete itsTempDir; itsTempDir=NULL; delete itsInterface; itsInterface=NULL; } static inline QUrl mostLocalUrl(const QUrl &url, QWidget *widget) { auto job = KIO::mostLocalUrl(url); KJobWidgets::setWindow(job, widget); job->exec(); return job->mostLocalUrl(); } bool CFontViewPart::openUrl(const QUrl &url) { if (!url.isValid() || !closeUrl()) return false; // itsMetaLabel->setText(QString()); // itsMetaInfo.clear(); itsFontDetails=FC::decode(url); if(!itsFontDetails.family.isEmpty() || KFI_KIO_FONTS_PROTOCOL==url.scheme() || mostLocalUrl(url, itsFrame).isLocalFile()) { setUrl(url); emit started(0); setLocalFilePath(this->url().path()); bool ret=openFile(); if (ret) emit completed(); return ret; } else return ReadOnlyPart::openUrl(url); } bool CFontViewPart::openFile() { // NOTE: Can't do the real open here, as we don't seem to be able to use KIO::NetAccess functions // during initial start-up. Bug report 111535 indicates that calling "konqueror " crashes. itsInstallButton->setEnabled(false); QTimer::singleShot(0, this, SLOT(timeout())); return true; } static inline bool statUrl(const QUrl &url, KIO::UDSEntry *udsEntry) { auto job = KIO::stat(url); job->exec(); if (job->error()) return false; *udsEntry = job->statResult(); return true; } void CFontViewPart::timeout() { if(!itsInstallButton) return; bool isFonts(KFI_KIO_FONTS_PROTOCOL==url().scheme()), showFs(false), package(false); int fileIndex(-1); QString fontFile; // itsMetaUrl=url(); delete itsTempDir; itsTempDir=NULL; itsOpening=true; if(!itsFontDetails.family.isEmpty()) { emit setWindowCaption(FC::createName(itsFontDetails.family, itsFontDetails.styleInfo)); fontFile=FC::getFile(url()); fileIndex=FC::getIndex(url()); } else if(isFonts) { KIO::UDSEntry udsEntry; bool found = statUrl(url(), &udsEntry); if(!found) { // Check if url is "fonts:/ if so try fonts:/System/, then fonts:/Personal QStringList pathList(url().adjusted(QUrl::StripTrailingSlash).path().split('/', QString::SkipEmptyParts)); if(pathList.count()==1) { found = statUrl(QUrl(QString("fonts:/"+i18n(KFI_KIO_FONTS_SYS)+'/'+pathList[0])), &udsEntry); if (!found) found = statUrl(QUrl(QString("fonts:/"+i18n(KFI_KIO_FONTS_USER)+'/'+pathList[0])), &udsEntry); } } if(found) { if(udsEntry.numberValue(KIO::UDSEntry::UDS_HIDDEN, 0)) { fontFile=udsEntry.stringValue(UDS_EXTRA_FILE_NAME); fileIndex=udsEntry.numberValue(UDS_EXTRA_FILE_FACE, 0); } itsFontDetails.family=getFamily(udsEntry.stringValue(KIO::UDSEntry::UDS_NAME)); itsFontDetails.styleInfo=udsEntry.numberValue(UDS_EXTRA_FC_STYLE); emit setWindowCaption(udsEntry.stringValue(KIO::UDSEntry::UDS_NAME)); } else { previewStatus(false); return; } } else { QString path(localFilePath()); // Is this a application/vnd.kde.fontspackage file? If so, extract 1 scalable font... if((package=Misc::isPackage(path))) { KZip zip(path); if(zip.open(QIODevice::ReadOnly)) { const KArchiveDirectory *zipDir=zip.directory(); if(zipDir) { QStringList fonts(zipDir->entries()); if(fonts.count()) { QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); for(; it!=end; ++it) { const KArchiveEntry *entry=zipDir->entry(*it); if(entry && entry->isFile()) { delete itsTempDir; itsTempDir=new QTemporaryDir(QDir::tempPath() + "/" KFI_TMP_DIR_PREFIX); itsTempDir->setAutoRemove(true); ((KArchiveFile *)entry)->copyTo(itsTempDir->path()); QMimeDatabase db; - QString mime(db.mimeTypeForFile(itsTempDir->path()+QLatin1Char('/')+entry->name()).name()); + QString mime(db.mimeTypeForFile(itsTempDir->filePath(entry->name())).name()); if(mime=="font/ttf" || mime=="font/otf" || mime=="application/x-font-ttf" || mime=="application/x-font-otf" || mime=="application/x-font-type1") { - fontFile=itsTempDir->path()+QLatin1Char('/')+entry->name(); + fontFile=itsTempDir->filePath(entry->name()); //setLocalFilePath(itsTempDir->path()+QLatin1Char('/')+entry->name()); // itsMetaUrl=QUrl::fromLocalFile(localFilePath()); break; } else - ::unlink(QFile::encodeName(itsTempDir->path()+QLatin1Char('/')+entry->name()).data()); + ::unlink(QFile::encodeName(itsTempDir->filePath(entry->name())).data()); } } } } } } } itsInstallButton->setEnabled(false); if(itsFontDetails.family.isEmpty()) emit setWindowCaption(url().toDisplayString()); else FcInitReinitialize(); itsPreview->showFont(!package && itsFontDetails.family.isEmpty() ? localFilePath() : fontFile.isEmpty() ? itsFontDetails.family : fontFile, itsFontDetails.styleInfo, fileIndex); if(!isFonts && itsPreview->engine()->getNumIndexes()>1) { showFs=true; itsFaceSelector->setRange(1, itsPreview->engine()->getNumIndexes()); itsFaceSelector->setSingleStep(1); itsFaceSelector->blockSignals(true); itsFaceSelector->setValue(1); itsFaceSelector->blockSignals(false); } itsFaceWidget->setVisible(showFs); } void CFontViewPart::previewStatus(bool st) { if(itsOpening) { bool printable(false); if(st) { checkInstallable(); if(Misc::app(KFI_PRINTER).isEmpty()) printable=false; if(KFI_KIO_FONTS_PROTOCOL==url().scheme()) printable=!Misc::isHidden(url()); else if(!FC::decode(url()).family.isEmpty()) printable=!Misc::isHidden(FC::getFile(url())); #ifdef KFI_PRINT_APP_FONTS else { // TODO: Make this work! Plus, printing of disabled TTF/OTF's should also be possible! KMimeType::Ptr mime=KMimeType::findByUrl(QUrl::fromLocalFile(localFilePath()), 0, false, true); printable=mime->is("application/x-font-ttf") || mime->is("application/x-font-otf"); } #endif } itsExtension->enablePrint(st && printable); itsOpening=false; } itsChangeTextAction->setEnabled(st); // if(st) // getMetaInfo(itsFaceSelector->isVisible() && itsFaceSelector->value()>0 // ? itsFaceSelector->value()-1 : 0); // else if(!st) KMessageBox::error(itsFrame, i18n("Could not read font.")); } void CFontViewPart::install() { if(!itsProc || QProcess::NotRunning==itsProc->state()) { QStringList args; if(!itsProc) itsProc=new QProcess(this); else itsProc->kill(); QString title = QGuiApplication::applicationDisplayName(); if (title.isEmpty()) title = QCoreApplication::applicationName(); args << "--embed" << QString().sprintf("0x%x", (unsigned int)(itsFrame->window()->winId())) << "--qwindowtitle" << title << "--qwindowicon" << "kfontview" << url().toDisplayString(); connect(itsProc, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(installlStatus())); itsProc->start(Misc::app(KFI_INSTALLER), args); itsInstallButton->setEnabled(false); } } void CFontViewPart::installlStatus() { checkInstallable(); } void CFontViewPart::dbusStatus(int pid, int status) { if(pid==getpid() && FontInst::STATUS_OK!=status) itsInstallButton->setEnabled(false); } void CFontViewPart::fontStat(int pid, const KFI::Family &font) { if(pid==getpid()) itsInstallButton->setEnabled(!Misc::app(KFI_INSTALLER).isEmpty() && font.styles().count()==0); } void CFontViewPart::changeText() { bool status; QString oldStr(itsPreview->engine()->getPreviewString()), newStr(QInputDialog::getText(itsFrame, i18n("Preview String"), i18n("Please enter new string:"), QLineEdit::Normal, oldStr, &status)); if(status && newStr!=oldStr) { itsPreview->engine()->setPreviewString(newStr); itsPreview->engine()->writeConfig(*itsConfig); itsPreview->showFont(); } } void CFontViewPart::print() { QStringList args; QString title = QGuiApplication::applicationDisplayName(); if (title.isEmpty()) title = QCoreApplication::applicationName(); if(!itsFontDetails.family.isEmpty()) { args << "--embed" << QString().sprintf("0x%x", (unsigned int)(itsFrame->window()->winId())) << "--qwindowtitle" << title << "--qwindowicon" << "kfontview" << "--size" << "0" << "--pfont" << QString(itsFontDetails.family+','+QString().setNum(itsFontDetails.styleInfo)); } #ifdef KFI_PRINT_APP_FONTS else args << "--embed" << QString().sprintf("0x%x", (unsigned int)(itsFrame->window()->winId())) << "--qwindowtitle" << title << "--qwindowicon" << "kfontview" << "--size " << "0" << localFilePath() << QString().setNum(KFI_NO_STYLE_INFO); #endif if(args.count()) QProcess::startDetached(Misc::app(KFI_PRINTER), args); } void CFontViewPart::displayType(const QList &range) { itsPreview->setUnicodeRange(range); itsChangeTextAction->setEnabled(0==range.count()); } void CFontViewPart::showFace(int face) { itsPreview->showFace(face-1); } void CFontViewPart::checkInstallable() { if(itsFontDetails.family.isEmpty()) { if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(OrgKdeFontinstInterface::staticInterfaceName())) QProcess::startDetached(QLatin1String(KFONTINST_LIB_EXEC_DIR"/fontinst"), QStringList()); itsInstallButton->setEnabled(false); itsInterface->statFont(itsPreview->engine()->descriptiveName(), FontInst::SYS_MASK|FontInst::USR_MASK, getpid()); } } #if 0 void CFontViewPart::getMetaInfo(int face) { if(itsMetaInfo[face].isEmpty()) { // Pass as much inofmration as possible to analyzer... if(KFI_KIO_FONTS_PROTOCOL!=itsMetaUrl.protocol()) { itsMetaUrl.removeQueryItem(KFI_KIO_FACE); if(face>0) itsMetaUrl.addQueryItem(KFI_KIO_FACE, QString().setNum(face)); } KFileMetaInfo meta(itsMetaUrl); if(meta.isValid() && meta.keys().count()) { QStringList keys(meta.keys()); QStringList::const_iterator it(keys.begin()), end(keys.end()); itsMetaInfo[face]=""; for(; it!=end; ++it) { KFileMetaInfoItem mi(meta.item(*it)); itsMetaInfo[face]+=""; } itsMetaInfo[face]+="
"+mi.name()+"
"+ mi.value().toString()+"
"; itsMetaLabel->setText(itsMetaInfo[face]); } else itsMetaLabel->setText(i18n("

No information

")); } else itsMetaLabel->setText(itsMetaInfo[face]); } #endif BrowserExtension::BrowserExtension(CFontViewPart *parent) : KParts::BrowserExtension(parent) { setURLDropHandlingEnabled(true); } void BrowserExtension::enablePrint(bool enable) { if(enable!=isActionEnabled("print") && (!enable || !Misc::app(KFI_PRINTER).isEmpty())) emit enableAction("print", enable); } void BrowserExtension::print() { if(!Misc::app(KFI_PRINTER).isEmpty()) static_cast(parent())->print(); } } #include "FontViewPart.moc"