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 #ifndef KURLNAVIGATOR_H
11 #define KURLNAVIGATOR_H
12 
13 #include "kiofilewidgets_export.h"
14 
15 #include <QByteArray>
16 #include <QUrl>
17 #include <QWidget>
18 
19 #include <memory>
20 
21 class QMouseEvent;
22 
23 class KFilePlacesModel;
24 class KUrlComboBox;
25 
26 class KUrlNavigatorPrivate;
27 
28 /**
29  * @class KUrlNavigator kurlnavigator.h <KUrlNavigator>
30  *
31  * @brief Widget that allows to navigate through the paths of an URL.
32  *
33  * The URL navigator offers two modes:
34  * - Editable:     The URL of the location is editable inside an editor.
35  *                 By pressing RETURN the URL will get activated.
36  * - Non editable ("breadcrumb view"): The URL of the location is represented by
37  *                 a number of buttons, where each button represents a path
38  *                 of the URL. By clicking on a button the path will get
39  *                 activated. This mode also supports drag and drop of items.
40  *
41  * The mode can be changed by clicking on the empty area of the URL navigator.
42  * It is recommended that the application remembers the setting
43  * or allows to configure the default mode (see KUrlNavigator::setUrlEditable()).
44  *
45  * The URL navigator remembers the URL history during navigation and allows to go
46  * back and forward within this history.
47  *
48  * In the non editable mode ("breadcrumb view") it can be configured whether
49  * the full path should be shown. It is recommended that the application
50  * remembers the setting or allows to configure the default mode (see
51  * KUrlNavigator::setShowFullPath()).
52  *
53  * The typical usage of the KUrlNavigator is:
54  * - Create an instance providing a places model and an URL.
55  * - Create an instance of QAbstractItemView which shows the content of the URL
56  *   given by the URL navigator.
57  * - Connect to the signal KUrlNavigator::urlChanged() and synchronize the content of
58  *   QAbstractItemView with the URL given by the URL navigator.
59  *
60  * It is recommended, that the application remembers the state of the QAbstractItemView
61  * when the URL has been changed. This allows to restore the view state when going back in history.
62  * KUrlNavigator offers support for remembering the view state:
63  * - The signal urlAboutToBeChanged() will be emitted before the URL change takes places.
64  *   This allows the application to store the view state by KUrlNavigator::saveLocationState().
65  * - The signal urlChanged() will be emitted after the URL change took place. This allows
66  *   the application to restore the view state by getting the values from
67  *   KUrlNavigator::locationState().
68  */
69 class KIOFILEWIDGETS_EXPORT KUrlNavigator : public QWidget
70 {
71     Q_OBJECT
72 
73 public:
74     /** @since 4.5 */
75     KUrlNavigator(QWidget *parent = nullptr);
76 
77     /**
78      * @param placesModel    Model for the places which are selectable inside a
79      *                       menu. A place can be a bookmark or a device. If it is 0,
80      *                       no places selector is displayed.
81      * @param url            URL which is used for the navigation or editing.
82      * @param parent         Parent widget.
83      */
84     KUrlNavigator(KFilePlacesModel *placesModel, const QUrl &url, QWidget *parent);
85     ~KUrlNavigator() override;
86 
87     /**
88      * @return URL of the location given by the \a historyIndex. If \a historyIndex
89      *         is smaller than 0, the URL of the current location is returned.
90      * @since  4.5
91      */
92     QUrl locationUrl(int historyIndex = -1) const;
93 
94     /**
95      * Saves the location state described by \a state for the current location. It is recommended
96      * that at least the scroll position of a view is remembered and restored when traversing
97      * through the history. Saving the location state should be done when the signal
98      * KUrlNavigator::urlAboutToBeChanged() has been emitted. Restoring the location state (see
99      * KUrlNavigator::locationState()) should be done when the signal KUrlNavigator::urlChanged()
100      * has been emitted.
101      *
102      * Example:
103      * \code
104      * QByteArray state;
105      * QDataStream data(&state, QIODevice::WriteOnly);
106      * data << QPoint(x, y);
107      * data << ...;
108      * ...
109      * urlNavigator->saveLocationState(state);
110      * \endcode
111      *
112      * @since 4.5
113      */
114     void saveLocationState(const QByteArray &state);
115 
116     /**
117      * @return Location state given by \a historyIndex. If \a historyIndex
118      *         is smaller than 0, the state of the current location is returned.
119      * @see    KUrlNavigator::saveLocationState()
120      * @since  4.5
121      */
122     QByteArray locationState(int historyIndex = -1) const;
123 
124     /**
125      * Goes back one step in the URL history. The signals
126      * KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged() and
127      * KUrlNavigator::historyChanged() are emitted if true is returned. False is returned
128      * if the beginning of the history has already been reached and hence going back was
129      * not possible. The history index (see KUrlNavigator::historyIndex()) is
130      * increased by one if the operation was successful.
131      */
132     bool goBack();
133 
134     /**
135      * Goes forward one step in the URL history. The signals
136      * KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged() and
137      * KUrlNavigator::historyChanged() are emitted if true is returned. False is returned
138      * if the end of the history has already been reached and hence going forward
139      * was not possible. The history index (see KUrlNavigator::historyIndex()) is
140      * decreased by one if the operation was successful.
141      */
142     bool goForward();
143 
144     /**
145      * Goes up one step of the URL path and remembers the old path
146      * in the history. The signals KUrlNavigator::urlAboutToBeChanged(),
147      * KUrlNavigator::urlChanged() and KUrlNavigator::historyChanged() are
148      * emitted if true is returned. False is returned if going up was not
149      * possible as the root has been reached.
150      */
151     bool goUp();
152 
153     /**
154      * Goes to the home URL and remembers the old URL in the history.
155      * The signals KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged()
156      * and KUrlNavigator::historyChanged() are emitted.
157      *
158      * @see KUrlNavigator::setHomeUrl()
159      */
160     // KDE5: Remove the home-property. It is sufficient to invoke
161     // KUrlNavigator::setLocationUrl(homeUrl) on application-side.
162     void goHome();
163 
164     /**
165      * Sets the home URL used by KUrlNavigator::goHome(). If no
166      * home URL is set, the default home path of the user is used.
167      * @since 4.5
168      */
169     // KDE5: Remove the home-property. It is sufficient to invoke
170     // KUrlNavigator::setLocationUrl(homeUrl) on application-side.
171     void setHomeUrl(const QUrl &url);
172 
173     QUrl homeUrl() const;
174 
175     /**
176      * Allows to edit the URL of the navigation bar if \a editable
177      * is true, and sets the focus accordingly.
178      * If \a editable is false, each part of
179      * the URL is presented by a button for a fast navigation ("breadcrumb view").
180      */
181     void setUrlEditable(bool editable);
182 
183     /**
184      * @return True, if the URL is editable within a line editor.
185      *         If false is returned, each part of the URL is presented by a button
186      *         for fast navigation ("breadcrumb view").
187      */
188     bool isUrlEditable() const;
189 
190     /**
191      * Shows the full path of the URL even if a place represents a part of the URL.
192      * Assuming that a place called "Pictures" uses the URL /home/user/Pictures.
193      * An URL like /home/user/Pictures/2008 is shown as [Pictures] > [2008]
194      * in the breadcrumb view, if showing the full path is turned off. If
195      * showing the full path is turned on, the URL is shown
196      * as [/] > [home] > [Pictures] > [2008].
197      * @since 4.2
198      */
199     void setShowFullPath(bool show);
200 
201     /**
202      * @return True, if the full path of the URL should be shown in the breadcrumb view.
203      * @since  4.2
204      */
205     bool showFullPath() const;
206 
207     /**
208      * Set the URL navigator to the active mode, if \a active
209      * is true. The active mode is default. The inactive mode only differs
210      * visually from the active mode, no change of the behavior is given.
211      *
212      * Using the URL navigator in the inactive mode is useful when having split views,
213      * where the inactive view is indicated by an inactive URL
214      * navigator visually.
215      */
216     void setActive(bool active);
217 
218     /**
219      * @return True, if the URL navigator is in the active mode.
220      * @see    KUrlNavigator::setActive()
221      */
222     bool isActive() const;
223 
224     /**
225      * Sets the places selector visible, if \a visible is true.
226      * The places selector allows to select the places provided
227      * by the places model passed in the constructor. Per default
228      * the places selector is visible.
229      */
230     void setPlacesSelectorVisible(bool visible);
231 
232     /** @return True, if the places selector is visible. */
233     bool isPlacesSelectorVisible() const;
234 
235     /**
236      * @return The currently entered, but not accepted URL.
237      *         It is possible that the returned URL is not valid.
238      */
239     QUrl uncommittedUrl() const;
240 
241     /**
242      * @return The amount of locations in the history. The data for each
243      *         location can be retrieved by KUrlNavigator::locationUrl() and
244      *         KUrlNavigator::locationState().
245      */
246     int historySize() const;
247 
248     /**
249      * @return  The history index of the current location, where
250      *          0 <= history index < KUrlNavigator::historySize(). 0 is the most
251      *          recent history entry.
252      */
253     int historyIndex() const;
254 
255     /**
256      * @return The used editor when the navigator is in the edit mode
257      * @see    KUrlNavigator::setUrlEditable()
258      */
259     KUrlComboBox *editor() const;
260 
261     /**
262      * If an application supports only some special protocols, they can be set
263      * with \a protocols .
264      */
265     // TODO KF6 rename to setSupportedSchemes to match KDirOperator and KFileWidget
266     void setCustomProtocols(const QStringList &protocols);
267 
268     /**
269      * @return The custom protocols if they are set, QStringList() otherwise.
270      */
271     QStringList customProtocols() const;
272 
273     /**
274      * The child widget that received the QDropEvent when dropping on the URL
275      * navigator. You can pass this widget to KJobWidgets::setWindow()
276      * if you need to show a drop menu with KIO::drop().
277      * @return Child widget that has received the last drop event, or nullptr if
278      *         nothing has been dropped yet on the URL navigator.
279      * @since 5.37
280      * @see KIO::drop()
281      */
282     QWidget *dropWidget() const;
283 
284     /**
285      * Sets whether to show hidden folders in the subdirectories popup.
286      * @since 5.87
287      */
288     void setShowHiddenFolders(bool showHiddenFolders);
289 
290     /**
291      * Returns whether to show hidden folders in the subdirectories popup.
292      * @since 5.87
293      */
294     bool showHiddenFolders() const;
295 
296     /**
297      * Sets whether to sort hidden folders in the subdirectories popup last.
298      * @since 5.87
299      */
300     void setSortHiddenFoldersLast(bool sortHiddenFoldersLast);
301 
302     /**
303      * Returns whether to sort hidden folders in the subdirectories popup last.
304      * @since 5.87
305      */
306     bool sortHiddenFoldersLast() const;
307 
308 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
309     /**
310      * @return     The current URL of the location.
311      * @deprecated Since 4.5, use KUrlNavigator::locationUrl() instead.
312      */
313     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::locationUrl(int)")
314     const QUrl &url() const;
315 #endif
316 
317 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
318     /**
319      * @return The portion of the current URL up to the path part given
320      * by \a index. Assuming that the current URL is /home/peter/Documents/Music,
321      * then the following URLs are returned for an index:
322      * - index <= 0: /home
323      * - index is 1: /home/peter
324      * - index is 2: /home/peter/Documents
325      * - index >= 3: /home/peter/Documents/Music
326      * @deprecated Since 4.5. It should not be necessary for a client of KUrlNavigator to query this information.
327      */
328     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Do not use")
329     QUrl url(int index) const;
330 #endif
331 
332 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
333     /**
334      * @return URL for the history element with the index \a historyIndex.
335      *         The history index 0 represents the most recent URL.
336      * @since 4.3
337      * @deprecated Since 4.5, use KUrlNavigator::locationUrl(historyIndex) instead.
338      */
339     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::locationUrl(int)")
340     QUrl historyUrl(int historyIndex) const;
341 #endif
342 
343 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
344     /**
345      * @return The saved root URL for the current URL (see KUrlNavigator::saveRootUrl()).
346      * @deprecated Since 4.5, use KUrlNavigator::locationState() instead.
347      */
348     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::locationState(int)")
349     const QUrl &savedRootUrl() const;
350 #endif
351 
352 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
353     /**
354      * @return The saved contents position of the upper left corner
355      *         for the current URL.
356      * @deprecated Since 4.5, use KUrlNavigator::locationState() instead.
357      */
358     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::locationState(int)")
359     QPoint savedPosition() const;
360 #endif
361 
362 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
363     /** @deprecated Since 4.5, use setHomeUrl(const QUrl& url) instead. */
364     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::setHomeUrl(const QUrl&)")
365     void setHomeUrl(const QString &homeUrl);
366 #endif
367 
368 public Q_SLOTS:
369     /**
370      * Sets the location to \a url. The old URL is added to the history.
371      * The signals KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged()
372      * and KUrlNavigator::historyChanged() are emitted. Use
373      * KUrlNavigator::locationUrl() to read the location.
374      * @since 4.5
375      */
376     void setLocationUrl(const QUrl &url);
377 
378     /**
379      * Activates the URL navigator (KUrlNavigator::isActive() will return true)
380      * and emits the signal KUrlNavigator::activated().
381      * @see KUrlNavigator::setActive()
382      */
383     void requestActivation();
384 
385 #if !defined(K_DOXYGEN)
386     // KDE5: Remove and listen for focus-signal instead
387     void setFocus();
388 #endif
389 
390 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
391     /**
392      * Sets the location to \a url.
393      * @deprecated Since 4.5, use KUrlNavigator::setLocationUrl(url).
394      */
395     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::setLocationUrl(const QUrl&))")
396     void setUrl(const QUrl &url);
397 #endif
398 
399 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
400     /**
401      * Saves the used root URL of the content for the current history element.
402      * @deprecated Since 4.5, use KUrlNavigator::saveLocationState() instead.
403      */
404     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::saveLocationState(const QByteArray &)")
405     void saveRootUrl(const QUrl &url);
406 #endif
407 
408 #if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(4, 5)
409     /**
410      * Saves the coordinates of the contents for the current history element.
411      * @deprecated Since 4.5, use KUrlNavigator::saveLocationState() instead.
412      */
413     KIOFILEWIDGETS_DEPRECATED_VERSION(4, 5, "Use KUrlNavigator::saveLocationState(const QByteArray &)")
414     void savePosition(int x, int y);
415 #endif
416 
417 Q_SIGNALS:
418     /**
419      * Is emitted, if the URL navigator has been activated by
420      * an user interaction
421      * @see KUrlNavigator::setActive()
422      */
423     void activated();
424 
425     /**
426      * Is emitted, if the location URL has been changed e. g. by
427      * the user.
428      * @see KUrlNavigator::setUrl()
429      */
430     void urlChanged(const QUrl &url);
431 
432     /**
433      * Is emitted, before the location URL is going to be changed to \a newUrl.
434      * The signal KUrlNavigator::urlChanged() will be emitted after the change
435      * has been done. Connecting to this signal is useful to save the state
436      * of a view with KUrlNavigator::saveLocationState().
437      * @since 4.5
438      */
439     void urlAboutToBeChanged(const QUrl &newUrl);
440 
441     /**
442      * Is emitted, if the editable state for the URL has been changed
443      * (see KUrlNavigator::setUrlEditable()).
444      */
445     void editableStateChanged(bool editable);
446 
447     /**
448      * Is emitted, if the history has been changed. Usually
449      * the history is changed if a new URL has been selected.
450      */
451     void historyChanged();
452 
453     /**
454      * Is emitted if a dropping has been done above the destination
455      * \a destination. The receiver must accept the drop event if
456      * the dropped data can be handled.
457      * @since 4.2
458      */
459     void urlsDropped(const QUrl &destination, QDropEvent *event);
460 
461     /**
462      * This signal is emitted when the Return or Enter key is pressed.
463      */
464     void returnPressed();
465 
466     /**
467      * Is emitted if the URL \a url should be opened in a new inactive tab because
468      * the user clicked on a breadcrumb with the middle mouse button or
469      * left-clicked with the ctrl modifier pressed.
470      * @since 4.5
471      */
472     void tabRequested(const QUrl &url);
473 
474     /**
475      * Is emitted if the URL \a url should be opened in a new active tab because
476      * the user clicked on a breadcrumb with the middle mouse button with
477      * the shift modifier pressed or left-clicked with both the ctrl and shift
478      * modifiers pressed.
479      * @since 5.89
480      */
481     void activeTabRequested(const QUrl &url);
482 
483     /**
484      * Is emitted if the URL \a url should be opened in a new window because
485      * the user left-clicked on a breadcrumb with the shift modifier pressed.
486      * @since 5.89
487      */
488     void newWindowRequested(const QUrl &url);
489 
490     /**
491      * When the URL is changed and the new URL (e.g. /home/user1/)
492      * is a parent of the previous URL (e.g. /home/user1/data/stuff),
493      * then this signal is emitted and \a url is set to the child
494      * directory of the new URL which is an ancestor of the old URL
495      * (in the example paths this would be /home/user1/data/).
496      * This signal allows file managers to pre-select the directory
497      * that the user is navigating up from.
498      * @since 5.37.0
499      */
500     void urlSelectionRequested(const QUrl &url);
501 
502 protected:
503 #if !defined(K_DOXYGEN)
504     /**
505      * If the Escape key is pressed, the navigation bar should switch
506      * to the breadcrumb view.
507      * @see QWidget::keyPressEvent()
508      */
509     void keyPressEvent(QKeyEvent *event) override;
510 
511     /**
512      * Reimplemented for internal purposes.
513      */
514     void keyReleaseEvent(QKeyEvent *event) override;
515 
516     /**
517      * Paste the clipboard content as URL, if the middle mouse
518      * button has been clicked.
519      * @see QWidget::mouseReleaseEvent()
520      */
521     void mouseReleaseEvent(QMouseEvent *event) override;
522 
523     /**
524      * Reimplemented to activate on middle mousse button click
525      */
526     void mousePressEvent(QMouseEvent *event) override;
527 
528     void resizeEvent(QResizeEvent *event) override;
529 
530     void wheelEvent(QWheelEvent *event) override;
531 
532     bool eventFilter(QObject *watched, QEvent *event) override;
533 #endif
534 
535 private:
536     friend class KUrlNavigatorPrivate;
537     std::unique_ptr<KUrlNavigatorPrivate> const d;
538 
539     Q_DISABLE_COPY(KUrlNavigator)
540 };
541 
542 #endif
543