1 /*
2 SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
3 SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
4 SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
5 SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10 #include "kurlnavigator.h"
11
12 #include "kurlnavigatorbutton_p.h"
13 #include "kurlnavigatordropdownbutton_p.h"
14 #include "kurlnavigatorpathselectoreventfilter_p.h"
15 #include "kurlnavigatorplacesselector_p.h"
16 #include "kurlnavigatorprotocolcombo_p.h"
17 #include "kurlnavigatortogglebutton_p.h"
18 #include "urlutil_p.h"
19
20 #include <KIO/StatJob>
21 #include <KLocalizedString>
22 #include <kfileitem.h>
23 #include <kfileplacesmodel.h>
24 #include <kprotocolinfo.h>
25 #include <kurifilter.h>
26 #include <kurlcombobox.h>
27 #include <kurlcompletion.h>
28
29 #include <QActionGroup>
30 #include <QApplication>
31 #include <QClipboard>
32 #include <QDir>
33 #include <QDropEvent>
34 #include <QHBoxLayout>
35 #include <QKeyEvent>
36 #include <QMenu>
37 #include <QMimeData>
38 #include <QMimeDatabase>
39 #include <QTimer>
40 #include <QUrlQuery>
41
42 #include <algorithm>
43
44 using namespace KDEPrivate;
45
46 struct LocationData {
LocationDataLocationData47 explicit LocationData(const QUrl &u)
48 : url(u)
49 {
50 }
51
52 QUrl url;
53 QUrl rootUrl; // KDE5: remove after the deprecated methods have been removed
54 QPoint pos; // KDE5: remove after the deprecated methods have been removed
55 QByteArray state;
56 };
57
58 class KUrlNavigatorPrivate
59 {
60 public:
61 KUrlNavigatorPrivate(KUrlNavigator *qq, KFilePlacesModel *placesModel);
62
~KUrlNavigatorPrivate()63 ~KUrlNavigatorPrivate()
64 {
65 m_dropDownButton->removeEventFilter(q);
66 m_pathBox->removeEventFilter(q);
67 m_toggleEditableMode->removeEventFilter(q);
68
69 for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
70 button->removeEventFilter(q);
71 }
72 }
73
74 /** Applies the edited URL in m_pathBox to the URL navigator */
75 void applyUncommittedUrl();
76
77 void slotReturnPressed();
78 void slotProtocolChanged(const QString &);
79 void openPathSelectorMenu();
80
81 /**
82 * Appends the widget at the end of the URL navigator. It is assured
83 * that the filler widget remains as last widget to fill the remaining
84 * width.
85 */
86 void appendWidget(QWidget *widget, int stretch = 0);
87
88 /**
89 * This slot is connected to the clicked signal of the navigation bar button. It calls switchView().
90 * Moreover, if switching from "editable" mode to the breadcrumb view, it calls applyUncommittedUrl().
91 */
92 void slotToggleEditableButtonPressed();
93
94 /**
95 * Switches the navigation bar between the breadcrumb view and the
96 * traditional view (see setUrlEditable()).
97 */
98 void switchView();
99
100 /** Emits the signal urlsDropped(). */
101 void dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton);
102
103 /**
104 * Is invoked when a navigator button has been clicked.
105 * Different combinations of mouse clicks and keyboard modifiers have different effects on how
106 * the url is opened. The behaviours are the following:
107 * - shift+middle-click or ctrl+shift+left-click => activeTabRequested() signal is emitted
108 * - ctrl+left-click or middle-click => tabRequested() signal is emitted
109 * - shift+left-click => newWindowRequested() signal is emitted
110 * - left-click => open the new url in-place
111 */
112 void slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
113
114 void openContextMenu(const QPoint &p);
115
116 void slotPathBoxChanged(const QString &text);
117
118 void updateContent();
119
120 /**
121 * Updates all buttons to have one button for each part of the
122 * current URL. Existing buttons, which are available by m_navButtons,
123 * are reused if possible. If the URL is longer, new buttons will be
124 * created, if the URL is shorter, the remaining buttons will be deleted.
125 * @param startIndex Start index of URL part (/), where the buttons
126 * should be created for each following part.
127 */
128 void updateButtons(int startIndex);
129
130 /**
131 * Updates the visibility state of all buttons describing the URL. If the
132 * width of the URL navigator is too small, the buttons representing the upper
133 * paths of the URL will be hidden and moved to a drop down menu.
134 */
135 void updateButtonVisibility();
136
137 /**
138 * @return Text for the first button of the URL navigator.
139 */
140 QString firstButtonText() const;
141
142 /**
143 * Returns the URL that should be applied for the button with the index \a index.
144 */
145 QUrl buttonUrl(int index) const;
146
147 void switchToBreadcrumbMode();
148
149 /**
150 * Deletes all URL navigator buttons. m_navButtons is
151 * empty after this operation.
152 */
153 void deleteButtons();
154
155 /**
156 * Retrieves the place url for the current url.
157 * E. g. for the path "fish://root@192.168.0.2/var/lib" the string
158 * "fish://root@192.168.0.2" will be returned, which leads to the
159 * navigation indication 'Custom Path > var > lib". For e. g.
160 * "settings:///System/" the path "settings://" will be returned.
161 */
162 QUrl retrievePlaceUrl() const;
163
164 /**
165 * Returns true, if the MIME type of the path represents a
166 * compressed file like TAR or ZIP, as listed in @p archiveMimetypes
167 */
168 bool isCompressedPath(const QUrl &path, const QStringList &archiveMimetypes) const;
169
170 void removeTrailingSlash(QString &url) const;
171
172 /**
173 * Returns the current history index, if \a historyIndex is
174 * smaller than 0. If \a historyIndex is greater or equal than
175 * the number of available history items, the largest possible
176 * history index is returned. For the other cases just \a historyIndex
177 * is returned.
178 */
179 int adjustedHistoryIndex(int historyIndex) const;
180
181 KUrlNavigator *const q;
182
183 QHBoxLayout *m_layout = new QHBoxLayout(q);
184 QList<LocationData> m_history;
185 QList<KUrlNavigatorButton *> m_navButtons;
186 QStringList m_customProtocols;
187 QUrl m_homeUrl;
188 KUrlNavigatorPlacesSelector *m_placesSelector = nullptr;
189 KUrlComboBox *m_pathBox = nullptr;
190 KUrlNavigatorProtocolCombo *m_protocols = nullptr;
191 KUrlNavigatorDropDownButton *m_dropDownButton = nullptr;
192 KUrlNavigatorButtonBase *m_toggleEditableMode = nullptr;
193 QWidget *m_dropWidget = nullptr;
194
195 bool m_editable = false;
196 bool m_active = true;
197 bool m_showPlacesSelector = false;
198 bool m_showFullPath = false;
199 int m_historyIndex = 0;
200
201 struct {
202 bool showHidden = false;
203 bool sortHiddenLast = false;
204 } m_subfolderOptions;
205 };
206
KUrlNavigatorPrivate(KUrlNavigator * qq,KFilePlacesModel * placesModel)207 KUrlNavigatorPrivate::KUrlNavigatorPrivate(KUrlNavigator *qq, KFilePlacesModel *placesModel)
208 : q(qq)
209 , m_showPlacesSelector(placesModel != nullptr)
210 {
211 m_layout->setSpacing(0);
212 m_layout->setContentsMargins(0, 0, 0, 0);
213
214 // initialize the places selector
215 q->setAutoFillBackground(false);
216
217 if (placesModel != nullptr) {
218 m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
219 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::placeActivated, q, &KUrlNavigator::setLocationUrl);
220 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::tabRequested, q, &KUrlNavigator::tabRequested);
221
222 auto updateContentFunc = [this]() {
223 updateContent();
224 };
225 q->connect(placesModel, &KFilePlacesModel::rowsInserted, q, updateContentFunc);
226 q->connect(placesModel, &KFilePlacesModel::rowsRemoved, q, updateContentFunc);
227 q->connect(placesModel, &KFilePlacesModel::dataChanged, q, updateContentFunc);
228 }
229
230 // create protocol combo
231 m_protocols = new KUrlNavigatorProtocolCombo(QString(), q);
232 q->connect(m_protocols, &KUrlNavigatorProtocolCombo::activated, q, [this](const QString &protocol) {
233 slotProtocolChanged(protocol);
234 });
235
236 // create drop down button for accessing all paths of the URL
237 m_dropDownButton = new KUrlNavigatorDropDownButton(q);
238 m_dropDownButton->setForegroundRole(QPalette::WindowText);
239 m_dropDownButton->installEventFilter(q);
240 q->connect(m_dropDownButton, &KUrlNavigatorDropDownButton::clicked, q, [this]() {
241 openPathSelectorMenu();
242 });
243
244 // initialize the path box of the traditional view
245 m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
246 m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
247 m_pathBox->installEventFilter(q);
248
249 KUrlCompletion *kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
250 m_pathBox->setCompletionObject(kurlCompletion);
251 m_pathBox->setAutoDeleteCompletionObject(true);
252
253 // TODO KF6: remove this QOverload, only KUrlComboBox::returnPressed(const QString &) will remain
254 q->connect(m_pathBox, qOverload<const QString &>(&KUrlComboBox::returnPressed), q, [this]() {
255 slotReturnPressed();
256 });
257 q->connect(m_pathBox, &KUrlComboBox::urlActivated, q, &KUrlNavigator::setLocationUrl);
258 q->connect(m_pathBox, &QComboBox::editTextChanged, q, [this](const QString &text) {
259 slotPathBoxChanged(text);
260 });
261
262 // create toggle button which allows to switch between
263 // the breadcrumb and traditional view
264 m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
265 m_toggleEditableMode->installEventFilter(q);
266 m_toggleEditableMode->setMinimumWidth(20);
267 q->connect(m_toggleEditableMode, &KUrlNavigatorToggleButton::clicked, q, [this]() {
268 slotToggleEditableButtonPressed();
269 });
270
271 if (m_placesSelector != nullptr) {
272 m_layout->addWidget(m_placesSelector);
273 }
274 m_layout->addWidget(m_protocols);
275 m_layout->addWidget(m_dropDownButton);
276 m_layout->addWidget(m_pathBox, 1);
277 m_layout->addWidget(m_toggleEditableMode);
278
279 q->setContextMenuPolicy(Qt::CustomContextMenu);
280 q->connect(q, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
281 openContextMenu(pos);
282 });
283 }
284
appendWidget(QWidget * widget,int stretch)285 void KUrlNavigatorPrivate::appendWidget(QWidget *widget, int stretch)
286 {
287 m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
288 }
289
applyUncommittedUrl()290 void KUrlNavigatorPrivate::applyUncommittedUrl()
291 {
292 auto applyUrl = [this](QUrl url) {
293 // Parts of the following code have been taken from the class KateFileSelector
294 // located in kate/app/katefileselector.hpp of Kate.
295 // SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
296 // SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
297 // SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
298
299 // For example "desktop:/" _not_ "desktop:", see the comment in slotProtocolChanged()
300 if (!url.isEmpty() && url.path().isEmpty() && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
301 url.setPath(QStringLiteral("/"));
302 }
303
304 const auto urlStr = url.toString();
305 QStringList urls = m_pathBox->urls();
306 urls.removeAll(urlStr);
307 urls.prepend(urlStr);
308 m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
309
310 q->setLocationUrl(url);
311 // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
312 // synchronize the result in the path box.
313 m_pathBox->setUrl(q->locationUrl());
314 };
315
316 const QString text = m_pathBox->currentText().trimmed();
317
318 KUriFilterData filteredData(text);
319 filteredData.setCheckForExecutables(false);
320 // Using kshorturifilter to fix up e.g. "ftp.kde.org" ---> "ftp://ftp.kde.org"
321 const auto filtersList = QStringList{QStringLiteral("kshorturifilter")};
322 if (KUriFilter::self()->filterUri(filteredData, filtersList)) {
323 applyUrl(filteredData.uri()); // The text was filtered
324 return;
325 }
326
327 QUrl url = q->locationUrl();
328 QString path = url.path();
329 if (!path.endsWith(QLatin1Char('/'))) {
330 path += QLatin1Char('/');
331 }
332 url.setPath(path + text);
333
334 // Dirs and symlinks to dirs
335 constexpr auto details = KIO::StatBasic | KIO::StatResolveSymlink;
336 auto *job = KIO::statDetails(url, KIO::StatJob::DestinationSide, details, KIO::HideProgressInfo);
337 q->connect(job, &KJob::result, q, [job, text, applyUrl]() {
338 // If there is a dir matching "text" relative to the current url, use that, e.g.
339 // typing "bar" while at "/path/to/foo", the url becomes "/path/to/foo/bar/"
340 if (!job->error() && job->statResult().isDir()) {
341 applyUrl(job->url());
342 } else { // ... otherwise fallback to whatever QUrl::fromUserInput() returns
343 applyUrl(QUrl::fromUserInput(text));
344 }
345 });
346 }
347
slotReturnPressed()348 void KUrlNavigatorPrivate::slotReturnPressed()
349 {
350 applyUncommittedUrl();
351
352 Q_EMIT q->returnPressed();
353
354 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
355 // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
356 // The switch must be done asynchronously, as we are in the context of the
357 // editor.
358 QMetaObject::invokeMethod(q, "switchToBreadcrumbMode", Qt::QueuedConnection);
359 }
360 }
361
slotProtocolChanged(const QString & protocol)362 void KUrlNavigatorPrivate::slotProtocolChanged(const QString &protocol)
363 {
364 Q_ASSERT(m_editable);
365
366 QUrl url;
367 url.setScheme(protocol);
368 if (KProtocolInfo::protocolClass(protocol) == QLatin1String(":local")) {
369 // E.g. "file:/" or "desktop:/", _not_ "file:" or "desktop:" respectively.
370 // This is the more expected behaviour, "file:somedir" treats somedir as
371 // a path relative to current dir; file:/somedir is an absolute path to /somedir.
372 url.setPath(QStringLiteral("/"));
373 } else {
374 // With no authority set we'll get e.g. "ftp:" instead of "ftp://".
375 // We want the latter, so let's set an empty authority.
376 url.setAuthority(QString());
377 }
378
379 m_pathBox->setEditUrl(url);
380 }
381
openPathSelectorMenu()382 void KUrlNavigatorPrivate::openPathSelectorMenu()
383 {
384 if (m_navButtons.count() <= 0) {
385 return;
386 }
387
388 const QUrl firstVisibleUrl = m_navButtons.constFirst()->url();
389
390 QString spacer;
391 QPointer<QMenu> popup = new QMenu(q);
392
393 auto *popupFilter = new KUrlNavigatorPathSelectorEventFilter(popup.data());
394 q->connect(popupFilter, &KUrlNavigatorPathSelectorEventFilter::tabRequested, q, &KUrlNavigator::tabRequested);
395 popup->installEventFilter(popupFilter);
396
397 popup->setLayoutDirection(Qt::LeftToRight);
398
399 const QUrl placeUrl = retrievePlaceUrl();
400 int idx = placeUrl.path().count(QLatin1Char('/')); // idx points to the first directory
401 // after the place path
402
403 const QString path = m_history.at(m_historyIndex).url.path();
404 QString dirName = path.section(QLatin1Char('/'), idx, idx);
405 if (dirName.isEmpty()) {
406 if (placeUrl.isLocalFile()) {
407 dirName = QStringLiteral("/");
408 } else {
409 dirName = placeUrl.toDisplayString();
410 }
411 }
412 do {
413 const QString text = spacer + dirName;
414
415 QAction *action = new QAction(text, popup);
416 const QUrl currentUrl = buttonUrl(idx);
417 if (currentUrl == firstVisibleUrl) {
418 popup->addSeparator();
419 }
420 action->setData(QVariant(currentUrl.toString()));
421 popup->addAction(action);
422
423 ++idx;
424 spacer.append(QLatin1String(" "));
425 dirName = path.section(QLatin1Char('/'), idx, idx);
426 } while (!dirName.isEmpty());
427
428 const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
429 const QAction *activatedAction = popup->exec(pos);
430 if (activatedAction != nullptr) {
431 const QUrl url(activatedAction->data().toString());
432 q->setLocationUrl(url);
433 }
434
435 // Delete the menu, unless it has been deleted in its own nested event loop already.
436 if (popup) {
437 popup->deleteLater();
438 }
439 }
440
slotToggleEditableButtonPressed()441 void KUrlNavigatorPrivate::slotToggleEditableButtonPressed()
442 {
443 if (m_editable) {
444 applyUncommittedUrl();
445 }
446
447 switchView();
448 }
449
switchView()450 void KUrlNavigatorPrivate::switchView()
451 {
452 m_toggleEditableMode->setFocus();
453 m_editable = !m_editable;
454 m_toggleEditableMode->setChecked(m_editable);
455 updateContent();
456 if (q->isUrlEditable()) {
457 m_pathBox->setFocus();
458 }
459
460 q->requestActivation();
461 Q_EMIT q->editableStateChanged(m_editable);
462 }
463
dropUrls(const QUrl & destination,QDropEvent * event,KUrlNavigatorButton * dropButton)464 void KUrlNavigatorPrivate::dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton)
465 {
466 if (event->mimeData()->hasUrls()) {
467 m_dropWidget = qobject_cast<QWidget *>(dropButton);
468 Q_EMIT q->urlsDropped(destination, event);
469 }
470 }
471
slotNavigatorButtonClicked(const QUrl & url,Qt::MouseButton button,Qt::KeyboardModifiers modifiers)472 void KUrlNavigatorPrivate::slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
473 {
474 if ((button & Qt::MiddleButton && modifiers & Qt::ShiftModifier) || (button & Qt::LeftButton && modifiers & (Qt::ControlModifier | Qt::ShiftModifier))) {
475 Q_EMIT q->activeTabRequested(url);
476 } else if (button & Qt::MiddleButton || (button & Qt::LeftButton && modifiers & Qt::ControlModifier)) {
477 Q_EMIT q->tabRequested(url);
478 } else if (button & Qt::LeftButton && modifiers & Qt::ShiftModifier) {
479 Q_EMIT q->newWindowRequested(url);
480 } else if (button & Qt::LeftButton) {
481 q->setLocationUrl(url);
482 }
483 }
484
openContextMenu(const QPoint & p)485 void KUrlNavigatorPrivate::openContextMenu(const QPoint &p)
486 {
487 q->setActive(true);
488
489 QPointer<QMenu> popup = new QMenu(q);
490
491 // provide 'Copy' action, which copies the current URL of
492 // the URL navigator into the clipboard
493 QAction *copyAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"));
494
495 // provide 'Paste' action, which copies the current clipboard text
496 // into the URL navigator
497 QAction *pasteAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("Paste"));
498 QClipboard *clipboard = QApplication::clipboard();
499 pasteAction->setEnabled(!clipboard->text().isEmpty());
500
501 popup->addSeparator();
502
503 // We are checking for receivers because it's odd to have a tab entry even
504 // if it's not supported, like in the case of the open dialog
505 if (q->receivers(SIGNAL(tabRequested(QUrl))) > 0) {
506 for (auto button : std::as_const(m_navButtons)) {
507 if (button->geometry().contains(p)) {
508 const auto url = button->url();
509 QAction *openInTab = popup->addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18n("Open %1 in tab", button->text()));
510 q->connect(openInTab, &QAction::triggered, q, [this, url]() {
511 Q_EMIT q->tabRequested(url);
512 });
513 break;
514 }
515 }
516 }
517
518 // provide radiobuttons for toggling between the edit and the navigation mode
519 QAction *editAction = popup->addAction(i18n("Edit"));
520 editAction->setCheckable(true);
521
522 QAction *navigateAction = popup->addAction(i18n("Navigate"));
523 navigateAction->setCheckable(true);
524
525 QActionGroup *modeGroup = new QActionGroup(popup);
526 modeGroup->addAction(editAction);
527 modeGroup->addAction(navigateAction);
528 if (q->isUrlEditable()) {
529 editAction->setChecked(true);
530 } else {
531 navigateAction->setChecked(true);
532 }
533
534 popup->addSeparator();
535
536 // allow showing of the full path
537 QAction *showFullPathAction = popup->addAction(i18n("Show Full Path"));
538 showFullPathAction->setCheckable(true);
539 showFullPathAction->setChecked(q->showFullPath());
540
541 QAction *activatedAction = popup->exec(QCursor::pos());
542 if (activatedAction == copyAction) {
543 QMimeData *mimeData = new QMimeData();
544 mimeData->setText(q->locationUrl().toDisplayString(QUrl::PreferLocalFile));
545 clipboard->setMimeData(mimeData);
546 } else if (activatedAction == pasteAction) {
547 q->setLocationUrl(QUrl::fromUserInput(clipboard->text()));
548 } else if (activatedAction == editAction) {
549 q->setUrlEditable(true);
550 } else if (activatedAction == navigateAction) {
551 q->setUrlEditable(false);
552 } else if (activatedAction == showFullPathAction) {
553 q->setShowFullPath(showFullPathAction->isChecked());
554 }
555
556 // Delete the menu, unless it has been deleted in its own nested event loop already.
557 if (popup) {
558 popup->deleteLater();
559 }
560 }
561
slotPathBoxChanged(const QString & text)562 void KUrlNavigatorPrivate::slotPathBoxChanged(const QString &text)
563 {
564 if (text.isEmpty()) {
565 const QString protocol = q->locationUrl().scheme();
566 m_protocols->setProtocol(protocol);
567 if (m_customProtocols.count() != 1) {
568 m_protocols->show();
569 }
570 } else {
571 m_protocols->hide();
572 }
573 }
574
updateContent()575 void KUrlNavigatorPrivate::updateContent()
576 {
577 const QUrl currentUrl = q->locationUrl();
578 if (m_placesSelector != nullptr) {
579 m_placesSelector->updateSelection(currentUrl);
580 }
581
582 if (m_editable) {
583 m_protocols->hide();
584 m_dropDownButton->hide();
585
586 deleteButtons();
587 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
588 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
589
590 m_pathBox->show();
591 m_pathBox->setUrl(currentUrl);
592 } else {
593 m_pathBox->hide();
594
595 m_protocols->hide();
596
597 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
598 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
599
600 // Calculate the start index for the directories that should be shown as buttons
601 // and create the buttons
602 QUrl placeUrl;
603 if ((m_placesSelector != nullptr) && !m_showFullPath) {
604 placeUrl = m_placesSelector->selectedPlaceUrl();
605 }
606
607 if (!placeUrl.isValid()) {
608 placeUrl = retrievePlaceUrl();
609 }
610 QString placePath = placeUrl.path();
611 removeTrailingSlash(placePath);
612
613 const int startIndex = placePath.count(QLatin1Char('/'));
614 updateButtons(startIndex);
615 }
616 }
617
updateButtons(int startIndex)618 void KUrlNavigatorPrivate::updateButtons(int startIndex)
619 {
620 QUrl currentUrl = q->locationUrl();
621 if (!currentUrl.isValid()) { // QFileDialog::setDirectory not called yet
622 return;
623 }
624
625 const QString path = currentUrl.path();
626
627 bool createButton = false;
628 const int oldButtonCount = m_navButtons.count();
629
630 int idx = startIndex;
631 bool hasNext = true;
632 do {
633 createButton = (idx - startIndex >= oldButtonCount);
634 const bool isFirstButton = (idx == startIndex);
635 const QString dirName = path.section(QLatin1Char('/'), idx, idx);
636 hasNext = isFirstButton || !dirName.isEmpty();
637 if (hasNext) {
638 KUrlNavigatorButton *button = nullptr;
639 if (createButton) {
640 button = new KUrlNavigatorButton(buttonUrl(idx), q);
641 button->installEventFilter(q);
642 button->setForegroundRole(QPalette::WindowText);
643 q->connect(button, &KUrlNavigatorButton::urlsDroppedOnNavButton, q, [this, button](const QUrl &destination, QDropEvent *event) {
644 dropUrls(destination, event, button);
645 });
646
647 q->connect(button, &KUrlNavigatorButton::navigatorButtonActivated, q, [this](const QUrl &url, Qt::MouseButton btn, Qt::KeyboardModifiers modifiers) {
648 slotNavigatorButtonClicked(url, btn, modifiers);
649 });
650
651 q->connect(button, &KUrlNavigatorButton::finishedTextResolving, q, [this]() {
652 updateButtonVisibility();
653 });
654
655 appendWidget(button);
656 } else {
657 button = m_navButtons[idx - startIndex];
658 button->setUrl(buttonUrl(idx));
659 }
660
661 if (isFirstButton) {
662 button->setText(firstButtonText());
663 }
664 button->setActive(q->isActive());
665
666 if (createButton) {
667 if (!isFirstButton) {
668 q->setTabOrder(m_navButtons.constLast(), button);
669 }
670 m_navButtons.append(button);
671 }
672
673 ++idx;
674 button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
675 }
676 } while (hasNext);
677
678 // delete buttons which are not used anymore
679 const int newButtonCount = idx - startIndex;
680 if (newButtonCount < oldButtonCount) {
681 const auto itBegin = m_navButtons.begin() + newButtonCount;
682 const auto itEnd = m_navButtons.end();
683 for (auto it = itBegin; it != itEnd; ++it) {
684 auto *navBtn = *it;
685 navBtn->hide();
686 navBtn->deleteLater();
687 }
688 m_navButtons.erase(itBegin, itEnd);
689 }
690
691 q->setTabOrder(m_dropDownButton, m_navButtons.constFirst());
692 q->setTabOrder(m_navButtons.constLast(), m_toggleEditableMode);
693
694 updateButtonVisibility();
695 }
696
updateButtonVisibility()697 void KUrlNavigatorPrivate::updateButtonVisibility()
698 {
699 if (m_editable) {
700 return;
701 }
702
703 const int buttonsCount = m_navButtons.count();
704 if (buttonsCount == 0) {
705 m_dropDownButton->hide();
706 return;
707 }
708
709 // Subtract all widgets from the available width, that must be shown anyway
710 int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
711
712 if ((m_placesSelector != nullptr) && m_placesSelector->isVisible()) {
713 availableWidth -= m_placesSelector->width();
714 }
715
716 if ((m_protocols != nullptr) && m_protocols->isVisible()) {
717 availableWidth -= m_protocols->width();
718 }
719
720 // Check whether buttons must be hidden at all...
721 int requiredButtonWidth = 0;
722 for (const KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
723 requiredButtonWidth += button->minimumWidth();
724 }
725
726 if (requiredButtonWidth > availableWidth) {
727 // At least one button must be hidden. This implies that the
728 // drop-down button must get visible, which again decreases the
729 // available width.
730 availableWidth -= m_dropDownButton->width();
731 }
732
733 // Hide buttons...
734 bool isLastButton = true;
735 bool hasHiddenButtons = false;
736 QList<KUrlNavigatorButton *> buttonsToShow;
737 for (auto it = m_navButtons.crbegin(); it != m_navButtons.crend(); ++it) {
738 KUrlNavigatorButton *button = *it;
739 availableWidth -= button->minimumWidth();
740 if ((availableWidth <= 0) && !isLastButton) {
741 button->hide();
742 hasHiddenButtons = true;
743 } else {
744 // Don't show the button immediately, as setActive()
745 // might change the size and a relayout gets triggered
746 // after showing the button. So the showing of all buttons
747 // is postponed until all buttons have the correct
748 // activation state.
749 buttonsToShow.append(button);
750 }
751 isLastButton = false;
752 }
753
754 // All buttons have the correct activation state and
755 // can be shown now
756 for (KUrlNavigatorButton *button : std::as_const(buttonsToShow)) {
757 button->show();
758 }
759
760 if (hasHiddenButtons) {
761 m_dropDownButton->show();
762 } else {
763 // Check whether going upwards is possible. If this is the case, show the drop-down button.
764 QUrl url(m_navButtons.front()->url());
765 const bool visible = !url.matches(KIO::upUrl(url), QUrl::StripTrailingSlash)
766 && url.scheme() != QLatin1String("baloosearch")
767 && url.scheme() != QLatin1String("filenamesearch");
768 m_dropDownButton->setVisible(visible);
769 }
770 }
771
firstButtonText() const772 QString KUrlNavigatorPrivate::firstButtonText() const
773 {
774 QString text;
775
776 // The first URL navigator button should get the name of the
777 // place instead of the directory name
778 if ((m_placesSelector != nullptr) && !m_showFullPath) {
779 text = m_placesSelector->selectedPlaceText();
780 }
781
782 const QUrl currentUrl = q->locationUrl();
783
784 if (text.isEmpty()) {
785 if (currentUrl.isLocalFile()) {
786 #ifdef Q_OS_WIN
787 text = currentUrl.path().length() > 1 ? currentUrl.path().left(2) : QDir::rootPath();
788 #else
789 text = QStringLiteral("/");
790 #endif
791 }
792 }
793
794 if (text.isEmpty()) {
795 if (currentUrl.path().isEmpty() || currentUrl.path() == QLatin1Char('/')) {
796 QUrlQuery query(currentUrl);
797 text = query.queryItemValue(QStringLiteral("title"));
798 }
799 }
800
801 if (text.isEmpty()) {
802 text = currentUrl.scheme() + QLatin1Char(':');
803 if (!currentUrl.host().isEmpty()) {
804 text += QLatin1Char(' ') + currentUrl.host();
805 }
806 }
807
808 return text;
809 }
810
buttonUrl(int index) const811 QUrl KUrlNavigatorPrivate::buttonUrl(int index) const
812 {
813 if (index < 0) {
814 index = 0;
815 }
816
817 // Keep scheme, hostname etc. as this is needed for e. g. browsing
818 // FTP directories
819 QUrl url = q->locationUrl();
820 QString path = url.path();
821
822 if (!path.isEmpty()) {
823 if (index == 0) {
824 // prevent the last "/" from being stripped
825 // or we end up with an empty path
826 #ifdef Q_OS_WIN
827 path = path.length() > 1 ? path.left(2) : QDir::rootPath();
828 #else
829 path = QStringLiteral("/");
830 #endif
831 } else {
832 path = path.section(QLatin1Char('/'), 0, index);
833 }
834 }
835
836 url.setPath(path);
837 return url;
838 }
839
switchToBreadcrumbMode()840 void KUrlNavigatorPrivate::switchToBreadcrumbMode()
841 {
842 q->setUrlEditable(false);
843 }
844
deleteButtons()845 void KUrlNavigatorPrivate::deleteButtons()
846 {
847 for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
848 button->hide();
849 button->deleteLater();
850 }
851 m_navButtons.clear();
852 }
853
retrievePlaceUrl() const854 QUrl KUrlNavigatorPrivate::retrievePlaceUrl() const
855 {
856 QUrl currentUrl = q->locationUrl();
857 currentUrl.setPath(QString());
858 return currentUrl;
859 }
860
isCompressedPath(const QUrl & url,const QStringList & archiveMimetypes) const861 bool KUrlNavigatorPrivate::isCompressedPath(const QUrl &url, const QStringList &archiveMimetypes) const
862 {
863 QMimeDatabase db;
864 const QMimeType mime = db.mimeTypeForUrl(QUrl(url.toString(QUrl::StripTrailingSlash)));
865 return std::any_of(archiveMimetypes.begin(), archiveMimetypes.end(), [mime](const QString &archiveType) {
866 return mime.inherits(archiveType);
867 });
868 }
869
removeTrailingSlash(QString & url) const870 void KUrlNavigatorPrivate::removeTrailingSlash(QString &url) const
871 {
872 const int length = url.length();
873 if ((length > 0) && (url.at(length - 1) == QLatin1Char('/'))) {
874 url.remove(length - 1, 1);
875 }
876 }
877
adjustedHistoryIndex(int historyIndex) const878 int KUrlNavigatorPrivate::adjustedHistoryIndex(int historyIndex) const
879 {
880 const int historySize = m_history.size();
881 if (historyIndex < 0) {
882 historyIndex = m_historyIndex;
883 } else if (historyIndex >= historySize) {
884 historyIndex = historySize - 1;
885 Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0
886 }
887 return historyIndex;
888 }
889
890 // ------------------------------------------------------------------------------------------------
891
KUrlNavigator(QWidget * parent)892 KUrlNavigator::KUrlNavigator(QWidget *parent)
893 : KUrlNavigator(nullptr, QUrl{}, parent)
894 {
895 }
896
KUrlNavigator(KFilePlacesModel * placesModel,const QUrl & url,QWidget * parent)897 KUrlNavigator::KUrlNavigator(KFilePlacesModel *placesModel, const QUrl &url, QWidget *parent)
898 : QWidget(parent)
899 , d(new KUrlNavigatorPrivate(this, placesModel))
900 {
901 d->m_history.prepend(LocationData{url.adjusted(QUrl::NormalizePathSegments)});
902
903 setLayoutDirection(Qt::LeftToRight);
904
905 const int minHeight = d->m_pathBox->sizeHint().height();
906 setMinimumHeight(minHeight);
907
908 setMinimumWidth(100);
909
910 d->updateContent();
911 }
912
913 KUrlNavigator::~KUrlNavigator() = default;
914
locationUrl(int historyIndex) const915 QUrl KUrlNavigator::locationUrl(int historyIndex) const
916 {
917 historyIndex = d->adjustedHistoryIndex(historyIndex);
918 return d->m_history.at(historyIndex).url;
919 }
920
saveLocationState(const QByteArray & state)921 void KUrlNavigator::saveLocationState(const QByteArray &state)
922 {
923 d->m_history[d->m_historyIndex].state = state;
924 }
925
locationState(int historyIndex) const926 QByteArray KUrlNavigator::locationState(int historyIndex) const
927 {
928 historyIndex = d->adjustedHistoryIndex(historyIndex);
929 return d->m_history.at(historyIndex).state;
930 }
931
goBack()932 bool KUrlNavigator::goBack()
933 {
934 const int count = d->m_history.size();
935 if (d->m_historyIndex < count - 1) {
936 const QUrl newUrl = locationUrl(d->m_historyIndex + 1);
937 Q_EMIT urlAboutToBeChanged(newUrl);
938
939 ++d->m_historyIndex;
940 d->updateContent();
941
942 Q_EMIT historyChanged();
943 Q_EMIT urlChanged(locationUrl());
944 return true;
945 }
946
947 return false;
948 }
949
goForward()950 bool KUrlNavigator::goForward()
951 {
952 if (d->m_historyIndex > 0) {
953 const QUrl newUrl = locationUrl(d->m_historyIndex - 1);
954 Q_EMIT urlAboutToBeChanged(newUrl);
955
956 --d->m_historyIndex;
957 d->updateContent();
958
959 Q_EMIT historyChanged();
960 Q_EMIT urlChanged(locationUrl());
961 return true;
962 }
963
964 return false;
965 }
966
goUp()967 bool KUrlNavigator::goUp()
968 {
969 const QUrl currentUrl = locationUrl();
970 const QUrl upUrl = KIO::upUrl(currentUrl);
971 if (upUrl != currentUrl) { // TODO use url.matches(KIO::upUrl(url), QUrl::StripTrailingSlash)
972 setLocationUrl(upUrl);
973 return true;
974 }
975
976 return false;
977 }
978
goHome()979 void KUrlNavigator::goHome()
980 {
981 if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
982 setLocationUrl(QUrl::fromLocalFile(QDir::homePath()));
983 } else {
984 setLocationUrl(d->m_homeUrl);
985 }
986 }
987
setHomeUrl(const QUrl & url)988 void KUrlNavigator::setHomeUrl(const QUrl &url)
989 {
990 d->m_homeUrl = url;
991 }
992
homeUrl() const993 QUrl KUrlNavigator::homeUrl() const
994 {
995 return d->m_homeUrl;
996 }
997
setUrlEditable(bool editable)998 void KUrlNavigator::setUrlEditable(bool editable)
999 {
1000 if (d->m_editable != editable) {
1001 d->switchView();
1002 }
1003 }
1004
isUrlEditable() const1005 bool KUrlNavigator::isUrlEditable() const
1006 {
1007 return d->m_editable;
1008 }
1009
setShowFullPath(bool show)1010 void KUrlNavigator::setShowFullPath(bool show)
1011 {
1012 if (d->m_showFullPath != show) {
1013 d->m_showFullPath = show;
1014 d->updateContent();
1015 }
1016 }
1017
showFullPath() const1018 bool KUrlNavigator::showFullPath() const
1019 {
1020 return d->m_showFullPath;
1021 }
1022
setActive(bool active)1023 void KUrlNavigator::setActive(bool active)
1024 {
1025 if (active != d->m_active) {
1026 d->m_active = active;
1027
1028 d->m_dropDownButton->setActive(active);
1029 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1030 button->setActive(active);
1031 }
1032
1033 update();
1034 if (active) {
1035 Q_EMIT activated();
1036 }
1037 }
1038 }
1039
isActive() const1040 bool KUrlNavigator::isActive() const
1041 {
1042 return d->m_active;
1043 }
1044
setPlacesSelectorVisible(bool visible)1045 void KUrlNavigator::setPlacesSelectorVisible(bool visible)
1046 {
1047 if (visible == d->m_showPlacesSelector) {
1048 return;
1049 }
1050
1051 if (visible && (d->m_placesSelector == nullptr)) {
1052 // the places selector cannot get visible as no
1053 // places model is available
1054 return;
1055 }
1056
1057 d->m_showPlacesSelector = visible;
1058 d->m_placesSelector->setVisible(visible);
1059 }
1060
isPlacesSelectorVisible() const1061 bool KUrlNavigator::isPlacesSelectorVisible() const
1062 {
1063 return d->m_showPlacesSelector;
1064 }
1065
uncommittedUrl() const1066 QUrl KUrlNavigator::uncommittedUrl() const
1067 {
1068 KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
1069 filteredData.setCheckForExecutables(false);
1070 if (KUriFilter::self()->filterUri(filteredData, QStringList{QStringLiteral("kshorturifilter")})) {
1071 return filteredData.uri();
1072 } else {
1073 return QUrl::fromUserInput(filteredData.typedString());
1074 }
1075 }
1076
setLocationUrl(const QUrl & newUrl)1077 void KUrlNavigator::setLocationUrl(const QUrl &newUrl)
1078 {
1079 if (newUrl == locationUrl()) {
1080 return;
1081 }
1082
1083 QUrl url = newUrl.adjusted(QUrl::NormalizePathSegments);
1084
1085 // This will be used below; we define it here because in the lower part of the
1086 // code locationUrl() and url become the same URLs
1087 QUrl firstChildUrl = KIO::UrlUtil::firstChildUrl(locationUrl(), url);
1088
1089 const QString scheme = url.scheme();
1090 if (!scheme.isEmpty()) {
1091 // Check if the URL represents a tar-, zip- or 7z-file, or an archive file supported by krarc.
1092 const QStringList archiveMimetypes = KProtocolInfo::archiveMimetypes(scheme);
1093
1094 if (!archiveMimetypes.isEmpty()) {
1095 // Check whether the URL is really part of the archive file, otherwise
1096 // replace it by the local path again.
1097 bool insideCompressedPath = d->isCompressedPath(url, archiveMimetypes);
1098 if (!insideCompressedPath) {
1099 QUrl prevUrl = url;
1100 QUrl parentUrl = KIO::upUrl(url);
1101 while (parentUrl != prevUrl) {
1102 if (d->isCompressedPath(parentUrl, archiveMimetypes)) {
1103 insideCompressedPath = true;
1104 break;
1105 }
1106 prevUrl = parentUrl;
1107 parentUrl = KIO::upUrl(parentUrl);
1108 }
1109 }
1110 if (!insideCompressedPath) {
1111 // drop the tar:, zip:, sevenz: or krarc: protocol since we are not
1112 // inside the compressed path
1113 url.setScheme(QStringLiteral("file"));
1114 firstChildUrl.setScheme(QStringLiteral("file"));
1115 }
1116 }
1117 }
1118
1119 // Check whether current history element has the same URL.
1120 // If this is the case, just ignore setting the URL.
1121 const LocationData &data = d->m_history.at(d->m_historyIndex);
1122 const bool isUrlEqual = url.matches(locationUrl(), QUrl::StripTrailingSlash) || (!url.isValid() && url.matches(data.url, QUrl::StripTrailingSlash));
1123 if (isUrlEqual) {
1124 return;
1125 }
1126
1127 Q_EMIT urlAboutToBeChanged(url);
1128
1129 if (d->m_historyIndex > 0) {
1130 // If an URL is set when the history index is not at the end (= 0),
1131 // then clear all previous history elements so that a new history
1132 // tree is started from the current position.
1133 auto begin = d->m_history.begin();
1134 auto end = begin + d->m_historyIndex;
1135 d->m_history.erase(begin, end);
1136 d->m_historyIndex = 0;
1137 }
1138
1139 Q_ASSERT(d->m_historyIndex == 0);
1140 d->m_history.insert(0, LocationData{url});
1141
1142 // Prevent an endless growing of the history: remembering
1143 // the last 100 Urls should be enough...
1144 const int historyMax = 100;
1145 if (d->m_history.size() > historyMax) {
1146 auto begin = d->m_history.begin() + historyMax;
1147 auto end = d->m_history.end();
1148 d->m_history.erase(begin, end);
1149 }
1150
1151 Q_EMIT historyChanged();
1152 Q_EMIT urlChanged(url);
1153
1154 KUrlCompletion *urlCompletion = qobject_cast<KUrlCompletion *>(d->m_pathBox->completionObject());
1155 if (urlCompletion) {
1156 urlCompletion->setDir(url);
1157 }
1158
1159 if (firstChildUrl.isValid()) {
1160 Q_EMIT urlSelectionRequested(firstChildUrl);
1161 }
1162
1163 d->updateContent();
1164
1165 requestActivation();
1166 }
1167
requestActivation()1168 void KUrlNavigator::requestActivation()
1169 {
1170 setActive(true);
1171 }
1172
setFocus()1173 void KUrlNavigator::setFocus()
1174 {
1175 if (isUrlEditable()) {
1176 d->m_pathBox->setFocus();
1177 } else {
1178 QWidget::setFocus();
1179 }
1180 }
1181
1182 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
setUrl(const QUrl & url)1183 void KUrlNavigator::setUrl(const QUrl &url)
1184 {
1185 // deprecated
1186 setLocationUrl(url);
1187 }
1188 #endif
1189
1190 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
saveRootUrl(const QUrl & url)1191 void KUrlNavigator::saveRootUrl(const QUrl &url)
1192 {
1193 // deprecated
1194 d->m_history[d->m_historyIndex].rootUrl = url;
1195 }
1196 #endif
1197
1198 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
savePosition(int x,int y)1199 void KUrlNavigator::savePosition(int x, int y)
1200 {
1201 // deprecated
1202 d->m_history[d->m_historyIndex].pos = QPoint(x, y);
1203 }
1204 #endif
1205
keyPressEvent(QKeyEvent * event)1206 void KUrlNavigator::keyPressEvent(QKeyEvent *event)
1207 {
1208 if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
1209 setUrlEditable(false);
1210 } else {
1211 QWidget::keyPressEvent(event);
1212 }
1213 }
1214
keyReleaseEvent(QKeyEvent * event)1215 void KUrlNavigator::keyReleaseEvent(QKeyEvent *event)
1216 {
1217 QWidget::keyReleaseEvent(event);
1218 }
1219
mousePressEvent(QMouseEvent * event)1220 void KUrlNavigator::mousePressEvent(QMouseEvent *event)
1221 {
1222 if (event->button() == Qt::MiddleButton) {
1223 requestActivation();
1224 }
1225 QWidget::mousePressEvent(event);
1226 }
1227
mouseReleaseEvent(QMouseEvent * event)1228 void KUrlNavigator::mouseReleaseEvent(QMouseEvent *event)
1229 {
1230 if (event->button() == Qt::MiddleButton) {
1231 const QRect bounds = d->m_toggleEditableMode->geometry();
1232 if (bounds.contains(event->pos())) {
1233 // The middle mouse button has been clicked above the
1234 // toggle-editable-mode-button. Paste the clipboard content
1235 // as location URL.
1236 QClipboard *clipboard = QApplication::clipboard();
1237 const QMimeData *mimeData = clipboard->mimeData();
1238 if (mimeData->hasText()) {
1239 const QString text = mimeData->text();
1240 setLocationUrl(QUrl::fromUserInput(text));
1241 }
1242 }
1243 }
1244 QWidget::mouseReleaseEvent(event);
1245 }
1246
resizeEvent(QResizeEvent * event)1247 void KUrlNavigator::resizeEvent(QResizeEvent *event)
1248 {
1249 QTimer::singleShot(0, this, [this]() {
1250 d->updateButtonVisibility();
1251 });
1252 QWidget::resizeEvent(event);
1253 }
1254
wheelEvent(QWheelEvent * event)1255 void KUrlNavigator::wheelEvent(QWheelEvent *event)
1256 {
1257 setActive(true);
1258 QWidget::wheelEvent(event);
1259 }
1260
eventFilter(QObject * watched,QEvent * event)1261 bool KUrlNavigator::eventFilter(QObject *watched, QEvent *event)
1262 {
1263 switch (event->type()) {
1264 case QEvent::FocusIn:
1265 if (watched == d->m_pathBox) {
1266 requestActivation();
1267 setFocus();
1268 }
1269 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1270 button->setShowMnemonic(true);
1271 }
1272 break;
1273
1274 case QEvent::FocusOut:
1275 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1276 button->setShowMnemonic(false);
1277 }
1278 break;
1279
1280 default:
1281 break;
1282 }
1283
1284 return QWidget::eventFilter(watched, event);
1285 }
1286
historySize() const1287 int KUrlNavigator::historySize() const
1288 {
1289 return d->m_history.count();
1290 }
1291
historyIndex() const1292 int KUrlNavigator::historyIndex() const
1293 {
1294 return d->m_historyIndex;
1295 }
1296
editor() const1297 KUrlComboBox *KUrlNavigator::editor() const
1298 {
1299 return d->m_pathBox;
1300 }
1301
setCustomProtocols(const QStringList & protocols)1302 void KUrlNavigator::setCustomProtocols(const QStringList &protocols)
1303 {
1304 d->m_customProtocols = protocols;
1305 d->m_protocols->setCustomProtocols(d->m_customProtocols);
1306 }
1307
customProtocols() const1308 QStringList KUrlNavigator::customProtocols() const
1309 {
1310 return d->m_customProtocols;
1311 }
1312
dropWidget() const1313 QWidget *KUrlNavigator::dropWidget() const
1314 {
1315 return d->m_dropWidget;
1316 }
1317
setShowHiddenFolders(bool showHiddenFolders)1318 void KUrlNavigator::setShowHiddenFolders(bool showHiddenFolders)
1319 {
1320 d->m_subfolderOptions.showHidden = showHiddenFolders;
1321 }
1322
showHiddenFolders() const1323 bool KUrlNavigator::showHiddenFolders() const
1324 {
1325 return d->m_subfolderOptions.showHidden;
1326 }
1327
setSortHiddenFoldersLast(bool sortHiddenFoldersLast)1328 void KUrlNavigator::setSortHiddenFoldersLast(bool sortHiddenFoldersLast)
1329 {
1330 d->m_subfolderOptions.sortHiddenLast = sortHiddenFoldersLast;
1331 }
1332
sortHiddenFoldersLast() const1333 bool KUrlNavigator::sortHiddenFoldersLast() const
1334 {
1335 return d->m_subfolderOptions.sortHiddenLast;
1336 }
1337
1338 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
url() const1339 const QUrl &KUrlNavigator::url() const
1340 {
1341 // deprecated
1342
1343 // Workaround required because of flawed interface ('const QUrl&' is returned
1344 // instead of 'QUrl'): remember the URL to prevent a dangling pointer
1345 static QUrl url;
1346 url = locationUrl();
1347 return url;
1348 }
1349 #endif
1350
1351 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
url(int index) const1352 QUrl KUrlNavigator::url(int index) const
1353 {
1354 // deprecated
1355 return d->buttonUrl(index);
1356 }
1357 #endif
1358
1359 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
historyUrl(int historyIndex) const1360 QUrl KUrlNavigator::historyUrl(int historyIndex) const
1361 {
1362 // deprecated
1363 return locationUrl(historyIndex);
1364 }
1365 #endif
1366
1367 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
savedRootUrl() const1368 const QUrl &KUrlNavigator::savedRootUrl() const
1369 {
1370 // deprecated
1371
1372 // Workaround required because of flawed interface ('const QUrl&' is returned
1373 // instead of 'QUrl'): remember the root URL to prevent a dangling pointer
1374 static QUrl rootUrl;
1375 rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1376 return rootUrl;
1377 }
1378 #endif
1379
1380 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
savedPosition() const1381 QPoint KUrlNavigator::savedPosition() const
1382 {
1383 // deprecated
1384 return d->m_history[d->m_historyIndex].pos;
1385 }
1386 #endif
1387
1388 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
setHomeUrl(const QString & homeUrl)1389 void KUrlNavigator::setHomeUrl(const QString &homeUrl)
1390 {
1391 // deprecated
1392 setLocationUrl(QUrl::fromUserInput(homeUrl));
1393 }
1394 #endif
1395
1396 #include "moc_kurlnavigator.cpp"
1397