Changeset View
Changeset View
Standalone View
Standalone View
src/filewidgets/kurlnavigator.cpp
Show All 31 Lines | |||||
32 | 32 | | |||
33 | #include <kfileitem.h> | 33 | #include <kfileitem.h> | ||
34 | #include <kfileplacesmodel.h> | 34 | #include <kfileplacesmodel.h> | ||
35 | #include <klocalizedstring.h> | 35 | #include <klocalizedstring.h> | ||
36 | #include <kprotocolinfo.h> | 36 | #include <kprotocolinfo.h> | ||
37 | #include <kurlcombobox.h> | 37 | #include <kurlcombobox.h> | ||
38 | #include <kurlcompletion.h> | 38 | #include <kurlcompletion.h> | ||
39 | #include <kurifilter.h> | 39 | #include <kurifilter.h> | ||
40 | #include "keypressedeventfilter.h" | ||||
41 | | ||||
40 | 42 | | |||
41 | #include <QDir> | 43 | #include <QDir> | ||
42 | #include <QLinkedList> | 44 | #include <QLinkedList> | ||
43 | #include <QTimer> | 45 | #include <QTimer> | ||
44 | #include <QApplication> | 46 | #include <QApplication> | ||
45 | #include <QBoxLayout> | 47 | #include <QBoxLayout> | ||
46 | #include <QClipboard> | 48 | #include <QClipboard> | ||
47 | #include <QDropEvent> | 49 | #include <QDropEvent> | ||
48 | #include <QKeyEvent> | 50 | #include <QKeyEvent> | ||
49 | #include <QLabel> | 51 | #include <QLabel> | ||
50 | #include <QMenu> | 52 | #include <QMenu> | ||
51 | #include <QPainter> | 53 | #include <QPainter> | ||
52 | #include <QStyleOption> | 54 | #include <QStyleOption> | ||
53 | #include <qmimedatabase.h> | 55 | #include <qmimedatabase.h> | ||
56 | #include <QDebug> | ||||
ngraham: Don't want this in production code | |||||
54 | #include <QMimeData> | 57 | #include <QMimeData> | ||
58 | #include <QLineEdit> | ||||
The existing list is not 100% sorted alphabetically, but let's assume alphabetical sorting for new entries, so this should go right below #include <QLabel> ngraham: The existing list is not 100% sorted alphabetically, but let's assume alphabetical sorting for… | |||||
55 | 59 | | |||
56 | using namespace KDEPrivate; | 60 | using namespace KDEPrivate; | ||
57 | 61 | | |||
58 | struct LocationData { | 62 | struct LocationData { | ||
59 | QUrl url; | 63 | QUrl url; | ||
60 | #ifndef KIOFILEWIDGETS_NO_DEPRECATED | 64 | #ifndef KIOFILEWIDGETS_NO_DEPRECATED | ||
61 | QUrl rootUrl; // KDE5: remove after the deprecated methods have been removed | 65 | QUrl rootUrl; // KDE5: remove after the deprecated methods have been removed | ||
62 | QPoint pos; // KDE5: remove after the deprecated methods have been removed | 66 | QPoint pos; // KDE5: remove after the deprecated methods have been removed | ||
Show All 9 Lines | 73 | public: | |||
72 | void initialize(const QUrl &url); | 76 | void initialize(const QUrl &url); | ||
73 | 77 | | |||
74 | /** Applies the edited URL in m_pathBox to the URL navigator */ | 78 | /** Applies the edited URL in m_pathBox to the URL navigator */ | ||
75 | void applyUncommittedUrl(); | 79 | void applyUncommittedUrl(); | ||
76 | 80 | | |||
77 | void slotReturnPressed(); | 81 | void slotReturnPressed(); | ||
78 | void slotProtocolChanged(const QString &); | 82 | void slotProtocolChanged(const QString &); | ||
79 | void openPathSelectorMenu(); | 83 | void openPathSelectorMenu(); | ||
84 | void openHierarchyMenu(); | ||||
85 | QString parentDirectory(const QUrl ¤t) const; | ||||
80 | 86 | | |||
81 | /** | 87 | /** | ||
82 | * Appends the widget at the end of the URL navigator. It is assured | 88 | * Appends the widget at the end of the URL navigator. It is assured | ||
83 | * that the filler widget remains as last widget to fill the remaining | 89 | * that the filler widget remains as last widget to fill the remaining | ||
84 | * width. | 90 | * width. | ||
85 | */ | 91 | */ | ||
86 | void appendWidget(QWidget *widget, int stretch = 0); | 92 | void appendWidget(QWidget *widget, int stretch = 0); | ||
87 | 93 | | |||
Show All 14 Lines | |||||
102 | 108 | | |||
103 | /** | 109 | /** | ||
104 | * Is invoked when a navigator button has been clicked. Changes the URL | 110 | * Is invoked when a navigator button has been clicked. Changes the URL | ||
105 | * of the navigator if the left mouse button has been used. If the middle | 111 | * of the navigator if the left mouse button has been used. If the middle | ||
106 | * mouse button has been used, the signal tabRequested() will be emitted. | 112 | * mouse button has been used, the signal tabRequested() will be emitted. | ||
107 | */ | 113 | */ | ||
108 | void slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers); | 114 | void slotNavigatorButtonClicked(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers); | ||
109 | 115 | | |||
116 | void slotUpButtonPressed(); | ||||
117 | | ||||
110 | void openContextMenu(const QPoint &p); | 118 | void openContextMenu(const QPoint &p); | ||
111 | 119 | | |||
112 | void slotPathBoxChanged(const QString &text); | 120 | void slotPathBoxChanged(const QString &text); | ||
113 | 121 | | |||
114 | void updateContent(); | 122 | void updateContent(); | ||
115 | 123 | | |||
ngraham: Unrelated whitespace change | |||||
116 | /** | 124 | /** | ||
117 | * Updates all buttons to have one button for each part of the | 125 | * Updates all buttons to have one button for each part of the | ||
118 | * current URL. Existing buttons, which are available by m_navButtons, | 126 | * current URL. Existing buttons, which are available by m_navButtons, | ||
119 | * are reused if possible. If the URL is longer, new buttons will be | 127 | * are reused if possible. If the URL is longer, new buttons will be | ||
120 | * created, if the URL is shorter, the remaining buttons will be deleted. | 128 | * created, if the URL is shorter, the remaining buttons will be deleted. | ||
121 | * @param startIndex Start index of URL part (/), where the buttons | 129 | * @param startIndex Start index of URL part (/), where the buttons | ||
122 | * should be created for each following part. | 130 | * should be created for each following part. | ||
123 | */ | 131 | */ | ||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Line(s) | |||||
188 | KUrlNavigatorProtocolCombo *m_protocols; | 196 | KUrlNavigatorProtocolCombo *m_protocols; | ||
189 | KUrlNavigatorDropDownButton *m_dropDownButton; | 197 | KUrlNavigatorDropDownButton *m_dropDownButton; | ||
190 | QList<KUrlNavigatorButton *> m_navButtons; | 198 | QList<KUrlNavigatorButton *> m_navButtons; | ||
191 | KUrlNavigatorButtonBase *m_toggleEditableMode; | 199 | KUrlNavigatorButtonBase *m_toggleEditableMode; | ||
192 | QUrl m_homeUrl; | 200 | QUrl m_homeUrl; | ||
193 | QStringList m_customProtocols; | 201 | QStringList m_customProtocols; | ||
194 | QWidget *m_dropWidget; | 202 | QWidget *m_dropWidget; | ||
195 | KUrlNavigator * const q; | 203 | KUrlNavigator * const q; | ||
204 | KeyPressedEventFilter *m_keyPressedEventFilter; | ||||
196 | }; | 205 | }; | ||
197 | 206 | | |||
198 | KUrlNavigator::Private::Private(KUrlNavigator *q, KFilePlacesModel *placesModel) : | 207 | KUrlNavigator::Private::Private(KUrlNavigator *q, KFilePlacesModel *placesModel) : | ||
199 | m_editable(false), | 208 | m_editable(false), | ||
200 | m_active(true), | 209 | m_active(true), | ||
201 | m_showPlacesSelector(placesModel != nullptr), | 210 | m_showPlacesSelector(placesModel != nullptr), | ||
202 | m_showFullPath(false), | 211 | m_showFullPath(false), | ||
203 | m_historyIndex(0), | 212 | m_historyIndex(0), | ||
204 | m_layout(new QHBoxLayout), | 213 | m_layout(new QHBoxLayout), | ||
205 | m_placesSelector(nullptr), | 214 | m_placesSelector(nullptr), | ||
206 | m_pathBox(nullptr), | 215 | m_pathBox(nullptr), | ||
207 | m_protocols(nullptr), | 216 | m_protocols(nullptr), | ||
208 | m_dropDownButton(nullptr), | 217 | m_dropDownButton(nullptr), | ||
209 | m_navButtons(), | 218 | m_navButtons(), | ||
210 | m_toggleEditableMode(nullptr), | 219 | m_toggleEditableMode(nullptr), | ||
211 | m_homeUrl(), | 220 | m_homeUrl(), | ||
212 | m_customProtocols(QStringList()), | 221 | m_customProtocols(QStringList()), | ||
213 | m_dropWidget(nullptr), | 222 | m_dropWidget(nullptr), | ||
214 | q(q) | 223 | q(q), | ||
224 | m_keyPressedEventFilter(nullptr) | ||||
215 | { | 225 | { | ||
216 | m_layout->setSpacing(0); | 226 | m_layout->setSpacing(0); | ||
217 | m_layout->setContentsMargins(0, 0, 0, 0); | 227 | m_layout->setContentsMargins(0, 0, 0, 0); | ||
218 | 228 | | |||
219 | // initialize the places selector | 229 | // initialize the places selector | ||
220 | q->setAutoFillBackground(false); | 230 | q->setAutoFillBackground(false); | ||
221 | 231 | | |||
222 | if (placesModel != nullptr) { | 232 | if (placesModel != nullptr) { | ||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Line(s) | |||||
276 | m_layout->addWidget(m_protocols); | 286 | m_layout->addWidget(m_protocols); | ||
277 | m_layout->addWidget(m_dropDownButton); | 287 | m_layout->addWidget(m_dropDownButton); | ||
278 | m_layout->addWidget(m_pathBox, 1); | 288 | m_layout->addWidget(m_pathBox, 1); | ||
279 | m_layout->addWidget(m_toggleEditableMode); | 289 | m_layout->addWidget(m_toggleEditableMode); | ||
280 | 290 | | |||
281 | q->setContextMenuPolicy(Qt::CustomContextMenu); | 291 | q->setContextMenuPolicy(Qt::CustomContextMenu); | ||
282 | connect(q, SIGNAL(customContextMenuRequested(QPoint)), | 292 | connect(q, SIGNAL(customContextMenuRequested(QPoint)), | ||
283 | q, SLOT(openContextMenu(QPoint))); | 293 | q, SLOT(openContextMenu(QPoint))); | ||
294 | m_keyPressedEventFilter = new KeyPressedEventFilter(); | ||||
295 | connect(m_keyPressedEventFilter, SIGNAL(keyUpPressed()), q, SLOT(slotUpButtonPressed())); | ||||
296 | connect(m_keyPressedEventFilter, SIGNAL(keyDownPressed()), | ||||
297 | q, SLOT(openHierarchyMenu())); | ||||
284 | } | 298 | } | ||
285 | 299 | | |||
286 | void KUrlNavigator::Private::initialize(const QUrl &url) | 300 | void KUrlNavigator::Private::initialize(const QUrl &url) | ||
287 | { | 301 | { | ||
288 | LocationData data; | 302 | LocationData data; | ||
289 | data.url = url.adjusted(QUrl::NormalizePathSegments); | 303 | data.url = url.adjusted(QUrl::NormalizePathSegments); | ||
290 | m_history.prepend(data); | 304 | m_history.prepend(data); | ||
291 | 305 | | |||
Show All 30 Lines | 323 | { | |||
322 | 336 | | |||
323 | q->setLocationUrl(typedUrl); | 337 | q->setLocationUrl(typedUrl); | ||
324 | // The URL might have been adjusted by KUrlNavigator::setUrl(), hence | 338 | // The URL might have been adjusted by KUrlNavigator::setUrl(), hence | ||
325 | // synchronize the result in the path box. | 339 | // synchronize the result in the path box. | ||
326 | const QUrl currentUrl = q->locationUrl(); | 340 | const QUrl currentUrl = q->locationUrl(); | ||
327 | m_pathBox->setUrl(currentUrl); | 341 | m_pathBox->setUrl(currentUrl); | ||
328 | } | 342 | } | ||
329 | 343 | | |||
344 | void KUrlNavigator::Private::slotUpButtonPressed() | ||||
345 | { | ||||
346 | emit q->goUp(); | ||||
347 | /* | ||||
348 | * Ugly hack to get focus back | ||||
349 | */ | ||||
350 | slotToggleEditableButtonPressed(); | ||||
This works in Dolphin, but causes the navigator to switch back to breadcrumbs mode in Gwenview. I think we need to instead fix whatever bug is causing the desire for thus ugly hack. :) ngraham: This works in Dolphin, but causes the navigator to switch back to breadcrumbs mode in Gwenview. | |||||
351 | QTimer::singleShot(10, q->editor()->lineEdit(), SLOT(setFocus())); | ||||
352 | } | ||||
353 | | ||||
330 | void KUrlNavigator::Private::slotReturnPressed() | 354 | void KUrlNavigator::Private::slotReturnPressed() | ||
331 | { | 355 | { | ||
332 | applyUncommittedUrl(); | 356 | applyUncommittedUrl(); | ||
333 | 357 | | |||
334 | emit q->returnPressed(); | 358 | emit q->returnPressed(); | ||
335 | 359 | | |||
336 | if (QApplication::keyboardModifiers() & Qt::ControlModifier) { | 360 | if (QApplication::keyboardModifiers() & Qt::ControlModifier) { | ||
337 | // Pressing Ctrl+Return automatically switches back to the breadcrumb mode. | 361 | // Pressing Ctrl+Return automatically switches back to the breadcrumb mode. | ||
Show All 15 Lines | 376 | } else { | |||
353 | // With no authority set we'll get e.g. "ftp:" instead of "ftp://". | 377 | // With no authority set we'll get e.g. "ftp:" instead of "ftp://". | ||
354 | // We want the latter, so let's set an empty authority. | 378 | // We want the latter, so let's set an empty authority. | ||
355 | url.setAuthority(QString()); | 379 | url.setAuthority(QString()); | ||
356 | } | 380 | } | ||
357 | 381 | | |||
358 | m_pathBox->setEditUrl(url); | 382 | m_pathBox->setEditUrl(url); | ||
359 | } | 383 | } | ||
360 | 384 | | |||
385 | QString KUrlNavigator::Private::parentDirectory(const QUrl ¤t) const | ||||
386 | { | ||||
387 | const QString currentPath = current.path(); | ||||
388 | const int slash = currentPath.lastIndexOf(QLatin1Char('/')); | ||||
389 | if (slash == -1) | ||||
ngraham: Coding style: don't omit braces for single-line conditionals | |||||
390 | return QString(); | ||||
391 | else if (slash == 0) | ||||
392 | return QString(QLatin1Char('/')); | ||||
393 | else if (slash == currentPath.length()-1) | ||||
394 | return parentDirectory(QUrl(currentPath.left(slash))); | ||||
395 | return currentPath.left(slash); | ||||
396 | } | ||||
397 | | ||||
398 | void KUrlNavigator::Private::openHierarchyMenu() { | ||||
399 | QUrl currentDirectory = q->locationUrl(); | ||||
400 | QList<QUrl>* hierarchyList = new QList<QUrl>(); | ||||
401 | QUrl levelUp(QStringLiteral("/../")); | ||||
402 | bool hasParent = true; | ||||
ngraham: Use `toLocalFile()` instead of `path()`, otherwise it breaks on Windows | |||||
403 | while (hasParent) { | ||||
404 | hierarchyList->prepend(currentDirectory); | ||||
ngraham: Don't want this in production code | |||||
405 | hasParent = (currentDirectory != currentDirectory.resolved(levelUp)); | ||||
406 | currentDirectory = QUrl(parentDirectory(currentDirectory)); | ||||
QUrl(currentDirectory.resolved(levelUp)) for some reason results in missed 1st level directory, eg list for /home/neko would be: /home/neko / Is this a proper approach or should I do it somehow differently? krutovmikhail: QUrl(currentDirectory.resolved(levelUp)) for some reason results in missed 1st level directory… | |||||
407 | } | ||||
408 | QMenu *dropdown = new QMenu(q); | ||||
krutovmikhail: Is QMenu a proper widget for dropdown list here? | |||||
409 | auto itBegin = hierarchyList->begin(); | ||||
410 | const auto itEnd = hierarchyList->end(); | ||||
411 | while (itBegin != itEnd) { | ||||
412 | QAction* action = new QAction(QIcon::fromTheme(QStringLiteral("folder")),itBegin->path(), dropdown); | ||||
413 | action->setData(QVariant(itBegin->toString())); | ||||
414 | dropdown->addAction(action); | ||||
415 | ++itBegin; | ||||
416 | } | ||||
417 | auto lineEdit = q->editor()->lineEdit(); | ||||
418 | dropdown->popup(lineEdit->mapToGlobal(QPoint(0, lineEdit->height()))); | ||||
419 | } | ||||
420 | | ||||
361 | void KUrlNavigator::Private::openPathSelectorMenu() | 421 | void KUrlNavigator::Private::openPathSelectorMenu() | ||
362 | { | 422 | { | ||
363 | if (m_navButtons.count() <= 0) { | 423 | if (m_navButtons.count() <= 0) { | ||
364 | return; | 424 | return; | ||
365 | } | 425 | } | ||
366 | 426 | | |||
367 | const QUrl firstVisibleUrl = m_navButtons.first()->url(); | 427 | const QUrl firstVisibleUrl = m_navButtons.first()->url(); | ||
368 | 428 | | |||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | 475 | if (popup) { | |||
416 | popup->deleteLater(); | 476 | popup->deleteLater(); | ||
417 | } | 477 | } | ||
418 | } | 478 | } | ||
419 | 479 | | |||
420 | void KUrlNavigator::Private::slotToggleEditableButtonPressed() | 480 | void KUrlNavigator::Private::slotToggleEditableButtonPressed() | ||
421 | { | 481 | { | ||
422 | if (m_editable) { | 482 | if (m_editable) { | ||
423 | applyUncommittedUrl(); | 483 | applyUncommittedUrl(); | ||
484 | m_pathBox->removeEventFilter(m_keyPressedEventFilter); | ||||
485 | } else { | ||||
486 | m_pathBox->installEventFilter(m_keyPressedEventFilter); | ||||
ngraham: This feels like a hack | |||||
424 | } | 487 | } | ||
425 | 488 | | |||
ngraham: Unrelated whitespace change | |||||
426 | switchView(); | 489 | switchView(); | ||
427 | } | 490 | } | ||
428 | 491 | | |||
429 | void KUrlNavigator::Private::switchView() | 492 | void KUrlNavigator::Private::switchView() | ||
430 | { | 493 | { | ||
431 | m_toggleEditableMode->setFocus(); | 494 | m_toggleEditableMode->setFocus(); | ||
432 | m_editable = !m_editable; | 495 | m_editable = !m_editable; | ||
433 | m_toggleEditableMode->setChecked(m_editable); | 496 | m_toggleEditableMode->setChecked(m_editable); | ||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Line(s) | 611 | { | |||
553 | 616 | | |||
554 | if (m_editable) { | 617 | if (m_editable) { | ||
555 | m_protocols->hide(); | 618 | m_protocols->hide(); | ||
556 | m_dropDownButton->hide(); | 619 | m_dropDownButton->hide(); | ||
557 | 620 | | |||
558 | deleteButtons(); | 621 | deleteButtons(); | ||
559 | m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); | 622 | m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); | ||
560 | q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); | 623 | q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); | ||
561 | 624 | | |||
ngraham: Unrelated whitespace change | |||||
562 | m_pathBox->show(); | 625 | m_pathBox->show(); | ||
563 | m_pathBox->setUrl(currentUrl); | 626 | m_pathBox->setUrl(currentUrl); | ||
564 | } else { | 627 | } else { | ||
565 | m_pathBox->hide(); | 628 | m_pathBox->hide(); | ||
566 | 629 | | |||
567 | m_protocols->hide(); | 630 | m_protocols->hide(); | ||
568 | 631 | | |||
569 | m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); | 632 | m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); | ||
▲ Show 20 Lines • Show All 660 Lines • ▼ Show 20 Line(s) | 1292 | foreach (KUrlNavigatorButton *button, d->m_navButtons) { | |||
1230 | button->setShowMnemonic(false); | 1293 | button->setShowMnemonic(false); | ||
1231 | } | 1294 | } | ||
1232 | break; | 1295 | break; | ||
1233 | 1296 | | |||
1234 | default: | 1297 | default: | ||
1235 | break; | 1298 | break; | ||
1236 | } | 1299 | } | ||
1237 | 1300 | | |||
1238 | return QWidget::eventFilter(watched, event); | 1301 | return QWidget::eventFilter(watched, event); | ||
ngraham: You could remove the `true` from these | |||||
1239 | } | 1302 | } | ||
1240 | 1303 | | |||
1241 | int KUrlNavigator::historySize() const | 1304 | int KUrlNavigator::historySize() const | ||
1242 | { | 1305 | { | ||
1243 | return d->m_history.count(); | 1306 | return d->m_history.count(); | ||
1244 | } | 1307 | } | ||
1245 | 1308 | | |||
1246 | int KUrlNavigator::historyIndex() const | 1309 | int KUrlNavigator::historyIndex() const | ||
▲ Show 20 Lines • Show All 84 Lines • Show Last 20 Lines |
Don't want this in production code