]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinfacetswidget.cpp
Merge branch 'release/19.12'
[dolphin.git] / src / search / dolphinfacetswidget.cpp
1 /***************************************************************************
2 * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2019 by Ismael Asensio <isma.af@mgmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 * **************************************************************************/
20
21 #include "dolphinfacetswidget.h"
22
23 #include <KLocalizedString>
24
25 #include <QComboBox>
26 #include <QDate>
27 #include <QEvent>
28 #include <QHBoxLayout>
29 #include <QIcon>
30 #include <QMenu>
31 #include <QToolButton>
32
33 DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) :
34 QWidget(parent),
35 m_typeSelector(nullptr),
36 m_dateSelector(nullptr),
37 m_ratingSelector(nullptr),
38 m_tagsSelector(nullptr)
39 {
40 m_typeSelector = new QComboBox(this);
41 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString());
42 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders") , QStringLiteral("Folder"));
43 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents") , QStringLiteral("Document"));
44 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images") , QStringLiteral("Image"));
45 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio"));
46 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos") , QStringLiteral("Video"));
47 initComboBox(m_typeSelector);
48
49 const QDate currentDate = QDate::currentDate();
50
51 m_dateSelector = new QComboBox(this);
52 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar")), i18nc("@item:inlistbox", "Any Date"), QDate());
53 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Today") , currentDate);
54 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Yesterday") , currentDate.addDays(-1));
55 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")), i18nc("@item:inlistbox", "This Week") , currentDate.addDays(1 - currentDate.dayOfWeek()));
56 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")), i18nc("@item:inlistbox", "This Month"), currentDate.addDays(1 - currentDate.day()));
57 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-year")), i18nc("@item:inlistbox", "This Year") , currentDate.addDays(1 - currentDate.dayOfYear()));
58 initComboBox(m_dateSelector);
59
60 m_ratingSelector = new QComboBox(this);
61 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("non-starred-symbolic")), i18nc("@item:inlistbox", "Any Rating"), 0);
62 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "1 or more"), 1);
63 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "2 or more"), 2);
64 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "3 or more"), 3);
65 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "4 or more"), 4);
66 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5);
67 initComboBox(m_ratingSelector);
68
69 m_tagsSelector = new QToolButton(this);
70 m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag")));
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
82 QHBoxLayout* topLayout = new QHBoxLayout(this);
83 topLayout->setContentsMargins(0, 0, 0, 0);
84 topLayout->addWidget(m_typeSelector);
85 topLayout->addWidget(m_dateSelector);
86 topLayout->addWidget(m_ratingSelector);
87 topLayout->addWidget(m_tagsSelector);
88
89 resetOptions();
90 }
91
92 DolphinFacetsWidget::~DolphinFacetsWidget()
93 {
94 }
95
96 void DolphinFacetsWidget::changeEvent(QEvent *event)
97 {
98 if (event->type() == QEvent::EnabledChange) {
99 if (isEnabled()) {
100 updateTagsSelector();
101 } else {
102 resetOptions();
103 }
104 }
105 }
106
107 void DolphinFacetsWidget::resetOptions()
108 {
109 m_typeSelector->setCurrentIndex(0);
110 m_dateSelector->setCurrentIndex(0);
111 m_ratingSelector->setCurrentIndex(0);
112
113 m_searchTags = QStringList();
114 updateTagsSelector();
115 updateTagsMenu();
116 }
117
118 QString DolphinFacetsWidget::ratingTerm() const
119 {
120 QStringList terms;
121
122 if (m_ratingSelector->currentIndex() > 0) {
123 const int rating = m_ratingSelector->currentData().toInt() * 2;
124 terms << QStringLiteral("rating>=%1").arg(rating);
125 }
126
127 if (m_dateSelector->currentIndex() > 0) {
128 const QDate date = m_dateSelector->currentData().toDate();
129 terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate));
130 }
131
132 if (!m_searchTags.isEmpty()) {
133 for (auto const &tag : m_searchTags) {
134 terms << QStringLiteral("tag:%1").arg(tag);
135 }
136 }
137
138 return terms.join(QLatin1String(" AND "));
139 }
140
141 QString DolphinFacetsWidget::facetType() const
142 {
143 return m_typeSelector->currentData().toString();
144 }
145
146 bool DolphinFacetsWidget::isRatingTerm(const QString& term) const
147 {
148 const QStringList subTerms = term.split(' ', QString::SkipEmptyParts);
149
150 // If term has sub terms, then sone of the sub terms are always "rating" and "modified" terms.
151 bool containsRating = false;
152 bool containsModified = false;
153 bool containsTag = false;
154
155 foreach (const QString& subTerm, subTerms) {
156 if (subTerm.startsWith(QLatin1String("rating>="))) {
157 containsRating = true;
158 } else if (subTerm.startsWith(QLatin1String("modified>="))) {
159 containsModified = true;
160 } else if (subTerm.startsWith(QLatin1String("tag:")) ||
161 subTerm.startsWith(QLatin1String("tag="))) {
162 containsTag = true;
163 }
164 }
165
166 return containsModified || containsRating || containsTag;
167 }
168
169 void DolphinFacetsWidget::setRatingTerm(const QString& term)
170 {
171 // If term has sub terms, then the sub terms are always "rating" and "modified" terms.
172 // If term has no sub terms, then the term itself is either a "rating" term or a "modified"
173 // term. To avoid code duplication we add term to subTerms list, if the list is empty.
174 QStringList subTerms = term.split(' ', QString::SkipEmptyParts);
175
176 foreach (const QString& subTerm, subTerms) {
177 if (subTerm.startsWith(QLatin1String("modified>="))) {
178 const QString value = subTerm.mid(10);
179 const QDate date = QDate::fromString(value, Qt::ISODate);
180 setTimespan(date);
181 } else if (subTerm.startsWith(QLatin1String("rating>="))) {
182 const QString value = subTerm.mid(8);
183 const int stars = value.toInt() / 2;
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);
189 }
190 }
191 }
192
193 void DolphinFacetsWidget::setFacetType(const QString& type)
194 {
195 for (int index = 0; index <= m_typeSelector->count(); index++) {
196 if (type == m_typeSelector->itemData(index).toString()) {
197 m_typeSelector->setCurrentIndex(index);
198 break;
199 }
200 }
201 }
202
203 void DolphinFacetsWidget::setRating(const int stars)
204 {
205 if (stars < 0 || stars > 5) {
206 return;
207 }
208 m_ratingSelector->setCurrentIndex(stars);
209 }
210
211 void DolphinFacetsWidget::setTimespan(const QDate& date)
212 {
213 if (!date.isValid()) {
214 return;
215 }
216 m_dateSelector->setCurrentIndex(0);
217 for (int index = 1; index <= m_dateSelector->count(); index++) {
218 if (date >= m_dateSelector->itemData(index).toDate()) {
219 m_dateSelector->setCurrentIndex(index);
220 break;
221 }
222 }
223 }
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
244 void DolphinFacetsWidget::initComboBox(QComboBox* combo)
245 {
246 combo->setFrame(false);
247 combo->setMinimumHeight(parentWidget()->height());
248 combo->setCurrentIndex(0);
249 connect(combo, QOverload<int>::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged);
250 }
251
252 void DolphinFacetsWidget::updateTagsSelector()
253 {
254 const bool hasListedTags = !m_tagsSelector->menu()->isEmpty();
255 const bool hasSelectedTags = !m_searchTags.isEmpty();
256
257 if (hasSelectedTags) {
258 const QString tagsText = m_searchTags.join(i18nc("String list separator", ", "));
259 m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags",
260 "Tag: %2", "Tags: %2",m_searchTags.count(), tagsText));
261 } else {
262 m_tagsSelector->setText(i18nc("@action:button", "Add Tags"));
263 }
264
265 m_tagsSelector->setEnabled(isEnabled() && (hasListedTags || hasSelectedTags));
266 }
267
268 void DolphinFacetsWidget::updateTagsMenu()
269 {
270 updateTagsMenuItems({}, {});
271 m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload);
272 }
273
274 void DolphinFacetsWidget::updateTagsMenuItems(const QUrl&, const KFileItemList& items)
275 {
276 m_tagsSelector->menu()->clear();
277
278 QStringList allTags = QStringList(m_searchTags);
279 for (const KFileItem &item: items) {
280 allTags.append(item.name());
281 }
282 allTags.sort(Qt::CaseInsensitive);
283 allTags.removeDuplicates();
284
285 for (const QString& tagName : qAsConst(allTags)) {
286 QAction* action = m_tagsSelector->menu()->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName);
287 action->setCheckable(true);
288 action->setChecked(m_searchTags.contains(tagName));
289
290 connect(action, &QAction::triggered, this, [this, tagName](bool isChecked) {
291 if (isChecked) {
292 addSearchTag(tagName);
293 } else {
294 removeSearchTag(tagName);
295 }
296 emit facetChanged();
297 });
298 }
299
300 updateTagsSelector();
301 }