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.
 
 
 
 
 
 

1900 lines
59 KiB

/*
SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "FontList.h"
#include "Fc.h"
#include "FcEngine.h"
#include "FontInstInterface.h"
#include "GroupList.h"
#include "KfiConstants.h"
#include "XmlStrings.h"
#include <KColorScheme>
#include <KIconLoader>
#include <KMessageBox>
#include <QDBusServiceWatcher>
#include <QDir>
#include <QDrag>
#include <QDropEvent>
#include <QFile>
#include <QFont>
#include <QHeaderView>
#include <QIcon>
#include <QMenu>
#include <QMimeData>
#include <QMimeDatabase>
#include <QPixmap>
#include <QProcess>
#include <QTimer>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
namespace KFI
{
const QStringList CFontList::fontMimeTypes(QStringList() << "font/ttf"
<< "font/otf"
<< "font/collection"
<< "application/x-font-ttf"
<< "application/x-font-otf"
<< "application/x-font-type1"
<< "application/x-font-pcf"
<< "application/x-font-bdf"
<< "application/vnd.kde.fontspackage");
static const int constMaxSlowed = 250;
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.untranslatedText() : name.mid(commaPos + 2);
}
static void addFont(CFontItem *font,
CJobRunner::ItemList &urls,
QStringList &fontNames,
QSet<Misc::TFont> *fonts,
QSet<CFontItem *> &usedFonts,
bool getEnabled,
bool getDisabled)
{
if (!usedFonts.contains(font) && ((getEnabled && font->isEnabled()) || (getDisabled && !font->isEnabled()))) {
urls.append(CJobRunner::Item(font->url(), font->name(), !font->isEnabled()));
fontNames.append(font->name());
usedFonts.insert(font);
if (fonts) {
fonts->insert(Misc::TFont(font->family(), font->styleInfo()));
}
}
}
static QString replaceEnvVar(const QString &text)
{
QString mod(text);
int endPos(text.indexOf('/'));
if (endPos == -1) {
endPos = text.length() - 1;
} else {
endPos--;
}
if (endPos > 0) {
QString envVar(text.mid(1, endPos));
const char *val = getenv(envVar.toLatin1().constData());
if (val) {
mod = Misc::fileSyntax(QFile::decodeName(val) + mod.mid(endPos + 1));
}
}
return mod;
}
//
// Convert from list such as:
//
// Arial
// Arial, Bold
// Courier
// Times
// Times, Italic
//
// To:
//
// Arial (Regular, Bold)
// Coutier
// Times (Regular, Italic)
QStringList CFontList::compact(const QStringList &fonts)
{
QString lastFamily, entry;
QStringList::ConstIterator it(fonts.begin()), end(fonts.end());
QStringList compacted;
QSet<QString> usedStyles;
for (; it != end; ++it) {
QString family, style;
decompose(*it, family, style);
if (family != lastFamily) {
usedStyles.clear();
if (entry.length()) {
entry += ')';
compacted.append(entry);
}
entry = QString(family + " (");
lastFamily = family;
}
if (!usedStyles.contains(style)) {
usedStyles.clear();
if (entry.length() && '(' != entry[entry.length() - 1]) {
entry += ", ";
}
entry += style;
usedStyles.insert(style);
}
}
if (entry.length()) {
entry += ')';
compacted.append(entry);
}
return compacted;
}
QString capitaliseFoundry(const QString &foundry)
{
QString f(foundry.toLower());
if (f == QLatin1String("ibm")) {
return QLatin1String("IBM");
} else if (f == QLatin1String("urw")) {
return QLatin1String("URW");
} else if (f == QLatin1String("itc")) {
return QLatin1String("ITC");
} else if (f == QLatin1String("nec")) {
return QLatin1String("NEC");
} else if (f == QLatin1String("b&h")) {
return QLatin1String("B&H");
} else if (f == QLatin1String("dec")) {
return QLatin1String("DEC");
} else {
QChar *ch(f.data());
int len(f.length());
bool isSpace(true);
while (len--) {
if (isSpace) {
*ch = ch->toUpper();
}
isSpace = ch->isSpace();
++ch;
}
}
return f;
}
inline bool isSysFolder(const QString &sect)
{
return KFI_KIO_FONTS_SYS.toString() == sect || KFI_KIO_FONTS_SYS.untranslatedText() == sect;
}
CFontItem::CFontItem(CFontModelItem *p, const Style &s, bool sys)
: CFontModelItem(p)
, m_styleName(FC::createStyleName(s.value()))
, m_style(s)
{
refresh();
if (!Misc::root()) {
setIsSystem(sys);
}
}
void CFontItem::refresh()
{
FileCont::ConstIterator it(m_style.files().begin()), end(m_style.files().end());
m_enabled = false;
for (; it != end; ++it) {
if (!Misc::isHidden(Misc::getFile((*it).path()))) {
m_enabled = true;
break;
}
}
}
CFamilyItem::CFamilyItem(CFontList &p, const Family &f, bool sys)
: CFontModelItem(nullptr)
, m_status(ENABLED)
, m_realStatus(ENABLED)
, m_regularFont(nullptr)
, m_parent(p)
{
m_name = f.name();
addFonts(f.styles(), sys);
// updateStatus();
}
CFamilyItem::~CFamilyItem()
{
qDeleteAll(m_fonts);
m_fonts.clear();
}
bool CFamilyItem::addFonts(const StyleCont &styles, bool sys)
{
StyleCont::ConstIterator it(styles.begin()), end(styles.end());
bool modified = false;
for (; it != end; ++it) {
CFontItem *font = findFont((*it).value(), sys);
if (!font) {
// New font style!
m_fonts.append(new CFontItem(this, *it, sys));
modified = true;
} else {
int before = (*it).files().size();
font->addFiles((*it).files());
if ((*it).files().size() != before) {
modified = true;
font->refresh();
}
}
}
return modified;
}
CFontItem *CFamilyItem::findFont(quint32 style, bool sys)
{
CFontItemCont::ConstIterator fIt(m_fonts.begin()), fEnd(m_fonts.end());
for (; fIt != fEnd; ++fIt) {
if ((*(*fIt)).styleInfo() == style && (*(*fIt)).isSystem() == sys) {
return (*fIt);
}
}
return nullptr;
}
void CFamilyItem::getFoundries(QSet<QString> &foundries) const
{
CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
for (; it != end; ++it) {
FileCont::ConstIterator fIt((*it)->files().begin()), fEnd((*it)->files().end());
for (; fIt != fEnd; ++fIt) {
if (!(*fIt).foundry().isEmpty()) {
foundries.insert(capitaliseFoundry((*fIt).foundry()));
}
}
}
}
bool CFamilyItem::usable(const CFontItem *font, bool root)
{
return (root || (font->isSystem() && m_parent.allowSys()) || (!font->isSystem() && m_parent.allowUser()));
}
void CFamilyItem::addFont(CFontItem *font, bool update)
{
m_fonts.append(font);
if (update) {
updateStatus();
updateRegularFont(font);
}
}
void CFamilyItem::removeFont(CFontItem *font, bool update)
{
m_fonts.removeAll(font);
if (update) {
updateStatus();
}
if (m_regularFont == font) {
m_regularFont = nullptr;
if (update) {
updateRegularFont(nullptr);
}
}
delete font;
}
void CFamilyItem::refresh()
{
updateStatus();
m_regularFont = nullptr;
updateRegularFont(nullptr);
}
bool CFamilyItem::updateStatus()
{
bool root(Misc::root());
EStatus oldStatus(m_status);
CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
int en(0), dis(0), allEn(0), allDis(0);
bool oldSys(isSystem()), sys(false);
m_fontCount = 0;
for (; it != end; ++it) {
if (usable(*it, root)) {
if ((*it)->isEnabled()) {
en++;
} else {
dis++;
}
if (!sys) {
sys = (*it)->isSystem();
}
m_fontCount++;
} else if ((*it)->isEnabled()) {
allEn++;
} else {
allDis++;
}
}
allEn += en;
allDis += dis;
m_status = en && dis ? PARTIAL : en ? ENABLED : DISABLED;
m_realStatus = allEn && allDis ? PARTIAL : allEn ? ENABLED : DISABLED;
if (!root) {
setIsSystem(sys);
}
return m_status != oldStatus || isSystem() != oldSys;
}
bool CFamilyItem::updateRegularFont(CFontItem *font)
{
static const quint32 constRegular = FC::createStyleVal(FC_WEIGHT_REGULAR, KFI_FC_WIDTH_NORMAL, FC_SLANT_ROMAN);
CFontItem *oldFont(m_regularFont);
bool root(Misc::root());
if (font && usable(font, root)) {
if (m_regularFont) {
int regDiff = abs((long)(m_regularFont->styleInfo() - constRegular)), fontDiff = abs((long)(font->styleInfo() - constRegular));
if (fontDiff < regDiff) {
m_regularFont = font;
}
} else {
m_regularFont = font;
}
} else // This case happens when the regular font is deleted...
{
CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
quint32 current = 0x0FFFFFFF;
for (; it != end; ++it) {
if (usable(*it, root)) {
quint32 diff = abs((long)((*it)->styleInfo() - constRegular));
if (diff < current) {
m_regularFont = (*it);
current = diff;
}
}
}
}
return oldFont != m_regularFont;
}
CFontList::CFontList(QWidget *parent)
: QAbstractItemModel(parent)
, m_allowSys(true)
, m_allowUser(true)
, m_slowUpdates(false)
{
FontInst::registerTypes();
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()),
QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForOwnerChange,
this);
connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &CFontList::dbusServiceOwnerChanged);
connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontsAdded, this, &CFontList::fontsAdded);
connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontsRemoved, this, &CFontList::fontsRemoved);
connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontList, this, &CFontList::fontList);
}
CFontList::~CFontList()
{
qDeleteAll(m_families);
m_families.clear();
m_familyHash.clear();
}
void CFontList::dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to)
{
Q_UNUSED(from);
Q_UNUSED(to);
if (name == QLatin1String(OrgKdeFontinstInterface::staticInterfaceName())) {
load();
}
}
void CFontList::fontList(int pid, const QList<KFI::Families> &families)
{
// printf("**** fontList:%d/%d %d\n", pid, getpid(), families.count());
if (pid == getpid()) {
QList<KFI::Families>::ConstIterator it(families.begin()), end(families.end());
int count(families.size());
for (int i = 0; it != end; ++it, ++i) {
fontsAdded(*it);
Q_EMIT listingPercent(i * 100 / count);
}
Q_EMIT listingPercent(100);
}
}
void CFontList::unsetSlowUpdates()
{
setSlowUpdates(false);
}
void CFontList::load()
{
for (int t = 0; t < NUM_MSGS_TYPES; ++t) {
for (int f = 0; f < FontInst::FOLDER_COUNT; ++f) {
m_slowedMsgs[t][f].clear();
}
}
setSlowUpdates(false);
Q_EMIT layoutAboutToBeChanged();
// beginRemoveRows(QModelIndex(), 0, m_families.count());
m_families.clear();
m_familyHash.clear();
// endRemoveRows();
Q_EMIT layoutChanged();
Q_EMIT listingPercent(0);
CJobRunner::startDbusService();
CJobRunner::dbus()->list(FontInst::SYS_MASK | FontInst::USR_MASK, getpid());
}
void CFontList::setSlowUpdates(bool slow)
{
if (m_slowUpdates != slow) {
if (!slow) {
for (int i = 0; i < FontInst::FOLDER_COUNT; ++i) {
actionSlowedUpdates(i == FontInst::FOLDER_SYS);
}
}
m_slowUpdates = slow;
}
}
int CFontList::columnCount(const QModelIndex &) const
{
return NUM_COLS;
}
QVariant CFontList::data(const QModelIndex &, int) const
{
return QVariant();
}
Qt::ItemFlags CFontList::flags(const QModelIndex &index) const
{
return !index.isValid() ? Qt::ItemIsEnabled | Qt::ItemIsDropEnabled
: Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
Qt::DropActions CFontList::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QMimeData *CFontList::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;
QModelIndexList::ConstIterator it(indexes.begin()), end(indexes.end());
QSet<QString> families;
QDataStream ds(&encodedData, QIODevice::WriteOnly);
for (; it != end; ++it) {
if ((*it).isValid()) {
if ((static_cast<CFontModelItem *>((*it).internalPointer()))->isFont()) {
CFontItem *font = static_cast<CFontItem *>((*it).internalPointer());
families.insert(font->family());
} else {
CFamilyItem *fam = static_cast<CFamilyItem *>((*it).internalPointer());
families.insert(fam->name());
}
}
}
ds << families;
mimeData->setData(KFI_FONT_DRAG_MIME, encodedData);
return mimeData;
}
QStringList CFontList::mimeTypes() const
{
QStringList types;
types << "text/uri-list";
return types;
}
QVariant CFontList::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal) {
switch (role) {
case Qt::DisplayRole:
switch (section) {
case COL_FONT:
return i18n("Font");
case COL_STATUS:
return i18n("Status");
default:
break;
}
break;
// case Qt::DecorationRole:
// if(COL_STATUS==section)
// return QIcon::fromTheme("fontstatus");
// break;
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case Qt::ToolTipRole:
if (COL_STATUS == section) {
return i18n(
"This column shows the status of the font family, and of the "
"individual font styles.");
}
break;
case Qt::WhatsThisRole:
return whatsThis();
default:
break;
}
}
return QVariant();
}
QModelIndex CFontList::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid()) // Then font...
{
CFamilyItem *fam = static_cast<CFamilyItem *>(parent.internalPointer());
if (row < fam->fonts().count()) {
return createIndex(row, column, fam->fonts().at(row));
}
} else // Family....
if (row < m_families.count()) {
return createIndex(row, column, m_families.at(row));
}
return QModelIndex();
}
QModelIndex CFontList::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer());
if (mi->isFamily()) {
return QModelIndex();
} else {
CFontItem *font = static_cast<CFontItem *>(index.internalPointer());
return createIndex(m_families.indexOf((static_cast<CFamilyItem *>(font->parent()))), 0, font->parent());
}
}
int CFontList::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
CFontModelItem *mi = static_cast<CFontModelItem *>(parent.internalPointer());
if (mi->isFont()) {
return 0;
}
CFamilyItem *fam = static_cast<CFamilyItem *>(parent.internalPointer());
return fam->fonts().count();
} else {
return m_families.count();
}
}
void CFontList::refresh(bool allowSys, bool allowUser)
{
m_allowSys = allowSys;
m_allowUser = allowUser;
CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end());
for (; it != end; ++it) {
(*it)->refresh();
}
}
void CFontList::getFamilyStats(QSet<QString> &enabled, QSet<QString> &disabled, QSet<QString> &partial)
{
CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end());
for (; it != end; ++it) {
switch ((*it)->realStatus()) {
case CFamilyItem::ENABLED:
enabled.insert((*it)->name());
break;
case CFamilyItem::PARTIAL:
partial.insert((*it)->name());
break;
case CFamilyItem::DISABLED:
disabled.insert((*it)->name());
break;
}
}
}
void CFontList::getFoundries(QSet<QString> &foundries) const
{
CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end());
for (; it != end; ++it) {
(*it)->getFoundries(foundries);
}
}
QString CFontList::whatsThis() const
{
return i18n(
"<p>This list shows your installed fonts. The fonts are grouped by family, and the"
" number in square brackets represents the number of styles in which the family is"
" available. e.g.</p>"
"<ul>"
"<li>Times [4]"
"<ul><li>Regular</li>"
"<li>Bold</li>"
"<li>Bold Italic</li>"
"<li>Italic</li>"
"</ul>"
"</li>"
"</ul>");
}
void CFontList::fontsAdded(const KFI::Families &families)
{
// printf("**** FONT ADDED:%d\n", families.items.count());
if (m_slowUpdates) {
storeSlowedMessage(families, MSG_ADD);
} else {
addFonts(families.items, families.isSystem && !Misc::root());
}
}
void CFontList::fontsRemoved(const KFI::Families &families)
{
// printf("**** FONT REMOVED:%d\n", families.items.count());
if (m_slowUpdates) {
storeSlowedMessage(families, MSG_DEL);
} else {
removeFonts(families.items, families.isSystem && !Misc::root());
}
}
void CFontList::storeSlowedMessage(const Families &families, EMsgType type)
{
int folder = families.isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER;
bool playOld = false;
for (int i = 0; i < NUM_MSGS_TYPES && !playOld; ++i) {
if (m_slowedMsgs[i][folder].count() > constMaxSlowed) {
playOld = true;
}
}
if (playOld) {
actionSlowedUpdates(families.isSystem);
}
FamilyCont::ConstIterator family(families.items.begin()), fend(families.items.end());
for (; family != fend; ++family) {
FamilyCont::ConstIterator f = m_slowedMsgs[type][folder].find(*family);
if (f != m_slowedMsgs[type][folder].end()) {
StyleCont::ConstIterator style((*family).styles().begin()), send((*family).styles().end());
for (; style != send; ++style) {
StyleCont::ConstIterator st = (*f).styles().find(*style);
if (st == (*f).styles().end()) {
(*f).add(*style);
} else {
(*st).addFiles((*style).files());
}
}
} else {
m_slowedMsgs[type][folder].insert(*family);
}
}
}
void CFontList::actionSlowedUpdates(bool sys)
{
int folder = sys ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER;
for (int i = 0; i < NUM_MSGS_TYPES; ++i) {
if (!m_slowedMsgs[i][folder].isEmpty()) {
if (MSG_ADD == i) {
addFonts(m_slowedMsgs[i][folder], sys && !Misc::root());
} else {
removeFonts(m_slowedMsgs[i][folder], sys && !Misc::root());
}
m_slowedMsgs[i][folder].clear();
}
}
}
void CFontList::addFonts(const FamilyCont &families, bool sys)
{
// bool emitLayout=!m_slowUpdates || m_families.isEmpty();
//
// if(emitLayout)
// Q_EMIT layoutAboutToBeChanged();
FamilyCont::ConstIterator family(families.begin()), end(families.end());
int famRowFrom = m_families.count();
QSet<CFamilyItem *> modifiedFamilies;
for (; family != end; ++family) {
if ((*family).styles().count() > 0) {
CFamilyItem *famItem = findFamily((*family).name());
if (famItem) {
int rowFrom = famItem->fonts().count();
if (famItem->addFonts((*family).styles(), sys)) {
int rowTo = famItem->fonts().count();
if (rowTo != rowFrom) {
beginInsertRows(createIndex(famItem->rowNumber(), 0, famItem), rowFrom, rowTo);
endInsertRows();
}
modifiedFamilies.insert(famItem);
}
} else {
famItem = new CFamilyItem(*this, *family, sys);
m_families.append(famItem);
m_familyHash[famItem->name()] = famItem;
modifiedFamilies.insert(famItem);
}
}
}
int famRowTo = m_families.count();
if (famRowTo != famRowFrom) {
beginInsertRows(QModelIndex(), famRowFrom, famRowTo);
endInsertRows();
}
if (!modifiedFamilies.isEmpty()) {
QSet<CFamilyItem *>::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end());
for (; it != end; ++it) {
(*it)->refresh();
}
}
// if(emitLayout)
// Q_EMIT layoutChanged();
}
void CFontList::removeFonts(const FamilyCont &families, bool sys)
{
// if(!m_slowUpdates)
// Q_EMIT layoutAboutToBeChanged();
FamilyCont::ConstIterator family(families.begin()), end(families.end());
QSet<CFamilyItem *> modifiedFamilies;
for (; family != end; ++family) {
if ((*family).styles().count() > 0) {
CFamilyItem *famItem = findFamily((*family).name());
if (famItem) {
StyleCont::ConstIterator it((*family).styles().begin()), end((*family).styles().end());
for (; it != end; ++it) {
CFontItem *fontItem = famItem->findFont((*it).value(), sys);
if (fontItem) {
int before = fontItem->files().count();
fontItem->removeFiles((*it).files());
if (fontItem->files().count() != before) {
if (fontItem->files().isEmpty()) {
int row = -1;
if (1 != famItem->fonts().count()) {
row = fontItem->rowNumber();
beginRemoveRows(createIndex(famItem->rowNumber(), 0, famItem), row, row);
}
famItem->removeFont(fontItem, false);
if (-1 != row) {
endRemoveRows();
}
} else {
fontItem->refresh();
}
}
}
}
if (famItem->fonts().isEmpty()) {
int row = famItem->rowNumber();
beginRemoveRows(QModelIndex(), row, row);
m_familyHash.remove(famItem->name());
m_families.removeAt(row);
endRemoveRows();
} else {
modifiedFamilies.insert(famItem);
}
}
}
}
if (!modifiedFamilies.isEmpty()) {
QSet<CFamilyItem *>::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end());
for (; it != end; ++it) {
(*it)->refresh();
}
}
// if(!m_slowUpdates)
// Q_EMIT layoutChanged();
}
CFamilyItem *CFontList::findFamily(const QString &familyName)
{
CFamilyItemHash::Iterator it = m_familyHash.find(familyName);
return it == m_familyHash.end() ? nullptr : *it;
}
inline bool matchString(const QString &str, const QString &pattern)
{
return pattern.isEmpty() || -1 != str.indexOf(pattern, 0, Qt::CaseInsensitive);
}
CFontListSortFilterProxy::CFontListSortFilterProxy(QObject *parent, QAbstractItemModel *model)
: QSortFilterProxyModel(parent)
, m_group(nullptr)
, m_filterCriteria(CFontFilter::CRIT_FAMILY)
, m_filterWs(0)
, m_fcQuery(nullptr)
{
setSourceModel(model);
setSortCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(0);
setDynamicSortFilter(false);
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &CFontListSortFilterProxy::timeout);
connect(model, &QAbstractItemModel::layoutChanged, this, &QSortFilterProxyModel::invalidate);
m_timer->setSingleShot(true);
}
QVariant CFontListSortFilterProxy::data(const QModelIndex &idx, int role) const
{
if (!idx.isValid()) {
return QVariant();
}
static const int constMaxFiles = 20;
QModelIndex index(mapToSource(idx));
CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer());
if (!mi) {
return QVariant();
}
switch (role) {
case Qt::ToolTipRole:
if (CFontFilter::CRIT_FILENAME == m_filterCriteria || CFontFilter::CRIT_LOCATION == m_filterCriteria
|| CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) {
if (mi->isFamily()) {
CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer());
CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end());
FileCont allFiles;
QString tip("<b>" + fam->name() + "</b>");
bool markMatch(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria);
tip += "<p style='white-space:pre'><table>";
for (; it != end; ++it) {
allFiles += (*it)->files();
}
// qSort(allFiles);
FileCont::ConstIterator fit(allFiles.begin()), fend(allFiles.end());
for (int i = 0; fit != fend && i < constMaxFiles; ++fit, ++i) {
if (markMatch && m_fcQuery && (*fit).path() == m_fcQuery->file()) {
tip += "<tr><td><b>" + Misc::contractHome((*fit).path()) + "</b></td></tr>";
} else {
tip += "<tr><td>" + Misc::contractHome((*fit).path()) + "</td></tr>";
}
}
if (allFiles.count() > constMaxFiles) {
tip += "<tr><td><i>" + i18n("…plus %1 more", allFiles.count() - constMaxFiles) + "</td></tr>";
}
tip += "</table></p>";
return tip;
} else {
CFontItem *font = static_cast<CFontItem *>(index.internalPointer());
QString tip("<b>" + font->name() + "</b>");
const FileCont &files(font->files());
bool markMatch(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria);
tip += "<p style='white-space:pre'><table>";
// qSort(files);
FileCont::ConstIterator fit(files.begin()), fend(files.end());
for (int i = 0; fit != fend && i < constMaxFiles; ++fit, ++i) {
if (markMatch && m_fcQuery && (*fit).path() == m_fcQuery->file()) {
tip += "<tr><td><b>" + Misc::contractHome((*fit).path()) + "</b></td></tr>";
} else {
tip += "<tr><td>" + Misc::contractHome((*fit).path()) + "</td></tr>";
}
}
if (files.count() > constMaxFiles) {
tip += "<tr><td><i>" + i18n("…plus %1 more", files.count() - constMaxFiles) + "</td></tr></li>";
}
tip += "</table></p>";
return tip;
}
}
break;
case Qt::FontRole:
if (COL_FONT == index.column() && mi->isSystem()) {
QFont font;
font.setItalic(true);
return font;
}
break;
case Qt::ForegroundRole:
if (COL_FONT == index.column()
&& ((mi->isFont() && !(static_cast<CFontItem *>(index.internalPointer()))->isEnabled())
|| (mi->isFamily() && CFamilyItem::DISABLED == (static_cast<CFamilyItem *>(index.internalPointer()))->status()))) {
return KColorScheme(QPalette::Active).foreground(KColorScheme::NegativeText).color();
}
break;
case Qt::DisplayRole:
if (COL_FONT == index.column()) {
if (mi->isFamily()) {
CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer());
return i18n("%1 [%2]", fam->name(), fam->fontCount());
} else {
return (static_cast<CFontItem *>(index.internalPointer()))->style();
}
}
break;
case Qt::DecorationRole:
if (mi->isFamily()) {
CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer());
switch (index.column()) {
case COL_STATUS:
switch (fam->status()) {
case CFamilyItem::PARTIAL:
return QIcon::fromTheme("dialog-ok");
case CFamilyItem::ENABLED:
return QIcon::fromTheme("dialog-ok");
case CFamilyItem::DISABLED:
return QIcon::fromTheme("dialog-cancel");
}
break;
default:
break;
}
} else if (COL_STATUS == index.column()) {
return QIcon::fromTheme((static_cast<CFontItem *>(index.internalPointer()))->isEnabled() ? "dialog-ok" : "dialog-cancel");
}
break;
case Qt::SizeHintRole:
if (mi->isFamily()) {
const int s = KIconLoader::global()->currentSize(KIconLoader::Small);
return QSize(s, s + 4);
}
default:
break;
}
return QVariant();
}
bool CFontListSortFilterProxy::acceptFont(CFontItem *fnt, bool checkFontText) const
{
if (m_group && (CGroupListItem::ALL != m_group->type() || (!filterText().isEmpty() && checkFontText))) {
bool fontMatch(!checkFontText);
if (!fontMatch) {
switch (m_filterCriteria) {
case CFontFilter::CRIT_FONTCONFIG:
fontMatch = m_fcQuery ? fnt->name() == m_fcQuery->font() // || fnt->files().contains(m_fcQuery->file())
: false;
break;
case CFontFilter::CRIT_STYLE:
fontMatch = matchString(fnt->style(), m_filterText);
break;
case CFontFilter::CRIT_FOUNDRY: {
FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end());
for (; it != end && !fontMatch; ++it) {
fontMatch = 0 == (*it).foundry().compare(m_filterText, Qt::CaseInsensitive);
}
break;
}
case CFontFilter::CRIT_FILENAME: {
FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end());
for (; it != end && !fontMatch; ++it) {
QString file(Misc::getFile((*it).path()));
int pos(Misc::isHidden(file) ? 1 : 0);
if (pos == file.indexOf(m_filterText, pos, Qt::CaseInsensitive)) {
fontMatch = true;
}
}
break;
}
case CFontFilter::CRIT_LOCATION: {
FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end());
for (; it != end && !fontMatch; ++it) {
if (0 == Misc::getDir((*it).path()).indexOf(m_filterText, 0, Qt::CaseInsensitive)) {
fontMatch = true;
}
}
break;
}
case CFontFilter::CRIT_FILETYPE: {
FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end());
QStringList::ConstIterator mimeEnd(m_filterTypes.constEnd());
for (; it != end && !fontMatch; ++it) {
QStringList::ConstIterator mime(m_filterTypes.constBegin());
for (; mime != mimeEnd; ++mime) {
if (Misc::checkExt((*it).path(), *mime)) {
fontMatch = true;
}
}
}
break;
}
case CFontFilter::CRIT_WS:
fontMatch = fnt->writingSystems() & m_filterWs;
break;
default:
break;
}
}
return fontMatch && m_group->hasFont(fnt);
}
return true;
}
bool CFontListSortFilterProxy::acceptFamily(CFamilyItem *fam) const
{
CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end());
bool familyMatch(CFontFilter::CRIT_FAMILY == m_filterCriteria && matchString(fam->name(), m_filterText));
for (; it != end; ++it) {
if (acceptFont(*it, !familyMatch)) {
return true;
}
}
return false;
}
bool CFontListSortFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex index(sourceModel()->index(sourceRow, 0, sourceParent));
if (index.isValid()) {
CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer());
if (mi->isFont()) {
CFontItem *font = static_cast<CFontItem *>(index.internalPointer());
return acceptFont(font, !(CFontFilter::CRIT_FAMILY == m_filterCriteria && matchString(font->family(), m_filterText)));
} else {
return acceptFamily(static_cast<CFamilyItem *>(index.internalPointer()));
}
}
return false;
}
bool CFontListSortFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if (left.isValid() && right.isValid()) {
CFontModelItem *lmi = static_cast<CFontModelItem *>(left.internalPointer()), *rmi = static_cast<CFontModelItem *>(right.internalPointer());
if (lmi->isFont() < rmi->isFont()) {
return true;
}
if (lmi->isFont()) {
CFontItem *lfi = static_cast<CFontItem *>(left.internalPointer()), *rfi = static_cast<CFontItem *>(right.internalPointer());
if (COL_STATUS == filterKeyColumn()) {
if (lfi->isEnabled() < rfi->isEnabled() || (lfi->isEnabled() == rfi->isEnabled() && lfi->styleInfo() < rfi->styleInfo())) {
return true;
}
} else if (lfi->styleInfo() < rfi->styleInfo()) {
return true;
}
} else {
CFamilyItem *lfi = static_cast<CFamilyItem *>(left.internalPointer()), *rfi = static_cast<CFamilyItem *>(right.internalPointer());
if (COL_STATUS == filterKeyColumn()) {
if (lfi->status() < rfi->status() || (lfi->status() == rfi->status() && QString::localeAwareCompare(lfi->name(), rfi->name()) < 0)) {
return true;
}
} else if (QString::localeAwareCompare(lfi->name(), rfi->name()) < 0) {
return true;
}
}
}
return false;
}
void CFontListSortFilterProxy::setFilterGroup(CGroupListItem *grp)
{
if (grp != m_group) {
// bool wasNull=!m_group;
m_group = grp;
// if(!(wasNull && m_group && CGroupListItem::ALL==m_group->type()))
invalidate();
}
}
void CFontListSortFilterProxy::setFilterText(const QString &text)
{
if (text != m_filterText) {
//
// If we are filtering on file location, then expand ~ to /home/user, etc.
if (CFontFilter::CRIT_LOCATION == m_filterCriteria && !text.isEmpty() && ('~' == text[0] || '$' == text[0])) {
if ('~' == text[0]) {
m_filterText = 1 == text.length() ? QDir::homePath() : QString(text).replace(0, 1, QDir::homePath());
} else {
m_filterText = replaceEnvVar(text);
}
} else {
m_filterText = text;
}
if (m_filterText.isEmpty()) {
m_timer->stop();
timeout();
} else {
m_timer->start(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria ? 750 : 400);
}
}
}
void CFontListSortFilterProxy::setFilterCriteria(CFontFilter::ECriteria crit, qulonglong ws, const QStringList &ft)
{
if (crit != m_filterCriteria || ws != m_filterWs || ft != m_filterTypes) {
m_filterWs = ws;
m_filterCriteria = crit;
m_filterTypes = ft;
if (CFontFilter::CRIT_LOCATION == m_filterCriteria) {
setFilterText(m_filterText);
}
m_timer->stop();
timeout();
}
}
void CFontListSortFilterProxy::timeout()
{
if (CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) {
int commaPos = m_filterText.indexOf(',');
QString query(m_filterText);
if (-1 != commaPos) {
QString style(query.mid(commaPos + 1));
query.truncate(commaPos);
query = query.trimmed();
query += ":style=";
style = style.trimmed();
query += style;
} else {
query = query.trimmed();
}
if (!m_fcQuery) {
m_fcQuery = new CFcQuery(this);
connect(m_fcQuery, &CFcQuery::finished, this, &CFontListSortFilterProxy::fcResults);
}
m_fcQuery->run(query);
} else {
invalidate();
Q_EMIT refresh();
}
}
void CFontListSortFilterProxy::fcResults()
{
if (CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) {
invalidate();
Q_EMIT refresh();
}
}
CFontListView::CFontListView(QWidget *parent, CFontList *model)
: QTreeView(parent)
, m_proxy(new CFontListSortFilterProxy(this, model))
, m_model(model)
, m_allowDrops(false)
{
setModel(m_proxy);
m_model = model;
header()->setStretchLastSection(false);
resizeColumnToContents(COL_STATUS);
header()->setSectionResizeMode(COL_STATUS, QHeaderView::Fixed);
header()->setSectionResizeMode(COL_FONT, QHeaderView::Stretch);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSortingEnabled(true);
sortByColumn(COL_FONT, Qt::AscendingOrder);
setAllColumnsShowFocus(true);
setAlternatingRowColors(true);
setAcceptDrops(true);
setDropIndicatorShown(false);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragDrop);
header()->setSectionsClickable(true);
header()->setSortIndicatorShown(true);
connect(this, &QTreeView::collapsed, this, &CFontListView::itemCollapsed);
connect(header(), &QHeaderView::sectionClicked, this, &CFontListView::setSortColumn);
connect(m_proxy, &CFontListSortFilterProxy::refresh, this, &CFontListView::refresh);
connect(m_model, &CFontList::listingPercent, this, &CFontListView::listingPercent);
setWhatsThis(model->whatsThis());
header()->setWhatsThis(whatsThis());
m_menu = new QMenu(this);
m_deleteAct = m_menu->addAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), this, &CFontListView::del);
m_menu->addSeparator();
m_enableAct = m_menu->addAction(QIcon::fromTheme("font-enable"), i18n("Enable"), this, &CFontListView::enable);
m_disableAct = m_menu->addAction(QIcon::fromTheme("font-disable"), i18n("Disable"), this, &CFontListView::disable);
if (!Misc::app(KFI_VIEWER).isEmpty()) {
m_menu->addSeparator();
}
m_printAct = Misc::app(KFI_VIEWER).isEmpty() ? nullptr : m_menu->addAction(QIcon::fromTheme("document-print"), i18n("Print…"), this, &CFontListView::print);
m_viewAct =
Misc::app(KFI_VIEWER).isEmpty() ? nullptr : m_menu->addAction(QIcon::fromTheme("kfontview"), i18n("Open in Font Viewer"), this, &CFontListView::view);
m_menu->addSeparator();
m_menu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), model, &CFontList::load);
}
void CFontListView::getFonts(CJobRunner::ItemList &urls, QStringList &fontNames, QSet<Misc::TFont> *fonts, bool selected, bool getEnabled, bool getDisabled)
{
QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes());
QSet<CFontItem *> usedFonts;
QModelIndex index;
foreach (index, selectedItems)
if (index.isValid()) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer());
addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled);
} else {
CFamilyItem *fam = static_cast<CFamilyItem *>(realIndex.internalPointer());
for (int ch = 0; ch < fam->fontCount(); ++ch) {
QModelIndex child(m_proxy->mapToSource(index.model()->index(ch, 0, index)));
if (child.isValid() && (static_cast<CFontModelItem *>(child.internalPointer()))->isFont()) {
CFontItem *font = static_cast<CFontItem *>(child.internalPointer());
addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled);
}
}
}
}
}
fontNames = CFontList::compact(fontNames);
}
QSet<QString> CFontListView::getFiles()
{
QModelIndexList items(allIndexes());
QModelIndex index;
QSet<QString> files;
foreach (index, items)
if (index.isValid()) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer());
FileCont::ConstIterator it(font->files().begin()), end(font->files().end());
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);
}
}
}
}
}
return files;
}
void CFontListView::getPrintableFonts(QSet<Misc::TFont> &items, bool selected)
{
QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes());
QModelIndex index;
foreach (index, selectedItems) {
CFontItem *font = nullptr;
if (index.isValid() && 0 == index.column()) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
font = static_cast<CFontItem *>(realIndex.internalPointer());
} else {
CFamilyItem *fam = static_cast<CFamilyItem *>(realIndex.internalPointer());
font = fam->regularFont();
}
}
}
if (font && !font->isBitmap() && font->isEnabled()) {
items.insert(Misc::TFont(font->family(), font->styleInfo()));
}
}
}
void CFontListView::setFilterGroup(CGroupListItem *grp)
{
CGroupListItem *oldGrp(m_proxy->filterGroup());
m_proxy->setFilterGroup(grp);
m_allowDrops = grp && !grp->isCustom();
if (!Misc::root()) {
bool refreshStats(false);
if (!grp || !oldGrp) {
refreshStats = true;
} else {
// Check to see whether we have changed from listing all fonts,
// listing just system or listing personal fonts.
CGroupListItem::EType aType(CGroupListItem::CUSTOM == grp->type() || CGroupListItem::ALL == grp->type()
|| CGroupListItem::UNCLASSIFIED == grp->type()
? CGroupListItem::CUSTOM
: grp->type()),
bType(CGroupListItem::CUSTOM == oldGrp->type() || CGroupListItem::ALL == oldGrp->type() || CGroupListItem::UNCLASSIFIED == oldGrp->type()
? CGroupListItem::CUSTOM
: oldGrp->type());
refreshStats = aType != bType;
}
if (refreshStats) {
m_model->refresh(!grp || !grp->isPersonal(), !grp || !grp->isSystem());
}
}
// when switching groups, for some reason it is not always sorted.
setSortingEnabled(true);
}
void CFontListView::listingPercent(int percent)
{
// when the font list is first loaded, for some reason it is not always sorted.
// re-enabling sorting here seems to fix the issue - BUG 221610
if (100 == percent) {
setSortingEnabled(true);
}
}
void CFontListView::refreshFilter()
{
m_proxy->invalidate();
}
void CFontListView::filterText(const QString &text)
{
m_proxy->setFilterText(text);
}
void CFontListView::filterCriteria(int crit, qulonglong ws, const QStringList &ft)
{
m_proxy->setFilterCriteria((CFontFilter::ECriteria)crit, ws, ft);
}
void CFontListView::stats(int &enabled, int &disabled, int &partial)
{
enabled = disabled = partial = 0;
for (int i = 0; i < m_proxy->rowCount(); ++i) {
QModelIndex idx(m_proxy->index(i, 0, QModelIndex()));
if (!idx.isValid()) {
break;
}
QModelIndex sourceIdx(m_proxy->mapToSource(idx));
if (!sourceIdx.isValid()) {
break;
}
if ((static_cast<CFontModelItem *>(sourceIdx.internalPointer()))->isFamily()) {
switch ((static_cast<CFamilyItem *>(sourceIdx.internalPointer()))->status()) {
case CFamilyItem::ENABLED:
enabled++;
break;
case CFamilyItem::DISABLED:
disabled++;
break;
case CFamilyItem::PARTIAL:
partial++;
break;
}
}
}
}
void CFontListView::selectedStatus(bool &enabled, bool &disabled)
{
QModelIndexList selected(selectedIndexes());
QModelIndex index;
enabled = disabled = false;
foreach (index, selected) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFamily()) {
switch ((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status()) {
case CFamilyItem::ENABLED:
enabled = true;
break;
case CFamilyItem::DISABLED:
disabled = true;
break;
case CFamilyItem::PARTIAL:
enabled = true;
disabled = true;
break;
}
} else {
if ((static_cast<CFontItem *>(realIndex.internalPointer()))->isEnabled()) {
enabled = true;
} else {
disabled = true;
}
}
}
if (enabled && disabled) {
break;
}
}
}
QModelIndexList CFontListView::allFonts()
{
QModelIndexList rv;
int rowCount(m_proxy->rowCount());
for (int i = 0; i < rowCount; ++i) {
QModelIndex idx(m_proxy->index(i, 0, QModelIndex()));
int childRowCount(m_proxy->rowCount(idx));
for (int j = 0; j < childRowCount; ++j) {
QModelIndex child(m_proxy->index(j, 0, idx));
if (child.isValid()) {
rv.append(m_proxy->mapToSource(child));
}
}
}
return rv;
}
void CFontListView::selectFirstFont()
{
if (0 == selectedIndexes().count()) {
for (int i = 0; i < NUM_COLS; ++i) {
QModelIndex idx(m_proxy->index(0, i, QModelIndex()));
if (idx.isValid()) {
selectionModel()->select(idx, QItemSelectionModel::Select);
}
}
}
}
void CFontListView::setSortColumn(int col)
{
if (col != m_proxy->filterKeyColumn()) {
m_proxy->setFilterKeyColumn(col);
m_proxy->invalidate();
}
}
void CFontListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
QAbstractItemView::selectionChanged(selected, deselected);
if (m_model->slowUpdates()) {
return;
}
Q_EMIT itemsSelected(getSelectedItems());
}
QModelIndexList CFontListView::getSelectedItems()
{
//
// Go through current selection, and for any 'font' items that are selected,
// ensure 'family' item is not...
QModelIndexList selectedItems(selectedIndexes()), deselectList;
QModelIndex index;
QSet<CFontModelItem *> selectedFamilies;
bool en(false), dis(false);
foreach (index, selectedItems)
if (index.isValid()) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer());
if (font->isEnabled()) {
en = true;
} else {
dis = true;
}
if (!selectedFamilies.contains(font->parent())) {
selectedFamilies.insert(font->parent());
for (int i = 0; i < NUM_COLS; ++i) {
deselectList.append(m_proxy->mapFromSource(m_model->createIndex(font->parent()->rowNumber(), i, font->parent())));
}
}
} else {
switch ((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status()) {
case CFamilyItem::ENABLED:
en = true;
break;
case CFamilyItem::DISABLED:
dis = true;
break;
case CFamilyItem::PARTIAL:
en = dis = true;
break;
}
}
}
}
if (deselectList.count()) {
foreach (index, deselectList)
selectionModel()->select(index, QItemSelectionModel::Deselect);
}
QModelIndexList sel;
QSet<void *> pointers;
selectedItems = selectedIndexes();
foreach (index, selectedItems) {
QModelIndex idx(m_proxy->mapToSource(index));
if (!pointers.contains(idx.internalPointer())) {
pointers.insert(idx.internalPointer());
sel.append(idx);
}
}
return sel;
}
void CFontListView::itemCollapsed(const QModelIndex &idx)
{
if (idx.isValid()) {
QModelIndex index(m_proxy->mapToSource(idx));
if (index.isValid() && (static_cast<CFontModelItem *>(index.internalPointer()))->isFamily()) {
CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer());
CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end());
for (; it != end; ++it) {
for (int i = 0; i < NUM_COLS; ++i) {
selectionModel()->select(m_proxy->mapFromSource(m_model->createIndex((*it)->rowNumber(), i, *it)), QItemSelectionModel::Deselect);
}
}
}
}
}
static bool isScalable(const QString &str)
{
QByteArray cFile(QFile::encodeName(str));
return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") || Misc::checkExt(cFile, "pfa")
|| Misc::checkExt(cFile, "pfb");
}
void CFontListView::view()
{
// Number of fonts user has selected, before we ask if they really want to view them all...
static const int constMaxBeforePrompt = 10;
QModelIndexList selectedItems(selectedIndexes());
QModelIndex index;
QSet<CFontItem *> fonts;
foreach (index, selectedItems) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
CFontItem *font(static_cast<CFontItem *>(realIndex.internalPointer()));
fonts.insert(font);
} else {
CFontItem *font((static_cast<CFamilyItem *>(realIndex.internalPointer()))->regularFont());
if (font) {
fonts.insert(font);
}
}
}
}
if (fonts.count()
&& (fonts.count() < constMaxBeforePrompt
|| KMessageBox::Yes == KMessageBox::questionYesNo(this, i18n("Open all %1 fonts in font viewer?", fonts.count())))) {
QSet<CFontItem *>::ConstIterator it(fonts.begin()), end(fonts.end());
QStringList args;
for (; it != end; ++it) {
QString file;
int index(0);
if (!(*it)->isEnabled()) {
// For a disabled font, we need to find the first scalable font entry in its file list...
FileCont::ConstIterator fit((*it)->files().begin()), fend((*it)->files().end());
for (; fit != fend; ++fit) {
if (isScalable((*fit).path())) {
file = (*fit).path();
index = (*fit).index();
break;
}
}
if (file.isEmpty()) {
file = (*it)->fileName();
index = (*it)->index();
}
}
args << FC::encode((*it)->family(), (*it)->styleInfo(), file, index).url();
}
QProcess::startDetached(Misc::app(KFI_VIEWER), args);
}
}
QModelIndexList CFontListView::allIndexes()
{
QModelIndexList rv;
int rowCount(m_proxy->rowCount());
for (int i = 0; i < rowCount; ++i) {
QModelIndex idx(m_proxy->index(i, 0, QModelIndex()));
int childRowCount(m_proxy->rowCount(idx));
rv.append(idx);
for (int j = 0; j < childRowCount; ++j) {
QModelIndex child(m_proxy->index(j, 0, idx));
if (child.isValid()) {
rv.append(child);
}
}
}
return rv;
}
void CFontListView::startDrag(Qt::DropActions supportedActions)
{
QModelIndexList indexes(selectedIndexes());
if (indexes.count()) {
QMimeData *data = model()->mimeData(indexes);
if (!data) {
return;
}
QModelIndex index(m_proxy->mapToSource(indexes.first()));
const char *icon = "application-x-font-pcf";
if (index.isValid()) {
CFontItem *font = (static_cast<CFontModelItem *>(index.internalPointer()))->isFont()
? static_cast<CFontItem *>(index.internalPointer())
: (static_cast<CFamilyItem *>(index.internalPointer()))->regularFont();
if (font && !font->isBitmap()) {
// if("application/x-font-type1"==font->mimetype())
// icon="application-x-font-type1";
// else
icon = "application-x-font-ttf";
}
}
QPoint hotspot;
QPixmap pix = QIcon::fromTheme(icon).pixmap(KIconLoader::SizeMedium);
hotspot.setX(0); // pix.width()/2);
hotspot.setY(0); // pix.height()/2);
QDrag *drag = new QDrag(this);
drag->setPixmap(pix);
drag->setMimeData(data);
drag->setHotSpot(hotspot);
drag->exec(supportedActions);
}
}
void CFontListView::dragEnterEvent(QDragEnterEvent *event)
{
if (m_allowDrops && event->mimeData()->hasFormat("text/uri-list")) { // "application/x-kde-urilist" ??
event->acceptProposedAction();
}
}
void CFontListView::dropEvent(QDropEvent *event)
{
if (m_allowDrops && event->mimeData()->hasFormat("text/uri-list")) {
event->acceptProposedAction();
QList<QUrl> urls(event->mimeData()->urls());
QList<QUrl>::ConstIterator it(urls.begin()), end(urls.end());
QSet<QUrl> kurls;
QMimeDatabase db;
for (; it != end; ++it) {
QMimeType mime = db.mimeTypeForUrl(*it);
foreach (const QString &fontMime, CFontList::fontMimeTypes) {
if (mime.inherits(fontMime)) {
kurls.insert(*it);
break;
}
}
}
if (!kurls.isEmpty()) {
Q_EMIT fontsDropped(kurls);
}
}
}
void CFontListView::contextMenuEvent(QContextMenuEvent *ev)
{
bool valid(indexAt(ev->pos()).isValid());
m_deleteAct->setEnabled(valid);
bool en(false), dis(false);
QModelIndexList selectedItems(selectedIndexes());
QModelIndex index;
foreach (index, selectedItems) {
QModelIndex realIndex(m_proxy->mapToSource(index));
if (realIndex.isValid()) {
if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) {
if ((static_cast<CFontItem *>(realIndex.internalPointer())->isEnabled())) {
en = true;
} else {
dis = true;
}
} else {
switch ((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status()) {
case CFamilyItem::ENABLED:
en = true;
break;
case CFamilyItem::DISABLED:
dis = true;
break;
case CFamilyItem::PARTIAL:
en = dis = true;
break;
}
}
}
if (en && dis) {
break;
}
}
m_enableAct->setEnabled(dis);
m_disableAct->setEnabled(en);
if (m_printAct) {
m_printAct->setEnabled(en | dis);
}
if (m_viewAct) {
m_viewAct->setEnabled(en | dis);
}
m_menu->popup(ev->globalPos());
}
bool CFontListView::viewportEvent(QEvent *event)
{
executeDelayedItemsLayout();
return QTreeView::viewportEvent(event);
}
}