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 bool m_backgroundEnabled = true;
203
204 int m_padding = 5;
205
206 struct {
207 bool showHidden = false;
208 bool sortHiddenLast = false;
209 } m_subfolderOptions;
210};
211
212KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, KFilePlacesModel *placesModel)
213 : q(qq)
214 , m_coreUrlNavigator(new KCoreUrlNavigator(url, qq))
215 , m_showPlacesSelector(placesModel != nullptr)
216{
217 m_layout->setSpacing(0);
218 m_layout->setContentsMargins(0, 0, 0, 0);
219 QStyleOption option;
220 option.initFrom(q);
221
222 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentLocationUrlChanged, q, [this]() {
223 Q_EMIT q->urlChanged(m_coreUrlNavigator->currentLocationUrl());
224 });
225 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentUrlAboutToChange, q, [this](const QUrl &url) {
226 Q_EMIT q->urlAboutToBeChanged(url);
227 });
228 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historySizeChanged, q, [this]() {
229 Q_EMIT q->historyChanged();
230 });
231 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyIndexChanged, q, [this]() {
232 Q_EMIT q->historyChanged();
233 });
234 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::historyChanged, q, [this]() {
235 Q_EMIT q->historyChanged();
236 });
237 q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::urlSelectionRequested, q, [this](const QUrl &url) {
238 Q_EMIT q->urlSelectionRequested(url);
239 });
240
241 // initialize the places selector
242 q->setAutoFillBackground(false);
243
244 if (placesModel != nullptr) {
245 m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
246 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::placeActivated, q, &KUrlNavigator::setLocationUrl);
247 q->connect(m_placesSelector, &KUrlNavigatorPlacesSelector::tabRequested, q, &KUrlNavigator::tabRequested);
248
249 auto updateContentFunc = [this]() {
250 updateContent();
251 };
252 q->connect(placesModel, &KFilePlacesModel::rowsInserted, q, updateContentFunc);
253 q->connect(placesModel, &KFilePlacesModel::rowsRemoved, q, updateContentFunc);
254 q->connect(placesModel, &KFilePlacesModel::dataChanged, q, updateContentFunc);
255 }
256
257 // create scheme combo
258 m_schemes = new KUrlNavigatorSchemeCombo(QString(), q);
259 q->connect(m_schemes, &KUrlNavigatorSchemeCombo::activated, q, [this](const QString &schene) {
260 slotSchemeChanged(schene);
261 });
262
263 // create drop down button for accessing all paths of the URL
264 m_dropDownButton = new KUrlNavigatorDropDownButton(q);
265 m_dropDownButton->setForegroundRole(QPalette::WindowText);
266 m_dropDownButton->installEventFilter(q);
267 q->connect(m_dropDownButton, &KUrlNavigatorDropDownButton::clicked, q, [this]() {
268 openPathSelectorMenu();
269 });
270
271 // initialize the path box of the traditional view
272 m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
273 m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
274 m_pathBox->installEventFilter(q);
275 m_pathBox->setAutoFillBackground(false);
276 m_pathBox->setBackgroundRole(QPalette::Base);
277 m_pathBox->setFrame(false);
278
279 KUrlCompletion *kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
280 m_pathBox->setCompletionObject(kurlCompletion);
281 m_pathBox->setAutoDeleteCompletionObject(true);
282
283 // TODO KF6: remove this QOverload, only KUrlComboBox::returnPressed(const QString &) will remain
284 q->connect(m_pathBox, &KUrlComboBox::returnPressed, q, [this]() {
285 slotReturnPressed();
286 });
288 q->connect(m_pathBox, &QComboBox::editTextChanged, q, [this](const QString &text) {
289 slotPathBoxChanged(text);
290 });
291
292 m_badgeWidgetContainer = new QWidget(q);
293 auto badgeLayout = new QHBoxLayout(m_badgeWidgetContainer);
294 badgeLayout->setContentsMargins(0, 0, 0, 0);
295
296 // create toggle button which allows to switch between
297 // the breadcrumb and traditional view
298 m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
299 m_toggleEditableMode->installEventFilter(q);
300 m_toggleEditableMode->setMinimumWidth(20);
301 q->connect(m_toggleEditableMode, &KUrlNavigatorToggleButton::clicked, q, [this]() {
302 slotToggleEditableButtonPressed();
303 });
304
305 if (m_placesSelector != nullptr) {
306 m_layout->addWidget(m_placesSelector);
307 }
308 m_layout->addWidget(m_schemes);
309 m_layout->addWidget(m_dropDownButton);
310 m_layout->addWidget(m_pathBox, 1);
311 m_layout->addWidget(m_badgeWidgetContainer);
312 m_layout->addSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &option, q));
313 m_layout->addWidget(m_toggleEditableMode);
314
315 q->setContextMenuPolicy(Qt::CustomContextMenu);
316 q->connect(q, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) {
317 openContextMenu(pos);
318 });
319
320 // Make sure pathBox does not portrude outside of the above frameLineEdit background
321 const int paddingLeft = q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
322 const int paddingRight = q->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
323 q->rect().adjust(0, -1, 0, 1);
324 q->setContentsMargins(paddingLeft, 1, paddingRight, 1);
325 m_pathBox->setContentsMargins(paddingLeft, 0, paddingRight, 0);
326}
327
328void KUrlNavigatorPrivate::appendWidget(QWidget *widget, int stretch)
329{
330 // insert to the left of: m_badgeWidgetContainer, m_toggleEditableMode
331 m_layout->insertWidget(m_layout->count() - 2, widget, stretch);
332}
333
334void KUrlNavigatorPrivate::slotApplyUrl(QUrl url)
335{
336 // Parts of the following code have been taken from the class KateFileSelector
337 // located in kate/app/katefileselector.hpp of Kate.
338 // SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
339 // SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
340 // SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
341
342 // For example "desktop:/" _not_ "desktop:", see the comment in slotSchemeChanged()
343 if (!url.isEmpty() && url.path().isEmpty() && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
344 url.setPath(QStringLiteral("/"));
345 }
346
347 const auto urlStr = url.toString();
348 QStringList urls = m_pathBox->urls();
349 urls.removeAll(urlStr);
350 urls.prepend(urlStr);
351 m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
352
353 q->setLocationUrl(url);
354 // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
355 // synchronize the result in the path box.
356 m_pathBox->setUrl(q->locationUrl());
357}
358
359std::optional<QUrl> KUrlNavigatorPrivate::checkFilters(const QString &text)
360{
361 KUriFilterData filteredData(text);
362 filteredData.setCheckForExecutables(false);
363 // Using kshorturifilter to fix up e.g. "ftp.kde.org" ---> "ftp://ftp.kde.org"
364 const auto filtersList = QStringList{QStringLiteral("kshorturifilter")};
365 const bool wasFiltered = KUriFilter::self()->filterUri(filteredData, filtersList);
366 if (wasFiltered) {
367 return filteredData.uri(); // The text was filtered
368 }
369 return std::nullopt;
370}
371
372void KUrlNavigatorPrivate::applyUncommittedUrl(ApplyUrlMethod method)
373{
374 const QString text = m_pathBox->currentText().trimmed();
375 QUrl url = q->locationUrl();
376
377 auto applyUrl = [this, method](const QUrl &url) {
378 switch (method) {
379 case ApplyUrlMethod::Apply:
380 slotApplyUrl(url);
381 break;
382 case ApplyUrlMethod::Tab:
383 Q_EMIT q->tabRequested(url);
384 break;
385 case ApplyUrlMethod::ActiveTab:
386 Q_EMIT q->activeTabRequested(url);
387 break;
388 case ApplyUrlMethod::NewWindow:
389 Q_EMIT q->newWindowRequested(url);
390 break;
391 }
392 };
393
394 // Using the stat job below, check if the url and text match a local dir; but first
395 // handle a special case where "url" is empty in the unittests which use
396 // KUrlNavigator::setLocationUrl(QUrl()); in practice (e.g. in Dolphin, or KFileWidget),
397 // locationUrl() is never empty
398 if (url.isEmpty() && !text.isEmpty()) {
399 if (const auto filteredUrl = checkFilters(text); filteredUrl) {
400 applyUrl(*filteredUrl);
401 return;
402 }
403 }
404
405 // Treat absolute paths as absolute paths.
406 // Relative paths get appended to the current path.
407 if (text.startsWith(QLatin1Char('/'))) {
408 url.setPath(text);
409 } else {
410 url.setPath(Utils::concatPaths(url.path(), text));
411 }
412
413 // Dirs and symlinks to dirs
414 constexpr auto details = KIO::StatBasic | KIO::StatResolveSymlink;
415 auto *job = KIO::stat(url, KIO::StatJob::DestinationSide, details, KIO::HideProgressInfo);
416 q->connect(job, &KJob::result, q, [this, job, text, applyUrl]() {
417 // If there is a dir matching "text" relative to the current url, use that, e.g.:
418 // - typing "bar" while at "/path/to/foo" ---> "/path/to/foo/bar/"
419 // - typing ".config" while at "/home/foo" ---> "/home/foo/.config"
420 if (!job->error() && job->statResult().isDir()) {
421 applyUrl(job->url());
422 return;
423 }
424
425 // Check if text matches a URI filter
426 if (const auto filteredUrl = checkFilters(text); filteredUrl) {
427 applyUrl(*filteredUrl);
428 return;
429 }
430
431 // ... otherwise fallback to whatever QUrl::fromUserInput() returns
432 applyUrl(QUrl::fromUserInput(text));
433 });
434}
435
436void KUrlNavigatorPrivate::slotReturnPressed()
437{
438 const auto keyboardModifiers = QApplication::keyboardModifiers();
439
440 if (keyboardModifiers & Qt::AltModifier) {
441 if (keyboardModifiers & Qt::ShiftModifier) {
442 applyUncommittedUrl(ApplyUrlMethod::Tab);
443 } else {
444 applyUncommittedUrl(ApplyUrlMethod::ActiveTab);
445 }
446 } else if (keyboardModifiers & Qt::ShiftModifier) {
447 applyUncommittedUrl(ApplyUrlMethod::NewWindow);
448 } else {
449 applyUncommittedUrl(ApplyUrlMethod::Apply);
450
451 Q_EMIT q->returnPressed();
452 }
453
454 if (keyboardModifiers & Qt::ControlModifier) {
455 // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
456 // The switch must be done asynchronously, as we are in the context of the
457 // editor.
458 auto switchModeFunc = [this]() {
459 switchToBreadcrumbMode();
460 };
462 }
463}
464
465void KUrlNavigatorPrivate::slotSchemeChanged(const QString &scheme)
466{
467 Q_ASSERT(m_editable);
468
469 QUrl url;
470 url.setScheme(scheme);
471 if (KProtocolInfo::protocolClass(scheme) == QLatin1String(":local")) {
472 // E.g. "file:/" or "desktop:/", _not_ "file:" or "desktop:" respectively.
473 // This is the more expected behaviour, "file:somedir" treats somedir as
474 // a path relative to current dir; file:/somedir is an absolute path to /somedir.
475 url.setPath(QStringLiteral("/"));
476 } else {
477 // With no authority set we'll get e.g. "ftp:" instead of "ftp://".
478 // We want the latter, so let's set an empty authority.
479 url.setAuthority(QString());
480 }
481
482 m_pathBox->setEditUrl(url);
483}
484
485void KUrlNavigatorPrivate::openPathSelectorMenu()
486{
487 if (m_navButtons.count() <= 0) {
488 return;
489 }
490
491 const QUrl firstVisibleUrl = m_navButtons.constFirst()->url();
492
493 QString spacer;
494 QPointer<QMenu> popup = new QMenu(q);
495
496 auto *popupFilter = new KUrlNavigatorPathSelectorEventFilter(popup.data());
497 q->connect(popupFilter, &KUrlNavigatorPathSelectorEventFilter::tabRequested, q, &KUrlNavigator::tabRequested);
498 popup->installEventFilter(popupFilter);
499
500 const QUrl placeUrl = retrievePlaceUrl();
501 int idx = placeUrl.path().count(QLatin1Char('/')); // idx points to the first directory
502 // after the place path
503
504 const QString path = m_coreUrlNavigator->locationUrl(m_coreUrlNavigator->historyIndex()).path();
505 QString dirName = path.section(QLatin1Char('/'), idx, idx);
506 if (dirName.isEmpty()) {
507 if (placeUrl.isLocalFile()) {
508 dirName = QStringLiteral("/");
509 } else {
510 dirName = placeUrl.toDisplayString();
511 }
512 }
513 do {
514 const QString text = spacer + dirName;
515
516 QAction *action = new QAction(text, popup);
517 const QUrl currentUrl = buttonUrl(idx);
518 if (currentUrl == firstVisibleUrl) {
519 popup->addSeparator();
520 }
521 action->setData(QVariant(currentUrl.toString()));
522 popup->addAction(action);
523
524 ++idx;
525 spacer.append(QLatin1String(" "));
526 dirName = path.section(QLatin1Char('/'), idx, idx);
527 } while (!dirName.isEmpty());
528
529 const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
530 const QAction *activatedAction = popup->exec(pos);
531 if (activatedAction != nullptr) {
532 const QUrl url(activatedAction->data().toString());
533 q->setLocationUrl(url);
534 }
535
536 // Delete the menu, unless it has been deleted in its own nested event loop already.
537 if (popup) {
538 popup->deleteLater();
539 }
540}
541
542void KUrlNavigatorPrivate::slotToggleEditableButtonPressed()
543{
544 if (m_editable) {
545 applyUncommittedUrl(ApplyUrlMethod::Apply);
546 }
547
548 switchView();
549}
550
551void KUrlNavigatorPrivate::switchView()
552{
553 m_toggleEditableMode->setFocus();
554 m_editable = !m_editable;
555 m_toggleEditableMode->setChecked(m_editable);
556 updateContent();
557 if (q->isUrlEditable()) {
558 m_pathBox->setFixedHeight(m_badgeWidgetContainer->height());
559 m_pathBox->setFocus();
560 }
561
562 q->requestActivation();
563 Q_EMIT q->editableStateChanged(m_editable);
564 // Make sure the colors are updated
565 q->update();
566}
567
568void KUrlNavigatorPrivate::dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton)
569{
570 if (event->mimeData()->hasUrls()) {
571 m_dropWidget = qobject_cast<QWidget *>(dropButton);
572 Q_EMIT q->urlsDropped(destination, event);
573 }
574}
575
576void KUrlNavigatorPrivate::slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
577{
578 if ((button & Qt::MiddleButton && modifiers & Qt::ShiftModifier) || (button & Qt::LeftButton && modifiers & (Qt::ControlModifier | Qt::ShiftModifier))) {
579 Q_EMIT q->activeTabRequested(url);
580 } else if (button & Qt::MiddleButton || (button & Qt::LeftButton && modifiers & Qt::ControlModifier)) {
581 Q_EMIT q->tabRequested(url);
582 } else if (button & Qt::LeftButton && modifiers & Qt::ShiftModifier) {
583 Q_EMIT q->newWindowRequested(url);
584 } else if (button & Qt::LeftButton) {
585 q->setLocationUrl(url);
586 }
587}
588
589void KUrlNavigatorPrivate::openContextMenu(const QPoint &p)
590{
591 q->setActive(true);
592
593 QPointer<QMenu> popup = new QMenu(q);
594
595 // provide 'Copy' action, which copies the current URL of
596 // the URL navigator into the clipboard
597 QAction *copyAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"));
598
599 // provide 'Paste' action, which copies the current clipboard text
600 // into the URL navigator
601 QAction *pasteAction = popup->addAction(QIcon::fromTheme(QStringLiteral("edit-paste")), i18n("Paste"));
602 QClipboard *clipboard = QApplication::clipboard();
603 pasteAction->setEnabled(!clipboard->text().isEmpty());
604
605 popup->addSeparator();
606
607 // We are checking whether the signal is connected because it's odd to have a tab entry even
608 // if it's not supported, like in the case of the open dialog
609 const bool isTabSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::tabRequested));
610 const bool isWindowSignal = q->isSignalConnected(QMetaMethod::fromSignal(&KUrlNavigator::newWindowRequested));
611 if (isTabSignal || isWindowSignal) {
612 auto it = std::find_if(m_navButtons.cbegin(), m_navButtons.cend(), [&p](const KUrlNavigatorButton *button) {
613 return button->geometry().contains(p);
614 });
615 if (it != m_navButtons.cend()) {
616 const auto *button = *it;
617 const QUrl url = button->url();
618 const QString text = button->text();
619
620 if (isTabSignal) {
621 QAction *openInTab = popup->addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open \"%1\" in New Tab", text));
622 q->connect(openInTab, &QAction::triggered, q, [this, url]() {
623 Q_EMIT q->tabRequested(url);
624 });
625 }
626
627 if (isWindowSignal) {
628 QAction *openInWindow =
629 popup->addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open \"%1\" in New Window", text));
630 q->connect(openInWindow, &QAction::triggered, q, [this, url]() {
631 Q_EMIT q->newWindowRequested(url);
632 });
633 }
634 }
635 }
636
637 // provide radiobuttons for toggling between the edit and the navigation mode
638 QAction *editAction = popup->addAction(i18n("Edit"));
639 editAction->setCheckable(true);
640
641 QAction *navigateAction = popup->addAction(i18n("Navigate"));
642 navigateAction->setCheckable(true);
643
644 QActionGroup *modeGroup = new QActionGroup(popup);
645 modeGroup->addAction(editAction);
646 modeGroup->addAction(navigateAction);
647 if (q->isUrlEditable()) {
648 editAction->setChecked(true);
649 } else {
650 navigateAction->setChecked(true);
651 }
652
653 popup->addSeparator();
654
655 // allow showing of the full path
656 QAction *showFullPathAction = popup->addAction(i18n("Show Full Path"));
657 showFullPathAction->setCheckable(true);
658 showFullPathAction->setChecked(q->showFullPath());
659
660 QAction *activatedAction = popup->exec(QCursor::pos());
661 if (activatedAction == copyAction) {
662 QMimeData *mimeData = new QMimeData();
663 mimeData->setText(q->locationUrl().toDisplayString(QUrl::PreferLocalFile));
664 clipboard->setMimeData(mimeData);
665 } else if (activatedAction == pasteAction) {
666 q->setLocationUrl(QUrl::fromUserInput(clipboard->text()));
667 } else if (activatedAction == editAction) {
668 q->setUrlEditable(true);
669 } else if (activatedAction == navigateAction) {
670 q->setUrlEditable(false);
671 } else if (activatedAction == showFullPathAction) {
672 q->setShowFullPath(showFullPathAction->isChecked());
673 }
674
675 // Delete the menu, unless it has been deleted in its own nested event loop already.
676 if (popup) {
677 popup->deleteLater();
678 }
679}
680
681void KUrlNavigatorPrivate::slotPathBoxChanged(const QString &text)
682{
683 if (text.isEmpty()) {
684 const QString scheme = q->locationUrl().scheme();
685 m_schemes->setScheme(scheme);
686 if (m_supportedSchemes.count() != 1) {
687 m_schemes->show();
688 updateTabOrder();
689 }
690 } else {
691 m_schemes->hide();
692 updateTabOrder();
693 }
694}
695
696void KUrlNavigatorPrivate::updateContent()
697{
698 const QUrl currentUrl = q->locationUrl();
699 if (m_placesSelector != nullptr) {
700 m_placesSelector->updateSelection(currentUrl);
701 }
702
703 if (m_editable) {
704 m_schemes->hide();
705 m_dropDownButton->hide();
706 m_badgeWidgetContainer->hide();
707
708 deleteButtons();
709 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
710 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
711
712 m_pathBox->show();
713 m_pathBox->setUrl(currentUrl);
714
715 q->setTabOrder(m_pathBox, m_toggleEditableMode); // Fixes order for the first time switchView() is called.
716 updateTabOrder();
717 } else {
718 m_pathBox->hide();
719 m_badgeWidgetContainer->show();
720
721 m_schemes->hide();
722
723 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
725
726 // Calculate the start index for the directories that should be shown as buttons
727 // and create the buttons
728 QUrl placeUrl;
729 if ((m_placesSelector != nullptr) && !m_showFullPath) {
730 placeUrl = m_placesSelector->selectedPlaceUrl();
731 }
732
733 if (!placeUrl.isValid()) {
734 placeUrl = retrievePlaceUrl();
735 }
736 QString placePath = Utils::trailingSlashRemoved(placeUrl.path());
737
738 const int startIndex = placePath.count(QLatin1Char('/'));
739 updateButtons(startIndex);
740 }
741}
742
743void KUrlNavigatorPrivate::updateButtons(int startIndex)
744{
745 QUrl currentUrl = q->locationUrl();
746 if (!currentUrl.isValid()) { // QFileDialog::setDirectory not called yet
747 return;
748 }
749
750 const QString path = currentUrl.path();
751
752 const int oldButtonCount = m_navButtons.count();
753
754 int idx = startIndex;
755 bool hasNext = true;
756 do {
757 const bool createButton = (idx - startIndex) >= oldButtonCount;
758 const bool isFirstButton = (idx == startIndex);
759 const QString dirName = path.section(QLatin1Char('/'), idx, idx);
760 hasNext = isFirstButton || !dirName.isEmpty();
761 if (hasNext) {
762 KUrlNavigatorButton *button = nullptr;
763 if (createButton) {
764 button = new KUrlNavigatorButton(buttonUrl(idx), q);
765 button->installEventFilter(q);
766 button->setForegroundRole(QPalette::WindowText);
767 q->connect(button, &KUrlNavigatorButton::urlsDroppedOnNavButton, q, [this, button](const QUrl &destination, QDropEvent *event) {
768 dropUrls(destination, event, button);
769 });
770
771 auto activatedFunc = [this](const QUrl &url, Qt::MouseButton btn, Qt::KeyboardModifiers modifiers) {
772 slotNavigatorButtonClicked(url, btn, modifiers);
773 };
774 q->connect(button, &KUrlNavigatorButton::navigatorButtonActivated, q, activatedFunc);
775
776 q->connect(button, &KUrlNavigatorButton::finishedTextResolving, q, [this]() {
777 updateButtonVisibility();
778 });
779
780 appendWidget(button);
781 } else {
782 button = m_navButtons[idx - startIndex];
783 button->setUrl(buttonUrl(idx));
784 }
785
786 if (isFirstButton) {
787 button->setText(firstButtonText());
788 }
789 button->setActive(q->isActive());
790
791 if (createButton) {
792 if (!isFirstButton) {
793 q->setTabOrder(m_navButtons.constLast(), button);
794 }
795 m_navButtons.append(button);
796 }
797
798 ++idx;
799 button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
800 }
801 } while (hasNext);
802
803 // delete buttons which are not used anymore
804 const int newButtonCount = idx - startIndex;
805 if (newButtonCount < oldButtonCount) {
806 const auto itBegin = m_navButtons.begin() + newButtonCount;
807 const auto itEnd = m_navButtons.end();
808 for (auto it = itBegin; it != itEnd; ++it) {
809 auto *navBtn = *it;
810 navBtn->hide();
811 navBtn->deleteLater();
812 }
813 m_navButtons.erase(itBegin, itEnd);
814 }
815
816 m_dropDownButton->setToolTip(xi18nc("@info:tooltip for button. 1 is path",
817 "Go to any location on the path <filename>%1</filename>",
819 .replace(QStringLiteral("///"), QStringLiteral("/")));
820 updateButtonVisibility();
821}
822
823void KUrlNavigatorPrivate::updateButtonVisibility()
824{
825 if (m_editable) {
826 return;
827 }
828
829 const int buttonsCount = m_navButtons.count();
830 if (buttonsCount == 0) {
831 m_dropDownButton->hide();
832 return;
833 }
834
835 // Subtract all widgets from the available width, that must be shown anyway
836 // Make sure to take the padding into account
837 int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
838
839 availableWidth -= m_badgeWidgetContainer->width();
840
841 if ((m_placesSelector != nullptr) && m_placesSelector->isVisible()) {
842 availableWidth -= m_placesSelector->width();
843 }
844
845 if ((m_schemes != nullptr) && m_schemes->isVisible()) {
846 availableWidth -= m_schemes->width();
847 }
848
849 availableWidth -= m_dropDownButton->width();
850
851 // Count the paddings of previous button and current button
852 availableWidth -= m_padding * 4;
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<KUrlNavigatorButtonBase *>();
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 d->m_backgroundEnabled = enabled;
1414}
1415
1417{
1418 return d->m_backgroundEnabled;
1419}
1420
1422{
1423 Q_UNUSED(event);
1424 QPainter painter(this);
1425 QStyleOptionFrame option;
1426 option.initFrom(this);
1427 option.state = QStyle::State_None;
1428
1429 if (hasFocus()) {
1430 option.palette.setColor(QPalette::Window, palette().color(QPalette::Highlight));
1431 }
1432
1433 if (d->m_backgroundEnabled) {
1434 // Draw primitive always, but change color if not editable
1435 if (!d->m_editable) {
1436 option.palette.setColor(QPalette::Base, palette().alternateBase().color());
1437 }
1438 style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this);
1439 } else {
1440 // Draw primitive only for the input field
1441 if (d->m_editable) {
1442 style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this);
1443 }
1444 }
1445}
1446
1447#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 setBackgroundEnabled(bool enabled)
Sets the background painting enabled or disabled for the buttons layout.
void requestActivation()
Activates the URL navigator (KUrlNavigator::isActive() will return true) and emits the signal KUrlNav...
bool isBackgroundEnabled() const
Returns true if the background of the buttons layout is being painted.
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_LayoutHorizontalSpacing
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 Apr 11 2025 11:51:43 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.