File indexing completed on 2024-04-21 14:55:53
0001 /* This file is part of the KDE libraries 0002 0003 Copyright (C) 2008 Chusslove Illich <caslav.ilic@gmx.net> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "kfontcombobox.h" 0022 #include "fonthelpers_p.h" 0023 0024 #include "kdebug.h" 0025 #include "klocalizedstring.h" 0026 #include "kcolorscheme.h" 0027 #include "kfontchooser.h" 0028 #include "kcompletion.h" 0029 0030 #include <QEvent> 0031 #include <QListView> 0032 #include <QFontDatabase> 0033 #include <QIcon> 0034 #include <QAbstractItemDelegate> 0035 #include <QStringListModel> 0036 #include <QPainter> 0037 #include <QList> 0038 #include <QHash> 0039 #include <QScrollBar> 0040 0041 static QString alphabetSample() 0042 { 0043 return i18nc("short", 0044 // i18n: A shorter version of the alphabet test phrase translated in 0045 // another message. It is displayed in the dropdown list of font previews 0046 // (the font selection combo box), so keep it under the length equivalent 0047 // to 60 or so proportional Latin characters. 0048 "The Quick Brown Fox Jumps Over The Lazy Dog"); 0049 } 0050 0051 class KFontFamilyDelegate : public QAbstractItemDelegate 0052 { 0053 Q_OBJECT 0054 public: 0055 KDELIBS4SUPPORT_DEPRECATED explicit KFontFamilyDelegate(QObject *parent); 0056 0057 void paint(QPainter *painter, 0058 const QStyleOptionViewItem &option, 0059 const QModelIndex &index) const override; 0060 0061 QSize sizeHint(const QStyleOptionViewItem &option, 0062 const QModelIndex &index) const override; 0063 0064 QIcon truetype; 0065 QIcon bitmap; 0066 double sizeFactFamily; 0067 double sizeFactSample; 0068 0069 QHash<QString, QString> fontFamilyTrMap; 0070 }; 0071 0072 KFontFamilyDelegate::KFontFamilyDelegate(QObject *parent) 0073 : QAbstractItemDelegate(parent) 0074 { 0075 truetype = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fonttruetype-16.png")); 0076 bitmap = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fontbitmap-16.png")); 0077 0078 // Font size factors for family name and text sample in font previes, 0079 // multiplies normal font size. 0080 sizeFactFamily = 1.0; 0081 sizeFactSample = 1.0; // better leave at 1, so that user can relate sizes to default 0082 } 0083 0084 void KFontFamilyDelegate::paint(QPainter *painter, 0085 const QStyleOptionViewItem &option, 0086 const QModelIndex &index) const 0087 { 0088 QBrush sampleBrush; 0089 if (option.state & QStyle::State_Selected) { 0090 painter->save(); 0091 painter->setBrush(option.palette.highlight()); 0092 painter->setPen(Qt::NoPen); 0093 painter->drawRect(option.rect); 0094 painter->setPen(QPen(option.palette.highlightedText(), 0)); 0095 sampleBrush = option.palette.highlightedText(); 0096 } else { 0097 sampleBrush = KColorScheme(QPalette::Normal).foreground(KColorScheme::InactiveText); 0098 } 0099 0100 QFont baseFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 0101 QString trFontFamily = index.data(Qt::DisplayRole).toString(); 0102 QString fontFamily = fontFamilyTrMap[trFontFamily]; 0103 0104 // Writing systems provided by the font. 0105 QList<QFontDatabase::WritingSystem> availableSystems = QFontDatabase().writingSystems(fontFamily); 0106 0107 // Intersect font's writing systems with that specified for 0108 // the language's sample text, to see if the sample can be shown. 0109 // If the font reports no writing systems, assume it can show the sample. 0110 bool canShowLanguageSample = true; 0111 if (availableSystems.count() > 0) { 0112 canShowLanguageSample = false; 0113 QString scriptsSpec = i18nc("Numeric IDs of scripts for font previews", 0114 // i18n: Integer which indicates the script you used in the sample text 0115 // for font previews in your language. For the possible values, see 0116 // https://doc.qt.io/qt-5/qfontdatabase.html#WritingSystem-enum 0117 // If the sample text contains several scripts, their IDs can be given 0118 // as a comma-separated list (e.g. for Japanese it is "1,27"). 0119 "1"); 0120 QStringList scriptStrIds = scriptsSpec.split(','); 0121 foreach (const QString &scriptStrId, scriptStrIds) { 0122 bool convOk; 0123 int ws = scriptStrId.toInt(&convOk); 0124 if (convOk && ws > 0 && ws < QFontDatabase::WritingSystemsCount 0125 && availableSystems.contains(static_cast<QFontDatabase::WritingSystem>(ws))) { 0126 canShowLanguageSample = true; 0127 break; 0128 } 0129 } 0130 } 0131 0132 // Choose and paint an icon according to the font type, scalable or bitmat. 0133 const QIcon *icon = &bitmap; 0134 if (QFontDatabase().isSmoothlyScalable(fontFamily)) { 0135 icon = &truetype; 0136 } 0137 QRect r = option.rect; 0138 icon->paint(painter, r, Qt::AlignLeft | Qt::AlignTop); 0139 0140 // Claim space taken up by the icon. 0141 QSize actualSize = icon->actualSize(r.size()); 0142 if (option.direction == Qt::RightToLeft) { 0143 r.setRight(r.right() - actualSize.width() - 4); 0144 } else { 0145 r.setLeft(r.left() + actualSize.width() + 4); 0146 } 0147 0148 // Draw the font family. 0149 QFont oldPainterFont = painter->font(); 0150 QFont familyFont = baseFont; 0151 familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily); 0152 painter->setFont(familyFont); 0153 painter->drawText(r, Qt::AlignTop | Qt::AlignLeading | Qt::TextSingleLine, trFontFamily); 0154 0155 // Claim space taken up by the font family name. 0156 int h = painter->fontMetrics().lineSpacing(); 0157 r.setTop(r.top() + h); 0158 0159 // Show text sample in user's language if the writing system is supported, 0160 // otherwise show a collage of generic script samples provided by Qt. 0161 // If the font does not report what it supports, assume all. 0162 QString sample; 0163 if (canShowLanguageSample) { 0164 sample = alphabetSample(); 0165 } else { 0166 foreach (const QFontDatabase::WritingSystem &ws, availableSystems) { 0167 sample += QFontDatabase::writingSystemSample(ws) + " "; 0168 if (sample.length() > 40) { // do not let the sample be too long 0169 break; 0170 } 0171 } 0172 sample = sample.trimmed(); 0173 } 0174 QFont sampleFont; 0175 sampleFont.setFamily(fontFamily); 0176 sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample); 0177 painter->setFont(sampleFont); 0178 QPen oldPen = painter->pen(); 0179 painter->setPen(sampleBrush.color()); 0180 painter->drawText(r, Qt::AlignTop | Qt::AlignLeading | Qt::TextSingleLine, sample); 0181 painter->setFont(oldPainterFont); 0182 painter->setPen(oldPen); 0183 0184 if (option.state & QStyle::State_Selected) { 0185 painter->restore(); 0186 } 0187 } 0188 0189 QSize KFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option, 0190 const QModelIndex &index) const 0191 { 0192 Q_UNUSED(option); 0193 0194 QFont baseFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 0195 QString trFontFamily = index.data(Qt::DisplayRole).toString(); 0196 QString fontFamily = fontFamilyTrMap[trFontFamily]; 0197 0198 QFont familyFont = baseFont; 0199 familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily); 0200 QFontMetrics familyMetrics(familyFont); 0201 0202 QFont sampleFont = baseFont; 0203 sampleFont.setFamily(fontFamily); 0204 sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample); 0205 QFontMetrics sampleMetrics(sampleFont); 0206 QString sample = alphabetSample(); 0207 0208 // Only the hight matters here, the width is mandated by KFontComboBox::event() 0209 return QSize(qMax(familyMetrics.width(trFontFamily), sampleMetrics.width(sample)), 0210 qRound(familyMetrics.lineSpacing() + sampleMetrics.lineSpacing() * 1.2)); 0211 } 0212 0213 class KFontComboBoxPrivate 0214 { 0215 public: 0216 KFontComboBoxPrivate(KFontComboBox *parent); 0217 void updateDatabase(); 0218 void updateIndexToFont(); 0219 void _k_currentFontChanged(int index); 0220 0221 KFontComboBox *k; 0222 QFont currentFont; 0223 bool onlyFixed; 0224 bool signalsAllowed; 0225 KFontFamilyDelegate *delegate; 0226 QStringListModel *model; 0227 QStringList fontList; 0228 }; 0229 0230 KFontComboBoxPrivate::KFontComboBoxPrivate(KFontComboBox *parent) 0231 : k(parent), 0232 currentFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)), 0233 onlyFixed(false), 0234 signalsAllowed(true) 0235 { 0236 } 0237 0238 void KFontComboBoxPrivate::updateDatabase() 0239 { 0240 QStringList fontFamilies = fontList; 0241 if (fontList.isEmpty()) { 0242 KFontChooser::getFontList(fontFamilies, 0243 onlyFixed ? KFontChooser::FixedWidthFonts : 0); 0244 } 0245 0246 // Translate font families for the list model. 0247 delegate->fontFamilyTrMap.clear(); 0248 QStringList trFontFamilies = 0249 translateFontNameList(fontFamilies, &(delegate->fontFamilyTrMap)); 0250 0251 // Add families to the list model and completion. 0252 model->setStringList(trFontFamilies); 0253 KCompletion *completion = k->completionObject(); 0254 if (completion) { 0255 completion->setItems(trFontFamilies); 0256 completion->setIgnoreCase(true); 0257 } 0258 } 0259 0260 void KFontComboBoxPrivate::updateIndexToFont() 0261 { 0262 // QFontInfo necessary to return the family with proper casing. 0263 QString selectedFontFamily = QFontInfo(currentFont).family(); 0264 QString trSelectedFontFamily = translateFontName(selectedFontFamily); 0265 const QStringList trFontFamilies = model->stringList(); 0266 if (trFontFamilies.isEmpty()) { 0267 return; 0268 } 0269 0270 // Match the font's family with an item in the list. 0271 int index = 0; 0272 foreach (const QString &trFontFamily, trFontFamilies) { 0273 if (trSelectedFontFamily == trFontFamily) { 0274 break; 0275 } 0276 ++index; 0277 } 0278 if (index == trFontFamilies.count()) { 0279 // If no family matched, change font to first on the list. 0280 index = 0; 0281 currentFont = QFont(delegate->fontFamilyTrMap[trFontFamilies[0]]); 0282 emit k->currentFontChanged(currentFont); 0283 } 0284 0285 // Set the new list item. 0286 signalsAllowed = false; 0287 k->setCurrentIndex(index); 0288 signalsAllowed = true; 0289 } 0290 0291 void KFontComboBoxPrivate::_k_currentFontChanged(int index) 0292 { 0293 if (!signalsAllowed) { 0294 return; 0295 } 0296 0297 QString trFontFamily = k->itemText(index); 0298 QString fontFamily = delegate->fontFamilyTrMap[trFontFamily]; 0299 if (!fontFamily.isEmpty()) { 0300 currentFont = QFont(fontFamily); 0301 emit k->currentFontChanged(currentFont); 0302 } else { 0303 // Unknown font family given. Just remove from the list. 0304 // This should not happen, as adding arbitrary font names is prevented. 0305 QStringList lst = model->stringList(); 0306 lst.removeAll(trFontFamily); 0307 model->setStringList(lst); 0308 } 0309 } 0310 0311 KFontComboBox::KFontComboBox(QWidget *parent) 0312 : KComboBox(true, parent), d(new KFontComboBoxPrivate(this)) 0313 { 0314 // Inputing arbitrary font names does not make sense. 0315 setInsertPolicy(QComboBox::NoInsert); 0316 0317 // Special list item painter showing font previews and its list model. 0318 d->delegate = new KFontFamilyDelegate(this); 0319 setItemDelegate(d->delegate); 0320 d->model = new QStringListModel(this); 0321 setModel(d->model); 0322 0323 // Set current font when a new family has been chosen in the combo. 0324 connect(this, SIGNAL(currentIndexChanged(int)), 0325 this, SLOT(_k_currentFontChanged(int))); 0326 0327 // Initialize font selection and list of available fonts. 0328 d->updateDatabase(); 0329 d->updateIndexToFont(); 0330 } 0331 0332 KFontComboBox::~KFontComboBox() 0333 { 0334 delete d; 0335 } 0336 0337 void KFontComboBox::setOnlyFixed(bool onlyFixed) 0338 { 0339 if (onlyFixed != d->onlyFixed) { 0340 d->onlyFixed = onlyFixed; 0341 d->updateDatabase(); 0342 } 0343 } 0344 0345 void KFontComboBox::setFontList(const QStringList &fontList) 0346 { 0347 if (fontList != d->fontList) { 0348 d->fontList = fontList; 0349 d->updateDatabase(); 0350 } 0351 } 0352 0353 QFont KFontComboBox::currentFont() const 0354 { 0355 return d->currentFont; 0356 } 0357 0358 void KFontComboBox::setCurrentFont(const QFont &font) 0359 { 0360 if (font != d->currentFont) { 0361 d->currentFont = font; 0362 emit currentFontChanged(d->currentFont); 0363 d->updateIndexToFont(); 0364 } 0365 } 0366 0367 bool KFontComboBox::event(QEvent *e) 0368 { 0369 if (e->type() == QEvent::Resize) { 0370 QListView *lview = qobject_cast<QListView *>(view()); 0371 if (lview) { 0372 QString sample = alphabetSample(); 0373 // Limit text sample length to avoid too wide list view. 0374 if (sample.length() > 60) { 0375 sample = sample.left(57) + "..."; 0376 } 0377 QFont approxFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); 0378 approxFont.setPointSizeF(approxFont.pointSizeF() 0379 * d->delegate->sizeFactSample); 0380 int widgetWidth = width(); 0381 int sampleWidth = QFontMetrics(approxFont).width(sample); 0382 sampleWidth = qRound(sampleWidth * 1.1); // extra for wider fonts 0383 int iconWidth = d->delegate->truetype.actualSize(size()).width(); 0384 int vsbarWidth = 0; 0385 if (lview->verticalScrollBar()) { 0386 vsbarWidth = lview->verticalScrollBar()->width(); 0387 } 0388 lview->window()->setFixedWidth(qMax(widgetWidth, sampleWidth) 0389 + iconWidth + vsbarWidth); 0390 } 0391 } 0392 return KComboBox::event(e); 0393 } 0394 0395 QSize KFontComboBox::sizeHint() const 0396 { 0397 QSize sz = KComboBox::sizeHint(); 0398 QFontMetrics fm(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); 0399 sz.setWidth(fm.width("m") * 14); 0400 return sz; 0401 } 0402 0403 #include "fonthelpers.cpp" 0404 0405 #include "kfontcombobox.moc" 0406 #include "moc_kfontcombobox.cpp"