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>
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;
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()));
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()
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);
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();
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;
364 QAction* action =
new QAction(text, popup);
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();
390 void KUrlNavigator::Private::switchView()
392 m_toggleEditableMode->setFocus();
393 m_editable = !m_editable;
394 m_toggleEditableMode->setChecked(m_editable);
397 m_pathBox->setFocus();
404 void KUrlNavigator::Private::dropUrls(
const KUrl& destination, QDropEvent* event)
407 if (!urls.isEmpty()) {
410 #ifndef KDE_NO_DEPRECATED
418 void KUrlNavigator::Private::slotNavigatorButtonClicked(
const KUrl& url, Qt::MouseButton button)
420 if (button & Qt::LeftButton) {
422 }
else if (button & Qt::MidButton) {
427 void KUrlNavigator::Private::openContextMenu()
431 QPointer<KMenu> popup =
new KMenu(q);
435 QAction* copyAction = popup->addAction(
KIcon(
"edit-copy"),
i18n(
"Copy"));
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();
489 void KUrlNavigator::Private::slotPathBoxChanged(
const QString& text)
491 if (text.isEmpty()) {
493 m_protocols->setProtocol(protocol);
500 void 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);
540 void 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();
611 void 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);
690 QString 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();
720 KUrl 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);
751 void KUrlNavigator::Private::switchToBreadcrumbMode()
756 void KUrlNavigator::Private::deleteButtons()
760 button->deleteLater();
762 m_navButtons.clear();
765 QString 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);
782 bool 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");
797 void 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);
805 int 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))
847 d->m_history[d->m_historyIndex].state = 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) {
1084 void KUrlNavigator::setFocus()
1087 d->m_pathBox->setFocus();
1089 QWidget::setFocus();
1093 #ifndef KDE_NO_DEPRECATED
1094 void KUrlNavigator::setUrl(
const KUrl& url)
1101 #ifndef KDE_NO_DEPRECATED
1102 void KUrlNavigator::saveRootUrl(
const KUrl& url)
1105 d->m_history[d->m_historyIndex].rootUrl = url;
1109 #ifndef KDE_NO_DEPRECATED
1110 void KUrlNavigator::savePosition(
int x,
int y)
1113 d->m_history[d->m_historyIndex].pos = QPoint(x, y);
1117 void KUrlNavigator::keyPressEvent(QKeyEvent* event)
1122 QWidget::keyPressEvent(event);
1126 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
1128 QWidget::keyReleaseEvent(event);
1131 void 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);
1150 void KUrlNavigator::resizeEvent(QResizeEvent* event)
1152 QTimer::singleShot(0,
this, SLOT(updateButtonVisibility()));
1153 QWidget::resizeEvent(event);
1156 void KUrlNavigator::wheelEvent(QWheelEvent* event)
1159 QWidget::wheelEvent(event);
1162 bool 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
1215 const KUrl& KUrlNavigator::url()
const
1227 #ifndef KDE_NO_DEPRECATED
1228 KUrl KUrlNavigator::url(
int index)
const
1231 return d->buttonUrl(index);
1235 #ifndef KDE_NO_DEPRECATED
1236 KUrl KUrlNavigator::historyUrl(
int historyIndex)
const
1243 #ifndef KDE_NO_DEPRECATED
1244 const KUrl& KUrlNavigator::savedRootUrl()
const
1250 static KUrl rootUrl;
1251 rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1256 #ifndef KDE_NO_DEPRECATED
1257 QPoint KUrlNavigator::savedPosition()
const
1260 return d->m_history[d->m_historyIndex].pos;
1264 #ifndef KDE_NO_DEPRECATED
1272 #include "kurlnavigator.moc"