KDEUI
kfontcombobox.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 00003 Copyright (C) 2008 Chusslove Illich <caslav.ilic@gmx.net> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kfontcombobox.h" 00022 #include "fonthelpers_p.h" 00023 00024 #include "kdebug.h" 00025 #include "klocale.h" 00026 #include "kcolorscheme.h" 00027 #include "kglobalsettings.h" 00028 #include "kfontchooser.h" 00029 #include "kcompletion.h" 00030 00031 #include <QEvent> 00032 #include <QListView> 00033 #include <QFontDatabase> 00034 #include <QIcon> 00035 #include <QAbstractItemDelegate> 00036 #include <QStringListModel> 00037 #include <QPainter> 00038 #include <QList> 00039 #include <QHash> 00040 #include <QScrollBar> 00041 00042 static QString alphabetSample () 00043 { 00044 return i18nc("short", 00045 // i18n: A shorter version of the alphabet test phrase translated in 00046 // another message. It is displayed in the dropdown list of font previews 00047 // (the font selection combo box), so keep it under the length equivalent 00048 // to 60 or so proportional Latin characters. 00049 "The Quick Brown Fox Jumps Over The Lazy Dog"); 00050 } 00051 00052 class KFontFamilyDelegate : public QAbstractItemDelegate 00053 { 00054 Q_OBJECT 00055 public: 00056 explicit KFontFamilyDelegate (QObject *parent); 00057 00058 void paint (QPainter *painter, 00059 const QStyleOptionViewItem &option, 00060 const QModelIndex &index) const; 00061 00062 QSize sizeHint (const QStyleOptionViewItem &option, 00063 const QModelIndex &index) const; 00064 00065 QIcon truetype; 00066 QIcon bitmap; 00067 double sizeFactFamily; 00068 double sizeFactSample; 00069 00070 QHash<QString, QString> fontFamilyTrMap; 00071 }; 00072 00073 KFontFamilyDelegate::KFontFamilyDelegate (QObject *parent) 00074 : QAbstractItemDelegate(parent) 00075 { 00076 truetype = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fonttruetype-16.png")); 00077 bitmap = QIcon(QLatin1String(":/trolltech/styles/commonstyle/images/fontbitmap-16.png")); 00078 00079 // Font size factors for family name and text sample in font previes, 00080 // multiplies normal font size. 00081 sizeFactFamily = 1.0; 00082 sizeFactSample = 1.0; // better leave at 1, so that user can relate sizes to default 00083 } 00084 00085 void KFontFamilyDelegate::paint (QPainter *painter, 00086 const QStyleOptionViewItem &option, 00087 const QModelIndex &index) const 00088 { 00089 QBrush sampleBrush; 00090 if (option.state & QStyle::State_Selected) { 00091 painter->save(); 00092 painter->setBrush(option.palette.highlight()); 00093 painter->setPen(Qt::NoPen); 00094 painter->drawRect(option.rect); 00095 painter->setPen(QPen(option.palette.highlightedText(), 0)); 00096 sampleBrush = option.palette.highlightedText(); 00097 } else { 00098 sampleBrush = KColorScheme(QPalette::Normal).foreground(KColorScheme::InactiveText); 00099 } 00100 00101 QFont baseFont = KGlobalSettings::generalFont(); 00102 QString trFontFamily = index.data(Qt::DisplayRole).toString(); 00103 QString fontFamily = fontFamilyTrMap[trFontFamily]; 00104 00105 // Writing systems provided by the font. 00106 QList<QFontDatabase::WritingSystem> availableSystems = QFontDatabase().writingSystems(fontFamily); 00107 00108 // Intersect font's writing systems with that specified for 00109 // the language's sample text, to see if the sample can be shown. 00110 // If the font reports no writing systems, assume it can show the sample. 00111 bool canShowLanguageSample = true; 00112 if (availableSystems.count() > 0) { 00113 canShowLanguageSample = false; 00114 QString scriptsSpec = i18nc("Numeric IDs of scripts for font previews", 00115 // i18n: Integer which indicates the script you used in the sample text 00116 // for font previews in your language. For the possible values, see 00117 // http://doc.trolltech.com/qfontdatabase.html#WritingSystem-enum 00118 // If the sample text contains several scripts, their IDs can be given 00119 // as a comma-separated list (e.g. for Japanese it is "1,27"). 00120 "1"); 00121 QStringList scriptStrIds = scriptsSpec.split(','); 00122 foreach (const QString &scriptStrId, scriptStrIds) { 00123 bool convOk; 00124 int ws = scriptStrId.toInt(&convOk); 00125 if ( convOk && ws > 0 && ws < QFontDatabase::WritingSystemsCount 00126 && availableSystems.contains(static_cast<QFontDatabase::WritingSystem>(ws))) { 00127 canShowLanguageSample = true; 00128 break; 00129 } 00130 } 00131 } 00132 00133 // Choose and paint an icon according to the font type, scalable or bitmat. 00134 const QIcon *icon = &bitmap; 00135 if (QFontDatabase().isSmoothlyScalable(fontFamily)) { 00136 icon = &truetype; 00137 } 00138 QRect r = option.rect; 00139 icon->paint(painter, r, Qt::AlignLeft|Qt::AlignTop); 00140 00141 // Claim space taken up by the icon. 00142 QSize actualSize = icon->actualSize(r.size()); 00143 if (option.direction == Qt::RightToLeft) { 00144 r.setRight(r.right() - actualSize.width() - 4); 00145 } else { 00146 r.setLeft(r.left() + actualSize.width() + 4); 00147 } 00148 00149 // Draw the font family. 00150 QFont oldPainterFont = painter->font(); 00151 QFont familyFont = baseFont; 00152 familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily); 00153 painter->setFont(familyFont); 00154 painter->drawText(r, Qt::AlignTop|Qt::AlignLeading|Qt::TextSingleLine, trFontFamily); 00155 00156 // Claim space taken up by the font family name. 00157 int h = painter->fontMetrics().lineSpacing(); 00158 r.setTop(r.top() + h); 00159 00160 // Show text sample in user's language if the writing system is supported, 00161 // otherwise show a collage of generic script samples provided by Qt. 00162 // If the font does not report what it supports, assume all. 00163 QString sample; 00164 if (canShowLanguageSample) { 00165 sample = alphabetSample(); 00166 } else { 00167 foreach (const QFontDatabase::WritingSystem &ws, availableSystems) { 00168 sample += QFontDatabase::writingSystemSample(ws) + " "; 00169 if (sample.length() > 40) { // do not let the sample be too long 00170 break; 00171 } 00172 } 00173 sample = sample.trimmed(); 00174 } 00175 QFont sampleFont; 00176 sampleFont.setFamily(fontFamily); 00177 sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample); 00178 painter->setFont(sampleFont); 00179 QPen oldPen = painter->pen(); 00180 painter->setPen(sampleBrush.color()); 00181 painter->drawText(r, Qt::AlignTop|Qt::AlignLeading|Qt::TextSingleLine, sample); 00182 painter->setFont(oldPainterFont); 00183 painter->setPen(oldPen); 00184 00185 if (option.state & QStyle::State_Selected) { 00186 painter->restore(); 00187 } 00188 } 00189 00190 QSize KFontFamilyDelegate::sizeHint (const QStyleOptionViewItem &option, 00191 const QModelIndex &index) const 00192 { 00193 Q_UNUSED(option); 00194 00195 QFont baseFont = KGlobalSettings::generalFont(); 00196 QString trFontFamily = index.data(Qt::DisplayRole).toString(); 00197 QString fontFamily = fontFamilyTrMap[trFontFamily]; 00198 00199 QFont familyFont = baseFont; 00200 familyFont.setPointSizeF(familyFont.pointSizeF() * sizeFactFamily); 00201 QFontMetrics familyMetrics(familyFont); 00202 00203 QFont sampleFont = baseFont; 00204 sampleFont.setFamily(fontFamily); 00205 sampleFont.setPointSizeF(sampleFont.pointSizeF() * sizeFactSample); 00206 QFontMetrics sampleMetrics(familyFont); 00207 QString sample = alphabetSample(); 00208 00209 // Only the hight matters here, the width is mandated by KFontComboBox::event() 00210 return QSize(qMax(familyMetrics.width(trFontFamily), sampleMetrics.width(sample)), 00211 qRound(familyMetrics.lineSpacing() + sampleMetrics.lineSpacing() * 1.2)); 00212 } 00213 00214 class KFontComboBoxPrivate 00215 { 00216 public: 00217 KFontComboBoxPrivate (KFontComboBox *parent); 00218 void updateDatabase (); 00219 void updateIndexToFont (); 00220 void _k_currentFontChanged (int index); 00221 00222 KFontComboBox *k; 00223 QFont currentFont; 00224 bool onlyFixed; 00225 bool signalsAllowed; 00226 KFontFamilyDelegate *delegate; 00227 QStringListModel *model; 00228 }; 00229 00230 KFontComboBoxPrivate::KFontComboBoxPrivate (KFontComboBox *parent) 00231 { 00232 k = parent; 00233 currentFont = KGlobalSettings::generalFont(); 00234 onlyFixed = false; 00235 signalsAllowed = true; 00236 } 00237 00238 void KFontComboBoxPrivate::updateDatabase () 00239 { 00240 QStringList fontFamilies; 00241 KFontChooser::getFontList(fontFamilies, 00242 onlyFixed ? KFontChooser::FixedWidthFonts : 0); 00243 00244 // Translate font families for the list model. 00245 delegate->fontFamilyTrMap.clear(); 00246 QStringList trFontFamilies = 00247 translateFontNameList(fontFamilies, &(delegate->fontFamilyTrMap)); 00248 00249 // Add families to the list model and completion. 00250 model->setStringList(trFontFamilies); 00251 KCompletion *completion = k->completionObject(); 00252 if (completion) { 00253 completion->insertItems(trFontFamilies); 00254 completion->setIgnoreCase(true); 00255 } 00256 } 00257 00258 void KFontComboBoxPrivate::updateIndexToFont () 00259 { 00260 // QFontInfo necessary to return the family with proper casing. 00261 QString selectedFontFamily = QFontInfo(currentFont).family(); 00262 QString trSelectedFontFamily = translateFontName(selectedFontFamily); 00263 const QStringList trFontFamilies = model->stringList(); 00264 if (!trFontFamilies.count()) { 00265 return; 00266 } 00267 00268 // Match the font's family with an item in the list. 00269 int index = 0; 00270 foreach (const QString &trFontFamily, trFontFamilies) { 00271 if (trSelectedFontFamily == trFontFamily) { 00272 break; 00273 } 00274 ++index; 00275 } 00276 if (index == trFontFamilies.count()) { 00277 // If no family matched, change font to first on the list. 00278 index = 0; 00279 currentFont = QFont(delegate->fontFamilyTrMap[trFontFamilies[0]]); 00280 emit k->currentFontChanged(currentFont); 00281 } 00282 00283 // Set the new list item. 00284 signalsAllowed = false; 00285 k->setCurrentIndex(index); 00286 signalsAllowed = true; 00287 } 00288 00289 void KFontComboBoxPrivate::_k_currentFontChanged (int index) 00290 { 00291 if (!signalsAllowed) { 00292 return; 00293 } 00294 00295 QString trFontFamily = k->itemText(index); 00296 QString fontFamily = delegate->fontFamilyTrMap[trFontFamily]; 00297 if (!fontFamily.isEmpty()) { 00298 currentFont = QFont(fontFamily); 00299 emit k->currentFontChanged(currentFont); 00300 } else { 00301 // Unknown font family given. Just remove from the list. 00302 // This should not happen, as adding arbitrary font names is prevented. 00303 QStringList lst = model->stringList(); 00304 lst.removeAll(trFontFamily); 00305 model->setStringList(lst); 00306 } 00307 } 00308 00309 KFontComboBox::KFontComboBox (QWidget *parent) 00310 : KComboBox(true, parent), d(new KFontComboBoxPrivate(this)) 00311 { 00312 // Inputing arbitrary font names does not make sense. 00313 setInsertPolicy(QComboBox::NoInsert); 00314 00315 // Special list item painter showing font previews and its list model. 00316 d->delegate = new KFontFamilyDelegate(this); 00317 setItemDelegate(d->delegate); 00318 d->model = new QStringListModel(this); 00319 setModel(d->model); 00320 00321 // Set current font when a new family has been chosen in the combo. 00322 connect(this, SIGNAL(currentIndexChanged(int)), 00323 this, SLOT(_k_currentFontChanged(int))); 00324 00325 // Initialize font selection and list of available fonts. 00326 d->updateDatabase(); 00327 d->updateIndexToFont(); 00328 } 00329 00330 KFontComboBox::~KFontComboBox () 00331 { 00332 delete d; 00333 } 00334 00335 void KFontComboBox::setOnlyFixed (bool onlyFixed) 00336 { 00337 if (onlyFixed != d->onlyFixed) { 00338 d->onlyFixed = onlyFixed; 00339 d->updateDatabase(); 00340 } 00341 } 00342 00343 QFont KFontComboBox::currentFont () const 00344 { 00345 return d->currentFont; 00346 } 00347 00348 void KFontComboBox::setCurrentFont (const QFont &font) 00349 { 00350 if (font != d->currentFont) { 00351 d->currentFont = font; 00352 emit currentFontChanged(d->currentFont); 00353 d->updateIndexToFont(); 00354 } 00355 } 00356 00357 bool KFontComboBox::event (QEvent *e) 00358 { 00359 if (e->type() == QEvent::Resize) { 00360 QListView *lview = qobject_cast<QListView*>(view()); 00361 if (lview) { 00362 QString sample = alphabetSample(); 00363 // Limit text sample length to avoid too wide list view. 00364 if (sample.length() > 60) { 00365 sample = sample.left(57) + "..."; 00366 } 00367 QFont approxFont = KGlobalSettings::generalFont(); 00368 approxFont.setPointSizeF(approxFont.pointSizeF() 00369 * d->delegate->sizeFactSample); 00370 int widgetWidth = width(); 00371 int sampleWidth = QFontMetrics(approxFont).width(sample); 00372 sampleWidth = qRound(sampleWidth * 1.1); // extra for wider fonts 00373 int iconWidth = d->delegate->truetype.actualSize(size()).width(); 00374 int vsbarWidth = 0; 00375 if (lview->verticalScrollBar()) { 00376 vsbarWidth = lview->verticalScrollBar()->width(); 00377 } 00378 lview->window()->setFixedWidth( qMax(widgetWidth, sampleWidth) 00379 + iconWidth + vsbarWidth); 00380 } 00381 } 00382 return KComboBox::event(e); 00383 } 00384 00385 QSize KFontComboBox::sizeHint() const 00386 { 00387 QSize sz = KComboBox::sizeHint(); 00388 QFontMetrics fm(KGlobalSettings::generalFont()); 00389 sz.setWidth(fm.width("m") * 14); 00390 return sz; 00391 } 00392 00393 #include "kfontcombobox.moc" 00394 #include "moc_kfontcombobox.moc" 00395
KDE 4.6 API Reference