Changeset View
Changeset View
Standalone View
Standalone View
src/search/dolphinfacetswidget.cpp
Show All 21 Lines | |||||
22 | 22 | | |||
23 | #include <KLocalizedString> | 23 | #include <KLocalizedString> | ||
24 | 24 | | |||
25 | #include <QComboBox> | 25 | #include <QComboBox> | ||
26 | #include <QDate> | 26 | #include <QDate> | ||
27 | #include <QEvent> | 27 | #include <QEvent> | ||
28 | #include <QHBoxLayout> | 28 | #include <QHBoxLayout> | ||
29 | #include <QIcon> | 29 | #include <QIcon> | ||
30 | #include <QMenu> | ||||
31 | #include <QToolButton> | ||||
30 | 32 | | |||
31 | DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : | 33 | DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) : | ||
32 | QWidget(parent), | 34 | QWidget(parent), | ||
33 | m_typeSelector(nullptr), | 35 | m_typeSelector(nullptr), | ||
34 | m_dateSelector(nullptr), | 36 | m_dateSelector(nullptr), | ||
35 | m_ratingSelector(nullptr) | 37 | m_ratingSelector(nullptr), | ||
38 | m_tagsSelector(nullptr) | ||||
36 | { | 39 | { | ||
37 | m_typeSelector = new QComboBox(this); | 40 | m_typeSelector = new QComboBox(this); | ||
38 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString()); | 41 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString()); | ||
39 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders") , QStringLiteral("Folder")); | 42 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders") , QStringLiteral("Folder")); | ||
40 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents") , QStringLiteral("Document")); | 43 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents") , QStringLiteral("Document")); | ||
41 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images") , QStringLiteral("Image")); | 44 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images") , QStringLiteral("Image")); | ||
42 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio")); | 45 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio")); | ||
43 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos") , QStringLiteral("Video")); | 46 | m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos") , QStringLiteral("Video")); | ||
Show All 14 Lines | |||||
58 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("non-starred-symbolic")), i18nc("@item:inlistbox", "Any Rating"), 0); | 61 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("non-starred-symbolic")), i18nc("@item:inlistbox", "Any Rating"), 0); | ||
59 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "1 or more"), 1); | 62 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "1 or more"), 1); | ||
60 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "2 or more"), 2); | 63 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "2 or more"), 2); | ||
61 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "3 or more"), 3); | 64 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "3 or more"), 3); | ||
62 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "4 or more"), 4); | 65 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "4 or more"), 4); | ||
63 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5); | 66 | m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5); | ||
64 | initComboBox(m_ratingSelector); | 67 | initComboBox(m_ratingSelector); | ||
65 | 68 | | |||
69 | m_tagsSelector = new QToolButton(this); | ||||
70 | m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag"))); | ||||
ngraham: Just `tag` is fine; the `*-symbolic` icon names are generally for compatibility with GNOME apps. | |||||
71 | m_tagsSelector->setMenu(new QMenu(this)); | ||||
72 | m_tagsSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); | ||||
73 | m_tagsSelector->setPopupMode(QToolButton::MenuButtonPopup); | ||||
74 | m_tagsSelector->setAutoRaise(true); | ||||
75 | updateTagsSelector(); | ||||
76 | | ||||
77 | connect(m_tagsSelector, &QToolButton::clicked, m_tagsSelector, &QToolButton::showMenu); | ||||
78 | connect(m_tagsSelector->menu(), &QMenu::aboutToShow, this, &DolphinFacetsWidget::updateTagsMenu); | ||||
79 | connect(&m_tagsLister, &KCoreDirLister::itemsAdded, this, &DolphinFacetsWidget::updateTagsMenuItems); | ||||
80 | updateTagsMenu(); | ||||
81 | | ||||
66 | QHBoxLayout* topLayout = new QHBoxLayout(this); | 82 | QHBoxLayout* topLayout = new QHBoxLayout(this); | ||
67 | topLayout->setContentsMargins(0, 0, 0, 0); | 83 | topLayout->setContentsMargins(0, 0, 0, 0); | ||
68 | topLayout->addWidget(m_typeSelector); | 84 | topLayout->addWidget(m_typeSelector); | ||
69 | topLayout->addWidget(m_dateSelector); | 85 | topLayout->addWidget(m_dateSelector); | ||
70 | topLayout->addWidget(m_ratingSelector); | 86 | topLayout->addWidget(m_ratingSelector); | ||
87 | topLayout->addWidget(m_tagsSelector); | ||||
71 | 88 | | |||
72 | resetOptions(); | 89 | resetOptions(); | ||
73 | } | 90 | } | ||
74 | 91 | | |||
75 | DolphinFacetsWidget::~DolphinFacetsWidget() | 92 | DolphinFacetsWidget::~DolphinFacetsWidget() | ||
76 | { | 93 | { | ||
77 | } | 94 | } | ||
78 | 95 | | |||
79 | void DolphinFacetsWidget::changeEvent(QEvent *event) | 96 | void DolphinFacetsWidget::changeEvent(QEvent *event) | ||
80 | { | 97 | { | ||
81 | if (event->type() == QEvent::EnabledChange && !isEnabled()) { | 98 | if (event->type() == QEvent::EnabledChange) { | ||
99 | if (isEnabled()) { | ||||
elvisangelaccio: `this->` not needed. | |||||
100 | updateTagsSelector(); | ||||
101 | } else { | ||||
82 | resetOptions(); | 102 | resetOptions(); | ||
83 | } | 103 | } | ||
84 | } | 104 | } | ||
105 | } | ||||
85 | 106 | | |||
86 | void DolphinFacetsWidget::resetOptions() | 107 | void DolphinFacetsWidget::resetOptions() | ||
87 | { | 108 | { | ||
88 | m_typeSelector->setCurrentIndex(0); | 109 | m_typeSelector->setCurrentIndex(0); | ||
89 | m_dateSelector->setCurrentIndex(0); | 110 | m_dateSelector->setCurrentIndex(0); | ||
90 | m_ratingSelector->setCurrentIndex(0); | 111 | m_ratingSelector->setCurrentIndex(0); | ||
112 | | ||||
113 | m_searchTags = QStringList(); | ||||
114 | updateTagsSelector(); | ||||
115 | updateTagsMenu(); | ||||
91 | } | 116 | } | ||
92 | 117 | | |||
93 | QString DolphinFacetsWidget::ratingTerm() const | 118 | QString DolphinFacetsWidget::ratingTerm() const | ||
94 | { | 119 | { | ||
95 | QStringList terms; | 120 | QStringList terms; | ||
96 | 121 | | |||
97 | if (m_ratingSelector->currentIndex() > 0) { | 122 | if (m_ratingSelector->currentIndex() > 0) { | ||
98 | const int rating = m_ratingSelector->currentData().toInt() * 2; | 123 | const int rating = m_ratingSelector->currentData().toInt() * 2; | ||
99 | terms << QStringLiteral("rating>=%1").arg(rating); | 124 | terms << QStringLiteral("rating>=%1").arg(rating); | ||
100 | } | 125 | } | ||
101 | 126 | | |||
102 | if (m_dateSelector->currentIndex() > 0) { | 127 | if (m_dateSelector->currentIndex() > 0) { | ||
103 | const QDate date = m_dateSelector->currentData().toDate(); | 128 | const QDate date = m_dateSelector->currentData().toDate(); | ||
104 | terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate)); | 129 | terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate)); | ||
105 | } | 130 | } | ||
106 | 131 | | |||
132 | if (!m_searchTags.isEmpty()) { | ||||
133 | for (auto const &tag : m_searchTags) { | ||||
134 | terms << QStringLiteral("tag:%1").arg(tag); | ||||
135 | } | ||||
136 | } | ||||
137 | | ||||
107 | return terms.join(QLatin1String(" AND ")); | 138 | return terms.join(QLatin1String(" AND ")); | ||
108 | } | 139 | } | ||
109 | 140 | | |||
110 | QString DolphinFacetsWidget::facetType() const | 141 | QString DolphinFacetsWidget::facetType() const | ||
111 | { | 142 | { | ||
112 | return m_typeSelector->currentData().toString(); | 143 | return m_typeSelector->currentData().toString(); | ||
113 | } | 144 | } | ||
114 | 145 | | |||
115 | bool DolphinFacetsWidget::isRatingTerm(const QString& term) const | 146 | bool DolphinFacetsWidget::isRatingTerm(const QString& term) const | ||
bruns: method name no longer fits | |||||
I totally agree. iasensio: I totally agree.
I'm planning a clean-up refactor patch for this methods, and to also remove… | |||||
116 | { | 147 | { | ||
117 | const QStringList subTerms = term.split(' ', QString::SkipEmptyParts); | 148 | const QStringList subTerms = term.split(' ', QString::SkipEmptyParts); | ||
118 | 149 | | |||
119 | // If term has sub terms, then sone of the sub terms are always "rating" and "modified" terms. | 150 | // If term has sub terms, then sone of the sub terms are always "rating" and "modified" terms. | ||
bruns: comment no longer fits | |||||
120 | bool containsRating = false; | 151 | bool containsRating = false; | ||
121 | bool containsModified = false; | 152 | bool containsModified = false; | ||
153 | bool containsTag = false; | ||||
122 | 154 | | |||
123 | foreach (const QString& subTerm, subTerms) { | 155 | foreach (const QString& subTerm, subTerms) { | ||
124 | if (subTerm.startsWith(QLatin1String("rating>="))) { | 156 | if (subTerm.startsWith(QLatin1String("rating>="))) { | ||
125 | containsRating = true; | 157 | containsRating = true; | ||
126 | } else if (subTerm.startsWith(QLatin1String("modified>="))) { | 158 | } else if (subTerm.startsWith(QLatin1String("modified>="))) { | ||
127 | containsModified = true; | 159 | containsModified = true; | ||
160 | } else if (subTerm.startsWith(QLatin1String("tag:")) || | ||||
161 | subTerm.startsWith(QLatin1String("tag="))) { | ||||
162 | containsTag = true; | ||||
128 | } | 163 | } | ||
129 | } | 164 | } | ||
130 | 165 | | |||
131 | return containsModified || containsRating; | 166 | return containsModified || containsRating || containsTag; | ||
132 | } | 167 | } | ||
133 | 168 | | |||
134 | void DolphinFacetsWidget::setRatingTerm(const QString& term) | 169 | void DolphinFacetsWidget::setRatingTerm(const QString& term) | ||
bruns: dito | |||||
135 | { | 170 | { | ||
136 | // If term has sub terms, then the sub terms are always "rating" and "modified" terms. | 171 | // If term has sub terms, then the sub terms are always "rating" and "modified" terms. | ||
137 | // If term has no sub terms, then the term itself is either a "rating" term or a "modified" | 172 | // If term has no sub terms, then the term itself is either a "rating" term or a "modified" | ||
138 | // term. To avoid code duplication we add term to subTerms list, if the list is empty. | 173 | // term. To avoid code duplication we add term to subTerms list, if the list is empty. | ||
139 | QStringList subTerms = term.split(' ', QString::SkipEmptyParts); | 174 | QStringList subTerms = term.split(' ', QString::SkipEmptyParts); | ||
140 | 175 | | |||
141 | foreach (const QString& subTerm, subTerms) { | 176 | foreach (const QString& subTerm, subTerms) { | ||
142 | if (subTerm.startsWith(QLatin1String("modified>="))) { | 177 | if (subTerm.startsWith(QLatin1String("modified>="))) { | ||
143 | const QString value = subTerm.mid(10); | 178 | const QString value = subTerm.mid(10); | ||
144 | const QDate date = QDate::fromString(value, Qt::ISODate); | 179 | const QDate date = QDate::fromString(value, Qt::ISODate); | ||
145 | setTimespan(date); | 180 | setTimespan(date); | ||
146 | } else if (subTerm.startsWith(QLatin1String("rating>="))) { | 181 | } else if (subTerm.startsWith(QLatin1String("rating>="))) { | ||
147 | const QString value = subTerm.mid(8); | 182 | const QString value = subTerm.mid(8); | ||
148 | const int stars = value.toInt() / 2; | 183 | const int stars = value.toInt() / 2; | ||
149 | setRating(stars); | 184 | setRating(stars); | ||
185 | } else if (subTerm.startsWith(QLatin1String("tag:")) || | ||||
186 | subTerm.startsWith(QLatin1String("tag="))) { | ||||
187 | const QString value = subTerm.mid(4); | ||||
188 | addSearchTag(value); | ||||
150 | } | 189 | } | ||
151 | } | 190 | } | ||
152 | } | 191 | } | ||
153 | 192 | | |||
154 | void DolphinFacetsWidget::setFacetType(const QString& type) | 193 | void DolphinFacetsWidget::setFacetType(const QString& type) | ||
155 | { | 194 | { | ||
156 | for (int index = 0; index <= m_typeSelector->count(); index++) { | 195 | for (int index = 0; index <= m_typeSelector->count(); index++) { | ||
157 | if (type == m_typeSelector->itemData(index).toString()) { | 196 | if (type == m_typeSelector->itemData(index).toString()) { | ||
Show All 20 Lines | 212 | { | |||
178 | for (int index = 1; index <= m_dateSelector->count(); index++) { | 217 | for (int index = 1; index <= m_dateSelector->count(); index++) { | ||
179 | if (date >= m_dateSelector->itemData(index).toDate()) { | 218 | if (date >= m_dateSelector->itemData(index).toDate()) { | ||
180 | m_dateSelector->setCurrentIndex(index); | 219 | m_dateSelector->setCurrentIndex(index); | ||
181 | break; | 220 | break; | ||
182 | } | 221 | } | ||
183 | } | 222 | } | ||
184 | } | 223 | } | ||
185 | 224 | | |||
225 | void DolphinFacetsWidget::addSearchTag(const QString& tag) | ||||
226 | { | ||||
227 | if (tag.isEmpty() || m_searchTags.contains(tag)) { | ||||
228 | return; | ||||
229 | } | ||||
230 | m_searchTags.append(tag); | ||||
231 | m_searchTags.sort(); | ||||
232 | updateTagsSelector(); | ||||
233 | } | ||||
234 | | ||||
235 | void DolphinFacetsWidget::removeSearchTag(const QString& tag) | ||||
236 | { | ||||
237 | if (tag.isEmpty() || !m_searchTags.contains(tag)) { | ||||
238 | return; | ||||
239 | } | ||||
240 | m_searchTags.removeAll(tag); | ||||
241 | updateTagsSelector(); | ||||
242 | } | ||||
243 | | ||||
186 | void DolphinFacetsWidget::initComboBox(QComboBox* combo) | 244 | void DolphinFacetsWidget::initComboBox(QComboBox* combo) | ||
187 | { | 245 | { | ||
188 | combo->setFrame(false); | 246 | combo->setFrame(false); | ||
189 | combo->setMinimumHeight(parentWidget()->height()); | 247 | combo->setMinimumHeight(parentWidget()->height()); | ||
190 | combo->setCurrentIndex(0); | 248 | combo->setCurrentIndex(0); | ||
191 | connect(combo, QOverload<int>::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged); | 249 | connect(combo, QOverload<int>::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged); | ||
192 | } | 250 | } | ||
193 | 251 | | |||
252 | void DolphinFacetsWidget::updateTagsSelector() | ||||
253 | { | ||||
254 | if (m_searchTags.isEmpty()) { | ||||
255 | m_tagsSelector->setText(i18nc("@action:button", "Add Tags")); | ||||
256 | } else { | ||||
257 | const QString tagsText = m_searchTags.join(i18nc("String list separator", ", ")); | ||||
258 | m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags", | ||||
259 | "Tag: %2", "Tags: %2",m_searchTags.count(), tagsText)); | ||||
260 | } | ||||
261 | | ||||
262 | m_tagsSelector->setEnabled(isEnabled() && | ||||
elvisangelaccio: `this->` not needed | |||||
263 | (!m_searchTags.isEmpty() || !m_tagsSelector->menu()->isEmpty())); | ||||
264 | } | ||||
265 | | ||||
266 | void DolphinFacetsWidget::updateTagsMenu() | ||||
267 | { | ||||
268 | updateTagsMenuItems({}, {}); | ||||
269 | m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload); | ||||
270 | } | ||||
271 | | ||||
272 | void DolphinFacetsWidget::updateTagsMenuItems(const QUrl&, const KFileItemList& items) | ||||
273 | { | ||||
274 | m_tagsSelector->menu()->clear(); | ||||
275 | | ||||
276 | QStringList allTags = QStringList(m_searchTags); | ||||
277 | for (const KFileItem &item: items) { | ||||
278 | allTags.append(item.name()); | ||||
279 | } | ||||
280 | allTags.sort(Qt::CaseInsensitive); | ||||
281 | allTags.removeDuplicates(); | ||||
282 | | ||||
283 | for (const QString& tagName : qAsConst(allTags)) { | ||||
elvisangelaccio: Missing reference usage | |||||
284 | QAction* action = m_tagsSelector->menu()->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName); | ||||
285 | action->setCheckable(true); | ||||
286 | action->setChecked(m_searchTags.contains(tagName)); | ||||
287 | | ||||
288 | connect(action, &QAction::triggered, this, [this, tagName](bool isChecked) { | ||||
289 | if (isChecked) { | ||||
290 | addSearchTag(tagName); | ||||
291 | } else { | ||||
292 | removeSearchTag(tagName); | ||||
293 | } | ||||
294 | emit facetChanged(); | ||||
295 | }); | ||||
296 | } | ||||
297 | | ||||
298 | updateTagsSelector(); | ||||
299 | } |
Just tag is fine; the *-symbolic icon names are generally for compatibility with GNOME apps.