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"