File indexing completed on 2025-01-26 05:08:07
0001 /* 0002 SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org> 0003 SPDX-FileCopyrightText: 2019 Guo Yunhe <i@guoyunhe.me> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "FontFilter.h" 0009 #include "FontFilterProxyStyle.h" 0010 #include "FontList.h" 0011 #include <KIconLoader> 0012 #include <KSelectAction> 0013 #include <KToggleAction> 0014 #include <QApplication> 0015 #include <QLabel> 0016 #include <QMimeDatabase> 0017 #include <QMouseEvent> 0018 #include <QPainter> 0019 #include <QSet> 0020 #include <QString> 0021 #include <QStyleOption> 0022 0023 namespace KFI 0024 { 0025 static void deselectCurrent(QActionGroup *act) 0026 { 0027 QAction *prev(act->checkedAction()); 0028 if (prev) { 0029 prev->setChecked(false); 0030 } 0031 } 0032 0033 static void deselectCurrent(KSelectAction *act) 0034 { 0035 deselectCurrent(act->selectableActionGroup()); 0036 } 0037 0038 // FIXME: Go back to using StyleSheets instead of a proxy style 0039 // once Qt has been fixed not to mess with widget font when 0040 // using StyleSheets 0041 class CFontFilterStyle : public CFontFilterProxyStyle 0042 { 0043 public: 0044 CFontFilterStyle(CFontFilter *parent, int ol) 0045 : CFontFilterProxyStyle(parent) 0046 , overlap(ol) 0047 { 0048 } 0049 0050 QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override; 0051 0052 int overlap; 0053 }; 0054 0055 QRect CFontFilterStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const 0056 { 0057 if (SE_LineEditContents == element) { 0058 QRect rect(style()->subElementRect(SE_LineEditContents, option, widget)); 0059 0060 return rect.adjusted(overlap, 0, -overlap, 0); 0061 } 0062 0063 return CFontFilterProxyStyle::subElementRect(element, option, widget); 0064 } 0065 0066 struct SortAction { 0067 SortAction(QAction *a) 0068 : action(a) 0069 { 0070 } 0071 bool operator<(const SortAction &o) const 0072 { 0073 return action->text().localeAwareCompare(o.action->text()) < 0; 0074 } 0075 QAction *action; 0076 }; 0077 0078 static void sortActions(KSelectAction *group) 0079 { 0080 if (group->actions().count() > 1) { 0081 QList<QAction *> actions = group->actions(); 0082 QList<QAction *>::ConstIterator it(actions.constBegin()), end(actions.constEnd()); 0083 QList<SortAction> sorted; 0084 0085 for (; it != end; ++it) { 0086 sorted.append(SortAction(*it)); 0087 group->removeAction(*it); 0088 } 0089 0090 std::sort(sorted.begin(), sorted.end()); 0091 QList<SortAction>::ConstIterator s(sorted.constBegin()), sEnd(sorted.constEnd()); 0092 0093 for (; s != sEnd; ++s) { 0094 group->addAction((*s).action); 0095 } 0096 } 0097 } 0098 0099 CFontFilter::CFontFilter(QWidget *parent) 0100 : QWidget(parent) 0101 { 0102 m_icons[CRIT_FAMILY] = QIcon::fromTheme("draw-text"); 0103 m_texts[CRIT_FAMILY] = i18n("Family"); 0104 m_icons[CRIT_STYLE] = QIcon::fromTheme("format-text-bold"); 0105 m_texts[CRIT_STYLE] = i18n("Style"); 0106 m_icons[CRIT_FOUNDRY] = QIcon::fromTheme("user-identity"); 0107 m_texts[CRIT_FOUNDRY] = i18n("Foundry"); 0108 m_icons[CRIT_FONTCONFIG] = QIcon::fromTheme("system-search"); 0109 m_texts[CRIT_FONTCONFIG] = i18n("FontConfig Match"); 0110 m_icons[CRIT_FILETYPE] = QIcon::fromTheme("preferences-desktop-font-installer"); 0111 m_texts[CRIT_FILETYPE] = i18n("File Type"); 0112 m_icons[CRIT_FILENAME] = QIcon::fromTheme("application-x-font-type1"); 0113 m_texts[CRIT_FILENAME] = i18n("File Name"); 0114 m_icons[CRIT_LOCATION] = QIcon::fromTheme("folder"); 0115 m_texts[CRIT_LOCATION] = i18n("File Location"); 0116 m_icons[CRIT_WS] = QIcon::fromTheme("character-set"); 0117 m_texts[CRIT_WS] = i18n("Writing System"); 0118 0119 m_layout = new QHBoxLayout(this); 0120 setLayout(m_layout); 0121 0122 m_lineEdit = new QLineEdit(this); 0123 m_lineEdit->setClearButtonEnabled(true); 0124 m_layout->addWidget(m_lineEdit); 0125 0126 m_menuButton = new QPushButton(this); 0127 m_menuButton->setIcon(QIcon::fromTheme("view-filter")); 0128 m_menuButton->setText(i18n("Set Criteria")); 0129 m_layout->addWidget(m_menuButton); 0130 0131 connect(m_lineEdit, &QLineEdit::textChanged, this, &CFontFilter::textChanged); 0132 0133 m_menu = new QMenu(this); 0134 m_menuButton->setMenu(m_menu); 0135 0136 m_actionGroup = new QActionGroup(this); 0137 addAction(CRIT_FAMILY, true); 0138 addAction(CRIT_STYLE, false); 0139 0140 KSelectAction *foundryMenu = new KSelectAction(m_icons[CRIT_FOUNDRY], m_texts[CRIT_FOUNDRY], this); 0141 m_actions[CRIT_FOUNDRY] = foundryMenu; 0142 m_menu->addAction(m_actions[CRIT_FOUNDRY]); 0143 foundryMenu->setData((int)CRIT_FOUNDRY); 0144 foundryMenu->setVisible(false); 0145 connect(foundryMenu, &KSelectAction::textTriggered, this, &CFontFilter::foundryChanged); 0146 0147 addAction(CRIT_FONTCONFIG, false); 0148 0149 KSelectAction *ftMenu = new KSelectAction(m_icons[CRIT_FILETYPE], m_texts[CRIT_FILETYPE], this); 0150 m_actions[CRIT_FILETYPE] = ftMenu; 0151 m_menu->addAction(m_actions[CRIT_FILETYPE]); 0152 ftMenu->setData((int)CRIT_FILETYPE); 0153 0154 QStringList::ConstIterator it(CFontList::fontMimeTypes.constBegin()), end(CFontList::fontMimeTypes.constEnd()); 0155 QMimeDatabase db; 0156 for (; it != end; ++it) { 0157 if ((*it) != "application/vnd.kde.fontspackage") { 0158 QMimeType mime = db.mimeTypeForName(*it); 0159 0160 KToggleAction *act = new KToggleAction(QIcon::fromTheme(mime.iconName()), mime.comment(), this); 0161 0162 ftMenu->addAction(act); 0163 act->setChecked(false); 0164 0165 QStringList mimes; 0166 foreach (QString pattern, mime.globPatterns()) 0167 mimes.append(pattern.remove(QStringLiteral("*."))); 0168 act->setData(mimes); 0169 } 0170 } 0171 0172 sortActions(ftMenu); 0173 connect(ftMenu, &KSelectAction::textTriggered, this, &CFontFilter::ftChanged); 0174 m_currentFileTypes.clear(); 0175 0176 addAction(CRIT_FILENAME, false); 0177 addAction(CRIT_LOCATION, false); 0178 0179 KSelectAction *wsMenu = new KSelectAction(m_icons[CRIT_WS], m_texts[CRIT_WS], this); 0180 m_actions[CRIT_WS] = wsMenu; 0181 m_menu->addAction(m_actions[CRIT_WS]); 0182 wsMenu->setData((int)CRIT_WS); 0183 0184 m_currentWs = QFontDatabase::Any; 0185 for (int i = QFontDatabase::Latin; i < QFontDatabase::WritingSystemsCount; ++i) { 0186 KToggleAction *wsAct = 0187 new KToggleAction(QFontDatabase::Other == i ? i18n("Symbol/Other") : QFontDatabase::writingSystemName((QFontDatabase::WritingSystem)i), this); 0188 0189 wsMenu->addAction(wsAct); 0190 wsAct->setChecked(false); 0191 wsAct->setData(i); 0192 } 0193 sortActions(wsMenu); 0194 connect(wsMenu, &KSelectAction::textTriggered, this, &CFontFilter::wsChanged); 0195 0196 setCriteria(CRIT_FAMILY); 0197 setStyle(new CFontFilterStyle(this, m_menuButton->width())); 0198 } 0199 0200 void CFontFilter::setFoundries(const QSet<QString> ¤tFoundries) 0201 { 0202 QAction *act(((KSelectAction *)m_actions[CRIT_FOUNDRY])->currentAction()); 0203 QString prev(act && act->isChecked() ? act->text() : QString()); 0204 bool changed(false); 0205 QList<QAction *> prevFoundries(((KSelectAction *)m_actions[CRIT_FOUNDRY])->actions()); 0206 QList<QAction *>::ConstIterator fIt(prevFoundries.constBegin()), fEnd(prevFoundries.constEnd()); 0207 QSet<QString> foundries(currentFoundries); 0208 0209 // Determine which of 'foundries' are new ones, and which old ones need to be removed... 0210 for (; fIt != fEnd; ++fIt) { 0211 if (foundries.contains((*fIt)->text())) { 0212 foundries.remove((*fIt)->text()); 0213 } else { 0214 ((KSelectAction *)m_actions[CRIT_FOUNDRY])->removeAction(*fIt); 0215 (*fIt)->deleteLater(); 0216 changed = true; 0217 } 0218 } 0219 0220 if (!foundries.isEmpty()) { 0221 // Add foundries to menu - replacing '&' with '&&', as '&' is taken to be 0222 // a shortcut! 0223 QSet<QString>::ConstIterator it(foundries.begin()), end(foundries.end()); 0224 0225 for (; it != end; ++it) { 0226 QString foundry(*it); 0227 0228 foundry.replace("&", "&&"); 0229 ((KSelectAction *)m_actions[CRIT_FOUNDRY])->addAction(foundry); 0230 } 0231 changed = true; 0232 } 0233 0234 if (changed) { 0235 sortActions((KSelectAction *)m_actions[CRIT_FOUNDRY]); 0236 if (!prev.isEmpty()) { 0237 act = ((KSelectAction *)m_actions[CRIT_FOUNDRY])->action(prev); 0238 if (act) { 0239 ((KSelectAction *)m_actions[CRIT_FOUNDRY])->setCurrentAction(act); 0240 } else { 0241 ((KSelectAction *)m_actions[CRIT_FOUNDRY])->setCurrentItem(0); 0242 } 0243 } 0244 0245 m_actions[CRIT_FOUNDRY]->setVisible(((KSelectAction *)m_actions[CRIT_FOUNDRY])->actions().count()); 0246 } 0247 } 0248 0249 void CFontFilter::filterChanged() 0250 { 0251 QAction *act(m_actionGroup->checkedAction()); 0252 0253 if (act) { 0254 ECriteria crit((ECriteria)act->data().toInt()); 0255 0256 if (m_currentCriteria != crit) { 0257 deselectCurrent((KSelectAction *)m_actions[CRIT_FOUNDRY]); 0258 deselectCurrent((KSelectAction *)m_actions[CRIT_FILETYPE]); 0259 deselectCurrent((KSelectAction *)m_actions[CRIT_WS]); 0260 m_lineEdit->setText(QString()); 0261 m_currentWs = QFontDatabase::Any; 0262 m_currentFileTypes.clear(); 0263 0264 setCriteria(crit); 0265 m_lineEdit->setPlaceholderText(i18n("Filter by %1…", act->text())); 0266 m_lineEdit->setReadOnly(false); 0267 } 0268 } 0269 } 0270 0271 void CFontFilter::ftChanged(const QString &ft) 0272 { 0273 deselectCurrent((KSelectAction *)m_actions[CRIT_FOUNDRY]); 0274 deselectCurrent((KSelectAction *)m_actions[CRIT_WS]); 0275 deselectCurrent(m_actionGroup); 0276 0277 QAction *act(((KSelectAction *)m_actions[CRIT_FILETYPE])->currentAction()); 0278 0279 if (act) { 0280 m_currentFileTypes = act->data().toStringList(); 0281 } 0282 m_currentCriteria = CRIT_FILETYPE; 0283 m_lineEdit->setReadOnly(true); 0284 setCriteria(m_currentCriteria); 0285 m_lineEdit->setText(ft); 0286 m_lineEdit->setPlaceholderText(m_lineEdit->text()); 0287 } 0288 0289 void CFontFilter::wsChanged(const QString &writingSystemName) 0290 { 0291 deselectCurrent((KSelectAction *)m_actions[CRIT_FOUNDRY]); 0292 deselectCurrent((KSelectAction *)m_actions[CRIT_FILETYPE]); 0293 deselectCurrent(m_actionGroup); 0294 0295 QAction *act(((KSelectAction *)m_actions[CRIT_WS])->currentAction()); 0296 0297 if (act) { 0298 m_currentWs = (QFontDatabase::WritingSystem)act->data().toInt(); 0299 } 0300 m_currentCriteria = CRIT_WS; 0301 m_lineEdit->setReadOnly(true); 0302 setCriteria(m_currentCriteria); 0303 m_lineEdit->setText(writingSystemName); 0304 m_lineEdit->setPlaceholderText(m_lineEdit->text()); 0305 } 0306 0307 void CFontFilter::foundryChanged(const QString &foundry) 0308 { 0309 deselectCurrent((KSelectAction *)m_actions[CRIT_WS]); 0310 deselectCurrent((KSelectAction *)m_actions[CRIT_FILETYPE]); 0311 deselectCurrent(m_actionGroup); 0312 0313 m_currentCriteria = CRIT_FOUNDRY; 0314 m_lineEdit->setReadOnly(true); 0315 m_lineEdit->setText(foundry); 0316 m_lineEdit->setPlaceholderText(m_lineEdit->text()); 0317 setCriteria(m_currentCriteria); 0318 } 0319 0320 void CFontFilter::textChanged(const QString &text) 0321 { 0322 Q_EMIT queryChanged(text); 0323 } 0324 0325 void CFontFilter::addAction(ECriteria crit, bool on) 0326 { 0327 m_actions[crit] = new KToggleAction(m_icons[crit], m_texts[crit], this); 0328 m_menu->addAction(m_actions[crit]); 0329 m_actionGroup->addAction(m_actions[crit]); 0330 m_actions[crit]->setData((int)crit); 0331 m_actions[crit]->setChecked(on); 0332 if (on) { 0333 m_lineEdit->setPlaceholderText(i18n("Filter by %1…", m_texts[crit])); 0334 } 0335 connect(m_actions[crit], &QAction::toggled, this, &CFontFilter::filterChanged); 0336 } 0337 0338 void CFontFilter::setCriteria(ECriteria crit) 0339 { 0340 m_currentCriteria = crit; 0341 0342 Q_EMIT criteriaChanged(crit, ((qulonglong)1) << (int)m_currentWs, m_currentFileTypes); 0343 } 0344 0345 }