add more spacing
[personal-kdebase.git] / workspace / kcontrol / kfontinst / kcmfontinst / FontList.cpp
blob4cd4a095d07f7774acae160ee6a6d42d81a73a83
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "FontList.h"
25 #include <KDE/KGlobal>
26 #include <KDE/KStandardDirs>
27 #include <KDE/KLocale>
28 #include <KDE/KMimeType>
29 #include <KDE/KIcon>
30 #include <KDE/KIconLoader>
31 #include <kde_file.h>
32 #include <KDE/KMessageBox>
33 #include <QtCore/QProcess>
34 #include <QtGui/QFont>
35 #include <QtCore/QMap>
36 #include <QtCore/QFile>
37 #include <QtGui/QImage>
38 #include <QtCore/QDir>
39 #include <QtCore/QFileInfo>
40 #include <QtGui/QPixmap>
41 #include <QtGui/QDropEvent>
42 #include <QtCore/QDateTime>
43 #include <QtGui/QX11Info>
44 #include <QtGui/QHeaderView>
45 #include <QtGui/QStyledItemDelegate>
46 #include <QtGui/QPainter>
47 #include <QtGui/QMenu>
48 #include <QtCore/QTimer>
49 #include <QtGui/QApplication>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <utime.h>
53 #include "FcEngine.h"
54 #include "KfiConstants.h"
55 #include "GroupList.h"
56 #include <config-workspace.h>
58 //#define KFI_FONTLIST_DEBUG
60 // #define to control whether generated inline preview thumbs should be
61 // cached to disk.
62 #define KFI_SAVE_PIXMAPS
64 #ifdef KFI_FONTLIST_DEBUG
65 #include <kdebug.h>
66 #endif
68 namespace KFI
71 int CFontList::theirPreviewSize=CFontList::constDefaultPreviewSize;
73 static void decompose(const QString &name, QString &family, QString &style)
75 int commaPos=name.lastIndexOf(',');
77 family=-1==commaPos ? name : name.left(commaPos);
78 style=-1==commaPos ? KFI_WEIGHT_REGULAR : name.mid(commaPos+2);
81 static void addFont(CFontItem *font, CJobRunner::ItemList &urls, QStringList &fontNames,
82 QSet<Misc::TFont> *fonts, bool *hasSys, QSet<CFontItem *> &usedFonts,
83 bool getEnabled, bool getDisabled)
85 if(!usedFonts.contains(font) &&
86 ( (getEnabled && font->isEnabled()) ||
87 (getDisabled && !font->isEnabled()) ) )
89 urls.append(CJobRunner::Item(font->url(), font->name()));
90 fontNames.append(font->name());
91 usedFonts.insert(font);
92 if(fonts)
93 fonts->insert(Misc::TFont(font->family(), font->styleInfo()));
94 if(hasSys && !(*hasSys) && font->isSystem())
95 *hasSys=true;
99 static QString getName(const KFileItem &item)
101 return !item.isNull()
102 ? item.entry().stringValue(KIO::UDSEntry::UDS_NAME)
103 : QString();
106 static QString replaceEnvVar(const QString &text)
108 QString mod(text);
109 int endPos(text.indexOf('/'));
111 if(endPos==-1)
112 endPos=text.length()-1;
113 else
114 endPos--;
116 if(endPos>0)
118 QString envVar(text.mid(1, endPos));
120 const char *val=getenv(envVar.toLatin1().constData());
122 if(val)
123 mod=Misc::fileSyntax(QFile::decodeName(val)+mod.mid(endPos+1));
126 return mod;
130 // Convert from list such as:
132 // Arial
133 // Arial, Bold
134 // Courier
135 // Times
136 // Times, Italic
138 // To:
140 // Arial (Regular, Bold)
141 // Coutier
142 // Times (Regular, Italic)
143 QStringList CFontList::compact(const QStringList &fonts)
145 QString lastFamily,
146 entry;
147 QStringList::ConstIterator it(fonts.begin()),
148 end(fonts.end());
149 QStringList compacted;
150 QSet<QString> usedStyles;
152 for(; it!=end; ++it)
154 QString family,
155 style;
157 decompose(*it, family, style);
159 if(family!=lastFamily)
161 usedStyles.clear();
162 if(entry.length())
164 entry+=')';
165 compacted.append(entry);
167 entry=QString(family+" (");
168 lastFamily=family;
170 if(!usedStyles.contains(style))
172 usedStyles.clear();
173 if(entry.length() && '('!=entry[entry.length()-1])
174 entry+=", ";
175 entry+=style;
176 usedStyles.insert(style);
180 if(entry.length())
182 entry+=')';
183 compacted.append(entry);
186 return compacted;
189 class CPreviewCache
191 public:
193 CPreviewCache(CFcEngine *eng);
194 #ifdef KFI_SAVE_PIXMAPS
195 ~CPreviewCache() { clearOld(); }
196 #endif
198 static QString thumbKey(const QString &family, quint32 style, int height, const QColor &col);
199 QPixmap * getPixmap(const QString &family, const QString &name, const QString &fileName,
200 int height, quint32 stlye, bool selected, bool force=false);
201 #ifdef KFI_SAVE_PIXMAPS
202 void clearOld();
203 #endif
204 void empty();
206 private:
208 CFcEngine *itsFcEngine;
209 QMap<QString, QPixmap> itsMap;
210 #ifdef KFI_SAVE_PIXMAPS
211 QString itsPath;
212 #endif
215 CPreviewCache::CPreviewCache(CFcEngine *eng)
216 : itsFcEngine(eng)
217 #ifdef KFI_SAVE_PIXMAPS
218 , itsPath(KGlobal::dirs()->saveLocation("cache", "fontpreview"))
219 #endif
223 static QString replaceChars(const QString &in)
225 QString rv(in);
227 rv=rv.replace('/', '_');
229 return rv;
232 QString capitaliseFoundry(const QString &foundry)
234 QString f(foundry.toLower());
236 if(f==QString::fromLatin1("ibm"))
237 return QString::fromLatin1("IBM");
238 else if(f==QString::fromLatin1("urw"))
239 return QString::fromLatin1("URW");
240 else if(f==QString::fromLatin1("itc"))
241 return QString::fromLatin1("ITC");
242 else if(f==QString::fromLatin1("nec"))
243 return QString::fromLatin1("NEC");
244 else if(f==QString::fromLatin1("b&h"))
245 return QString::fromLatin1("B&H");
246 else
248 QChar *ch(f.data());
249 int len(f.length());
250 bool isSpace(true);
252 while(len--)
254 if (isSpace)
255 *ch=ch->toUpper();
257 isSpace=ch->isSpace();
258 ++ch;
263 return f;
266 static void toggle(QString &file, bool enable)
268 QString newFile(enable
269 ? Misc::getDir(file)+Misc::unhide(Misc::getFile(file))
270 : Misc::getDir(file)+QChar('.')+Misc::unhide(Misc::getFile(file)));
272 if(!Misc::fExists(file) && Misc::fExists(newFile))
273 file=newFile;
276 #ifdef KFI_SAVE_PIXMAPS
277 static void setTimeStamp(const QString &f)
279 QByteArray fC(QFile::encodeName(f));
280 KDE_struct_stat fStat;
282 if(0==KDE_lstat(fC, &fStat))
284 struct utimbuf times;
286 times.actime=times.modtime=time(NULL);
287 utime(fC, &times);
290 #endif
291 QString CPreviewCache::thumbKey(const QString &name, quint32 style, int height, const QColor &col)
293 return replaceChars(name)+
294 QString().sprintf("-%06lX%02d%02X%02X%02X.png", (long unsigned int)style, height,
295 col.red(), col.green(), col.blue());
298 QPixmap * CPreviewCache::getPixmap(const QString &family, const QString &name, const QString &fileName,
299 int height, quint32 style, bool selected, bool force)
301 #ifdef KFI_SAVE_PIXMAPS
302 static const char *constFileType="PNG";
303 #endif
305 QColor col(QApplication::palette().color(selected ? QPalette::HighlightedText : QPalette::Text));
306 QString thumbName(thumbKey(family, style, height, col));
308 if(!force && !itsMap[thumbName].isNull())
309 return &(itsMap[thumbName]);
311 #ifdef KFI_SAVE_PIXMAPS
312 QString thumbFile(itsPath+thumbName);
314 if(!force && itsMap[thumbName].load(thumbFile, constFileType))
315 return &(itsMap[thumbName]);
316 #endif
318 itsMap[thumbName]=QPixmap();
320 if(itsFcEngine->drawPreview(fileName.isEmpty() ? name : fileName, itsMap[thumbName], col,
321 height, style)) // CPD:TODO face???
323 #ifdef KFI_SAVE_PIXMAPS
324 QFile pngFile(thumbFile);
326 if(pngFile.open(QIODevice::WriteOnly))
328 #endif
329 QImage thumb=itsMap[thumbName].toImage();
331 #ifdef KFI_SAVE_PIXMAPS
332 thumb.save(&pngFile, constFileType);
333 pngFile.close();
334 #endif
335 itsMap[thumbName]=QPixmap::fromImage(thumb);
336 return &(itsMap[thumbName]);
337 #ifdef KFI_SAVE_PIXMAPS
339 #endif
341 else
342 itsMap[thumbName]=QPixmap(1, 1);
344 return NULL;
347 #ifdef KFI_SAVE_PIXMAPS
348 void CPreviewCache::clearOld()
351 // Remove any files that have not been accessed for constMaxAge days.
352 // ...this should be OK, as this function is called after the font list is completed,
353 // so any existing fonts will already have accessed their thumbnail.
355 static const int constMaxAge = 7;
357 QDir d(itsPath);
359 if(d.isReadable())
361 d.setFilter(QDir::Files);
363 QFileInfoList list(d.entryInfoList());
364 QDateTime current(QDateTime::currentDateTime());
366 for (int i = 0; i < list.size(); ++i)
368 QFileInfo fileInfo(list.at(i));
369 int diff=abs(current.daysTo(fileInfo.lastRead()));
371 if(diff>constMaxAge) // More than constMaxAge days ago, so remove
372 ::unlink(QFile::encodeName(fileInfo.absoluteFilePath()));
376 #endif
378 void CPreviewCache::empty()
380 #ifdef KFI_SAVE_PIXMAPS
381 QDir d(itsPath);
383 if(d.isReadable())
385 d.setFilter(QDir::Files);
387 QFileInfoList list(d.entryInfoList());
389 for (int i = 0; i < list.size(); ++i)
391 QFileInfo fileInfo(list.at(i));
393 if(-1!=fileInfo.fileName().lastIndexOf(".png"))
394 ::unlink(QFile::encodeName(fileInfo.absoluteFilePath()));
397 #endif
398 itsMap.clear();
401 static CPreviewCache *theCache=NULL;
403 inline bool isSysFolder(const QString &sect)
405 return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect;
408 CFontItem::CFontItem(CFontModelItem *p, const KFileItem &item, const QString &style)
409 : CFontModelItem(p),
410 itsStyle(style)
412 const KIO::UDSEntry &udsEntry(item.entry());
414 itsPixmap[0]=itsPixmap[1]=0L;
415 setUrl(item.url());
416 itsName=udsEntry.stringValue(KIO::UDSEntry::UDS_NAME);
417 itsFileName=udsEntry.stringValue((uint)UDS_EXTRA_FILE_NAME);
418 itsStyleInfo=udsEntry.numberValue((uint)UDS_EXTRA_FC_STYLE);
419 itsIndex=Misc::getIntQueryVal(KUrl(udsEntry.stringValue((uint)KIO::UDSEntry::UDS_URL)),
420 KFI_KIO_FACE, 0);
421 itsWritingSystems=udsEntry.numberValue((uint)UDS_EXTRA_WRITING_SYSTEMS);
422 itsMimeType=item.mimetype();
423 itsBitmap="application/x-font-pcf"==itsMimeType || "application/x-font-bdf"==itsMimeType;
424 itsSize=item.size();
426 if(!Misc::root())
427 setIsSystem(isSysFolder(url().path().section('/', 1, 1)));
429 QString files=udsEntry.stringValue((uint)UDS_EXTRA_FILE_LIST);
431 if(!files.isEmpty())
432 itsFiles.fromString(files);
434 itsFiles.prepend(CDisabledFonts::TFile(itsFileName, itsIndex,
435 udsEntry.stringValue((uint)UDS_EXTRA_FOUNDRY)));
438 void CFontItem::touchThumbnail()
440 #ifdef KFI_SAVE_PIXMAPS
441 // Access thumbFile, if it exists, to prevent its removal from the cache
442 if(itsParent)
444 QColor norm(QApplication::palette().color(QPalette::Text)),
445 sel(QApplication::palette().color(QPalette::HighlightedText));
447 setTimeStamp(CPreviewCache::thumbKey(family(), itsStyleInfo, CFontList::previewSize(), norm));
448 if(norm!=sel)
449 setTimeStamp(CPreviewCache::thumbKey(family(), itsStyleInfo, CFontList::previewSize(), sel));
451 #endif
454 void CFontItem::setUrl(const KUrl &url)
456 itsUrl=url;
457 itsEnabled=!Misc::isHidden(itsUrl);
459 if(!itsFiles.isEmpty()) // Then we changed state, so need to alter filename list...
461 toggle(itsFileName, itsEnabled);
463 CDisabledFonts::TFileList::Iterator it(itsFiles.begin()),
464 end(itsFiles.end());
466 for(; it!=end; ++it)
467 toggle((*it).path, itsEnabled);
471 const QPixmap * CFontItem::pixmap(bool selected, bool force)
473 int idx(selected ? 1 : 0);
475 if(parent() &&
476 (!itsPixmap[idx] || itsPixmap[idx]->isNull() || force ||
477 itsPixmap[idx]->height()!=CFontList::previewSize()))
478 itsPixmap[idx]=theCache->getPixmap(family(), name(), isEnabled()
479 ? QString()
480 : itsFileName,
481 CFontList::previewSize(), itsStyleInfo, selected, force);
483 return itsPixmap[idx];
486 CFamilyItem::CFamilyItem(CFontList &p, const QString &n)
487 : CFontModelItem(NULL),
488 itsName(n),
489 itsStatus(ENABLED),
490 itsRealStatus(ENABLED),
491 itsRegularFont(NULL),
492 itsParent(p)
496 CFamilyItem::~CFamilyItem()
498 qDeleteAll(itsFonts);
499 itsFonts.clear();
502 void CFamilyItem::touchThumbnail()
504 if(itsRegularFont)
505 itsRegularFont->touchThumbnail();
508 CFontItem * CFamilyItem::findFont(const KFileItem &i)
510 QList<CFontItem *>::ConstIterator fIt(itsFonts.begin()),
511 fEnd(itsFonts.end());
513 for(; fIt!=fEnd; ++fIt)
514 if((*(*fIt)).url()==i.url())
515 return (*fIt);
517 return NULL;
520 void CFamilyItem::getFoundries(QSet<QString> &foundries) const
522 QList<CFontItem *>::ConstIterator it(itsFonts.begin()),
523 end(itsFonts.end());
525 for(; it!=end; ++it)
527 CDisabledFonts::TFileList::ConstIterator fIt((*it)->files().begin()),
528 fEnd((*it)->files().end());
530 for(; fIt!=fEnd; ++fIt)
531 if(!(*fIt).foundry.isEmpty())
532 foundries.insert(capitaliseFoundry((*fIt).foundry));
536 bool CFamilyItem::usable(const CFontItem *font, bool root)
538 return (!font->isHidden() || itsParent.allowDisabled()) &&
539 ( root ||
540 (font->isSystem() && itsParent.allowSys()) ||
541 (!font->isSystem() && itsParent.allowUser()));
544 void CFamilyItem::addFont(CFontItem *font)
546 itsFonts.append(font);
547 updateStatus();
548 updateRegularFont(font);
551 void CFamilyItem::removeFont(CFontItem *font)
553 itsFonts.removeAll(font);
554 updateStatus();
555 if(itsRegularFont==font)
557 itsRegularFont=NULL;
558 updateRegularFont(NULL);
560 delete font;
563 void CFamilyItem::refresh()
565 updateStatus();
566 itsRegularFont=NULL;
567 updateRegularFont(NULL);
568 touchThumbnail();
571 bool CFamilyItem::updateStatus()
573 bool root(Misc::root());
574 QString oldIcon(itsIcon);
575 EStatus oldStatus(itsStatus);
576 QList<CFontItem *>::ConstIterator it(itsFonts.begin()),
577 end(itsFonts.end());
578 int en(0), dis(0), allEn(0), allDis(0);
579 bool oldSys(isSystem()),
580 sys(false);
581 QStringList mimeTypes;
583 itsFontCount=0;
584 for(; it!=end; ++it)
585 if(usable(*it, root))
587 QString mime((*it)->mimetype());
589 if((*it)->isEnabled())
590 en++;
591 else
592 dis++;
593 if(!mimeTypes.contains(mime))
594 mimeTypes.append(mime);
595 if(!sys)
596 sys=(*it)->isSystem();
597 itsFontCount++;
599 else
600 if((*it)->isEnabled())
601 allEn++;
602 else
603 allDis++;
605 allEn+=en;
606 allDis+=dis;
608 itsStatus=en && dis
609 ? PARTIAL
610 : en
611 ? ENABLED
612 : DISABLED;
614 itsRealStatus=allEn && allDis
615 ? PARTIAL
616 : allEn
617 ? ENABLED
618 : DISABLED;
620 itsIcon=1==mimeTypes.count()
621 ? KMimeType::mimeType(mimeTypes[0])->iconName()
622 : "application-x-font-ttf";
624 if(!root)
625 setIsSystem(sys);
627 return itsStatus!=oldStatus || itsIcon!=oldIcon || isSystem()!=oldSys;
630 bool CFamilyItem::updateRegularFont(CFontItem *font)
632 static const quint32 constRegular=FC::createStyleVal(FC_WEIGHT_REGULAR, KFI_FC_WIDTH_NORMAL, FC_SLANT_ROMAN);
634 CFontItem *oldFont(itsRegularFont);
635 bool root(Misc::root());
637 if(font && usable(font, root))
639 if(itsRegularFont)
641 int regDiff=abs((long)(itsRegularFont->styleInfo()-constRegular)),
642 fontDiff=abs((long)(font->styleInfo()-constRegular));
644 if(fontDiff<regDiff)
645 itsRegularFont=font;
647 else
648 itsRegularFont=font;
650 else // This case happens when the regular font is deleted...
652 QList<CFontItem *>::ConstIterator it(itsFonts.begin()),
653 end(itsFonts.end());
654 quint32 current=0x0FFFFFFF;
656 for(; it!=end; ++it)
657 if(usable(*it, root))
659 quint32 diff=abs((long) ((*it)->styleInfo()-constRegular));
661 if(diff<current)
663 itsRegularFont=(*it);
664 current=diff;
669 return oldFont!=itsRegularFont;
672 CFontList::CFontList(CFcEngine *eng, QWidget *parent)
673 : QAbstractItemModel(parent),
674 itsAllowSys(true),
675 itsAllowUser(true)
677 if(!theCache)
678 theCache=new CPreviewCache(eng);
680 QFont font;
681 int pixelSize((int)(((font.pointSizeF()*QX11Info::appDpiY())/72.0)+0.5));
683 setPreviewSize(pixelSize+12);
684 itsLister=new CFontLister(this);
685 connect(itsLister, SIGNAL(started()), SIGNAL(started()));
686 connect(itsLister, SIGNAL(completed()), SLOT(listingCompleted()));
687 connect(itsLister, SIGNAL(newItems(const KFileItemList &)),
688 SLOT(newItems(const KFileItemList &)));
689 connect(itsLister, SIGNAL(deleteItems(const KFileItemList &)),
690 SLOT(deleteItems(const KFileItemList &)));
691 connect(itsLister, SIGNAL(renameItems(const RenameList &)),
692 SLOT(renameItems(const RenameList &)));
693 connect(itsLister, SIGNAL(percent(int)), SIGNAL(percent(int)));
694 connect(itsLister, SIGNAL(message(QString)), SIGNAL(status(QString)));
697 CFontList::~CFontList()
699 delete theCache;
700 theCache=NULL;
701 delete itsLister;
702 itsLister=NULL;
703 qDeleteAll(itsFamilies);
704 itsFamilies.clear();
705 itsFonts.clear();
708 int CFontList::columnCount(const QModelIndex &) const
710 return 3;
713 QVariant CFontList::data(const QModelIndex &, int) const
715 return QVariant();
718 Qt::ItemFlags CFontList::flags(const QModelIndex &index) const
720 if (!index.isValid())
721 return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
722 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
725 Qt::DropActions CFontList::supportedDropActions() const
727 return Qt::CopyAction | Qt::MoveAction;
730 QMimeData * CFontList::mimeData(const QModelIndexList &indexes) const
732 QMimeData *mimeData = new QMimeData();
733 QByteArray encodedData;
734 QModelIndexList::ConstIterator it(indexes.begin()),
735 end(indexes.end());
736 QSet<QString> families;
737 QDataStream ds(&encodedData, QIODevice::WriteOnly);
739 for(; it!=end; ++it)
740 if((*it).isValid())
741 if((static_cast<CFontModelItem *>((*it).internalPointer()))->isFont())
743 CFontItem *font=static_cast<CFontItem *>((*it).internalPointer());
745 families.insert(font->family());
747 else
749 CFamilyItem *fam=static_cast<CFamilyItem *>((*it).internalPointer());
751 families.insert(fam->name());
754 ds << families;
755 mimeData->setData(KFI_FONT_DRAG_MIME, encodedData);
756 return mimeData;
759 QStringList CFontList::mimeTypes() const
761 QStringList types;
763 types << "text/uri-list";
764 return types;
767 QVariant CFontList::headerData(int section, Qt::Orientation orientation,
768 int role) const
770 if (orientation == Qt::Horizontal)
771 switch(role)
773 case Qt::DisplayRole:
774 switch(section)
776 case COL_FONT:
777 return i18n("Font");
778 case COL_PREVIEW:
779 return i18n("Preview");
780 default:
781 break;
783 break;
784 case Qt::DecorationRole:
785 if(COL_STATUS==section)
786 return SmallIcon("fontstatus");
787 break;
788 case Qt::TextAlignmentRole:
789 return Qt::AlignLeft;
790 case Qt::ToolTipRole:
791 if(COL_STATUS==section)
792 return i18n("This column shows the status of the font family, and of the "
793 "individual font styles.");
794 break;
795 case Qt::WhatsThisRole:
796 return whatsThis();
797 default:
798 break;
801 return QVariant();
804 QModelIndex CFontList::index(int row, int column, const QModelIndex &parent) const
806 if(parent.isValid()) // Then font...
808 CFamilyItem *fam=static_cast<CFamilyItem*>(parent.internalPointer());
810 if(row<fam->fonts().count())
811 return createIndex(row, column, fam->fonts().at(row));
813 else // Family....
814 if(row<itsFamilies.count())
815 return createIndex(row, column, itsFamilies.at(row));
817 return QModelIndex();
820 QModelIndex CFontList::parent(const QModelIndex &index) const
822 if(!index.isValid())
823 return QModelIndex();
825 CFontModelItem *mi=static_cast<CFontModelItem *>(index.internalPointer());
827 if(mi->isFamily())
828 return QModelIndex();
829 else
831 CFontItem *font=static_cast<CFontItem *>(index.internalPointer());
833 return createIndex(itsFamilies.indexOf(((CFamilyItem *)font->parent())), 0, font->parent());
837 int CFontList::rowCount(const QModelIndex &parent) const
839 if(parent.isValid())
841 CFontModelItem *mi=static_cast<CFontModelItem *>(parent.internalPointer());
843 if(mi->isFont())
844 return 0;
846 CFamilyItem *fam=static_cast<CFamilyItem *>(parent.internalPointer());
848 return fam->fonts().count();
850 else
851 return itsFamilies.count();
854 void CFontList::forceNewPreviews()
856 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
857 end(itsFamilies.end());
859 for(; it!=end; ++it)
861 QList<CFontItem *>::ConstIterator fit((*it)->fonts().begin()),
862 fend((*it)->fonts().end());
864 for(; fit!=fend; ++fit)
865 (*fit)->clearPixmap();
868 theCache->empty();
871 void CFontList::refresh(bool allowSys, bool allowUser)
873 itsAllowSys=allowSys;
874 itsAllowUser=allowUser;
875 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
876 end(itsFamilies.end());
878 for(; it!=end; ++it)
879 (*it)->refresh();
882 void CFontList::setAllowDisabled(bool on)
884 itsAllowDisabled=on;
885 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
886 end(itsFamilies.end());
888 for(; it!=end; ++it)
889 (*it)->refresh();
892 void CFontList::getFamilyStats(QSet<QString> &enabled, QSet<QString> &disabled, QSet<QString> &partial)
894 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
895 end(itsFamilies.end());
897 for(; it!=end; ++it)
899 switch((*it)->realStatus())
901 case CFamilyItem::ENABLED:
902 enabled.insert((*it)->name());
903 break;
904 case CFamilyItem::PARTIAL:
905 partial.insert((*it)->name());
906 break;
907 case CFamilyItem::DISABLED:
908 disabled.insert((*it)->name());
909 break;
914 void CFontList::getFoundries(QSet<QString> &foundries) const
916 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
917 end(itsFamilies.end());
919 for(; it!=end; ++it)
920 (*it)->getFoundries(foundries);
923 QString CFontList::whatsThis() const
925 return i18n("<p>This list shows your installed fonts. The fonts are grouped by family, and the"
926 " number in square brackets represents the number of styles in which the family is"
927 " available. e.g.</p>"
928 "<ul>"
929 "<li>Times [4]"
930 "<ul><li>Regular</li>"
931 "<li>Bold</li>"
932 "<li>Bold Italic</li>"
933 "<li>Italic</li>"
934 "</ul>"
935 "</li>"
936 "</ul>");
939 void CFontList::listingCompleted()
941 touchThumbnails();
942 emit finished();
945 void CFontList::newItems(const KFileItemList &items)
947 emit layoutAboutToBeChanged();
949 #ifdef KFI_FONTLIST_DEBUG
950 kDebug() << "************** " << items.count();
952 // for(KFileItemList::const_iterator it(items.begin()), end(items.end()) ; it!=end ; ++it)
953 // kDebug() << " " << (int)(*it);
954 #endif
956 for(KFileItemList::const_iterator it(items.begin()), end(items.end()) ; it!=end ; ++it)
957 addItem(*it);
959 emit layoutChanged();
962 void CFontList::clearItems()
964 #ifdef KFI_FONTLIST_DEBUG
965 kDebug() << "**************";
966 #endif
968 beginRemoveRows(QModelIndex(), 0, itsFamilies.count());
969 endRemoveRows();
970 qDeleteAll(itsFamilies);
971 itsFamilies.clear();
972 itsFonts.clear();
975 void CFontList::renameItems(const RenameList &items)
977 emit layoutAboutToBeChanged();
979 #ifdef KFI_FONTLIST_DEBUG
980 kDebug() << "************** " << items.count();
982 for(RenameList::const_iterator it(items.begin()), end(items.end()) ; it!=end ; ++it)
983 kDebug() << " " << (*it).from.prettyUrl();
984 #endif
986 QSet<CFamilyItem *> families;
988 for(RenameList::const_iterator it(items.begin()), end(items.end()) ; it!=end ; ++it)
990 CFontItem *font=findFont((*it).from);
992 if(font)
994 font->setUrl((*it).to);
995 itsFonts.insert((*it).to, font);
996 itsFonts.erase(itsFonts.find((*it).from));
997 #ifdef KFI_FONTLIST_DEBUG
998 kDebug() << " Found font, status now:" << font->isEnabled()
999 << " from:" << (*it).from.prettyUrl()
1000 << " to:" << (*it).to.prettyUrl();
1001 #endif
1002 families.insert(static_cast<CFamilyItem *>(font->parent()));
1004 #ifdef KFI_FONTLIST_DEBUG
1005 else
1006 kDebug() << " Could not locate font :-( " << (*it).from.prettyUrl();
1007 #endif
1010 QSet<CFamilyItem *>::ConstIterator it(families.begin()),
1011 end(families.end());
1013 for(; it!=end; ++it)
1014 (*it)->updateStatus();
1016 emit layoutChanged();
1019 void CFontList::deleteItems(const KFileItemList &items)
1021 emit layoutAboutToBeChanged();
1023 #ifdef KFI_FONTLIST_DEBUG
1024 kDebug() << "************** " << items.count();
1025 #endif
1027 KFileItemList::ConstIterator it(items.begin()),
1028 end(items.end());
1030 for(; it!=end; ++it)
1032 CFontItem *font=findFont((*it).url());
1034 if(font)
1036 CFamilyItem *fam=static_cast<CFamilyItem *>(font->parent());
1038 if(1==fam->fonts().count())
1039 itsFamilies.removeAll(fam);
1040 else
1041 fam->removeFont(font);
1042 itsFonts.remove((*it).url());
1046 emit layoutChanged();
1049 void CFontList::addItem(const KFileItem &item)
1051 CFontItem *font=findFont(item.url());
1053 #ifdef KFI_FONTLIST_DEBUG
1054 kDebug() << "************** " << item.url();
1055 #endif
1056 if(!font)
1058 QString family,
1059 style;
1061 decompose(getName(item), family, style);
1063 CFamilyItem *fam=findFamily(family, true);
1065 if(fam)
1067 font=new CFontItem(fam, item, style);
1069 fam->addFont(font);
1070 itsFonts.insert(item.url(), font);
1072 #ifdef KFI_FONTLIST_DEBUG
1073 else
1074 kDebug() << " Could not locate family!";
1075 #endif
1077 #ifdef KFI_FONTLIST_DEBUG
1078 else
1079 kDebug() << " Font already exists!";
1080 #endif
1083 CFamilyItem * CFontList::findFamily(const QString &familyName, bool create)
1085 CFamilyItem *fam=NULL;
1086 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
1087 end(itsFamilies.end());
1089 for(; it!=end && !fam; ++it)
1090 if((*it)->name()==familyName)
1091 fam=(*it);
1093 if(!fam && create)
1095 fam=new CFamilyItem(*this, familyName);
1096 itsFamilies.append(fam);
1099 return fam;
1102 CFontItem * CFontList::findFont(const KUrl &url)
1104 return itsFonts.contains(url)
1105 ? itsFonts[url]
1106 : NULL;
1109 void CFontList::touchThumbnails()
1111 #ifdef KFI_SAVE_PIXMAPS
1112 QList<CFamilyItem *>::ConstIterator it(itsFamilies.begin()),
1113 end(itsFamilies.end());
1115 for(; it!=end; ++it)
1116 (*it)->touchThumbnail();
1117 #endif
1120 inline bool matchString(const QString &str, const QString &pattern)
1122 return pattern.isEmpty() || -1!=str.indexOf(pattern, 0, Qt::CaseInsensitive);
1125 CFontListSortFilterProxy::CFontListSortFilterProxy(QObject *parent, QAbstractItemModel *model)
1126 : QSortFilterProxyModel(parent),
1127 itsMgtMode(false),
1128 itsGroup(NULL),
1129 itsFilterCriteria(CFontFilter::CRIT_FAMILY),
1130 itsFilterWs(0),
1131 itsFcQuery(NULL)
1133 setSourceModel(model);
1134 setSortCaseSensitivity(Qt::CaseInsensitive);
1135 setFilterKeyColumn(0);
1136 setDynamicSortFilter(false);
1137 itsTimer=new QTimer(this);
1138 connect(itsTimer, SIGNAL(timeout()), SLOT(timeout()));
1139 itsTimer->setSingleShot(true);
1142 QVariant CFontListSortFilterProxy::data(const QModelIndex &idx, int role) const
1144 if (!idx.isValid())
1145 return QVariant();
1147 static const int constMaxFiles=20;
1149 QModelIndex index(mapToSource(idx));
1150 CFontModelItem *mi=static_cast<CFontModelItem *>(index.internalPointer());
1152 switch(role)
1154 case Qt::ToolTipRole:
1155 if(itsMgtMode && (CFontFilter::CRIT_FILENAME==itsFilterCriteria || CFontFilter::CRIT_LOCATION==itsFilterCriteria ||
1156 CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria))
1157 if(mi->isFamily())
1159 CFamilyItem *fam=static_cast<CFamilyItem *>(index.internalPointer());
1160 QList<CFontItem *>::ConstIterator it(fam->fonts().begin()),
1161 end(fam->fonts().end());
1162 CDisabledFonts::TFileList allFiles;
1163 QString tip("<b>"+fam->name()+"</b>");
1164 int size(0);
1165 bool markMatch(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria);
1166 tip+="<p style='white-space:pre'><table>";
1168 for(; it!=end; ++it)
1170 allFiles+=(*it)->files();
1171 size+=(*it)->size();
1174 //qSort(allFiles);
1175 CDisabledFonts::TFileList::ConstIterator fit(allFiles.begin()),
1176 fend(allFiles.end());
1178 for(int i=0; fit!=fend && i<constMaxFiles; ++fit, ++i)
1179 if(markMatch && itsFcQuery && (*fit)==itsFcQuery->file())
1180 tip+="<tr><td><b>"+Misc::contractHome(*fit)+"</b></td></tr>";
1181 else
1182 tip+="<tr><td>"+Misc::contractHome(*fit)+"</td></tr>";
1183 if(allFiles.count()>constMaxFiles)
1184 tip+="<tr><td><i>"+i18n("...plus %1 more", allFiles.count()-constMaxFiles)+"</td></tr>";
1186 tip+="</table></p>";
1187 return tip;
1189 else
1191 CFontItem *font=static_cast<CFontItem *>(index.internalPointer());
1192 QString tip("<b>"+font->name()+"</b>");
1193 const CDisabledFonts::TFileList &files(font->files());
1194 bool markMatch(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria);
1196 tip+="<p style='white-space:pre'><table>";
1198 //qSort(files);
1199 CDisabledFonts::TFileList::ConstIterator fit(files.begin()),
1200 fend(files.end());
1202 for(int i=0; fit!=fend && i<constMaxFiles; ++fit, ++i)
1203 if(markMatch && itsFcQuery && (*fit)==itsFcQuery->file())
1204 tip+="<tr><td><b>"+Misc::contractHome(*fit)+"</b></td></tr>";
1205 else
1206 tip+="<tr><td>"+Misc::contractHome(*fit)+"</td></tr>";
1207 if(files.count()>constMaxFiles)
1208 tip+="<tr><td><i>"+i18n("...plus %1 more", files.count()-constMaxFiles)+"</td></tr></li>";
1210 tip+="</table></p>";
1211 return tip;
1213 break;
1214 case Qt::FontRole:
1215 if(COL_FONT==index.column())
1217 bool sys(mi->isSystem()),
1218 disabled( (mi->isFont() && !(static_cast<CFontItem *>(index.internalPointer()))->isEnabled()) ||
1219 (mi->isFamily() &&
1220 CFamilyItem::DISABLED==(static_cast<CFamilyItem *>(index.internalPointer()))->status()));
1221 if(sys||disabled)
1223 QFont font;
1224 font.setItalic(disabled);
1225 font.setBold(sys);
1226 return font;
1229 break;
1230 case Qt::DisplayRole:
1231 if(COL_FONT==index.column())
1232 if(mi->isFamily())
1234 CFamilyItem *fam=static_cast<CFamilyItem *>(index.internalPointer());
1236 return i18n("%1 [%2]", fam->name(), fam->fontCount());
1238 else
1239 return (static_cast<CFontItem *>(index.internalPointer()))->style();
1240 break;
1241 case Qt::DecorationRole:
1242 if(mi->isFamily())
1244 CFamilyItem *fam=static_cast<CFamilyItem *>(index.internalPointer());
1246 switch(index.column())
1248 case COL_FONT:
1249 return SmallIcon(fam->icon(), 0,
1250 CFamilyItem::ENABLED==fam->status()
1251 ? KIconLoader::DefaultState
1252 : KIconLoader::DisabledState);
1253 break;
1254 case COL_STATUS:
1255 switch(fam->status())
1257 case CFamilyItem::PARTIAL:
1258 return SmallIcon("dialog-ok", 0, KIconLoader::DisabledState);
1259 case CFamilyItem::ENABLED:
1260 return SmallIcon("dialog-ok");
1261 case CFamilyItem::DISABLED:
1262 return SmallIcon("dialog-cancel");
1264 break;
1265 default:
1266 break;
1269 else
1270 if(COL_STATUS==index.column())
1271 return SmallIcon( (static_cast<CFontItem *>(index.internalPointer()))->isEnabled()
1272 ? "dialog-ok" : "dialog-cancel", 10);
1273 default:
1274 break;
1276 return QVariant();
1279 bool CFontListSortFilterProxy::acceptFont(CFontItem *fnt, bool checkFontText) const
1281 if((fnt->isBitmap() && !itsMgtMode) ||
1282 (fnt->isHidden() && !itsMgtMode))
1283 return false;
1285 if(itsGroup && (CGroupListItem::ALL!=itsGroup->type() || (!filterText().isEmpty() && checkFontText)))
1287 bool fontMatch(!checkFontText);
1289 if(!fontMatch)
1290 switch(itsFilterCriteria)
1292 case CFontFilter::CRIT_FONTCONFIG:
1293 fontMatch=itsFcQuery
1294 ? fnt->name()==itsFcQuery->font() // || fnt->files().contains(itsFcQuery->file())
1295 : false;
1296 break;
1297 case CFontFilter::CRIT_STYLE:
1298 fontMatch=matchString(fnt->style(), itsFilterText);
1299 break;
1300 case CFontFilter::CRIT_FOUNDRY:
1302 CDisabledFonts::TFileList::ConstIterator it(fnt->files().begin()),
1303 end(fnt->files().end());
1305 for(; it!=end && !fontMatch; ++it)
1306 fontMatch=0==(*it).foundry.compare(itsFilterText, Qt::CaseInsensitive);
1307 break;
1309 case CFontFilter::CRIT_FILENAME:
1311 CDisabledFonts::TFileList::ConstIterator it(fnt->files().begin()),
1312 end(fnt->files().end());
1314 for(; it!=end && !fontMatch; ++it)
1316 QString file(Misc::getFile(*it));
1317 int pos(Misc::isHidden(file) ? 1 : 0);
1319 if(pos==file.indexOf(itsFilterText, pos, Qt::CaseInsensitive))
1320 fontMatch=true;
1322 break;
1324 case CFontFilter::CRIT_LOCATION:
1326 CDisabledFonts::TFileList::ConstIterator it(fnt->files().begin()),
1327 end(fnt->files().end());
1329 for(; it!=end && !fontMatch; ++it)
1330 if(0==Misc::getDir(*it).indexOf(itsFilterText, 0, Qt::CaseInsensitive))
1331 fontMatch=true;
1332 break;
1334 case CFontFilter::CRIT_WS:
1335 fontMatch=fnt->writingSystems()&itsFilterWs;
1336 break;
1337 default:
1338 break;
1341 return fontMatch && itsGroup->hasFont(fnt);
1344 return true;
1347 bool CFontListSortFilterProxy::acceptFamily(CFamilyItem *fam) const
1349 if(CFamilyItem::DISABLED==fam->status() && !itsMgtMode)
1350 return false;
1352 QList<CFontItem *>::ConstIterator it(fam->fonts().begin()),
1353 end(fam->fonts().end());
1354 bool familyMatch(CFontFilter::CRIT_FAMILY==itsFilterCriteria &&
1355 matchString(fam->name(), itsFilterText));
1357 for(; it!=end; ++it)
1358 if(acceptFont(*it, !familyMatch))
1359 return true;
1360 return false;
1363 bool CFontListSortFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
1365 QModelIndex index(sourceModel()->index(sourceRow, 0, sourceParent));
1367 if(index.isValid())
1369 CFontModelItem *mi=static_cast<CFontModelItem *>(index.internalPointer());
1371 if(mi->isFont())
1373 CFontItem *font=static_cast<CFontItem *>(index.internalPointer());
1375 return acceptFont(font, !(CFontFilter::CRIT_FAMILY==itsFilterCriteria &&
1376 matchString(font->family(), itsFilterText)));
1378 else
1379 return acceptFamily(static_cast<CFamilyItem *>(index.internalPointer()));
1382 return false;
1385 bool CFontListSortFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
1387 if(left.isValid() && right.isValid())
1389 CFontModelItem *lmi=static_cast<CFontModelItem *>(left.internalPointer()),
1390 *rmi=static_cast<CFontModelItem *>(right.internalPointer());
1392 if(lmi->isFont()<rmi->isFont())
1393 return true;
1395 if(lmi->isFont())
1397 CFontItem *lfi=static_cast<CFontItem *>(left.internalPointer()),
1398 *rfi=static_cast<CFontItem *>(right.internalPointer());
1400 if(COL_STATUS==filterKeyColumn())
1402 if(lfi->isEnabled()<rfi->isEnabled() ||
1403 (lfi->isEnabled()==rfi->isEnabled() &&
1404 lfi->styleInfo()<rfi->styleInfo()))
1405 return true;
1407 else
1408 if(lfi->styleInfo()<rfi->styleInfo())
1409 return true;
1411 else
1413 CFamilyItem *lfi=static_cast<CFamilyItem *>(left.internalPointer()),
1414 *rfi=static_cast<CFamilyItem *>(right.internalPointer());
1416 if(COL_STATUS==filterKeyColumn())
1418 if(lfi->status()<rfi->status() ||
1419 (lfi->status()==rfi->status() && QString::localeAwareCompare(lfi->name(), rfi->name())<0))
1420 return true;
1422 else
1423 if(QString::localeAwareCompare(lfi->name(), rfi->name())<0)
1424 return true;
1428 return false;
1431 void CFontListSortFilterProxy::setFilterGroup(CGroupListItem *grp)
1433 if(grp!=itsGroup)
1435 // bool wasNull=!itsGroup;
1437 itsGroup=grp;
1439 // if(!(wasNull && itsGroup && CGroupListItem::ALL==itsGroup->type()))
1440 clear();
1444 void CFontListSortFilterProxy::setFilterText(const QString &text)
1446 if(text!=itsFilterText)
1449 // If we are filtering on file location, then expand ~ to /home/user, etc.
1450 if (CFontFilter::CRIT_LOCATION==itsFilterCriteria && !text.isEmpty() && ('~'==text[0] || '$'==text[0]))
1451 if('~'==text[0])
1452 itsFilterText=1==text.length()
1453 ? QDir::homePath()
1454 : QString(text).replace(0, 1, QDir::homePath());
1455 else
1456 itsFilterText=replaceEnvVar(text);
1457 else
1458 itsFilterText=text;
1460 if(itsFilterText.isEmpty())
1462 itsTimer->stop();
1463 timeout();
1465 else
1466 itsTimer->start(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria ? 750 : 400);
1470 void CFontListSortFilterProxy::setFilterCriteria(CFontFilter::ECriteria crit, qulonglong ws)
1472 if(crit!=itsFilterCriteria || ws!=itsFilterWs)
1474 itsFilterWs=ws;
1475 itsFilterCriteria=crit;
1476 if(CFontFilter::CRIT_LOCATION==itsFilterCriteria)
1477 setFilterText(itsFilterText);
1478 itsTimer->stop();
1479 timeout();
1483 void CFontListSortFilterProxy::setMgtMode(bool on)
1485 if(on!=itsMgtMode)
1487 itsMgtMode=on;
1488 clear();
1492 void CFontListSortFilterProxy::timeout()
1494 if(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria)
1496 int commaPos=itsFilterText.indexOf(',');
1497 QString query(itsFilterText);
1499 if(-1!=commaPos)
1501 QString style(query.mid(commaPos+1));
1502 query=query.left(commaPos);
1503 query=query.trimmed();
1504 query+=":style=";
1505 style=style.trimmed();
1506 query+=style;
1508 else
1509 query=query.trimmed();
1511 if(!itsFcQuery)
1513 itsFcQuery=new CFcQuery(this);
1514 connect(itsFcQuery, SIGNAL(finished()), SLOT(fcResults()));
1517 itsFcQuery->run(query);
1519 else
1521 clear();
1522 emit refresh();
1526 void CFontListSortFilterProxy::fcResults()
1528 if(CFontFilter::CRIT_FONTCONFIG==itsFilterCriteria)
1530 clear();
1531 emit refresh();
1535 class CFontListViewDelegate : public QStyledItemDelegate
1537 public:
1539 CFontListViewDelegate(QObject *p, QSortFilterProxyModel *px) : QStyledItemDelegate(p), itsProxy(px) { }
1540 virtual ~CFontListViewDelegate() { }
1542 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const
1544 QStyledItemDelegate::paint(painter, option, idx);
1546 QModelIndex index(itsProxy->mapToSource(idx));
1548 if(index.isValid() && (static_cast<CFontModelItem *>(index.internalPointer()))->isFamily() &&
1549 COL_PREVIEW==index.column())
1551 CFamilyItem *fam=static_cast<CFamilyItem *>(index.internalPointer());
1553 if(fam->regularFont())
1555 const QPixmap *pix=fam->regularFont()->pixmap(option.state&QStyle::State_Selected);
1557 if(pix)
1558 if(Qt::RightToLeft==QApplication::layoutDirection())
1559 painter->drawPixmap(option.rect.x()-(pix->width()-option.rect.width()),
1560 option.rect.y(), *pix);
1561 else
1562 painter->drawPixmap(option.rect.x(), option.rect.y(), *pix);
1567 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &idx) const
1569 QModelIndex index(itsProxy->mapToSource(idx));
1571 return (index.isValid() && (static_cast<CFontModelItem *>(index.internalPointer()))->isFamily())
1572 ? QSize(CFontList::previewSize(), CFontList::previewSize())
1573 : QStyledItemDelegate::sizeHint(option, index);
1576 private:
1578 QSortFilterProxyModel *itsProxy;
1581 #define COL_FONT_SIZE "FontNameColSize"
1583 CFontListView::CFontListView(QWidget *parent, CFontList *model)
1584 : QTreeView(parent),
1585 itsProxy(new CFontListSortFilterProxy(this, model)),
1586 itsModel(model),
1587 itsAllowDrops(false)
1589 setModel(itsProxy);
1590 setItemDelegate(new CFontListViewDelegate(this, itsProxy));
1591 itsModel=model;
1592 resizeColumnToContents(COL_STATUS);
1593 setSelectionMode(QAbstractItemView::ExtendedSelection);
1594 setSelectionBehavior(QAbstractItemView::SelectRows);
1595 setSortingEnabled(true);
1596 sortByColumn(COL_FONT, Qt::AscendingOrder);
1597 setAllColumnsShowFocus(true);
1598 setAlternatingRowColors(true);
1599 setAcceptDrops(true);
1600 setDropIndicatorShown(false);
1601 setDragEnabled(true);
1602 setDragDropMode(QAbstractItemView::DragDrop);
1603 header()->setClickable(true);
1604 header()->setSortIndicatorShown(true);
1605 setColumnHidden(COL_STATUS, true);
1606 connect(this, SIGNAL(collapsed(const QModelIndex &)), SLOT(itemCollapsed(const QModelIndex &)));
1607 connect(header(), SIGNAL(sectionClicked(int)), SLOT(setSortColumn(int)));
1608 connect(itsProxy, SIGNAL(refresh()), SIGNAL(refresh()));
1610 setWhatsThis(model->whatsThis());
1611 header()->setWhatsThis(whatsThis());
1612 itsStdMenu=new QMenu(this);
1613 itsDeleteAct=itsStdMenu->addAction(KIcon("edit-delete"), i18n("Delete"),
1614 this, SIGNAL(del()));
1615 itsPrintAct=itsStdMenu->addAction(KIcon("document-print"), i18n("Print..."),
1616 this, SIGNAL(print()));
1617 itsViewAct=itsStdMenu->addAction(KIcon("kfontview"), i18n("Open in Font Viewer"),
1618 this, SLOT(view()));
1619 itsStdMenu->addSeparator();
1620 QAction *reloadAct=itsStdMenu->addAction(KIcon("view-refresh"), i18n("Reload"), this, SIGNAL(reload()));
1622 itsMgtMenu=new QMenu(this);
1623 itsMgtMenu->addAction(itsDeleteAct);
1624 itsMgtMenu->addSeparator();
1625 itsEnableAct=itsMgtMenu->addAction(KIcon("enablefont"), i18n("Enable"),
1626 this, SIGNAL(enable()));
1627 itsDisableAct=itsMgtMenu->addAction(KIcon("disablefont"), i18n("Disable"),
1628 this, SIGNAL(disable()));
1629 itsMgtMenu->addSeparator();
1630 itsMgtMenu->addAction(itsPrintAct);
1631 itsMgtMenu->addAction(itsViewAct);
1632 itsMgtMenu->addSeparator();
1633 itsMgtMenu->addAction(reloadAct);
1636 void CFontListView::readConfig(KConfigGroup &cg)
1638 setColumnWidth(COL_FONT, cg.readEntry(COL_FONT_SIZE, 200));
1641 void CFontListView::writeConfig(KConfigGroup &cg)
1643 cg.writeEntry(COL_FONT_SIZE, columnWidth(COL_FONT));
1646 void CFontListView::getFonts(CJobRunner::ItemList &urls, QStringList &fontNames, QSet<Misc::TFont> *fonts,
1647 bool *hasSys, bool selected, bool getEnabled, bool getDisabled)
1649 QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes());
1650 QSet<CFontItem *> usedFonts;
1651 QModelIndex index;
1653 foreach(index, selectedItems)
1654 if(index.isValid())
1656 QModelIndex realIndex(itsProxy->mapToSource(index));
1658 if(realIndex.isValid())
1659 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont())
1661 CFontItem *font=static_cast<CFontItem *>(realIndex.internalPointer());
1663 addFont(font, urls, fontNames, fonts, hasSys, usedFonts,
1664 getEnabled, getDisabled);
1666 else
1668 CFamilyItem *fam=static_cast<CFamilyItem *>(realIndex.internalPointer());
1670 for(int ch=0; ch<fam->fontCount(); ++ch)
1672 QModelIndex child(itsProxy->mapToSource(index.child(ch, 0)));
1674 if(child.isValid() &&
1675 (static_cast<CFontModelItem *>(child.internalPointer()))->isFont())
1677 CFontItem *font=static_cast<CFontItem *>(child.internalPointer());
1679 addFont(font, urls, fontNames, fonts, hasSys, usedFonts,
1680 getEnabled, getDisabled);
1686 fontNames=CFontList::compact(fontNames);
1689 void CFontListView::getPrintableFonts(QSet<Misc::TFont> &items, bool selected)
1691 QModelIndexList selectedItems(selected ? selectedIndexes() : allIndexes());
1692 QModelIndex index;
1694 foreach(index, selectedItems)
1696 CFontItem *font=NULL;
1698 if(index.isValid() && 0==index.column())
1700 QModelIndex realIndex(itsProxy->mapToSource(index));
1702 if(realIndex.isValid())
1703 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont())
1704 font=static_cast<CFontItem *>(realIndex.internalPointer());
1705 else
1707 CFamilyItem *fam=static_cast<CFamilyItem *>(realIndex.internalPointer());
1708 font=fam->regularFont();
1712 if(font && Misc::printable(font->mimetype()) && font->isEnabled())
1713 items.insert(Misc::TFont(font->family(), font->styleInfo()));
1717 void CFontListView::setFilterGroup(CGroupListItem *grp)
1719 CGroupListItem *oldGrp(itsProxy->filterGroup());
1721 itsProxy->setFilterGroup(grp);
1722 itsAllowDrops=grp && !grp->isCustom();
1724 if(!Misc::root())
1726 bool refreshStats(false);
1728 if(!grp || !oldGrp)
1729 refreshStats=true;
1730 else
1732 // Check to see whether we have changed from listing all fonts,
1733 // listing just system or listing personal fonts.
1734 CGroupListItem::EType aType(CGroupListItem::CUSTOM==grp->type() ||
1735 CGroupListItem::ALL==grp->type() ||
1736 CGroupListItem::UNCLASSIFIED==grp->type()
1737 ? CGroupListItem::CUSTOM : grp->type()),
1738 bType(CGroupListItem::CUSTOM==oldGrp->type() ||
1739 CGroupListItem::ALL==oldGrp->type() ||
1740 CGroupListItem::UNCLASSIFIED==oldGrp->type()
1741 ? CGroupListItem::CUSTOM : oldGrp->type());
1742 refreshStats=aType!=bType;
1745 if(refreshStats)
1746 itsModel->refresh(!grp || !grp->isPersonal(),
1747 !grp || !grp->isSystem());
1751 void CFontListView::refreshFilter()
1753 itsProxy->clear();
1756 void CFontListView::filterText(const QString &text)
1758 itsProxy->setFilterText(text);
1761 void CFontListView::filterCriteria(int crit, qulonglong ws)
1763 itsProxy->setFilterCriteria((CFontFilter::ECriteria)crit, ws);
1766 void CFontListView::stats(int &enabled, int &disabled, int &partial)
1768 enabled=disabled=partial=0;
1770 for(int i=0; i<itsProxy->rowCount(); ++i)
1772 QModelIndex idx(itsProxy->index(i, 0, QModelIndex()));
1774 if(!idx.isValid())
1775 break;
1777 QModelIndex sourceIdx(itsProxy->mapToSource(idx));
1779 if(!sourceIdx.isValid())
1780 break;
1782 if((static_cast<CFontModelItem *>(sourceIdx.internalPointer()))->isFamily())
1783 switch((static_cast<CFamilyItem *>(sourceIdx.internalPointer()))->status())
1785 case CFamilyItem::ENABLED:
1786 enabled++;
1787 break;
1788 case CFamilyItem::DISABLED:
1789 disabled++;
1790 break;
1791 case CFamilyItem::PARTIAL:
1792 partial++;
1793 break;
1798 void CFontListView::selectedStatus(bool &enabled, bool &disabled)
1800 QModelIndexList selected(selectedItems());
1801 QModelIndex index;
1803 enabled=disabled=false;
1805 foreach(index, selected)
1807 QModelIndex realIndex(itsProxy->mapToSource(index));
1809 if(realIndex.isValid())
1810 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFamily())
1811 switch((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status())
1813 case CFamilyItem::ENABLED:
1814 enabled=true;
1815 break;
1816 case CFamilyItem::DISABLED:
1817 disabled=true;
1818 break;
1819 case CFamilyItem::PARTIAL:
1820 enabled=true;
1821 disabled=true;
1822 break;
1824 else
1825 if((static_cast<CFontItem *>(realIndex.internalPointer()))->isEnabled())
1826 enabled=true;
1827 else
1828 disabled=true;
1829 if(enabled && disabled)
1830 break;
1834 QModelIndexList CFontListView::allFonts()
1836 QModelIndexList rv;
1837 int rowCount(itsProxy->rowCount());
1839 for(int i=0; i<rowCount; ++i)
1841 QModelIndex idx(itsProxy->index(i, 0, QModelIndex()));
1842 int childRowCount(itsProxy->rowCount(idx));
1844 for(int j=0; j<childRowCount; ++j)
1846 QModelIndex child(itsProxy->index(j, 0, idx));
1848 if(child.isValid())
1849 rv.append(itsProxy->mapToSource(child));
1853 return rv;
1856 void CFontListView::setMgtMode(bool on)
1858 if(on!=itsProxy->mgtMode())
1860 setDragEnabled(on);
1861 setDragDropMode(on ? QAbstractItemView::DragDrop : QAbstractItemView::DropOnly);
1862 setColumnHidden(COL_STATUS, !on);
1863 if(on)
1864 resizeColumnToContents(COL_STATUS);
1866 itsModel->setAllowDisabled(on);
1867 itsProxy->setMgtMode(on);
1868 setMouseTracking(on);
1872 void CFontListView::selectFirstFont()
1874 if(0==selectedItems().count())
1875 for(int i=0; i<NUM_COLS; ++i)
1877 QModelIndex idx(itsProxy->index(0, i, QModelIndex()));
1879 if(idx.isValid())
1880 selectionModel()->select(idx, QItemSelectionModel::Select);
1884 void CFontListView::setSortColumn(int col)
1886 if(col!=itsProxy->filterKeyColumn())
1888 itsProxy->setFilterKeyColumn(col);
1889 itsProxy->clear();
1893 void CFontListView::selectionChanged(const QItemSelection &selected,
1894 const QItemSelection &deselected)
1896 QAbstractItemView::selectionChanged(selected, deselected);
1899 // Go throgh current selection, and for any 'font' items that are selected,
1900 // ensure 'family' item is not...
1901 QModelIndexList selectedItems(selectedIndexes()),
1902 deselectList;
1903 QModelIndex index;
1904 QSet<CFontModelItem *> selectedFamilies;
1905 bool en(false),
1906 dis(false);
1908 foreach(index, selectedItems)
1909 if(index.isValid())
1911 QModelIndex realIndex(itsProxy->mapToSource(index));
1913 if(realIndex.isValid())
1914 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont())
1916 CFontItem *font=static_cast<CFontItem *>(realIndex.internalPointer());
1918 if(font->isEnabled())
1919 en=true;
1920 else
1921 dis=true;
1922 if(!selectedFamilies.contains(font->parent()))
1924 selectedFamilies.insert(font->parent());
1926 for(int i=0; i<NUM_COLS; ++i)
1927 deselectList.append(itsProxy->mapFromSource(
1928 itsModel->createIndex(font->parent()->rowNumber(),
1929 i, font->parent())));
1932 else
1933 switch((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status())
1935 case CFamilyItem::ENABLED:
1936 en=true;
1937 break;
1938 case CFamilyItem::DISABLED:
1939 dis=true;
1940 break;
1941 case CFamilyItem::PARTIAL:
1942 en=dis=true;
1943 break;
1947 if(deselectList.count())
1948 foreach(index, deselectList)
1949 selectionModel()->select(index, QItemSelectionModel::Deselect);
1951 emit itemSelected(selectedItems.count()
1952 ? itsProxy->mapToSource(selectedItems.last())
1953 : QModelIndex(), en, dis);
1956 void CFontListView::itemCollapsed(const QModelIndex &idx)
1958 if(idx.isValid())
1960 QModelIndex index(itsProxy->mapToSource(idx));
1962 if(index.isValid() && (static_cast<CFontModelItem *>(index.internalPointer()))->isFamily())
1964 CFamilyItem *fam=static_cast<CFamilyItem *>(index.internalPointer());
1965 QList<CFontItem *>::ConstIterator it(fam->fonts().begin()),
1966 end(fam->fonts().end());
1968 for(; it!=end; ++it)
1969 for(int i=0; i<NUM_COLS; ++i)
1970 selectionModel()->select(itsProxy->mapFromSource(itsModel->createIndex((*it)->rowNumber(),
1971 i, *it)),
1972 QItemSelectionModel::Deselect);
1977 static bool isScalable(const QString &str)
1979 QByteArray cFile(QFile::encodeName(str));
1981 return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") ||
1982 Misc::checkExt(cFile, "pfa") || Misc::checkExt(cFile, "pfb");
1985 void CFontListView::view()
1987 // Number of fonts user has selected, before we ask if they really want to view them all...
1988 static const int constMaxBeforePrompt=10;
1990 QModelIndexList selectedItems(selectedIndexes());
1991 QModelIndex index;
1992 QSet<CFontItem *> fonts;
1994 foreach(index, selectedItems)
1996 QModelIndex realIndex(itsProxy->mapToSource(index));
1998 if(realIndex.isValid())
1999 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont())
2001 CFontItem *font(static_cast<CFontItem *>(realIndex.internalPointer()));
2003 fonts.insert(font);
2005 else
2007 CFontItem *font((static_cast<CFamilyItem *>(realIndex.internalPointer()))->regularFont());
2009 if(font)
2010 fonts.insert(font);
2014 if(fonts.count() &&
2015 (fonts.count()<constMaxBeforePrompt ||
2016 KMessageBox::Yes==KMessageBox::questionYesNo(this, i18n("Open all %1 fonts in font viewer?", fonts.count()))))
2018 QSet<CFontItem *>::ConstIterator it(fonts.begin()),
2019 end(fonts.end());
2020 QStringList args;
2022 for(; it!=end; ++it)
2025 // If we can, speed up font viewer by passing the font details...
2026 if((*it)->isEnabled())
2028 KUrl url((*it)->url());
2030 url.addQueryItem(KFI_NAME_QUERY, (*it)->name());
2031 url.addQueryItem(KFI_STYLE_QUERY, QString().setNum((*it)->styleInfo()));
2032 url.addQueryItem(KFI_MIME_QUERY, (*it)->mimetype());
2034 args << url.url();
2036 else
2038 // For a disalbed font, we need to find the first scalable font entry in its file list...
2039 CDisabledFonts::TFileList::ConstIterator fit((*it)->files().begin()),
2040 fend((*it)->files().end());
2041 bool done(false);
2043 for(; fit!=fend; ++fit)
2044 if(isScalable(*fit))
2046 QString urlStr(KFI_KIO_FONTS_PROTOCOL":/");
2048 if(!Misc::root())
2049 if((*it)->isSystem())
2050 urlStr+=KFI_KIO_FONTS_SYS"/";
2051 else
2052 urlStr+=KFI_KIO_FONTS_USER"/";
2053 urlStr+=(*it)->name();
2055 KUrl url(urlStr);
2057 url.addQueryItem(KFI_FILE_QUERY, *fit);
2058 url.addQueryItem(KFI_KIO_FACE, QString().setNum((*it)->index()));
2059 url.addQueryItem(KFI_MIME_QUERY, (*it)->mimetype());
2061 args << url.url();
2062 done=true;
2063 break;
2066 if(!done)
2067 args << (*it)->url().url();
2071 QProcess::startDetached(KFI_VIEWER, args);
2075 QModelIndexList CFontListView::allIndexes()
2077 QModelIndexList rv;
2078 int rowCount(itsProxy->rowCount());
2080 for(int i=0; i<rowCount; ++i)
2082 QModelIndex idx(itsProxy->index(i, 0, QModelIndex()));
2083 int childRowCount(itsProxy->rowCount(idx));
2085 rv.append(idx);
2087 for(int j=0; j<childRowCount; ++j)
2089 QModelIndex child(itsProxy->index(j, 0, idx));
2091 if(child.isValid())
2092 rv.append(child);
2096 return rv;
2099 void CFontListView::startDrag(Qt::DropActions supportedActions)
2101 QModelIndexList indexes(selectedIndexes());
2103 if (indexes.count())
2105 QMimeData *data = model()->mimeData(indexes);
2106 if (!data)
2107 return;
2109 QModelIndex index(itsProxy->mapToSource(indexes.first()));
2110 const char *icon="application-x-font-pcf";
2112 if(index.isValid())
2114 CFontItem *font=(static_cast<CFontModelItem *>(index.internalPointer()))->isFont()
2115 ? static_cast<CFontItem *>(index.internalPointer())
2116 : (static_cast<CFamilyItem *>(index.internalPointer()))->regularFont();
2118 if(font && !font->isBitmap())
2119 if("application/x-font-type1"==font->mimetype())
2120 icon="application-x-font-type1";
2121 else
2122 icon="application-x-font-ttf";
2125 QPoint hotspot;
2126 QPixmap pix(DesktopIcon(icon, KIconLoader::SizeMedium));
2128 hotspot.setX(0); // pix.width()/2);
2129 hotspot.setY(0); // pix.height()/2);
2131 QDrag *drag = new QDrag(this);
2132 drag->setPixmap(pix);
2133 drag->setMimeData(data);
2134 drag->setHotSpot(hotspot);
2135 drag->start(supportedActions);
2139 void CFontListView::dragEnterEvent(QDragEnterEvent *event)
2141 if(itsAllowDrops && event->provides("text/uri-list")) // "application/x-kde-urilist" ??
2142 event->acceptProposedAction();
2145 void CFontListView::dropEvent(QDropEvent *event)
2147 if(itsAllowDrops && event->provides("text/uri-list"))
2149 event->acceptProposedAction();
2151 QList<QUrl> urls(event->mimeData()->urls());
2152 QList<QUrl>::ConstIterator it(urls.begin()),
2153 end(urls.end());
2154 QSet<KUrl> kurls;
2156 for(; it!=end; ++it)
2158 KMimeType::Ptr mime=KMimeType::findByUrl(*it, 0, false, true);
2160 if(mime->is("application/x-font-ttf") ||
2161 mime->is("application/x-font-otf") ||
2162 mime->is("application/x-font-type1") ||
2163 mime->is("fonts/package") ||
2164 (itsProxy->mgtMode() &&
2165 (mime->is("application/x-font-pcf") ||
2166 mime->is("application/x-font-bdf"))))
2167 kurls.insert(*it);
2170 if(kurls.count())
2171 emit fontsDropped(kurls);
2175 void CFontListView::contextMenuEvent(QContextMenuEvent *ev)
2177 bool valid(indexAt(ev->pos()).isValid());
2179 itsDeleteAct->setEnabled(valid);
2181 if(!itsProxy->mgtMode())
2183 itsPrintAct->setEnabled(valid);
2184 itsViewAct->setEnabled(valid);
2185 itsStdMenu->popup(ev->globalPos());
2187 else
2189 bool en(false),
2190 dis(false);
2191 QModelIndexList selectedItems(selectedIndexes());
2192 QModelIndex index;
2194 foreach(index, selectedItems)
2196 QModelIndex realIndex(itsProxy->mapToSource(index));
2198 if(realIndex.isValid())
2199 if((static_cast<CFontModelItem *>(realIndex.internalPointer()))->isFont())
2201 if((static_cast<CFontItem *>(realIndex.internalPointer())->isEnabled()))
2202 en=true;
2203 else
2204 dis=true;
2206 else
2207 switch((static_cast<CFamilyItem *>(realIndex.internalPointer()))->status())
2209 case CFamilyItem::ENABLED:
2210 en=true;
2211 break;
2212 case CFamilyItem::DISABLED:
2213 dis=true;
2214 break;
2215 case CFamilyItem::PARTIAL:
2216 en=dis=true;
2217 break;
2219 if(en && dis)
2220 break;
2223 itsEnableAct->setEnabled(dis);
2224 itsDisableAct->setEnabled(en);
2225 itsPrintAct->setEnabled(en|dis);
2226 itsViewAct->setEnabled(en|dis);
2227 itsMgtMenu->popup(ev->globalPos());
2231 bool CFontListView::viewportEvent(QEvent *event)
2233 executeDelayedItemsLayout();
2234 return QTreeView::viewportEvent(event);
2239 #include "FontList.moc"