]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinfacetswidget.cpp
DolphinContextMenu: Allow disabling "Open Terminal" action
[dolphin.git] / src / search / dolphinfacetswidget.cpp
1 /*
2 * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2019 Ismael Asensio <isma.af@mgmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "dolphinfacetswidget.h"
9
10 #include <KLocalizedString>
11
12 #include <QComboBox>
13 #include <QDate>
14 #include <QEvent>
15 #include <QHBoxLayout>
16 #include <QIcon>
17 #include <QMenu>
18 #include <QToolButton>
19
20 DolphinFacetsWidget::DolphinFacetsWidget(QWidget* parent) :
21 QWidget(parent),
22 m_typeSelector(nullptr),
23 m_dateSelector(nullptr),
24 m_ratingSelector(nullptr),
25 m_tagsSelector(nullptr)
26 {
27 m_typeSelector = new QComboBox(this);
28 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("none")), i18nc("@item:inlistbox", "Any Type"), QString());
29 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("inode-directory")), i18nc("@item:inlistbox", "Folders") , QStringLiteral("Folder"));
30 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("text-x-generic")), i18nc("@item:inlistbox", "Documents") , QStringLiteral("Document"));
31 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("image-x-generic")), i18nc("@item:inlistbox", "Images") , QStringLiteral("Image"));
32 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("audio-x-generic")), i18nc("@item:inlistbox", "Audio Files"), QStringLiteral("Audio"));
33 m_typeSelector->addItem(QIcon::fromTheme(QStringLiteral("video-x-generic")), i18nc("@item:inlistbox", "Videos") , QStringLiteral("Video"));
34 initComboBox(m_typeSelector);
35
36 const QDate currentDate = QDate::currentDate();
37
38 m_dateSelector = new QComboBox(this);
39 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar")), i18nc("@item:inlistbox", "Any Date"), QDate());
40 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Today") , currentDate);
41 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("go-jump-today")), i18nc("@item:inlistbox", "Yesterday") , currentDate.addDays(-1));
42 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-week")), i18nc("@item:inlistbox", "This Week") , currentDate.addDays(1 - currentDate.dayOfWeek()));
43 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-month")), i18nc("@item:inlistbox", "This Month"), currentDate.addDays(1 - currentDate.day()));
44 m_dateSelector->addItem(QIcon::fromTheme(QStringLiteral("view-calendar-year")), i18nc("@item:inlistbox", "This Year") , currentDate.addDays(1 - currentDate.dayOfYear()));
45 initComboBox(m_dateSelector);
46
47 m_ratingSelector = new QComboBox(this);
48 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("non-starred-symbolic")), i18nc("@item:inlistbox", "Any Rating"), 0);
49 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "1 or more"), 1);
50 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "2 or more"), 2);
51 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "3 or more"), 3);
52 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "4 or more"), 4);
53 m_ratingSelector->addItem(QIcon::fromTheme(QStringLiteral("starred-symbolic")), i18nc("@item:inlistbox", "Highest Rating"), 5);
54 initComboBox(m_ratingSelector);
55
56 m_clearTagsAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear-all")), i18nc("@action:inmenu", "Clear Selection"), this);
57 connect(m_clearTagsAction, &QAction::triggered, this, [this]() {
58 resetSearchTags();
59 Q_EMIT facetChanged();
60 });
61
62 m_tagsSelector = new QToolButton(this);
63 m_tagsSelector->setIcon(QIcon::fromTheme(QStringLiteral("tag")));
64 m_tagsSelector->setMenu(new QMenu(this));
65 m_tagsSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
66 m_tagsSelector->setPopupMode(QToolButton::MenuButtonPopup);
67 m_tagsSelector->setAutoRaise(true);
68 updateTagsSelector();
69
70 connect(m_tagsSelector, &QToolButton::clicked, m_tagsSelector, &QToolButton::showMenu);
71 connect(m_tagsSelector->menu(), &QMenu::aboutToShow, this, &DolphinFacetsWidget::updateTagsMenu);
72 connect(&m_tagsLister, &KCoreDirLister::itemsAdded, this, &DolphinFacetsWidget::updateTagsMenuItems);
73 updateTagsMenu();
74
75 QHBoxLayout* topLayout = new QHBoxLayout(this);
76 topLayout->setContentsMargins(0, 0, 0, 0);
77 topLayout->addWidget(m_typeSelector);
78 topLayout->addWidget(m_dateSelector);
79 topLayout->addWidget(m_ratingSelector);
80 topLayout->addWidget(m_tagsSelector);
81
82 resetSearchTerms();
83 }
84
85 DolphinFacetsWidget::~DolphinFacetsWidget()
86 {
87 }
88
89 void DolphinFacetsWidget::changeEvent(QEvent *event)
90 {
91 if (event->type() == QEvent::EnabledChange) {
92 if (isEnabled()) {
93 updateTagsSelector();
94 } else {
95 resetSearchTerms();
96 }
97 }
98 }
99
100 void DolphinFacetsWidget::resetSearchTerms()
101 {
102 m_typeSelector->setCurrentIndex(0);
103 m_dateSelector->setCurrentIndex(0);
104 m_ratingSelector->setCurrentIndex(0);
105
106 resetSearchTags();
107 }
108
109 QStringList DolphinFacetsWidget::searchTerms() const
110 {
111 QStringList terms;
112
113 if (m_ratingSelector->currentIndex() > 0) {
114 const int rating = m_ratingSelector->currentData().toInt() * 2;
115 terms << QStringLiteral("rating>=%1").arg(rating);
116 }
117
118 if (m_dateSelector->currentIndex() > 0) {
119 const QDate date = m_dateSelector->currentData().toDate();
120 terms << QStringLiteral("modified>=%1").arg(date.toString(Qt::ISODate));
121 }
122
123 if (!m_searchTags.isEmpty()) {
124 for (auto const &tag : m_searchTags) {
125 if (tag.contains(QLatin1Char(' '))) {
126 terms << QStringLiteral("tag:\"%1\"").arg(tag);
127 } else {
128 terms << QStringLiteral("tag:%1").arg(tag);
129 }
130 }
131 }
132
133 return terms;
134 }
135
136 QString DolphinFacetsWidget::facetType() const
137 {
138 return m_typeSelector->currentData().toString();
139 }
140
141 bool DolphinFacetsWidget::isSearchTerm(const QString& term) const
142 {
143 static const QLatin1String searchTokens[] {
144 QLatin1String("modified>="),
145 QLatin1String("rating>="),
146 QLatin1String("tag:"), QLatin1String("tag=")
147 };
148
149 for (const auto &searchToken : searchTokens) {
150 if (term.startsWith(searchToken)) {
151 return true;
152 }
153 }
154 return false;
155 }
156
157 void DolphinFacetsWidget::setSearchTerm(const QString& term)
158 {
159 if (term.startsWith(QLatin1String("modified>="))) {
160 const QString value = term.mid(10);
161 const QDate date = QDate::fromString(value, Qt::ISODate);
162 setTimespan(date);
163 } else if (term.startsWith(QLatin1String("rating>="))) {
164 const QString value = term.mid(8);
165 const int stars = value.toInt() / 2;
166 setRating(stars);
167 } else if (term.startsWith(QLatin1String("tag:")) ||
168 term.startsWith(QLatin1String("tag="))) {
169 const QString value = term.mid(4);
170 addSearchTag(value);
171 }
172 }
173
174 void DolphinFacetsWidget::setFacetType(const QString& type)
175 {
176 for (int index = 0; index <= m_typeSelector->count(); index++) {
177 if (type == m_typeSelector->itemData(index).toString()) {
178 m_typeSelector->setCurrentIndex(index);
179 break;
180 }
181 }
182 }
183
184 void DolphinFacetsWidget::setRating(const int stars)
185 {
186 if (stars < 0 || stars > 5) {
187 return;
188 }
189 m_ratingSelector->setCurrentIndex(stars);
190 }
191
192 void DolphinFacetsWidget::setTimespan(const QDate& date)
193 {
194 if (!date.isValid()) {
195 return;
196 }
197 m_dateSelector->setCurrentIndex(0);
198 for (int index = 1; index <= m_dateSelector->count(); index++) {
199 if (date >= m_dateSelector->itemData(index).toDate()) {
200 m_dateSelector->setCurrentIndex(index);
201 break;
202 }
203 }
204 }
205
206 void DolphinFacetsWidget::addSearchTag(const QString& tag)
207 {
208 if (tag.isEmpty() || m_searchTags.contains(tag)) {
209 return;
210 }
211 m_searchTags.append(tag);
212 m_searchTags.sort();
213 updateTagsSelector();
214 }
215
216 void DolphinFacetsWidget::removeSearchTag(const QString& tag)
217 {
218 if (tag.isEmpty() || !m_searchTags.contains(tag)) {
219 return;
220 }
221 m_searchTags.removeAll(tag);
222 updateTagsSelector();
223 }
224
225 void DolphinFacetsWidget::resetSearchTags()
226 {
227 m_searchTags = QStringList();
228 updateTagsSelector();
229 updateTagsMenu();
230 }
231
232 void DolphinFacetsWidget::initComboBox(QComboBox* combo)
233 {
234 combo->setFrame(false);
235 combo->setMinimumHeight(parentWidget()->height());
236 combo->setCurrentIndex(0);
237 connect(combo, QOverload<int>::of(&QComboBox::activated), this, &DolphinFacetsWidget::facetChanged);
238 }
239
240 void DolphinFacetsWidget::updateTagsSelector()
241 {
242 const bool hasListedTags = !m_tagsSelector->menu()->isEmpty();
243 const bool hasSelectedTags = !m_searchTags.isEmpty();
244
245 if (hasSelectedTags) {
246 const QString tagsText = m_searchTags.join(i18nc("String list separator", ", "));
247 m_tagsSelector->setText(i18ncp("@action:button %2 is a list of tags",
248 "Tag: %2", "Tags: %2",m_searchTags.count(), tagsText));
249 } else {
250 m_tagsSelector->setText(i18nc("@action:button", "Add Tags"));
251 }
252
253 m_tagsSelector->setEnabled(isEnabled() && (hasListedTags || hasSelectedTags));
254 m_clearTagsAction->setEnabled(hasSelectedTags);
255 }
256
257 void DolphinFacetsWidget::updateTagsMenu()
258 {
259 updateTagsMenuItems({}, {});
260 m_tagsLister.openUrl(QUrl(QStringLiteral("tags:/")), KCoreDirLister::OpenUrlFlag::Reload);
261 }
262
263 void DolphinFacetsWidget::updateTagsMenuItems(const QUrl&, const KFileItemList& items)
264 {
265 QMenu *tagsMenu = m_tagsSelector->menu();
266 tagsMenu->clear();
267
268 QStringList allTags = QStringList(m_searchTags);
269 for (const KFileItem &item: items) {
270 allTags.append(item.name());
271 }
272 allTags.sort(Qt::CaseInsensitive);
273 allTags.removeDuplicates();
274
275 const bool onlyOneTag = allTags.count() == 1;
276
277 for (const QString& tagName : qAsConst(allTags)) {
278 QAction *action = tagsMenu->addAction(QIcon::fromTheme(QStringLiteral("tag")), tagName);
279 action->setCheckable(true);
280 action->setChecked(m_searchTags.contains(tagName));
281
282 connect(action, &QAction::triggered, this, [this, tagName, onlyOneTag](bool isChecked) {
283 if (isChecked) {
284 addSearchTag(tagName);
285 } else {
286 removeSearchTag(tagName);
287 }
288 Q_EMIT facetChanged();
289
290 if (!onlyOneTag) {
291 m_tagsSelector->menu()->show();
292 }
293 });
294 }
295
296 if (allTags.count() > 1) {
297 tagsMenu->addSeparator();
298 tagsMenu->addAction(m_clearTagsAction);
299 }
300
301 updateTagsSelector();
302 }