42 #include <QtCore/QDir>
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>
56 using namespace KDEPrivate;
61 #ifndef KDE_NO_DEPRECATED
68 class 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;
168 int adjustedHistoryIndex(
int historyIndex)
const;
172 bool m_showPlacesSelector : 1;
173 bool m_showFullPath : 1;
176 QHBoxLayout* m_layout;
194 m_showPlacesSelector(placesModel != 0),
195 m_showFullPath(false),
197 m_layout(new QHBoxLayout),
203 m_toggleEditableMode(0),
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()));
276 void 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);
293 void KUrlNavigator::Private::appendWidget(
QWidget* widget,
int stretch)
295 m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
298 void KUrlNavigator::Private::slotReturnPressed()
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);
329 void KUrlNavigator::Private::slotProtocolChanged(
const QString& protocol)
331 Q_ASSERT(m_editable);
335 url.
setPath((protocol == QLatin1String(
"file")) ? QLatin1String(
"/") : QLatin1String(
"//"));
337 m_pathBox->setEditUrl(url);
340 void KUrlNavigator::Private::openPathSelectorMenu()
342 if (m_navButtons.count() <= 0) {
346 const KUrl firstVisibleUrl = m_navButtons.first()->
url();
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();
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());
384 popup->deleteLater();
387 void KUrlNavigator::Private::switchView()
389 m_toggleEditableMode->setFocus();
390 m_editable = !m_editable;
391 m_toggleEditableMode->setChecked(m_editable);
394 m_pathBox->setFocus();
401 void KUrlNavigator::Private::dropUrls(
const KUrl& destination, QDropEvent* event)
404 if (!urls.isEmpty()) {
407 #ifndef KDE_NO_DEPRECATED
415 void KUrlNavigator::Private::slotNavigatorButtonClicked(
const KUrl& url, Qt::MouseButton button)
417 if (button & Qt::LeftButton) {
419 }
else if (button & Qt::MidButton) {
424 void KUrlNavigator::Private::openContextMenu()
437 QClipboard* clipboard = QApplication::clipboard();
438 pasteAction->setEnabled(!clipboard->text().isEmpty());
440 popup.addSeparator();
443 QAction* editAction = popup.addAction(
i18n(
"Edit"));
444 editAction->setCheckable(
true);
446 QAction* navigateAction = popup.addAction(
i18n(
"Navigate"));
447 navigateAction->setCheckable(
true);
449 QActionGroup* modeGroup =
new QActionGroup(&popup);
450 modeGroup->addAction(editAction);
451 modeGroup->addAction(navigateAction);
453 editAction->setChecked(
true);
455 navigateAction->setChecked(
true);
458 popup.addSeparator();
461 QAction* showFullPathAction = popup.addAction(
i18n(
"Show Full Path"));
462 showFullPathAction->setCheckable(
true);
465 QAction* activatedAction = popup.exec(QCursor::pos());
466 if (activatedAction == copyAction) {
467 QMimeData* mimeData =
new QMimeData();
469 clipboard->setMimeData(mimeData);
470 }
else if (activatedAction == pasteAction) {
472 }
else if (activatedAction == editAction) {
474 }
else if (activatedAction == navigateAction) {
476 }
else if (activatedAction == showFullPathAction) {
481 void KUrlNavigator::Private::slotPathBoxChanged(
const QString& text)
483 if (text.isEmpty()) {
485 m_protocols->setProtocol(protocol);
492 void KUrlNavigator::Private::updateContent()
495 if (m_placesSelector != 0) {
496 m_placesSelector->updateSelection(currentUrl);
501 m_dropDownButton->hide();
504 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
505 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
508 m_pathBox->setUrl(currentUrl);
514 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
515 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
520 if ((m_placesSelector != 0) && !m_showFullPath) {
521 placeUrl = m_placesSelector->selectedPlaceUrl();
524 QString placePath = placeUrl.isValid() ? placeUrl.
pathOrUrl() : retrievePlacePath();
525 removeTrailingSlash(placePath);
527 const int startIndex = placePath.count(
'/');
528 updateButtons(startIndex);
532 void KUrlNavigator::Private::updateButtons(
int startIndex)
538 bool createButton =
false;
539 const int oldButtonCount = m_navButtons.count();
541 int idx = startIndex;
544 createButton = (idx - startIndex >= oldButtonCount);
545 const bool isFirstButton = (idx == startIndex);
546 const QString dirName = path.section(QLatin1Char(
'/'), idx, idx);
547 hasNext = isFirstButton || !dirName.isEmpty();
552 button->installEventFilter(q);
553 button->setForegroundRole(QPalette::WindowText);
554 connect(button, SIGNAL(urlsDropped(
KUrl,QDropEvent*)),
555 q, SLOT(dropUrls(
KUrl,QDropEvent*)));
556 connect(button, SIGNAL(clicked(
KUrl,Qt::MouseButton)),
557 q, SLOT(slotNavigatorButtonClicked(
KUrl,Qt::MouseButton)));
558 connect(button, SIGNAL(finishedTextResolving()),
559 q, SLOT(updateButtonVisibility()));
560 appendWidget(button);
562 button = m_navButtons[idx - startIndex];
563 button->
setUrl(buttonUrl(idx));
567 button->
setText(firstButtonText());
572 if (!isFirstButton) {
573 setTabOrder(m_navButtons.last(), button);
575 m_navButtons.append(button);
584 const int newButtonCount = idx - startIndex;
585 if (newButtonCount < oldButtonCount) {
589 while (it != itEnd) {
591 (*it)->deleteLater();
594 m_navButtons.erase(itBegin, itEnd);
597 setTabOrder(m_dropDownButton, m_navButtons.first());
598 setTabOrder(m_navButtons.last(), m_toggleEditableMode);
600 updateButtonVisibility();
603 void KUrlNavigator::Private::updateButtonVisibility()
609 const int buttonsCount = m_navButtons.count();
610 if (buttonsCount == 0) {
611 m_dropDownButton->hide();
616 int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
618 if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
619 availableWidth -= m_placesSelector->width();
622 if ((m_protocols != 0) && m_protocols->isVisible()) {
623 availableWidth -= m_protocols->width();
627 int requiredButtonWidth = 0;
629 requiredButtonWidth += button->minimumWidth();
632 if (requiredButtonWidth > availableWidth) {
636 availableWidth -= m_dropDownButton->width();
642 bool isLastButton =
true;
643 bool hasHiddenButtons =
false;
645 QLinkedList<KUrlNavigatorButton*> buttonsToShow;
646 while (it != itBegin) {
649 availableWidth -= button->minimumWidth();
650 if ((availableWidth <= 0) && !isLastButton) {
652 hasHiddenButtons =
true;
660 buttonsToShow.append(button);
662 isLastButton =
false;
671 if (hasHiddenButtons) {
672 m_dropDownButton->show();
675 KUrl url = m_navButtons.front()->
url();
678 m_dropDownButton->setVisible(visible);
682 QString KUrlNavigator::Private::firstButtonText()
const
688 if ((m_placesSelector != 0) && !m_showFullPath) {
689 const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
690 text = m_placesSelector->selectedPlaceText();
693 if (text.isEmpty()) {
697 text = currentUrl.
path().length() > 1 ? currentUrl.
path().left(2) : QDir::rootPath();
699 text = m_showFullPath ? QLatin1String(
"/") :
i18n(
"Custom Path");
702 text = currentUrl.
protocol() + QLatin1Char(
':');
703 if (!currentUrl.host().isEmpty()) {
704 text += QLatin1Char(
' ') + currentUrl.host();
712 KUrl KUrlNavigator::Private::buttonUrl(
int index)
const
721 KUrl newUrl = currentUrl;
725 if (!pathOrUrl.isEmpty()) {
730 pathOrUrl = pathOrUrl.length() > 1 ? pathOrUrl.left(2) : QDir::rootPath();
732 pathOrUrl = QLatin1String(
"/");
735 pathOrUrl = pathOrUrl.section(
'/', 0, index);
743 void KUrlNavigator::Private::switchToBreadcrumbMode()
748 void KUrlNavigator::Private::deleteButtons()
752 button->deleteLater();
754 m_navButtons.clear();
757 QString KUrlNavigator::Private::retrievePlacePath()
const
761 int idx = path.indexOf(QLatin1String(
"///"));
765 idx = path.indexOf(QLatin1String(
"//"));
766 idx = path.indexOf(QLatin1Char(
'/'), (idx < 0) ? 0 : idx + 2);
769 QString placePath = (idx < 0) ? path : path.left(idx);
770 removeTrailingSlash(placePath);
774 bool KUrlNavigator::Private::isCompressedPath(
const KUrl& url)
const
778 return mime->is(
"application/x-compressed-tar") ||
779 mime->is(
"application/x-bzip-compressed-tar") ||
780 mime->is(
"application/x-lzma-compressed-tar") ||
781 mime->is(
"application/x-xz-compressed-tar") ||
782 mime->is(
"application/x-tar") ||
783 mime->is(
"application/x-tarz") ||
784 mime->is(
"application/x-tzo") ||
785 mime->is(
"application/zip") ||
786 mime->is(
"application/x-archive");
789 void KUrlNavigator::Private::removeTrailingSlash(
QString& url)
const
791 const int length = url.length();
792 if ((length > 0) && (url.at(length - 1) == QChar(
'/'))) {
793 url.remove(length - 1, 1);
797 int KUrlNavigator::Private::adjustedHistoryIndex(
int historyIndex)
const
799 if (historyIndex < 0) {
800 historyIndex = m_historyIndex;
801 }
else if (historyIndex >= m_history.size()) {
802 historyIndex = m_history.size() - 1;
803 Q_ASSERT(historyIndex >= 0);
812 d(new Private(this, 0))
814 d->initialize(
KUrl());
821 d(new Private(this, placesModel))
833 historyIndex = d->adjustedHistoryIndex(historyIndex);
839 d->m_history[d->m_historyIndex].state = state;
844 historyIndex = d->adjustedHistoryIndex(historyIndex);
850 const int count = d->m_history.count();
851 if (d->m_historyIndex < count - 1) {
868 if (d->m_historyIndex > 0) {
887 if (upUrl != currentUrl) {
897 if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
916 if (d->m_editable != editable) {
923 return d->m_editable;
928 if (d->m_showFullPath != show) {
929 d->m_showFullPath = show;
936 return d->m_showFullPath;
942 if (active != d->m_active) {
943 d->m_active = active;
945 d->m_dropDownButton->setActive(active);
964 if (visible == d->m_showPlacesSelector) {
968 if (visible && (d->m_placesSelector == 0)) {
974 d->m_showPlacesSelector = visible;
975 d->m_placesSelector->setVisible(visible);
980 return d->m_showPlacesSelector;
985 KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
988 return filteredData.uri();
991 return KUrl(filteredData.typedString());
1004 if ((url.
protocol() == QLatin1String(
"tar")) || (url.
protocol() == QLatin1String(
"zip"))) {
1008 bool insideCompressedPath = d->isCompressedPath(url);
1009 if (!insideCompressedPath) {
1012 while (parentUrl != prevUrl) {
1013 if (d->isCompressedPath(parentUrl)) {
1014 insideCompressedPath =
true;
1017 prevUrl = parentUrl;
1018 parentUrl = parentUrl.
upUrl();
1021 if (!insideCompressedPath) {
1030 const LocationData& data = d->m_history[d->m_historyIndex];
1039 if (d->m_historyIndex > 0) {
1045 d->m_history.erase(begin, end);
1046 d->m_historyIndex = 0;
1049 Q_ASSERT(d->m_historyIndex == 0);
1050 LocationData newData;
1052 d->m_history.insert(0, newData);
1056 const int historyMax = 100;
1057 if (d->m_history.size() > historyMax) {
1060 d->m_history.erase(begin, end);
1076 void KUrlNavigator::setFocus()
1079 d->m_pathBox->setFocus();
1081 QWidget::setFocus();
1085 #ifndef KDE_NO_DEPRECATED
1086 void KUrlNavigator::setUrl(
const KUrl& url)
1093 #ifndef KDE_NO_DEPRECATED
1094 void KUrlNavigator::saveRootUrl(
const KUrl& url)
1097 d->m_history[d->m_historyIndex].rootUrl = url;
1101 #ifndef KDE_NO_DEPRECATED
1102 void KUrlNavigator::savePosition(
int x,
int y)
1105 d->m_history[d->m_historyIndex].pos =
QPoint(x, y);
1109 void KUrlNavigator::keyPressEvent(QKeyEvent* event)
1114 QWidget::keyPressEvent(event);
1118 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
1120 QWidget::keyReleaseEvent(event);
1123 void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
1125 if (event->button() == Qt::MidButton) {
1126 const QRect bounds = d->m_toggleEditableMode->geometry();
1127 if (bounds.contains(event->pos())) {
1131 QClipboard* clipboard = QApplication::clipboard();
1132 const QMimeData* mimeData = clipboard->mimeData();
1133 if (mimeData->hasText()) {
1134 const QString text = mimeData->text();
1139 QWidget::mouseReleaseEvent(event);
1142 void KUrlNavigator::resizeEvent(QResizeEvent* event)
1144 QTimer::singleShot(0,
this, SLOT(updateButtonVisibility()));
1145 QWidget::resizeEvent(event);
1148 void KUrlNavigator::wheelEvent(QWheelEvent* event)
1151 QWidget::wheelEvent(event);
1154 bool KUrlNavigator::eventFilter(
QObject* watched, QEvent* event)
1156 switch (event->type()) {
1157 case QEvent::FocusIn:
1158 if (watched == d->m_pathBox) {
1167 case QEvent::FocusOut:
1177 return QWidget::eventFilter(watched, event);
1182 return d->m_history.count();
1187 return d->m_historyIndex;
1192 return d->m_pathBox;
1197 d->m_customProtocols = protocols;
1198 d->m_protocols->setCustomProtocols(d->m_customProtocols);
1203 return d->m_customProtocols;
1206 #ifndef KDE_NO_DEPRECATED
1207 const KUrl& KUrlNavigator::url()
const
1219 #ifndef KDE_NO_DEPRECATED
1220 KUrl KUrlNavigator::url(
int index)
const
1223 return d->buttonUrl(index);
1227 #ifndef KDE_NO_DEPRECATED
1228 KUrl KUrlNavigator::historyUrl(
int historyIndex)
const
1235 #ifndef KDE_NO_DEPRECATED
1236 const KUrl& KUrlNavigator::savedRootUrl()
const
1242 static KUrl rootUrl;
1243 rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1248 #ifndef KDE_NO_DEPRECATED
1249 QPoint KUrlNavigator::savedPosition()
const
1252 return d->m_history[d->m_historyIndex].pos;
1256 #ifndef KDE_NO_DEPRECATED
1264 #include "kurlnavigator.moc"
void editableStateChanged(bool editable)
Is emitted, if the editable state for the URL has been changed (see KUrlNavigator::setUrlEditable())...
QString i18n(const char *text)
void urlAboutToBeChanged(const KUrl &newUrl)
Is emitted, before the location URL is going to be changed to newUrl.
void adjustPath(AdjustPathOption trailing)
bool goUp()
Goes up one step of the URL path and remembers the old path in the history.
void setActive(bool active)
Set the URL navigator to the active mode, if active is true.
QStringList customProtocols() const
QByteArray locationState(int historyIndex=-1) const
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
KUrl uncommittedUrl() const
void cleanPath(const CleanPathOption &options=SimplifyDirSeparators)
void setHomeUrl(const KUrl &url)
Sets the home URL used by KUrlNavigator::goHome().
void urlsDropped(const KUrl &destination, QDropEvent *event)
Is emitted if a dropping has been done above the destination destination.
void goHome()
Goes to the home URL and remembers the old URL in the history.
void setLocationUrl(const KUrl &url)
Sets the location to url.
Allows to select a bookmark from a popup menu.
void setPath(const QString &path)
bool isUrlEditable() const
void setProtocol(const QString &proto)
static KUrl::List fromMimeData(const QMimeData *mimeData, KUrl::MetaDataMap *metaData=0)
QString pathOrUrl() const
bool showFullPath() const
bool goBack()
Goes back one step in the URL history.
void setShowFullPath(bool show)
Shows the full path of the URL even if a place represents a part of the URL.
QString path(AdjustPathOption trailing=LeaveTrailingSlash) const
void activated()
Is emitted, if the URL navigator has been activated by an user interaction.
void saveLocationState(const QByteArray &state)
Saves the location state described by state for the current location.
static KUriFilter * self()
bool isPlacesSelectorVisible() const
A combobox listing available protocols.
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...
KUrlComboBox * editor() const
bool goForward()
Goes forward one step in the URL history.
void returnPressed()
This signal is emitted when the Return or Enter key is pressed.
void historyChanged()
Is emitted, if the history has been changed.
KUrlNavigator(QWidget *parent=0)
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
void setPlacesSelectorVisible(bool visible)
Sets the places selector visible, if visible is true.
Widget that allows to navigate through the paths of an URL.
void setUrlEditable(bool editable)
Allows to edit the URL of the navigation bar if editable is true, and sets the focus accordingly...
void requestActivation()
Activates the URL navigator (KUrlNavigator::isActive() will return true) and emits the signal KUrlNav...
void urlChanged(const KUrl &url)
Is emitted, if the location URL has been changed e.
bool equals(const KUrl &u, const EqualsOptions &options=0) const
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
void setCheckForExecutables(bool check)
const KShortcut & begin()
This class is a list view model.