File indexing completed on 2025-01-26 05:08:08
0001 /* 0002 SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "FontList.h" 0007 #include "Fc.h" 0008 #include "FontInstInterface.h" 0009 #include "GroupList.h" 0010 #include "KfiConstants.h" 0011 #include <KColorScheme> 0012 #include <KIconLoader> 0013 #include <KMessageBox> 0014 #include <QDBusServiceWatcher> 0015 #include <QDir> 0016 #include <QDrag> 0017 #include <QDropEvent> 0018 #include <QFile> 0019 #include <QFont> 0020 #include <QHeaderView> 0021 #include <QIcon> 0022 #include <QMenu> 0023 #include <QMimeData> 0024 #include <QMimeDatabase> 0025 #include <QPixmap> 0026 #include <QProcess> 0027 #include <QTimer> 0028 #include <stdlib.h> 0029 #include <unistd.h> 0030 #include <utime.h> 0031 0032 namespace KFI 0033 { 0034 const QStringList CFontList::fontMimeTypes(QStringList() << "font/ttf" 0035 << "font/otf" 0036 << "font/collection" 0037 << "application/x-font-ttf" 0038 << "application/x-font-otf" 0039 << "application/x-font-type1" 0040 << "application/x-font-pcf" 0041 << "application/x-font-bdf" 0042 << "application/vnd.kde.fontspackage"); 0043 0044 static const int constMaxSlowed = 250; 0045 0046 static void decompose(const QString &name, QString &family, QString &style) 0047 { 0048 int commaPos = name.lastIndexOf(','); 0049 0050 family = -1 == commaPos ? name : name.left(commaPos); 0051 style = -1 == commaPos ? KFI_WEIGHT_REGULAR.untranslatedText() : name.mid(commaPos + 2); 0052 } 0053 0054 static void addFont(CFontItem *font, 0055 CJobRunner::ItemList &urls, 0056 QStringList &fontNames, 0057 QSet<Misc::TFont> *fonts, 0058 QSet<CFontItem *> &usedFonts, 0059 bool getEnabled, 0060 bool getDisabled) 0061 { 0062 if (!usedFonts.contains(font) && ((getEnabled && font->isEnabled()) || (getDisabled && !font->isEnabled()))) { 0063 urls.append(CJobRunner::Item(font->url(), font->name(), !font->isEnabled())); 0064 fontNames.append(font->name()); 0065 usedFonts.insert(font); 0066 if (fonts) { 0067 fonts->insert(Misc::TFont(font->family(), font->styleInfo())); 0068 } 0069 } 0070 } 0071 0072 static QString replaceEnvVar(const QString &text) 0073 { 0074 QString mod(text); 0075 int endPos(text.indexOf('/')); 0076 0077 if (endPos == -1) { 0078 endPos = text.length() - 1; 0079 } else { 0080 endPos--; 0081 } 0082 0083 if (endPos > 0) { 0084 QString envVar(text.mid(1, endPos)); 0085 0086 const char *val = getenv(envVar.toLatin1().constData()); 0087 0088 if (val) { 0089 mod = Misc::fileSyntax(QFile::decodeName(val) + mod.mid(endPos + 1)); 0090 } 0091 } 0092 0093 return mod; 0094 } 0095 0096 // 0097 // Convert from list such as: 0098 // 0099 // Arial 0100 // Arial, Bold 0101 // Courier 0102 // Times 0103 // Times, Italic 0104 // 0105 // To: 0106 // 0107 // Arial (Regular, Bold) 0108 // Coutier 0109 // Times (Regular, Italic) 0110 QStringList CFontList::compact(const QStringList &fonts) 0111 { 0112 QString lastFamily, entry; 0113 QStringList::ConstIterator it(fonts.begin()), end(fonts.end()); 0114 QStringList compacted; 0115 QSet<QString> usedStyles; 0116 0117 for (; it != end; ++it) { 0118 QString family, style; 0119 0120 decompose(*it, family, style); 0121 0122 if (family != lastFamily) { 0123 usedStyles.clear(); 0124 if (entry.length()) { 0125 entry += ')'; 0126 compacted.append(entry); 0127 } 0128 entry = QString(family + " ("); 0129 lastFamily = family; 0130 } 0131 if (!usedStyles.contains(style)) { 0132 usedStyles.clear(); 0133 if (entry.length() && '(' != entry[entry.length() - 1]) { 0134 entry += ", "; 0135 } 0136 entry += style; 0137 usedStyles.insert(style); 0138 } 0139 } 0140 0141 if (entry.length()) { 0142 entry += ')'; 0143 compacted.append(entry); 0144 } 0145 0146 return compacted; 0147 } 0148 0149 QString capitaliseFoundry(const QString &foundry) 0150 { 0151 QString f(foundry.toLower()); 0152 0153 if (f == QLatin1String("ibm")) { 0154 return QLatin1String("IBM"); 0155 } else if (f == QLatin1String("urw")) { 0156 return QLatin1String("URW"); 0157 } else if (f == QLatin1String("itc")) { 0158 return QLatin1String("ITC"); 0159 } else if (f == QLatin1String("nec")) { 0160 return QLatin1String("NEC"); 0161 } else if (f == QLatin1String("b&h")) { 0162 return QLatin1String("B&H"); 0163 } else if (f == QLatin1String("dec")) { 0164 return QLatin1String("DEC"); 0165 } else { 0166 QChar *ch(f.data()); 0167 int len(f.length()); 0168 bool isSpace(true); 0169 0170 while (len--) { 0171 if (isSpace) { 0172 *ch = ch->toUpper(); 0173 } 0174 0175 isSpace = ch->isSpace(); 0176 ++ch; 0177 } 0178 } 0179 0180 return f; 0181 } 0182 0183 inline bool isSysFolder(const QString §) 0184 { 0185 return KFI_KIO_FONTS_SYS.toString() == sect || KFI_KIO_FONTS_SYS.untranslatedText() == sect; 0186 } 0187 0188 CFontItem::CFontItem(CFontModelItem *p, const Style &s, bool sys) 0189 : CFontModelItem(p) 0190 , m_styleName(FC::createStyleName(s.value())) 0191 , m_style(s) 0192 { 0193 refresh(); 0194 if (!Misc::root()) { 0195 setIsSystem(sys); 0196 } 0197 } 0198 0199 void CFontItem::refresh() 0200 { 0201 FileCont::ConstIterator it(m_style.files().begin()), end(m_style.files().end()); 0202 0203 m_enabled = false; 0204 for (; it != end; ++it) { 0205 if (!Misc::isHidden(Misc::getFile((*it).path()))) { 0206 m_enabled = true; 0207 break; 0208 } 0209 } 0210 } 0211 0212 CFamilyItem::CFamilyItem(CFontList &p, const Family &f, bool sys) 0213 : CFontModelItem(nullptr) 0214 , m_name(f.name()) 0215 , m_status(ENABLED) 0216 , m_realStatus(ENABLED) 0217 , m_regularFont(nullptr) 0218 , m_parent(p) 0219 { 0220 addFonts(f.styles(), sys); 0221 // updateStatus(); 0222 } 0223 0224 CFamilyItem::~CFamilyItem() 0225 { 0226 qDeleteAll(m_fonts); 0227 m_fonts.clear(); 0228 } 0229 0230 bool CFamilyItem::addFonts(const StyleCont &styles, bool sys) 0231 { 0232 StyleCont::ConstIterator it(styles.begin()), end(styles.end()); 0233 bool modified = false; 0234 0235 for (; it != end; ++it) { 0236 CFontItem *font = findFont((*it).value(), sys); 0237 0238 if (!font) { 0239 // New font style! 0240 m_fonts.append(new CFontItem(this, *it, sys)); 0241 modified = true; 0242 } else { 0243 int before = (*it).files().size(); 0244 0245 font->addFiles((*it).files()); 0246 0247 if ((*it).files().size() != before) { 0248 modified = true; 0249 font->refresh(); 0250 } 0251 } 0252 } 0253 return modified; 0254 } 0255 0256 CFontItem *CFamilyItem::findFont(quint32 style, bool sys) 0257 { 0258 CFontItemCont::ConstIterator fIt(m_fonts.begin()), fEnd(m_fonts.end()); 0259 0260 for (; fIt != fEnd; ++fIt) { 0261 if ((*(*fIt)).styleInfo() == style && (*(*fIt)).isSystem() == sys) { 0262 return (*fIt); 0263 } 0264 } 0265 0266 return nullptr; 0267 } 0268 0269 void CFamilyItem::getFoundries(QSet<QString> &foundries) const 0270 { 0271 CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end()); 0272 0273 for (; it != end; ++it) { 0274 FileCont::ConstIterator fIt((*it)->files().begin()), fEnd((*it)->files().end()); 0275 0276 for (; fIt != fEnd; ++fIt) { 0277 if (!(*fIt).foundry().isEmpty()) { 0278 foundries.insert(capitaliseFoundry((*fIt).foundry())); 0279 } 0280 } 0281 } 0282 } 0283 0284 bool CFamilyItem::usable(const CFontItem *font, bool root) 0285 { 0286 return (root || (font->isSystem() && m_parent.allowSys()) || (!font->isSystem() && m_parent.allowUser())); 0287 } 0288 0289 void CFamilyItem::addFont(CFontItem *font, bool update) 0290 { 0291 m_fonts.append(font); 0292 if (update) { 0293 updateStatus(); 0294 updateRegularFont(font); 0295 } 0296 } 0297 0298 void CFamilyItem::removeFont(CFontItem *font, bool update) 0299 { 0300 m_fonts.removeAll(font); 0301 if (update) { 0302 updateStatus(); 0303 } 0304 if (m_regularFont == font) { 0305 m_regularFont = nullptr; 0306 if (update) { 0307 updateRegularFont(nullptr); 0308 } 0309 } 0310 delete font; 0311 } 0312 0313 void CFamilyItem::refresh() 0314 { 0315 updateStatus(); 0316 m_regularFont = nullptr; 0317 updateRegularFont(nullptr); 0318 } 0319 0320 bool CFamilyItem::updateStatus() 0321 { 0322 bool root(Misc::root()); 0323 EStatus oldStatus(m_status); 0324 CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end()); 0325 int en(0), dis(0), allEn(0), allDis(0); 0326 bool oldSys(isSystem()), sys(false); 0327 0328 m_fontCount = 0; 0329 for (; it != end; ++it) { 0330 if (usable(*it, root)) { 0331 if ((*it)->isEnabled()) { 0332 en++; 0333 } else { 0334 dis++; 0335 } 0336 if (!sys) { 0337 sys = (*it)->isSystem(); 0338 } 0339 m_fontCount++; 0340 } else if ((*it)->isEnabled()) { 0341 allEn++; 0342 } else { 0343 allDis++; 0344 } 0345 } 0346 0347 allEn += en; 0348 allDis += dis; 0349 0350 m_status = en && dis ? PARTIAL : en ? ENABLED : DISABLED; 0351 0352 m_realStatus = allEn && allDis ? PARTIAL : allEn ? ENABLED : DISABLED; 0353 0354 if (!root) { 0355 setIsSystem(sys); 0356 } 0357 0358 return m_status != oldStatus || isSystem() != oldSys; 0359 } 0360 0361 bool CFamilyItem::updateRegularFont(CFontItem *font) 0362 { 0363 static const quint32 constRegular = FC::createStyleVal(FC_WEIGHT_REGULAR, KFI_FC_WIDTH_NORMAL, FC_SLANT_ROMAN); 0364 0365 CFontItem *oldFont(m_regularFont); 0366 bool root(Misc::root()); 0367 0368 if (font && usable(font, root)) { 0369 if (m_regularFont) { 0370 int regDiff = abs((long)(m_regularFont->styleInfo() - constRegular)), fontDiff = abs((long)(font->styleInfo() - constRegular)); 0371 0372 if (fontDiff < regDiff) { 0373 m_regularFont = font; 0374 } 0375 } else { 0376 m_regularFont = font; 0377 } 0378 } else // This case happens when the regular font is deleted... 0379 { 0380 CFontItemCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end()); 0381 quint32 current = 0x0FFFFFFF; 0382 0383 for (; it != end; ++it) { 0384 if (usable(*it, root)) { 0385 quint32 diff = abs((long)((*it)->styleInfo() - constRegular)); 0386 0387 if (diff < current) { 0388 m_regularFont = (*it); 0389 current = diff; 0390 } 0391 } 0392 } 0393 } 0394 0395 return oldFont != m_regularFont; 0396 } 0397 0398 CFontList::CFontList(QWidget *parent) 0399 : QAbstractItemModel(parent) 0400 , m_allowSys(true) 0401 , m_allowUser(true) 0402 , m_slowUpdates(false) 0403 { 0404 FontInst::registerTypes(); 0405 0406 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String(OrgKdeFontinstInterface::staticInterfaceName()), 0407 QDBusConnection::sessionBus(), 0408 QDBusServiceWatcher::WatchForOwnerChange, 0409 this); 0410 0411 connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &CFontList::dbusServiceOwnerChanged); 0412 connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontsAdded, this, &CFontList::fontsAdded); 0413 connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontsRemoved, this, &CFontList::fontsRemoved); 0414 connect(CJobRunner::dbus(), &OrgKdeFontinstInterface::fontList, this, &CFontList::fontList); 0415 } 0416 0417 CFontList::~CFontList() 0418 { 0419 qDeleteAll(m_families); 0420 m_families.clear(); 0421 m_familyHash.clear(); 0422 } 0423 0424 void CFontList::dbusServiceOwnerChanged(const QString &name, const QString &from, const QString &to) 0425 { 0426 Q_UNUSED(from); 0427 Q_UNUSED(to); 0428 0429 if (name == QLatin1String(OrgKdeFontinstInterface::staticInterfaceName())) { 0430 load(); 0431 } 0432 } 0433 0434 void CFontList::fontList(int pid, const QList<KFI::Families> &families) 0435 { 0436 // printf("**** fontList:%d/%d %d\n", pid, getpid(), families.count()); 0437 0438 if (pid == getpid()) { 0439 QList<KFI::Families>::ConstIterator it(families.begin()), end(families.end()); 0440 int count(families.size()); 0441 0442 for (int i = 0; it != end; ++it, ++i) { 0443 fontsAdded(*it); 0444 Q_EMIT listingPercent(i * 100 / count); 0445 } 0446 Q_EMIT listingPercent(100); 0447 } 0448 } 0449 0450 void CFontList::unsetSlowUpdates() 0451 { 0452 setSlowUpdates(false); 0453 } 0454 0455 void CFontList::load() 0456 { 0457 for (int t = 0; t < NUM_MSGS_TYPES; ++t) { 0458 for (int f = 0; f < FontInst::FOLDER_COUNT; ++f) { 0459 m_slowedMsgs[t][f].clear(); 0460 } 0461 } 0462 0463 setSlowUpdates(false); 0464 0465 Q_EMIT layoutAboutToBeChanged(); 0466 // beginRemoveRows(QModelIndex(), 0, m_families.count()); 0467 m_families.clear(); 0468 m_familyHash.clear(); 0469 // endRemoveRows(); 0470 Q_EMIT layoutChanged(); 0471 Q_EMIT listingPercent(0); 0472 0473 CJobRunner::startDbusService(); 0474 CJobRunner::dbus()->list(FontInst::SYS_MASK | FontInst::USR_MASK, getpid()); 0475 } 0476 0477 void CFontList::setSlowUpdates(bool slow) 0478 { 0479 if (m_slowUpdates != slow) { 0480 if (!slow) { 0481 for (int i = 0; i < FontInst::FOLDER_COUNT; ++i) { 0482 actionSlowedUpdates(i == FontInst::FOLDER_SYS); 0483 } 0484 } 0485 m_slowUpdates = slow; 0486 } 0487 } 0488 0489 int CFontList::columnCount(const QModelIndex &) const 0490 { 0491 return NUM_COLS; 0492 } 0493 0494 QVariant CFontList::data(const QModelIndex &, int) const 0495 { 0496 return QVariant(); 0497 } 0498 0499 Qt::ItemFlags CFontList::flags(const QModelIndex &index) const 0500 { 0501 return !index.isValid() ? Qt::ItemIsEnabled | Qt::ItemIsDropEnabled 0502 : Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; 0503 } 0504 0505 Qt::DropActions CFontList::supportedDropActions() const 0506 { 0507 return Qt::CopyAction | Qt::MoveAction; 0508 } 0509 0510 QMimeData *CFontList::mimeData(const QModelIndexList &indexes) const 0511 { 0512 QMimeData *mimeData = new QMimeData(); 0513 QByteArray encodedData; 0514 QModelIndexList::ConstIterator it(indexes.begin()), end(indexes.end()); 0515 QSet<QString> families; 0516 QDataStream ds(&encodedData, QIODevice::WriteOnly); 0517 0518 for (; it != end; ++it) { 0519 if ((*it).isValid()) { 0520 if ((static_cast<CFontModelItem *>((*it).internalPointer()))->isFont()) { 0521 CFontItem *font = static_cast<CFontItem *>((*it).internalPointer()); 0522 0523 families.insert(font->family()); 0524 } else { 0525 CFamilyItem *fam = static_cast<CFamilyItem *>((*it).internalPointer()); 0526 0527 families.insert(fam->name()); 0528 } 0529 } 0530 } 0531 0532 ds << families; 0533 mimeData->setData(KFI_FONT_DRAG_MIME, encodedData); 0534 return mimeData; 0535 } 0536 0537 QStringList CFontList::mimeTypes() const 0538 { 0539 QStringList types; 0540 0541 types << "text/uri-list"; 0542 return types; 0543 } 0544 0545 QVariant CFontList::headerData(int section, Qt::Orientation orientation, int role) const 0546 { 0547 if (orientation == Qt::Horizontal) { 0548 switch (role) { 0549 case Qt::DisplayRole: 0550 switch (section) { 0551 case COL_FONT: 0552 return i18n("Font"); 0553 case COL_STATUS: 0554 return i18n("Status"); 0555 default: 0556 break; 0557 } 0558 break; 0559 // case Qt::DecorationRole: 0560 // if(COL_STATUS==section) 0561 // return QIcon::fromTheme("fontstatus"); 0562 // break; 0563 case Qt::TextAlignmentRole: 0564 return QVariant(Qt::AlignLeft | Qt::AlignVCenter); 0565 case Qt::ToolTipRole: 0566 if (COL_STATUS == section) { 0567 return i18n( 0568 "This column shows the status of the font family, and of the " 0569 "individual font styles."); 0570 } 0571 break; 0572 case Qt::WhatsThisRole: 0573 return whatsThis(); 0574 default: 0575 break; 0576 } 0577 } 0578 0579 return QVariant(); 0580 } 0581 0582 QModelIndex CFontList::index(int row, int column, const QModelIndex &parent) const 0583 { 0584 if (parent.isValid()) // Then font... 0585 { 0586 CFamilyItem *fam = static_cast<CFamilyItem *>(parent.internalPointer()); 0587 0588 if (row < fam->fonts().count()) { 0589 return createIndex(row, column, fam->fonts().at(row)); 0590 } 0591 } else // Family.... 0592 if (row < m_families.count()) { 0593 return createIndex(row, column, m_families.at(row)); 0594 } 0595 0596 return QModelIndex(); 0597 } 0598 0599 QModelIndex CFontList::parent(const QModelIndex &index) const 0600 { 0601 if (!index.isValid()) { 0602 return QModelIndex(); 0603 } 0604 0605 CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer()); 0606 0607 if (mi->isFamily()) { 0608 return QModelIndex(); 0609 } else { 0610 CFontItem *font = static_cast<CFontItem *>(index.internalPointer()); 0611 0612 return createIndex(m_families.indexOf((static_cast<CFamilyItem *>(font->parent()))), 0, font->parent()); 0613 } 0614 } 0615 0616 int CFontList::rowCount(const QModelIndex &parent) const 0617 { 0618 if (parent.isValid()) { 0619 CFontModelItem *mi = static_cast<CFontModelItem *>(parent.internalPointer()); 0620 0621 if (mi->isFont()) { 0622 return 0; 0623 } 0624 0625 CFamilyItem *fam = static_cast<CFamilyItem *>(parent.internalPointer()); 0626 0627 return fam->fonts().count(); 0628 } else { 0629 return m_families.count(); 0630 } 0631 } 0632 0633 void CFontList::refresh(bool allowSys, bool allowUser) 0634 { 0635 m_allowSys = allowSys; 0636 m_allowUser = allowUser; 0637 CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end()); 0638 0639 for (; it != end; ++it) { 0640 (*it)->refresh(); 0641 } 0642 } 0643 0644 void CFontList::getFamilyStats(QSet<QString> &enabled, QSet<QString> &disabled, QSet<QString> &partial) 0645 { 0646 CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end()); 0647 0648 for (; it != end; ++it) { 0649 switch ((*it)->realStatus()) { 0650 case CFamilyItem::ENABLED: 0651 enabled.insert((*it)->name()); 0652 break; 0653 case CFamilyItem::PARTIAL: 0654 partial.insert((*it)->name()); 0655 break; 0656 case CFamilyItem::DISABLED: 0657 disabled.insert((*it)->name()); 0658 break; 0659 } 0660 } 0661 } 0662 0663 void CFontList::getFoundries(QSet<QString> &foundries) const 0664 { 0665 CFamilyItemCont::ConstIterator it(m_families.begin()), end(m_families.end()); 0666 0667 for (; it != end; ++it) { 0668 (*it)->getFoundries(foundries); 0669 } 0670 } 0671 0672 QString CFontList::whatsThis() const 0673 { 0674 return i18n( 0675 "<p>This list shows your installed fonts. The fonts are grouped by family, and the" 0676 " number in square brackets represents the number of styles in which the family is" 0677 " available. e.g.</p>" 0678 "<ul>" 0679 "<li>Times [4]" 0680 "<ul><li>Regular</li>" 0681 "<li>Bold</li>" 0682 "<li>Bold Italic</li>" 0683 "<li>Italic</li>" 0684 "</ul>" 0685 "</li>" 0686 "</ul>"); 0687 } 0688 0689 void CFontList::fontsAdded(const KFI::Families &families) 0690 { 0691 // printf("**** FONT ADDED:%d\n", families.items.count()); 0692 if (m_slowUpdates) { 0693 storeSlowedMessage(families, MSG_ADD); 0694 } else { 0695 addFonts(families.items, families.isSystem && !Misc::root()); 0696 } 0697 } 0698 0699 void CFontList::fontsRemoved(const KFI::Families &families) 0700 { 0701 // printf("**** FONT REMOVED:%d\n", families.items.count()); 0702 if (m_slowUpdates) { 0703 storeSlowedMessage(families, MSG_DEL); 0704 } else { 0705 removeFonts(families.items, families.isSystem && !Misc::root()); 0706 } 0707 } 0708 0709 void CFontList::storeSlowedMessage(const Families &families, EMsgType type) 0710 { 0711 int folder = families.isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER; 0712 bool playOld = false; 0713 0714 for (int i = 0; i < NUM_MSGS_TYPES && !playOld; ++i) { 0715 if (m_slowedMsgs[i][folder].count() > constMaxSlowed) { 0716 playOld = true; 0717 } 0718 } 0719 0720 if (playOld) { 0721 actionSlowedUpdates(families.isSystem); 0722 } 0723 0724 FamilyCont::ConstIterator family(families.items.begin()), fend(families.items.end()); 0725 0726 for (; family != fend; ++family) { 0727 FamilyCont::ConstIterator f = m_slowedMsgs[type][folder].find(*family); 0728 0729 if (f != m_slowedMsgs[type][folder].end()) { 0730 StyleCont::ConstIterator style((*family).styles().begin()), send((*family).styles().end()); 0731 0732 for (; style != send; ++style) { 0733 StyleCont::ConstIterator st = (*f).styles().find(*style); 0734 0735 if (st == (*f).styles().end()) { 0736 (*f).add(*style); 0737 } else { 0738 (*st).addFiles((*style).files()); 0739 } 0740 } 0741 } else { 0742 m_slowedMsgs[type][folder].insert(*family); 0743 } 0744 } 0745 } 0746 0747 void CFontList::actionSlowedUpdates(bool sys) 0748 { 0749 int folder = sys ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER; 0750 0751 for (int i = 0; i < NUM_MSGS_TYPES; ++i) { 0752 if (!m_slowedMsgs[i][folder].isEmpty()) { 0753 if (MSG_ADD == i) { 0754 addFonts(m_slowedMsgs[i][folder], sys && !Misc::root()); 0755 } else { 0756 removeFonts(m_slowedMsgs[i][folder], sys && !Misc::root()); 0757 } 0758 m_slowedMsgs[i][folder].clear(); 0759 } 0760 } 0761 } 0762 0763 void CFontList::addFonts(const FamilyCont &families, bool sys) 0764 { 0765 // bool emitLayout=!m_slowUpdates || m_families.isEmpty(); 0766 // 0767 // if(emitLayout) 0768 // Q_EMIT layoutAboutToBeChanged(); 0769 0770 FamilyCont::ConstIterator family(families.begin()), end(families.end()); 0771 int famRowFrom = m_families.count(); 0772 QSet<CFamilyItem *> modifiedFamilies; 0773 0774 for (; family != end; ++family) { 0775 if ((*family).styles().count() > 0) { 0776 CFamilyItem *famItem = findFamily((*family).name()); 0777 0778 if (famItem) { 0779 int rowFrom = famItem->fonts().count(); 0780 if (famItem->addFonts((*family).styles(), sys)) { 0781 int rowTo = famItem->fonts().count(); 0782 0783 if (rowTo != rowFrom) { 0784 beginInsertRows(createIndex(famItem->rowNumber(), 0, famItem), rowFrom, rowTo); 0785 endInsertRows(); 0786 } 0787 0788 modifiedFamilies.insert(famItem); 0789 } 0790 } else { 0791 famItem = new CFamilyItem(*this, *family, sys); 0792 m_families.append(famItem); 0793 m_familyHash[famItem->name()] = famItem; 0794 modifiedFamilies.insert(famItem); 0795 } 0796 } 0797 } 0798 0799 int famRowTo = m_families.count(); 0800 if (famRowTo != famRowFrom) { 0801 beginInsertRows(QModelIndex(), famRowFrom, famRowTo); 0802 endInsertRows(); 0803 } 0804 0805 if (!modifiedFamilies.isEmpty()) { 0806 QSet<CFamilyItem *>::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end()); 0807 0808 for (; it != end; ++it) { 0809 (*it)->refresh(); 0810 } 0811 } 0812 0813 // if(emitLayout) 0814 // Q_EMIT layoutChanged(); 0815 } 0816 0817 void CFontList::removeFonts(const FamilyCont &families, bool sys) 0818 { 0819 // if(!m_slowUpdates) 0820 // Q_EMIT layoutAboutToBeChanged(); 0821 0822 FamilyCont::ConstIterator family(families.begin()), end(families.end()); 0823 QSet<CFamilyItem *> modifiedFamilies; 0824 0825 for (; family != end; ++family) { 0826 if ((*family).styles().count() > 0) { 0827 CFamilyItem *famItem = findFamily((*family).name()); 0828 0829 if (famItem) { 0830 StyleCont::ConstIterator it((*family).styles().begin()), end((*family).styles().end()); 0831 0832 for (; it != end; ++it) { 0833 CFontItem *fontItem = famItem->findFont((*it).value(), sys); 0834 0835 if (fontItem) { 0836 int before = fontItem->files().count(); 0837 0838 fontItem->removeFiles((*it).files()); 0839 0840 if (fontItem->files().count() != before) { 0841 if (fontItem->files().isEmpty()) { 0842 int row = -1; 0843 if (1 != famItem->fonts().count()) { 0844 row = fontItem->rowNumber(); 0845 beginRemoveRows(createIndex(famItem->rowNumber(), 0, famItem), row, row); 0846 } 0847 famItem->removeFont(fontItem, false); 0848 if (-1 != row) { 0849 endRemoveRows(); 0850 } 0851 } else { 0852 fontItem->refresh(); 0853 } 0854 } 0855 } 0856 } 0857 0858 if (famItem->fonts().isEmpty()) { 0859 int row = famItem->rowNumber(); 0860 beginRemoveRows(QModelIndex(), row, row); 0861 m_familyHash.remove(famItem->name()); 0862 m_families.removeAt(row); 0863 endRemoveRows(); 0864 } else { 0865 modifiedFamilies.insert(famItem); 0866 } 0867 } 0868 } 0869 } 0870 0871 if (!modifiedFamilies.isEmpty()) { 0872 QSet<CFamilyItem *>::Iterator it(modifiedFamilies.begin()), end(modifiedFamilies.end()); 0873 0874 for (; it != end; ++it) { 0875 (*it)->refresh(); 0876 } 0877 } 0878 0879 // if(!m_slowUpdates) 0880 // Q_EMIT layoutChanged(); 0881 } 0882 0883 CFamilyItem *CFontList::findFamily(const QString &familyName) 0884 { 0885 CFamilyItemHash::Iterator it = m_familyHash.find(familyName); 0886 0887 return it == m_familyHash.end() ? nullptr : *it; 0888 } 0889 0890 inline bool matchString(const QString &str, const QString &pattern) 0891 { 0892 return pattern.isEmpty() || -1 != str.indexOf(pattern, 0, Qt::CaseInsensitive); 0893 } 0894 0895 CFontListSortFilterProxy::CFontListSortFilterProxy(QObject *parent, QAbstractItemModel *model) 0896 : QSortFilterProxyModel(parent) 0897 , m_group(nullptr) 0898 , m_filterCriteria(CFontFilter::CRIT_FAMILY) 0899 , m_filterWs(0) 0900 , m_fcQuery(nullptr) 0901 { 0902 setSourceModel(model); 0903 setSortCaseSensitivity(Qt::CaseInsensitive); 0904 setFilterKeyColumn(0); 0905 setDynamicSortFilter(false); 0906 m_timer = new QTimer(this); 0907 connect(m_timer, &QTimer::timeout, this, &CFontListSortFilterProxy::timeout); 0908 connect(model, &QAbstractItemModel::layoutChanged, this, &QSortFilterProxyModel::invalidate); 0909 m_timer->setSingleShot(true); 0910 } 0911 0912 QVariant CFontListSortFilterProxy::data(const QModelIndex &idx, int role) const 0913 { 0914 if (!idx.isValid()) { 0915 return QVariant(); 0916 } 0917 0918 static const int constMaxFiles = 20; 0919 0920 QModelIndex index(mapToSource(idx)); 0921 CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer()); 0922 0923 if (!mi) { 0924 return QVariant(); 0925 } 0926 0927 switch (role) { 0928 case Qt::ToolTipRole: 0929 if (CFontFilter::CRIT_FILENAME == m_filterCriteria || CFontFilter::CRIT_LOCATION == m_filterCriteria 0930 || CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) { 0931 if (mi->isFamily()) { 0932 CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer()); 0933 CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); 0934 FileCont allFiles; 0935 QString tip("<b>" + fam->name() + "</b>"); 0936 bool markMatch(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria); 0937 tip += "<p style='white-space:pre'><table>"; 0938 0939 for (; it != end; ++it) { 0940 allFiles += (*it)->files(); 0941 } 0942 0943 // qSort(allFiles); 0944 FileCont::ConstIterator fit(allFiles.begin()), fend(allFiles.end()); 0945 0946 for (int i = 0; fit != fend && i < constMaxFiles; ++fit, ++i) { 0947 if (markMatch && m_fcQuery && (*fit).path() == m_fcQuery->file()) { 0948 tip += "<tr><td><b>" + Misc::contractHome((*fit).path()) + "</b></td></tr>"; 0949 } else { 0950 tip += "<tr><td>" + Misc::contractHome((*fit).path()) + "</td></tr>"; 0951 } 0952 } 0953 if (allFiles.count() > constMaxFiles) { 0954 tip += "<tr><td><i>" + i18n("…plus %1 more", allFiles.count() - constMaxFiles) + "</td></tr>"; 0955 } 0956 0957 tip += "</table></p>"; 0958 return tip; 0959 } else { 0960 CFontItem *font = static_cast<CFontItem *>(index.internalPointer()); 0961 QString tip("<b>" + font->name() + "</b>"); 0962 const FileCont &files(font->files()); 0963 bool markMatch(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria); 0964 0965 tip += "<p style='white-space:pre'><table>"; 0966 0967 // qSort(files); 0968 FileCont::ConstIterator fit(files.begin()), fend(files.end()); 0969 0970 for (int i = 0; fit != fend && i < constMaxFiles; ++fit, ++i) { 0971 if (markMatch && m_fcQuery && (*fit).path() == m_fcQuery->file()) { 0972 tip += "<tr><td><b>" + Misc::contractHome((*fit).path()) + "</b></td></tr>"; 0973 } else { 0974 tip += "<tr><td>" + Misc::contractHome((*fit).path()) + "</td></tr>"; 0975 } 0976 } 0977 if (files.count() > constMaxFiles) { 0978 tip += "<tr><td><i>" + i18n("…plus %1 more", files.count() - constMaxFiles) + "</td></tr></li>"; 0979 } 0980 0981 tip += "</table></p>"; 0982 return tip; 0983 } 0984 } 0985 break; 0986 case Qt::FontRole: 0987 if (COL_FONT == index.column() && mi->isSystem()) { 0988 QFont font; 0989 font.setItalic(true); 0990 return font; 0991 } 0992 break; 0993 case Qt::ForegroundRole: 0994 if (COL_FONT == index.column() 0995 && ((mi->isFont() && !(static_cast<CFontItem *>(index.internalPointer()))->isEnabled()) 0996 || (mi->isFamily() && CFamilyItem::DISABLED == (static_cast<CFamilyItem *>(index.internalPointer()))->status()))) { 0997 return KColorScheme(QPalette::Active).foreground(KColorScheme::NegativeText).color(); 0998 } 0999 break; 1000 case Qt::DisplayRole: 1001 if (COL_FONT == index.column()) { 1002 if (mi->isFamily()) { 1003 CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer()); 1004 1005 return i18n("%1 [%2]", fam->name(), fam->fontCount()); 1006 } else { 1007 return (static_cast<CFontItem *>(index.internalPointer()))->style(); 1008 } 1009 } 1010 break; 1011 case Qt::DecorationRole: 1012 if (mi->isFamily()) { 1013 CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer()); 1014 1015 switch (index.column()) { 1016 case COL_STATUS: 1017 switch (fam->status()) { 1018 case CFamilyItem::PARTIAL: 1019 return QIcon::fromTheme("dialog-ok"); 1020 case CFamilyItem::ENABLED: 1021 return QIcon::fromTheme("dialog-ok"); 1022 case CFamilyItem::DISABLED: 1023 return QIcon::fromTheme("dialog-cancel"); 1024 } 1025 break; 1026 default: 1027 break; 1028 } 1029 } else if (COL_STATUS == index.column()) { 1030 return QIcon::fromTheme((static_cast<CFontItem *>(index.internalPointer()))->isEnabled() ? "dialog-ok" : "dialog-cancel"); 1031 } 1032 break; 1033 case Qt::SizeHintRole: 1034 if (mi->isFamily()) { 1035 const int s = KIconLoader::global()->currentSize(KIconLoader::Small); 1036 return QSize(s, s + 4); 1037 } 1038 default: 1039 break; 1040 } 1041 return QVariant(); 1042 } 1043 1044 bool CFontListSortFilterProxy::acceptFont(CFontItem *fnt, bool checkFontText) const 1045 { 1046 if (m_group && (CGroupListItem::ALL != m_group->type() || (!filterText().isEmpty() && checkFontText))) { 1047 bool fontMatch(!checkFontText); 1048 1049 if (!fontMatch) { 1050 switch (m_filterCriteria) { 1051 case CFontFilter::CRIT_FONTCONFIG: 1052 fontMatch = m_fcQuery ? fnt->name() == m_fcQuery->font() // || fnt->files().contains(m_fcQuery->file()) 1053 : false; 1054 break; 1055 case CFontFilter::CRIT_STYLE: 1056 fontMatch = matchString(fnt->style(), m_filterText); 1057 break; 1058 case CFontFilter::CRIT_FOUNDRY: { 1059 FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); 1060 1061 for (; it != end && !fontMatch; ++it) { 1062 fontMatch = 0 == (*it).foundry().compare(m_filterText, Qt::CaseInsensitive); 1063 } 1064 break; 1065 } 1066 case CFontFilter::CRIT_FILENAME: { 1067 FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); 1068 1069 for (; it != end && !fontMatch; ++it) { 1070 QString file(Misc::getFile((*it).path())); 1071 int pos(Misc::isHidden(file) ? 1 : 0); 1072 1073 if (pos == file.indexOf(m_filterText, pos, Qt::CaseInsensitive)) { 1074 fontMatch = true; 1075 } 1076 } 1077 break; 1078 } 1079 case CFontFilter::CRIT_LOCATION: { 1080 FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); 1081 1082 for (; it != end && !fontMatch; ++it) { 1083 if (0 == Misc::getDir((*it).path()).indexOf(m_filterText, 0, Qt::CaseInsensitive)) { 1084 fontMatch = true; 1085 } 1086 } 1087 break; 1088 } 1089 case CFontFilter::CRIT_FILETYPE: { 1090 FileCont::ConstIterator it(fnt->files().begin()), end(fnt->files().end()); 1091 QStringList::ConstIterator mimeEnd(m_filterTypes.constEnd()); 1092 1093 for (; it != end && !fontMatch; ++it) { 1094 QStringList::ConstIterator mime(m_filterTypes.constBegin()); 1095 1096 for (; mime != mimeEnd; ++mime) { 1097 if (Misc::checkExt((*it).path(), *mime)) { 1098 fontMatch = true; 1099 } 1100 } 1101 } 1102 break; 1103 } 1104 case CFontFilter::CRIT_WS: 1105 fontMatch = fnt->writingSystems() & m_filterWs; 1106 break; 1107 default: 1108 break; 1109 } 1110 } 1111 1112 return fontMatch && m_group->hasFont(fnt); 1113 } 1114 1115 return true; 1116 } 1117 1118 bool CFontListSortFilterProxy::acceptFamily(CFamilyItem *fam) const 1119 { 1120 CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); 1121 bool familyMatch(CFontFilter::CRIT_FAMILY == m_filterCriteria && matchString(fam->name(), m_filterText)); 1122 1123 for (; it != end; ++it) { 1124 if (acceptFont(*it, !familyMatch)) { 1125 return true; 1126 } 1127 } 1128 return false; 1129 } 1130 1131 bool CFontListSortFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const 1132 { 1133 QModelIndex index(sourceModel()->index(sourceRow, 0, sourceParent)); 1134 1135 if (index.isValid()) { 1136 CFontModelItem *mi = static_cast<CFontModelItem *>(index.internalPointer()); 1137 1138 if (mi->isFont()) { 1139 CFontItem *font = static_cast<CFontItem *>(index.internalPointer()); 1140 1141 return acceptFont(font, !(CFontFilter::CRIT_FAMILY == m_filterCriteria && matchString(font->family(), m_filterText))); 1142 } else { 1143 return acceptFamily(static_cast<CFamilyItem *>(index.internalPointer())); 1144 } 1145 } 1146 1147 return false; 1148 } 1149 1150 bool CFontListSortFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const 1151 { 1152 if (left.isValid() && right.isValid()) { 1153 CFontModelItem *lmi = static_cast<CFontModelItem *>(left.internalPointer()), *rmi = static_cast<CFontModelItem *>(right.internalPointer()); 1154 1155 if (lmi->isFont() < rmi->isFont()) { 1156 return true; 1157 } 1158 1159 if (lmi->isFont()) { 1160 CFontItem *lfi = static_cast<CFontItem *>(left.internalPointer()), *rfi = static_cast<CFontItem *>(right.internalPointer()); 1161 1162 if (COL_STATUS == filterKeyColumn()) { 1163 if (lfi->isEnabled() < rfi->isEnabled() || (lfi->isEnabled() == rfi->isEnabled() && lfi->styleInfo() < rfi->styleInfo())) { 1164 return true; 1165 } 1166 } else if (lfi->styleInfo() < rfi->styleInfo()) { 1167 return true; 1168 } 1169 } else { 1170 CFamilyItem *lfi = static_cast<CFamilyItem *>(left.internalPointer()), *rfi = static_cast<CFamilyItem *>(right.internalPointer()); 1171 1172 if (COL_STATUS == filterKeyColumn()) { 1173 if (lfi->status() < rfi->status() || (lfi->status() == rfi->status() && QString::localeAwareCompare(lfi->name(), rfi->name()) < 0)) { 1174 return true; 1175 } 1176 } else if (QString::localeAwareCompare(lfi->name(), rfi->name()) < 0) { 1177 return true; 1178 } 1179 } 1180 } 1181 1182 return false; 1183 } 1184 1185 void CFontListSortFilterProxy::setFilterGroup(CGroupListItem *grp) 1186 { 1187 if (grp != m_group) { 1188 // bool wasNull=!m_group; 1189 1190 m_group = grp; 1191 1192 // if(!(wasNull && m_group && CGroupListItem::ALL==m_group->type())) 1193 invalidate(); 1194 } 1195 } 1196 1197 void CFontListSortFilterProxy::setFilterText(const QString &text) 1198 { 1199 if (text != m_filterText) { 1200 // 1201 // If we are filtering on file location, then expand ~ to /home/user, etc. 1202 if (CFontFilter::CRIT_LOCATION == m_filterCriteria && !text.isEmpty() && ('~' == text[0] || '$' == text[0])) { 1203 if ('~' == text[0]) { 1204 m_filterText = 1 == text.length() ? QDir::homePath() : QString(text).replace(0, 1, QDir::homePath()); 1205 } else { 1206 m_filterText = replaceEnvVar(text); 1207 } 1208 } else { 1209 m_filterText = text; 1210 } 1211 1212 if (m_filterText.isEmpty()) { 1213 m_timer->stop(); 1214 timeout(); 1215 } else { 1216 m_timer->start(CFontFilter::CRIT_FONTCONFIG == m_filterCriteria ? 750 : 400); 1217 } 1218 } 1219 } 1220 1221 void CFontListSortFilterProxy::setFilterCriteria(CFontFilter::ECriteria crit, qulonglong ws, const QStringList &ft) 1222 { 1223 if (crit != m_filterCriteria || ws != m_filterWs || ft != m_filterTypes) { 1224 m_filterWs = ws; 1225 m_filterCriteria = crit; 1226 m_filterTypes = ft; 1227 if (CFontFilter::CRIT_LOCATION == m_filterCriteria) { 1228 setFilterText(m_filterText); 1229 } 1230 m_timer->stop(); 1231 timeout(); 1232 } 1233 } 1234 1235 void CFontListSortFilterProxy::timeout() 1236 { 1237 if (CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) { 1238 int commaPos = m_filterText.indexOf(','); 1239 QString query(m_filterText); 1240 1241 if (-1 != commaPos) { 1242 QString style(query.mid(commaPos + 1)); 1243 query.truncate(commaPos); 1244 query = std::move(query).trimmed(); 1245 query += ":style="; 1246 style = std::move(style).trimmed(); 1247 query += style; 1248 } else { 1249 query = std::move(query).trimmed(); 1250 } 1251 1252 if (!m_fcQuery) { 1253 m_fcQuery = new CFcQuery(this); 1254 connect(m_fcQuery, &CFcQuery::finished, this, &CFontListSortFilterProxy::fcResults); 1255 } 1256 1257 m_fcQuery->run(query); 1258 } else { 1259 invalidate(); 1260 Q_EMIT refresh(); 1261 } 1262 } 1263 1264 void CFontListSortFilterProxy::fcResults() 1265 { 1266 if (CFontFilter::CRIT_FONTCONFIG == m_filterCriteria) { 1267 invalidate(); 1268 Q_EMIT refresh(); 1269 } 1270 } 1271 1272 CFontListView::CFontListView(QWidget *parent, CFontList *model) 1273 : QTreeView(parent) 1274 , m_proxy(new CFontListSortFilterProxy(this, model)) 1275 , m_model(model) 1276 , m_allowDrops(false) 1277 { 1278 setModel(m_proxy); 1279 m_model = model; 1280 header()->setStretchLastSection(false); 1281 resizeColumnToContents(COL_STATUS); 1282 header()->setSectionResizeMode(COL_STATUS, QHeaderView::Fixed); 1283 header()->setSectionResizeMode(COL_FONT, QHeaderView::Stretch); 1284 setSelectionMode(QAbstractItemView::ExtendedSelection); 1285 setSelectionBehavior(QAbstractItemView::SelectRows); 1286 setSortingEnabled(true); 1287 sortByColumn(COL_FONT, Qt::AscendingOrder); 1288 setAllColumnsShowFocus(true); 1289 setAlternatingRowColors(true); 1290 setAcceptDrops(true); 1291 setDropIndicatorShown(false); 1292 setDragEnabled(true); 1293 setDragDropMode(QAbstractItemView::DragDrop); 1294 header()->setSectionsClickable(true); 1295 header()->setSortIndicatorShown(true); 1296 connect(this, &QTreeView::collapsed, this, &CFontListView::itemCollapsed); 1297 connect(header(), &QHeaderView::sectionClicked, this, &CFontListView::setSortColumn); 1298 connect(m_proxy, &CFontListSortFilterProxy::refresh, this, &CFontListView::refresh); 1299 connect(m_model, &CFontList::listingPercent, this, &CFontListView::listingPercent); 1300 1301 setWhatsThis(model->whatsThis()); 1302 header()->setWhatsThis(whatsThis()); 1303 m_menu = new QMenu(this); 1304 m_deleteAct = m_menu->addAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), this, &CFontListView::del); 1305 m_menu->addSeparator(); 1306 m_enableAct = m_menu->addAction(QIcon::fromTheme("font-enable"), i18n("Enable"), this, &CFontListView::enable); 1307 m_disableAct = m_menu->addAction(QIcon::fromTheme("font-disable"), i18n("Disable"), this, &CFontListView::disable); 1308 if (!Misc::app(KFI_VIEWER).isEmpty()) { 1309 m_menu->addSeparator(); 1310 } 1311 m_printAct = Misc::app(KFI_VIEWER).isEmpty() ? nullptr : m_menu->addAction(QIcon::fromTheme("document-print"), i18n("Print…"), this, &CFontListView::print); 1312 m_viewAct = 1313 Misc::app(KFI_VIEWER).isEmpty() ? nullptr : m_menu->addAction(QIcon::fromTheme("kfontview"), i18n("Open in Font Viewer"), this, &CFontListView::view); 1314 m_menu->addSeparator(); 1315 m_menu->addAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), model, &CFontList::load); 1316 } 1317 1318 void CFontListView::getFonts(CJobRunner::ItemList &urls, QStringList &fontNames, QSet<Misc::TFont> *fonts, bool selected, bool getEnabled, bool getDisabled) 1319 { 1320 QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes()); 1321 QSet<CFontItem *> usedFonts; 1322 QModelIndex index; 1323 1324 foreach (index, selectedItems) 1325 if (index.isValid()) { 1326 QModelIndex realIndex(m_proxy->mapToSource(index)); 1327 1328 if (realIndex.isValid()) { 1329 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1330 CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer()); 1331 1332 addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled); 1333 } else { 1334 CFamilyItem *fam = static_cast<CFamilyItem *>(realIndex.internalPointer()); 1335 1336 for (int ch = 0; ch < fam->fontCount(); ++ch) { 1337 QModelIndex child(m_proxy->mapToSource(index.model()->index(ch, 0, index))); 1338 1339 if (child.isValid() && (static_cast<CFontModelItem *>(child.internalPointer()))->isFont()) { 1340 CFontItem *font = static_cast<CFontItem *>(child.internalPointer()); 1341 1342 addFont(font, urls, fontNames, fonts, usedFonts, getEnabled, getDisabled); 1343 } 1344 } 1345 } 1346 } 1347 } 1348 1349 fontNames = CFontList::compact(fontNames); 1350 } 1351 1352 QSet<QString> CFontListView::getFiles() 1353 { 1354 QModelIndexList items(allIndexes()); 1355 QModelIndex index; 1356 QSet<QString> files; 1357 1358 foreach (index, items) 1359 if (index.isValid()) { 1360 QModelIndex realIndex(m_proxy->mapToSource(index)); 1361 1362 if (realIndex.isValid()) { 1363 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1364 CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer()); 1365 1366 FileCont::ConstIterator it(font->files().begin()), end(font->files().end()); 1367 1368 for (; it != end; ++it) { 1369 QStringList assoc; 1370 1371 files.insert((*it).path()); 1372 Misc::getAssociatedFiles((*it).path(), assoc); 1373 1374 QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd()); 1375 1376 for (; ait != aend; ++ait) { 1377 files.insert(*ait); 1378 } 1379 } 1380 } 1381 } 1382 } 1383 1384 return files; 1385 } 1386 1387 void CFontListView::getPrintableFonts(QSet<Misc::TFont> &items, bool selected) 1388 { 1389 QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes()); 1390 QModelIndex index; 1391 1392 foreach (index, selectedItems) { 1393 CFontItem *font = nullptr; 1394 1395 if (index.isValid() && 0 == index.column()) { 1396 QModelIndex realIndex(m_proxy->mapToSource(index)); 1397 1398 if (realIndex.isValid()) { 1399 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1400 font = static_cast<CFontItem *>(realIndex.internalPointer()); 1401 } else { 1402 CFamilyItem *fam = static_cast<CFamilyItem *>(realIndex.internalPointer()); 1403 font = fam->regularFont(); 1404 } 1405 } 1406 } 1407 1408 if (font && !font->isBitmap() && font->isEnabled()) { 1409 items.insert(Misc::TFont(font->family(), font->styleInfo())); 1410 } 1411 } 1412 } 1413 1414 void CFontListView::setFilterGroup(CGroupListItem *grp) 1415 { 1416 CGroupListItem *oldGrp(m_proxy->filterGroup()); 1417 1418 m_proxy->setFilterGroup(grp); 1419 m_allowDrops = grp && !grp->isCustom(); 1420 1421 if (!Misc::root()) { 1422 bool refreshStats(false); 1423 1424 if (!grp || !oldGrp) { 1425 refreshStats = true; 1426 } else { 1427 // Check to see whether we have changed from listing all fonts, 1428 // listing just system or listing personal fonts. 1429 CGroupListItem::EType aType(CGroupListItem::CUSTOM == grp->type() || CGroupListItem::ALL == grp->type() 1430 || CGroupListItem::UNCLASSIFIED == grp->type() 1431 ? CGroupListItem::CUSTOM 1432 : grp->type()), 1433 bType(CGroupListItem::CUSTOM == oldGrp->type() || CGroupListItem::ALL == oldGrp->type() || CGroupListItem::UNCLASSIFIED == oldGrp->type() 1434 ? CGroupListItem::CUSTOM 1435 : oldGrp->type()); 1436 refreshStats = aType != bType; 1437 } 1438 1439 if (refreshStats) { 1440 m_model->refresh(!grp || !grp->isPersonal(), !grp || !grp->isSystem()); 1441 } 1442 } 1443 // when switching groups, for some reason it is not always sorted. 1444 setSortingEnabled(true); 1445 } 1446 1447 void CFontListView::listingPercent(int percent) 1448 { 1449 // when the font list is first loaded, for some reason it is not always sorted. 1450 // re-enabling sorting here seems to fix the issue - BUG 221610 1451 if (100 == percent) { 1452 setSortingEnabled(true); 1453 } 1454 } 1455 1456 void CFontListView::refreshFilter() 1457 { 1458 m_proxy->invalidate(); 1459 } 1460 1461 void CFontListView::filterText(const QString &text) 1462 { 1463 m_proxy->setFilterText(text); 1464 } 1465 1466 void CFontListView::filterCriteria(int crit, qulonglong ws, const QStringList &ft) 1467 { 1468 m_proxy->setFilterCriteria((CFontFilter::ECriteria)crit, ws, ft); 1469 } 1470 1471 void CFontListView::stats(int &enabled, int &disabled, int &partial) 1472 { 1473 enabled = disabled = partial = 0; 1474 1475 for (int i = 0; i < m_proxy->rowCount(); ++i) { 1476 QModelIndex idx(m_proxy->index(i, 0, QModelIndex())); 1477 1478 if (!idx.isValid()) { 1479 break; 1480 } 1481 1482 QModelIndex sourceIdx(m_proxy->mapToSource(idx)); 1483 1484 if (!sourceIdx.isValid()) { 1485 break; 1486 } 1487 1488 if ((static_cast<CFontModelItem *>(sourceIdx.internalPointer()))->isFamily()) { 1489 switch ((static_cast<CFamilyItem *>(sourceIdx.internalPointer()))->status()) { 1490 case CFamilyItem::ENABLED: 1491 enabled++; 1492 break; 1493 case CFamilyItem::DISABLED: 1494 disabled++; 1495 break; 1496 case CFamilyItem::PARTIAL: 1497 partial++; 1498 break; 1499 } 1500 } 1501 } 1502 } 1503 1504 void CFontListView::selectedStatus(bool &enabled, bool &disabled) 1505 { 1506 QModelIndexList selected(selectedIndexes()); 1507 QModelIndex index; 1508 1509 enabled = disabled = false; 1510 1511 foreach (index, selected) { 1512 QModelIndex realIndex(m_proxy->mapToSource(index)); 1513 1514 if (realIndex.isValid()) { 1515 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFamily()) { 1516 switch ((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status()) { 1517 case CFamilyItem::ENABLED: 1518 enabled = true; 1519 break; 1520 case CFamilyItem::DISABLED: 1521 disabled = true; 1522 break; 1523 case CFamilyItem::PARTIAL: 1524 enabled = true; 1525 disabled = true; 1526 break; 1527 } 1528 } else { 1529 if ((static_cast<CFontItem *>(realIndex.internalPointer()))->isEnabled()) { 1530 enabled = true; 1531 } else { 1532 disabled = true; 1533 } 1534 } 1535 } 1536 if (enabled && disabled) { 1537 break; 1538 } 1539 } 1540 } 1541 1542 QModelIndexList CFontListView::allFonts() 1543 { 1544 QModelIndexList rv; 1545 int rowCount(m_proxy->rowCount()); 1546 1547 for (int i = 0; i < rowCount; ++i) { 1548 QModelIndex idx(m_proxy->index(i, 0, QModelIndex())); 1549 int childRowCount(m_proxy->rowCount(idx)); 1550 1551 for (int j = 0; j < childRowCount; ++j) { 1552 QModelIndex child(m_proxy->index(j, 0, idx)); 1553 1554 if (child.isValid()) { 1555 rv.append(m_proxy->mapToSource(child)); 1556 } 1557 } 1558 } 1559 1560 return rv; 1561 } 1562 1563 void CFontListView::selectFirstFont() 1564 { 1565 if (0 == selectedIndexes().count()) { 1566 for (int i = 0; i < NUM_COLS; ++i) { 1567 QModelIndex idx(m_proxy->index(0, i, QModelIndex())); 1568 1569 if (idx.isValid()) { 1570 selectionModel()->select(idx, QItemSelectionModel::Select); 1571 } 1572 } 1573 } 1574 } 1575 1576 void CFontListView::setSortColumn(int col) 1577 { 1578 if (col != m_proxy->filterKeyColumn()) { 1579 m_proxy->setFilterKeyColumn(col); 1580 m_proxy->invalidate(); 1581 } 1582 } 1583 1584 void CFontListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) 1585 { 1586 QAbstractItemView::selectionChanged(selected, deselected); 1587 if (m_model->slowUpdates()) { 1588 return; 1589 } 1590 Q_EMIT itemsSelected(getSelectedItems()); 1591 } 1592 1593 QModelIndexList CFontListView::getSelectedItems() 1594 { 1595 // Go through current selection, and for any 'font' items that are selected, 1596 // ensure 'family' item is not... 1597 QModelIndexList selectedItems(selectedIndexes()), deselectList; 1598 QModelIndex index; 1599 QSet<CFontModelItem *> selectedFamilies; 1600 1601 foreach (index, selectedItems) { 1602 if (index.isValid()) { 1603 QModelIndex realIndex(m_proxy->mapToSource(index)); 1604 1605 if (realIndex.isValid()) { 1606 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1607 CFontItem *font = static_cast<CFontItem *>(realIndex.internalPointer()); 1608 1609 if (!selectedFamilies.contains(font->parent())) { 1610 selectedFamilies.insert(font->parent()); 1611 1612 for (int i = 0; i < NUM_COLS; ++i) { 1613 deselectList.append(m_proxy->mapFromSource(m_model->createIndex(font->parent()->rowNumber(), i, font->parent()))); 1614 } 1615 } 1616 } 1617 } 1618 } 1619 } 1620 1621 if (deselectList.count()) { 1622 foreach (index, deselectList) { 1623 selectionModel()->select(index, QItemSelectionModel::Deselect); 1624 } 1625 } 1626 1627 QModelIndexList sel; 1628 QSet<void *> pointers; 1629 selectedItems = selectedIndexes(); 1630 foreach (index, selectedItems) { 1631 QModelIndex idx(m_proxy->mapToSource(index)); 1632 1633 if (!pointers.contains(idx.internalPointer())) { 1634 pointers.insert(idx.internalPointer()); 1635 sel.append(idx); 1636 } 1637 } 1638 1639 return sel; 1640 } 1641 1642 void CFontListView::itemCollapsed(const QModelIndex &idx) 1643 { 1644 if (idx.isValid()) { 1645 QModelIndex index(m_proxy->mapToSource(idx)); 1646 1647 if (index.isValid() && (static_cast<CFontModelItem *>(index.internalPointer()))->isFamily()) { 1648 CFamilyItem *fam = static_cast<CFamilyItem *>(index.internalPointer()); 1649 CFontItemCont::ConstIterator it(fam->fonts().begin()), end(fam->fonts().end()); 1650 1651 for (; it != end; ++it) { 1652 for (int i = 0; i < NUM_COLS; ++i) { 1653 selectionModel()->select(m_proxy->mapFromSource(m_model->createIndex((*it)->rowNumber(), i, *it)), QItemSelectionModel::Deselect); 1654 } 1655 } 1656 } 1657 } 1658 } 1659 1660 static bool isScalable(const QString &str) 1661 { 1662 QByteArray cFile(QFile::encodeName(str)); 1663 1664 return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") || Misc::checkExt(cFile, "pfa") 1665 || Misc::checkExt(cFile, "pfb"); 1666 } 1667 1668 void CFontListView::view() 1669 { 1670 // Number of fonts user has selected, before we ask if they really want to view them all... 1671 static const int constMaxBeforePrompt = 10; 1672 1673 QModelIndexList selectedItems(selectedIndexes()); 1674 QModelIndex index; 1675 QSet<CFontItem *> fonts; 1676 1677 foreach (index, selectedItems) { 1678 QModelIndex realIndex(m_proxy->mapToSource(index)); 1679 1680 if (realIndex.isValid()) { 1681 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1682 CFontItem *font(static_cast<CFontItem *>(realIndex.internalPointer())); 1683 1684 fonts.insert(font); 1685 } else { 1686 CFontItem *font((static_cast<CFamilyItem *>(realIndex.internalPointer()))->regularFont()); 1687 1688 if (font) { 1689 fonts.insert(font); 1690 } 1691 } 1692 } 1693 } 1694 1695 if (fonts.count() 1696 && (fonts.count() < constMaxBeforePrompt 1697 || KMessageBox::PrimaryAction 1698 == KMessageBox::questionTwoActions(this, 1699 i18n("Open all %1 fonts in font viewer?", fonts.count()), 1700 QString(), 1701 KStandardGuiItem::open(), 1702 KStandardGuiItem::cancel()))) { 1703 QSet<CFontItem *>::ConstIterator it(fonts.begin()), end(fonts.end()); 1704 QStringList args; 1705 1706 for (; it != end; ++it) { 1707 QString file; 1708 int index(0); 1709 1710 if (!(*it)->isEnabled()) { 1711 // For a disabled font, we need to find the first scalable font entry in its file list... 1712 FileCont::ConstIterator fit((*it)->files().begin()), fend((*it)->files().end()); 1713 1714 for (; fit != fend; ++fit) { 1715 if (isScalable((*fit).path())) { 1716 file = (*fit).path(); 1717 index = (*fit).index(); 1718 break; 1719 } 1720 } 1721 if (file.isEmpty()) { 1722 file = (*it)->fileName(); 1723 index = (*it)->index(); 1724 } 1725 } 1726 args << FC::encode((*it)->family(), (*it)->styleInfo(), file, index).url(); 1727 } 1728 1729 QProcess::startDetached(Misc::app(KFI_VIEWER), args); 1730 } 1731 } 1732 1733 QModelIndexList CFontListView::allIndexes() 1734 { 1735 QModelIndexList rv; 1736 int rowCount(m_proxy->rowCount()); 1737 1738 for (int i = 0; i < rowCount; ++i) { 1739 QModelIndex idx(m_proxy->index(i, 0, QModelIndex())); 1740 int childRowCount(m_proxy->rowCount(idx)); 1741 1742 rv.append(idx); 1743 1744 for (int j = 0; j < childRowCount; ++j) { 1745 QModelIndex child(m_proxy->index(j, 0, idx)); 1746 1747 if (child.isValid()) { 1748 rv.append(child); 1749 } 1750 } 1751 } 1752 1753 return rv; 1754 } 1755 1756 void CFontListView::startDrag(Qt::DropActions supportedActions) 1757 { 1758 QModelIndexList indexes(selectedIndexes()); 1759 1760 if (indexes.count()) { 1761 QMimeData *data = model()->mimeData(indexes); 1762 if (!data) { 1763 return; 1764 } 1765 1766 QModelIndex index(m_proxy->mapToSource(indexes.first())); 1767 const char *icon = "application-x-font-pcf"; 1768 1769 if (index.isValid()) { 1770 CFontItem *font = (static_cast<CFontModelItem *>(index.internalPointer()))->isFont() 1771 ? static_cast<CFontItem *>(index.internalPointer()) 1772 : (static_cast<CFamilyItem *>(index.internalPointer()))->regularFont(); 1773 1774 if (font && !font->isBitmap()) { 1775 // if("application/x-font-type1"==font->mimetype()) 1776 // icon="application-x-font-type1"; 1777 // else 1778 icon = "application-x-font-ttf"; 1779 } 1780 } 1781 1782 QPoint hotspot; 1783 QPixmap pix = QIcon::fromTheme(icon).pixmap(KIconLoader::SizeMedium); 1784 1785 hotspot.setX(0); // pix.width()/2); 1786 hotspot.setY(0); // pix.height()/2); 1787 1788 QDrag *drag = new QDrag(this); 1789 drag->setPixmap(pix); 1790 drag->setMimeData(data); 1791 drag->setHotSpot(hotspot); 1792 drag->exec(supportedActions); 1793 } 1794 } 1795 1796 void CFontListView::dragEnterEvent(QDragEnterEvent *event) 1797 { 1798 if (m_allowDrops && event->mimeData()->hasFormat("text/uri-list")) { // "application/x-kde-urilist" ?? 1799 event->acceptProposedAction(); 1800 } 1801 } 1802 1803 void CFontListView::dropEvent(QDropEvent *event) 1804 { 1805 if (m_allowDrops && event->mimeData()->hasFormat("text/uri-list")) { 1806 event->acceptProposedAction(); 1807 1808 QList<QUrl> urls(event->mimeData()->urls()); 1809 QList<QUrl>::ConstIterator it(urls.begin()), end(urls.end()); 1810 QSet<QUrl> kurls; 1811 QMimeDatabase db; 1812 1813 for (; it != end; ++it) { 1814 QMimeType mime = db.mimeTypeForUrl(*it); 1815 1816 foreach (const QString &fontMime, CFontList::fontMimeTypes) { 1817 if (mime.inherits(fontMime)) { 1818 kurls.insert(*it); 1819 break; 1820 } 1821 } 1822 } 1823 1824 if (!kurls.isEmpty()) { 1825 Q_EMIT fontsDropped(kurls); 1826 } 1827 } 1828 } 1829 1830 void CFontListView::contextMenuEvent(QContextMenuEvent *ev) 1831 { 1832 bool valid(indexAt(ev->pos()).isValid()); 1833 1834 m_deleteAct->setEnabled(valid); 1835 1836 bool en(false), dis(false); 1837 QModelIndexList selectedItems(selectedIndexes()); 1838 QModelIndex index; 1839 1840 foreach (index, selectedItems) { 1841 QModelIndex realIndex(m_proxy->mapToSource(index)); 1842 1843 if (realIndex.isValid()) { 1844 if ((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont()) { 1845 if ((static_cast<CFontItem *>(realIndex.internalPointer())->isEnabled())) { 1846 en = true; 1847 } else { 1848 dis = true; 1849 } 1850 } else { 1851 switch ((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status()) { 1852 case CFamilyItem::ENABLED: 1853 en = true; 1854 break; 1855 case CFamilyItem::DISABLED: 1856 dis = true; 1857 break; 1858 case CFamilyItem::PARTIAL: 1859 en = dis = true; 1860 break; 1861 } 1862 } 1863 } 1864 if (en && dis) { 1865 break; 1866 } 1867 } 1868 1869 m_enableAct->setEnabled(dis); 1870 m_disableAct->setEnabled(en); 1871 if (m_printAct) { 1872 m_printAct->setEnabled(en | dis); 1873 } 1874 if (m_viewAct) { 1875 m_viewAct->setEnabled(en | dis); 1876 } 1877 m_menu->popup(ev->globalPos()); 1878 } 1879 1880 bool CFontListView::viewportEvent(QEvent *event) 1881 { 1882 executeDelayedItemsLayout(); 1883 return QTreeView::viewportEvent(event); 1884 } 1885 1886 }