1 /*****************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * Copyright (C) 2006 by Aaron J. Seigo <aseigo@kde.org> *
4 * Copyright (C) 2007 by Kevin Ottens <ervin@kde.org> *
5 * Copyright (C) 2007 by Urs Wolfer <uwolfer @ kde.org> *
7 * This library is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU Library General Public *
9 * License version 2 as published by the Free Software Foundation. *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
16 * You should have received a copy of the GNU Library General Public License *
17 * along with this library; see the file COPYING.LIB. If not, write to *
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
19 * Boston, MA 02110-1301, USA. *
20 *****************************************************************************/
22 #include "kurlnavigator.h"
24 #include "kfileplacesselector_p.h"
25 #include "kprotocolcombo_p.h"
26 #include "kurldropdownbutton_p.h"
27 #include "kurlnavigatorbutton_p.h"
28 #include "kurltogglebutton_p.h"
30 #include <kfileitem.h>
31 #include <kfileplacesmodel.h>
32 #include <kglobalsettings.h>
34 #include <klineedit.h>
37 #include <kprotocolinfo.h>
38 #include <kurlcombobox.h>
39 #include <kurlcompletion.h>
41 #include <QtCore/QDir>
42 #include <QtCore/QLinkedList>
43 #include <QtCore/QTimer>
44 #include <QtGui/QApplication>
45 #include <QtGui/QClipboard>
46 #include <QtGui/QDropEvent>
47 #include <QtGui/QKeyEvent>
48 #include <QtGui/QBoxLayout>
49 #include <QtGui/QLabel>
54 * @brief Represents the history element of an URL.
56 * A history element contains the URL and
57 * the x- and y-position of the content.
63 HistoryElem(const KUrl
& url
);
64 ~HistoryElem(); // non virtual
66 const KUrl
& url() const;
68 void setRootUrl(const KUrl
& url
);
69 const KUrl
& rootUrl() const;
71 void setContentsX(int x
);
72 int contentsX() const;
74 void setContentsY(int y
);
75 int contentsY() const;
84 HistoryElem::HistoryElem() :
92 HistoryElem::HistoryElem(const KUrl
& url
) :
100 HistoryElem::~HistoryElem()
104 inline const KUrl
& HistoryElem::url() const
109 inline void HistoryElem::setRootUrl(const KUrl
& url
)
114 inline const KUrl
& HistoryElem::rootUrl() const
119 inline void HistoryElem::setContentsX(int x
)
124 inline int HistoryElem::contentsX() const
129 inline void HistoryElem::setContentsY(int y
)
134 inline int HistoryElem::contentsY() const
141 class KUrlNavigator::Private
144 Private(KUrlNavigator
* q
, KFilePlacesModel
* placesModel
);
146 void slotReturnPressed();
147 void slotRemoteHostActivated();
148 void slotProtocolChanged(const QString
&);
149 void openPathSelectorMenu();
152 * Appends the widget at the end of the URL navigator. It is assured
153 * that the filler widget remains as last widget to fill the remaining
156 void appendWidget(QWidget
* widget
, int stretch
= 0);
159 * Switches the navigation bar between the breadcrumb view and the
160 * traditional view (see setUrlEditable()) and is connected to the clicked signal
161 * of the navigation bar button.
165 /** Emits the signal urlsDropped(). */
166 void dropUrls(const KUrl
& destination
, QDropEvent
* event
);
168 void updateContent();
171 * Updates all buttons to have one button for each part of the
172 * path \a path. Existing buttons, which are available by m_navButtons,
173 * are reused if possible. If the path is longer, new buttons will be
174 * created, if the path is shorter, the remaining buttons will be deleted.
175 * @param startIndex Start index of path part (/), where the buttons
176 * should be created for each following part.
178 void updateButtons(const QString
& path
, int startIndex
);
181 * Updates the visibility state of all buttons describing the URL. If the
182 * width of the URL navigator is too small, the buttons representing the upper
183 * paths of the URL will be hidden and moved to a drop down menu.
185 void updateButtonVisibility();
187 void switchToBreadcrumbMode();
190 * Deletes all URL navigator buttons. m_navButtons is
191 * empty after this operation.
193 void deleteButtons();
196 * Retrieves the place path for the current path.
197 * E. g. for the path "fish://root@192.168.0.2/var/lib" the string
198 * "fish://root@192.168.0.2" will be returned, which leads to the
199 * navigation indication 'Custom Path > var > lib". For e. g.
200 * "settings:///System/" the path "settings://" will be returned.
202 QString
retrievePlacePath() const;
205 * Returns true, if the MIME type of the path represents a
206 * compressed file like TAR or ZIP.
208 bool isCompressedPath(const KUrl
& path
) const;
210 void removeTrailingSlash(QString
& url
) const;
213 * Returns a KUrl for the typed text \a typedUrl.
214 * '\' is replaced by '/', whitespaces at the begin
215 * and end of the typed text get removed.
217 KUrl
adjustedUrl(const QString
& typedUrl
) const;
221 bool m_showPlacesSelector
: 1;
222 bool m_showFullPath
: 1;
225 QHBoxLayout
* m_layout
;
227 QList
<HistoryElem
> m_history
;
228 KFilePlacesSelector
* m_placesSelector
;
229 KUrlComboBox
* m_pathBox
;
230 KProtocolCombo
* m_protocols
;
232 KUrlDropDownButton
* m_dropDownButton
;
233 QLinkedList
<KUrlNavigatorButton
*> m_navButtons
;
234 KUrlButton
* m_toggleEditableMode
;
236 QStringList m_customProtocols
;
241 KUrlNavigator::Private::Private(KUrlNavigator
* q
, KFilePlacesModel
* placesModel
) :
244 m_showPlacesSelector(placesModel
!= 0),
245 m_showFullPath(false),
247 m_layout(new QHBoxLayout
),
253 m_toggleEditableMode(0),
254 m_customProtocols(QStringList()),
257 m_layout
->setSpacing(0);
258 m_layout
->setMargin(0);
260 // initialize the places selector
261 q
->setAutoFillBackground(false);
263 if (placesModel
!= 0) {
264 m_placesSelector
= new KFilePlacesSelector(q
, placesModel
);
265 connect(m_placesSelector
, SIGNAL(placeActivated(const KUrl
&)),
266 q
, SLOT(setUrl(const KUrl
&)));
268 connect(placesModel
, SIGNAL(rowsInserted(QModelIndex
, int, int)),
269 q
, SLOT(updateContent()));
270 connect(placesModel
, SIGNAL(rowsRemoved(QModelIndex
, int, int)),
271 q
, SLOT(updateContent()));
272 connect(placesModel
, SIGNAL(dataChanged(QModelIndex
, QModelIndex
)),
273 q
, SLOT(updateContent()));
276 // create protocol combo
277 m_protocols
= new KProtocolCombo(QString(), q
);
278 connect(m_protocols
, SIGNAL(activated(QString
)),
279 q
, SLOT(slotProtocolChanged(QString
)));
281 // create editor for editing the host
282 m_host
= new KLineEdit(QString(), q
);
283 m_host
->setClearButtonShown(true);
284 connect(m_host
, SIGNAL(editingFinished()),
285 q
, SLOT(slotRemoteHostActivated()));
286 connect(m_host
, SIGNAL(returnPressed()),
287 q
, SIGNAL(returnPressed()));
289 // create drop down button for accessing all paths of the URL
290 m_dropDownButton
= new KUrlDropDownButton(q
);
291 connect(m_dropDownButton
, SIGNAL(clicked()),
292 q
, SLOT(openPathSelectorMenu()));
294 // initialize the path box of the traditional view
295 m_pathBox
= new KUrlComboBox(KUrlComboBox::Both
, true, q
);
296 m_pathBox
->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength
);
297 m_pathBox
->installEventFilter(q
);
299 KUrlCompletion
* kurlCompletion
= new KUrlCompletion(KUrlCompletion::DirCompletion
);
300 m_pathBox
->setCompletionObject(kurlCompletion
);
301 m_pathBox
->setAutoDeleteCompletionObject(true);
303 connect(m_pathBox
, SIGNAL(returnPressed()),
304 q
, SLOT(slotReturnPressed()));
305 connect(m_pathBox
, SIGNAL(urlActivated(KUrl
)),
306 q
, SLOT(setUrl(KUrl
)));
308 m_toggleEditableMode
= new KUrlToggleButton(q
);
309 m_toggleEditableMode
->setMinimumWidth(20);
310 connect(m_toggleEditableMode
, SIGNAL(clicked()),
311 q
, SLOT(switchView()));
313 if (m_placesSelector
!= 0) {
314 m_layout
->addWidget(m_placesSelector
);
316 m_layout
->addWidget(m_protocols
);
317 m_layout
->addWidget(m_dropDownButton
);
318 m_layout
->addWidget(m_host
);
319 m_layout
->setStretchFactor(m_host
, 1);
320 m_layout
->addWidget(m_pathBox
, 1);
321 m_layout
->addWidget(m_toggleEditableMode
);
324 void KUrlNavigator::Private::appendWidget(QWidget
* widget
, int stretch
)
326 m_layout
->insertWidget(m_layout
->count() - 1, widget
, stretch
);
329 void KUrlNavigator::Private::slotReturnPressed()
331 // Parts of the following code have been taken
332 // from the class KateFileSelector located in
333 // kate/app/katefileselector.hpp of Kate.
334 // Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
335 // Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
336 // Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
338 const KUrl typedUrl
= q
->uncommittedUrl();
339 QStringList urls
= m_pathBox
->urls();
340 urls
.removeAll(typedUrl
.url());
341 urls
.prepend(typedUrl
.url());
342 m_pathBox
->setUrls(urls
, KUrlComboBox::RemoveBottom
);
345 // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
346 // synchronize the result in the path box.
347 m_pathBox
->setUrl(q
->url());
349 emit q
->returnPressed();
351 if (QApplication::keyboardModifiers() & Qt::ControlModifier
) {
352 // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
353 // The switch must be done asynchronously, as we are in the context of the
355 QMetaObject::invokeMethod(q
, "switchToBreadcrumbMode", Qt::QueuedConnection
);
359 void KUrlNavigator::Private::slotRemoteHostActivated()
363 KUrl
n(m_protocols
->currentProtocol() + "://" + m_host
->text());
365 if (n
.scheme() != u
.scheme() ||
366 n
.host() != u
.host() ||
367 n
.user() != u
.user() ||
368 n
.port() != u
.port()) {
369 u
.setScheme(n
.scheme());
374 //TODO: get rid of this HACK for file:///!
375 if (u
.scheme() == "file") {
377 if (u
.path().isEmpty()) {
386 void KUrlNavigator::Private::slotProtocolChanged(const QString
& protocol
)
389 url
.setScheme(protocol
);
391 QLinkedList
<KUrlNavigatorButton
*>::const_iterator it
= m_navButtons
.begin();
392 const QLinkedList
<KUrlNavigatorButton
*>::const_iterator itEnd
= m_navButtons
.end();
393 while (it
!= itEnd
) {
395 (*it
)->deleteLater();
398 m_navButtons
.clear();
400 if (KProtocolInfo::protocolClass(protocol
) == ":local") {
403 m_host
->setText(QString());
409 void KUrlNavigator::Private::openPathSelectorMenu()
411 if (m_navButtons
.count() <= 0) {
415 const KUrl firstVisibleUrl
= q
->url(m_navButtons
.first()->index());
418 KMenu
* popup
= new KMenu(q
);
419 popup
->setLayoutDirection(Qt::LeftToRight
);
421 const QString placePath
= retrievePlacePath();
422 int idx
= placePath
.count('/'); // idx points to the first directory
423 // after the place path
425 const QString path
= q
->url().pathOrUrl();
426 QString dirName
= path
.section('/', idx
, idx
);
427 if (dirName
.isEmpty()) {
428 dirName
= QChar('/');
431 const QString text
= spacer
+ dirName
;
433 QAction
* action
= new QAction(text
, popup
);
434 const KUrl currentUrl
= q
->url(idx
);
435 if (currentUrl
== firstVisibleUrl
) {
436 popup
->addSeparator();
438 action
->setData(QVariant(currentUrl
.prettyUrl()));
439 popup
->addAction(action
);
443 dirName
= path
.section('/', idx
, idx
);
444 } while (!dirName
.isEmpty());
446 const QPoint pos
= q
->mapToGlobal(m_dropDownButton
->geometry().bottomRight());
447 const QAction
* activatedAction
= popup
->exec(pos
);
448 if (activatedAction
!= 0) {
449 const KUrl url
= KUrl(activatedAction
->data().toString());
453 popup
->deleteLater();
456 void KUrlNavigator::Private::switchView()
458 m_toggleEditableMode
->setFocus();
459 m_editable
= !m_editable
;
460 m_toggleEditableMode
->setChecked(m_editable
);
462 if (q
->isUrlEditable()) {
463 m_pathBox
->setFocus();
466 emit q
->requestActivation();
467 emit q
->editableStateChanged(m_editable
);
470 void KUrlNavigator::Private::dropUrls(const KUrl
& destination
, QDropEvent
* event
)
472 const KUrl::List urls
= KUrl::List::fromMimeData(event
->mimeData());
473 if (!urls
.isEmpty()) {
474 emit q
->urlsDropped(destination
, event
);
476 // KDE5: remove, as the signal has been replaced by
477 // urlsDropped(const KUrl& destination, QDropEvent* event)
478 emit q
->urlsDropped(urls
, destination
);
482 void KUrlNavigator::Private::updateContent()
484 if (m_placesSelector
!= 0) {
485 m_placesSelector
->updateSelection(q
->url());
491 m_dropDownButton
->hide();
494 m_toggleEditableMode
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Preferred
);
495 q
->setSizePolicy(QSizePolicy::Minimum
, QSizePolicy::Fixed
);
498 m_pathBox
->setUrl(q
->url());
500 m_dropDownButton
->setVisible(!m_showFullPath
);
503 const KUrl currentUrl
= q
->url();
504 QString path
= currentUrl
.pathOrUrl();
505 removeTrailingSlash(path
);
507 m_toggleEditableMode
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Preferred
);
508 q
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
);
510 // The URL consists of a protocol, a host and a variable number of directories.
511 // The directories are mapped to URL navigator buttons represented by m_buttons.
512 // - If a part of the URL is represented by a Places bookmark, this part will
513 // be also represented as button if m_showFullPath is false.
514 // - If no part of the URL is represented by a Places bookmark, a protocols-combo
515 // and a host-editor will be shown if no subdirectories are available.
517 if ((m_placesSelector
!= 0) && !m_showFullPath
) {
518 placeUrl
= m_placesSelector
->selectedPlaceUrl();
521 QString placePath
= placeUrl
.isValid() ? placeUrl
.pathOrUrl() : retrievePlacePath();
522 removeTrailingSlash(placePath
);
524 // update the protocol-combo
525 const QString protocol
= currentUrl
.scheme();
526 m_protocols
->setProtocol(protocol
);
528 // update the host-editor
529 QString hostText
= currentUrl
.host();
530 if (!currentUrl
.user().isEmpty()) {
531 hostText
= currentUrl
.user() + '@' + hostText
;
533 if (currentUrl
.port() != -1) {
534 hostText
= hostText
+ ':' + QString::number(currentUrl
.port());
536 m_host
->setText(hostText
);
538 // check whether the protocol-combo and the host-editor should be shown
539 const bool hasPlaceItem
= currentUrl
.isLocalFile() || placeUrl
.isValid();
540 const bool isVisible
= !hasPlaceItem
&&
541 (placePath
== path
) &&
542 (KProtocolInfo::protocolClass(protocol
) != ":local");
544 m_host
->setVisible(isVisible
);
545 m_protocols
->setVisible(isVisible
);
547 // calculate the start index for the directories that should be shown as buttons
548 // and create the buttons
549 int startIndex
= placePath
.count('/');
550 if (!isVisible
&& !hasPlaceItem
&& hostText
.isEmpty()) {
553 updateButtons(path
, startIndex
);
557 void KUrlNavigator::Private::updateButtons(const QString
& path
, int startIndex
)
559 QLinkedList
<KUrlNavigatorButton
*>::iterator it
= m_navButtons
.begin();
560 const QLinkedList
<KUrlNavigatorButton
*>::const_iterator itEnd
= m_navButtons
.end();
561 bool createButton
= false;
562 const KUrl currentUrl
= q
->url();
564 int idx
= startIndex
;
567 createButton
= (it
== itEnd
);
569 const QString dirName
= path
.section('/', idx
, idx
);
570 const bool isFirstButton
= (idx
== startIndex
);
571 hasNext
= isFirstButton
|| !dirName
.isEmpty();
575 // the first URL navigator button should get the name of the
576 // place instead of the directory name
577 if ((m_placesSelector
!= 0) && !m_showFullPath
) {
578 const KUrl placeUrl
= m_placesSelector
->selectedPlaceUrl();
579 text
= m_placesSelector
->selectedPlaceText();
581 if (text
.isEmpty()) {
582 if (currentUrl
.isLocalFile()) {
583 text
= m_showFullPath
? "/" : i18n("Custom Path");
584 } else if (!m_host
->isVisible() && !m_host
->text().isEmpty()) {
585 text
= m_host
->text();
587 // The host is already displayed by the m_host widget,
588 // no button may be added for this index.
595 KUrlNavigatorButton
* button
= 0;
597 button
= new KUrlNavigatorButton(idx
, q
);
598 connect(button
, SIGNAL(urlsDropped(const KUrl
&, QDropEvent
*)),
599 q
, SLOT(dropUrls(const KUrl
&, QDropEvent
*)));
600 appendWidget(button
);
603 button
->setIndex(idx
);
607 button
->setText(text
);
611 m_navButtons
.append(button
);
619 // delete buttons which are not used anymore
620 QLinkedList
<KUrlNavigatorButton
*>::iterator itBegin
= it
;
621 while (it
!= itEnd
) {
623 (*it
)->deleteLater();
626 m_navButtons
.erase(itBegin
, m_navButtons
.end());
628 updateButtonVisibility();
631 void KUrlNavigator::Private::updateButtonVisibility()
637 const int buttonsCount
= m_navButtons
.count();
638 if (buttonsCount
== 0) {
639 m_dropDownButton
->hide();
643 // subtract all widgets from the available width, that must be shown anyway
644 int availableWidth
= q
->width() - m_toggleEditableMode
->minimumWidth();
646 if ((m_placesSelector
!= 0) && m_placesSelector
->isVisible()) {
647 availableWidth
-= m_placesSelector
->width();
650 if ((m_protocols
!= 0) && m_protocols
->isVisible()) {
651 availableWidth
-= m_protocols
->width();
654 if (m_host
->isVisible()) {
655 availableWidth
-= m_host
->width();
658 // check whether buttons must be hidden at all...
659 int requiredButtonWidth
= 0;
660 foreach (KUrlNavigatorButton
* button
, m_navButtons
) {
661 requiredButtonWidth
+= button
->minimumWidth();
663 if (requiredButtonWidth
> availableWidth
) {
664 // At least one button must be hidden. This implies that the
665 // drop-down button must get visible, which again decreases the
667 availableWidth
-= m_dropDownButton
->width();
671 QLinkedList
<KUrlNavigatorButton
*>::iterator it
= m_navButtons
.end();
672 const QLinkedList
<KUrlNavigatorButton
*>::const_iterator itBegin
= m_navButtons
.begin();
673 bool isLastButton
= true;
674 bool hasHiddenButtons
= false;
676 QLinkedList
<KUrlNavigatorButton
*> buttonsToShow
;
677 while (it
!= itBegin
) {
679 KUrlNavigatorButton
* button
= (*it
);
680 availableWidth
-= button
->minimumWidth();
681 if ((availableWidth
<= 0) && !isLastButton
) {
683 hasHiddenButtons
= true;
686 button
->setActive(isLastButton
);
687 // Don't show the button immediately, as setActive()
688 // might change the size and a relayout gets triggered
689 // after showing the button. So the showing of all buttons
690 // is postponed until all buttons have the correct
692 buttonsToShow
.append(button
);
694 isLastButton
= false;
697 // all buttons have the correct activation state and
699 foreach (KUrlNavigatorButton
* button
, buttonsToShow
) {
703 const int startIndex
= retrievePlacePath().count('/');
704 const bool showDropDownButton
= hasHiddenButtons
||
705 (!hasHiddenButtons
&& (m_navButtons
.front()->index() > startIndex
));
706 m_dropDownButton
->setVisible(showDropDownButton
);
709 void KUrlNavigator::Private::switchToBreadcrumbMode()
711 q
->setUrlEditable(false);
714 void KUrlNavigator::Private::deleteButtons()
716 foreach (KUrlNavigatorButton
* button
, m_navButtons
) {
718 button
->deleteLater();
720 m_navButtons
.clear();
723 QString
KUrlNavigator::Private::retrievePlacePath() const
725 const QString path
= q
->url().pathOrUrl();
726 int idx
= path
.indexOf(QLatin1String("///"));
730 idx
= path
.indexOf(QLatin1String("//"));
731 idx
= path
.indexOf(QLatin1Char('/'), (idx
< 0) ? 0 : idx
+ 2);
734 QString placePath
= (idx
< 0) ? path
: path
.left(idx
);
735 removeTrailingSlash(placePath
);
739 bool KUrlNavigator::Private::isCompressedPath(const KUrl
& url
) const
741 const KMimeType::Ptr mime
= KMimeType::findByPath(url
.path(KUrl::RemoveTrailingSlash
));
742 // Note: this list of MIME types depends on the protocols implemented by kio_archive
743 return mime
->is("application/x-compressed-tar") ||
744 mime
->is("application/x-bzip-compressed-tar") ||
745 mime
->is("application/x-tar") ||
746 mime
->is("application/x-tarz") ||
747 mime
->is("application/x-tzo") || // (not sure KTar supports those?)
748 mime
->is("application/zip") ||
749 mime
->is("application/x-archive");
752 void KUrlNavigator::Private::removeTrailingSlash(QString
& url
) const
754 const int length
= url
.length();
755 if ((length
> 0) && (url
.at(length
- 1) == QChar('/'))) {
756 url
.remove(length
-1, 1);
760 KUrl
KUrlNavigator::Private::adjustedUrl(const QString
& typedUrl
) const
762 KUrl
url(typedUrl
.trimmed());
764 url
.setPass(QString());
771 KUrlNavigator::KUrlNavigator(KFilePlacesModel
* placesModel
,
775 d(new Private(this, placesModel
))
777 d
->m_history
.prepend(HistoryElem(url
));
778 setLayoutDirection(Qt::LeftToRight
);
780 const QFont font
= KGlobalSettings::generalFont();
783 const int minHeight
= d
->m_pathBox
->sizeHint().height();
784 setMinimumHeight(minHeight
);
786 setLayout(d
->m_layout
);
787 setMinimumWidth(100);
792 KUrlNavigator::~KUrlNavigator()
797 const KUrl
& KUrlNavigator::url() const
799 Q_ASSERT(!d
->m_history
.empty());
800 return d
->m_history
[d
->m_historyIndex
].url();
803 KUrl
KUrlNavigator::uncommittedUrl() const
805 if (isUrlEditable()) {
806 return d
->adjustedUrl(d
->m_pathBox
->currentText());
808 return KUrl(d
->m_protocols
->currentProtocol() + "://" + d
->m_host
->text());
812 KUrl
KUrlNavigator::url(int index
) const
818 // keep scheme, hostname etc. as this is needed for e. g. browsing
821 newUrl
.setPath(QString());
823 QString pathOrUrl
= url().pathOrUrl();
824 if (!pathOrUrl
.isEmpty()) {
826 // prevent the last "/" from being stripped
827 // or we end up with an empty path
829 pathOrUrl
= pathOrUrl
.length() > 2 ? pathOrUrl
.left(3) : QDir::rootPath();
831 pathOrUrl
= QLatin1String("/");
834 pathOrUrl
= pathOrUrl
.section('/', 0, index
);
838 newUrl
.setPath(KUrl(pathOrUrl
).path());
842 bool KUrlNavigator::goBack()
844 const int count
= d
->m_history
.count();
845 if (d
->m_historyIndex
< count
- 1) {
848 emit
historyChanged();
849 emit
urlChanged(url());
856 bool KUrlNavigator::goForward()
858 if (d
->m_historyIndex
> 0) {
861 emit
historyChanged();
862 emit
urlChanged(url());
869 bool KUrlNavigator::goUp()
871 const KUrl
& currentUrl
= url();
872 const KUrl upUrl
= currentUrl
.upUrl();
873 if (upUrl
!= currentUrl
) {
881 void KUrlNavigator::goHome()
883 if (d
->m_homeUrl
.isEmpty()) {
884 setUrl(QDir::homePath());
886 setUrl(d
->m_homeUrl
);
890 void KUrlNavigator::setHomeUrl(const QString
& homeUrl
)
892 d
->m_homeUrl
= homeUrl
;
895 void KUrlNavigator::setUrlEditable(bool editable
)
897 if (d
->m_editable
!= editable
) {
902 bool KUrlNavigator::isUrlEditable() const
904 return d
->m_editable
;
907 void KUrlNavigator::setShowFullPath(bool show
)
909 if (d
->m_showFullPath
!= show
) {
910 d
->m_showFullPath
= show
;
915 bool KUrlNavigator::showFullPath() const
917 return d
->m_showFullPath
;
921 void KUrlNavigator::setActive(bool active
)
923 if (active
!= d
->m_active
) {
924 d
->m_active
= active
;
932 bool KUrlNavigator::isActive() const
937 void KUrlNavigator::setPlacesSelectorVisible(bool visible
)
939 if (visible
== d
->m_showPlacesSelector
) {
943 if (visible
&& (d
->m_placesSelector
== 0)) {
944 // the places selector cannot get visible as no
945 // places model is available
949 d
->m_showPlacesSelector
= visible
;
950 d
->m_placesSelector
->setVisible(visible
);
953 bool KUrlNavigator::isPlacesSelectorVisible() const
955 return d
->m_showPlacesSelector
;
958 void KUrlNavigator::setUrl(const KUrl
& newUrl
)
960 if (newUrl
== url()) {
964 QString urlStr
= KUrlCompletion::replacedPath(newUrl
.pathOrUrl(), true, true);
965 if ((urlStr
.length() > 0) && (urlStr
.at(0) == '~')) {
966 // replace '~' by the home directory
968 urlStr
.insert(0, QDir::homePath());
971 if ((newUrl
.protocol() == "tar") || (newUrl
.protocol() == "zip")) {
972 // The URL represents a tar- or zip-file. Check whether
973 // the URL is really part of the tar- or zip-file, otherwise
974 // replace it by the local path again.
975 bool insideCompressedPath
= d
->isCompressedPath(newUrl
);
976 if (!insideCompressedPath
) {
977 KUrl prevUrl
= newUrl
;
978 KUrl parentUrl
= newUrl
.upUrl();
979 while (parentUrl
!= prevUrl
) {
980 if (d
->isCompressedPath(parentUrl
)) {
981 insideCompressedPath
= true;
985 parentUrl
= parentUrl
.upUrl();
988 if (!insideCompressedPath
) {
989 // drop the tar: or zip: protocol since we are not
990 // inside the compressed path anymore
991 urlStr
= newUrl
.path();
995 const KUrl
transformedUrl(urlStr
);
997 // Check whether current history element has the same URL.
998 // If this is the case, just ignore setting the URL.
999 const HistoryElem
& historyElem
= d
->m_history
[d
->m_historyIndex
];
1000 const bool isUrlEqual
= transformedUrl
.equals(historyElem
.url(), KUrl::CompareWithoutTrailingSlash
) ||
1001 (!transformedUrl
.isValid() && (urlStr
== historyElem
.url().url()));
1006 if (d
->m_historyIndex
> 0) {
1007 // If an URL is set when the history index is not at the end (= 0),
1008 // then clear all previous history elements so that a new history
1009 // tree is started from the current position.
1010 QList
<HistoryElem
>::iterator begin
= d
->m_history
.begin();
1011 QList
<HistoryElem
>::iterator end
= begin
+ d
->m_historyIndex
;
1012 d
->m_history
.erase(begin
, end
);
1013 d
->m_historyIndex
= 0;
1016 Q_ASSERT(d
->m_historyIndex
== 0);
1017 d
->m_history
.insert(0, HistoryElem(transformedUrl
));
1019 // Prevent an endless growing of the history: remembering
1020 // the last 100 Urls should be enough...
1021 const int historyMax
= 100;
1022 if (d
->m_history
.size() > historyMax
) {
1023 QList
<HistoryElem
>::iterator begin
= d
->m_history
.begin() + historyMax
;
1024 QList
<HistoryElem
>::iterator end
= d
->m_history
.end();
1025 d
->m_history
.erase(begin
, end
);
1028 emit
historyChanged();
1029 emit
urlChanged(transformedUrl
);
1033 requestActivation();
1036 void KUrlNavigator::requestActivation()
1041 void KUrlNavigator::saveRootUrl(const KUrl
& url
)
1043 HistoryElem
& hist
= d
->m_history
[d
->m_historyIndex
];
1044 hist
.setRootUrl(url
);
1047 void KUrlNavigator::savePosition(int x
, int y
)
1049 HistoryElem
& hist
= d
->m_history
[d
->m_historyIndex
];
1050 hist
.setContentsX(x
);
1051 hist
.setContentsY(y
);
1054 void KUrlNavigator::keyReleaseEvent(QKeyEvent
* event
)
1056 QWidget::keyReleaseEvent(event
);
1057 if (isUrlEditable() && (event
->key() == Qt::Key_Escape
)) {
1058 setUrlEditable(false);
1062 void KUrlNavigator::mouseReleaseEvent(QMouseEvent
* event
)
1064 if (event
->button() == Qt::MidButton
) {
1065 QClipboard
* clipboard
= QApplication::clipboard();
1066 const QMimeData
* mimeData
= clipboard
->mimeData();
1067 if (mimeData
->hasText()) {
1068 const QString text
= mimeData
->text();
1072 QWidget::mouseReleaseEvent(event
);
1075 void KUrlNavigator::resizeEvent(QResizeEvent
* event
)
1077 QTimer::singleShot(0, this, SLOT(updateButtonVisibility()));
1078 QWidget::resizeEvent(event
);
1081 bool KUrlNavigator::eventFilter(QObject
* watched
, QEvent
* event
)
1083 if ((watched
== d
->m_pathBox
) && (event
->type() == QEvent::FocusIn
)) {
1084 requestActivation();
1088 return QWidget::eventFilter(watched
, event
);
1091 int KUrlNavigator::historySize() const
1093 return d
->m_history
.count();
1096 int KUrlNavigator::historyIndex() const
1098 return d
->m_historyIndex
;
1101 const KUrl
& KUrlNavigator::savedRootUrl() const
1103 const HistoryElem
& histElem
= d
->m_history
[d
->m_historyIndex
];
1104 return histElem
.rootUrl();
1107 QPoint
KUrlNavigator::savedPosition() const
1109 const HistoryElem
& histElem
= d
->m_history
[d
->m_historyIndex
];
1110 return QPoint(histElem
.contentsX(), histElem
.contentsY());
1113 KUrlComboBox
* KUrlNavigator::editor() const
1115 return d
->m_pathBox
;
1118 void KUrlNavigator::setCustomProtocols(const QStringList
&protocols
)
1120 d
->m_customProtocols
= protocols
;
1121 d
->m_protocols
->setCustomProtocols(d
->m_customProtocols
);
1124 QStringList
KUrlNavigator::customProtocols() const
1126 return d
->m_customProtocols
;
1129 void KUrlNavigator::setFocus()
1131 if (isUrlEditable()) {
1132 d
->m_pathBox
->setFocus();
1133 } else if (d
->m_host
) {
1134 d
->m_host
->setFocus();
1136 QWidget::setFocus();
1140 #include "kurlnavigator.moc"