File indexing completed on 2024-04-28 15:26:48

0001 /*
0002     SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
0003     SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
0004     SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
0005     SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kurlnavigator.h"
0011 #include "kcoreurlnavigator.h"
0012 
0013 #include "../utils_p.h"
0014 #include "kurlnavigatorbutton_p.h"
0015 #include "kurlnavigatordropdownbutton_p.h"
0016 #include "kurlnavigatorpathselectoreventfilter_p.h"
0017 #include "kurlnavigatorplacesselector_p.h"
0018 #include "kurlnavigatorprotocolcombo_p.h"
0019 #include "kurlnavigatortogglebutton_p.h"
0020 
0021 #include <KIO/StatJob>
0022 #include <KLocalizedString>
0023 #include <kfileitem.h>
0024 #include <kfileplacesmodel.h>
0025 #include <kprotocolinfo.h>
0026 #include <kurifilter.h>
0027 #include <kurlcombobox.h>
0028 #include <kurlcompletion.h>
0029 
0030 #include <QActionGroup>
0031 #include <QApplication>
0032 #include <QClipboard>
0033 #include <QDir>
0034 #include <QDropEvent>
0035 #include <QHBoxLayout>
0036 #include <QKeyEvent>
0037 #include <QMenu>
0038 #include <QMetaMethod>
0039 #include <QMimeData>
0040 #include <QMimeDatabase>
0041 #include <QTimer>
0042 #include <QUrlQuery>
0043 
0044 #include <algorithm>
0045 #include <numeric>
0046 
0047 using namespace KDEPrivate;
0048 
0049 struct KUrlNavigatorData {
0050     QUrl rootUrl; // KF6: remove after the deprecated methods have been removed
0051     QPoint pos; // KF6: remove after the deprecated methods have been removed
0052     QByteArray state;
0053 };
0054 Q_DECLARE_METATYPE(KUrlNavigatorData)
0055 
0056 class KUrlNavigatorPrivate
0057 {
0058 public:
0059     KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, KFilePlacesModel *placesModel);
0060 
0061     ~KUrlNavigatorPrivate()
0062     {
0063         m_dropDownButton->removeEventFilter(q);
0064         m_pathBox->removeEventFilter(q);
0065         m_toggleEditableMode->removeEventFilter(q);
0066 
0067         for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
0068             button->removeEventFilter(q);
0069         }
0070     }
0071 
0072     /** Applies the edited URL in m_pathBox to the URL navigator */
0073     void applyUncommittedUrl();
0074     void slotApplyUrl(QUrl url);
0075     // Returns true if "text" matched a URI filter (i.e. was fitlered),
0076     // otherwise returns false
0077     bool slotCheckFilters(const QString &text);
0078 
0079     void slotReturnPressed();
0080     void slotProtocolChanged(const QString &);
0081     void openPathSelectorMenu();
0082 
0083     /**
0084      * Appends the widget at the end of the URL navigator. It is assured
0085      * that the filler widget remains as last widget to fill the remaining
0086      * width.
0087      */
0088     void appendWidget(QWidget *widget, int stretch = 0);
0089 
0090     /**
0091      * This slot is connected to the clicked signal of the navigation bar button. It calls switchView().
0092      * Moreover, if switching from "editable" mode to the breadcrumb view, it calls applyUncommittedUrl().
0093      */
0094     void slotToggleEditableButtonPressed();
0095 
0096     /**
0097      * Switches the navigation bar between the breadcrumb view and the
0098      * traditional view (see setUrlEditable()).
0099      */
0100     void switchView();
0101 
0102     /** Emits the signal urlsDropped(). */
0103     void dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton);
0104 
0105     /**
0106      * Is invoked when a navigator button has been clicked.
0107      * Different combinations of mouse clicks and keyboard modifiers have different effects on how
0108      * the url is opened. The behaviours are the following:
0109      * - shift+middle-click or ctrl+shift+left-click => activeTabRequested() signal is emitted
0110      * - ctrl+left-click or middle-click => tabRequested() signal is emitted
0111      * - shift+left-click => newWindowRequested() signal is emitted
0112      * - left-click => open the new url in-place
0113      */
0114     void slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
0115 
0116     void openContextMenu(const QPoint &p);
0117 
0118     void slotPathBoxChanged(const QString &text);
0119 
0120     void updateContent();
0121 
0122     /**
0123      * Updates all buttons to have one button for each part of the
0124      * current URL. Existing buttons, which are available by m_navButtons,
0125      * are reused if possible. If the URL is longer, new buttons will be
0126      * created, if the URL is shorter, the remaining buttons will be deleted.
0127      * @param startIndex    Start index of URL part (/), where the buttons
0128      *                      should be created for each following part.
0129      */
0130     void updateButtons(int startIndex);
0131 
0132     /**
0133      * Updates the visibility state of all buttons describing the URL. If the
0134      * width of the URL navigator is too small, the buttons representing the upper
0135      * paths of the URL will be hidden and moved to a drop down menu.
0136      */
0137     void updateButtonVisibility();
0138 
0139     /**
0140      * @return Text for the first button of the URL navigator.
0141      */
0142     QString firstButtonText() const;
0143 
0144     /**
0145      * Returns the URL that should be applied for the button with the index \a index.
0146      */
0147     QUrl buttonUrl(int index) const;
0148 
0149     void switchToBreadcrumbMode();
0150 
0151     /**
0152      * Deletes all URL navigator buttons. m_navButtons is
0153      * empty after this operation.
0154      */
0155     void deleteButtons();
0156 
0157     /**
0158      * Retrieves the place url for the current url.
0159      * E. g. for the path "fish://root@192.168.0.2/var/lib" the string
0160      * "fish://root@192.168.0.2" will be returned, which leads to the
0161      * navigation indication 'Custom Path > var > lib". For e. g.
0162      * "settings:///System/" the path "settings://" will be returned.
0163      */
0164     QUrl retrievePlaceUrl() const;
0165 
0166     KUrlNavigator *const q;
0167 
0168     QHBoxLayout *m_layout = new QHBoxLayout(q);
0169     KCoreUrlNavigator *m_coreUrlNavigator = nullptr;
0170     QList<KUrlNavigatorButton *> m_navButtons;
0171     QStringList m_customProtocols;
0172     QUrl m_homeUrl;
0173     KUrlNavigatorPlacesSelector *m_placesSelector = nullptr;
0174     KUrlComboBox *m_pathBox = nullptr;
0175     KUrlNavigatorProtocolCombo *m_protocols = nullptr;
0176     KUrlNavigatorDropDownButton *m_dropDownButton = nullptr;
0177     KUrlNavigatorButtonBase *m_toggleEditableMode = nullptr;
0178     QWidget *m_dropWidget = nullptr;
0179 
0180     bool m_editable = false;
0181     bool m_active = true;
0182     bool m_showPlacesSelector = false;
0183     bool m_showFullPath = false;
0184 
0185     struct {
0186         bool showHidden = false;
0187         bool sortHiddenLast = false;
0188     } m_subfolderOptions;
0189 };
0190 
0191 KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, KFilePlacesModel *placesModel)
0192     : q(qq)
0193     , m_coreUrlNavigator(new KCoreUrlNavigator(url, qq))
0194     , m_showPlacesSelector(placesModel != nullptr)
0195 {
0196     m_layout->setSpacing(0);
0197     m_layout->setContentsMargins(0, 0, 0, 0);
0198 
0199     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentLocationUrlChanged, q, [this]() {
0200         Q_EMIT q->urlChanged(m_coreUrlNavigator->currentLocationUrl());
0201     });
0202     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentUrlAboutToChange, q, [this](const QUrl &url) {
0203         Q_EMIT q->urlAboutToBeChanged(url);
0204     });
0205     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historySizeChanged, q, [this]() {
0206         Q_EMIT q->historyChanged();
0207     });
0208     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyIndexChanged, q, [this]() {
0209         Q_EMIT q->historyChanged();
0210     });
0211     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyChanged, q, [this]() {
0212         Q_EMIT q->historyChanged();
0213     });
0214     q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::urlSelectionRequested, q, [this](const QUrl &url) {
0215         Q_EMIT q->urlSelectionRequested(url);
0216     });
0217 
0218     // initialize the places selector
0219     q->setAutoFillBackground(false);
0220 
0221     if (placesModel != nullptr) {
0222         m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
0223         q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::placeActivated, q, &KUrlNavigator::setLocationUrl);
0224         q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::tabRequested, q, &KUrlNavigator::tabRequested);
0225 
0226         auto updateContentFunc = [this]() {
0227             updateContent();
0228         };
0229         q->connect(placesModel, &KFilePlacesModel::rowsInserted, q, updateContentFunc);
0230         q->connect(placesModel, &KFilePlacesModel::rowsRemoved, q, updateContentFunc);
0231         q->connect(placesModel, &KFilePlacesModel::dataChanged, q, updateContentFunc);
0232     }
0233 
0234     // create protocol combo
0235     m_protocols = new KUrlNavigatorProtocolCombo(QString(), q);
0236     q->connect(m_protocols, &KUrlNavigatorProtocolCombo::activated, q, [this](const QString &protocol) {
0237         slotProtocolChanged(protocol);
0238     });
0239 
0240     // create drop down button for accessing all paths of the URL
0241     m_dropDownButton = new KUrlNavigatorDropDownButton(q);
0242     m_dropDownButton->setForegroundRole(QPalette::WindowText);
0243     m_dropDownButton->installEventFilter(q);
0244     q->connect(m_dropDownButton, &KUrlNavigatorDropDownButton::clicked, q, [this]() {
0245         openPathSelectorMenu();
0246     });
0247 
0248     // initialize the path box of the traditional view
0249     m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
0250     m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
0251     m_pathBox->installEventFilter(q);
0252 
0253     KUrlCompletion *kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
0254     m_pathBox->setCompletionObject(kurlCompletion);
0255     m_pathBox->setAutoDeleteCompletionObject(true);
0256 
0257     // TODO KF6: remove this QOverload, only KUrlComboBox::returnPressed(const QString &) will remain
0258     q->connect(m_pathBox, qOverload<const QString &>(&KUrlComboBox::returnPressed), q, [this]() {
0259         slotReturnPressed();
0260     });
0261     q->connect(m_pathBox, &KUrlComboBox::urlActivated, q, &KUrlNavigator::setLocationUrl);
0262     q->connect(m_pathBox, &QComboBox::editTextChanged, q, [this](const QString &text) {
0263         slotPathBoxChanged(text);
0264     });
0265 
0266     // create toggle button which allows to switch between
0267     // the breadcrumb and traditional view
0268     m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
0269     m_toggleEditableMode->installEventFilter(q);
0270     m_toggleEditableMode->setMinimumWidth(20);
0271     q->connect(m_toggleEditableMode, &KUrlNavigatorToggleButton::clicked, q, [this]() {
0272         slotToggleEditableButtonPressed();
0273     });
0274 
0275     if (m_placesSelector != nullptr) {
0276         m_layout->addWidget(m_placesSelector);
0277     }
0278     m_layout->addWidget(m_protocols);
0279     m_layout->addWidget(m_dropDownButton);
0280     m_layout->addWidget(m_pathBox, 1);
0281     m_layout->addWidget(m_toggleEditableMode);
0282 
0283     q->setContextMenuPolicy(Qt::CustomContextMenu);
0284     q->connect(q, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
0285         openContextMenu(pos);
0286     });
0287 }
0288 
0289 void KUrlNavigatorPrivate::appendWidget(QWidget *widget, int stretch)
0290 {
0291     m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
0292 }
0293 
0294 void KUrlNavigatorPrivate::slotApplyUrl(QUrl url)
0295 {
0296     // Parts of the following code have been taken from the class KateFileSelector
0297     // located in kate/app/katefileselector.hpp of Kate.
0298     // SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0299     // SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
0300     // SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
0301 
0302     // For example "desktop:/" _not_ "desktop:", see the comment in slotProtocolChanged()
0303     if (!url.isEmpty() && url.path().isEmpty() && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
0304         url.setPath(QStringLiteral("/"));
0305     }
0306 
0307     const auto urlStr = url.toString();
0308     QStringList urls = m_pathBox->urls();
0309     urls.removeAll(urlStr);
0310     urls.prepend(urlStr);
0311     m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
0312 
0313     q->setLocationUrl(url);
0314     // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
0315     // synchronize the result in the path box.
0316     m_pathBox->setUrl(q->locationUrl());
0317 }
0318 
0319 bool KUrlNavigatorPrivate::slotCheckFilters(const QString &text)
0320 {
0321     KUriFilterData filteredData(text);
0322     filteredData.setCheckForExecutables(false);
0323     // Using kshorturifilter to fix up e.g. "ftp.kde.org" ---> "ftp://ftp.kde.org"
0324     const auto filtersList = QStringList{QStringLiteral("kshorturifilter")};
0325     const bool wasFiltered = KUriFilter::self()->filterUri(filteredData, filtersList);
0326     if (wasFiltered) {
0327         slotApplyUrl(filteredData.uri()); // The text was filtered
0328     }
0329     return wasFiltered;
0330 }
0331 
0332 void KUrlNavigatorPrivate::applyUncommittedUrl()
0333 {
0334     const QString text = m_pathBox->currentText().trimmed();
0335     QUrl url = q->locationUrl();
0336 
0337     // Using the stat job below, check if the url and text match a local dir; but first
0338     // handle a special case where "url" is empty in the unittests which use
0339     // KUrlNavigator::setLocationUrl(QUrl()); in practice (e.g. in Dolphin, or KFileWidget),
0340     // locationUrl() is never empty
0341     if (url.isEmpty() && !text.isEmpty()) {
0342         if (slotCheckFilters(text)) {
0343             return;
0344         }
0345     }
0346 
0347     // Treat absolute paths as absolute paths.
0348     // Relative paths get appended to the current path.
0349     if (text.startsWith(QLatin1Char('/'))) {
0350         url.setPath(text);
0351     } else {
0352         url.setPath(Utils::concatPaths(url.path(), text));
0353     }
0354 
0355     // Dirs and symlinks to dirs
0356     constexpr auto details = KIO::StatBasic | KIO::StatResolveSymlink;
0357     auto *job = KIO::statDetails(url, KIO::StatJob::DestinationSide, details, KIO::HideProgressInfo);
0358     q->connect(job, &KJob::result, q, [this, job, text]() {
0359         // If there is a dir matching "text" relative to the current url, use that, e.g.:
0360         // - typing "bar" while at "/path/to/foo" ---> "/path/to/foo/bar/"
0361         // - typing ".config" while at "/home/foo" ---> "/home/foo/.config"
0362         if (!job->error() && job->statResult().isDir()) {
0363             slotApplyUrl(job->url());
0364             return;
0365         }
0366 
0367         // Check if text matches a URI filter
0368         if (slotCheckFilters(text)) {
0369             return;
0370         }
0371 
0372         // ... otherwise fallback to whatever QUrl::fromUserInput() returns
0373         slotApplyUrl(QUrl::fromUserInput(text));
0374     });
0375 }
0376 
0377 void KUrlNavigatorPrivate::slotReturnPressed()
0378 {
0379     applyUncommittedUrl();
0380 
0381     Q_EMIT q->returnPressed();
0382 
0383     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
0384         // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
0385         // The switch must be done asynchronously, as we are in the context of the
0386         // editor.
0387         auto switchModeFunc = [this]() {
0388             switchToBreadcrumbMode();
0389         };
0390         QMetaObject::invokeMethod(q, switchModeFunc, Qt::QueuedConnection);
0391     }
0392 }
0393 
0394 void KUrlNavigatorPrivate::slotProtocolChanged(const QString &protocol)
0395 {
0396     Q_ASSERT(m_editable);
0397 
0398     QUrl url;
0399     url.setScheme(protocol);
0400     if (KProtocolInfo::protocolClass(protocol) == QLatin1String(":local")) {
0401         // E.g. "file:/" or "desktop:/", _not_ "file:" or "desktop:" respectively.
0402         // This is the more expected behaviour, "file:somedir" treats somedir as
0403         // a path relative to current dir; file:/somedir is an absolute path to /somedir.
0404         url.setPath(QStringLiteral("/"));
0405     } else {
0406         // With no authority set we'll get e.g. "ftp:" instead of "ftp://".
0407         // We want the latter, so let's set an empty authority.
0408         url.setAuthority(QString());
0409     }
0410 
0411     m_pathBox->setEditUrl(url);
0412 }
0413 
0414 void KUrlNavigatorPrivate::openPathSelectorMenu()
0415 {
0416     if (m_navButtons.count() <= 0) {
0417         return;
0418     }
0419 
0420     const QUrl firstVisibleUrl = m_navButtons.constFirst()->url();
0421 
0422     QString spacer;
0423     QPointer<QMenu> popup = new QMenu(q);
0424 
0425     auto *popupFilter = new KUrlNavigatorPathSelectorEventFilter(popup.data());
0426     q->connect(popupFilter, &KUrlNavigatorPathSelectorEventFilter::tabRequested, q, &KUrlNavigator::tabRequested);
0427     popup->installEventFilter(popupFilter);
0428 
0429     const QUrl placeUrl = retrievePlaceUrl();
0430     int idx = placeUrl.path().count(QLatin1Char('/')); // idx points to the first directory
0431     // after the place path
0432 
0433     const QString path = m_coreUrlNavigator->locationUrl(m_coreUrlNavigator->historyIndex()).path();
0434     QString dirName = path.section(QLatin1Char('/'), idx, idx);
0435     if (dirName.isEmpty()) {
0436         if (placeUrl.isLocalFile()) {
0437             dirName = QStringLiteral("/");
0438         } else {
0439             dirName = placeUrl.toDisplayString();
0440         }
0441     }
0442     do {
0443         const QString text = spacer + dirName;
0444 
0445         QAction *action = new QAction(text, popup);
0446         const QUrl currentUrl = buttonUrl(idx);
0447         if (currentUrl == firstVisibleUrl) {
0448             popup->addSeparator();
0449         }
0450         action->setData(QVariant(currentUrl.toString()));
0451         popup->addAction(action);
0452 
0453         ++idx;
0454         spacer.append(QLatin1String("  "));
0455         dirName = path.section(QLatin1Char('/'), idx, idx);
0456     } while (!dirName.isEmpty());
0457 
0458     const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
0459     const QAction *activatedAction = popup->exec(pos);
0460     if (activatedAction != nullptr) {
0461         const QUrl url(activatedAction->data().toString());
0462         q->setLocationUrl(url);
0463     }
0464 
0465     // Delete the menu, unless it has been deleted in its own nested event loop already.
0466     if (popup) {
0467         popup->deleteLater();
0468     }
0469 }
0470 
0471 void KUrlNavigatorPrivate::slotToggleEditableButtonPressed()
0472 {
0473     if (m_editable) {
0474         applyUncommittedUrl();
0475     }
0476 
0477     switchView();
0478 }
0479 
0480 void KUrlNavigatorPrivate::switchView()
0481 {
0482     m_toggleEditableMode->setFocus();
0483     m_editable = !m_editable;
0484     m_toggleEditableMode->setChecked(m_editable);
0485     updateContent();
0486     if (q->isUrlEditable()) {
0487         m_pathBox->setFocus();
0488     }
0489 
0490     q->requestActivation();
0491     Q_EMIT q->editableStateChanged(m_editable);
0492 }
0493 
0494 void KUrlNavigatorPrivate::dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton)
0495 {
0496     if (event->mimeData()->hasUrls()) {
0497         m_dropWidget = qobject_cast<QWidget *>(dropButton);
0498         Q_EMIT q->urlsDropped(destination, event);
0499     }
0500 }
0501 
0502 void KUrlNavigatorPrivate::slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
0503 {
0504     if ((button & Qt::MiddleButton && modifiers & Qt::ShiftModifier) || (button & Qt::LeftButton && modifiers & (Qt::ControlModifier | Qt::ShiftModifier))) {
0505         Q_EMIT q->activeTabRequested(url);
0506     } else if (button & Qt::MiddleButton || (button & Qt::LeftButton && modifiers & Qt::ControlModifier)) {
0507         Q_EMIT q->tabRequested(url);
0508     } else if (button & Qt::LeftButton && modifiers & Qt::ShiftModifier) {
0509         Q_EMIT q->newWindowRequested(url);
0510     } else if (button & Qt::LeftButton) {
0511         q->setLocationUrl(url);
0512     }
0513 }
0514 
0515 void KUrlNavigatorPrivate::openContextMenu(const QPoint &p)
0516 {
0517     q->setActive(true);
0518 
0519     QPointer<QMenu> popup = new QMenu(q);
0520 
0521     // provide 'Copy' action, which copies the current URL of
0522     // the URL navigator into the clipboard
0523     QAction *copyAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"));
0524 
0525     // provide 'Paste' action, which copies the current clipboard text
0526     // into the URL navigator
0527     QAction *pasteAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("Paste"));
0528     QClipboard *clipboard = QApplication::clipboard();
0529     pasteAction->setEnabled(!clipboard->text().isEmpty());
0530 
0531     popup->addSeparator();
0532 
0533     // We are checking whether the signal is connected because it's odd to have a tab entry even
0534     // if it's not supported, like in the case of the open dialog
0535     const bool isTabSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::tabRequested));
0536     const bool isWindowSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::newWindowRequested));
0537     if (isTabSignal || isWindowSignal) {
0538         auto it = std::find_if(m_navButtons.cbegin(), m_navButtons.cend(), [&p](const KUrlNavigatorButton *button) {
0539             return button->geometry().contains(p);
0540         });
0541         if (it != m_navButtons.cend()) {
0542             const auto *button = *it;
0543             const QUrl url = button->url();
0544             const QString text = button->text();
0545 
0546             if (isTabSignal) {
0547                 QAction *openInTab = popup->addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open \"%1\" in New Tab", text));
0548                 q->connect(openInTab, &QAction::triggered, q, [this, url]() {
0549                     Q_EMIT q->tabRequested(url);
0550                 });
0551             }
0552 
0553             if (isWindowSignal) {
0554                 QAction *openInWindow =
0555                     popup->addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open \"%1\" in New Window", text));
0556                 q->connect(openInWindow, &QAction::triggered, q, [this, url]() {
0557                     Q_EMIT q->newWindowRequested(url);
0558                 });
0559             }
0560         }
0561     }
0562 
0563     // provide radiobuttons for toggling between the edit and the navigation mode
0564     QAction *editAction = popup->addAction(i18n("Edit"));
0565     editAction->setCheckable(true);
0566 
0567     QAction *navigateAction = popup->addAction(i18n("Navigate"));
0568     navigateAction->setCheckable(true);
0569 
0570     QActionGroup *modeGroup = new QActionGroup(popup);
0571     modeGroup->addAction(editAction);
0572     modeGroup->addAction(navigateAction);
0573     if (q->isUrlEditable()) {
0574         editAction->setChecked(true);
0575     } else {
0576         navigateAction->setChecked(true);
0577     }
0578 
0579     popup->addSeparator();
0580 
0581     // allow showing of the full path
0582     QAction *showFullPathAction = popup->addAction(i18n("Show Full Path"));
0583     showFullPathAction->setCheckable(true);
0584     showFullPathAction->setChecked(q->showFullPath());
0585 
0586     QAction *activatedAction = popup->exec(QCursor::pos());
0587     if (activatedAction == copyAction) {
0588         QMimeData *mimeData = new QMimeData();
0589         mimeData->setText(q->locationUrl().toDisplayString(QUrl::PreferLocalFile));
0590         clipboard->setMimeData(mimeData);
0591     } else if (activatedAction == pasteAction) {
0592         q->setLocationUrl(QUrl::fromUserInput(clipboard->text()));
0593     } else if (activatedAction == editAction) {
0594         q->setUrlEditable(true);
0595     } else if (activatedAction == navigateAction) {
0596         q->setUrlEditable(false);
0597     } else if (activatedAction == showFullPathAction) {
0598         q->setShowFullPath(showFullPathAction->isChecked());
0599     }
0600 
0601     // Delete the menu, unless it has been deleted in its own nested event loop already.
0602     if (popup) {
0603         popup->deleteLater();
0604     }
0605 }
0606 
0607 void KUrlNavigatorPrivate::slotPathBoxChanged(const QString &text)
0608 {
0609     if (text.isEmpty()) {
0610         const QString protocol = q->locationUrl().scheme();
0611         m_protocols->setProtocol(protocol);
0612         if (m_customProtocols.count() != 1) {
0613             m_protocols->show();
0614         }
0615     } else {
0616         m_protocols->hide();
0617     }
0618 }
0619 
0620 void KUrlNavigatorPrivate::updateContent()
0621 {
0622     const QUrl currentUrl = q->locationUrl();
0623     if (m_placesSelector != nullptr) {
0624         m_placesSelector->updateSelection(currentUrl);
0625     }
0626 
0627     if (m_editable) {
0628         m_protocols->hide();
0629         m_dropDownButton->hide();
0630 
0631         deleteButtons();
0632         m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
0633         q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
0634 
0635         m_pathBox->show();
0636         m_pathBox->setUrl(currentUrl);
0637     } else {
0638         m_pathBox->hide();
0639 
0640         m_protocols->hide();
0641 
0642         m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
0643         q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0644 
0645         // Calculate the start index for the directories that should be shown as buttons
0646         // and create the buttons
0647         QUrl placeUrl;
0648         if ((m_placesSelector != nullptr) && !m_showFullPath) {
0649             placeUrl = m_placesSelector->selectedPlaceUrl();
0650         }
0651 
0652         if (!placeUrl.isValid()) {
0653             placeUrl = retrievePlaceUrl();
0654         }
0655         QString placePath = Utils::trailingSlashRemoved(placeUrl.path());
0656 
0657         const int startIndex = placePath.count(QLatin1Char('/'));
0658         updateButtons(startIndex);
0659     }
0660 }
0661 
0662 void KUrlNavigatorPrivate::updateButtons(int startIndex)
0663 {
0664     QUrl currentUrl = q->locationUrl();
0665     if (!currentUrl.isValid()) { // QFileDialog::setDirectory not called yet
0666         return;
0667     }
0668 
0669     const QString path = currentUrl.path();
0670 
0671     const int oldButtonCount = m_navButtons.count();
0672 
0673     int idx = startIndex;
0674     bool hasNext = true;
0675     do {
0676         const bool createButton = (idx - startIndex) >= oldButtonCount;
0677         const bool isFirstButton = (idx == startIndex);
0678         const QString dirName = path.section(QLatin1Char('/'), idx, idx);
0679         hasNext = isFirstButton || !dirName.isEmpty();
0680         if (hasNext) {
0681             KUrlNavigatorButton *button = nullptr;
0682             if (createButton) {
0683                 button = new KUrlNavigatorButton(buttonUrl(idx), q);
0684                 button->installEventFilter(q);
0685                 button->setForegroundRole(QPalette::WindowText);
0686                 q->connect(button, &KUrlNavigatorButton::urlsDroppedOnNavButton, q, [this, button](const QUrl &destination, QDropEvent *event) {
0687                     dropUrls(destination, event, button);
0688                 });
0689 
0690                 auto activatedFunc = [this](const QUrl &url, Qt::MouseButton btn, Qt::KeyboardModifiers modifiers) {
0691                     slotNavigatorButtonClicked(url, btn, modifiers);
0692                 };
0693                 q->connect(button, &KUrlNavigatorButton::navigatorButtonActivated, q, activatedFunc);
0694 
0695                 q->connect(button, &KUrlNavigatorButton::finishedTextResolving, q, [this]() {
0696                     updateButtonVisibility();
0697                 });
0698 
0699                 appendWidget(button);
0700             } else {
0701                 button = m_navButtons[idx - startIndex];
0702                 button->setUrl(buttonUrl(idx));
0703             }
0704 
0705             if (isFirstButton) {
0706                 button->setText(firstButtonText());
0707             }
0708             button->setActive(q->isActive());
0709 
0710             if (createButton) {
0711                 if (!isFirstButton) {
0712                     q->setTabOrder(m_navButtons.constLast(), button);
0713                 }
0714                 m_navButtons.append(button);
0715             }
0716 
0717             ++idx;
0718             button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
0719         }
0720     } while (hasNext);
0721 
0722     // delete buttons which are not used anymore
0723     const int newButtonCount = idx - startIndex;
0724     if (newButtonCount < oldButtonCount) {
0725         const auto itBegin = m_navButtons.begin() + newButtonCount;
0726         const auto itEnd = m_navButtons.end();
0727         for (auto it = itBegin; it != itEnd; ++it) {
0728             auto *navBtn = *it;
0729             navBtn->hide();
0730             navBtn->deleteLater();
0731         }
0732         m_navButtons.erase(itBegin, itEnd);
0733     }
0734 
0735     q->setTabOrder(m_dropDownButton, m_navButtons.constFirst());
0736     q->setTabOrder(m_navButtons.constLast(), m_toggleEditableMode);
0737 
0738     updateButtonVisibility();
0739 }
0740 
0741 void KUrlNavigatorPrivate::updateButtonVisibility()
0742 {
0743     if (m_editable) {
0744         return;
0745     }
0746 
0747     const int buttonsCount = m_navButtons.count();
0748     if (buttonsCount == 0) {
0749         m_dropDownButton->hide();
0750         return;
0751     }
0752 
0753     // Subtract all widgets from the available width, that must be shown anyway
0754     int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
0755 
0756     if ((m_placesSelector != nullptr) && m_placesSelector->isVisible()) {
0757         availableWidth -= m_placesSelector->width();
0758     }
0759 
0760     if ((m_protocols != nullptr) && m_protocols->isVisible()) {
0761         availableWidth -= m_protocols->width();
0762     }
0763 
0764     // Check whether buttons must be hidden at all...
0765     int requiredButtonWidth = 0;
0766     for (const auto *button : std::as_const(m_navButtons)) {
0767         requiredButtonWidth += button->minimumWidth();
0768     }
0769 
0770     if (requiredButtonWidth > availableWidth) {
0771         // At least one button must be hidden. This implies that the
0772         // drop-down button must get visible, which again decreases the
0773         // available width.
0774         availableWidth -= m_dropDownButton->width();
0775     }
0776 
0777     // Hide buttons...
0778     bool isLastButton = true;
0779     bool hasHiddenButtons = false;
0780     QList<KUrlNavigatorButton *> buttonsToShow;
0781     for (auto it = m_navButtons.crbegin(); it != m_navButtons.crend(); ++it) {
0782         KUrlNavigatorButton *button = *it;
0783         availableWidth -= button->minimumWidth();
0784         if ((availableWidth <= 0) && !isLastButton) {
0785             button->hide();
0786             hasHiddenButtons = true;
0787         } else {
0788             // Don't show the button immediately, as setActive()
0789             // might change the size and a relayout gets triggered
0790             // after showing the button. So the showing of all buttons
0791             // is postponed until all buttons have the correct
0792             // activation state.
0793             buttonsToShow.append(button);
0794         }
0795         isLastButton = false;
0796     }
0797 
0798     // All buttons have the correct activation state and
0799     // can be shown now
0800     for (KUrlNavigatorButton *button : std::as_const(buttonsToShow)) {
0801         button->show();
0802     }
0803 
0804     if (hasHiddenButtons) {
0805         m_dropDownButton->show();
0806     } else {
0807         // Check whether going upwards is possible. If this is the case, show the drop-down button.
0808         QUrl url(m_navButtons.front()->url());
0809         const bool visible = !url.matches(KIO::upUrl(url), QUrl::StripTrailingSlash) //
0810             && url.scheme() != QLatin1String("baloosearch") //
0811             && url.scheme() != QLatin1String("filenamesearch");
0812         m_dropDownButton->setVisible(visible);
0813     }
0814 }
0815 
0816 QString KUrlNavigatorPrivate::firstButtonText() const
0817 {
0818     QString text;
0819 
0820     // The first URL navigator button should get the name of the
0821     // place instead of the directory name
0822     if ((m_placesSelector != nullptr) && !m_showFullPath) {
0823         text = m_placesSelector->selectedPlaceText();
0824     }
0825 
0826     const QUrl currentUrl = q->locationUrl();
0827 
0828     if (text.isEmpty()) {
0829         if (currentUrl.isLocalFile()) {
0830 #ifdef Q_OS_WIN
0831             text = currentUrl.path().length() > 1 ? currentUrl.path().left(2) : QDir::rootPath();
0832 #else
0833             text = QStringLiteral("/");
0834 #endif
0835         }
0836     }
0837 
0838     if (text.isEmpty()) {
0839         if (currentUrl.path().isEmpty() || currentUrl.path() == QLatin1Char('/')) {
0840             QUrlQuery query(currentUrl);
0841             text = query.queryItemValue(QStringLiteral("title"));
0842         }
0843     }
0844 
0845     if (text.isEmpty()) {
0846         text = currentUrl.scheme() + QLatin1Char(':');
0847         if (!currentUrl.host().isEmpty()) {
0848             text += QLatin1Char(' ') + currentUrl.host();
0849         }
0850     }
0851 
0852     return text;
0853 }
0854 
0855 QUrl KUrlNavigatorPrivate::buttonUrl(int index) const
0856 {
0857     if (index < 0) {
0858         index = 0;
0859     }
0860 
0861     // Keep scheme, hostname etc. as this is needed for e. g. browsing
0862     // FTP directories
0863     QUrl url = q->locationUrl();
0864     QString path = url.path();
0865 
0866     if (!path.isEmpty()) {
0867         if (index == 0) {
0868             // prevent the last "/" from being stripped
0869             // or we end up with an empty path
0870 #ifdef Q_OS_WIN
0871             path = path.length() > 1 ? path.left(2) : QDir::rootPath();
0872 #else
0873             path = QStringLiteral("/");
0874 #endif
0875         } else {
0876             path = path.section(QLatin1Char('/'), 0, index);
0877         }
0878     }
0879 
0880     url.setPath(path);
0881     return url;
0882 }
0883 
0884 void KUrlNavigatorPrivate::switchToBreadcrumbMode()
0885 {
0886     q->setUrlEditable(false);
0887 }
0888 
0889 void KUrlNavigatorPrivate::deleteButtons()
0890 {
0891     for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
0892         button->hide();
0893         button->deleteLater();
0894     }
0895     m_navButtons.clear();
0896 }
0897 
0898 QUrl KUrlNavigatorPrivate::retrievePlaceUrl() const
0899 {
0900     QUrl currentUrl = q->locationUrl();
0901     currentUrl.setPath(QString());
0902     return currentUrl;
0903 }
0904 
0905 // ------------------------------------------------------------------------------------------------
0906 
0907 KUrlNavigator::KUrlNavigator(QWidget *parent)
0908     : KUrlNavigator(nullptr, QUrl{}, parent)
0909 {
0910 }
0911 
0912 KUrlNavigator::KUrlNavigator(KFilePlacesModel *placesModel, const QUrl &url, QWidget *parent)
0913     : QWidget(parent)
0914     , d(new KUrlNavigatorPrivate(url, this, placesModel))
0915 {
0916     const int minHeight = d->m_pathBox->sizeHint().height();
0917     setMinimumHeight(minHeight);
0918 
0919     setMinimumWidth(100);
0920 
0921     d->updateContent();
0922 }
0923 
0924 KUrlNavigator::~KUrlNavigator()
0925 {
0926     d->m_dropDownButton->removeEventFilter(this);
0927     d->m_pathBox->removeEventFilter(this);
0928     for (auto *button : std::as_const(d->m_navButtons)) {
0929         button->removeEventFilter(this);
0930     }
0931 }
0932 
0933 QUrl KUrlNavigator::locationUrl(int historyIndex) const
0934 {
0935     return d->m_coreUrlNavigator->locationUrl(historyIndex);
0936 }
0937 
0938 void KUrlNavigator::saveLocationState(const QByteArray &state)
0939 {
0940     auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
0941     current.state = state;
0942     d->m_coreUrlNavigator->saveLocationState(QVariant::fromValue(current));
0943 }
0944 
0945 QByteArray KUrlNavigator::locationState(int historyIndex) const
0946 {
0947     return d->m_coreUrlNavigator->locationState(historyIndex).value<KUrlNavigatorData>().state;
0948 }
0949 
0950 bool KUrlNavigator::goBack()
0951 {
0952     return d->m_coreUrlNavigator->goBack();
0953 }
0954 
0955 bool KUrlNavigator::goForward()
0956 {
0957     return d->m_coreUrlNavigator->goForward();
0958 }
0959 
0960 bool KUrlNavigator::goUp()
0961 {
0962     return d->m_coreUrlNavigator->goUp();
0963 }
0964 
0965 void KUrlNavigator::goHome()
0966 {
0967     if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
0968         setLocationUrl(QUrl::fromLocalFile(QDir::homePath()));
0969     } else {
0970         setLocationUrl(d->m_homeUrl);
0971     }
0972 }
0973 
0974 void KUrlNavigator::setHomeUrl(const QUrl &url)
0975 {
0976     d->m_homeUrl = url;
0977 }
0978 
0979 QUrl KUrlNavigator::homeUrl() const
0980 {
0981     return d->m_homeUrl;
0982 }
0983 
0984 void KUrlNavigator::setUrlEditable(bool editable)
0985 {
0986     if (d->m_editable != editable) {
0987         d->switchView();
0988     }
0989 }
0990 
0991 bool KUrlNavigator::isUrlEditable() const
0992 {
0993     return d->m_editable;
0994 }
0995 
0996 void KUrlNavigator::setShowFullPath(bool show)
0997 {
0998     if (d->m_showFullPath != show) {
0999         d->m_showFullPath = show;
1000         d->updateContent();
1001     }
1002 }
1003 
1004 bool KUrlNavigator::showFullPath() const
1005 {
1006     return d->m_showFullPath;
1007 }
1008 
1009 void KUrlNavigator::setActive(bool active)
1010 {
1011     if (active != d->m_active) {
1012         d->m_active = active;
1013 
1014         d->m_dropDownButton->setActive(active);
1015         for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1016             button->setActive(active);
1017         }
1018 
1019         update();
1020         if (active) {
1021             Q_EMIT activated();
1022         }
1023     }
1024 }
1025 
1026 bool KUrlNavigator::isActive() const
1027 {
1028     return d->m_active;
1029 }
1030 
1031 void KUrlNavigator::setPlacesSelectorVisible(bool visible)
1032 {
1033     if (visible == d->m_showPlacesSelector) {
1034         return;
1035     }
1036 
1037     if (visible && (d->m_placesSelector == nullptr)) {
1038         // the places selector cannot get visible as no
1039         // places model is available
1040         return;
1041     }
1042 
1043     d->m_showPlacesSelector = visible;
1044     d->m_placesSelector->setVisible(visible);
1045 }
1046 
1047 bool KUrlNavigator::isPlacesSelectorVisible() const
1048 {
1049     return d->m_showPlacesSelector;
1050 }
1051 
1052 QUrl KUrlNavigator::uncommittedUrl() const
1053 {
1054     KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
1055     filteredData.setCheckForExecutables(false);
1056     if (KUriFilter::self()->filterUri(filteredData, QStringList{QStringLiteral("kshorturifilter")})) {
1057         return filteredData.uri();
1058     } else {
1059         return QUrl::fromUserInput(filteredData.typedString());
1060     }
1061 }
1062 
1063 void KUrlNavigator::setLocationUrl(const QUrl &newUrl)
1064 {
1065     d->m_coreUrlNavigator->setCurrentLocationUrl(newUrl);
1066 
1067     d->updateContent();
1068 
1069     requestActivation();
1070 }
1071 
1072 void KUrlNavigator::requestActivation()
1073 {
1074     setActive(true);
1075 }
1076 
1077 void KUrlNavigator::setFocus()
1078 {
1079     if (isUrlEditable()) {
1080         d->m_pathBox->setFocus();
1081     } else {
1082         QWidget::setFocus();
1083     }
1084 }
1085 
1086 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1087 void KUrlNavigator::setUrl(const QUrl &url)
1088 {
1089     // deprecated
1090     setLocationUrl(url);
1091 }
1092 #endif
1093 
1094 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1095 void KUrlNavigator::saveRootUrl(const QUrl &url)
1096 {
1097     auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
1098     current.rootUrl = url;
1099     d->m_coreUrlNavigator->saveLocationState(QVariant::fromValue(current));
1100 }
1101 #endif
1102 
1103 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1104 void KUrlNavigator::savePosition(int x, int y)
1105 {
1106     auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
1107     current.pos = QPoint(x, y);
1108     d->m_coreUrlNavigator->saveLocationState(QVariant::fromValue(current));
1109 }
1110 #endif
1111 
1112 void KUrlNavigator::keyPressEvent(QKeyEvent *event)
1113 {
1114     if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
1115         setUrlEditable(false);
1116     } else {
1117         QWidget::keyPressEvent(event);
1118     }
1119 }
1120 
1121 void KUrlNavigator::keyReleaseEvent(QKeyEvent *event)
1122 {
1123     QWidget::keyReleaseEvent(event);
1124 }
1125 
1126 void KUrlNavigator::mousePressEvent(QMouseEvent *event)
1127 {
1128     if (event->button() == Qt::MiddleButton) {
1129         requestActivation();
1130     }
1131     QWidget::mousePressEvent(event);
1132 }
1133 
1134 void KUrlNavigator::mouseReleaseEvent(QMouseEvent *event)
1135 {
1136     if (event->button() == Qt::MiddleButton) {
1137         const QRect bounds = d->m_toggleEditableMode->geometry();
1138         if (bounds.contains(event->pos())) {
1139             // The middle mouse button has been clicked above the
1140             // toggle-editable-mode-button. Paste the clipboard content
1141             // as location URL.
1142             QClipboard *clipboard = QApplication::clipboard();
1143             const QMimeData *mimeData = clipboard->mimeData();
1144             if (mimeData->hasText()) {
1145                 const QString text = mimeData->text();
1146                 setLocationUrl(QUrl::fromUserInput(text));
1147             }
1148         }
1149     }
1150     QWidget::mouseReleaseEvent(event);
1151 }
1152 
1153 void KUrlNavigator::resizeEvent(QResizeEvent *event)
1154 {
1155     QTimer::singleShot(0, this, [this]() {
1156         d->updateButtonVisibility();
1157     });
1158     QWidget::resizeEvent(event);
1159 }
1160 
1161 void KUrlNavigator::wheelEvent(QWheelEvent *event)
1162 {
1163     setActive(true);
1164     QWidget::wheelEvent(event);
1165 }
1166 
1167 bool KUrlNavigator::eventFilter(QObject *watched, QEvent *event)
1168 {
1169     switch (event->type()) {
1170     case QEvent::FocusIn:
1171         if (watched == d->m_pathBox) {
1172             requestActivation();
1173             setFocus();
1174         }
1175         for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1176             button->setShowMnemonic(true);
1177         }
1178         break;
1179 
1180     case QEvent::FocusOut:
1181         for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1182             button->setShowMnemonic(false);
1183         }
1184         break;
1185 
1186     default:
1187         break;
1188     }
1189 
1190     return QWidget::eventFilter(watched, event);
1191 }
1192 
1193 int KUrlNavigator::historySize() const
1194 {
1195     return d->m_coreUrlNavigator->historySize();
1196 }
1197 
1198 int KUrlNavigator::historyIndex() const
1199 {
1200     return d->m_coreUrlNavigator->historyIndex();
1201 }
1202 
1203 KUrlComboBox *KUrlNavigator::editor() const
1204 {
1205     return d->m_pathBox;
1206 }
1207 
1208 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 103)
1209 void KUrlNavigator::setCustomProtocols(const QStringList &protocols)
1210 {
1211     setSupportedSchemes(protocols);
1212 }
1213 #endif
1214 
1215 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 103)
1216 QStringList KUrlNavigator::customProtocols() const
1217 {
1218     return supportedSchemes();
1219 }
1220 #endif
1221 
1222 void KUrlNavigator::setSupportedSchemes(const QStringList &schemes)
1223 {
1224     d->m_customProtocols = schemes;
1225     d->m_protocols->setCustomProtocols(d->m_customProtocols);
1226 }
1227 
1228 QStringList KUrlNavigator::supportedSchemes() const
1229 {
1230     return d->m_customProtocols;
1231 }
1232 
1233 QWidget *KUrlNavigator::dropWidget() const
1234 {
1235     return d->m_dropWidget;
1236 }
1237 
1238 void KUrlNavigator::setShowHiddenFolders(bool showHiddenFolders)
1239 {
1240     d->m_subfolderOptions.showHidden = showHiddenFolders;
1241 }
1242 
1243 bool KUrlNavigator::showHiddenFolders() const
1244 {
1245     return d->m_subfolderOptions.showHidden;
1246 }
1247 
1248 void KUrlNavigator::setSortHiddenFoldersLast(bool sortHiddenFoldersLast)
1249 {
1250     d->m_subfolderOptions.sortHiddenLast = sortHiddenFoldersLast;
1251 }
1252 
1253 bool KUrlNavigator::sortHiddenFoldersLast() const
1254 {
1255     return d->m_subfolderOptions.sortHiddenLast;
1256 }
1257 
1258 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1259 const QUrl &KUrlNavigator::url() const
1260 {
1261     // deprecated
1262 
1263     // Workaround required because of flawed interface ('const QUrl&' is returned
1264     // instead of 'QUrl'): remember the URL to prevent a dangling pointer
1265     static QUrl url;
1266     url = locationUrl();
1267     return url;
1268 }
1269 #endif
1270 
1271 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1272 QUrl KUrlNavigator::url(int index) const
1273 {
1274     // deprecated
1275     return d->buttonUrl(index);
1276 }
1277 #endif
1278 
1279 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1280 QUrl KUrlNavigator::historyUrl(int historyIndex) const
1281 {
1282     // deprecated
1283     return locationUrl(historyIndex);
1284 }
1285 #endif
1286 
1287 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1288 const QUrl &KUrlNavigator::savedRootUrl() const
1289 {
1290     // deprecated
1291     // Workaround required because of flawed interface ('const QUrl&' is returned
1292     // instead of 'QUrl'): remember the root URL to prevent a dangling pointer
1293     static QUrl rootUrl;
1294     const auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
1295     rootUrl = current.rootUrl;
1296     return rootUrl;
1297 }
1298 #endif
1299 
1300 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1301 QPoint KUrlNavigator::savedPosition() const
1302 {
1303     // deprecated
1304     const auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
1305     return current.pos;
1306 }
1307 #endif
1308 
1309 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(4, 5)
1310 void KUrlNavigator::setHomeUrl(const QString &homeUrl)
1311 {
1312     // deprecated
1313     setLocationUrl(QUrl::fromUserInput(homeUrl));
1314 }
1315 #endif
1316 
1317 #include "moc_kurlnavigator.cpp"