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 "qwindowdefs.h"
41 #include "qfontdialog.h"
42 
43 #include "qfontdialog_p.h"
44 
45 #include <qapplication.h>
46 #include <qcheckbox.h>
47 #include <qcombobox.h>
48 #include <qevent.h>
49 #include <qfontdatabase.h>
50 #include <qgroupbox.h>
51 #include <qlabel.h>
52 #include <qlayout.h>
53 #include <qlineedit.h>
54 #include <qpushbutton.h>
55 #include <qstyle.h>
56 #include <qdialogbuttonbox.h>
57 #include <qheaderview.h>
58 #include <qlistview.h>
59 #include <qstringlistmodel.h>
60 #include <qvalidator.h>
61 #include <private/qdialog_p.h>
62 #include <private/qfont_p.h>
63 
64 QT_BEGIN_NAMESPACE
65 
66 class QFontListView : public QListView
67 {
68     Q_OBJECT
69 public:
70     QFontListView(QWidget *parent);
model() const71     inline QStringListModel *model() const {
72         return static_cast<QStringListModel *>(QListView::model());
73     }
setCurrentItem(int item)74     inline void setCurrentItem(int item) {
75         QListView::setCurrentIndex(static_cast<QAbstractListModel*>(model())->index(item));
76     }
currentItem() const77     inline int currentItem() const {
78         return QListView::currentIndex().row();
79     }
count() const80     inline int count() const {
81         return model()->rowCount();
82     }
currentText() const83     inline QString currentText() const {
84         int row = QListView::currentIndex().row();
85         return row < 0 ? QString() : model()->stringList().at(row);
86     }
currentChanged(const QModelIndex & current,const QModelIndex & previous)87     void currentChanged(const QModelIndex &current, const QModelIndex &previous) override {
88         QListView::currentChanged(current, previous);
89         if (current.isValid())
90             emit highlighted(current.row());
91     }
text(int i) const92     QString text(int i) const {
93         return model()->stringList().at(i);
94     }
95 signals:
96     void highlighted(int);
97 };
98 
QFontListView(QWidget * parent)99 QFontListView::QFontListView(QWidget *parent)
100     : QListView(parent)
101 {
102     setModel(new QStringListModel(parent));
103     setEditTriggers(NoEditTriggers);
104 }
105 
106 static const Qt::WindowFlags DefaultWindowFlags =
107         Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
108 
QFontDialogPrivate()109 QFontDialogPrivate::QFontDialogPrivate()
110     : writingSystem(QFontDatabase::Any),
111       options(QFontDialogOptions::create())
112 {
113 }
114 
~QFontDialogPrivate()115 QFontDialogPrivate::~QFontDialogPrivate()
116 {
117 }
118 
119 /*!
120   \class QFontDialog
121   \ingroup standard-dialogs
122   \inmodule QtWidgets
123 
124   \brief The QFontDialog class provides a dialog widget for selecting a font.
125 
126     A font dialog is created through one of the static getFont()
127     functions.
128 
129   Examples:
130 
131   \snippet code/src_gui_dialogs_qfontdialog.cpp 0
132 
133     The dialog can also be used to set a widget's font directly:
134   \snippet code/src_gui_dialogs_qfontdialog.cpp 1
135   If the user clicks OK the font they chose will be used for myWidget,
136   and if they click Cancel the original font is used.
137 
138   \image fusion-fontdialog.png A font dialog in the Fusion widget style.
139 
140   \sa QFont, QFontInfo, QFontMetrics, QColorDialog, QFileDialog,
141       {Standard Dialogs Example}
142 */
143 
144 /*!
145     \since 4.5
146 
147     Constructs a standard font dialog.
148 
149     Use setCurrentFont() to set the initial font attributes.
150 
151     The \a parent parameter is passed to the QDialog constructor.
152 
153     \sa getFont()
154 */
QFontDialog(QWidget * parent)155 QFontDialog::QFontDialog(QWidget *parent)
156     : QDialog(*new QFontDialogPrivate, parent, DefaultWindowFlags)
157 {
158     Q_D(QFontDialog);
159     d->init();
160 }
161 
162 /*!
163     \since 4.5
164 
165     Constructs a standard font dialog with the given \a parent and specified
166     \a initial font.
167 */
QFontDialog(const QFont & initial,QWidget * parent)168 QFontDialog::QFontDialog(const QFont &initial, QWidget *parent)
169     : QFontDialog(parent)
170 {
171     setCurrentFont(initial);
172 }
173 
init()174 void QFontDialogPrivate::init()
175 {
176     Q_Q(QFontDialog);
177 
178     q->setSizeGripEnabled(true);
179     q->setWindowTitle(QFontDialog::tr("Select Font"));
180 
181     // grid
182     familyEdit = new QLineEdit(q);
183     familyEdit->setReadOnly(true);
184     familyList = new QFontListView(q);
185     familyEdit->setFocusProxy(familyList);
186 
187     familyAccel = new QLabel(q);
188 #ifndef QT_NO_SHORTCUT
189     familyAccel->setBuddy(familyList);
190 #endif
191     familyAccel->setIndent(2);
192 
193     styleEdit = new QLineEdit(q);
194     styleEdit->setReadOnly(true);
195     styleList = new QFontListView(q);
196     styleEdit->setFocusProxy(styleList);
197 
198     styleAccel = new QLabel(q);
199 #ifndef QT_NO_SHORTCUT
200     styleAccel->setBuddy(styleList);
201 #endif
202     styleAccel->setIndent(2);
203 
204     sizeEdit = new QLineEdit(q);
205     sizeEdit->setFocusPolicy(Qt::ClickFocus);
206     QIntValidator *validator = new QIntValidator(1, 512, q);
207     sizeEdit->setValidator(validator);
208     sizeList = new QFontListView(q);
209 
210     sizeAccel = new QLabel(q);
211 #ifndef QT_NO_SHORTCUT
212     sizeAccel->setBuddy(sizeEdit);
213 #endif
214     sizeAccel->setIndent(2);
215 
216     // effects box
217     effects = new QGroupBox(q);
218     QVBoxLayout *vbox = new QVBoxLayout(effects);
219     strikeout = new QCheckBox(effects);
220     vbox->addWidget(strikeout);
221     underline = new QCheckBox(effects);
222     vbox->addWidget(underline);
223 
224     sample = new QGroupBox(q);
225     QHBoxLayout *hbox = new QHBoxLayout(sample);
226     sampleEdit = new QLineEdit(sample);
227     sampleEdit->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
228     sampleEdit->setAlignment(Qt::AlignCenter);
229     // Note that the sample text is *not* translated with tr(), as the
230     // characters used depend on the charset encoding.
231     sampleEdit->setText(QLatin1String("AaBbYyZz"));
232     hbox->addWidget(sampleEdit);
233 
234     writingSystemCombo = new QComboBox(q);
235 
236     writingSystemAccel = new QLabel(q);
237 #ifndef QT_NO_SHORTCUT
238     writingSystemAccel->setBuddy(writingSystemCombo);
239 #endif
240     writingSystemAccel->setIndent(2);
241 
242     size = 0;
243     smoothScalable = false;
244 
245     QObject::connect(writingSystemCombo, SIGNAL(activated(int)), q, SLOT(_q_writingSystemHighlighted(int)));
246     QObject::connect(familyList, SIGNAL(highlighted(int)), q, SLOT(_q_familyHighlighted(int)));
247     QObject::connect(styleList, SIGNAL(highlighted(int)), q, SLOT(_q_styleHighlighted(int)));
248     QObject::connect(sizeList, SIGNAL(highlighted(int)), q, SLOT(_q_sizeHighlighted(int)));
249     QObject::connect(sizeEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_sizeChanged(QString)));
250 
251     QObject::connect(strikeout, SIGNAL(clicked()), q, SLOT(_q_updateSample()));
252     QObject::connect(underline, SIGNAL(clicked()), q, SLOT(_q_updateSample()));
253 
254     for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
255         QFontDatabase::WritingSystem ws = QFontDatabase::WritingSystem(i);
256         QString writingSystemName = QFontDatabase::writingSystemName(ws);
257         if (writingSystemName.isEmpty())
258             break;
259         writingSystemCombo->addItem(writingSystemName);
260     }
261 
262     updateFamilies();
263     if (familyList->count() != 0) {
264         familyList->setCurrentItem(0);
265         sizeList->setCurrentItem(0);
266     }
267 
268     // grid layout
269     QGridLayout *mainGrid = new QGridLayout(q);
270 
271     int spacing = mainGrid->spacing();
272     if (spacing >= 0) {     // uniform spacing
273        mainGrid->setSpacing(0);
274 
275        mainGrid->setColumnMinimumWidth(1, spacing);
276        mainGrid->setColumnMinimumWidth(3, spacing);
277 
278        int margin = 0;
279        mainGrid->getContentsMargins(nullptr, nullptr, nullptr, &margin);
280 
281        mainGrid->setRowMinimumHeight(3, margin);
282        mainGrid->setRowMinimumHeight(6, 2);
283        mainGrid->setRowMinimumHeight(8, margin);
284     }
285 
286     mainGrid->addWidget(familyAccel, 0, 0);
287     mainGrid->addWidget(familyEdit, 1, 0);
288     mainGrid->addWidget(familyList, 2, 0);
289 
290     mainGrid->addWidget(styleAccel, 0, 2);
291     mainGrid->addWidget(styleEdit, 1, 2);
292     mainGrid->addWidget(styleList, 2, 2);
293 
294     mainGrid->addWidget(sizeAccel, 0, 4);
295     mainGrid->addWidget(sizeEdit, 1, 4);
296     mainGrid->addWidget(sizeList, 2, 4);
297 
298     mainGrid->setColumnStretch(0, 38);
299     mainGrid->setColumnStretch(2, 24);
300     mainGrid->setColumnStretch(4, 10);
301 
302     mainGrid->addWidget(effects, 4, 0);
303 
304     mainGrid->addWidget(sample, 4, 2, 4, 3);
305 
306     mainGrid->addWidget(writingSystemAccel, 5, 0);
307     mainGrid->addWidget(writingSystemCombo, 7, 0);
308 
309     buttonBox = new QDialogButtonBox(q);
310     mainGrid->addWidget(buttonBox, 9, 0, 1, 5);
311 
312     QPushButton *button
313             = static_cast<QPushButton *>(buttonBox->addButton(QDialogButtonBox::Ok));
314     QObject::connect(buttonBox, SIGNAL(accepted()), q, SLOT(accept()));
315     button->setDefault(true);
316 
317     buttonBox->addButton(QDialogButtonBox::Cancel);
318     QObject::connect(buttonBox, SIGNAL(rejected()), q, SLOT(reject()));
319 
320     q->resize(500, 360);
321 
322     sizeEdit->installEventFilter(q);
323     familyList->installEventFilter(q);
324     styleList->installEventFilter(q);
325     sizeList->installEventFilter(q);
326 
327     familyList->setFocus();
328     retranslateStrings();
329     sampleEdit->setObjectName(QLatin1String("qt_fontDialog_sampleEdit"));
330 }
331 
332 /*!
333   \internal
334  Destroys the font dialog and frees up its storage.
335 */
336 
~QFontDialog()337 QFontDialog::~QFontDialog()
338 {
339 }
340 
341 /*!
342   Executes a modal font dialog and returns a font.
343 
344   If the user clicks \uicontrol OK, the selected font is returned. If the user
345   clicks \uicontrol Cancel, the \a initial font is returned.
346 
347   The dialog is constructed with the given \a parent and the options specified
348   in \a options. \a title is shown as the window title of the dialog and  \a
349   initial is the initially selected font. If the \a ok parameter is not-null,
350   the value it refers to is set to true if the user clicks \uicontrol OK, and set to
351   false if the user clicks \uicontrol Cancel.
352 
353   Examples:
354   \snippet code/src_gui_dialogs_qfontdialog.cpp 2
355 
356     The dialog can also be used to set a widget's font directly:
357   \snippet code/src_gui_dialogs_qfontdialog.cpp 3
358   In this example, if the user clicks OK the font they chose will be
359   used, and if they click Cancel the original font is used.
360 
361   \warning Do not delete \a parent during the execution of the dialog.
362            If you want to do this, you should create the dialog
363            yourself using one of the QFontDialog constructors.
364 */
getFont(bool * ok,const QFont & initial,QWidget * parent,const QString & title,FontDialogOptions options)365 QFont QFontDialog::getFont(bool *ok, const QFont &initial, QWidget *parent, const QString &title,
366                            FontDialogOptions options)
367 {
368     return QFontDialogPrivate::getFont(ok, initial, parent, title, options);
369 }
370 
371 /*!
372     \overload
373 
374   Executes a modal font dialog and returns a font.
375 
376   If the user clicks \uicontrol OK, the selected font is returned. If the user
377   clicks \uicontrol Cancel, the Qt default font is returned.
378 
379   The dialog is constructed with the given \a parent.
380   If the \a ok parameter is not-null, the value it refers to is set
381   to true if the user clicks \uicontrol OK, and false if the user clicks
382   \uicontrol Cancel.
383 
384   Example:
385   \snippet code/src_gui_dialogs_qfontdialog.cpp 4
386 
387   \warning Do not delete \a parent during the execution of the dialog.
388            If you want to do this, you should create the dialog
389            yourself using one of the QFontDialog constructors.
390 */
getFont(bool * ok,QWidget * parent)391 QFont QFontDialog::getFont(bool *ok, QWidget *parent)
392 {
393     QFont initial;
394     return QFontDialogPrivate::getFont(ok, initial, parent, QString(), { });
395 }
396 
getFont(bool * ok,const QFont & initial,QWidget * parent,const QString & title,QFontDialog::FontDialogOptions options)397 QFont QFontDialogPrivate::getFont(bool *ok, const QFont &initial, QWidget *parent,
398                                   const QString &title, QFontDialog::FontDialogOptions options)
399 {
400     QFontDialog dlg(parent);
401     dlg.setOptions(options);
402     dlg.setCurrentFont(initial);
403     if (!title.isEmpty())
404         dlg.setWindowTitle(title);
405 
406     int ret = (dlg.exec() || (options & QFontDialog::NoButtons));
407     if (ok)
408         *ok = !!ret;
409     if (ret) {
410         return dlg.selectedFont();
411     } else {
412         return initial;
413     }
414 }
415 
416 /*!
417     \internal
418     An event filter to make the Up, Down, PageUp and PageDown keys work
419     correctly in the line edits. The source of the event is the object
420     \a o and the event is \a e.
421 */
422 
eventFilter(QObject * o,QEvent * e)423 bool QFontDialog::eventFilter(QObject *o , QEvent *e)
424 {
425     Q_D(QFontDialog);
426     if (e->type() == QEvent::KeyPress) {
427         QKeyEvent *k = (QKeyEvent *)e;
428         if (o == d->sizeEdit &&
429         (k->key() == Qt::Key_Up ||
430              k->key() == Qt::Key_Down ||
431          k->key() == Qt::Key_PageUp ||
432          k->key() == Qt::Key_PageDown)) {
433 
434             int ci = d->sizeList->currentItem();
435             QCoreApplication::sendEvent(d->sizeList, k);
436 
437             if (ci != d->sizeList->currentItem()
438                     && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, this))
439                 d->sizeEdit->selectAll();
440             return true;
441         } else if ((o == d->familyList || o == d->styleList) &&
442                     (k->key() == Qt::Key_Return || k->key() == Qt::Key_Enter)) {
443             k->accept();
444         accept();
445             return true;
446         }
447     } else if (e->type() == QEvent::FocusIn
448                && style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, this)) {
449         if (o == d->familyList)
450             d->familyEdit->selectAll();
451         else if (o == d->styleList)
452             d->styleEdit->selectAll();
453         else if (o == d->sizeList)
454             d->sizeEdit->selectAll();
455     } else if (e->type() == QEvent::MouseButtonPress && o == d->sizeList) {
456             d->sizeEdit->setFocus();
457     }
458     return QDialog::eventFilter(o, e);
459 }
460 
initHelper(QPlatformDialogHelper * h)461 void QFontDialogPrivate::initHelper(QPlatformDialogHelper *h)
462 {
463     QFontDialog *d = q_func();
464     QObject::connect(h, SIGNAL(currentFontChanged(QFont)), d, SIGNAL(currentFontChanged(QFont)));
465     QObject::connect(h, SIGNAL(fontSelected(QFont)), d, SIGNAL(fontSelected(QFont)));
466     static_cast<QPlatformFontDialogHelper *>(h)->setOptions(options);
467 }
468 
helperPrepareShow(QPlatformDialogHelper *)469 void QFontDialogPrivate::helperPrepareShow(QPlatformDialogHelper *)
470 {
471     options->setWindowTitle(q_func()->windowTitle());
472 }
473 
474 /*
475     Updates the contents of the "font family" list box. This
476     function can be reimplemented if you have special requirements.
477 */
478 
updateFamilies()479 void QFontDialogPrivate::updateFamilies()
480 {
481     Q_Q(QFontDialog);
482 
483     enum match_t { MATCH_NONE = 0, MATCH_LAST_RESORT = 1, MATCH_APP = 2, MATCH_FAMILY = 3 };
484 
485     const QFontDialog::FontDialogOptions scalableMask = (QFontDialog::ScalableFonts | QFontDialog::NonScalableFonts);
486     const QFontDialog::FontDialogOptions spacingMask = (QFontDialog::ProportionalFonts | QFontDialog::MonospacedFonts);
487     const QFontDialog::FontDialogOptions options = q->options();
488 
489     QFontDatabase fdb;
490 
491     QStringList familyNames;
492     const auto families = fdb.families(writingSystem);
493     for (const QString &family : families) {
494         if (fdb.isPrivateFamily(family))
495             continue;
496 
497         if ((options & scalableMask) && (options & scalableMask) != scalableMask) {
498             if (bool(options & QFontDialog::ScalableFonts) != fdb.isSmoothlyScalable(family))
499                 continue;
500         }
501         if ((options & spacingMask) && (options & spacingMask) != spacingMask) {
502             if (bool(options & QFontDialog::MonospacedFonts) != fdb.isFixedPitch(family))
503                 continue;
504         }
505         familyNames << family;
506     }
507 
508     familyList->model()->setStringList(familyNames);
509 
510     QString foundryName1, familyName1, foundryName2, familyName2;
511     int bestFamilyMatch = -1;
512     match_t bestFamilyType = MATCH_NONE;
513 
514     QFont f;
515 
516     // ##### do the right thing for a list of family names in the font.
517     QFontDatabase::parseFontName(family, foundryName1, familyName1);
518 
519     QStringList::const_iterator it = familyNames.constBegin();
520     int i = 0;
521     for(; it != familyNames.constEnd(); ++it, ++i) {
522         QFontDatabase::parseFontName(*it, foundryName2, familyName2);
523 
524         //try to match...
525         if (familyName1 == familyName2) {
526             bestFamilyType = MATCH_FAMILY;
527             if (foundryName1 == foundryName2) {
528                 bestFamilyMatch = i;
529                 break;
530             }
531             if (bestFamilyMatch < MATCH_FAMILY)
532                 bestFamilyMatch = i;
533         }
534 
535         //and try some fall backs
536         match_t type = MATCH_NONE;
537         if (bestFamilyType <= MATCH_NONE && familyName2 == QStringLiteral("helvetica"))
538             type = MATCH_LAST_RESORT;
539         if (bestFamilyType <= MATCH_LAST_RESORT && familyName2 == f.family())
540             type = MATCH_APP;
541         // ### add fallback for writingSystem
542         if (type != MATCH_NONE) {
543             bestFamilyType = type;
544             bestFamilyMatch = i;
545         }
546     }
547 
548     if (i != -1 && bestFamilyType != MATCH_NONE)
549         familyList->setCurrentItem(bestFamilyMatch);
550     else
551         familyList->setCurrentItem(0);
552     familyEdit->setText(familyList->currentText());
553     if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
554             && familyList->hasFocus())
555         familyEdit->selectAll();
556 
557     updateStyles();
558 }
559 
560 /*
561     Updates the contents of the "font style" list box. This
562     function can be reimplemented if you have special requirements.
563 */
updateStyles()564 void QFontDialogPrivate::updateStyles()
565 {
566     Q_Q(QFontDialog);
567     QStringList styles = fdb.styles(familyList->currentText());
568     styleList->model()->setStringList(styles);
569 
570     if (styles.isEmpty()) {
571         styleEdit->clear();
572         smoothScalable = false;
573     } else {
574         if (!style.isEmpty()) {
575             bool found = false;
576             bool first = true;
577             QString cstyle = style;
578 
579         redo:
580             for (int i = 0; i < (int)styleList->count(); i++) {
581                 if (cstyle == styleList->text(i)) {
582                      styleList->setCurrentItem(i);
583                      found = true;
584                      break;
585                  }
586             }
587             if (!found && first) {
588                 if (cstyle.contains(QLatin1String("Italic"))) {
589                     cstyle.replace(QLatin1String("Italic"), QLatin1String("Oblique"));
590                     first = false;
591                     goto redo;
592                 } else if (cstyle.contains(QLatin1String("Oblique"))) {
593                     cstyle.replace(QLatin1String("Oblique"), QLatin1String("Italic"));
594                     first = false;
595                     goto redo;
596                 }
597             }
598             if (!found)
599                 styleList->setCurrentItem(0);
600         } else {
601             styleList->setCurrentItem(0);
602         }
603 
604         styleEdit->setText(styleList->currentText());
605         if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
606                 && styleList->hasFocus())
607             styleEdit->selectAll();
608 
609         smoothScalable = fdb.isSmoothlyScalable(familyList->currentText(), styleList->currentText());
610     }
611 
612     updateSizes();
613 }
614 
615 /*!
616     \internal
617     Updates the contents of the "font size" list box. This
618   function can be reimplemented if you have special requirements.
619 */
620 
updateSizes()621 void QFontDialogPrivate::updateSizes()
622 {
623     Q_Q(QFontDialog);
624 
625     if (!familyList->currentText().isEmpty()) {
626         QList<int> sizes = fdb.pointSizes(familyList->currentText(), styleList->currentText());
627 
628         int i = 0;
629         int current = -1;
630         QStringList str_sizes;
631         str_sizes.reserve(sizes.size());
632         for(QList<int>::const_iterator it = sizes.constBegin(); it != sizes.constEnd(); ++it) {
633             str_sizes.append(QString::number(*it));
634             if (current == -1 && *it == size)
635                 current = i;
636             ++i;
637         }
638         sizeList->model()->setStringList(str_sizes);
639         if (current != -1)
640             sizeList->setCurrentItem(current);
641 
642         const QSignalBlocker blocker(sizeEdit);
643         sizeEdit->setText((smoothScalable ? QString::number(size) : sizeList->currentText()));
644         if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
645                 && sizeList->hasFocus())
646             sizeEdit->selectAll();
647     } else {
648         sizeEdit->clear();
649     }
650 
651     _q_updateSample();
652 }
653 
_q_updateSample()654 void QFontDialogPrivate::_q_updateSample()
655 {
656     // compute new font
657     int pSize = sizeEdit->text().toInt();
658     QFont newFont(fdb.font(familyList->currentText(), style, pSize));
659     newFont.setStrikeOut(strikeout->isChecked());
660     newFont.setUnderline(underline->isChecked());
661 
662     if (familyList->currentText().isEmpty())
663         sampleEdit->clear();
664 
665     updateSampleFont(newFont);
666 }
667 
updateSampleFont(const QFont & newFont)668 void QFontDialogPrivate::updateSampleFont(const QFont &newFont)
669 {
670     Q_Q(QFontDialog);
671     if (newFont != sampleEdit->font()) {
672         sampleEdit->setFont(newFont);
673         emit q->currentFontChanged(newFont);
674     }
675 }
676 
677 /*!
678     \internal
679 */
_q_writingSystemHighlighted(int index)680 void QFontDialogPrivate::_q_writingSystemHighlighted(int index)
681 {
682     writingSystem = QFontDatabase::WritingSystem(index);
683     sampleEdit->setText(QFontDatabase::writingSystemSample(writingSystem));
684     updateFamilies();
685 }
686 
687 /*!
688     \internal
689 */
_q_familyHighlighted(int i)690 void QFontDialogPrivate::_q_familyHighlighted(int i)
691 {
692     Q_Q(QFontDialog);
693     family = familyList->text(i);
694     familyEdit->setText(family);
695     if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
696             && familyList->hasFocus())
697         familyEdit->selectAll();
698 
699     updateStyles();
700 }
701 
702 
703 /*!
704     \internal
705 */
706 
_q_styleHighlighted(int index)707 void QFontDialogPrivate::_q_styleHighlighted(int index)
708 {
709     Q_Q(QFontDialog);
710     QString s = styleList->text(index);
711     styleEdit->setText(s);
712     if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
713             && styleList->hasFocus())
714         styleEdit->selectAll();
715 
716     style = s;
717 
718     updateSizes();
719 }
720 
721 
722 /*!
723     \internal
724 */
725 
_q_sizeHighlighted(int index)726 void QFontDialogPrivate::_q_sizeHighlighted(int index)
727 {
728     Q_Q(QFontDialog);
729     QString s = sizeList->text(index);
730     sizeEdit->setText(s);
731     if (q->style()->styleHint(QStyle::SH_FontDialog_SelectAssociatedText, nullptr, q)
732             && sizeEdit->hasFocus())
733         sizeEdit->selectAll();
734 
735     size = s.toInt();
736     _q_updateSample();
737 }
738 
739 /*!
740     \internal
741     This slot is called if the user changes the font size.
742     The size is passed in the \a s argument as a \e string.
743 */
744 
_q_sizeChanged(const QString & s)745 void QFontDialogPrivate::_q_sizeChanged(const QString &s)
746 {
747     // no need to check if the conversion is valid, since we have an QIntValidator in the size edit
748     int size = s.toInt();
749     if (this->size == size)
750         return;
751 
752     this->size = size;
753     if (sizeList->count() != 0) {
754         int i;
755         for (i = 0; i < sizeList->count() - 1; i++) {
756             if (sizeList->text(i).toInt() >= this->size)
757                 break;
758         }
759         const QSignalBlocker blocker(sizeList);
760         if (sizeList->text(i).toInt() == this->size)
761             sizeList->setCurrentItem(i);
762         else
763             sizeList->clearSelection();
764     }
765     _q_updateSample();
766 }
767 
retranslateStrings()768 void QFontDialogPrivate::retranslateStrings()
769 {
770     familyAccel->setText(QFontDialog::tr("&Font"));
771     styleAccel->setText(QFontDialog::tr("Font st&yle"));
772     sizeAccel->setText(QFontDialog::tr("&Size"));
773     effects->setTitle(QFontDialog::tr("Effects"));
774     strikeout->setText(QFontDialog::tr("Stri&keout"));
775     underline->setText(QFontDialog::tr("&Underline"));
776     sample->setTitle(QFontDialog::tr("Sample"));
777     writingSystemAccel->setText(QFontDialog::tr("Wr&iting System"));
778 }
779 
780 /*!
781     \reimp
782 */
changeEvent(QEvent * e)783 void QFontDialog::changeEvent(QEvent *e)
784 {
785     Q_D(QFontDialog);
786     if (e->type() == QEvent::LanguageChange) {
787         d->retranslateStrings();
788     }
789     QDialog::changeEvent(e);
790 }
791 
792 /*!
793     \since 4.5
794 
795     \property QFontDialog::currentFont
796     \brief the current font of the dialog.
797 */
798 
799 /*!
800     \since 4.5
801 
802     Sets the font highlighted in the QFontDialog to the given \a font.
803 
804     \sa selectedFont()
805 */
setCurrentFont(const QFont & font)806 void QFontDialog::setCurrentFont(const QFont &font)
807 {
808     Q_D(QFontDialog);
809     d->family = font.family();
810     d->style = d->fdb.styleString(font);
811     d->size = font.pointSize();
812     if (d->size == -1) {
813         QFontInfo fi(font);
814         d->size = fi.pointSize();
815     }
816     d->strikeout->setChecked(font.strikeOut());
817     d->underline->setChecked(font.underline());
818     d->updateFamilies();
819     if (QPlatformFontDialogHelper *helper = d->platformFontDialogHelper())
820         helper->setCurrentFont(font);
821 }
822 
823 /*!
824     \since 4.5
825 
826     Returns the current font.
827 
828     \sa selectedFont()
829 */
currentFont() const830 QFont QFontDialog::currentFont() const
831 {
832     Q_D(const QFontDialog);
833     if (const QPlatformFontDialogHelper *helper = d->platformFontDialogHelper())
834         return helper->currentFont();
835     return d->sampleEdit->font();
836 }
837 
838 /*!
839     Returns the font that the user selected by clicking the \uicontrol{OK}
840     or equivalent button.
841 
842     \note This font is not always the same as the font held by the
843     \l currentFont property since the user can choose different fonts
844     before finally selecting the one to use.
845 */
selectedFont() const846 QFont QFontDialog::selectedFont() const
847 {
848     Q_D(const QFontDialog);
849     return d->selectedFont;
850 }
851 
852 /*!
853     \enum QFontDialog::FontDialogOption
854     \since 4.5
855 
856     This enum specifies various options that affect the look and feel
857     of a font dialog.
858 
859     For instance, it allows to specify which type of font should be
860     displayed. If none are specified all fonts available will be listed.
861 
862     Note that the font filtering options might not be supported on some
863     platforms (e.g. Mac). They are always supported by the non native
864     dialog (used on Windows or Linux).
865 
866     \value NoButtons Don't display \uicontrol{OK} and \uicontrol{Cancel} buttons. (Useful for "live dialogs".)
867     \value DontUseNativeDialog Use Qt's standard font dialog on the Mac instead of Apple's
868                                native font panel.
869     \value ScalableFonts Show scalable fonts
870     \value NonScalableFonts Show non scalable fonts
871     \value MonospacedFonts Show monospaced fonts
872     \value ProportionalFonts Show proportional fonts
873 
874     \sa options, setOption(), testOption()
875 */
876 
877 /*!
878     Sets the given \a option to be enabled if \a on is true;
879     otherwise, clears the given \a option.
880 
881     \sa options, testOption()
882 */
setOption(FontDialogOption option,bool on)883 void QFontDialog::setOption(FontDialogOption option, bool on)
884 {
885     const QFontDialog::FontDialogOptions previousOptions = options();
886     if (!(previousOptions & option) != !on)
887         setOptions(previousOptions ^ option);
888 }
889 
890 /*!
891     Returns \c true if the given \a option is enabled; otherwise, returns
892     false.
893 
894     \sa options, setOption()
895 */
testOption(FontDialogOption option) const896 bool QFontDialog::testOption(FontDialogOption option) const
897 {
898     Q_D(const QFontDialog);
899     return d->options->testOption(static_cast<QFontDialogOptions::FontDialogOption>(option));
900 }
901 
902 /*!
903     \property QFontDialog::options
904     \brief the various options that affect the look and feel of the dialog
905     \since 4.5
906 
907     By default, all options are disabled.
908 
909     Options should be set before showing the dialog. Setting them while the
910     dialog is visible is not guaranteed to have an immediate effect on the
911     dialog (depending on the option and on the platform).
912 
913     \sa setOption(), testOption()
914 */
setOptions(FontDialogOptions options)915 void QFontDialog::setOptions(FontDialogOptions options)
916 {
917     Q_D(QFontDialog);
918 
919     if (QFontDialog::options() == options)
920         return;
921 
922     d->options->setOptions(QFontDialogOptions::FontDialogOptions(int(options)));
923     d->buttonBox->setVisible(!(options & NoButtons));
924 }
925 
options() const926 QFontDialog::FontDialogOptions QFontDialog::options() const
927 {
928     Q_D(const QFontDialog);
929     return QFontDialog::FontDialogOptions(int(d->options->options()));
930 }
931 
932 /*!
933     \since 4.5
934 
935     Opens the dialog and connects its fontSelected() signal to the slot specified
936     by \a receiver and \a member.
937 
938     The signal will be disconnected from the slot when the dialog is closed.
939 */
open(QObject * receiver,const char * member)940 void QFontDialog::open(QObject *receiver, const char *member)
941 {
942     Q_D(QFontDialog);
943     connect(this, SIGNAL(fontSelected(QFont)), receiver, member);
944     d->receiverToDisconnectOnClose = receiver;
945     d->memberToDisconnectOnClose = member;
946     QDialog::open();
947 }
948 
949 /*!
950     \since 4.5
951 
952     \fn void QFontDialog::currentFontChanged(const QFont &font)
953 
954     This signal is emitted when the current font is changed. The new font is
955     specified in \a font.
956 
957     The signal is emitted while a user is selecting a font. Ultimately, the
958     chosen font may differ from the font currently selected.
959 
960     \sa currentFont, fontSelected(), selectedFont()
961 */
962 
963 /*!
964     \since 4.5
965 
966     \fn void QFontDialog::fontSelected(const QFont &font)
967 
968     This signal is emitted when a font has been selected. The selected font is
969     specified in \a font.
970 
971     The signal is only emitted when a user has chosen the final font to be
972     used. It is not emitted while the user is changing the current font in the
973     font dialog.
974 
975     \sa selectedFont(), currentFontChanged(), currentFont
976 */
977 
978 /*!
979     \reimp
980 */
setVisible(bool visible)981 void QFontDialog::setVisible(bool visible)
982 {
983     if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden) != visible)
984         return;
985     Q_D(QFontDialog);
986     if (d->canBeNativeDialog())
987         d->setNativeDialogVisible(visible);
988     if (d->nativeDialogInUse) {
989         // Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
990         // updates the state correctly, but skips showing the non-native version:
991         setAttribute(Qt::WA_DontShowOnScreen, true);
992     } else {
993         d->nativeDialogInUse = false;
994         setAttribute(Qt::WA_DontShowOnScreen, false);
995     }
996     QDialog::setVisible(visible);
997 }
998 
999 /*!
1000   Closes the dialog and sets its result code to \a result. If this dialog
1001   is shown with exec(), done() causes the local event loop to finish,
1002   and exec() to return \a result.
1003 
1004   \sa QDialog::done()
1005 */
done(int result)1006 void QFontDialog::done(int result)
1007 {
1008     Q_D(QFontDialog);
1009     if (result == Accepted) {
1010         // We check if this is the same font we had before, if so we emit currentFontChanged
1011         QFont selectedFont = currentFont();
1012         if(selectedFont != d->selectedFont)
1013             emit(currentFontChanged(selectedFont));
1014         d->selectedFont = selectedFont;
1015         emit fontSelected(d->selectedFont);
1016     } else
1017         d->selectedFont = QFont();
1018     if (d->receiverToDisconnectOnClose) {
1019         disconnect(this, SIGNAL(fontSelected(QFont)),
1020                    d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
1021         d->receiverToDisconnectOnClose = nullptr;
1022     }
1023     d->memberToDisconnectOnClose.clear();
1024     QDialog::done(result);
1025 }
1026 
canBeNativeDialog() const1027 bool QFontDialogPrivate::canBeNativeDialog() const
1028 {
1029     // Don't use Q_Q here! This function is called from ~QDialog,
1030     // so Q_Q calling q_func() invokes undefined behavior (invalid cast in q_func()).
1031     const QDialog * const q = static_cast<const QDialog*>(q_ptr);
1032     if (nativeDialogInUse)
1033         return true;
1034     if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)
1035         || q->testAttribute(Qt::WA_DontShowOnScreen)
1036         || (options->options() & QFontDialog::DontUseNativeDialog)) {
1037         return false;
1038     }
1039 
1040     QLatin1String staticName(QFontDialog::staticMetaObject.className());
1041     QLatin1String dynamicName(q->metaObject()->className());
1042     return (staticName == dynamicName);
1043 }
1044 
1045 QT_END_NAMESPACE
1046 
1047 #include "qfontdialog.moc"
1048 #include "moc_qfontdialog.cpp"
1049