KIO

kurlcombobox.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "kurlcombobox.h"
9
10#include "../utils_p.h"
11
12#include <QApplication>
13#include <QDir>
14#include <QDrag>
15#include <QMimeData>
16#include <QMouseEvent>
17
18#include <KIconLoader>
19#include <KLocalizedString>
20#include <QDebug>
21#include <kio/global.h>
22
23#include <algorithm>
24#include <vector>
25
26class KUrlComboBoxPrivate
27{
28public:
29 KUrlComboBoxPrivate(KUrlComboBox *parent)
30 : m_parent(parent)
31 , dirIcon(QIcon::fromTheme(QStringLiteral("folder")))
32 {
33 }
34
35 struct KUrlComboItem {
36 KUrlComboItem(const QUrl &url, const QIcon &icon, const QString &text = QString())
37 : url(url)
38 , icon(icon)
39 , text(text)
40 {
41 }
42 QUrl url;
43 QIcon icon;
44 QString text; // if empty, calculated from the QUrl
45 };
46
47 void init(KUrlComboBox::Mode mode);
48 QString textForItem(const KUrlComboItem *item) const;
49 void insertUrlItem(const KUrlComboItem *);
50 QIcon getIcon(const QUrl &url) const;
51 void updateItem(const KUrlComboItem *item, int index, const QIcon &icon);
52
53 void slotActivated(int);
54
55 KUrlComboBox *const m_parent;
56 QIcon dirIcon;
57 bool urlAdded;
58 int myMaximum;
59 KUrlComboBox::Mode myMode;
60 QPoint m_dragPoint;
61
62 using KUrlComboItemList = std::vector<std::unique_ptr<const KUrlComboItem>>;
63 KUrlComboItemList itemList;
64 KUrlComboItemList defaultList;
66
67 QIcon opendirIcon;
68};
69
70QString KUrlComboBoxPrivate::textForItem(const KUrlComboItem *item) const
71{
72 if (!item->text.isEmpty()) {
73 return item->text;
74 }
75 QUrl url = item->url;
76
77 if (myMode == KUrlComboBox::Directories) {
78 Utils::appendSlashToPath(url);
79 } else {
81 }
82 if (url.isLocalFile()) {
83 return url.toLocalFile();
84 } else {
85 return url.toDisplayString();
86 }
87}
88
90 : KComboBox(parent)
91 , d(new KUrlComboBoxPrivate(this))
92{
93 d->init(mode);
94}
95
96KUrlComboBox::KUrlComboBox(Mode mode, bool rw, QWidget *parent)
97 : KComboBox(rw, parent)
98 , d(new KUrlComboBoxPrivate(this))
99{
100 d->init(mode);
101}
102
104
105void KUrlComboBoxPrivate::init(KUrlComboBox::Mode mode)
106{
107 myMode = mode;
108 urlAdded = false;
109 myMaximum = 10; // default
111 m_parent->setTrapReturnKey(true);
114 if (m_parent->completionObject()) {
116 }
117
118 opendirIcon = QIcon::fromTheme(QStringLiteral("folder-open"));
119
120 m_parent->connect(m_parent, &KUrlComboBox::activated, m_parent, [this](int index) {
121 slotActivated(index);
122 });
123}
124
125QStringList KUrlComboBox::urls() const
126{
127 // qDebug() << "::urls()";
128 QStringList list;
129 QString url;
130 for (int i = static_cast<int>(d->defaultList.size()); i < count(); i++) {
131 url = itemText(i);
132 if (!url.isEmpty()) {
133 if (Utils::isAbsoluteLocalPath(url)) {
134 list.append(QUrl::fromLocalFile(url).toString());
135 } else {
136 list.append(url);
137 }
138 }
139 }
140
141 return list;
142}
143
144void KUrlComboBox::addDefaultUrl(const QUrl &url, const QString &text)
145{
146 addDefaultUrl(url, d->getIcon(url), text);
147}
148
149void KUrlComboBox::addDefaultUrl(const QUrl &url, const QIcon &icon, const QString &text)
150{
151 d->defaultList.push_back(std::unique_ptr<KUrlComboBoxPrivate::KUrlComboItem>(new KUrlComboBoxPrivate::KUrlComboItem(url, icon, text)));
152}
153
155{
156 clear();
157 d->itemMapper.clear();
158
159 for (const auto &item : d->defaultList) {
160 d->insertUrlItem(item.get());
161 }
162}
163
165{
166 setUrls(urls, RemoveBottom);
167}
168
170{
171 setDefaults();
172 d->itemList.clear();
173 d->urlAdded = false;
174
175 if (_urls.isEmpty()) {
176 return;
177 }
178
179 QStringList urls;
181
182 // kill duplicates
183 while (it != _urls.constEnd()) {
184 if (!urls.contains(*it)) {
185 urls += *it;
186 }
187 ++it;
188 }
189
190 // limit to myMaximum items
191 /* Note: overload is an (old) C++ keyword, some compilers (KCC) choke
192 on that, so call it Overload (capital 'O'). (matz) */
193 int Overload = urls.count() - d->myMaximum + static_cast<int>(d->defaultList.size());
194 while (Overload > 0) {
195 if (remove == RemoveBottom) {
196 if (!urls.isEmpty()) {
197 urls.removeLast();
198 }
199 } else {
200 if (!urls.isEmpty()) {
201 urls.removeFirst();
202 }
203 }
204 Overload--;
205 }
206
207 it = urls.constBegin();
208
209 while (it != urls.constEnd()) {
210 if ((*it).isEmpty()) {
211 ++it;
212 continue;
213 }
214 QUrl u;
215 if (Utils::isAbsoluteLocalPath(*it)) {
216 u = QUrl::fromLocalFile(*it);
217 } else {
218 u.setUrl(*it);
219 }
220
221 // Don't restore if file doesn't exist anymore
222 if (u.isLocalFile() && !QFile::exists(u.toLocalFile())) {
223 ++it;
224 continue;
225 }
226
227 std::unique_ptr<KUrlComboBoxPrivate::KUrlComboItem> item(new KUrlComboBoxPrivate::KUrlComboItem(u, d->getIcon(u)));
228 d->insertUrlItem(item.get());
229 d->itemList.push_back(std::move(item));
230 ++it;
231 }
232}
233
235{
236 if (url.isEmpty()) {
237 return;
238 }
239
240 bool blocked = blockSignals(true);
241
242 // check for duplicates
243 auto mit = d->itemMapper.constBegin();
244 QString urlToInsert = url.toString(QUrl::StripTrailingSlash);
245 while (mit != d->itemMapper.constEnd()) {
246 Q_ASSERT(mit.value());
247
248 if (urlToInsert == mit.value()->url.toString(QUrl::StripTrailingSlash)) {
249 setCurrentIndex(mit.key());
250
251 if (d->myMode == Directories) {
252 d->updateItem(mit.value(), mit.key(), d->opendirIcon);
253 }
254
255 blockSignals(blocked);
256 return;
257 }
258 ++mit;
259 }
260
261 // not in the combo yet -> create a new item and insert it
262
263 // first remove the old item
264 if (d->urlAdded) {
265 Q_ASSERT(!d->itemList.empty());
266 d->itemList.pop_back();
267 d->urlAdded = false;
268 }
269
270 setDefaults();
271
272 const int offset = qMax(0, static_cast<int>(d->itemList.size() + d->defaultList.size()) - d->myMaximum);
273 for (size_t i = offset; i < d->itemList.size(); ++i) {
274 d->insertUrlItem(d->itemList.at(i).get());
275 }
276
277 std::unique_ptr<KUrlComboBoxPrivate::KUrlComboItem> item(new KUrlComboBoxPrivate::KUrlComboItem(url, d->getIcon(url)));
278
279 const int id = count();
280 const QString text = d->textForItem(item.get());
281 if (d->myMode == Directories) {
282 KComboBox::insertItem(id, d->opendirIcon, text);
283 } else {
284 KComboBox::insertItem(id, item->icon, text);
285 }
286
287 d->itemMapper.insert(id, item.get());
288 d->itemList.push_back(std::move(item));
289
290 setCurrentIndex(id);
291 Q_ASSERT(!d->itemList.empty());
292 d->urlAdded = true;
293 blockSignals(blocked);
294}
295
296void KUrlComboBoxPrivate::slotActivated(int index)
297{
298 auto item = itemMapper.value(index);
299
300 if (item) {
301 m_parent->setUrl(item->url);
302 Q_EMIT m_parent->urlActivated(item->url);
303 }
304}
305
306void KUrlComboBoxPrivate::insertUrlItem(const KUrlComboItem *item)
307{
308 Q_ASSERT(item);
309
310 // qDebug() << "insertURLItem " << d->textForItem(item);
311 int id = m_parent->count();
312 m_parent->KComboBox::insertItem(id, item->icon, textForItem(item));
313 itemMapper.insert(id, item);
314}
315
317{
318 d->myMaximum = max;
319
320 if (count() > d->myMaximum) {
321 int oldCurrent = currentIndex();
322
323 setDefaults();
324
325 const int offset = qMax(0, static_cast<int>(d->itemList.size() + d->defaultList.size()) - d->myMaximum);
326 for (size_t i = offset; i < d->itemList.size(); ++i) {
327 d->insertUrlItem(d->itemList.at(i).get());
328 }
329
330 if (count() > 0) { // restore the previous currentItem
331 if (oldCurrent >= count()) {
332 oldCurrent = count() - 1;
333 }
334 setCurrentIndex(oldCurrent);
335 }
336 }
337}
338
339int KUrlComboBox::maxItems() const
340{
341 return d->myMaximum;
342}
343
344void KUrlComboBox::removeUrl(const QUrl &url, bool checkDefaultURLs)
345{
346 auto mit = d->itemMapper.constBegin();
347 while (mit != d->itemMapper.constEnd()) {
348 if (url.toString(QUrl::StripTrailingSlash) == mit.value()->url.toString(QUrl::StripTrailingSlash)) {
349 auto removePredicate = [&mit](const std::unique_ptr<const KUrlComboBoxPrivate::KUrlComboItem> &item) {
350 return item.get() == mit.value();
351 };
352 d->itemList.erase(std::remove_if(d->itemList.begin(), d->itemList.end(), removePredicate), d->itemList.end());
353 if (checkDefaultURLs) {
354 d->defaultList.erase(std::remove_if(d->defaultList.begin(), d->defaultList.end(), removePredicate), d->defaultList.end());
355 }
356 }
357 ++mit;
358 }
359
360 bool blocked = blockSignals(true);
361 setDefaults();
362 for (const auto &item : d->itemList) {
363 d->insertUrlItem(item.get());
364 }
365 blockSignals(blocked);
366}
367
369{
370 if (compObj) {
371 // on a url combo box we want completion matches to be sorted. This way, if we are given
372 // a suggestion, we match the "best" one. For instance, if we have "foo" and "foobar",
373 // and we write "foo", the match is "foo" and never "foobar". (ereslibre)
375 }
377}
378
379void KUrlComboBox::mousePressEvent(QMouseEvent *event)
380{
381 QStyleOptionComboBox comboOpt;
382 comboOpt.initFrom(this);
383 const int x0 =
385 const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &comboOpt, this);
386
387 if (qRound(event->position().x()) < (x0 + KIconLoader::SizeSmall + frameWidth)) {
388 d->m_dragPoint = event->pos();
389 } else {
390 d->m_dragPoint = QPoint();
391 }
392
394}
395
396void KUrlComboBox::mouseMoveEvent(QMouseEvent *event)
397{
398 const int index = currentIndex();
399 auto item = d->itemMapper.value(index);
400
401 if (item && !d->m_dragPoint.isNull() && event->buttons() & Qt::LeftButton
402 && (event->pos() - d->m_dragPoint).manhattanLength() > QApplication::startDragDistance()) {
403 QDrag *drag = new QDrag(this);
404 QMimeData *mime = new QMimeData();
405 mime->setUrls(QList<QUrl>() << item->url);
406 mime->setText(itemText(index));
407 if (!itemIcon(index).isNull()) {
408 drag->setPixmap(itemIcon(index).pixmap(KIconLoader::SizeMedium));
409 }
410 drag->setMimeData(mime);
411 drag->exec();
412 }
413
415}
416
417QIcon KUrlComboBoxPrivate::getIcon(const QUrl &url) const
418{
419 if (myMode == KUrlComboBox::Directories) {
420 return dirIcon;
421 } else {
423 }
424}
425
426// updates "item" with icon "icon"
427// kdelibs4 used to also say "and sets the URL instead of text", but this breaks const-ness,
428// now that it would require clearing the text, and I don't see the point since the URL was already in the text.
429void KUrlComboBoxPrivate::updateItem(const KUrlComboItem *item, int index, const QIcon &icon)
430{
431 m_parent->setItemIcon(index, icon);
432 m_parent->setItemText(index, textForItem(item));
433}
434
435#include "moc_kurlcombobox.cpp"
void setTrapReturnKey(bool trap)
KCompletion * compObj() const
virtual void setCompletionObject(KCompletion *completionObject, bool handleSignals=true)
KCompletion * completionObject(bool handleSignals=true)
virtual void setOrder(CompOrder order)
This combobox shows a number of recent URLs/directories, as well as some default directories.
void setUrls(const QStringList &urls)
Inserts urls into the combobox below the "default urls" (see addDefaultUrl).
OverLoadResolving
This Enumeration is used in setUrl() to determine which items will be removed when the given list is ...
KUrlComboBox(Mode mode, QWidget *parent=nullptr)
Constructs a KUrlComboBox.
void setUrl(const QUrl &url)
Sets the current url.
~KUrlComboBox() override
Destructs the combo box.
void urlActivated(const QUrl &url)
Emitted when an item was clicked at.
void removeUrl(const QUrl &url, bool checkDefaultURLs=true)
Removes any occurrence of url.
void setCompletionObject(KCompletion *compObj, bool hsig=true) override
Reimplemented from KComboBox (from KCompletion)
void setMaxItems(int)
Sets how many items should be handled and displayed by the combobox.
Mode
This enum describes which kind of items is shown in the combo box.
void setDefaults()
Clears all items and inserts the default urls into the combo.
void addDefaultUrl(const QUrl &url, const QString &text=QString())
Adds a url that will always be shown in the combobox, it can't be "rotated away".
KIOCORE_EXPORT QString iconNameForUrl(const QUrl &url)
Return the icon name for a URL.
Definition global.cpp:186
void activated(int index)
void clear()
void setCurrentIndex(int index)
virtual bool event(QEvent *event) override
void insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
void setInsertPolicy(InsertPolicy policy)
QIcon itemIcon(int index) const const
QString itemText(int index) const const
virtual void mousePressEvent(QMouseEvent *e) override
void setItemIcon(int index, const QIcon &icon)
void setItemText(int index, const QString &text)
Qt::DropAction exec(Qt::DropActions supportedActions)
void setMimeData(QMimeData *data)
void setPixmap(const QPixmap &pixmap)
bool exists() const const
QIcon fromTheme(const QString &name)
typedef ConstIterator
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
bool isEmpty() const const
void removeFirst()
void removeLast()
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) const const
void setText(const QString &text)
void setUrls(const QList< QUrl > &urls)
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int x() const const
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
PM_DefaultFrameWidth
SC_ComboBoxEditField
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
QRect visualRect(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QRect &logicalRectangle)
void initFrom(const QWidget *widget)
LeftToRight
LeftButton
StripTrailingSlash
QUrl adjusted(FormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
void setUrl(const QString &url, ParsingMode parsingMode)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
void setLayoutDirection(Qt::LayoutDirection direction)
virtual void mouseMoveEvent(QMouseEvent *event)
void setSizePolicy(QSizePolicy)
QStyle * style() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:52 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.