KWidgetsAddons

kfontchooser.cpp
1 /*
2  SPDX-FileCopyrightText: 1996 Bernd Johannes Wuebben <[email protected]>
3  SPDX-FileCopyrightText: 1999 Preston Brown <[email protected]>
4  SPDX-FileCopyrightText: 1999 Mario Weilguni <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kfontchooser.h"
10 #include "fonthelpers_p.h"
11 
12 #include <QCheckBox>
13 #include <QDoubleSpinBox>
14 #include <QGuiApplication>
15 #include <QLabel>
16 #include <QLayout>
17 #include <QLocale>
18 #include <QSplitter>
19 #include <QScrollBar>
20 #include <QFontDatabase>
21 #include <QGroupBox>
22 #include <QListWidget>
23 #include <QTextEdit>
24 
25 #include <cmath>
26 
27 // When message extraction needs to be avoided.
28 #define TR_NOX tr
29 
30 static int minimumListWidth(const QListWidget *list)
31 {
32  int w = 0;
33  for (int i = 0; i < list->count(); i++) {
34  int itemWidth = list->visualItemRect(list->item(i)).width();
35  // ...and add a space on both sides for not too tight look.
36  itemWidth += list->fontMetrics().horizontalAdvance(QLatin1Char(' ')) * 2;
37  w = qMax(w, itemWidth);
38  }
39  if (w == 0) {
40  w = 40;
41  }
42  w += list->frameWidth() * 2;
43  w += list->verticalScrollBar()->sizeHint().width();
44  return w;
45 }
46 
47 static int minimumListHeight(const QListWidget *list, int numVisibleEntry)
48 {
49  int w = list->count() > 0 ? list->visualItemRect(list->item(0)).height() :
50  list->fontMetrics().lineSpacing();
51 
52  if (w < 0) {
53  w = 10;
54  }
55  if (numVisibleEntry <= 0) {
56  numVisibleEntry = 4;
57  }
58  return (w * numVisibleEntry + 2 * list->frameWidth());
59 }
60 
61 static QString formatFontSize(qreal size)
62 {
63  return QLocale::system().toString(size, 'f', (size == floor(size)) ? 0 : 1);
64 }
65 
66 class Q_DECL_HIDDEN KFontChooser::Private
67 {
68 public:
69  Private(KFontChooser *qq)
70  : q(qq)
71  {
72  m_palette.setColor(QPalette::Active, QPalette::Text, Qt::black);
73  m_palette.setColor(QPalette::Active, QPalette::Base, Qt::white);
74  signalsAllowed = true;
75  selectedSize = -1;
76  customSizeRow = -1;
77  }
78 
79  // pointer to an optinally supplied list of fonts to
80  // inserted into the fontdialog font-family combo-box
81 // QStringList fontList;
82 
83  void init(const DisplayFlags &flags, const QStringList &fontList,
84  int visibleListSize, Qt::CheckState *sizeIsRelativeState);
85  void setFamilyBoxItems(const QStringList &fonts);
86  void fillFamilyListBox(bool onlyFixedFonts = false);
87  int nearestSizeRow(qreal val, bool customize);
88  qreal fillSizeList(const QList<qreal> &sizes = QList<qreal>());
89  qreal setupSizeListBox(const QString &family, const QString &style);
90 
91  void setupDisplay();
92  QString styleIdentifier(const QFont &font);
93 
94  void _k_family_chosen_slot(const QString &);
95  void _k_size_chosen_slot(const QString &);
96  void _k_style_chosen_slot(const QString &);
97  void _k_displaySample(const QFont &font);
98  void _k_size_value_slot(double);
99 
100  KFontChooser *q;
101 
102  QPalette m_palette;
103 
104  QDoubleSpinBox *sizeOfFont = nullptr;
105 
106  QTextEdit *sampleEdit = nullptr;
107 
108  QLabel *familyLabel = nullptr;
109  QLabel *styleLabel = nullptr;
110  QCheckBox *familyCheckbox = nullptr;
111  QCheckBox *styleCheckbox = nullptr;
112  QCheckBox *sizeCheckbox = nullptr;
113  QLabel *sizeLabel = nullptr;
114  QListWidget *familyListBox = nullptr;
115  QListWidget *styleListBox = nullptr;
116  QListWidget *sizeListBox = nullptr;
117  QCheckBox *sizeIsRelativeCheckBox = nullptr;
118 
119  QCheckBox *onlyFixedCheckbox = nullptr;
120 
121  QFont selFont;
122 
123  QString selectedStyle;
124  qreal selectedSize;
125 
126  QString standardSizeAtCustom;
127  int customSizeRow;
128 
129  bool signalsAllowed: 1;
130 
131  bool usingFixed: 1;
132 
133  // Mappings of translated to Qt originated family and style strings.
134  QHash<QString, QString> qtFamilies;
135  QHash<QString, QString> qtStyles;
136  // Mapping of translated style strings to internal style identifiers.
137  QHash<QString, QString> styleIDs;
138 
139 };
140 
142  const DisplayFlags &flags,
143  const QStringList &fontList,
144  int visibleListSize,
145  Qt::CheckState *sizeIsRelativeState)
146  : QWidget(parent),
147  d(new KFontChooser::Private(this))
148 {
149  d->init(flags, fontList, visibleListSize, sizeIsRelativeState);
150 }
151 
153 {
154  delete d;
155 }
156 
157 void KFontChooser::Private::init(const DisplayFlags &flags, const QStringList &fontList,
158  int visibleListSize, Qt::CheckState *sizeIsRelativeState)
159 {
160  usingFixed = flags & FixedFontsOnly;
161 
162  // The main layout is divided horizontally into a top part with
163  // the font attribute widgets (family, style, size) and a bottom
164  // part with a preview of the selected font
165  QVBoxLayout *mainLayout = new QVBoxLayout(q);
166  mainLayout->setContentsMargins(0, 0, 0, 0);
167  auto *style = q->style();
168  const int sizeListBoxGap = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2;
169  const int checkBoxGap = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2;
170 
171  // Build the grid of font attribute widgets for the upper part of mainLayout
172  QWidget *page;
173  QGridLayout *gridLayout;
174  int row = flags & DisplayFrame ? 1 : 0;
175 
176  if (flags & DisplayFrame) {
177  page = new QGroupBox(KFontChooser::tr("Requested Font", "@title:group"), q);
178  mainLayout->addWidget(page);
179  gridLayout = new QGridLayout(page);
180  } else {
181  page = new QWidget(q);
182  mainLayout->addWidget(page);
183  gridLayout = new QGridLayout(page);
184  gridLayout->setContentsMargins(0, 0, 0, 0);
185  }
186 
187  // set up the family list view and checkbox
188  QHBoxLayout *familyLayout = new QHBoxLayout();
189  familyLayout->addSpacing(checkBoxGap);
190  if (flags & ShowDifferences) {
191  familyCheckbox = new QCheckBox(KFontChooser::tr("Font", "@option:check"), page);
192  familyLayout->addWidget(familyCheckbox, 0, Qt::AlignLeft);
193  familyLabel = nullptr;
194  } else {
195  familyCheckbox = nullptr;
196  familyLabel = new QLabel(KFontChooser::tr("Font:", "@label"), page);
197  familyLayout->addWidget(familyLabel, 1, Qt::AlignLeft);
198  }
199  gridLayout->addLayout(familyLayout, row, 0);
200 
201  ++row;
202 
203  familyListBox = new QListWidget(page);
204  gridLayout->addWidget(familyListBox, row, 0);
205 
206  connect(familyListBox, &QListWidget::currentTextChanged, [this](const QString &family) {
207  _k_family_chosen_slot(family);
208  });
209 
210  if (flags & ShowDifferences) {
211  familyListBox->setEnabled(false);
212  connect(familyCheckbox, &QAbstractButton::toggled, familyListBox, &QWidget::setEnabled);
213  }
214 
215  if (!fontList.isEmpty()) {
216  setFamilyBoxItems(fontList);
217  } else {
218  fillFamilyListBox(flags & FixedFontsOnly);
219  }
220 
221  familyListBox->setMinimumWidth(minimumListWidth(familyListBox));
222  familyListBox->setMinimumHeight(minimumListHeight(familyListBox, visibleListSize));
223 
224 
225  // set up the sytle list view and checkbox
226  row = flags & DisplayFrame ? 1 : 0;
227  QHBoxLayout *styleLayout = new QHBoxLayout();
228  if (flags & ShowDifferences) {
229  styleCheckbox = new QCheckBox(KFontChooser::tr("Font style", "@option:check"), page);
230  styleLayout->addWidget(styleCheckbox, 0, Qt::AlignLeft);
231  styleLabel = nullptr;
232  } else {
233  styleCheckbox = nullptr;
234  styleLabel = new QLabel(KFontChooser::tr("Font style:", "@label"), page);
235  styleLayout->addWidget(styleLabel, 1, Qt::AlignLeft);
236  }
237  styleLayout->addSpacing(checkBoxGap);
238  gridLayout->addLayout(styleLayout, row, 1);
239 
240  ++row;
241 
242  styleListBox = new QListWidget(page);
243  gridLayout->addWidget(styleListBox, row, 1);
244 
245  // Populate usual styles, to determine minimum list width;
246  // will be replaced later with correct styles.
247  styleListBox->addItem(KFontChooser::tr("Normal", "@item font"));
248  styleListBox->addItem(KFontChooser::tr("Italic", "@item font"));
249  styleListBox->addItem(KFontChooser::tr("Oblique", "@item font"));
250  styleListBox->addItem(KFontChooser::tr("Bold", "@item font"));
251  styleListBox->addItem(KFontChooser::tr("Bold Italic", "@item font"));
252  styleListBox->setMinimumWidth(minimumListWidth(styleListBox));
253  styleListBox->setMinimumHeight(minimumListHeight(styleListBox, visibleListSize));
254 
255  connect(styleListBox, &QListWidget::currentTextChanged, [this](const QString &style) {
256  _k_style_chosen_slot(style);
257  });
258 
259  if (flags & ShowDifferences) {
260  styleListBox->setEnabled(false);
261  connect(styleCheckbox, &QAbstractButton::toggled, styleListBox, &QWidget::setEnabled);
262  }
263 
264  // set the the size list view / font size spinbox; and the font size checkbox
265  row = flags & DisplayFrame ? 1 : 0;
266  QHBoxLayout *sizeLayout = new QHBoxLayout();
267  if (flags & ShowDifferences) {
268  sizeCheckbox = new QCheckBox(KFontChooser::tr("Size", "@option:check"), page);
269  sizeLayout->addWidget(sizeCheckbox, 0, Qt::AlignLeft);
270  sizeLabel = nullptr;
271  } else {
272  sizeCheckbox = nullptr;
273  sizeLabel = new QLabel(KFontChooser::tr("Size:", "@label:listbox Font size"), page);
274  sizeLayout->addWidget(sizeLabel, 1, Qt::AlignLeft);
275  }
276  sizeLayout->addSpacing(checkBoxGap);
277  sizeLayout->addSpacing(checkBoxGap); // prevent label from eating border
278  gridLayout->addLayout(sizeLayout, row, 2);
279 
280  ++row;
281 
282  sizeListBox = new QListWidget(page);
283  sizeOfFont = new QDoubleSpinBox(page);
284  sizeOfFont->setMinimum(4);
285  sizeOfFont->setMaximum(512);
286  sizeOfFont->setDecimals(1);
287  sizeOfFont->setSingleStep(1);
288 
289  if (sizeIsRelativeState) {
290  QString sizeIsRelativeCBText =
291  KFontChooser::tr("Relative", "@item font size");
292  QString sizeIsRelativeCBToolTipText =
293  KFontChooser::tr("Font size<br /><i>fixed</i> or <i>relative</i><br />to environment", "@info:tooltip");
294  QString sizeIsRelativeCBWhatsThisText =
295  KFontChooser::tr("Here you can switch between fixed font size and font size "
296  "to be calculated dynamically and adjusted to changing "
297  "environment (e.g. widget dimensions, paper size).", "@info:whatsthis");
298  sizeIsRelativeCheckBox = new QCheckBox(sizeIsRelativeCBText, page);
299  sizeIsRelativeCheckBox->setTristate(flags & ShowDifferences);
300  QGridLayout *sizeLayout2 = new QGridLayout();
301  sizeLayout2->setVerticalSpacing(sizeListBoxGap);
302  gridLayout->addLayout(sizeLayout2, row, 2);
303  sizeLayout2->setColumnStretch(1, 1); // to prevent text from eating the right border
304  sizeLayout2->addWidget(sizeOfFont, 0, 0, 1, 2);
305  sizeLayout2->addWidget(sizeListBox, 1, 0, 1, 2);
306  sizeLayout2->addWidget(sizeIsRelativeCheckBox, 2, 0, Qt::AlignLeft);
307  sizeIsRelativeCheckBox->setWhatsThis(sizeIsRelativeCBWhatsThisText);
308  sizeIsRelativeCheckBox->setToolTip(sizeIsRelativeCBToolTipText);
309  } else {
310  sizeIsRelativeCheckBox = nullptr;
311  QGridLayout *sizeLayout2 = new QGridLayout();
312  sizeLayout2->setVerticalSpacing(sizeListBoxGap);
313  gridLayout->addLayout(sizeLayout2, row, 2);
314  sizeLayout2->addWidget(sizeOfFont, 0, 0);
315  sizeLayout2->addWidget(sizeListBox, 1, 0);
316  }
317 
318  // Populate with usual sizes, to determine minimum list width;
319  // will be replaced later with correct sizes.
320  fillSizeList();
321  sizeListBox->setMinimumWidth(minimumListWidth(sizeListBox) + sizeListBox->fontMetrics().maxWidth());
322  sizeListBox->setMinimumHeight(minimumListHeight(sizeListBox, visibleListSize));
323 
324  connect(sizeOfFont, QOverload<double>::of(&QDoubleSpinBox::valueChanged), [this](const double size) {
325  _k_size_value_slot(size);
326  });
327 
328  connect(sizeListBox, &QListWidget::currentTextChanged, [this](const QString &size) {
329  _k_size_chosen_slot(size);
330  });
331 
332  if (flags & ShowDifferences) {
333  sizeListBox->setEnabled(false);
334  sizeOfFont->setEnabled(false);
335  connect(sizeCheckbox, &QAbstractButton::toggled, sizeListBox, &QWidget::setEnabled);
336  connect(sizeCheckbox, &QAbstractButton::toggled, sizeOfFont, &QWidget::setEnabled);
337  }
338 
339  // Add the font preview into the lower part of mainLayout
340  sampleEdit = new QTextEdit(page);
341  sampleEdit->setAcceptRichText(false);
342  QFont tmpFont(q->font().family(), 64, QFont::Black);
343  sampleEdit->setFont(tmpFont);
344  sampleEdit->setMinimumHeight(sampleEdit->fontMetrics().lineSpacing());
345  // tr: A classical test phrase, with all letters of the English alphabet.
346  // Replace it with a sample text in your language, such that it is
347  // representative of language's writing system.
348  // If you wish, you can input several lines of text separated by \n.
349  q->setSampleText(KFontChooser::tr("The Quick Brown Fox Jumps Over The Lazy Dog"));
350  sampleEdit->setTextCursor(QTextCursor(sampleEdit->document()));
351  QString sampleEditWhatsThisText =
352  KFontChooser::tr("This sample text illustrates the current settings. "
353  "You may edit it to test special characters.", "@info:whatsthis");
354  sampleEdit->setWhatsThis(sampleEditWhatsThisText);
355 
356  connect(q, &KFontChooser::fontSelected, q, [this](const QFont &font) {
357  _k_displaySample(font);
358  });
359 
360  mainLayout->addWidget(sampleEdit);
361 
362  // If the calling app sets FixedFontsOnly, respect its decision
363  if (!usingFixed) {
364  // Add a checkbox to toggle showing only monospace/fixed-width fonts
365  onlyFixedCheckbox = new QCheckBox(KFontChooser::tr("Show only monospaced fonts", "@option:check"));
366  onlyFixedCheckbox->setChecked(usingFixed);
367 
368  connect(onlyFixedCheckbox, &QAbstractButton::toggled, q, [this](const bool state) {
369  q->setFont(selFont, state);
370  });
371 
372  if (flags & ShowDifferences) { // In this mode follow the state of the familyCheckbox
373  onlyFixedCheckbox->setEnabled(false);
374  connect(familyCheckbox, &QAbstractButton::toggled, onlyFixedCheckbox, &QWidget::setEnabled);
375  }
376  mainLayout->addWidget(onlyFixedCheckbox);
377  }
378  // Finished setting up the chooser layout
379 
380  // lets initialize the display if possible
381  if (usingFixed) {
383  } else {
384  q->setFont(QGuiApplication::font(), false);
385  }
386 
387  // check or uncheck or gray out the "relative" checkbox
388  if (sizeIsRelativeState && sizeIsRelativeCheckBox) {
389  q->setSizeIsRelative(*sizeIsRelativeState);
390  }
391 
392  // Set focus to the size list as this is the most commonly changed property
393  sizeListBox->setFocus();
394 }
395 
397 {
398  d->m_palette.setColor(QPalette::Active, QPalette::Text, col);
399  QPalette pal = d->sampleEdit->palette();
401  d->sampleEdit->setPalette(pal);
402  QTextCursor cursor = d->sampleEdit->textCursor();
403  d->sampleEdit->selectAll();
404  d->sampleEdit->setTextColor(col);
405  d->sampleEdit->setTextCursor(cursor);
406 }
407 
409 {
410  return d->m_palette.color(QPalette::Active, QPalette::Text);
411 }
412 
414 {
415  d->m_palette.setColor(QPalette::Active, QPalette::Base, col);
416  QPalette pal = d->sampleEdit->palette();
418  d->sampleEdit->setPalette(pal);
419 }
420 
422 {
423  return d->m_palette.color(QPalette::Active, QPalette::Base);
424 }
425 
427 {
428  // check or uncheck or gray out the "relative" checkbox
429  if (d->sizeIsRelativeCheckBox) {
430  if (Qt::PartiallyChecked == relative) {
431  d->sizeIsRelativeCheckBox->setCheckState(Qt::PartiallyChecked);
432  } else {
433  d->sizeIsRelativeCheckBox->setCheckState((Qt::Checked == relative) ? Qt::Checked : Qt::Unchecked);
434  }
435  }
436 }
437 
439 {
440  return d->sizeIsRelativeCheckBox
441  ? d->sizeIsRelativeCheckBox->checkState()
443 }
444 
446 {
447  return d->sampleEdit->toPlainText();
448 }
449 
451 {
452  d->sampleEdit->setPlainText(text);
453 }
454 
456 {
457  d->sampleEdit->setVisible(visible);
458 }
459 
461 {
462  return minimumSizeHint();
463 }
464 
465 void KFontChooser::enableColumn(int column, bool state)
466 {
467  if (column & FamilyList) {
468  d->familyListBox->setEnabled(state);
469  }
470  if (column & StyleList) {
471  d->styleListBox->setEnabled(state);
472  }
473  if (column & SizeList) {
474  d->sizeListBox->setEnabled(state);
475  d->sizeOfFont->setEnabled(state);
476  }
477 }
478 
479 void KFontChooser::setFont(const QFont &aFont, bool onlyFixed)
480 {
481  d->selFont = aFont;
482  d->selectedSize = aFont.pointSizeF();
483  if (d->selectedSize == -1) {
484  d->selectedSize = QFontInfo(aFont).pointSizeF();
485  }
486 
487  if (onlyFixed != d->usingFixed) {
488  d->usingFixed = onlyFixed;
489  d->fillFamilyListBox(d->usingFixed);
490  }
491  d->setupDisplay();
492 }
493 
495 {
496  FontDiffFlags diffFlags = NoFontDiffFlags;
497 
498  if (d->familyCheckbox && d->familyCheckbox->isChecked()) {
499  diffFlags |= FontDiffFamily;
500  }
501 
502  if (d->styleCheckbox && d->styleCheckbox->isChecked()) {
503  diffFlags |= FontDiffStyle;
504  }
505 
506  if (d->sizeCheckbox && d->sizeCheckbox->isChecked()) {
507  diffFlags |= FontDiffSize;
508  }
509 
510  return diffFlags;
511 }
512 
514 {
515  return d->selFont;
516 }
517 
518 void KFontChooser::Private::_k_family_chosen_slot(const QString &family)
519 {
520  if (!signalsAllowed) {
521  return;
522  }
523  signalsAllowed = false;
524 
525  QString currentFamily;
526  if (family.isEmpty()) {
527  Q_ASSERT(familyListBox->currentItem());
528  if (familyListBox->currentItem()) {
529  currentFamily = qtFamilies[familyListBox->currentItem()->text()];
530  }
531  } else {
532  currentFamily = qtFamilies[family];
533  }
534 
535  // Get the list of styles available in this family.
536  QFontDatabase dbase;
537  QStringList styles = dbase.styles(currentFamily);
538  if (styles.isEmpty()) {
539  // Avoid extraction, it is in kdeqt.po
540  styles.append(TR_NOX("Normal", "QFontDatabase"));
541  }
542 
543  // Filter style strings and add to the listbox.
544  QString pureFamily;
545  splitFontString(family, &pureFamily);
546  QStringList filteredStyles;
547  qtStyles.clear();
548  styleIDs.clear();
549 
550  const QStringList origStyles = styles;
551  for (const QString &style : origStyles) {
552  // Sometimes the font database will report an invalid style,
553  // that falls back back to another when set.
554  // Remove such styles, by checking set/get round-trip.
555  QFont testFont = dbase.font(currentFamily, style, 10);
556  if (dbase.styleString(testFont) != style) {
557  styles.removeAll(style);
558  continue;
559  }
560 
561  QString fstyle = tr("%1", "@item Font style").arg(style);
562  if (!filteredStyles.contains(fstyle)) {
563  filteredStyles.append(fstyle);
564  qtStyles.insert(fstyle, style);
565  styleIDs.insert(fstyle, styleIdentifier(testFont));
566  }
567  }
568  styleListBox->clear();
569  styleListBox->addItems(filteredStyles);
570 
571  // Try to set the current style in the listbox to that previous.
572  int listPos = filteredStyles.indexOf(selectedStyle.isEmpty() ? TR_NOX("Normal", "QFontDatabase") : selectedStyle);
573  if (listPos < 0) {
574  // Make extra effort to have Italic selected when Oblique was chosen,
575  // and vice versa, as that is what the user would probably want.
576  QString styleIt = tr("Italic", "@item font");
577  QString styleOb = tr("Oblique", "@item font");
578  for (int i = 0; i < 2; ++i) {
579  int pos = selectedStyle.indexOf(styleIt);
580  if (pos >= 0) {
581  QString style = selectedStyle;
582  style.replace(pos, styleIt.length(), styleOb);
583  listPos = filteredStyles.indexOf(style);
584  if (listPos >= 0) {
585  break;
586  }
587  }
588  qSwap(styleIt, styleOb);
589  }
590  }
591  styleListBox->setCurrentRow(listPos >= 0 ? listPos : 0);
592  QString currentStyle = qtStyles[styleListBox->currentItem()->text()];
593 
594  // Recompute the size listbox for this family/style.
595  qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
596  sizeOfFont->setValue(currentSize);
597 
598  selFont = dbase.font(currentFamily, currentStyle, int(currentSize));
599  if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) {
600  selFont.setPointSizeF(currentSize);
601  }
602  emit q->fontSelected(selFont);
603 
604  signalsAllowed = true;
605 }
606 
607 void KFontChooser::Private::_k_style_chosen_slot(const QString &style)
608 {
609  if (!signalsAllowed) {
610  return;
611  }
612  signalsAllowed = false;
613 
614  QFontDatabase dbase;
615  QString currentFamily = qtFamilies[familyListBox->currentItem()->text()];
616  QString currentStyle;
617  if (style.isEmpty()) {
618  currentStyle = qtStyles[styleListBox->currentItem()->text()];
619  } else {
620  currentStyle = qtStyles[style];
621  }
622 
623  // Recompute the size listbox for this family/style.
624  qreal currentSize = setupSizeListBox(currentFamily, currentStyle);
625  sizeOfFont->setValue(currentSize);
626 
627  selFont = dbase.font(currentFamily, currentStyle, int(currentSize));
628  if (dbase.isSmoothlyScalable(currentFamily, currentStyle) && selFont.pointSize() == floor(currentSize)) {
629  selFont.setPointSizeF(currentSize);
630  }
631  emit q->fontSelected(selFont);
632 
633  if (!style.isEmpty()) {
634  selectedStyle = currentStyle;
635  }
636 
637  signalsAllowed = true;
638 }
639 
640 void KFontChooser::Private::_k_size_chosen_slot(const QString &size)
641 {
642  if (!signalsAllowed) {
643  return;
644  }
645 
646  signalsAllowed = false;
647 
648  qreal currentSize;
649  if (size.isEmpty()) {
650  currentSize = QLocale::system().toDouble(sizeListBox->currentItem()->text());
651  } else {
652  currentSize = QLocale::system().toDouble(size);
653  }
654 
655  // Reset the customized size slot in the list if not needed.
656  if (customSizeRow >= 0 && selFont.pointSizeF() != currentSize) {
657  sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom);
658  customSizeRow = -1;
659  }
660 
661  sizeOfFont->setValue(currentSize);
662  selFont.setPointSizeF(currentSize);
663  emit q->fontSelected(selFont);
664 
665  if (!size.isEmpty()) {
666  selectedSize = currentSize;
667  }
668 
669  signalsAllowed = true;
670 }
671 
672 void KFontChooser::Private::_k_size_value_slot(double dval)
673 {
674  if (!signalsAllowed) {
675  return;
676  }
677  signalsAllowed = false;
678 
679  // We compare with qreal, so convert for platforms where qreal != double.
680  qreal val = qreal(dval);
681 
682  QFontDatabase dbase;
683  QString family = qtFamilies[familyListBox->currentItem()->text()];
684  QString style = qtStyles[styleListBox->currentItem()->text()];
685 
686  // Reset current size slot in list if it was customized.
687  if (customSizeRow >= 0 && sizeListBox->currentRow() == customSizeRow) {
688  sizeListBox->item(customSizeRow)->setText(standardSizeAtCustom);
689  customSizeRow = -1;
690  }
691 
692  bool canCustomize = true;
693 
694  // For Qt-bad-sizes workaround: skip this block unconditionally
695  if (!dbase.isSmoothlyScalable(family, style)) {
696  // Bitmap font, allow only discrete sizes.
697  // Determine the nearest in the direction of change.
698  canCustomize = false;
699  int nrows = sizeListBox->count();
700  int row = sizeListBox->currentRow();
701  int nrow;
702  if (val - selFont.pointSizeF() > 0) {
703  for (nrow = row + 1; nrow < nrows; ++nrow)
704  if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) >= val) {
705  break;
706  }
707  } else {
708  for (nrow = row - 1; nrow >= 0; --nrow)
709  if (QLocale::system().toDouble(sizeListBox->item(nrow)->text()) <= val) {
710  break;
711  }
712  }
713  // Make sure the new row is not out of bounds.
714  nrow = nrow < 0 ? 0 : nrow >= nrows ? nrows - 1 : nrow;
715  // Get the size from the new row and set the spinbox to that size.
716  val = QLocale::system().toDouble(sizeListBox->item(nrow)->text());
717  sizeOfFont->setValue(val);
718  }
719 
720  // Set the current size in the size listbox.
721  int row = nearestSizeRow(val, canCustomize);
722  sizeListBox->setCurrentRow(row);
723 
724  selectedSize = val;
725  selFont.setPointSizeF(val);
726  emit q->fontSelected(selFont);
727 
728  signalsAllowed = true;
729 }
730 
731 void KFontChooser::Private::_k_displaySample(const QFont &font)
732 {
733  sampleEdit->setFont(font);
734  //sampleEdit->setCursorPosition(0);
735 
736  //QFontInfo a = QFontInfo(font);
737  //qCDebug(KWidgetsAddonsLog) << "font: " << a.family () << ", " << a.pointSize ();
738  //qCDebug(KWidgetsAddonsLog) << " (" << font.toString() << ")\n";
739 }
740 
741 int KFontChooser::Private::nearestSizeRow(qreal val, bool customize)
742 {
743  qreal diff = 1000;
744  int row = 0;
745  for (int r = 0; r < sizeListBox->count(); ++r) {
746  qreal cval = QLocale::system().toDouble(sizeListBox->item(r)->text());
747  if (qAbs(cval - val) < diff) {
748  diff = qAbs(cval - val);
749  row = r;
750  }
751  }
752  // For Qt-bad-sizes workaround: ignore value of customize, use true
753  if (customize && diff > 0) {
754  customSizeRow = row;
755  standardSizeAtCustom = sizeListBox->item(row)->text();
756  sizeListBox->item(row)->setText(formatFontSize(val));
757  }
758  return row;
759 }
760 
761 qreal KFontChooser::Private::fillSizeList(const QList<qreal> &sizes_)
762 {
763  if (!sizeListBox) {
764  return 0; //assertion.
765  }
766 
767  QList<qreal> sizes = sizes_;
768  bool canCustomize = false;
769  if (sizes.isEmpty()) {
770  static const int c[] = {
771  4, 5, 6, 7,
772  8, 9, 10, 11,
773  12, 13, 14, 15,
774  16, 17, 18, 19,
775  20, 22, 24, 26,
776  28, 32, 48, 64,
777  72, 80, 96, 128,
778  0
779  };
780  for (int i = 0; c[i]; ++i) {
781  sizes.append(c[i]);
782  }
783  // Since sizes were not supplied, this is a vector font,
784  // and size slot customization is allowed.
785  canCustomize = true;
786  }
787 
788  // Insert sizes into the listbox.
789  sizeListBox->clear();
790  std::sort(sizes.begin(), sizes.end());
791  for (qreal size : qAsConst(sizes)) {
792  sizeListBox->addItem(formatFontSize(size));
793  }
794 
795  // Return the nearest to selected size.
796  // If the font is vector, the nearest size is always same as selected,
797  // thus size slot customization is allowed.
798  // If the font is bitmap, the nearest size need not be same as selected,
799  // thus size slot customization is not allowed.
800  customSizeRow = -1;
801  int row = nearestSizeRow(selectedSize, canCustomize);
802  return QLocale::system().toDouble(sizeListBox->item(row)->text());
803 }
804 
805 qreal KFontChooser::Private::setupSizeListBox(const QString &family, const QString &style)
806 {
807  QFontDatabase dbase;
808  QList<qreal> sizes;
809  const bool smoothlyScalable = dbase.isSmoothlyScalable(family, style);
810  if (!smoothlyScalable) {
811  const QList<int> smoothSizes = dbase.smoothSizes(family, style);
812  for (int size : smoothSizes) {
813  sizes.append(size);
814  }
815  }
816 
817  // Fill the listbox (uses default list of sizes if the given is empty).
818  // Collect the best fitting size to selected size, to use if not smooth.
819  qreal bestFitSize = fillSizeList(sizes);
820 
821  // Set the best fit size as current in the listbox if available.
822  const QList<QListWidgetItem *> selectedSizeList = sizeListBox->findItems(
823  formatFontSize(bestFitSize), Qt::MatchExactly);
824  if (!selectedSizeList.isEmpty()) {
825  sizeListBox->setCurrentItem(selectedSizeList.first());
826  }
827 
828  return bestFitSize;
829 }
830 
831 void KFontChooser::Private::setupDisplay()
832 {
833  QFontDatabase dbase;
834  QString family = selFont.family().toLower();
835  QString styleID = styleIdentifier(selFont);
836  qreal size = selFont.pointSizeF();
837  if (size == -1) {
838  size = QFontInfo(selFont).pointSizeF();
839  }
840 
841  int numEntries, i;
842 
843  // Direct family match.
844  numEntries = familyListBox->count();
845  for (i = 0; i < numEntries; ++i) {
846  if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) {
847  familyListBox->setCurrentRow(i);
848  break;
849  }
850  }
851 
852  // 1st family fallback.
853  if (i == numEntries) {
854  const int bracketPos = family.indexOf(QLatin1Char('['));
855  if (bracketPos != -1) {
856  family = family.leftRef(bracketPos).trimmed().toString();
857  for (i = 0; i < numEntries; ++i) {
858  if (family == qtFamilies[familyListBox->item(i)->text()].toLower()) {
859  familyListBox->setCurrentRow(i);
860  break;
861  }
862  }
863  }
864  }
865 
866  // 2nd family fallback.
867  if (i == numEntries) {
868  QString fallback = family + QLatin1String(" [");
869  for (i = 0; i < numEntries; ++i) {
870  if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(fallback)) {
871  familyListBox->setCurrentRow(i);
872  break;
873  }
874  }
875  }
876 
877  // 3rd family fallback.
878  if (i == numEntries) {
879  for (i = 0; i < numEntries; ++i) {
880  if (qtFamilies[familyListBox->item(i)->text()].toLower().startsWith(family)) {
881  familyListBox->setCurrentRow(i);
882  break;
883  }
884  }
885  }
886 
887  // Family fallback in case nothing matched. Otherwise, diff doesn't work
888  if (i == numEntries) {
889  familyListBox->setCurrentRow(0);
890  }
891 
892  // By setting the current item in the family box, the available
893  // styles and sizes for that family have been collected.
894  // Try now to set the current items in the style and size boxes.
895 
896  // Set current style in the listbox.
897  numEntries = styleListBox->count();
898  for (i = 0; i < numEntries; ++i) {
899  if (styleID == styleIDs[styleListBox->item(i)->text()]) {
900  styleListBox->setCurrentRow(i);
901  break;
902  }
903  }
904  if (i == numEntries) {
905  // Style not found, fallback.
906  styleListBox->setCurrentRow(0);
907  }
908 
909  // Set current size in the listbox.
910  // If smoothly scalable, allow customizing one of the standard size slots,
911  // otherwise just select the nearest available size.
912  QString currentFamily = qtFamilies[familyListBox->currentItem()->text()];
913  QString currentStyle = qtStyles[styleListBox->currentItem()->text()];
914  bool canCustomize = dbase.isSmoothlyScalable(currentFamily, currentStyle);
915  sizeListBox->setCurrentRow(nearestSizeRow(size, canCustomize));
916 
917  // Set current size in the spinbox.
918  sizeOfFont->setValue(QLocale::system().toDouble(sizeListBox->currentItem()->text()));
919 }
920 
921 void KFontChooser::getFontList(QStringList &list, uint fontListCriteria)
922 {
923  QFontDatabase dbase;
924  QStringList lstSys(dbase.families());
925 
926  // if we have criteria; then check fonts before adding
927  if (fontListCriteria) {
928  QStringList lstFonts;
929  for (const QString &family : qAsConst(lstSys)) {
930  if ((fontListCriteria & FixedWidthFonts) > 0 && !dbase.isFixedPitch(family)) {
931  continue;
932  }
933  if (((fontListCriteria & (SmoothScalableFonts | ScalableFonts)) == ScalableFonts) &&
934  !dbase.isBitmapScalable(family)) {
935  continue;
936  }
937  if ((fontListCriteria & SmoothScalableFonts) > 0 && !dbase.isSmoothlyScalable(family)) {
938  continue;
939  }
940  lstFonts.append(family);
941  }
942 
943  if ((fontListCriteria & FixedWidthFonts) > 0) {
944  // Fallback.. if there are no fixed fonts found, it's probably a
945  // bug in the font server or Qt. In this case, just use 'fixed'
946  if (lstFonts.isEmpty()) {
947  lstFonts.append(QStringLiteral("fixed"));
948  }
949  }
950 
951  lstSys = lstFonts;
952  }
953 
954  lstSys.sort();
955 
956  list = lstSys;
957 }
958 
959 void KFontChooser::Private::setFamilyBoxItems(const QStringList &fonts)
960 {
961  signalsAllowed = false;
962 
963  QStringList trfonts = translateFontNameList(fonts, &qtFamilies);
964  familyListBox->clear();
965  familyListBox->addItems(trfonts);
966 
967  signalsAllowed = true;
968 }
969 
970 void KFontChooser::Private::fillFamilyListBox(bool onlyFixedFonts)
971 {
972  QStringList fontList;
973  getFontList(fontList, onlyFixedFonts ? FixedWidthFonts : 0);
974  setFamilyBoxItems(fontList);
975 }
976 
977 // Human-readable style identifiers returned by QFontDatabase::styleString()
978 // do not always survive round trip of QFont serialization/deserialization,
979 // causing wrong style in the style box to be highlighted when
980 // the chooser dialog is opened. This will cause the style to be changed
981 // when the dialog is closed and the user did not touch the style box.
982 // Hence, construct custom style identifiers sufficient for the purpose.
983 QString KFontChooser::Private::styleIdentifier(const QFont &font)
984 {
985  const int weight = font.weight();
986  QString styleName = font.styleName();
987  // If the styleName property is empty and the weight is QFont::Normal, that
988  // could mean it's a "Regular"-like style with the styleName part stripped
989  // so that subsequent calls to setBold(true) can work properly (i.e. selecting
990  // the "Bold" style provided by the font itself) without resorting to font
991  // "emboldening" which looks ugly.
992  // See also KConfigGroupGui::writeEntryGui().
993  if (styleName.isEmpty() && weight == QFont::Normal) {
994  QFontDatabase fdb;
995  const QStringList styles = fdb.styles(font.family());
996  for (const QString &style : styles) {
997  // orderded by commonness, i.e. "Regular" is the most common
998  if (style == QLatin1String("Regular")
999  || style == QLatin1String("Normal")
1000  || style == QLatin1String("Book")
1001  || style == QLatin1String("Roman")) {
1002  styleName = style;
1003  } else {
1004  // nothing more we can do
1005  }
1006  }
1007  }
1008 
1009  const QChar comma(QLatin1Char(','));
1010  return QString::number(weight) + comma
1011  + QString::number((int)font.style()) + comma
1012  + QString::number(font.stretch()) + comma
1013  + styleName;
1014 }
1015 
1016 #include "moc_kfontchooser.cpp"
void clear()
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
virtual QSize sizeHint() const const override
void setContentsMargins(int left, int top, int right, int bottom)
int width() const const
QCursor cursor() const const
QColor color() const
MatchExactly
QString toString(qlonglong i) const const
void setColor(QPalette::ColorGroup group, QPalette::ColorRole role, const QColor &color)
void addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
Identifies a requested change in the font style.
Definition: kfontchooser.h:59
qreal pointSizeF() const const
Identifies a requested change in the font family.
Definition: kfontchooser.h:58
QSize sizeHint(void) const override
Reimplemented for internal reasons.
int weight() const const
QStyle * style() const const
QString toString() const const
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const =0
static void getFontList(QStringList &list, uint fontListCriteria)
Creates a list of font strings.
Identifies the style (center) list.
Definition: kfontchooser.h:48
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
int stretch() const const
bool isSmoothlyScalable(const QString &family, const QString &style) const const
void setColor(const QColor &col)
Sets the color to use in the preview.
virtual QSize minimumSizeHint() const const
QRect visualItemRect(const QListWidgetItem *item) const const
void addSpacing(int size)
QString tr(const char *sourceText, const char *disambiguation, int n)
double toDouble(const QString &s, bool *ok) const const
Qt::CheckState sizeIsRelative() const
AlignLeft
QStringList styles(const QString &family) const const
void setSampleText(const QString &text)
Sets the sample text.
Identifies the family (leftmost) list.
Definition: kfontchooser.h:47
QStringRef trimmed() const const
FontDiffFlags fontDiffFlags() const
QLocale system()
When included only return smooth scalable fonts.
Definition: kfontchooser.h:237
QString styleName() const const
QColor backgroundColor() const
void setEnabled(bool)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QString number(int n, int base)
void enableColumn(int column, bool state)
Enables or disable a font column in the chooser.
void append(const T &value)
Display the font differences interfaces.
Definition: kfontchooser.h:77
void setFont(const QFont &font, bool onlyFixed=false)
Sets the currently selected font in the chooser.
Only show monospaced/fixed-width fonts, excluding proportional fonts, (the checkbox to toggle showing...
Definition: kfontchooser.h:74
Identifies the size (rightmost) list.
Definition: kfontchooser.h:49
void valueChanged(double d)
QStringRef leftRef(int n) const const
bool isEmpty() const const
bool isEmpty() const const
int removeAll(const T &value)
QPoint pos() const const
Identifies a requested change in the font size.
Definition: kfontchooser.h:60
T & first()
When included only scalable fonts are returned; certain configurations allow bitmap fonts to remain u...
Definition: kfontchooser.h:231
QListWidgetItem * item(int row) const const
QScrollBar * verticalScrollBar() const const
int indexOf(QStringView str, int from) const const
void setSizeIsRelative(Qt::CheckState relative)
Sets the state of the checkbox indicating whether the font size is to be interpreted as relative size...
QList< int > smoothSizes(const QString &family, const QString &styleName)
~KFontChooser() override
Destructs the font chooser.
QString styleString(const QFont &font)
QList::iterator end()
QString toLower() const const
void setVerticalSpacing(int spacing)
int horizontalAdvance(const QString &text, int len) const const
void setPointSizeF(qreal pointSize)
QCA_EXPORT void init()
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
QString & replace(int position, int n, QChar after)
QFont systemFont(QFontDatabase::SystemFont type)
PM_LayoutVerticalSpacing
A font selection widget.
Definition: kfontchooser.h:33
QString sampleText() const
QFontMetrics fontMetrics() const const
void currentTextChanged(const QString &currentText)
QStringList families(QFontDatabase::WritingSystem writingSystem) const const
void setColumnStretch(int column, int stretch)
int count() const const
QString family() const const
void setBackgroundColor(const QColor &col)
Sets the background color to use in the preview.
void fontSelected(const QFont &font)
Emitted whenever the selected font changes.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void toggled(bool checked)
QWidget(QWidget *parent, Qt::WindowFlags f)
int length() const const
qreal pointSizeF() const const
When included only fixed-width fonts are returned.
Definition: kfontchooser.h:225
CheckState
void sort(Qt::CaseSensitivity cs)
bool isBitmapScalable(const QString &family, const QString &style) const const
bool isFixedPitch(const QString &family, const QString &style) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QFont font(const QString &family, const QString &style, int pointSize) const const
void setSampleBoxVisible(bool visible)
Shows or hides the sample text box.
Show a visual frame around the chooser.
Definition: kfontchooser.h:76
QFont font() const
QList::iterator begin()
int lineSpacing() const const
KFontChooser(QWidget *parent=nullptr, const DisplayFlags &flags=DisplayFrame, const QStringList &fontList=QStringList(), int visibleListSize=8, Qt::CheckState *sizeIsRelativeState=nullptr)
Constructs a font picker widget.
QFont::Style style() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 5 2020 22:42:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.