File indexing completed on 2024-12-01 06:45:10
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: Stephan Kulow <coolo@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kfilefiltercombo.h" 0009 #include "kfilefilter.h" 0010 #include "kfilefiltercombo_debug.h" 0011 0012 #include <KLocalizedString> 0013 #include <QDebug> 0014 #include <QEvent> 0015 #include <QLineEdit> 0016 #include <QMimeDatabase> 0017 0018 #include <config-kiofilewidgets.h> 0019 0020 #include <algorithm> 0021 #include <utility> 0022 0023 class KFileFilterComboPrivate 0024 { 0025 public: 0026 explicit KFileFilterComboPrivate(KFileFilterCombo *qq) 0027 : q(qq) 0028 { 0029 } 0030 0031 void slotFilterChanged(); 0032 0033 KFileFilterCombo *const q; 0034 // when we have more than 3 mimefilters and no default-filter, 0035 // we don't show the comments of all mimefilters in one line, 0036 // instead we show "All supported files". We have to translate 0037 // that back to the list of mimefilters in currentFilter() tho. 0038 bool m_hasAllSupportedFiles = false; 0039 // true when setMimeFilter was called 0040 bool m_isMimeFilter = false; 0041 QString m_lastFilter; 0042 KFileFilter m_defaultFilter = KFileFilter::fromFilterString(i18nc("Default mime type filter that shows all file types", "*|All Files")).first(); 0043 0044 QList<KFileFilter> m_filters; 0045 bool m_allTypes; 0046 }; 0047 0048 KFileFilterCombo::KFileFilterCombo(QWidget *parent) 0049 : KComboBox(true, parent) 0050 , d(new KFileFilterComboPrivate(this)) 0051 { 0052 setTrapReturnKey(true); 0053 setInsertPolicy(QComboBox::NoInsert); 0054 connect(this, &QComboBox::activated, this, &KFileFilterCombo::filterChanged); 0055 connect(this, &KComboBox::returnPressed, this, &KFileFilterCombo::filterChanged); 0056 connect(this, &KFileFilterCombo::filterChanged, this, [this]() { 0057 d->slotFilterChanged(); 0058 }); 0059 d->m_allTypes = false; 0060 } 0061 0062 KFileFilterCombo::~KFileFilterCombo() = default; 0063 0064 void KFileFilterCombo::setFilters(const QList<KFileFilter> &types, const KFileFilter &defaultFilter) 0065 { 0066 clear(); 0067 d->m_filters.clear(); 0068 QString delim = QStringLiteral(", "); 0069 d->m_hasAllSupportedFiles = false; 0070 bool hasAllFilesFilter = false; 0071 QMimeDatabase db; 0072 0073 if (types.isEmpty()) { 0074 d->m_filters = {d->m_defaultFilter}; 0075 addItem(d->m_defaultFilter.label()); 0076 0077 d->m_lastFilter = currentText(); 0078 return; 0079 } 0080 0081 d->m_allTypes = defaultFilter.isEmpty() && (types.count() > 1); 0082 0083 if (!types.isEmpty() && types.first().mimePatterns().isEmpty()) { 0084 d->m_allTypes = false; 0085 } 0086 0087 // If there's MIME types that have the same comment, we will show the extension 0088 // in addition to the MIME type comment 0089 QHash<QString, int> allTypeComments; 0090 for (const KFileFilter &filter : types) { 0091 allTypeComments[filter.label()] += 1; 0092 } 0093 0094 for (const KFileFilter &filter : types) { 0095 if (!filter.isValid()) { 0096 continue; 0097 } 0098 0099 const QStringList mimeTypes = filter.mimePatterns(); 0100 0101 const bool isAllFileFilters = std::any_of(mimeTypes.cbegin(), mimeTypes.cend(), [&db](const QString &mimeTypeName) { 0102 const QMimeType type = db.mimeTypeForName(mimeTypeName); 0103 0104 if (!type.isValid()) { 0105 qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << mimeTypeName << "is not a valid MIME type"; 0106 return false; 0107 } 0108 0109 return type.name().startsWith(QLatin1String("all/")) || type.isDefault(); 0110 }); 0111 0112 if (isAllFileFilters) { 0113 hasAllFilesFilter = true; 0114 continue; 0115 } 0116 0117 if (allTypeComments.value(filter.label()) > 1) { 0118 QStringList mimeSuffixes; 0119 0120 for (const QString &mimeTypeName : filter.mimePatterns()) { 0121 const QMimeType type = db.mimeTypeForName(mimeTypeName); 0122 mimeSuffixes << type.suffixes(); 0123 } 0124 0125 const QString label = i18nc("%1 is the mimetype name, %2 is the extensions", "%1 (%2)", filter.label(), mimeSuffixes.join(QLatin1String(", "))); 0126 KFileFilter newFilter(label, filter.filePatterns(), filter.mimePatterns()); 0127 0128 d->m_filters.append(newFilter); 0129 addItem(newFilter.label()); 0130 } else { 0131 d->m_filters.append(filter); 0132 addItem(filter.label()); 0133 } 0134 0135 if (filter == defaultFilter) { 0136 setCurrentIndex(count() - 1); 0137 } 0138 } 0139 0140 if (count() == 1) { 0141 d->m_allTypes = false; 0142 } 0143 0144 if (d->m_allTypes) { 0145 QStringList allTypes; 0146 for (const KFileFilter &filter : std::as_const(d->m_filters)) { 0147 allTypes << filter.mimePatterns().join(QLatin1Char(' ')); 0148 } 0149 0150 KFileFilter allSupportedFilesFilter; 0151 0152 if (count() <= 3) { // show the MIME type comments of at max 3 types 0153 QStringList allComments; 0154 for (const KFileFilter &filter : std::as_const(d->m_filters)) { 0155 allComments << filter.label(); 0156 } 0157 0158 allSupportedFilesFilter = KFileFilter(allComments.join(delim), {}, allTypes); 0159 } else { 0160 allSupportedFilesFilter = KFileFilter(i18n("All Supported Files"), {}, allTypes); 0161 d->m_hasAllSupportedFiles = true; 0162 } 0163 0164 insertItem(0, allSupportedFilesFilter.label()); 0165 d->m_filters.prepend(allSupportedFilesFilter); 0166 setCurrentIndex(0); 0167 } 0168 0169 if (hasAllFilesFilter) { 0170 addItem(i18n("All Files")); 0171 d->m_filters.append(KFileFilter(i18n("All Files"), {}, {QStringLiteral("application/octet-stream")})); 0172 } 0173 0174 d->m_lastFilter = currentText(); 0175 } 0176 0177 KFileFilter KFileFilterCombo::currentFilter() const 0178 { 0179 if (currentText() != itemText(currentIndex())) { 0180 // The user edited the text 0181 0182 const QList<KFileFilter> filter = KFileFilter::fromFilterString(currentText()); 0183 0184 if (!filter.isEmpty()) { 0185 return filter.first(); 0186 } else { 0187 return KFileFilter(); 0188 } 0189 } else { 0190 if (currentIndex() == -1) { 0191 return KFileFilter(); 0192 } 0193 0194 return d->m_filters[currentIndex()]; 0195 } 0196 } 0197 0198 bool KFileFilterCombo::showsAllTypes() const 0199 { 0200 return d->m_allTypes; 0201 } 0202 0203 QList<KFileFilter> KFileFilterCombo::filters() const 0204 { 0205 return d->m_filters; 0206 } 0207 0208 void KFileFilterCombo::setCurrentFilter(const KFileFilter &filter) 0209 { 0210 auto it = std::find(d->m_filters.cbegin(), d->m_filters.cend(), filter); 0211 0212 if (it == d->m_filters.cend()) { 0213 qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << "Could not find file filter"; 0214 setCurrentIndex(-1); 0215 Q_EMIT filterChanged(); 0216 return; 0217 } 0218 0219 setCurrentIndex(std::distance(d->m_filters.cbegin(), it)); 0220 Q_EMIT filterChanged(); 0221 } 0222 0223 void KFileFilterComboPrivate::slotFilterChanged() 0224 { 0225 m_lastFilter = q->currentText(); 0226 } 0227 0228 bool KFileFilterCombo::eventFilter(QObject *o, QEvent *e) 0229 { 0230 if (o == lineEdit() && e->type() == QEvent::FocusOut) { 0231 if (currentText() != d->m_lastFilter) { 0232 Q_EMIT filterChanged(); 0233 } 0234 } 0235 0236 return KComboBox::eventFilter(o, e); 0237 } 0238 0239 void KFileFilterCombo::setDefaultFilter(const KFileFilter &filter) 0240 { 0241 d->m_defaultFilter = filter; 0242 } 0243 0244 KFileFilter KFileFilterCombo::defaultFilter() const 0245 { 0246 return d->m_defaultFilter; 0247 } 0248 0249 #include "moc_kfilefiltercombo.cpp"