/***************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * Copyright (C) 2006 by Aaron J. Seigo * * Copyright (C) 2007 by Kevin Ottens * * Copyright (C) 2007 by Urs Wolfer * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *****************************************************************************/ #include "kurlnavigator.h" #include "kurlnavigatorplacesselector_p.h" #include "kurlnavigatorprotocolcombo_p.h" #include "kurlnavigatordropdownbutton_p.h" #include "kurlnavigatorbutton_p.h" #include "kurlnavigatortogglebutton_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDEPrivate; struct LocationData { KUrl url; QByteArray state; }; class KUrlNavigator::Private { public: Private(KUrlNavigator* q, KFilePlacesModel* placesModel); void initialize(const KUrl& url); void slotReturnPressed(); void slotProtocolChanged(const QString&); void openPathSelectorMenu(); /** * Appends the widget at the end of the URL navigator. It is assured * that the filler widget remains as last widget to fill the remaining * width. */ void appendWidget(QWidget* widget, int stretch = 0); /** * Switches the navigation bar between the breadcrumb view and the * traditional view (see setUrlEditable()) and is connected to the clicked signal * of the navigation bar button. */ void switchView(); /** Emits the signal urlsDropped(). */ void dropUrls(const KUrl& destination, QDropEvent* event); /** * Is invoked when a navigator button has been clicked. Changes the URL * of the navigator if the left mouse button has been used. If the middle * mouse button has been used, the signal tabRequested() will be emitted. */ void slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button); void openContextMenu(); void slotPathBoxChanged(const QString& text); void updateContent(); /** * Updates all buttons to have one button for each part of the * current URL. Existing buttons, which are available by m_navButtons, * are reused if possible. If the URL is longer, new buttons will be * created, if the URL is shorter, the remaining buttons will be deleted. * @param startIndex Start index of URL part (/), where the buttons * should be created for each following part. */ void updateButtons(int startIndex); /** * Updates the visibility state of all buttons describing the URL. If the * width of the URL navigator is too small, the buttons representing the upper * paths of the URL will be hidden and moved to a drop down menu. */ void updateButtonVisibility(); /** * @return Text for the first button of the URL navigator. */ QString firstButtonText() const; /** * Returns the URL that should be applied for the button with the index \a index. */ KUrl buttonUrl(int index) const; void switchToBreadcrumbMode(); /** * Deletes all URL navigator buttons. m_navButtons is * empty after this operation. */ void deleteButtons(); /** * Retrieves the place path for the current path. * E. g. for the path "fish://root@192.168.0.2/var/lib" the string * "fish://root@192.168.0.2" will be returned, which leads to the * navigation indication 'Custom Path > var > lib". For e. g. * "settings:///System/" the path "settings://" will be returned. */ QString retrievePlacePath() const; void removeTrailingSlash(QString& url) const; /** * Returns the current history index, if \a historyIndex is * smaller than 0. If \a historyIndex is greater or equal than * the number of available history items, the largest possible * history index is returned. For the other cases just \a historyIndex * is returned. */ int adjustedHistoryIndex(int historyIndex) const; bool m_editable : 1; bool m_active : 1; bool m_showPlacesSelector : 1; bool m_showFullPath : 1; int m_historyIndex; QHBoxLayout* m_layout; QList m_history; KUrlNavigatorPlacesSelector* m_placesSelector; KUrlComboBox* m_pathBox; KUrlNavigatorProtocolCombo* m_protocols; KUrlNavigatorDropDownButton* m_dropDownButton; QList m_navButtons; KUrlNavigatorButtonBase* m_toggleEditableMode; KUrl m_homeUrl; QStringList m_customProtocols; KUrlNavigator* q; }; KUrlNavigator::Private::Private(KUrlNavigator* q, KFilePlacesModel* placesModel) : m_editable(false), m_active(true), m_showPlacesSelector(placesModel != 0), m_showFullPath(false), m_historyIndex(0), m_layout(new QHBoxLayout), m_placesSelector(0), m_pathBox(0), m_protocols(0), m_dropDownButton(0), m_navButtons(), m_toggleEditableMode(0), m_homeUrl(), m_customProtocols(QStringList()), q(q) { m_layout->setSpacing(0); m_layout->setMargin(0); // initialize the places selector q->setAutoFillBackground(false); if (placesModel != 0) { m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel); connect(m_placesSelector, SIGNAL(placeActivated(KUrl)), q, SLOT(setLocationUrl(KUrl))); connect(placesModel, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(updateContent())); connect(placesModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(updateContent())); connect(placesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(updateContent())); } // create protocol combo m_protocols = new KUrlNavigatorProtocolCombo(QString(), q); connect(m_protocols, SIGNAL(activated(QString)), q, SLOT(slotProtocolChanged(QString))); // create drop down button for accessing all paths of the URL m_dropDownButton = new KUrlNavigatorDropDownButton(q); m_dropDownButton->setForegroundRole(QPalette::WindowText); m_dropDownButton->installEventFilter(q); connect(m_dropDownButton, SIGNAL(clicked()), q, SLOT(openPathSelectorMenu())); // initialize the path box of the traditional view m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q); m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); m_pathBox->installEventFilter(q); KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion); m_pathBox->setCompletionObject(kurlCompletion); m_pathBox->setAutoDeleteCompletionObject(true); connect(m_pathBox, SIGNAL(returnPressed()), q, SLOT(slotReturnPressed())); connect(m_pathBox, SIGNAL(urlActivated(KUrl)), q, SLOT(setLocationUrl(KUrl))); connect(m_pathBox, SIGNAL(editTextChanged(QString)), q, SLOT(slotPathBoxChanged(QString))); // create toggle button which allows to switch between // the breadcrumb and traditional view m_toggleEditableMode = new KUrlNavigatorToggleButton(q); m_toggleEditableMode->installEventFilter(q); m_toggleEditableMode->setMinimumWidth(20); connect(m_toggleEditableMode, SIGNAL(clicked()), q, SLOT(switchView())); if (m_placesSelector != 0) { m_layout->addWidget(m_placesSelector); } m_layout->addWidget(m_protocols); m_layout->addWidget(m_dropDownButton); m_layout->addWidget(m_pathBox, 1); m_layout->addWidget(m_toggleEditableMode); q->setContextMenuPolicy(Qt::CustomContextMenu); connect(q, SIGNAL(customContextMenuRequested(QPoint)), q, SLOT(openContextMenu())); } void KUrlNavigator::Private::initialize(const KUrl& url) { LocationData data; data.url = url; m_history.prepend(data); q->setLayoutDirection(Qt::LeftToRight); const int minHeight = m_pathBox->sizeHint().height(); q->setMinimumHeight(minHeight); q->setLayout(m_layout); q->setMinimumWidth(100); updateContent(); } void KUrlNavigator::Private::appendWidget(QWidget* widget, int stretch) { m_layout->insertWidget(m_layout->count() - 1, widget, stretch); } void KUrlNavigator::Private::slotReturnPressed() { // Parts of the following code have been taken // from the class KateFileSelector located in // kate/app/katefileselector.hpp of Kate. // Copyright (C) 2001 Christoph Cullmann // Copyright (C) 2001 Joseph Wenninger // Copyright (C) 2001 Anders Lund const KUrl typedUrl = q->uncommittedUrl(); QStringList urls = m_pathBox->urls(); urls.removeAll(typedUrl.url()); urls.prepend(typedUrl.url()); m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom); q->setLocationUrl(typedUrl); // The URL might have been adjusted by KUrlNavigator::setUrl(), hence // synchronize the result in the path box. const KUrl currentUrl = q->locationUrl(); m_pathBox->setUrl(currentUrl); emit q->returnPressed(); if (QApplication::keyboardModifiers() & Qt::ControlModifier) { // Pressing Ctrl+Return automatically switches back to the breadcrumb mode. // The switch must be done asynchronously, as we are in the context of the // editor. QMetaObject::invokeMethod(q, "switchToBreadcrumbMode", Qt::QueuedConnection); } } void KUrlNavigator::Private::slotProtocolChanged(const QString& protocol) { Q_ASSERT(m_editable); KUrl url; url.setScheme(protocol); url.setPath((protocol == QLatin1String("file")) ? QLatin1String("/") : QLatin1String("//")); m_pathBox->setEditUrl(url); } void KUrlNavigator::Private::openPathSelectorMenu() { if (m_navButtons.count() <= 0) { return; } const KUrl firstVisibleUrl = m_navButtons.first()->url(); QString spacer; QPointer popup = new KMenu(q); popup->setLayoutDirection(Qt::LeftToRight); const QString placePath = retrievePlacePath(); int idx = placePath.count(QLatin1Char('/')); // idx points to the first directory // after the place path const QString path = m_history[m_historyIndex].url.pathOrUrl(); QString dirName = path.section(QLatin1Char('/'), idx, idx); if (dirName.isEmpty()) { dirName = QLatin1Char('/'); } do { const QString text = spacer + dirName; QAction* action = new QAction(text, popup); const KUrl currentUrl = buttonUrl(idx); if (currentUrl == firstVisibleUrl) { popup->addSeparator(); } action->setData(QVariant(currentUrl.prettyUrl())); popup->addAction(action); ++idx; spacer.append(" "); dirName = path.section('/', idx, idx); } while (!dirName.isEmpty()); const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight()); const QAction* activatedAction = popup->exec(pos); if (activatedAction != 0) { const KUrl url = KUrl(activatedAction->data().toString()); q->setLocationUrl(url); } // Delete the menu, unless it has been deleted in its own nested event loop already. if (popup) { popup->deleteLater(); } } void KUrlNavigator::Private::switchView() { m_toggleEditableMode->setFocus(); m_editable = !m_editable; m_toggleEditableMode->setChecked(m_editable); updateContent(); if (q->isUrlEditable()) { m_pathBox->setFocus(); } emit q->requestActivation(); emit q->editableStateChanged(m_editable); } void KUrlNavigator::Private::dropUrls(const KUrl& destination, QDropEvent* event) { const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); if (!urls.isEmpty()) { emit q->urlsDropped(destination, event); } } void KUrlNavigator::Private::slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button) { if (button & Qt::LeftButton) { q->setLocationUrl(url); } else if (button & Qt::MiddleButton) { emit q->tabRequested(url); } } void KUrlNavigator::Private::openContextMenu() { q->setActive(true); QPointer popup = new KMenu(q); // provide 'Copy' action, which copies the current URL of // the URL navigator into the clipboard QAction* copyAction = popup->addAction(KIcon("edit-copy"), i18n("Copy")); // provide 'Paste' action, which copies the current clipboard text // into the URL navigator QAction* pasteAction = popup->addAction(KIcon("edit-paste"), i18n("Paste")); QClipboard* clipboard = QApplication::clipboard(); pasteAction->setEnabled(!clipboard->text().isEmpty()); popup->addSeparator(); // provide radiobuttons for toggling between the edit and the navigation mode QAction* editAction = popup->addAction(i18n("Edit")); editAction->setCheckable(true); QAction* navigateAction = popup->addAction(i18n("Navigate")); navigateAction->setCheckable(true); QActionGroup* modeGroup = new QActionGroup(popup); modeGroup->addAction(editAction); modeGroup->addAction(navigateAction); if (q->isUrlEditable()) { editAction->setChecked(true); } else { navigateAction->setChecked(true); } popup->addSeparator(); // allow showing of the full path QAction* showFullPathAction = popup->addAction(i18n("Show Full Path")); showFullPathAction->setCheckable(true); showFullPathAction->setChecked(q->showFullPath()); QAction* activatedAction = popup->exec(QCursor::pos()); if (activatedAction == copyAction) { QMimeData* mimeData = new QMimeData(); mimeData->setText(q->locationUrl().pathOrUrl()); clipboard->setMimeData(mimeData); } else if (activatedAction == pasteAction) { q->setLocationUrl(KUrl(clipboard->text())); } else if (activatedAction == editAction) { q->setUrlEditable(true); } else if (activatedAction == navigateAction) { q->setUrlEditable(false); } else if (activatedAction == showFullPathAction) { q->setShowFullPath(showFullPathAction->isChecked()); } // Delete the menu, unless it has been deleted in its own nested event loop already. if (popup) { popup->deleteLater(); } } void KUrlNavigator::Private::slotPathBoxChanged(const QString& text) { if (text.isEmpty()) { const QString protocol = q->locationUrl().protocol(); m_protocols->setProtocol(protocol); m_protocols->show(); } else { m_protocols->hide(); } } void KUrlNavigator::Private::updateContent() { const KUrl currentUrl = q->locationUrl(); if (m_placesSelector != 0) { m_placesSelector->updateSelection(currentUrl); } if (m_editable) { m_protocols->hide(); m_dropDownButton->hide(); deleteButtons(); m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); m_pathBox->show(); m_pathBox->setUrl(currentUrl); } else { m_pathBox->hide(); m_protocols->hide(); m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // Calculate the start index for the directories that should be shown as buttons // and create the buttons KUrl placeUrl; if ((m_placesSelector != 0) && !m_showFullPath) { placeUrl = m_placesSelector->selectedPlaceUrl(); } QString placePath = placeUrl.isValid() ? placeUrl.pathOrUrl() : retrievePlacePath(); removeTrailingSlash(placePath); const int startIndex = placePath.count('/'); updateButtons(startIndex); } } void KUrlNavigator::Private::updateButtons(int startIndex) { KUrl currentUrl = q->locationUrl(); const QString path = currentUrl.pathOrUrl(); bool createButton = false; const int oldButtonCount = m_navButtons.count(); int idx = startIndex; bool hasNext = true; do { createButton = (idx - startIndex >= oldButtonCount); const bool isFirstButton = (idx == startIndex); const QString dirName = path.section(QLatin1Char('/'), idx, idx); hasNext = isFirstButton || !dirName.isEmpty(); if (hasNext) { KUrlNavigatorButton* button = 0; if (createButton) { button = new KUrlNavigatorButton(buttonUrl(idx), q); button->installEventFilter(q); button->setForegroundRole(QPalette::WindowText); connect(button, SIGNAL(urlsDropped(KUrl,QDropEvent*)), q, SLOT(dropUrls(KUrl,QDropEvent*))); connect(button, SIGNAL(clicked(KUrl,Qt::MouseButton)), q, SLOT(slotNavigatorButtonClicked(KUrl,Qt::MouseButton))); connect(button, SIGNAL(finishedTextResolving()), q, SLOT(updateButtonVisibility())); appendWidget(button); } else { button = m_navButtons[idx - startIndex]; button->setUrl(buttonUrl(idx)); } if (isFirstButton) { button->setText(firstButtonText()); } button->setActive(q->isActive()); if (createButton) { if (!isFirstButton) { setTabOrder(m_navButtons.last(), button); } m_navButtons.append(button); } ++idx; button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx)); } } while (hasNext); // delete buttons which are not used anymore const int newButtonCount = idx - startIndex; if (newButtonCount < oldButtonCount) { const QList::iterator itBegin = m_navButtons.begin() + newButtonCount; const QList::iterator itEnd = m_navButtons.end(); QList::iterator it = itBegin; while (it != itEnd) { (*it)->hide(); (*it)->deleteLater(); ++it; } m_navButtons.erase(itBegin, itEnd); } setTabOrder(m_dropDownButton, m_navButtons.first()); setTabOrder(m_navButtons.last(), m_toggleEditableMode); updateButtonVisibility(); } void KUrlNavigator::Private::updateButtonVisibility() { if (m_editable) { return; } const int buttonsCount = m_navButtons.count(); if (buttonsCount == 0) { m_dropDownButton->hide(); return; } // Subtract all widgets from the available width, that must be shown anyway int availableWidth = q->width() - m_toggleEditableMode->minimumWidth(); if ((m_placesSelector != 0) && m_placesSelector->isVisible()) { availableWidth -= m_placesSelector->width(); } if ((m_protocols != 0) && m_protocols->isVisible()) { availableWidth -= m_protocols->width(); } // Check whether buttons must be hidden at all... int requiredButtonWidth = 0; foreach (const KUrlNavigatorButton* button, m_navButtons) { requiredButtonWidth += button->minimumWidth(); } if (requiredButtonWidth > availableWidth) { // At least one button must be hidden. This implies that the // drop-down button must get visible, which again decreases the // available width. availableWidth -= m_dropDownButton->width(); } // Hide buttons... QList::const_iterator it = m_navButtons.constEnd(); const QList::const_iterator itBegin = m_navButtons.constBegin(); bool isLastButton = true; bool hasHiddenButtons = false; QList buttonsToShow; while (it != itBegin) { --it; KUrlNavigatorButton* button = (*it); availableWidth -= button->minimumWidth(); if ((availableWidth <= 0) && !isLastButton) { button->hide(); hasHiddenButtons = true; } else { // Don't show the button immediately, as setActive() // might change the size and a relayout gets triggered // after showing the button. So the showing of all buttons // is postponed until all buttons have the correct // activation state. buttonsToShow.append(button); } isLastButton = false; } // All buttons have the correct activation state and // can be shown now foreach (KUrlNavigatorButton* button, buttonsToShow) { button->show(); } if (hasHiddenButtons) { m_dropDownButton->show(); } else { // Check whether going upwards is possible. If this is the case, show the drop-down button. KUrl url = m_navButtons.front()->url(); url.adjustPath(KUrl::AddTrailingSlash); const bool visible = !url.equals(url.upUrl()); m_dropDownButton->setVisible(visible); } } QString KUrlNavigator::Private::firstButtonText() const { QString text; // The first URL navigator button should get the name of the // place instead of the directory name if ((m_placesSelector != 0) && !m_showFullPath) { const KUrl placeUrl = m_placesSelector->selectedPlaceUrl(); text = m_placesSelector->selectedPlaceText(); } if (text.isEmpty()) { const KUrl currentUrl = q->locationUrl(); if (currentUrl.isLocalFile()) { text = m_showFullPath ? QLatin1String("/") : i18n("Custom Path"); } else { text = currentUrl.protocol() + QLatin1Char(':'); if (!currentUrl.host().isEmpty()) { text += QLatin1Char(' ') + currentUrl.host(); } } } return text; } KUrl KUrlNavigator::Private::buttonUrl(int index) const { if (index < 0) { index = 0; } // Keep scheme, hostname etc. as this is needed for e. g. browsing // FTP directories const KUrl currentUrl = q->locationUrl(); KUrl newUrl = currentUrl; newUrl.setPath(QString()); QString pathOrUrl = currentUrl.pathOrUrl(); if (!pathOrUrl.isEmpty()) { if (index == 0) { // prevent the last "/" from being stripped // or we end up with an empty path pathOrUrl = QLatin1String("/"); } else { pathOrUrl = pathOrUrl.section('/', 0, index); } } newUrl.setPath(KUrl(pathOrUrl).path()); return newUrl; } void KUrlNavigator::Private::switchToBreadcrumbMode() { q->setUrlEditable(false); } void KUrlNavigator::Private::deleteButtons() { foreach (KUrlNavigatorButton* button, m_navButtons) { button->hide(); button->deleteLater(); } m_navButtons.clear(); } QString KUrlNavigator::Private::retrievePlacePath() const { const KUrl currentUrl = q->locationUrl(); const QString path = currentUrl.pathOrUrl(); int idx = path.indexOf(QLatin1String("///")); if (idx >= 0) { idx += 3; } else { idx = path.indexOf(QLatin1String("//")); idx = path.indexOf(QLatin1Char('/'), (idx < 0) ? 0 : idx + 2); } QString placePath = (idx < 0) ? path : path.left(idx); removeTrailingSlash(placePath); return placePath; } void KUrlNavigator::Private::removeTrailingSlash(QString& url) const { const int length = url.length(); if ((length > 0) && (url.at(length - 1) == QChar('/'))) { url.remove(length - 1, 1); } } int KUrlNavigator::Private::adjustedHistoryIndex(int historyIndex) const { if (historyIndex < 0) { historyIndex = m_historyIndex; } else if (historyIndex >= m_history.size()) { historyIndex = m_history.size() - 1; Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0 } return historyIndex; } // ------------------------------------------------------------------------------------------------ KUrlNavigator::KUrlNavigator(QWidget* parent) : QWidget(parent), d(new Private(this, 0)) { d->initialize(KUrl()); } KUrlNavigator::KUrlNavigator(KFilePlacesModel* placesModel, const KUrl& url, QWidget* parent) : QWidget(parent), d(new Private(this, placesModel)) { d->initialize(url); } KUrlNavigator::~KUrlNavigator() { delete d; } KUrl KUrlNavigator::locationUrl(int historyIndex) const { historyIndex = d->adjustedHistoryIndex(historyIndex); return d->m_history[historyIndex].url; } void KUrlNavigator::saveLocationState(const QByteArray& state) { d->m_history[d->m_historyIndex].state = state; } QByteArray KUrlNavigator::locationState(int historyIndex) const { historyIndex = d->adjustedHistoryIndex(historyIndex); return d->m_history[historyIndex].state; } bool KUrlNavigator::goBack() { const int count = d->m_history.count(); if (d->m_historyIndex < count - 1) { const KUrl newUrl = locationUrl(d->m_historyIndex + 1); emit urlAboutToBeChanged(newUrl); ++d->m_historyIndex; d->updateContent(); emit historyChanged(); emit urlChanged(locationUrl()); return true; } return false; } bool KUrlNavigator::goForward() { if (d->m_historyIndex > 0) { const KUrl newUrl = locationUrl(d->m_historyIndex - 1); emit urlAboutToBeChanged(newUrl); --d->m_historyIndex; d->updateContent(); emit historyChanged(); emit urlChanged(locationUrl()); return true; } return false; } bool KUrlNavigator::goUp() { const KUrl currentUrl = locationUrl(); const KUrl upUrl = currentUrl.upUrl(); if (upUrl != currentUrl) { setLocationUrl(upUrl); return true; } return false; } void KUrlNavigator::goHome() { if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) { setLocationUrl(KUrl(QDir::homePath())); } else { setLocationUrl(d->m_homeUrl); } } void KUrlNavigator::setHomeUrl(const KUrl& url) { d->m_homeUrl = url; } KUrl KUrlNavigator::homeUrl() const { return d->m_homeUrl; } void KUrlNavigator::setUrlEditable(bool editable) { if (d->m_editable != editable) { d->switchView(); } } bool KUrlNavigator::isUrlEditable() const { return d->m_editable; } void KUrlNavigator::setShowFullPath(bool show) { if (d->m_showFullPath != show) { d->m_showFullPath = show; d->updateContent(); } } bool KUrlNavigator::showFullPath() const { return d->m_showFullPath; } void KUrlNavigator::setActive(bool active) { if (active != d->m_active) { d->m_active = active; d->m_dropDownButton->setActive(active); foreach(KUrlNavigatorButton* button, d->m_navButtons) { button->setActive(active); } update(); if (active) { emit activated(); } } } bool KUrlNavigator::isActive() const { return d->m_active; } void KUrlNavigator::setPlacesSelectorVisible(bool visible) { if (visible == d->m_showPlacesSelector) { return; } if (visible && (d->m_placesSelector == 0)) { // the places selector cannot get visible as no // places model is available return; } d->m_showPlacesSelector = visible; d->m_placesSelector->setVisible(visible); } bool KUrlNavigator::isPlacesSelectorVisible() const { return d->m_showPlacesSelector; } KUrl KUrlNavigator::uncommittedUrl() const { KUriFilterData filteredData(d->m_pathBox->currentText().trimmed()); filteredData.setCheckForExecutables(false); if (KUriFilter::self()->filterUri(filteredData, QStringList() << "kshorturifilter" << "kurisearchfilter")) { return filteredData.uri(); } else { return KUrl(filteredData.typedString()); } } void KUrlNavigator::setLocationUrl(const KUrl& newUrl) { if (newUrl == locationUrl()) { return; } KUrl url = newUrl; url.cleanPath(); // Check whether current history element has the same URL. // If this is the case, just ignore setting the URL. const LocationData& data = d->m_history[d->m_historyIndex]; const bool isUrlEqual = url.equals(locationUrl(), KUrl::RemoveTrailingSlash) || (!url.isValid() && url.equals(data.url, KUrl::RemoveTrailingSlash)); if (isUrlEqual) { return; } emit urlAboutToBeChanged(url); if (d->m_historyIndex > 0) { // If an URL is set when the history index is not at the end (= 0), // then clear all previous history elements so that a new history // tree is started from the current position. QList::iterator begin = d->m_history.begin(); QList::iterator end = begin + d->m_historyIndex; d->m_history.erase(begin, end); d->m_historyIndex = 0; } Q_ASSERT(d->m_historyIndex == 0); LocationData newData; newData.url = url; d->m_history.insert(0, newData); // Prevent an endless growing of the history: remembering // the last 100 Urls should be enough... const int historyMax = 100; if (d->m_history.size() > historyMax) { QList::iterator begin = d->m_history.begin() + historyMax; QList::iterator end = d->m_history.end(); d->m_history.erase(begin, end); } emit historyChanged(); emit urlChanged(url); d->updateContent(); requestActivation(); } void KUrlNavigator::requestActivation() { setActive(true); } void KUrlNavigator::setFocus() { if (isUrlEditable()) { d->m_pathBox->setFocus(); } else { QWidget::setFocus(); } } void KUrlNavigator::keyPressEvent(QKeyEvent* event) { if (isUrlEditable() && (event->key() == Qt::Key_Escape)) { setUrlEditable(false); } else { QWidget::keyPressEvent(event); } } void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::MiddleButton) { const QRect bounds = d->m_toggleEditableMode->geometry(); if (bounds.contains(event->pos())) { // The middle mouse button has been clicked above the // toggle-editable-mode-button. Paste the clipboard content // as location URL. QClipboard* clipboard = QApplication::clipboard(); const QMimeData* mimeData = clipboard->mimeData(); if (mimeData->hasText()) { const QString text = mimeData->text(); setLocationUrl(KUrl(text)); } } } QWidget::mouseReleaseEvent(event); } void KUrlNavigator::resizeEvent(QResizeEvent* event) { QTimer::singleShot(0, this, SLOT(updateButtonVisibility())); QWidget::resizeEvent(event); } void KUrlNavigator::wheelEvent(QWheelEvent* event) { setActive(true); QWidget::wheelEvent(event); } bool KUrlNavigator::eventFilter(QObject* watched, QEvent* event) { switch (event->type()) { case QEvent::FocusIn: if (watched == d->m_pathBox) { requestActivation(); setFocus(); } foreach (KUrlNavigatorButton* button, d->m_navButtons) { button->setShowMnemonic(true); } break; case QEvent::FocusOut: foreach (KUrlNavigatorButton* button, d->m_navButtons) { button->setShowMnemonic(false); } break; default: break; } return QWidget::eventFilter(watched, event); } int KUrlNavigator::historySize() const { return d->m_history.count(); } int KUrlNavigator::historyIndex() const { return d->m_historyIndex; } KUrlComboBox* KUrlNavigator::editor() const { return d->m_pathBox; } void KUrlNavigator::setCustomProtocols(const QStringList &protocols) { d->m_customProtocols = protocols; d->m_protocols->setCustomProtocols(d->m_customProtocols); } QStringList KUrlNavigator::customProtocols() const { return d->m_customProtocols; } #include "moc_kurlnavigator.cpp"