KDELibs4Support

kfontcombobox.cpp
1 /* This file is part of the KDE libraries
2 
3  Copyright (C) 2008 Chusslove Illich <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "kfontcombobox.h"
22 #include "fonthelpers_p.h"
23 
24 #include "kdebug.h"
25 #include "klocalizedstring.h"
26 #include "kcolorscheme.h"
27 #include "kfontchooser.h"
28 #include "kcompletion.h"
29 
30 #include <QEvent>
31 #include <QListView>
32 #include <QFontDatabase>
33 #include <QIcon>
34 #include <QAbstractItemDelegate>
35 #include <QStringListModel>
36 #include <QPainter>
37 #include <QList>
38 #include <QHash>
39 #include <QScrollBar>
40 
41 static QString alphabetSample()
42 {
43  return i18nc("short",
44  // i18n: A shorter version of the alphabet test phrase translated in
45  // another message. It is displayed in the dropdown list of font previews
46  // (the font selection combo box), so keep it under the length equivalent
47  // to 60 or so proportional Latin characters.
48  "The Quick Brown Fox Jumps Over The Lazy Dog");
49 }
50 
51 class KFontFamilyDelegate : public QAbstractItemDelegate
52 {
53  Q_OBJECT
54 public:
55  KDELIBS4SUPPORT_DEPRECATED explicit KFontFamilyDelegate(QObject *parent);
56 
57  void paint(QPainter *painter,
58  const QStyleOptionViewItem &option,
59  const QModelIndex &index) const override;
60 
61  QSize sizeHint(const QStyleOptionViewItem &option,
62  const QModelIndex &index) const override;
63 
64  QIcon truetype;
65  QIcon bitmap;
66  double sizeFactFamily;
67  double sizeFactSample;
68 
69  QHash<QString, QString> fontFamilyTrMap;
70 };
71 
72 KFontFamilyDelegate::KFontFamilyDelegate(QObject *parent)
73  : QAbstractItemDelegate(parent)
74 {
75  truetype = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fonttruetype-16.png"));
76  bitmap = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fontbitmap-16.png"));
77 
78  // Font size factors for family name and text sample in font previes,
79  // multiplies normal font size.
80  sizeFactFamily = 1.0;
81  sizeFactSample = 1.0; // better leave at 1, so that user can relate sizes to default
82 }
83 
84 void KFontFamilyDelegate::paint(QPainter *painter,
85  const QStyleOptionViewItem &option,
86  const QModelIndex &index) const
87 {
88  QBrush sampleBrush;
89  if (option.state & QStyle::State_Selected) {
90  painter->save();
91  painter->setBrush(option.palette.highlight());
92  painter->setPen(Qt::NoPen);
93  painter->drawRect(option.rect);
94  painter->setPen(QPen(option.palette.highlightedText(), 0));
95  sampleBrush = option.palette.highlightedText();
96  } else {
98  }
99 
101  QString trFontFamily = index.data(Qt::DisplayRole).toString();
102  QString fontFamily = fontFamilyTrMap[trFontFamily];
103 
104  // Writing systems provided by the font.
105  QList<QFontDatabase::WritingSystem> availableSystems = QFontDatabase().writingSystems(fontFamily);
106 
107  // Intersect font's writing systems with that specified for
108  // the language's sample text, to see if the sample can be shown.
109  // If the font reports no writing systems, assume it can show the sample.
110  bool canShowLanguageSample = true;
111  if (availableSystems.count() > 0) {
112  canShowLanguageSample = false;
113  QString scriptsSpec = i18nc("Numeric IDs of scripts for font previews",
114  // i18n: Integer which indicates the script you used in the sample text
115  // for font previews in your language. For the possible values, see
116  // https://doc.qt.io/qt-5/qfontdatabase.html#WritingSystem-enum
117  // If the sample text contains several scripts, their IDs can be given
118  // as a comma-separated list (e.g. for Japanese it is "1,27").
119  "1");
120  QStringList scriptStrIds = scriptsSpec.split(',');
121  foreach (const QString &scriptStrId, scriptStrIds) {
122  bool convOk;
123  int ws = scriptStrId.toInt(&convOk);
124  if (convOk && ws > 0 && ws < QFontDatabase::WritingSystemsCount
125  && availableSystems.contains(static_cast<QFontDatabase::WritingSystem>(ws))) {
126  canShowLanguageSample = true;
127  break;
128  }
129  }
130  }
131 
132  // Choose and paint an icon according to the font type, scalable or bitmat.
133  const QIcon *icon = &bitmap;
134  if (QFontDatabase().isSmoothlyScalable(fontFamily)) {
135  icon = &truetype;
136  }
137  QRect r = option.rect;
138  icon->paint(painter, r, Qt::AlignLeft | Qt::AlignTop);
139 
140  // Claim space taken up by the icon.
141  QSize actualSize = icon->actualSize(r.size());
142  if (option.direction == Qt::RightToLeft) {
143  r.setRight(r.right() - actualSize.width() - 4);
144  } else {
145  r.setLeft(r.left() + actualSize.width() + 4);
146  }
147 
148  // Draw the font family.
149  QFont oldPainterFont = painter->font();
150  QFont familyFont = baseFont;
151  familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily);
152  painter->setFont(familyFont);
153  painter->drawText(r, Qt::AlignTop | Qt::AlignLeading | Qt::TextSingleLine, trFontFamily);
154 
155  // Claim space taken up by the font family name.
156  int h = painter->fontMetrics().lineSpacing();
157  r.setTop(r.top() + h);
158 
159  // Show text sample in user's language if the writing system is supported,
160  // otherwise show a collage of generic script samples provided by Qt.
161  // If the font does not report what it supports, assume all.
162  QString sample;
163  if (canShowLanguageSample) {
164  sample = alphabetSample();
165  } else {
166  foreach (const QFontDatabase::WritingSystem &ws, availableSystems) {
167  sample += QFontDatabase::writingSystemSample(ws) + " ";
168  if (sample.length() > 40) { // do not let the sample be too long
169  break;
170  }
171  }
172  sample = sample.trimmed();
173  }
174  QFont sampleFont;
175  sampleFont.setFamily(fontFamily);
176  sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample);
177  painter->setFont(sampleFont);
178  QPen oldPen = painter->pen();
179  painter->setPen(sampleBrush.color());
180  painter->drawText(r, Qt::AlignTop | Qt::AlignLeading | Qt::TextSingleLine, sample);
181  painter->setFont(oldPainterFont);
182  painter->setPen(oldPen);
183 
184  if (option.state & QStyle::State_Selected) {
185  painter->restore();
186  }
187 }
188 
189 QSize KFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option,
190  const QModelIndex &index) const
191 {
192  Q_UNUSED(option);
193 
195  QString trFontFamily = index.data(Qt::DisplayRole).toString();
196  QString fontFamily = fontFamilyTrMap[trFontFamily];
197 
198  QFont familyFont = baseFont;
199  familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily);
200  QFontMetrics familyMetrics(familyFont);
201 
202  QFont sampleFont = baseFont;
203  sampleFont.setFamily(fontFamily);
204  sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample);
205  QFontMetrics sampleMetrics(sampleFont);
206  QString sample = alphabetSample();
207 
208  // Only the hight matters here, the width is mandated by KFontComboBox::event()
209  return QSize(qMax(familyMetrics.width(trFontFamily), sampleMetrics.width(sample)),
210  qRound(familyMetrics.lineSpacing() + sampleMetrics.lineSpacing() * 1.2));
211 }
212 
213 class KFontComboBoxPrivate
214 {
215 public:
216  KFontComboBoxPrivate(KFontComboBox *parent);
217  void updateDatabase();
218  void updateIndexToFont();
219  void _k_currentFontChanged(int index);
220 
221  KFontComboBox *k;
222  QFont currentFont;
223  bool onlyFixed;
224  bool signalsAllowed;
225  KFontFamilyDelegate *delegate;
226  QStringListModel *model;
227  QStringList fontList;
228 };
229 
230 KFontComboBoxPrivate::KFontComboBoxPrivate(KFontComboBox *parent)
231  : k(parent),
232  currentFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)),
233  onlyFixed(false),
234  signalsAllowed(true)
235 {
236 }
237 
238 void KFontComboBoxPrivate::updateDatabase()
239 {
240  QStringList fontFamilies = fontList;
241  if (fontList.isEmpty()) {
242  KFontChooser::getFontList(fontFamilies,
243  onlyFixed ? KFontChooser::FixedWidthFonts : 0);
244  }
245 
246  // Translate font families for the list model.
247  delegate->fontFamilyTrMap.clear();
248  QStringList trFontFamilies =
249  translateFontNameList(fontFamilies, &(delegate->fontFamilyTrMap));
250 
251  // Add families to the list model and completion.
252  model->setStringList(trFontFamilies);
253  KCompletion *completion = k->completionObject();
254  if (completion) {
255  completion->setItems(trFontFamilies);
256  completion->setIgnoreCase(true);
257  }
258 }
259 
260 void KFontComboBoxPrivate::updateIndexToFont()
261 {
262  // QFontInfo necessary to return the family with proper casing.
263  QString selectedFontFamily = QFontInfo(currentFont).family();
264  QString trSelectedFontFamily = translateFontName(selectedFontFamily);
265  const QStringList trFontFamilies = model->stringList();
266  if (trFontFamilies.isEmpty()) {
267  return;
268  }
269 
270  // Match the font's family with an item in the list.
271  int index = 0;
272  foreach (const QString &trFontFamily, trFontFamilies) {
273  if (trSelectedFontFamily == trFontFamily) {
274  break;
275  }
276  ++index;
277  }
278  if (index == trFontFamilies.count()) {
279  // If no family matched, change font to first on the list.
280  index = 0;
281  currentFont = QFont(delegate->fontFamilyTrMap[trFontFamilies[0]]);
282  emit k->currentFontChanged(currentFont);
283  }
284 
285  // Set the new list item.
286  signalsAllowed = false;
287  k->setCurrentIndex(index);
288  signalsAllowed = true;
289 }
290 
291 void KFontComboBoxPrivate::_k_currentFontChanged(int index)
292 {
293  if (!signalsAllowed) {
294  return;
295  }
296 
297  QString trFontFamily = k->itemText(index);
298  QString fontFamily = delegate->fontFamilyTrMap[trFontFamily];
299  if (!fontFamily.isEmpty()) {
300  currentFont = QFont(fontFamily);
301  emit k->currentFontChanged(currentFont);
302  } else {
303  // Unknown font family given. Just remove from the list.
304  // This should not happen, as adding arbitrary font names is prevented.
305  QStringList lst = model->stringList();
306  lst.removeAll(trFontFamily);
307  model->setStringList(lst);
308  }
309 }
310 
312  : KComboBox(true, parent), d(new KFontComboBoxPrivate(this))
313 {
314  // Inputing arbitrary font names does not make sense.
316 
317  // Special list item painter showing font previews and its list model.
318  d->delegate = new KFontFamilyDelegate(this);
319  setItemDelegate(d->delegate);
320  d->model = new QStringListModel(this);
321  setModel(d->model);
322 
323  // Set current font when a new family has been chosen in the combo.
324  connect(this, SIGNAL(currentIndexChanged(int)),
325  this, SLOT(_k_currentFontChanged(int)));
326 
327  // Initialize font selection and list of available fonts.
328  d->updateDatabase();
329  d->updateIndexToFont();
330 }
331 
333 {
334  delete d;
335 }
336 
337 void KFontComboBox::setOnlyFixed(bool onlyFixed)
338 {
339  if (onlyFixed != d->onlyFixed) {
340  d->onlyFixed = onlyFixed;
341  d->updateDatabase();
342  }
343 }
344 
346 {
347  if (fontList != d->fontList) {
348  d->fontList = fontList;
349  d->updateDatabase();
350  }
351 }
352 
354 {
355  return d->currentFont;
356 }
357 
359 {
360  if (font != d->currentFont) {
361  d->currentFont = font;
362  emit currentFontChanged(d->currentFont);
363  d->updateIndexToFont();
364  }
365 }
366 
367 bool KFontComboBox::event(QEvent *e)
368 {
369  if (e->type() == QEvent::Resize) {
370  QListView *lview = qobject_cast<QListView *>(view());
371  if (lview) {
372  QString sample = alphabetSample();
373  // Limit text sample length to avoid too wide list view.
374  if (sample.length() > 60) {
375  sample = sample.left(57) + "...";
376  }
378  approxFont.setPointSizeF(approxFont.pointSizeF()
379  * d->delegate->sizeFactSample);
380  int widgetWidth = width();
381  int sampleWidth = QFontMetrics(approxFont).width(sample);
382  sampleWidth = qRound(sampleWidth * 1.1); // extra for wider fonts
383  int iconWidth = d->delegate->truetype.actualSize(size()).width();
384  int vsbarWidth = 0;
385  if (lview->verticalScrollBar()) {
386  vsbarWidth = lview->verticalScrollBar()->width();
387  }
388  lview->window()->setFixedWidth(qMax(widgetWidth, sampleWidth)
389  + iconWidth + vsbarWidth);
390  }
391  }
392  return KComboBox::event(e);
393 }
394 
396 {
397  QSize sz = KComboBox::sizeHint();
399  sz.setWidth(fm.width("m") * 14);
400  return sz;
401 }
402 
403 #include "fonthelpers.cpp"
404 
405 #include "kfontcombobox.moc"
406 #include "moc_kfontcombobox.moc"
407 
QTextStream & ws(QTextStream &stream)
QString writingSystemSample(QFontDatabase::WritingSystem writingSystem)
QSize size() const const
QEvent::Type type() const const
int width() const const
void setFixedWidth(int w)
QAction * actualSize(const QObject *recvr, const char *slot, QObject *parent)
QWidget * window() const const
int right() const const
A lightweight font selection widget.
Definition: kfontcombobox.h:51
void setOnlyFixed(bool onlyFixed)
Toggle selectable fonts to be only those of fixed width or all.
qreal pointSizeF() const const
const QFont & font() const const
static void getFontList(QStringList &list, uint fontListCriteria)
void setItemDelegate(QAbstractItemDelegate *delegate)
const QList< QKeySequence > & completion()
void save()
bool isSmoothlyScalable(const QString &family, const QString &style) const const
KFontComboBox(QWidget *parent=nullptr)
Constructor.
RightToLeft
AlignLeft
int width() const const
const QColor & color() const const
QSize size() const const
QSize sizeHint() const override
The recommended size of the widget.
virtual bool event(QEvent *event) override
void drawRect(const QRectF &rectangle)
void setFont(const QFont &font)
int count(const T &value) const const
QBrush foreground(ForegroundRole=NormalText) const
Q_OBJECTQ_OBJECT
int top() const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setPen(const QColor &color)
void setTop(int y)
int left() const const
QList< QFontDatabase::WritingSystem > writingSystems() const const
int toInt(bool *ok, int base) const const
void setWidth(int width)
bool isEmpty() const const
DisplayRole
bool isEmpty() const const
int removeAll(const T &value)
QString trimmed() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void setFontList(const QStringList &fontList)
Set selectable fonts to be only those present in the list.
virtual void setIgnoreCase(bool ignoreCase)
QScrollBar * verticalScrollBar() const const
void paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, QIcon::Mode mode, QIcon::State state) const const
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const const =0
QString family() const const
void currentFontChanged(const QFont &font)
Emitted when a new font has been selected, either through user input or by setFont().
const QFont & font() const const
bool contains(const T &value) const const
void setPointSizeF(qreal pointSize)
void restore()
void setRight(int x)
QFont systemFont(QFontDatabase::SystemFont type)
QVariant data(int role) const const
void setInsertPolicy(QComboBox::InsertPolicy policy)
void setModel(QAbstractItemModel *model)
QFontMetrics fontMetrics() const const
void setFamily(const QString &family)
TextSingleLine
int length() const const
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const const =0
QString left(int n) const const
int width(const QString &text, int len) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
virtual ~KFontComboBox()
Destructor.
const QPen & pen() const const
void setCurrentFont(const QFont &font)
Set the font to show as selected in the combobox.
QString toString() const const
virtual void setItems(const QStringList &itemList)
void setLeft(int x)
virtual QSize sizeHint() const const override
int lineSpacing() const const
void currentIndexChanged(int index)
QFont currentFont() const
The font currently selected from the list.
QAbstractItemView * view() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue Aug 4 2020 22:57:33 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.