KIO

kurlnavigator.cpp
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#include "kcoreurlnavigator.h"
12
13#include "../utils_p.h"
14#include "kurlnavigatorbutton_p.h"
15#include "kurlnavigatordropdownbutton_p.h"
16#include "kurlnavigatorpathselectoreventfilter_p.h"
17#include "kurlnavigatorplacesselector_p.h"
18#include "kurlnavigatorschemecombo_p.h"
19#include "kurlnavigatortogglebutton_p.h"
20
21#include <KIO/StatJob>
22#include <KLocalizedString>
23#include <kfileitem.h>
24#include <kfileplacesmodel.h>
25#include <kprotocolinfo.h>
26#include <kurifilter.h>
27#include <kurlcombobox.h>
28#include <kurlcompletion.h>
29
30#include <QActionGroup>
31#include <QApplication>
32#include <QClipboard>
33#include <QDir>
34#include <QDropEvent>
35#include <QHBoxLayout>
36#include <QKeyEvent>
37#include <QMenu>
38#include <QMetaMethod>
39#include <QMimeData>
40#include <QMimeDatabase>
41#include <QPainter>
42#include <QStyle>
43#include <QTimer>
44#include <QUrlQuery>
45
46#include <algorithm>
47#include <numeric>
48#include <optional>
49
50using namespace KDEPrivate;
51
52struct KUrlNavigatorData {
53 QByteArray state;
54};
55Q_DECLARE_METATYPE(KUrlNavigatorData)
56
57class KUrlNavigatorPrivate
58{
59public:
60 KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, KFilePlacesModel *placesModel);
61
62 ~KUrlNavigatorPrivate()
63 {
64 m_dropDownButton->removeEventFilter(q);
65 m_pathBox->removeEventFilter(q);
66 m_toggleEditableMode->removeEventFilter(q);
67
68 for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
69 button->removeEventFilter(q);
70 }
71 }
72
73 enum class ApplyUrlMethod {
74 Apply,
75 Tab,
76 ActiveTab,
77 NewWindow
78 };
79
80 /** Applies the edited URL in m_pathBox to the URL navigator */
81 void applyUncommittedUrl(ApplyUrlMethod method);
82 void slotApplyUrl(QUrl url);
83
84 // Returns the URI if "text" matched a URI filter (i.e. was fitlered),
85 // otherwise returns nullopt.
86 std::optional<QUrl> checkFilters(const QString &text);
87
88 void slotReturnPressed();
89 void slotSchemeChanged(const QString &);
90 void openPathSelectorMenu();
91
92 /**
93 * Appends the widget at the end of the URL navigator. It is assured
94 * that the filler widget remains as last widget to fill the remaining
95 * width.
96 */
97 void appendWidget(QWidget *widget, int stretch = 0);
98
99 /**
100 * This slot is connected to the clicked signal of the navigation bar button. It calls switchView().
101 * Moreover, if switching from "editable" mode to the breadcrumb view, it calls applyUncommittedUrl().
102 */
103 void slotToggleEditableButtonPressed();
104
105 /**
106 * Switches the navigation bar between the breadcrumb view and the
107 * traditional view (see setUrlEditable()).
108 */
109 void switchView();
110
111 /** Emits the signal urlsDropped(). */
112 void dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton);
113
114 /**
115 * Is invoked when a navigator button has been clicked.
116 * Different combinations of mouse clicks and keyboard modifiers have different effects on how
117 * the url is opened. The behaviours are the following:
118 * - shift+middle-click or ctrl+shift+left-click => activeTabRequested() signal is emitted
119 * - ctrl+left-click or middle-click => tabRequested() signal is emitted
120 * - shift+left-click => newWindowRequested() signal is emitted
121 * - left-click => open the new url in-place
122 */
123 void slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
124
125 void openContextMenu(const QPoint &p);
126
127 void slotPathBoxChanged(const QString &text);
128
129 void updateContent();
130
131 /**
132 * Updates all buttons to have one button for each part of the
133 * current URL. Existing buttons, which are available by m_navButtons,
134 * are reused if possible. If the URL is longer, new buttons will be
135 * created, if the URL is shorter, the remaining buttons will be deleted.
136 * @param startIndex Start index of URL part (/), where the buttons
137 * should be created for each following part.
138 */
139 void updateButtons(int startIndex);
140
141 /**
142 * Updates the visibility state of all buttons describing the URL. If the
143 * width of the URL navigator is too small, the buttons representing the upper
144 * paths of the URL will be hidden and moved to a drop down menu.
145 */
146 void updateButtonVisibility();
147
148 /**
149 * Set a sensible Tab key focus order which goes left to right all the way
150 * through all visible child widgets. For right-to-left layout directions
151 * the order goes right to left.
152 * The first widget is set as the focusProxy() of this KUrlNavigator.
153 */
154 void updateTabOrder();
155
156 /**
157 * @return Text for the first button of the URL navigator.
158 */
159 QString firstButtonText() const;
160
161 /**
162 * Returns the URL that should be applied for the button with the index \a index.
163 */
164 QUrl buttonUrl(int index) const;
165
166 void switchToBreadcrumbMode();
167
168 /**
169 * Deletes all URL navigator buttons. m_navButtons is
170 * empty after this operation.
171 */
172 void deleteButtons();
173
174 /**
175 * Retrieves the place url for the current url.
176 * E. g. for the path "fish://root@192.168.0.2/var/lib" the string
177 * "fish://root@192.168.0.2" will be returned, which leads to the
178 * navigation indication 'Custom Path > var > lib". For e. g.
179 * "settings:///System/" the path "settings://" will be returned.
180 */
181 QUrl retrievePlaceUrl() const;
182
183 KUrlNavigator *const q;
184
185 QHBoxLayout *m_layout = new QHBoxLayout(q);
186 KCoreUrlNavigator *m_coreUrlNavigator = nullptr;
187 QList<KUrlNavigatorButton *> m_navButtons;
188 QStringList m_supportedSchemes;
189 QUrl m_homeUrl;
190 KUrlNavigatorPlacesSelector *m_placesSelector = nullptr;
191 KUrlComboBox *m_pathBox = nullptr;
192 KUrlNavigatorSchemeCombo *m_schemes = nullptr;
193 KUrlNavigatorDropDownButton *m_dropDownButton = nullptr;
194 KUrlNavigatorButtonBase *m_toggleEditableMode = nullptr;
195 QWidget *m_dropWidget = nullptr;
196 QWidget *m_badgeWidgetContainer = nullptr;
197
198 bool m_editable = false;
199 bool m_active = true;
200 bool m_showPlacesSelector = false;
201 bool m_showFullPath = false;
202
203 int m_padding = 4;
204
205 struct {
206 bool showHidden = false;
207 bool sortHiddenLast = false;
208 } m_subfolderOptions;
209};
210
211KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, KFilePlacesModel *placesModel)
212 : q(qq)
213 , m_coreUrlNavigator(new KCoreUrlNavigator(url, qq))
214 , m_showPlacesSelector(placesModel != nullptr)
215{
216 m_layout->setSpacing(0);
217 m_layout->setContentsMargins(0, 0, 0, 0);
218
219 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentLocationUrlChanged, q, [this]() {
220 Q_EMIT q->urlChanged(m_coreUrlNavigator->currentLocationUrl());
221 });
222 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentUrlAboutToChange, q, [this](const QUrl &url) {
223 Q_EMIT q->urlAboutToBeChanged(url);
224 });
225 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historySizeChanged, q, [this]() {
226 Q_EMIT q->historyChanged();
227 });
228 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyIndexChanged, q, [this]() {
229 Q_EMIT q->historyChanged();
230 });
231 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyChanged, q, [this]() {
232 Q_EMIT q->historyChanged();
233 });
234 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::urlSelectionRequested, q, [this](const QUrl &url) {
235 Q_EMIT q->urlSelectionRequested(url);
236 });
237
238 // initialize the places selector
239 q->setAutoFillBackground(false);
240
241 if (placesModel != nullptr) {
242 m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
243 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::placeActivated, q, &KUrlNavigator::setLocationUrl);
244 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::tabRequested, q, &KUrlNavigator::tabRequested);
245
246 auto updateContentFunc = [this]() {
247 updateContent();
248 };
249 q->connect(placesModel, &KFilePlacesModel::rowsInserted, q, updateContentFunc);
250 q->connect(placesModel, &KFilePlacesModel::rowsRemoved, q, updateContentFunc);
251 q->connect(placesModel, &KFilePlacesModel::dataChanged, q, updateContentFunc);
252 }
253
254 // create scheme combo
255 m_schemes = new KUrlNavigatorSchemeCombo(QString(), q);
256 q->connect(m_schemes, &KUrlNavigatorSchemeCombo::activated, q, [this](const QString &schene) {
257 slotSchemeChanged(schene);
258 });
259
260 // create drop down button for accessing all paths of the URL
261 m_dropDownButton = new KUrlNavigatorDropDownButton(q);
262 m_dropDownButton->setForegroundRole(QPalette::WindowText);
263 m_dropDownButton->installEventFilter(q);
264 q->connect(m_dropDownButton, &KUrlNavigatorDropDownButton::clicked, q, [this]() {
265 openPathSelectorMenu();
266 });
267
268 // initialize the path box of the traditional view
269 m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
270 m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
271 m_pathBox->installEventFilter(q);
272 m_pathBox->setAutoFillBackground(false);
273 m_pathBox->setBackgroundRole(QPalette::Base);
274 m_pathBox->setFrame(false);
275
276 KUrlCompletion *kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
277 m_pathBox->setCompletionObject(kurlCompletion);
278 m_pathBox->setAutoDeleteCompletionObject(true);
279
280 // TODO KF6: remove this QOverload, only KUrlComboBox::returnPressed(const QString &) will remain
281 q->connect(m_pathBox, &KUrlComboBox::returnPressed, q, [this]() {
282 slotReturnPressed();
283 });
285 q->connect(m_pathBox, &QComboBox::editTextChanged, q, [this](const QString &text) {
286 slotPathBoxChanged(text);
287 });
288
289 m_badgeWidgetContainer = new QWidget(q);
290 auto badgeLayout = new QHBoxLayout(m_badgeWidgetContainer);
291 badgeLayout->setContentsMargins(0, 0, 0, 0);
292
293 // create toggle button which allows to switch between
294 // the breadcrumb and traditional view
295 m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
296 m_toggleEditableMode->installEventFilter(q);
297 m_toggleEditableMode->setMinimumWidth(20);
298 q->connect(m_toggleEditableMode, &KUrlNavigatorToggleButton::clicked, q, [this]() {
299 slotToggleEditableButtonPressed();
300 });
301
302 if (m_placesSelector != nullptr) {
303 m_layout->addWidget(m_placesSelector);
304 }
305 m_layout->addWidget(m_schemes);
306 m_layout->addWidget(m_dropDownButton);
307 m_layout->addWidget(m_pathBox, 1);
308 m_layout->addWidget(m_badgeWidgetContainer);
309 m_layout->addWidget(m_toggleEditableMode);
310
311 q->setContextMenuPolicy(Qt::CustomContextMenu);
312 q->connect(q, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
313 openContextMenu(pos);
314 });
315
316 // Make sure pathBox does not portrude outside of the above frameLineEdit background
317 const int paddingLeft = q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
318 const int paddingRight = q->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
319 q->setContentsMargins(paddingLeft, 0, paddingRight, 0);
320 m_pathBox->setContentsMargins(paddingLeft, 0, paddingRight, 0);
321}
322
323void KUrlNavigatorPrivate::appendWidget(QWidget *widget, int stretch)
324{
325 // insert to the left of: m_badgeWidgetContainer, m_toggleEditableMode
326 m_layout->insertWidget(m_layout->count() - 2, widget, stretch);
327}
328
329void KUrlNavigatorPrivate::slotApplyUrl(QUrl url)
330{
331 // Parts of the following code have been taken from the class KateFileSelector
332 // located in kate/app/katefileselector.hpp of Kate.
333 // SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
334 // SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
335 // SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
336
337 // For example "desktop:/" _not_ "desktop:", see the comment in slotSchemeChanged()
338 if (!url.isEmpty() && url.path().isEmpty() && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
339 url.setPath(QStringLiteral("/"));
340 }
341
342 const auto urlStr = url.toString();
343 QStringList urls = m_pathBox->urls();
344 urls.removeAll(urlStr);
345 urls.prepend(urlStr);
346 m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
347
348 q->setLocationUrl(url);
349 // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
350 // synchronize the result in the path box.
351 m_pathBox->setUrl(q->locationUrl());
352}
353
354std::optional<QUrl> KUrlNavigatorPrivate::checkFilters(const QString &text)
355{
356 KUriFilterData filteredData(text);
357 filteredData.setCheckForExecutables(false);
358 // Using kshorturifilter to fix up e.g. "ftp.kde.org" ---> "ftp://ftp.kde.org"
359 const auto filtersList = QStringList{QStringLiteral("kshorturifilter")};
360 const bool wasFiltered = KUriFilter::self()->filterUri(filteredData, filtersList);
361 if (wasFiltered) {
362 return filteredData.uri(); // The text was filtered
363 }
364 return std::nullopt;
365}
366
367void KUrlNavigatorPrivate::applyUncommittedUrl(ApplyUrlMethod method)
368{
369 const QString text = m_pathBox->currentText().trimmed();
370 QUrl url = q->locationUrl();
371
372 auto applyUrl = [this, method](const QUrl &url) {
373 switch (method) {
374 case ApplyUrlMethod::Apply:
375 slotApplyUrl(url);
376 break;
377 case ApplyUrlMethod::Tab:
378 Q_EMIT q->tabRequested(url);
379 break;
380 case ApplyUrlMethod::ActiveTab:
381 Q_EMIT q->activeTabRequested(url);
382 break;
383 case ApplyUrlMethod::NewWindow:
384 Q_EMIT q->newWindowRequested(url);
385 break;
386 }
387 };
388
389 // Using the stat job below, check if the url and text match a local dir; but first
390 // handle a special case where "url" is empty in the unittests which use
391 // KUrlNavigator::setLocationUrl(QUrl()); in practice (e.g. in Dolphin, or KFileWidget),
392 // locationUrl() is never empty
393 if (url.isEmpty() && !text.isEmpty()) {
394 if (const auto filteredUrl = checkFilters(text); filteredUrl) {
395 applyUrl(*filteredUrl);
396 return;
397 }
398 }
399
400 // Treat absolute paths as absolute paths.
401 // Relative paths get appended to the current path.
402 if (text.startsWith(QLatin1Char('/'))) {
403 url.setPath(text);
404 } else {
405 url.setPath(Utils::concatPaths(url.path(), text));
406 }
407
408 // Dirs and symlinks to dirs
409 constexpr auto details = KIO::StatBasic | KIO::StatResolveSymlink;
410 auto *job = KIO::stat(url, KIO::StatJob::DestinationSide, details, KIO::HideProgressInfo);
411 q->connect(job, &KJob::result, q, [this, job, text, applyUrl]() {
412 // If there is a dir matching "text" relative to the current url, use that, e.g.:
413 // - typing "bar" while at "/path/to/foo" ---> "/path/to/foo/bar/"
414 // - typing ".config" while at "/home/foo" ---> "/home/foo/.config"
415 if (!job->error() && job->statResult().isDir()) {
416 applyUrl(job->url());
417 return;
418 }
419
420 // Check if text matches a URI filter
421 if (const auto filteredUrl = checkFilters(text); filteredUrl) {
422 applyUrl(*filteredUrl);
423 return;
424 }
425
426 // ... otherwise fallback to whatever QUrl::fromUserInput() returns
427 applyUrl(QUrl::fromUserInput(text));
428 });
429}
430
431void KUrlNavigatorPrivate::slotReturnPressed()
432{
433 const auto keyboardModifiers = QApplication::keyboardModifiers();
434
435 if (keyboardModifiers & Qt::AltModifier) {
436 if (keyboardModifiers & Qt::ShiftModifier) {
437 applyUncommittedUrl(ApplyUrlMethod::Tab);
438 } else {
439 applyUncommittedUrl(ApplyUrlMethod::ActiveTab);
440 }
441 } else if (keyboardModifiers & Qt::ShiftModifier) {
442 applyUncommittedUrl(ApplyUrlMethod::NewWindow);
443 } else {
444 applyUncommittedUrl(ApplyUrlMethod::Apply);
445
446 Q_EMIT q->returnPressed();
447 }
448
449 if (keyboardModifiers & Qt::ControlModifier) {
450 // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
451 // The switch must be done asynchronously, as we are in the context of the
452 // editor.
453 auto switchModeFunc = [this]() {
454 switchToBreadcrumbMode();
455 };
457 }
458}
459
460void KUrlNavigatorPrivate::slotSchemeChanged(const QString &scheme)
461{
462 Q_ASSERT(m_editable);
463
464 QUrl url;
465 url.setScheme(scheme);
466 if (KProtocolInfo::protocolClass(scheme) == QLatin1String(":local")) {
467 // E.g. "file:/" or "desktop:/", _not_ "file:" or "desktop:" respectively.
468 // This is the more expected behaviour, "file:somedir" treats somedir as
469 // a path relative to current dir; file:/somedir is an absolute path to /somedir.
470 url.setPath(QStringLiteral("/"));
471 } else {
472 // With no authority set we'll get e.g. "ftp:" instead of "ftp://".
473 // We want the latter, so let's set an empty authority.
474 url.setAuthority(QString());
475 }
476
477 m_pathBox->setEditUrl(url);
478}
479
480void KUrlNavigatorPrivate::openPathSelectorMenu()
481{
482 if (m_navButtons.count() <= 0) {
483 return;
484 }
485
486 const QUrl firstVisibleUrl = m_navButtons.constFirst()->url();
487
488 QString spacer;
489 QPointer<QMenu> popup = new QMenu(q);
490
491 auto *popupFilter = new KUrlNavigatorPathSelectorEventFilter(popup.data());
492 q->connect(popupFilter, &KUrlNavigatorPathSelectorEventFilter::tabRequested, q, &KUrlNavigator::tabRequested);
493 popup->installEventFilter(popupFilter);
494
495 const QUrl placeUrl = retrievePlaceUrl();
496 int idx = placeUrl.path().count(QLatin1Char('/')); // idx points to the first directory
497 // after the place path
498
499 const QString path = m_coreUrlNavigator->locationUrl(m_coreUrlNavigator->historyIndex()).path();
500 QString dirName = path.section(QLatin1Char('/'), idx, idx);
501 if (dirName.isEmpty()) {
502 if (placeUrl.isLocalFile()) {
503 dirName = QStringLiteral("/");
504 } else {
505 dirName = placeUrl.toDisplayString();
506 }
507 }
508 do {
509 const QString text = spacer + dirName;
510
511 QAction *action = new QAction(text, popup);
512 const QUrl currentUrl = buttonUrl(idx);
513 if (currentUrl == firstVisibleUrl) {
514 popup->addSeparator();
515 }
516 action->setData(QVariant(currentUrl.toString()));
517 popup->addAction(action);
518
519 ++idx;
520 spacer.append(QLatin1String(" "));
521 dirName = path.section(QLatin1Char('/'), idx, idx);
522 } while (!dirName.isEmpty());
523
524 const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
525 const QAction *activatedAction = popup->exec(pos);
526 if (activatedAction != nullptr) {
527 const QUrl url(activatedAction->data().toString());
528 q->setLocationUrl(url);
529 }
530
531 // Delete the menu, unless it has been deleted in its own nested event loop already.
532 if (popup) {
533 popup->deleteLater();
534 }
535}
536
537void KUrlNavigatorPrivate::slotToggleEditableButtonPressed()
538{
539 if (m_editable) {
540 applyUncommittedUrl(ApplyUrlMethod::Apply);
541 }
542
543 switchView();
544}
545
546void KUrlNavigatorPrivate::switchView()
547{
548 m_toggleEditableMode->setFocus();
549 m_editable = !m_editable;
550 m_toggleEditableMode->setChecked(m_editable);
551 updateContent();
552 if (q->isUrlEditable()) {
553 m_pathBox->setFocus();
554 }
555
556 q->requestActivation();
557 Q_EMIT q->editableStateChanged(m_editable);
558}
559
560void KUrlNavigatorPrivate::dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton)
561{
562 if (event->mimeData()->hasUrls()) {
563 m_dropWidget = qobject_cast<QWidget *>(dropButton);
564 Q_EMIT q->urlsDropped(destination, event);
565 }
566}
567
568void KUrlNavigatorPrivate::slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
569{
570 if ((button & Qt::MiddleButton && modifiers & Qt::ShiftModifier) || (button & Qt::LeftButton && modifiers & (Qt::ControlModifier | Qt::ShiftModifier))) {
571 Q_EMIT q->activeTabRequested(url);
572 } else if (button & Qt::MiddleButton || (button & Qt::LeftButton && modifiers & Qt::ControlModifier)) {
573 Q_EMIT q->tabRequested(url);
574 } else if (button & Qt::LeftButton && modifiers & Qt::ShiftModifier) {
575 Q_EMIT q->newWindowRequested(url);
576 } else if (button & Qt::LeftButton) {
577 q->setLocationUrl(url);
578 }
579}
580
581void KUrlNavigatorPrivate::openContextMenu(const QPoint &p)
582{
583 q->setActive(true);
584
585 QPointer<QMenu> popup = new QMenu(q);
586
587 // provide 'Copy' action, which copies the current URL of
588 // the URL navigator into the clipboard
589 QAction *copyAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"));
590
591 // provide 'Paste' action, which copies the current clipboard text
592 // into the URL navigator
593 QAction *pasteAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("Paste"));
594 QClipboard *clipboard = QApplication::clipboard();
595 pasteAction->setEnabled(!clipboard->text().isEmpty());
596
597 popup->addSeparator();
598
599 // We are checking whether the signal is connected because it's odd to have a tab entry even
600 // if it's not supported, like in the case of the open dialog
601 const bool isTabSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::tabRequested));
602 const bool isWindowSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::newWindowRequested));
603 if (isTabSignal || isWindowSignal) {
604 auto it = std::find_if(m_navButtons.cbegin(), m_navButtons.cend(), [&p](const KUrlNavigatorButton *button) {
605 return button->geometry().contains(p);
606 });
607 if (it != m_navButtons.cend()) {
608 const auto *button = *it;
609 const QUrl url = button->url();
610 const QString text = button->text();
611
612 if (isTabSignal) {
613 QAction *openInTab = popup->addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open \"%1\" in New Tab", text));
614 q->connect(openInTab, &QAction::triggered, q, [this, url]() {
615 Q_EMIT q->tabRequested(url);
616 });
617 }
618
619 if (isWindowSignal) {
620 QAction *openInWindow =
621 popup->addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open \"%1\" in New Window", text));
622 q->connect(openInWindow, &QAction::triggered, q, [this, url]() {
623 Q_EMIT q->newWindowRequested(url);
624 });
625 }
626 }
627 }
628
629 // provide radiobuttons for toggling between the edit and the navigation mode
630 QAction *editAction = popup->addAction(i18n("Edit"));
631 editAction->setCheckable(true);
632
633 QAction *navigateAction = popup->addAction(i18n("Navigate"));
634 navigateAction->setCheckable(true);
635
636 QActionGroup *modeGroup = new QActionGroup(popup);
637 modeGroup->addAction(editAction);
638 modeGroup->addAction(navigateAction);
639 if (q->isUrlEditable()) {
640 editAction->setChecked(true);
641 } else {
642 navigateAction->setChecked(true);
643 }
644
645 popup->addSeparator();
646
647 // allow showing of the full path
648 QAction *showFullPathAction = popup->addAction(i18n("Show Full Path"));
649 showFullPathAction->setCheckable(true);
650 showFullPathAction->setChecked(q->showFullPath());
651
652 QAction *activatedAction = popup->exec(QCursor::pos());
653 if (activatedAction == copyAction) {
654 QMimeData *mimeData = new QMimeData();
655 mimeData->setText(q->locationUrl().toDisplayString(QUrl::PreferLocalFile));
656 clipboard->setMimeData(mimeData);
657 } else if (activatedAction == pasteAction) {
658 q->setLocationUrl(QUrl::fromUserInput(clipboard->text()));
659 } else if (activatedAction == editAction) {
660 q->setUrlEditable(true);
661 } else if (activatedAction == navigateAction) {
662 q->setUrlEditable(false);
663 } else if (activatedAction == showFullPathAction) {
664 q->setShowFullPath(showFullPathAction->isChecked());
665 }
666
667 // Delete the menu, unless it has been deleted in its own nested event loop already.
668 if (popup) {
669 popup->deleteLater();
670 }
671}
672
673void KUrlNavigatorPrivate::slotPathBoxChanged(const QString &text)
674{
675 if (text.isEmpty()) {
676 const QString scheme = q->locationUrl().scheme();
677 m_schemes->setScheme(scheme);
678 if (m_supportedSchemes.count() != 1) {
679 m_schemes->show();
680 updateTabOrder();
681 }
682 } else {
683 m_schemes->hide();
684 updateTabOrder();
685 }
686}
687
688void KUrlNavigatorPrivate::updateContent()
689{
690 const QUrl currentUrl = q->locationUrl();
691 if (m_placesSelector != nullptr) {
692 m_placesSelector->updateSelection(currentUrl);
693 }
694
695 if (m_editable) {
696 m_schemes->hide();
697 m_dropDownButton->hide();
698 m_badgeWidgetContainer->hide();
699
700 deleteButtons();
701 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
702 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
703
704 m_pathBox->show();
705 m_pathBox->setUrl(currentUrl);
706
707 q->setTabOrder(m_pathBox, m_toggleEditableMode); // Fixes order for the first time switchView() is called.
708 updateTabOrder();
709 } else {
710 m_pathBox->hide();
711 m_badgeWidgetContainer->show();
712
713 m_schemes->hide();
714
715 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
717
718 // Calculate the start index for the directories that should be shown as buttons
719 // and create the buttons
720 QUrl placeUrl;
721 if ((m_placesSelector != nullptr) && !m_showFullPath) {
722 placeUrl = m_placesSelector->selectedPlaceUrl();
723 }
724
725 if (!placeUrl.isValid()) {
726 placeUrl = retrievePlaceUrl();
727 }
728 QString placePath = Utils::trailingSlashRemoved(placeUrl.path());
729
730 const int startIndex = placePath.count(QLatin1Char('/'));
731 updateButtons(startIndex);
732 }
733}
734
735void KUrlNavigatorPrivate::updateButtons(int startIndex)
736{
737 QUrl currentUrl = q->locationUrl();
738 if (!currentUrl.isValid()) { // QFileDialog::setDirectory not called yet
739 return;
740 }
741
742 const QString path = currentUrl.path();
743
744 const int oldButtonCount = m_navButtons.count();
745
746 int idx = startIndex;
747 bool hasNext = true;
748 do {
749 const bool createButton = (idx - startIndex) >= oldButtonCount;
750 const bool isFirstButton = (idx == startIndex);
751 const QString dirName = path.section(QLatin1Char('/'), idx, idx);
752 hasNext = isFirstButton || !dirName.isEmpty();
753 if (hasNext) {
754 KUrlNavigatorButton *button = nullptr;
755 if (createButton) {
756 button = new KUrlNavigatorButton(buttonUrl(idx), q);
757 button->installEventFilter(q);
758 button->setForegroundRole(QPalette::WindowText);
759 q->connect(button, &KUrlNavigatorButton::urlsDroppedOnNavButton, q, [this, button](const QUrl &destination, QDropEvent *event) {
760 dropUrls(destination, event, button);
761 });
762
763 auto activatedFunc = [this](const QUrl &url, Qt::MouseButton btn, Qt::KeyboardModifiers modifiers) {
764 slotNavigatorButtonClicked(url, btn, modifiers);
765 };
766 q->connect(button, &KUrlNavigatorButton::navigatorButtonActivated, q, activatedFunc);
767
768 q->connect(button, &KUrlNavigatorButton::finishedTextResolving, q, [this]() {
769 updateButtonVisibility();
770 });
771
772 appendWidget(button);
773 } else {
774 button = m_navButtons[idx - startIndex];
775 button->setUrl(buttonUrl(idx));
776 }
777
778 if (isFirstButton) {
779 button->setText(firstButtonText());
780 }
781 button->setActive(q->isActive());
782
783 if (createButton) {
784 if (!isFirstButton) {
785 q->setTabOrder(m_navButtons.constLast(), button);
786 }
787 m_navButtons.append(button);
788 }
789
790 ++idx;
791 button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
792 }
793 } while (hasNext);
794
795 // delete buttons which are not used anymore
796 const int newButtonCount = idx - startIndex;
797 if (newButtonCount < oldButtonCount) {
798 const auto itBegin = m_navButtons.begin() + newButtonCount;
799 const auto itEnd = m_navButtons.end();
800 for (auto it = itBegin; it != itEnd; ++it) {
801 auto *navBtn = *it;
802 navBtn->hide();
803 navBtn->deleteLater();
804 }
805 m_navButtons.erase(itBegin, itEnd);
806 }
807
808 m_dropDownButton->setToolTip(xi18nc("@info:tooltip for button. 1 is path",
809 "Go to any location on the path <filename>%1</filename>",
811 .replace(QStringLiteral("///"), QStringLiteral("/")));
812 updateButtonVisibility();
813}
814
815void KUrlNavigatorPrivate::updateButtonVisibility()
816{
817 if (m_editable) {
818 return;
819 }
820
821 const int buttonsCount = m_navButtons.count();
822 if (buttonsCount == 0) {
823 m_dropDownButton->hide();
824 return;
825 }
826
827 // Subtract all widgets from the available width, that must be shown anyway
828 // Make sure to take the padding into account
829 int availableWidth = q->width() - (m_padding * 2) - m_toggleEditableMode->minimumWidth();
830
831 availableWidth -= m_badgeWidgetContainer->width();
832
833 if ((m_placesSelector != nullptr) && m_placesSelector->isVisible()) {
834 availableWidth -= m_placesSelector->width();
835 }
836
837 if ((m_schemes != nullptr) && m_schemes->isVisible()) {
838 availableWidth -= m_schemes->width();
839 }
840
841 // Check whether buttons must be hidden at all...
842 int requiredButtonWidth = 0;
843 for (const auto *button : std::as_const(m_navButtons)) {
844 requiredButtonWidth += button->minimumWidth();
845 }
846
847 if (requiredButtonWidth > availableWidth) {
848 // At least one button must be hidden. This implies that the
849 // drop-down button must get visible, which again decreases the
850 // available width.
851 availableWidth -= m_dropDownButton->width();
852 }
853
854 // Hide buttons...
855 bool isLastButton = true;
856 bool hasHiddenButtons = false;
857 QList<KUrlNavigatorButton *> buttonsToShow;
858 for (auto it = m_navButtons.crbegin(); it != m_navButtons.crend(); ++it) {
859 KUrlNavigatorButton *button = *it;
860 availableWidth -= button->minimumWidth();
861 if ((availableWidth <= 0) && !isLastButton) {
862 button->hide();
863 hasHiddenButtons = true;
864 } else {
865 // Don't show the button immediately, as setActive()
866 // might change the size and a relayout gets triggered
867 // after showing the button. So the showing of all buttons
868 // is postponed until all buttons have the correct
869 // activation state.
870 buttonsToShow.append(button);
871 }
872 isLastButton = false;
873 }
874
875 // All buttons have the correct activation state and
876 // can be shown now
877 for (KUrlNavigatorButton *button : std::as_const(buttonsToShow)) {
878 button->show();
879 }
880
881 if (hasHiddenButtons) {
882 m_dropDownButton->show();
883 } else {
884 // Check whether going upwards is possible. If this is the case, show the drop-down button.
885 QUrl url(m_navButtons.front()->url());
886 const bool visible = !url.matches(KIO::upUrl(url), QUrl::StripTrailingSlash) //
887 && url.scheme() != QLatin1String("baloosearch") //
888 && url.scheme() != QLatin1String("filenamesearch");
889 m_dropDownButton->setVisible(visible);
890 }
891
892 auto lastButton = m_navButtons.last();
893 for (const auto &button : m_navButtons) {
894 if (button != lastButton) {
895 button->setDrawSeparator(true);
896 } else {
897 button->setDrawSeparator(false);
898 }
899 }
900
901 updateTabOrder();
902}
903
904void KUrlNavigatorPrivate::updateTabOrder()
905{
906 QMultiMap<int, QWidget *> visibleChildrenSortedByX;
907 const auto childWidgets = q->findChildren<QWidget *>();
908 for (auto childWidget : childWidgets) {
909 if (childWidget->isVisible()) {
910 if (q->layoutDirection() == Qt::LeftToRight) {
911 visibleChildrenSortedByX.insert(childWidget->x(), childWidget); // sort ascending
912 } else {
913 visibleChildrenSortedByX.insert(-childWidget->x(), childWidget); // sort descending
914 }
915 }
916 }
917
918 if (visibleChildrenSortedByX.isEmpty()) {
919 return;
920 }
921 q->setFocusProxy(visibleChildrenSortedByX.first());
922 auto it = visibleChildrenSortedByX.begin();
923 auto nextIt = ++visibleChildrenSortedByX.begin();
924 while (nextIt != visibleChildrenSortedByX.end()) {
925 q->setTabOrder(*it, *nextIt);
926 it++;
927 nextIt++;
928 }
929 Q_EMIT q->layoutChanged();
930}
931
932QString KUrlNavigatorPrivate::firstButtonText() const
933{
934 QString text;
935
936 // The first URL navigator button should get the name of the
937 // place instead of the directory name
938 if ((m_placesSelector != nullptr) && !m_showFullPath) {
939 text = m_placesSelector->selectedPlaceText();
940 }
941
942 const QUrl currentUrl = q->locationUrl();
943
944 if (text.isEmpty()) {
945 if (currentUrl.isLocalFile()) {
946#ifdef Q_OS_WIN
947 text = currentUrl.path().length() > 1 ? currentUrl.path().left(2) : QDir::rootPath();
948#else
949 text = QStringLiteral("/");
950#endif
951 }
952 }
953
954 if (text.isEmpty()) {
955 if (currentUrl.path().isEmpty() || currentUrl.path() == QLatin1Char('/')) {
956 QUrlQuery query(currentUrl);
957 text = query.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded);
958 }
959 }
960
961 if (text.isEmpty()) {
962 text = currentUrl.scheme() + QLatin1Char(':');
963 if (!currentUrl.host().isEmpty()) {
964 text += QLatin1Char(' ') + currentUrl.host();
965 }
966 }
967
968 return text;
969}
970
971QUrl KUrlNavigatorPrivate::buttonUrl(int index) const
972{
973 if (index < 0) {
974 index = 0;
975 }
976
977 // Keep scheme, hostname etc. as this is needed for e. g. browsing
978 // FTP directories
979 QUrl url = q->locationUrl();
980 QString path = url.path();
981
982 if (!path.isEmpty()) {
983 if (index == 0) {
984 // prevent the last "/" from being stripped
985 // or we end up with an empty path
986#ifdef Q_OS_WIN
987 path = path.length() > 1 ? path.left(2) : QDir::rootPath();
988#else
989 path = QStringLiteral("/");
990#endif
991 } else {
992 path = path.section(QLatin1Char('/'), 0, index);
993 }
994 }
995
996 url.setPath(path);
997 return url;
998}
999
1000void KUrlNavigatorPrivate::switchToBreadcrumbMode()
1001{
1002 q->setUrlEditable(false);
1003}
1004
1005void KUrlNavigatorPrivate::deleteButtons()
1006{
1007 for (KUrlNavigatorButton *button : std::as_const(m_navButtons)) {
1008 button->hide();
1009 button->deleteLater();
1010 }
1011 m_navButtons.clear();
1012}
1013
1014QUrl KUrlNavigatorPrivate::retrievePlaceUrl() const
1015{
1016 QUrl currentUrl = q->locationUrl();
1017 currentUrl.setPath(QString());
1018 return currentUrl;
1019}
1020
1021// ------------------------------------------------------------------------------------------------
1022
1027
1029 : QWidget(parent)
1030 , d(new KUrlNavigatorPrivate(url, this, placesModel))
1031{
1032 const int minHeight = d->m_pathBox->sizeHint().height();
1033 setMinimumHeight(minHeight);
1034
1035 setMinimumWidth(100);
1036
1037 installEventFilter(this);
1038 d->updateContent();
1039 d->updateTabOrder();
1040}
1041
1042KUrlNavigator::~KUrlNavigator()
1043{
1044 d->m_dropDownButton->removeEventFilter(this);
1045 d->m_pathBox->removeEventFilter(this);
1046 for (auto *button : std::as_const(d->m_navButtons)) {
1047 button->removeEventFilter(this);
1048 }
1049 removeEventFilter(this);
1050}
1051
1053{
1054 return d->m_coreUrlNavigator->locationUrl(historyIndex);
1055}
1056
1058{
1059 auto current = d->m_coreUrlNavigator->locationState().value<KUrlNavigatorData>();
1060 current.state = state;
1061 d->m_coreUrlNavigator->saveLocationState(QVariant::fromValue(current));
1062}
1063
1065{
1066 return d->m_coreUrlNavigator->locationState(historyIndex).value<KUrlNavigatorData>().state;
1067}
1068
1070{
1071 return d->m_coreUrlNavigator->goBack();
1072}
1073
1075{
1076 return d->m_coreUrlNavigator->goForward();
1077}
1078
1080{
1081 return d->m_coreUrlNavigator->goUp();
1082}
1083
1085{
1086 if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
1088 } else {
1089 setLocationUrl(d->m_homeUrl);
1090 }
1091}
1092
1094{
1095 d->m_homeUrl = url;
1096}
1097
1098QUrl KUrlNavigator::homeUrl() const
1099{
1100 return d->m_homeUrl;
1101}
1102
1104{
1105 if (d->m_editable != editable) {
1106 d->switchView();
1107 }
1108}
1109
1111{
1112 return d->m_editable;
1113}
1114
1116{
1117 if (d->m_showFullPath != show) {
1118 d->m_showFullPath = show;
1119 d->updateContent();
1120 }
1121}
1122
1124{
1125 return d->m_showFullPath;
1126}
1127
1129{
1130 if (active != d->m_active) {
1131 d->m_active = active;
1132
1133 d->m_dropDownButton->setActive(active);
1134 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1135 button->setActive(active);
1136 }
1137
1138 update();
1139 if (active) {
1140 Q_EMIT activated();
1141 }
1142 }
1143}
1144
1146{
1147 return d->m_active;
1148}
1149
1151{
1152 if (visible == d->m_showPlacesSelector) {
1153 return;
1154 }
1155
1156 if (visible && (d->m_placesSelector == nullptr)) {
1157 // the places selector cannot get visible as no
1158 // places model is available
1159 return;
1160 }
1161
1162 d->m_showPlacesSelector = visible;
1163
1164 if (d->m_placesSelector) {
1165 d->m_placesSelector->setVisible(visible);
1166 d->updateTabOrder();
1167 }
1168}
1169
1171{
1172 return d->m_showPlacesSelector;
1173}
1174
1176{
1177 KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
1178 filteredData.setCheckForExecutables(false);
1179 if (KUriFilter::self()->filterUri(filteredData, QStringList{QStringLiteral("kshorturifilter")})) {
1180 return filteredData.uri();
1181 } else {
1182 return QUrl::fromUserInput(filteredData.typedString());
1183 }
1184}
1185
1187{
1188 d->m_coreUrlNavigator->setCurrentLocationUrl(newUrl);
1189
1190 d->updateContent();
1191
1193}
1194
1196{
1197 setActive(true);
1198}
1199
1201{
1202 if (isUrlEditable()) {
1203 d->m_pathBox->setFocus();
1204 } else {
1206 }
1207}
1208
1210{
1211 if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
1212 setUrlEditable(false);
1213 } else {
1215 }
1216}
1217
1219{
1221}
1222
1224{
1225 if (event->button() == Qt::MiddleButton) {
1227 }
1229}
1230
1232{
1233 if (event->button() == Qt::MiddleButton) {
1234 const QRect bounds = d->m_toggleEditableMode->geometry();
1235 if (bounds.contains(event->pos())) {
1236 // The middle mouse button has been clicked above the
1237 // toggle-editable-mode-button. Paste the clipboard content
1238 // as location URL.
1239 QClipboard *clipboard = QApplication::clipboard();
1240 const QMimeData *mimeData = clipboard->mimeData(QClipboard::Mode::Selection);
1241 if (mimeData && mimeData->hasText()) {
1242 const QString text = mimeData->text();
1243 const auto currentUrl = d->m_coreUrlNavigator->currentLocationUrl();
1244 QString workindDirectory;
1245 if (currentUrl.isLocalFile()) {
1246 workindDirectory = currentUrl.toLocalFile();
1247 }
1248 auto url = QUrl::fromUserInput(text, workindDirectory);
1249 if (url.isValid()) {
1250 setLocationUrl(url);
1251 }
1252 }
1253 }
1254 }
1256}
1257
1259{
1260 QTimer::singleShot(0, this, [this]() {
1261 d->updateButtonVisibility();
1262 });
1264}
1265
1267{
1268 setActive(true);
1270}
1271
1273{
1274 d->updateTabOrder();
1276}
1277
1278bool KUrlNavigator::eventFilter(QObject *watched, QEvent *event)
1279{
1280 switch (event->type()) {
1281 case QEvent::FocusIn:
1282 if (watched == d->m_pathBox) {
1284 setFocus();
1285 }
1286 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1287 button->setShowMnemonic(true);
1288 }
1289 update();
1290 break;
1291
1292 case QEvent::FocusOut:
1293 for (KUrlNavigatorButton *button : std::as_const(d->m_navButtons)) {
1294 button->setShowMnemonic(false);
1295 }
1296 update();
1297 break;
1298
1299 // Avoid the "Properties" action from triggering instead of new tab.
1301 auto *keyEvent = static_cast<QKeyEvent *>(event);
1302 if ((keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
1303 && (keyEvent->modifiers() & Qt::AltModifier || keyEvent->modifiers() & Qt::ShiftModifier)) {
1304 event->accept();
1305 return true;
1306 }
1307 break;
1308 }
1309
1310#if KIO_VERSION < QT_VERSION_CHECK(7, 0, 0)
1311 case QEvent::Paint: {
1312 // We can't call this in overridden paintEvent since applications using
1313 // the paint event is handled through the event filter:
1314 // Overriding paintEvent might not have an effect in applications
1315 // compiled against the older KIO, as they might work with an older vtable.
1316 // However, they would still see the new button style.
1317 // This makes sure the background is always drawn.
1318 if (watched == this) {
1319 auto *pEvent = static_cast<QPaintEvent *>(event);
1320 if (pEvent) {
1322 return true;
1323 }
1324 }
1325 break;
1326 }
1327#endif
1328
1329 default:
1330 break;
1331 }
1332
1333 return QWidget::eventFilter(watched, event);
1334}
1335
1337{
1338 return d->m_coreUrlNavigator->historySize();
1339}
1340
1342{
1343 return d->m_coreUrlNavigator->historyIndex();
1344}
1345
1347{
1348 return d->m_pathBox;
1349}
1350
1352{
1353 d->m_supportedSchemes = schemes;
1354 d->m_schemes->setSupportedSchemes(d->m_supportedSchemes);
1355}
1356
1358{
1359 return d->m_supportedSchemes;
1360}
1361
1363{
1364 return d->m_dropWidget;
1365}
1366
1368{
1369 d->m_subfolderOptions.showHidden = showHiddenFolders;
1370}
1371
1373{
1374 return d->m_subfolderOptions.showHidden;
1375}
1376
1378{
1379 d->m_subfolderOptions.sortHiddenLast = sortHiddenFoldersLast;
1380}
1381
1383{
1384 return d->m_subfolderOptions.sortHiddenLast;
1385}
1386
1388{
1389 QWidget *oldWidget = badgeWidget();
1390 if (oldWidget) {
1391 if (widget == oldWidget) {
1392 return;
1393 }
1394 d->m_badgeWidgetContainer->layout()->replaceWidget(oldWidget, widget);
1395 oldWidget->deleteLater();
1396 } else {
1397 d->m_badgeWidgetContainer->layout()->addWidget(widget);
1398 }
1399}
1400
1402{
1403 QLayoutItem *item = d->m_badgeWidgetContainer->layout()->itemAt(0);
1404 if (item) {
1405 return item->widget();
1406 } else {
1407 return nullptr;
1408 }
1409}
1410
1412{
1413 Q_UNUSED(event);
1414 QPainter painter(this);
1415 QStyleOptionFrame option;
1416 option.initFrom(this);
1417 option.state = QStyle::State_Sunken;
1418
1419 if (hasFocus()) {
1420 option.palette.setColor(QPalette::Window, palette().color(QPalette::Highlight));
1421 }
1422
1423 // Adjust the rectangle due to how QRect coordinates work
1424 option.rect = option.rect.adjusted(1, 0, -1, 0);
1425 // Draw the background
1426 style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this);
1427}
1428
1429#include "moc_kurlnavigator.cpp"
void returnPressed(const QString &text)
Object that helps with keeping track of URLs in file-manager like interfaces.
Q_SIGNAL void urlSelectionRequested(const QUrl &url)
When the URL is changed and the new URL (e.g. /home/user1/) is a parent of the previous URL (e....
Q_SIGNAL void currentUrlAboutToChange(const QUrl &newUrl)
Is emitted, before the location URL is going to be changed to newUrl.
Q_SIGNAL void historyChanged()
Is emitted, if the history has been changed.
This class is a list view model.
void result(KJob *job)
static QString protocolClass(const QString &protocol)
Returns the protocol class for the specified protocol.
This class is a basic messaging class used to exchange filtering information between the filter plugi...
Definition kurifilter.h:153
QUrl uri() const
Returns the filtered or the original URL.
QString typedString() const
The string as typed by the user, before any URL processing is done.
void setCheckForExecutables(bool check)
Check whether the provided uri is executable or not.
static KUriFilter * self()
Returns an instance of KUriFilter.
bool filterUri(KUriFilterData &data, const QStringList &filters=QStringList())
Filters data using the specified filters.
This combobox shows a number of recent URLs/directories, as well as some default directories.
void urlActivated(const QUrl &url)
Emitted when an item was clicked at.
This class does completion of URLs including user directories (~user) and environment variables.
Widget that allows to navigate through the paths of an URL.
void newWindowRequested(const QUrl &url)
Is emitted if the URL url should be opened in a new window because the user left-clicked on a breadcr...
void setShowFullPath(bool show)
Shows the full path of the URL even if a place represents a part of the URL.
void setBadgeWidget(QWidget *widget)
Puts widget to the right of the breadcrumb.
void setSortHiddenFoldersLast(bool sortHiddenFoldersLast)
Sets whether to sort hidden folders in the subdirectories popup last.
void setPlacesSelectorVisible(bool visible)
Sets the places selector visible, if visible is true.
void setLocationUrl(const QUrl &url)
Sets the location to url.
bool goUp()
Goes up one step of the URL path and remembers the old path in the history.
bool showHiddenFolders() const
Returns whether to show hidden folders in the subdirectories popup.
QUrl uncommittedUrl() const
KUrlComboBox * editor() const
int historyIndex() const
QStringList supportedSchemes() const
Returns the URL schemes that the navigator should allow navigating to.
KUrlNavigator(QWidget *parent=nullptr)
bool sortHiddenFoldersLast() const
Returns whether to sort hidden folders in the subdirectories popup last.
void tabRequested(const QUrl &url)
Is emitted if the URL url should be opened in a new inactive tab because the user clicked on a breadc...
void saveLocationState(const QByteArray &state)
Saves the location state described by state for the current location.
void goHome()
Goes to the home URL and remembers the old URL in the history.
void setHomeUrl(const QUrl &url)
Sets the home URL used by KUrlNavigator::goHome().
QWidget * badgeWidget() const
Returns the badge widget set by setBadgeWidget().
bool goBack()
Goes back one step in the URL history.
QUrl locationUrl(int historyIndex=-1) const
void requestActivation()
Activates the URL navigator (KUrlNavigator::isActive() will return true) and emits the signal KUrlNav...
void setUrlEditable(bool editable)
Allows to edit the URL of the navigation bar if editable is true, and sets the focus accordingly.
bool isPlacesSelectorVisible() const
bool isActive() const
int historySize() const
bool showFullPath() const
bool goForward()
Goes forward one step in the URL history.
void setShowHiddenFolders(bool showHiddenFolders)
Sets whether to show hidden folders in the subdirectories popup.
void setActive(bool active)
Set the URL navigator to the active mode, if active is true.
QWidget * dropWidget() const
The child widget that received the QDropEvent when dropping on the URL navigator.
void activated()
Is emitted, if the URL navigator has been activated by an user interaction.
QByteArray locationState(int historyIndex=-1) const
bool isUrlEditable() const
void setSupportedSchemes(const QStringList &schemes)
Set the URL schemes that the navigator should allow navigating to.
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition statjob.cpp:203
KIOCORE_EXPORT QUrl upUrl(const QUrl &url)
This function is useful to implement the "Up" button in a file manager for example.
Definition global.cpp:238
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ StatBasic
Filename, access, type, size, linkdest.
Definition global.h:255
@ StatResolveSymlink
Resolve symlinks.
Definition global.h:261
QString path(const QString &relativePath)
const QList< QKeySequence > & openContextMenu()
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
void setCheckable(bool)
void setChecked(bool)
QVariant data() const const
void setEnabled(bool)
void setData(const QVariant &data)
void triggered(bool checked)
QAction * addAction(QAction *action)
const QMimeData * mimeData(Mode mode) const const
void setMimeData(QMimeData *src, Mode mode)
QString text(Mode mode) const const
AdjustToContentsOnFirstShow
void editTextChanged(const QString &text)
QPoint pos()
QString homePath()
QString rootPath()
QClipboard * clipboard()
Qt::KeyboardModifiers keyboardModifiers()
QIcon fromTheme(const QString &name)
virtual QLayoutItem * itemAt(int index) const const=0
virtual QLayout * layout()
virtual QWidget * widget() const const
void append(QList< T > &&value)
void prepend(parameter_type value)
qsizetype removeAll(const AT &t)
QMetaMethod fromSignal(PointerToMemberFunction signal)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
bool hasText() const const
void setText(const QString &text)
QString text() const const
iterator begin()
iterator end()
T & first()
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
Q_EMITQ_EMIT
void deleteLater()
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
QObject * parent() const const
void removeEventFilter(QObject *obj)
T * data() const const
bool contains(const QPoint &point, bool proper) const const
qsizetype count() const const
QString & append(QChar ch)
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString trimmed() const const
PM_LayoutLeftMargin
PE_FrameLineEdit
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
void initFrom(const QWidget *widget)
QueuedConnection
CustomContextMenu
Key_Escape
typedef KeyboardModifiers
LeftToRight
MouseButton
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
FullyDecoded
PreferLocalFile
void clear()
QUrl fromLocalFile(const QString &localFile)
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
QString host(ComponentFormattingOptions options) const const
bool isEmpty() const const
bool isLocalFile() const const
bool isValid() const const
bool matches(const QUrl &url, FormattingOptions options) const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setAuthority(const QString &authority, ParsingMode mode)
void setPath(const QString &path, ParsingMode mode)
void setScheme(const QString &scheme)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QString url(FormattingOptions options) const const
QVariant fromValue(T &&value)
QString toString() const const
QWidget(QWidget *parent, Qt::WindowFlags f)
void customContextMenuRequested(const QPoint &pos)
virtual bool event(QEvent *event) override
bool hasFocus() const const
virtual void keyPressEvent(QKeyEvent *event)
virtual void keyReleaseEvent(QKeyEvent *event)
void setMinimumHeight(int minh)
void setMinimumWidth(int minw)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)
virtual void paintEvent(QPaintEvent *event)
virtual void resizeEvent(QResizeEvent *event)
void setFocus()
void show()
virtual void showEvent(QShowEvent *event)
QStyle * style() const const
void update()
virtual void wheelEvent(QWheelEvent *event)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 21 2025 11:54:39 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.