43#include <QtCore/QLinkedList>
44#include <QtCore/QTimer>
45#include <QtGui/QApplication>
46#include <QtGui/QBoxLayout>
47#include <QtGui/QClipboard>
48#include <QtGui/QDropEvent>
49#include <QtGui/QKeyEvent>
50#include <QtGui/QLabel>
51#include <QtGui/QPainter>
52#include <QtGui/QStyleOption>
61#ifndef KDE_NO_DEPRECATED
68class KUrlNavigator::Private
73 void initialize(
const KUrl& url);
75 void slotReturnPressed();
76 void slotProtocolChanged(
const QString&);
77 void openPathSelectorMenu();
84 void appendWidget(
QWidget* widget,
int stretch = 0);
94 void dropUrls(
const KUrl& destination, QDropEvent* event);
101 void slotNavigatorButtonClicked(
const KUrl& url, Qt::MouseButton button);
103 void openContextMenu();
105 void slotPathBoxChanged(
const QString& text);
107 void updateContent();
117 void updateButtons(
int startIndex);
124 void updateButtonVisibility();
129 QString firstButtonText()
const;
134 KUrl buttonUrl(
int index)
const;
136 void switchToBreadcrumbMode();
142 void deleteButtons();
151 QString retrievePlacePath()
const;
157 bool isCompressedPath(
const KUrl& path)
const;
159 void removeTrailingSlash(QString& url)
const;
172 bool m_showPlacesSelector : 1;
173 bool m_showFullPath : 1;
176 QHBoxLayout* m_layout;
186 QStringList m_customProtocols;
194 m_showPlacesSelector(placesModel != 0),
195 m_showFullPath(false),
197 m_layout(new QHBoxLayout),
203 m_toggleEditableMode(0),
205 m_customProtocols(QStringList()),
208 m_layout->setSpacing(0);
209 m_layout->setMargin(0);
212 q->setAutoFillBackground(
false);
214 if (placesModel != 0) {
216 connect(m_placesSelector, SIGNAL(placeActivated(
KUrl)),
217 q, SLOT(setLocationUrl(
KUrl)));
219 connect(placesModel, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
220 q, SLOT(updateContent()));
221 connect(placesModel, SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
222 q, SLOT(updateContent()));
223 connect(placesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
224 q, SLOT(updateContent()));
229 connect(m_protocols, SIGNAL(activated(QString)),
230 q, SLOT(slotProtocolChanged(QString)));
234 m_dropDownButton->setForegroundRole(QPalette::WindowText);
235 m_dropDownButton->installEventFilter(q);
236 connect(m_dropDownButton, SIGNAL(clicked()),
237 q, SLOT(openPathSelectorMenu()));
241 m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
242 m_pathBox->installEventFilter(q);
245 m_pathBox->setCompletionObject(kurlCompletion);
246 m_pathBox->setAutoDeleteCompletionObject(
true);
248 connect(m_pathBox, SIGNAL(returnPressed()),
249 q, SLOT(slotReturnPressed()));
250 connect(m_pathBox, SIGNAL(urlActivated(
KUrl)),
251 q, SLOT(setLocationUrl(
KUrl)));
252 connect(m_pathBox, SIGNAL(editTextChanged(QString)),
253 q, SLOT(slotPathBoxChanged(QString)));
258 m_toggleEditableMode->installEventFilter(q);
259 m_toggleEditableMode->setMinimumWidth(20);
260 connect(m_toggleEditableMode, SIGNAL(clicked()),
261 q, SLOT(switchView()));
263 if (m_placesSelector != 0) {
264 m_layout->addWidget(m_placesSelector);
266 m_layout->addWidget(m_protocols);
267 m_layout->addWidget(m_dropDownButton);
268 m_layout->addWidget(m_pathBox, 1);
269 m_layout->addWidget(m_toggleEditableMode);
271 q->setContextMenuPolicy(Qt::CustomContextMenu);
272 connect(q, SIGNAL(customContextMenuRequested(QPoint)),
273 q, SLOT(openContextMenu()));
276void KUrlNavigator::Private::initialize(
const KUrl& url)
280 m_history.prepend(data);
282 q->setLayoutDirection(Qt::LeftToRight);
284 const int minHeight = m_pathBox->sizeHint().height();
285 q->setMinimumHeight(minHeight);
287 q->setLayout(m_layout);
288 q->setMinimumWidth(100);
293void KUrlNavigator::Private::appendWidget(
QWidget* widget,
int stretch)
295 m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
298void KUrlNavigator::Private::slotReturnPressed()
308 QStringList urls = m_pathBox->urls();
309 urls.removeAll(typedUrl.
url());
310 urls.prepend(typedUrl.
url());
317 m_pathBox->setUrl(currentUrl);
321 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
325 QMetaObject::invokeMethod(q,
"switchToBreadcrumbMode", Qt::QueuedConnection);
329void KUrlNavigator::Private::slotProtocolChanged(
const QString& protocol)
331 Q_ASSERT(m_editable);
335 url.
setPath((protocol == QLatin1String(
"file")) ? QLatin1String(
"/") : QLatin1String(
"//"));
337 m_pathBox->setEditUrl(url);
340void KUrlNavigator::Private::openPathSelectorMenu()
342 if (m_navButtons.count() <= 0) {
346 const KUrl firstVisibleUrl = m_navButtons.first()->
url();
349 QPointer<KMenu> popup =
new KMenu(q);
350 popup->setLayoutDirection(Qt::LeftToRight);
352 const QString placePath = retrievePlacePath();
353 int idx = placePath.count(QLatin1Char(
'/'));
356 const QString path = m_history[m_historyIndex].url.pathOrUrl();
357 QString dirName = path.section(QLatin1Char(
'/'), idx, idx);
358 if (dirName.isEmpty()) {
359 dirName = QLatin1Char(
'/');
362 const QString text = spacer + dirName;
365 const KUrl currentUrl = buttonUrl(idx);
366 if (currentUrl == firstVisibleUrl) {
367 popup->addSeparator();
369 action->setData(QVariant(currentUrl.
prettyUrl()));
370 popup->addAction(action);
374 dirName = path.section(
'/', idx, idx);
375 }
while (!dirName.isEmpty());
377 const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
378 const QAction* activatedAction = popup->exec(pos);
379 if (activatedAction != 0) {
380 const KUrl url =
KUrl(activatedAction->data().toString());
386 popup->deleteLater();
390void KUrlNavigator::Private::switchView()
392 m_toggleEditableMode->setFocus();
393 m_editable = !m_editable;
394 m_toggleEditableMode->setChecked(m_editable);
397 m_pathBox->setFocus();
404void KUrlNavigator::Private::dropUrls(
const KUrl& destination, QDropEvent* event)
407 if (!urls.isEmpty()) {
410#ifndef KDE_NO_DEPRECATED
418void KUrlNavigator::Private::slotNavigatorButtonClicked(
const KUrl& url, Qt::MouseButton button)
420 if (button & Qt::LeftButton) {
422 }
else if (button & Qt::MidButton) {
427void KUrlNavigator::Private::openContextMenu()
431 QPointer<KMenu> popup =
new KMenu(q);
439 QAction* pasteAction = popup->addAction(
KIcon(
"edit-paste"),
i18n(
"Paste"));
440 QClipboard* clipboard = QApplication::clipboard();
441 pasteAction->setEnabled(!clipboard->text().isEmpty());
443 popup->addSeparator();
446 QAction* editAction = popup->addAction(
i18n(
"Edit"));
447 editAction->setCheckable(
true);
449 QAction* navigateAction = popup->addAction(
i18n(
"Navigate"));
450 navigateAction->setCheckable(
true);
452 QActionGroup* modeGroup =
new QActionGroup(popup);
453 modeGroup->addAction(editAction);
454 modeGroup->addAction(navigateAction);
456 editAction->setChecked(
true);
458 navigateAction->setChecked(
true);
461 popup->addSeparator();
464 QAction* showFullPathAction = popup->addAction(
i18n(
"Show Full Path"));
465 showFullPathAction->setCheckable(
true);
468 QAction* activatedAction = popup->exec(QCursor::pos());
469 if (activatedAction == copyAction) {
470 QMimeData* mimeData =
new QMimeData();
472 clipboard->setMimeData(mimeData);
473 }
else if (activatedAction == pasteAction) {
475 }
else if (activatedAction == editAction) {
477 }
else if (activatedAction == navigateAction) {
479 }
else if (activatedAction == showFullPathAction) {
485 popup->deleteLater();
489void KUrlNavigator::Private::slotPathBoxChanged(
const QString& text)
491 if (text.isEmpty()) {
493 m_protocols->setProtocol(protocol);
500void KUrlNavigator::Private::updateContent()
503 if (m_placesSelector != 0) {
504 m_placesSelector->updateSelection(currentUrl);
509 m_dropDownButton->hide();
512 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
513 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
516 m_pathBox->setUrl(currentUrl);
522 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
523 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
528 if ((m_placesSelector != 0) && !m_showFullPath) {
529 placeUrl = m_placesSelector->selectedPlaceUrl();
532 QString placePath = placeUrl.isValid() ? placeUrl.
pathOrUrl() : retrievePlacePath();
533 removeTrailingSlash(placePath);
535 const int startIndex = placePath.count(
'/');
536 updateButtons(startIndex);
540void KUrlNavigator::Private::updateButtons(
int startIndex)
544 const QString path = currentUrl.
pathOrUrl();
546 bool createButton =
false;
547 const int oldButtonCount = m_navButtons.count();
549 int idx = startIndex;
552 createButton = (idx - startIndex >= oldButtonCount);
553 const bool isFirstButton = (idx == startIndex);
554 const QString dirName = path.section(QLatin1Char(
'/'), idx, idx);
555 hasNext = isFirstButton || !dirName.isEmpty();
560 button->installEventFilter(q);
561 button->setForegroundRole(QPalette::WindowText);
562 connect(button, SIGNAL(urlsDropped(
KUrl,QDropEvent*)),
563 q, SLOT(dropUrls(
KUrl,QDropEvent*)));
564 connect(button, SIGNAL(clicked(
KUrl,Qt::MouseButton)),
565 q, SLOT(slotNavigatorButtonClicked(
KUrl,Qt::MouseButton)));
566 connect(button, SIGNAL(finishedTextResolving()),
567 q, SLOT(updateButtonVisibility()));
568 appendWidget(button);
570 button = m_navButtons[idx - startIndex];
571 button->
setUrl(buttonUrl(idx));
575 button->
setText(firstButtonText());
580 if (!isFirstButton) {
581 setTabOrder(m_navButtons.last(), button);
583 m_navButtons.append(button);
592 const int newButtonCount = idx - startIndex;
593 if (newButtonCount < oldButtonCount) {
597 while (it != itEnd) {
599 (*it)->deleteLater();
602 m_navButtons.erase(itBegin, itEnd);
605 setTabOrder(m_dropDownButton, m_navButtons.first());
606 setTabOrder(m_navButtons.last(), m_toggleEditableMode);
608 updateButtonVisibility();
611void KUrlNavigator::Private::updateButtonVisibility()
617 const int buttonsCount = m_navButtons.count();
618 if (buttonsCount == 0) {
619 m_dropDownButton->hide();
624 int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
626 if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
627 availableWidth -= m_placesSelector->width();
630 if ((m_protocols != 0) && m_protocols->isVisible()) {
631 availableWidth -= m_protocols->width();
635 int requiredButtonWidth = 0;
637 requiredButtonWidth += button->minimumWidth();
640 if (requiredButtonWidth > availableWidth) {
644 availableWidth -= m_dropDownButton->width();
650 bool isLastButton =
true;
651 bool hasHiddenButtons =
false;
653 QLinkedList<KUrlNavigatorButton*> buttonsToShow;
654 while (it != itBegin) {
657 availableWidth -= button->minimumWidth();
658 if ((availableWidth <= 0) && !isLastButton) {
660 hasHiddenButtons =
true;
668 buttonsToShow.append(button);
670 isLastButton =
false;
679 if (hasHiddenButtons) {
680 m_dropDownButton->show();
683 KUrl url = m_navButtons.front()->
url();
686 m_dropDownButton->setVisible(visible);
690QString KUrlNavigator::Private::firstButtonText()
const
696 if ((m_placesSelector != 0) && !m_showFullPath) {
697 const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
698 text = m_placesSelector->selectedPlaceText();
701 if (text.isEmpty()) {
705 text = currentUrl.
path().length() > 1 ? currentUrl.
path().left(2) : QDir::rootPath();
707 text = m_showFullPath ? QLatin1String(
"/") :
i18n(
"Custom Path");
710 text = currentUrl.
protocol() + QLatin1Char(
':');
711 if (!currentUrl.host().isEmpty()) {
712 text += QLatin1Char(
' ') + currentUrl.host();
720KUrl KUrlNavigator::Private::buttonUrl(
int index)
const
729 KUrl newUrl = currentUrl;
732 QString pathOrUrl = currentUrl.
pathOrUrl();
733 if (!pathOrUrl.isEmpty()) {
738 pathOrUrl = pathOrUrl.length() > 1 ? pathOrUrl.left(2) : QDir::rootPath();
740 pathOrUrl = QLatin1String(
"/");
743 pathOrUrl = pathOrUrl.section(
'/', 0, index);
751void KUrlNavigator::Private::switchToBreadcrumbMode()
756void KUrlNavigator::Private::deleteButtons()
760 button->deleteLater();
762 m_navButtons.clear();
765QString KUrlNavigator::Private::retrievePlacePath()
const
768 const QString path = currentUrl.
pathOrUrl();
769 int idx = path.indexOf(QLatin1String(
"///"));
773 idx = path.indexOf(QLatin1String(
"//"));
774 idx = path.indexOf(QLatin1Char(
'/'), (idx < 0) ? 0 : idx + 2);
777 QString placePath = (idx < 0) ? path : path.left(idx);
778 removeTrailingSlash(placePath);
782bool KUrlNavigator::Private::isCompressedPath(
const KUrl& url)
const
786 return mime->is(
"application/x-compressed-tar") ||
787 mime->is(
"application/x-bzip-compressed-tar") ||
788 mime->is(
"application/x-lzma-compressed-tar") ||
789 mime->is(
"application/x-xz-compressed-tar") ||
790 mime->is(
"application/x-tar") ||
791 mime->is(
"application/x-tarz") ||
792 mime->is(
"application/x-tzo") ||
793 mime->is(
"application/zip") ||
794 mime->is(
"application/x-archive");
797void KUrlNavigator::Private::removeTrailingSlash(QString& url)
const
799 const int length = url.length();
800 if ((length > 0) && (url.at(length - 1) == QChar(
'/'))) {
801 url.remove(length - 1, 1);
805int KUrlNavigator::Private::adjustedHistoryIndex(
int historyIndex)
const
807 if (historyIndex < 0) {
808 historyIndex = m_historyIndex;
809 }
else if (historyIndex >= m_history.size()) {
810 historyIndex = m_history.size() - 1;
811 Q_ASSERT(historyIndex >= 0);
820 d(new Private(this, 0))
822 d->initialize(
KUrl());
829 d(new Private(this, placesModel))
841 historyIndex = d->adjustedHistoryIndex(historyIndex);
842 return d->m_history[historyIndex].url;
847 d->m_history[d->m_historyIndex].state = state;
852 historyIndex = d->adjustedHistoryIndex(historyIndex);
853 return d->m_history[historyIndex].state;
858 const int count = d->m_history.count();
859 if (d->m_historyIndex < count - 1) {
876 if (d->m_historyIndex > 0) {
895 if (upUrl != currentUrl) {
905 if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
924 if (d->m_editable != editable) {
931 return d->m_editable;
936 if (d->m_showFullPath != show) {
937 d->m_showFullPath = show;
944 return d->m_showFullPath;
950 if (active != d->m_active) {
951 d->m_active = active;
953 d->m_dropDownButton->setActive(active);
972 if (visible == d->m_showPlacesSelector) {
976 if (visible && (d->m_placesSelector == 0)) {
982 d->m_showPlacesSelector = visible;
983 d->m_placesSelector->setVisible(visible);
988 return d->m_showPlacesSelector;
993 KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
995 if (
KUriFilter::self()->filterUri(filteredData, QStringList() <<
"kshorturifilter" <<
"kurisearchfilter")) {
996 return filteredData.
uri();
1012 if ((url.
protocol() == QLatin1String(
"tar")) || (url.
protocol() == QLatin1String(
"zip"))) {
1016 bool insideCompressedPath = d->isCompressedPath(url);
1017 if (!insideCompressedPath) {
1020 while (parentUrl != prevUrl) {
1021 if (d->isCompressedPath(parentUrl)) {
1022 insideCompressedPath =
true;
1025 prevUrl = parentUrl;
1026 parentUrl = parentUrl.
upUrl();
1029 if (!insideCompressedPath) {
1038 const LocationData& data = d->m_history[d->m_historyIndex];
1047 if (d->m_historyIndex > 0) {
1054 d->m_historyIndex = 0;
1057 Q_ASSERT(d->m_historyIndex == 0);
1058 LocationData newData;
1060 d->m_history.insert(0, newData);
1064 const int historyMax = 100;
1065 if (d->m_history.size() > historyMax) {
1084void KUrlNavigator::setFocus()
1086 if (isUrlEditable()) {
1087 d->m_pathBox->setFocus();
1089 QWidget::setFocus();
1093#ifndef KDE_NO_DEPRECATED
1094void KUrlNavigator::setUrl(
const KUrl& url)
1101#ifndef KDE_NO_DEPRECATED
1102void KUrlNavigator::saveRootUrl(
const KUrl& url)
1105 d->m_history[d->m_historyIndex].rootUrl = url;
1109#ifndef KDE_NO_DEPRECATED
1110void KUrlNavigator::savePosition(
int x,
int y)
1113 d->m_history[d->m_historyIndex].pos = QPoint(x, y);
1117void KUrlNavigator::keyPressEvent(QKeyEvent* event)
1122 QWidget::keyPressEvent(event);
1126void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
1128 QWidget::keyReleaseEvent(event);
1131void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
1133 if (event->button() == Qt::MidButton) {
1134 const QRect bounds = d->m_toggleEditableMode->geometry();
1135 if (bounds.contains(event->pos())) {
1139 QClipboard* clipboard = QApplication::clipboard();
1140 const QMimeData* mimeData = clipboard->mimeData();
1141 if (mimeData->hasText()) {
1142 const QString text = mimeData->text();
1147 QWidget::mouseReleaseEvent(event);
1150void KUrlNavigator::resizeEvent(QResizeEvent* event)
1152 QTimer::singleShot(0,
this, SLOT(updateButtonVisibility()));
1153 QWidget::resizeEvent(event);
1156void KUrlNavigator::wheelEvent(QWheelEvent* event)
1159 QWidget::wheelEvent(event);
1162bool KUrlNavigator::eventFilter(
QObject* watched, QEvent* event)
1164 switch (event->type()) {
1165 case QEvent::FocusIn:
1166 if (watched == d->m_pathBox) {
1175 case QEvent::FocusOut:
1185 return QWidget::eventFilter(watched, event);
1190 return d->m_history.count();
1195 return d->m_historyIndex;
1200 return d->m_pathBox;
1205 d->m_customProtocols = protocols;
1206 d->m_protocols->setCustomProtocols(d->m_customProtocols);
1211 return d->m_customProtocols;
1214#ifndef KDE_NO_DEPRECATED
1215const KUrl& KUrlNavigator::url()
const
1222 url = locationUrl();
1227#ifndef KDE_NO_DEPRECATED
1228KUrl KUrlNavigator::url(
int index)
const
1231 return d->buttonUrl(index);
1235#ifndef KDE_NO_DEPRECATED
1236KUrl KUrlNavigator::historyUrl(
int historyIndex)
const
1243#ifndef KDE_NO_DEPRECATED
1244const KUrl& KUrlNavigator::savedRootUrl()
const
1250 static KUrl rootUrl;
1251 rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1256#ifndef KDE_NO_DEPRECATED
1257QPoint KUrlNavigator::savedPosition()
const
1260 return d->m_history[d->m_historyIndex].pos;
1264#ifndef KDE_NO_DEPRECATED
1272#include "kurlnavigator.moc"
Allows to select a bookmark from a popup menu.
A combobox listing available protocols.
This class is a list view model.
static Ptr findByPath(const QString &path, mode_t mode=0, bool fast_mode=false, int *accuracy=0)
QString typedString() const
void setCheckForExecutables(bool check)
static KUriFilter * self()
Widget that allows to navigate through the paths of an URL.
void urlChanged(const KUrl &url)
Is emitted, if the location URL has been changed e.
void setShowFullPath(bool show)
Shows the full path of the URL even if a place represents a part of the URL.
void setCustomProtocols(const QStringList &protocols)
If an application supports only some special protocols, they can be set with protocols .
KUrl locationUrl(int historyIndex=-1) const
void setPlacesSelectorVisible(bool visible)
Sets the places selector visible, if visible is true.
KUrl uncommittedUrl() const
bool goUp()
Goes up one step of the URL path and remembers the old path in the history.
void tabRequested(const KUrl &url)
Is emitted if the URL url should be opened in a new tab because the user clicked on a breadcrumb with...
void editableStateChanged(bool editable)
Is emitted, if the editable state for the URL has been changed (see KUrlNavigator::setUrlEditable()).
KUrlComboBox * editor() const
void urlAboutToBeChanged(const KUrl &newUrl)
Is emitted, before the location URL is going to be changed to newUrl.
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.
QStringList customProtocols() const
bool goBack()
Goes back one step in the URL history.
KUrlNavigator(QWidget *parent=0)
void requestActivation()
Activates the URL navigator (KUrlNavigator::isActive() will return true) and emits the signal KUrlNav...
void setUrlEditable(bool editable)
Allows to edit the URL of the navigation bar if editable is true, and sets the focus accordingly.
bool isPlacesSelectorVisible() const
void setLocationUrl(const KUrl &url)
Sets the location to url.
bool showFullPath() const
bool goForward()
Goes forward one step in the URL history.
void setActive(bool active)
Set the URL navigator to the active mode, if active is true.
void historyChanged()
Is emitted, if the history has been changed.
void urlsDropped(const KUrl &destination, QDropEvent *event)
Is emitted if a dropping has been done above the destination destination.
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 returnPressed()
This signal is emitted when the Return or Enter key is pressed.
void setHomeUrl(const KUrl &url)
Sets the home URL used by KUrlNavigator::goHome().
static KUrl::List fromMimeData(const QMimeData *mimeData, DecodeOptions decodeOptions, KUrl::MetaDataMap *metaData=0)
void cleanPath(const CleanPathOption &options=SimplifyDirSeparators)
QString pathOrUrl() const
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
void adjustPath(AdjustPathOption trailing)
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
QString path(AdjustPathOption trailing=LeaveTrailingSlash) const
void setProtocol(const QString &proto)
bool equals(const KUrl &u, const EqualsOptions &options=0) const
CompareWithoutTrailingSlash
void setPath(const QString &path)
QString i18n(const char *text)
const KShortcut & begin()