util/src/gui/widgets/qfontcombobox.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qfontcombobox.h"
       
    43 
       
    44 #ifndef QT_NO_FONTCOMBOBOX
       
    45 
       
    46 #include <qstringlistmodel.h>
       
    47 #include <qitemdelegate.h>
       
    48 #include <qlistview.h>
       
    49 #include <qpainter.h>
       
    50 #include <qevent.h>
       
    51 #include <qapplication.h>
       
    52 #include <private/qcombobox_p.h>
       
    53 #include <qdebug.h>
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 static QFontDatabase::WritingSystem writingSystemForFont(const QFont &font, bool *hasLatin)
       
    58 {
       
    59     *hasLatin = true;
       
    60 
       
    61     QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase().writingSystems(font.family());
       
    62 //     qDebug() << font.family() << writingSystems;
       
    63 
       
    64     // this just confuses the algorithm below. Vietnamese is Latin with lots of special chars
       
    65     writingSystems.removeAll(QFontDatabase::Vietnamese);
       
    66 
       
    67     QFontDatabase::WritingSystem system = QFontDatabase::Any;
       
    68 
       
    69     if (!writingSystems.contains(QFontDatabase::Latin)) {
       
    70         *hasLatin = false;
       
    71         // we need to show something
       
    72         if (writingSystems.count())
       
    73             system = writingSystems.last();
       
    74     } else {
       
    75         writingSystems.removeAll(QFontDatabase::Latin);
       
    76     }
       
    77 
       
    78     if (writingSystems.isEmpty())
       
    79         return system;
       
    80 
       
    81     if (writingSystems.count() == 1 && writingSystems.at(0) > QFontDatabase::Cyrillic) {
       
    82         system = writingSystems.at(0);
       
    83         return system;
       
    84     }
       
    85 
       
    86     if (writingSystems.count() <= 2
       
    87         && writingSystems.last() > QFontDatabase::Armenian
       
    88         && writingSystems.last() < QFontDatabase::Vietnamese) {
       
    89         system = writingSystems.last();
       
    90         return system;
       
    91     }
       
    92 
       
    93     if (writingSystems.count() <= 5
       
    94         && writingSystems.last() >= QFontDatabase::SimplifiedChinese
       
    95         && writingSystems.last() <= QFontDatabase::Korean)
       
    96         system = writingSystems.last();
       
    97 
       
    98     return system;
       
    99 }
       
   100 
       
   101 class QFontFamilyDelegate : public QAbstractItemDelegate
       
   102 {
       
   103     Q_OBJECT
       
   104 public:
       
   105     explicit QFontFamilyDelegate(QObject *parent);
       
   106 
       
   107     // painting
       
   108     void paint(QPainter *painter,
       
   109                const QStyleOptionViewItem &option,
       
   110                const QModelIndex &index) const;
       
   111 
       
   112     QSize sizeHint(const QStyleOptionViewItem &option,
       
   113                    const QModelIndex &index) const;
       
   114 
       
   115     QIcon truetype;
       
   116     QIcon bitmap;
       
   117     QFontDatabase::WritingSystem writingSystem;
       
   118 };
       
   119 
       
   120 QFontFamilyDelegate::QFontFamilyDelegate(QObject *parent)
       
   121     : QAbstractItemDelegate(parent)
       
   122 {
       
   123     truetype = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fonttruetype-16.png"));
       
   124     bitmap = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fontbitmap-16.png"));
       
   125     writingSystem = QFontDatabase::Any;
       
   126 }
       
   127 
       
   128 void QFontFamilyDelegate::paint(QPainter *painter,
       
   129                                 const QStyleOptionViewItem &option,
       
   130                                 const QModelIndex &index) const
       
   131 {
       
   132     QString text = index.data(Qt::DisplayRole).toString();
       
   133     QFont font(option.font);
       
   134     font.setPointSize(QFontInfo(font).pointSize() * 3 / 2);
       
   135     QFont font2 = font;
       
   136     font2.setFamily(text);
       
   137 
       
   138     bool hasLatin;
       
   139     QFontDatabase::WritingSystem system = writingSystemForFont(font2, &hasLatin);
       
   140     if (hasLatin)
       
   141         font = font2;
       
   142 
       
   143     QRect r = option.rect;
       
   144 
       
   145     if (option.state & QStyle::State_Selected) {
       
   146         painter->save();
       
   147         painter->setBrush(option.palette.highlight());
       
   148         painter->setPen(Qt::NoPen);
       
   149         painter->drawRect(option.rect);
       
   150         painter->setPen(QPen(option.palette.highlightedText(), 0));
       
   151     }
       
   152 
       
   153     const QIcon *icon = &bitmap;
       
   154     if (QFontDatabase().isSmoothlyScalable(text)) {
       
   155         icon = &truetype;
       
   156     }
       
   157     QSize actualSize = icon->actualSize(r.size());
       
   158 
       
   159     icon->paint(painter, r, Qt::AlignLeft|Qt::AlignVCenter);
       
   160     if (option.direction == Qt::RightToLeft)
       
   161         r.setRight(r.right() - actualSize.width() - 4);
       
   162     else
       
   163         r.setLeft(r.left() + actualSize.width() + 4);
       
   164 
       
   165     QFont old = painter->font();
       
   166     painter->setFont(font);
       
   167     painter->drawText(r, Qt::AlignVCenter|Qt::AlignLeading|Qt::TextSingleLine, text);
       
   168 
       
   169     if (writingSystem != QFontDatabase::Any)
       
   170         system = writingSystem;
       
   171 
       
   172     if (system != QFontDatabase::Any) {
       
   173         int w = painter->fontMetrics().width(text + QLatin1String("  "));
       
   174         painter->setFont(font2);
       
   175         QString sample = QFontDatabase().writingSystemSample(system);
       
   176         if (option.direction == Qt::RightToLeft)
       
   177             r.setRight(r.right() - w);
       
   178         else
       
   179             r.setLeft(r.left() + w);
       
   180         painter->drawText(r, Qt::AlignVCenter|Qt::AlignLeading|Qt::TextSingleLine, sample);
       
   181     }
       
   182     painter->setFont(old);
       
   183 
       
   184     if (option.state & QStyle::State_Selected)
       
   185         painter->restore();
       
   186 
       
   187 }
       
   188 
       
   189 QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option,
       
   190                                     const QModelIndex &index) const
       
   191 {
       
   192     QString text = index.data(Qt::DisplayRole).toString();
       
   193     QFont font(option.font);
       
   194 //     font.setFamily(text);
       
   195     font.setPointSize(QFontInfo(font).pointSize() * 3/2);
       
   196     QFontMetrics fontMetrics(font);
       
   197     return QSize(fontMetrics.width(text), fontMetrics.height());
       
   198 }
       
   199 
       
   200 
       
   201 class QFontComboBoxPrivate : public QComboBoxPrivate
       
   202 {
       
   203 public:
       
   204     inline QFontComboBoxPrivate() { filters = QFontComboBox::AllFonts; }
       
   205 
       
   206     QFontComboBox::FontFilters filters;
       
   207     QFont currentFont;
       
   208 
       
   209     void _q_updateModel();
       
   210     void _q_currentChanged(const QString &);
       
   211 
       
   212     Q_DECLARE_PUBLIC(QFontComboBox)
       
   213 };
       
   214 
       
   215 
       
   216 void QFontComboBoxPrivate::_q_updateModel()
       
   217 {
       
   218     Q_Q(QFontComboBox);
       
   219     const int scalableMask = (QFontComboBox::ScalableFonts | QFontComboBox::NonScalableFonts);
       
   220     const int spacingMask = (QFontComboBox::ProportionalFonts | QFontComboBox::MonospacedFonts);
       
   221 
       
   222     QStringListModel *m = qobject_cast<QStringListModel *>(q->model());
       
   223     if (!m)
       
   224         return;
       
   225     QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(q->view()->itemDelegate());
       
   226     QFontDatabase::WritingSystem system = delegate ? delegate->writingSystem : QFontDatabase::Any;
       
   227 
       
   228     QFontDatabase fdb;
       
   229     QStringList list = fdb.families(system);
       
   230     QStringList result;
       
   231 
       
   232     int offset = 0;
       
   233     QFontInfo fi(currentFont);
       
   234 
       
   235     for (int i = 0; i < list.size(); ++i) {
       
   236         if ((filters & scalableMask) && (filters & scalableMask) != scalableMask) {
       
   237             if (bool(filters & QFontComboBox::ScalableFonts) != fdb.isSmoothlyScalable(list.at(i)))
       
   238                 continue;
       
   239         }
       
   240         if ((filters & spacingMask) && (filters & spacingMask) != spacingMask) {
       
   241             if (bool(filters & QFontComboBox::MonospacedFonts) != fdb.isFixedPitch(list.at(i)))
       
   242                 continue;
       
   243         }
       
   244         result += list.at(i);
       
   245         if (list.at(i) == fi.family() || list.at(i).startsWith(fi.family() + QLatin1String(" [")))
       
   246             offset = result.count() - 1;
       
   247     }
       
   248     list = result;
       
   249 
       
   250     //we need to block the signals so that the model doesn't emit reset
       
   251     //this prevents the current index from changing
       
   252     //it will be updated just after this
       
   253     ///TODO: we should finda way to avoid blocking signals and have a real update of the model
       
   254     const bool old = m->blockSignals(true);
       
   255     m->setStringList(list);
       
   256     m->blockSignals(old);
       
   257 
       
   258     if (list.isEmpty()) {
       
   259         if (currentFont != QFont()) {
       
   260             currentFont = QFont();
       
   261             emit q->currentFontChanged(currentFont);
       
   262         }
       
   263     } else {
       
   264         q->setCurrentIndex(offset);
       
   265     }
       
   266 }
       
   267 
       
   268 
       
   269 void QFontComboBoxPrivate::_q_currentChanged(const QString &text)
       
   270 {
       
   271     Q_Q(QFontComboBox);
       
   272     if (currentFont.family() != text) {
       
   273         currentFont.setFamily(text);
       
   274         emit q->currentFontChanged(currentFont);
       
   275     }
       
   276 }
       
   277 
       
   278 /*!
       
   279     \class QFontComboBox
       
   280     \brief The QFontComboBox widget is a combobox that lets the user
       
   281     select a font family.
       
   282 
       
   283     \since 4.2
       
   284     \ingroup basicwidgets
       
   285 
       
   286     The combobox is populated with an alphabetized list of font
       
   287     family names, such as Arial, Helvetica, and Times New Roman.
       
   288     Family names are displayed using the actual font when possible.
       
   289     For fonts such as Symbol, where the name is not representable in
       
   290     the font itself, a sample of the font is displayed next to the
       
   291     family name.
       
   292 
       
   293     QFontComboBox is often used in toolbars, in conjunction with a
       
   294     QComboBox for controlling the font size and two \l{QToolButton}s
       
   295     for bold and italic.
       
   296 
       
   297     When the user selects a new font, the currentFontChanged() signal
       
   298     is emitted in addition to currentIndexChanged().
       
   299 
       
   300     Call setWritingSystem() to tell QFontComboBox to show only fonts
       
   301     that support a given writing system, and setFontFilters() to
       
   302     filter out certain types of fonts as e.g. non scalable fonts or
       
   303     monospaced fonts.
       
   304 
       
   305     \image windowsxp-fontcombobox.png Screenshot of QFontComboBox on Windows XP
       
   306 
       
   307     \sa QComboBox, QFont, QFontInfo, QFontMetrics, QFontDatabase, {Character Map Example}
       
   308 */
       
   309 
       
   310 /*!
       
   311     \fn void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
       
   312 */
       
   313 
       
   314 /*!
       
   315     \fn void QFontComboBox::setCurrentFont(const QFont &font);
       
   316 */
       
   317 
       
   318 /*!
       
   319     Constructs a font combobox with the given \a parent.
       
   320 */
       
   321 QFontComboBox::QFontComboBox(QWidget *parent)
       
   322     : QComboBox(*new QFontComboBoxPrivate, parent)
       
   323 {
       
   324     Q_D(QFontComboBox);
       
   325     d->currentFont = font();
       
   326     setEditable(true);
       
   327 
       
   328     QStringListModel *m = new QStringListModel(this);
       
   329     setModel(m);
       
   330     setItemDelegate(new QFontFamilyDelegate(this));
       
   331     QListView *lview = qobject_cast<QListView*>(view());
       
   332     if (lview)
       
   333         lview->setUniformItemSizes(true);
       
   334     setWritingSystem(QFontDatabase::Any);
       
   335 
       
   336     connect(this, SIGNAL(currentIndexChanged(QString)),
       
   337             this, SLOT(_q_currentChanged(QString)));
       
   338 
       
   339     connect(qApp, SIGNAL(fontDatabaseChanged()),
       
   340             this, SLOT(_q_updateModel()));
       
   341 }
       
   342 
       
   343 
       
   344 /*!
       
   345     Destroys the combobox.
       
   346 */
       
   347 QFontComboBox::~QFontComboBox()
       
   348 {
       
   349 }
       
   350 
       
   351 /*!
       
   352     \property QFontComboBox::writingSystem
       
   353     \brief the writing system that serves as a filter for the combobox
       
   354 
       
   355     If \a script is QFontDatabase::Any (the default), all fonts are
       
   356     listed.
       
   357 
       
   358     \sa fontFilters
       
   359 */
       
   360 
       
   361 void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
       
   362 {
       
   363     Q_D(QFontComboBox);
       
   364     QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(view()->itemDelegate());
       
   365     if (delegate)
       
   366         delegate->writingSystem = script;
       
   367     d->_q_updateModel();
       
   368 }
       
   369 
       
   370 QFontDatabase::WritingSystem QFontComboBox::writingSystem() const
       
   371 {
       
   372     QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(view()->itemDelegate());
       
   373     if (delegate)
       
   374         return delegate->writingSystem;
       
   375     return QFontDatabase::Any;
       
   376 }
       
   377 
       
   378 
       
   379 /*!
       
   380   \enum QFontComboBox::FontFilter
       
   381 
       
   382   This enum can be used to only show certain types of fonts in the font combo box.
       
   383 
       
   384   \value AllFonts Show all fonts
       
   385   \value ScalableFonts Show scalable fonts
       
   386   \value NonScalableFonts Show non scalable fonts
       
   387   \value MonospacedFonts Show monospaced fonts
       
   388   \value ProportionalFonts Show proportional fonts
       
   389 */
       
   390 
       
   391 /*!
       
   392     \property QFontComboBox::fontFilters
       
   393     \brief the filter for the combobox
       
   394 
       
   395     By default, all fonts are listed.
       
   396 
       
   397     \sa writingSystem
       
   398 */
       
   399 void QFontComboBox::setFontFilters(FontFilters filters)
       
   400 {
       
   401     Q_D(QFontComboBox);
       
   402     d->filters = filters;
       
   403     d->_q_updateModel();
       
   404 }
       
   405 
       
   406 QFontComboBox::FontFilters QFontComboBox::fontFilters() const
       
   407 {
       
   408     Q_D(const QFontComboBox);
       
   409     return d->filters;
       
   410 }
       
   411 
       
   412 /*!
       
   413     \property QFontComboBox::currentFont
       
   414     \brief the currently selected font
       
   415 
       
   416     \sa currentFontChanged(), currentIndex, currentText
       
   417 */
       
   418 QFont QFontComboBox::currentFont() const
       
   419 {
       
   420     Q_D(const QFontComboBox);
       
   421     return d->currentFont;
       
   422 }
       
   423 
       
   424 void QFontComboBox::setCurrentFont(const QFont &font)
       
   425 {
       
   426     Q_D(QFontComboBox);
       
   427     if (font != d->currentFont) {
       
   428         d->currentFont = font;
       
   429         d->_q_updateModel();
       
   430         if (d->currentFont == font) { //else the signal has already be emitted by _q_updateModel
       
   431             emit currentFontChanged(d->currentFont);
       
   432         }
       
   433     }
       
   434 }
       
   435 
       
   436 /*!
       
   437     \fn QFontComboBox::currentFontChanged(const QFont &font)
       
   438 
       
   439     This signal is emitted whenever the current font changes, with
       
   440     the new \a font.
       
   441 
       
   442     \sa currentFont
       
   443 */
       
   444 
       
   445 /*!
       
   446     \reimp
       
   447 */
       
   448 bool QFontComboBox::event(QEvent *e)
       
   449 {
       
   450     if (e->type() == QEvent::Resize) {
       
   451         QListView *lview = qobject_cast<QListView*>(view());
       
   452         if (lview)
       
   453             lview->window()->setFixedWidth(width() * 5 / 3);
       
   454     }
       
   455     return QComboBox::event(e);
       
   456 }
       
   457 
       
   458 /*!
       
   459     \reimp
       
   460 */
       
   461 QSize QFontComboBox::sizeHint() const
       
   462 {
       
   463     QSize sz = QComboBox::sizeHint();
       
   464     QFontMetrics fm(font());
       
   465     sz.setWidth(fm.width(QLatin1Char('m'))*14);
       
   466     return sz;
       
   467 }
       
   468 
       
   469 QT_END_NAMESPACE
       
   470 
       
   471 #include "qfontcombobox.moc"
       
   472 #include "moc_qfontcombobox.cpp"
       
   473 
       
   474 #endif // QT_NO_FONTCOMBOBOX