1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qfontcombobox.h"
41
42#include <qstringlistmodel.h>
43#include <qitemdelegate.h>
44#include <qlistview.h>
45#include <qpainter.h>
46#include <qevent.h>
47#include <qapplication.h>
48#include <private/qcombobox_p.h>
49#include <QDesktopWidget>
50#include <private/qdesktopwidget_p.h>
51#include <qdebug.h>
52
53QT_BEGIN_NAMESPACE
54
55static QFontDatabase::WritingSystem writingSystemFromScript(QLocale::Script script)
56{
57 switch (script) {
58 case QLocale::ArabicScript:
59 return QFontDatabase::Arabic;
60 case QLocale::CyrillicScript:
61 return QFontDatabase::Cyrillic;
62 case QLocale::GurmukhiScript:
63 return QFontDatabase::Gurmukhi;
64 case QLocale::SimplifiedHanScript:
65 return QFontDatabase::SimplifiedChinese;
66 case QLocale::TraditionalHanScript:
67 return QFontDatabase::TraditionalChinese;
68 case QLocale::LatinScript:
69 return QFontDatabase::Latin;
70 case QLocale::ArmenianScript:
71 return QFontDatabase::Armenian;
72 case QLocale::BengaliScript:
73 return QFontDatabase::Bengali;
74 case QLocale::DevanagariScript:
75 return QFontDatabase::Devanagari;
76 case QLocale::GeorgianScript:
77 return QFontDatabase::Georgian;
78 case QLocale::GreekScript:
79 return QFontDatabase::Greek;
80 case QLocale::GujaratiScript:
81 return QFontDatabase::Gujarati;
82 case QLocale::HebrewScript:
83 return QFontDatabase::Hebrew;
84 case QLocale::JapaneseScript:
85 return QFontDatabase::Japanese;
86 case QLocale::KhmerScript:
87 return QFontDatabase::Khmer;
88 case QLocale::KannadaScript:
89 return QFontDatabase::Kannada;
90 case QLocale::KoreanScript:
91 return QFontDatabase::Korean;
92 case QLocale::LaoScript:
93 return QFontDatabase::Lao;
94 case QLocale::MalayalamScript:
95 return QFontDatabase::Malayalam;
96 case QLocale::MyanmarScript:
97 return QFontDatabase::Myanmar;
98 case QLocale::TamilScript:
99 return QFontDatabase::Tamil;
100 case QLocale::TeluguScript:
101 return QFontDatabase::Telugu;
102 case QLocale::ThaanaScript:
103 return QFontDatabase::Thaana;
104 case QLocale::ThaiScript:
105 return QFontDatabase::Thai;
106 case QLocale::TibetanScript:
107 return QFontDatabase::Tibetan;
108 case QLocale::SinhalaScript:
109 return QFontDatabase::Sinhala;
110 case QLocale::SyriacScript:
111 return QFontDatabase::Syriac;
112 case QLocale::OriyaScript:
113 return QFontDatabase::Oriya;
114 case QLocale::OghamScript:
115 return QFontDatabase::Ogham;
116 case QLocale::RunicScript:
117 return QFontDatabase::Runic;
118 case QLocale::NkoScript:
119 return QFontDatabase::Nko;
120 default:
121 return QFontDatabase::Any;
122 }
123}
124
125static QFontDatabase::WritingSystem writingSystemFromLocale()
126{
127 QStringList uiLanguages = QLocale::system().uiLanguages();
128 QLocale::Script script;
129 if (!uiLanguages.isEmpty())
130 script = QLocale(uiLanguages.at(i: 0)).script();
131 else
132 script = QLocale::system().script();
133
134 return writingSystemFromScript(script);
135}
136
137static QFontDatabase::WritingSystem writingSystemForFont(const QFont &font, bool *hasLatin)
138{
139 QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase().writingSystems(family: font.family());
140// qDebug() << font.family() << writingSystems;
141
142 // this just confuses the algorithm below. Vietnamese is Latin with lots of special chars
143 writingSystems.removeOne(t: QFontDatabase::Vietnamese);
144 *hasLatin = writingSystems.removeOne(t: QFontDatabase::Latin);
145
146 if (writingSystems.isEmpty())
147 return QFontDatabase::Any;
148
149 QFontDatabase::WritingSystem system = writingSystemFromLocale();
150
151 if (writingSystems.contains(t: system))
152 return system;
153
154 if (system == QFontDatabase::TraditionalChinese
155 && writingSystems.contains(t: QFontDatabase::SimplifiedChinese)) {
156 return QFontDatabase::SimplifiedChinese;
157 }
158
159 if (system == QFontDatabase::SimplifiedChinese
160 && writingSystems.contains(t: QFontDatabase::TraditionalChinese)) {
161 return QFontDatabase::TraditionalChinese;
162 }
163
164 system = writingSystems.constLast();
165
166 if (!*hasLatin) {
167 // we need to show something
168 return system;
169 }
170
171 if (writingSystems.count() == 1 && system > QFontDatabase::Cyrillic)
172 return system;
173
174 if (writingSystems.count() <= 2 && system > QFontDatabase::Armenian && system < QFontDatabase::Vietnamese)
175 return system;
176
177 if (writingSystems.count() <= 5 && system >= QFontDatabase::SimplifiedChinese && system <= QFontDatabase::Korean)
178 return system;
179
180 return QFontDatabase::Any;
181}
182
183class QFontFamilyDelegate : public QAbstractItemDelegate
184{
185 Q_OBJECT
186public:
187 explicit QFontFamilyDelegate(QObject *parent);
188
189 // painting
190 void paint(QPainter *painter,
191 const QStyleOptionViewItem &option,
192 const QModelIndex &index) const override;
193
194 QSize sizeHint(const QStyleOptionViewItem &option,
195 const QModelIndex &index) const override;
196
197 const QIcon truetype;
198 const QIcon bitmap;
199 QFontDatabase::WritingSystem writingSystem;
200};
201
202QFontFamilyDelegate::QFontFamilyDelegate(QObject *parent)
203 : QAbstractItemDelegate(parent),
204 truetype(QStringLiteral(":/qt-project.org/styles/commonstyle/images/fonttruetype-16.png")),
205 bitmap(QStringLiteral(":/qt-project.org/styles/commonstyle/images/fontbitmap-16.png")),
206 writingSystem(QFontDatabase::Any)
207{
208}
209
210void QFontFamilyDelegate::paint(QPainter *painter,
211 const QStyleOptionViewItem &option,
212 const QModelIndex &index) const
213{
214 QString text = index.data(arole: Qt::DisplayRole).toString();
215 QFont font(option.font);
216 font.setPointSize(QFontInfo(font).pointSize() * 3 / 2);
217 QFont font2 = font;
218 font2.setFamily(text);
219
220 bool hasLatin;
221 QFontDatabase::WritingSystem system = writingSystemForFont(font: font2, hasLatin: &hasLatin);
222 if (hasLatin)
223 font = font2;
224
225 QRect r = option.rect;
226
227 if (option.state & QStyle::State_Selected) {
228 painter->save();
229 painter->setBrush(option.palette.highlight());
230 painter->setPen(Qt::NoPen);
231 painter->drawRect(r: option.rect);
232 painter->setPen(QPen(option.palette.highlightedText(), 0));
233 }
234
235 const QIcon *icon = &bitmap;
236 if (QFontDatabase().isSmoothlyScalable(family: text)) {
237 icon = &truetype;
238 }
239 const QSize actualSize = icon->actualSize(size: r.size());
240 const QRect iconRect = QStyle::alignedRect(direction: option.direction, alignment: option.displayAlignment,
241 size: actualSize, rectangle: r);
242 icon->paint(painter, rect: iconRect, alignment: Qt::AlignLeft|Qt::AlignVCenter);
243 if (option.direction == Qt::RightToLeft)
244 r.setRight(r.right() - actualSize.width() - 4);
245 else
246 r.setLeft(r.left() + actualSize.width() + 4);
247
248 QFont old = painter->font();
249 painter->setFont(font);
250
251 const Qt::Alignment textAlign = QStyle::visualAlignment(direction: option.direction, alignment: option.displayAlignment);
252 // If the ascent of the font is larger than the height of the rect,
253 // we will clip the text, so it's better to align the tight bounding rect in this case
254 // This is specifically for fonts where the ascent is very large compared to
255 // the descent, like certain of the Stix family.
256 QFontMetricsF fontMetrics(font);
257 if (fontMetrics.ascent() > r.height()) {
258 QRectF tbr = fontMetrics.tightBoundingRect(text);
259 QRect textRect(r);
260 textRect.setHeight(textRect.height() + (r.height() - tbr.height()));
261 painter->drawText(r: textRect, flags: Qt::AlignBottom|Qt::TextSingleLine|textAlign, text);
262 } else {
263 painter->drawText(r, flags: Qt::AlignVCenter|Qt::TextSingleLine|textAlign, text);
264 }
265
266 if (writingSystem != QFontDatabase::Any)
267 system = writingSystem;
268
269 if (system != QFontDatabase::Any) {
270 int w = painter->fontMetrics().horizontalAdvance(text + QLatin1String(" "));
271 painter->setFont(font2);
272 QString sample = QFontDatabase().writingSystemSample(writingSystem: system);
273 if (option.direction == Qt::RightToLeft)
274 r.setRight(r.right() - w);
275 else
276 r.setLeft(r.left() + w);
277 painter->drawText(r, flags: Qt::AlignVCenter|Qt::TextSingleLine|textAlign, text: sample);
278 }
279 painter->setFont(old);
280
281 if (option.state & QStyle::State_Selected)
282 painter->restore();
283
284}
285
286QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option,
287 const QModelIndex &index) const
288{
289 QString text = index.data(arole: Qt::DisplayRole).toString();
290 QFont font(option.font);
291// font.setFamily(text);
292 font.setPointSize(QFontInfo(font).pointSize() * 3/2);
293 QFontMetrics fontMetrics(font);
294 return QSize(fontMetrics.horizontalAdvance(text), fontMetrics.height());
295}
296
297
298class QFontComboBoxPrivate : public QComboBoxPrivate
299{
300public:
301 inline QFontComboBoxPrivate() { filters = QFontComboBox::AllFonts; }
302
303 QFontComboBox::FontFilters filters;
304 QFont currentFont;
305
306 void _q_updateModel();
307 void _q_currentChanged(const QString &);
308
309 Q_DECLARE_PUBLIC(QFontComboBox)
310};
311
312
313void QFontComboBoxPrivate::_q_updateModel()
314{
315 Q_Q(QFontComboBox);
316
317 if (QCoreApplication::closingDown())
318 return;
319
320 const int scalableMask = (QFontComboBox::ScalableFonts | QFontComboBox::NonScalableFonts);
321 const int spacingMask = (QFontComboBox::ProportionalFonts | QFontComboBox::MonospacedFonts);
322
323 QStringListModel *m = qobject_cast<QStringListModel *>(object: q->model());
324 if (!m)
325 return;
326 QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(object: q->view()->itemDelegate());
327 QFontDatabase::WritingSystem system = delegate ? delegate->writingSystem : QFontDatabase::Any;
328
329 QFontDatabase fdb;
330 QStringList list = fdb.families(writingSystem: system);
331 QStringList result;
332
333 int offset = 0;
334 QFontInfo fi(currentFont);
335
336 for (int i = 0; i < list.size(); ++i) {
337 if (fdb.isPrivateFamily(family: list.at(i)))
338 continue;
339
340 if ((filters & scalableMask) && (filters & scalableMask) != scalableMask) {
341 if (bool(filters & QFontComboBox::ScalableFonts) != fdb.isSmoothlyScalable(family: list.at(i)))
342 continue;
343 }
344 if ((filters & spacingMask) && (filters & spacingMask) != spacingMask) {
345 if (bool(filters & QFontComboBox::MonospacedFonts) != fdb.isFixedPitch(family: list.at(i)))
346 continue;
347 }
348 result += list.at(i);
349 if (list.at(i) == fi.family() || list.at(i).startsWith(s: fi.family() + QLatin1String(" [")))
350 offset = result.count() - 1;
351 }
352 list = result;
353
354 //we need to block the signals so that the model doesn't emit reset
355 //this prevents the current index from changing
356 //it will be updated just after this
357 ///TODO: we should finda way to avoid blocking signals and have a real update of the model
358 {
359 const QSignalBlocker blocker(m);
360 m->setStringList(list);
361 }
362
363 if (list.isEmpty()) {
364 if (currentFont != QFont()) {
365 currentFont = QFont();
366 emit q->currentFontChanged(f: currentFont);
367 }
368 } else {
369 q->setCurrentIndex(offset);
370 }
371}
372
373
374void QFontComboBoxPrivate::_q_currentChanged(const QString &text)
375{
376 Q_Q(QFontComboBox);
377 if (currentFont.family() != text) {
378 currentFont.setFamily(text);
379 emit q->currentFontChanged(f: currentFont);
380 }
381}
382
383/*!
384 \class QFontComboBox
385 \brief The QFontComboBox widget is a combobox that lets the user
386 select a font family.
387
388 \since 4.2
389 \ingroup basicwidgets
390 \inmodule QtWidgets
391
392 The combobox is populated with an alphabetized list of font
393 family names, such as Arial, Helvetica, and Times New Roman.
394 Family names are displayed using the actual font when possible.
395 For fonts such as Symbol, where the name is not representable in
396 the font itself, a sample of the font is displayed next to the
397 family name.
398
399 QFontComboBox is often used in toolbars, in conjunction with a
400 QComboBox for controlling the font size and two \l{QToolButton}s
401 for bold and italic.
402
403 When the user selects a new font, the currentFontChanged() signal
404 is emitted in addition to currentIndexChanged().
405
406 Call setWritingSystem() to tell QFontComboBox to show only fonts
407 that support a given writing system, and setFontFilters() to
408 filter out certain types of fonts as e.g. non scalable fonts or
409 monospaced fonts.
410
411 \image windowsvista-fontcombobox.png Screenshot of QFontComboBox on Windows Vista
412
413 \sa QComboBox, QFont, QFontInfo, QFontMetrics, QFontDatabase, {Character Map Example}
414*/
415
416/*!
417 Constructs a font combobox with the given \a parent.
418*/
419QFontComboBox::QFontComboBox(QWidget *parent)
420 : QComboBox(*new QFontComboBoxPrivate, parent)
421{
422 Q_D(QFontComboBox);
423 d->currentFont = font();
424 setEditable(true);
425
426 QStringListModel *m = new QStringListModel(this);
427 setModel(m);
428 setItemDelegate(new QFontFamilyDelegate(this));
429 QListView *lview = qobject_cast<QListView*>(object: view());
430 if (lview)
431 lview->setUniformItemSizes(true);
432 setWritingSystem(QFontDatabase::Any);
433
434 connect(sender: this, SIGNAL(currentIndexChanged(QString)),
435 receiver: this, SLOT(_q_currentChanged(QString)));
436
437 connect(qApp, SIGNAL(fontDatabaseChanged()),
438 receiver: this, SLOT(_q_updateModel()));
439}
440
441
442/*!
443 Destroys the combobox.
444*/
445QFontComboBox::~QFontComboBox()
446{
447}
448
449/*!
450 \property QFontComboBox::writingSystem
451 \brief the writing system that serves as a filter for the combobox
452
453 If \a script is QFontDatabase::Any (the default), all fonts are
454 listed.
455
456 \sa fontFilters
457*/
458
459void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
460{
461 Q_D(QFontComboBox);
462 QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(object: view()->itemDelegate());
463 if (delegate)
464 delegate->writingSystem = script;
465 d->_q_updateModel();
466}
467
468QFontDatabase::WritingSystem QFontComboBox::writingSystem() const
469{
470 QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(object: view()->itemDelegate());
471 if (delegate)
472 return delegate->writingSystem;
473 return QFontDatabase::Any;
474}
475
476
477/*!
478 \enum QFontComboBox::FontFilter
479
480 This enum can be used to only show certain types of fonts in the font combo box.
481
482 \value AllFonts Show all fonts
483 \value ScalableFonts Show scalable fonts
484 \value NonScalableFonts Show non scalable fonts
485 \value MonospacedFonts Show monospaced fonts
486 \value ProportionalFonts Show proportional fonts
487*/
488
489/*!
490 \property QFontComboBox::fontFilters
491 \brief the filter for the combobox
492
493 By default, all fonts are listed.
494
495 \sa writingSystem
496*/
497void QFontComboBox::setFontFilters(FontFilters filters)
498{
499 Q_D(QFontComboBox);
500 d->filters = filters;
501 d->_q_updateModel();
502}
503
504QFontComboBox::FontFilters QFontComboBox::fontFilters() const
505{
506 Q_D(const QFontComboBox);
507 return d->filters;
508}
509
510/*!
511 \property QFontComboBox::currentFont
512 \brief the currently selected font
513
514 \sa currentIndex, currentText
515*/
516QFont QFontComboBox::currentFont() const
517{
518 Q_D(const QFontComboBox);
519 return d->currentFont;
520}
521
522void QFontComboBox::setCurrentFont(const QFont &font)
523{
524 Q_D(QFontComboBox);
525 if (font != d->currentFont) {
526 d->currentFont = font;
527 d->_q_updateModel();
528 if (d->currentFont == font) { //else the signal has already be emitted by _q_updateModel
529 emit currentFontChanged(f: d->currentFont);
530 }
531 }
532}
533
534/*!
535 \fn void QFontComboBox::currentFontChanged(const QFont &font)
536
537 This signal is emitted whenever the current font changes, with
538 the new \a font.
539
540 \sa currentFont
541*/
542
543/*!
544 \reimp
545*/
546bool QFontComboBox::event(QEvent *e)
547{
548 if (e->type() == QEvent::Resize) {
549 QListView *lview = qobject_cast<QListView*>(object: view());
550 if (lview) {
551 lview->window()->setFixedWidth(qMin(a: width() * 5 / 3,
552 b: QDesktopWidgetPrivate::availableGeometry(widget: lview).width()));
553 }
554 }
555 return QComboBox::event(event: e);
556}
557
558/*!
559 \reimp
560*/
561QSize QFontComboBox::sizeHint() const
562{
563 QSize sz = QComboBox::sizeHint();
564 QFontMetrics fm(font());
565 sz.setWidth(fm.horizontalAdvance(QLatin1Char('m'))*14);
566 return sz;
567}
568
569QT_END_NAMESPACE
570
571#include "qfontcombobox.moc"
572#include "moc_qfontcombobox.cpp"
573

source code of qtbase/src/widgets/widgets/qfontcombobox.cpp