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;
178 QList<LocationData> m_history;
183 QList<KUrlNavigatorButton*> m_navButtons;
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->installEventFilter(q);
235 connect(m_dropDownButton, SIGNAL(clicked()),
236 q, SLOT(openPathSelectorMenu()));
240 m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
241 m_pathBox->installEventFilter(q);
244 m_pathBox->setCompletionObject(kurlCompletion);
245 m_pathBox->setAutoDeleteCompletionObject(
true);
247 connect(m_pathBox, SIGNAL(returnPressed()),
248 q, SLOT(slotReturnPressed()));
249 connect(m_pathBox, SIGNAL(urlActivated(
KUrl)),
250 q, SLOT(setLocationUrl(
KUrl)));
251 connect(m_pathBox, SIGNAL(editTextChanged(
QString)),
252 q, SLOT(slotPathBoxChanged(
QString)));
257 m_toggleEditableMode->installEventFilter(q);
258 m_toggleEditableMode->setMinimumWidth(20);
259 connect(m_toggleEditableMode, SIGNAL(clicked()),
260 q, SLOT(switchView()));
262 if (m_placesSelector != 0) {
263 m_layout->addWidget(m_placesSelector);
265 m_layout->addWidget(m_protocols);
266 m_layout->addWidget(m_dropDownButton);
267 m_layout->addWidget(m_pathBox, 1);
268 m_layout->addWidget(m_toggleEditableMode);
270 q->setContextMenuPolicy(Qt::CustomContextMenu);
271 connect(q, SIGNAL(customContextMenuRequested(
QPoint)),
272 q, SLOT(openContextMenu()));
275 void KUrlNavigator::Private::initialize(
const KUrl& url)
279 m_history.prepend(data);
281 q->setLayoutDirection(Qt::LeftToRight);
283 const int minHeight = m_pathBox->sizeHint().height();
284 q->setMinimumHeight(minHeight);
286 q->setLayout(m_layout);
287 q->setMinimumWidth(100);
292 void KUrlNavigator::Private::appendWidget(
QWidget* widget,
int stretch)
294 m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
297 void KUrlNavigator::Private::slotReturnPressed()
308 urls.removeAll(typedUrl.
url());
309 urls.prepend(typedUrl.
url());
316 m_pathBox->setUrl(currentUrl);
320 if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
324 QMetaObject::invokeMethod(q,
"switchToBreadcrumbMode", Qt::QueuedConnection);
328 void KUrlNavigator::Private::slotProtocolChanged(
const QString& protocol)
330 Q_ASSERT(m_editable);
334 url.
setPath((protocol == QLatin1String(
"file")) ? QLatin1String(
"/") : QLatin1String(
"//"));
336 m_pathBox->setEditUrl(url);
339 void KUrlNavigator::Private::openPathSelectorMenu()
341 if (m_navButtons.count() <= 0) {
345 const KUrl firstVisibleUrl = m_navButtons.first()->
url();
349 popup->setLayoutDirection(Qt::LeftToRight);
351 const QString placePath = retrievePlacePath();
352 int idx = placePath.count(QLatin1Char(
'/'));
355 const QString path = m_history[m_historyIndex].url.pathOrUrl();
356 QString dirName = path.section(QLatin1Char(
'/'), idx, idx);
357 if (dirName.isEmpty()) {
358 dirName = QLatin1Char(
'/');
361 const QString text = spacer + dirName;
364 const KUrl currentUrl = buttonUrl(idx);
365 if (currentUrl == firstVisibleUrl) {
366 popup->addSeparator();
369 popup->addAction(action);
373 dirName = path.section(
'/', idx, idx);
374 }
while (!dirName.isEmpty());
376 const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
377 const QAction* activatedAction = popup->exec(pos);
378 if (activatedAction != 0) {
379 const KUrl url =
KUrl(activatedAction->data().toString());
383 popup->deleteLater();
386 void KUrlNavigator::Private::switchView()
388 m_toggleEditableMode->setFocus();
389 m_editable = !m_editable;
390 m_toggleEditableMode->setChecked(m_editable);
393 m_pathBox->setFocus();
400 void KUrlNavigator::Private::dropUrls(
const KUrl& destination, QDropEvent* event)
403 if (!urls.isEmpty()) {
406 #ifndef KDE_NO_DEPRECATED
414 void KUrlNavigator::Private::slotNavigatorButtonClicked(
const KUrl& url, Qt::MouseButton button)
416 if (button & Qt::LeftButton) {
418 }
else if (button & Qt::MidButton) {
423 void KUrlNavigator::Private::openContextMenu()
436 QClipboard* clipboard = QApplication::clipboard();
437 pasteAction->setEnabled(!clipboard->text().isEmpty());
439 popup.addSeparator();
442 QAction* editAction = popup.addAction(
i18n(
"Edit"));
443 editAction->setCheckable(
true);
445 QAction* navigateAction = popup.addAction(
i18n(
"Navigate"));
446 navigateAction->setCheckable(
true);
448 QActionGroup* modeGroup =
new QActionGroup(&popup);
449 modeGroup->addAction(editAction);
450 modeGroup->addAction(navigateAction);
452 editAction->setChecked(
true);
454 navigateAction->setChecked(
true);
457 popup.addSeparator();
460 QAction* showFullPathAction = popup.addAction(
i18n(
"Show Full Path"));
461 showFullPathAction->setCheckable(
true);
464 QAction* activatedAction = popup.exec(QCursor::pos());
465 if (activatedAction == copyAction) {
466 QMimeData* mimeData =
new QMimeData();
468 clipboard->setMimeData(mimeData);
469 }
else if (activatedAction == pasteAction) {
471 }
else if (activatedAction == editAction) {
473 }
else if (activatedAction == navigateAction) {
475 }
else if (activatedAction == showFullPathAction) {
480 void KUrlNavigator::Private::slotPathBoxChanged(
const QString& text)
482 if (text.isEmpty()) {
484 m_protocols->setProtocol(protocol);
491 void KUrlNavigator::Private::updateContent()
494 if (m_placesSelector != 0) {
495 m_placesSelector->updateSelection(currentUrl);
500 m_dropDownButton->hide();
503 m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
504 q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
507 m_pathBox->setUrl(currentUrl);
513 m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
514 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
519 if ((m_placesSelector != 0) && !m_showFullPath) {
520 placeUrl = m_placesSelector->selectedPlaceUrl();
523 QString placePath = placeUrl.isValid() ? placeUrl.
pathOrUrl() : retrievePlacePath();
524 removeTrailingSlash(placePath);
526 const int startIndex = placePath.count(
'/');
527 updateButtons(startIndex);
531 void KUrlNavigator::Private::updateButtons(
int startIndex)
537 bool createButton =
false;
538 const int oldButtonCount = m_navButtons.count();
540 int idx = startIndex;
543 createButton = (idx - startIndex >= oldButtonCount);
544 const bool isFirstButton = (idx == startIndex);
545 const QString dirName = path.section(QLatin1Char(
'/'), idx, idx);
546 hasNext = isFirstButton || !dirName.isEmpty();
551 button->installEventFilter(q);
552 button->setForegroundRole(QPalette::WindowText);
553 connect(button, SIGNAL(urlsDropped(
KUrl,QDropEvent*)),
554 q, SLOT(dropUrls(
KUrl,QDropEvent*)));
555 connect(button, SIGNAL(clicked(
KUrl,Qt::MouseButton)),
556 q, SLOT(slotNavigatorButtonClicked(
KUrl,Qt::MouseButton)));
557 connect(button, SIGNAL(finishedTextResolving()),
558 q, SLOT(updateButtonVisibility()));
559 appendWidget(button);
561 button = m_navButtons[idx - startIndex];
562 button->
setUrl(buttonUrl(idx));
566 button->
setText(firstButtonText());
571 if (!isFirstButton) {
572 setTabOrder(m_navButtons.last(), button);
574 m_navButtons.append(button);
583 const int newButtonCount = idx - startIndex;
584 if (newButtonCount < oldButtonCount) {
585 const QList<KUrlNavigatorButton*>::iterator itBegin = m_navButtons.begin() + newButtonCount;
586 const QList<KUrlNavigatorButton*>::iterator itEnd = m_navButtons.end();
587 QList<KUrlNavigatorButton*>::iterator it = itBegin;
588 while (it != itEnd) {
590 (*it)->deleteLater();
593 m_navButtons.erase(itBegin, itEnd);
596 setTabOrder(m_dropDownButton, m_navButtons.first());
597 setTabOrder(m_navButtons.last(), m_toggleEditableMode);
599 updateButtonVisibility();
602 void KUrlNavigator::Private::updateButtonVisibility()
608 const int buttonsCount = m_navButtons.count();
609 if (buttonsCount == 0) {
610 m_dropDownButton->hide();
615 int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
617 if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
618 availableWidth -= m_placesSelector->width();
621 if ((m_protocols != 0) && m_protocols->isVisible()) {
622 availableWidth -= m_protocols->width();
626 int requiredButtonWidth = 0;
628 requiredButtonWidth += button->minimumWidth();
631 if (requiredButtonWidth > availableWidth) {
635 availableWidth -= m_dropDownButton->width();
639 QList<KUrlNavigatorButton*>::const_iterator it = m_navButtons.constEnd();
640 const QList<KUrlNavigatorButton*>::const_iterator itBegin = m_navButtons.constBegin();
641 bool isLastButton =
true;
642 bool hasHiddenButtons =
false;
644 QLinkedList<KUrlNavigatorButton*> buttonsToShow;
645 while (it != itBegin) {
648 availableWidth -= button->minimumWidth();
649 if ((availableWidth <= 0) && !isLastButton) {
651 hasHiddenButtons =
true;
659 buttonsToShow.append(button);
661 isLastButton =
false;
670 if (hasHiddenButtons) {
671 m_dropDownButton->show();
674 KUrl url = m_navButtons.front()->
url();
677 m_dropDownButton->setVisible(visible);
681 QString KUrlNavigator::Private::firstButtonText()
const
687 if ((m_placesSelector != 0) && !m_showFullPath) {
688 const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
689 text = m_placesSelector->selectedPlaceText();
692 if (text.isEmpty()) {
696 text = currentUrl.
path().length() > 1 ? currentUrl.
path().left(2) : QDir::rootPath();
698 text = m_showFullPath ? QLatin1String(
"/") :
i18n(
"Custom Path");
701 text = currentUrl.
protocol() + QLatin1Char(
':');
702 if (!currentUrl.host().isEmpty()) {
703 text += QLatin1Char(
' ') + currentUrl.host();
711 KUrl KUrlNavigator::Private::buttonUrl(
int index)
const
720 KUrl newUrl = currentUrl;
724 if (!pathOrUrl.isEmpty()) {
729 pathOrUrl = pathOrUrl.length() > 1 ? pathOrUrl.left(2) : QDir::rootPath();
731 pathOrUrl = QLatin1String(
"/");
734 pathOrUrl = pathOrUrl.section(
'/', 0, index);
742 void KUrlNavigator::Private::switchToBreadcrumbMode()
747 void KUrlNavigator::Private::deleteButtons()
751 button->deleteLater();
753 m_navButtons.clear();
756 QString KUrlNavigator::Private::retrievePlacePath()
const
760 int idx = path.indexOf(QLatin1String(
"///"));
764 idx = path.indexOf(QLatin1String(
"//"));
765 idx = path.indexOf(QLatin1Char(
'/'), (idx < 0) ? 0 : idx + 2);
768 QString placePath = (idx < 0) ? path : path.left(idx);
769 removeTrailingSlash(placePath);
773 bool KUrlNavigator::Private::isCompressedPath(
const KUrl& url)
const
777 return mime->is(
"application/x-compressed-tar") ||
778 mime->is(
"application/x-bzip-compressed-tar") ||
779 mime->is(
"application/x-lzma-compressed-tar") ||
780 mime->is(
"application/x-xz-compressed-tar") ||
781 mime->is(
"application/x-tar") ||
782 mime->is(
"application/x-tarz") ||
783 mime->is(
"application/x-tzo") ||
784 mime->is(
"application/zip") ||
785 mime->is(
"application/x-archive");
788 void KUrlNavigator::Private::removeTrailingSlash(
QString& url)
const
790 const int length = url.length();
791 if ((length > 0) && (url.at(length - 1) == QChar(
'/'))) {
792 url.remove(length - 1, 1);
796 int KUrlNavigator::Private::adjustedHistoryIndex(
int historyIndex)
const
798 if (historyIndex < 0) {
799 historyIndex = m_historyIndex;
800 }
else if (historyIndex >= m_history.size()) {
801 historyIndex = m_history.size() - 1;
802 Q_ASSERT(historyIndex >= 0);
811 d(new Private(this, 0))
813 d->initialize(
KUrl());
820 d(new Private(this, placesModel))
832 historyIndex = d->adjustedHistoryIndex(historyIndex);
838 d->m_history[d->m_historyIndex].state = state;
843 historyIndex = d->adjustedHistoryIndex(historyIndex);
849 const int count = d->m_history.count();
850 if (d->m_historyIndex < count - 1) {
867 if (d->m_historyIndex > 0) {
886 if (upUrl != currentUrl) {
896 if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
915 if (d->m_editable != editable) {
922 return d->m_editable;
927 if (d->m_showFullPath != show) {
928 d->m_showFullPath = show;
935 return d->m_showFullPath;
941 if (active != d->m_active) {
942 d->m_active = active;
944 d->m_dropDownButton->setActive(active);
963 if (visible == d->m_showPlacesSelector) {
967 if (visible && (d->m_placesSelector == 0)) {
973 d->m_showPlacesSelector = visible;
974 d->m_placesSelector->setVisible(visible);
979 return d->m_showPlacesSelector;
984 KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
987 return filteredData.uri();
990 return KUrl(filteredData.typedString());
1003 if ((url.
protocol() == QLatin1String(
"tar")) || (url.
protocol() == QLatin1String(
"zip"))) {
1007 bool insideCompressedPath = d->isCompressedPath(url);
1008 if (!insideCompressedPath) {
1011 while (parentUrl != prevUrl) {
1012 if (d->isCompressedPath(parentUrl)) {
1013 insideCompressedPath =
true;
1016 prevUrl = parentUrl;
1017 parentUrl = parentUrl.
upUrl();
1020 if (!insideCompressedPath) {
1029 const LocationData& data = d->m_history[d->m_historyIndex];
1038 if (d->m_historyIndex > 0) {
1042 QList<LocationData>::iterator
begin = d->m_history.begin();
1043 QList<LocationData>::iterator
end = begin + d->m_historyIndex;
1044 d->m_history.erase(begin, end);
1045 d->m_historyIndex = 0;
1048 Q_ASSERT(d->m_historyIndex == 0);
1049 LocationData newData;
1051 d->m_history.insert(0, newData);
1055 const int historyMax = 100;
1056 if (d->m_history.size() > historyMax) {
1057 QList<LocationData>::iterator
begin = d->m_history.begin() + historyMax;
1058 QList<LocationData>::iterator
end = d->m_history.end();
1059 d->m_history.erase(begin, end);
1075 void KUrlNavigator::setFocus()
1078 d->m_pathBox->setFocus();
1080 QWidget::setFocus();
1084 #ifndef KDE_NO_DEPRECATED
1085 void KUrlNavigator::setUrl(
const KUrl& url)
1092 #ifndef KDE_NO_DEPRECATED
1093 void KUrlNavigator::saveRootUrl(
const KUrl& url)
1096 d->m_history[d->m_historyIndex].rootUrl = url;
1100 #ifndef KDE_NO_DEPRECATED
1101 void KUrlNavigator::savePosition(
int x,
int y)
1104 d->m_history[d->m_historyIndex].pos =
QPoint(x, y);
1108 void KUrlNavigator::keyPressEvent(QKeyEvent* event)
1113 QWidget::keyPressEvent(event);
1117 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
1119 QWidget::keyReleaseEvent(event);
1122 void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
1124 if (event->button() == Qt::MidButton) {
1125 const QRect bounds = d->m_toggleEditableMode->geometry();
1126 if (bounds.contains(event->pos())) {
1130 QClipboard* clipboard = QApplication::clipboard();
1131 const QMimeData* mimeData = clipboard->mimeData();
1132 if (mimeData->hasText()) {
1133 const QString text = mimeData->text();
1138 QWidget::mouseReleaseEvent(event);
1141 void KUrlNavigator::resizeEvent(QResizeEvent* event)
1143 QTimer::singleShot(0,
this, SLOT(updateButtonVisibility()));
1144 QWidget::resizeEvent(event);
1147 void KUrlNavigator::wheelEvent(QWheelEvent* event)
1150 QWidget::wheelEvent(event);
1153 bool KUrlNavigator::eventFilter(
QObject* watched, QEvent* event)
1155 switch (event->type()) {
1156 case QEvent::FocusIn:
1157 if (watched == d->m_pathBox) {
1166 case QEvent::FocusOut:
1176 return QWidget::eventFilter(watched, event);
1181 return d->m_history.count();
1186 return d->m_historyIndex;
1191 return d->m_pathBox;
1196 d->m_customProtocols = protocols;
1197 d->m_protocols->setCustomProtocols(d->m_customProtocols);
1202 return d->m_customProtocols;
1205 #ifndef KDE_NO_DEPRECATED
1206 const KUrl& KUrlNavigator::url()
const
1218 #ifndef KDE_NO_DEPRECATED
1219 KUrl KUrlNavigator::url(
int index)
const
1222 return d->buttonUrl(index);
1226 #ifndef KDE_NO_DEPRECATED
1227 KUrl KUrlNavigator::historyUrl(
int historyIndex)
const
1234 #ifndef KDE_NO_DEPRECATED
1235 const KUrl& KUrlNavigator::savedRootUrl()
const
1241 static KUrl rootUrl;
1242 rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1247 #ifndef KDE_NO_DEPRECATED
1248 QPoint KUrlNavigator::savedPosition()
const
1251 return d->m_history[d->m_historyIndex].pos;
1255 #ifndef KDE_NO_DEPRECATED
1263 #include "kurlnavigator.moc"