diff options
| author | Kevin Ottens <[email protected]> | 2007-04-02 19:20:07 +0000 |
|---|---|---|
| committer | Kevin Ottens <[email protected]> | 2007-04-02 19:20:07 +0000 |
| commit | 03713b0ab408a42a20c9ce6a51d069b3246d2502 (patch) | |
| tree | 1aff603918b502ae1927b74b5b668e4659268f51 /src/kurlnavigator.cpp | |
| parent | 78c312a0f33b15b34aed8ea7cf9e9c344dea1f87 (diff) | |
Rename all the URL navigator related classes to prepare their migration
in kdelibs.
svn path=/trunk/KDE/kdebase/apps/; revision=649514
Diffstat (limited to 'src/kurlnavigator.cpp')
| -rw-r--r-- | src/kurlnavigator.cpp | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/src/kurlnavigator.cpp b/src/kurlnavigator.cpp new file mode 100644 index 000000000..2d2cd8cf2 --- /dev/null +++ b/src/kurlnavigator.cpp @@ -0,0 +1,823 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz (<[email protected]>) * + * Copyright (C) 2006 by Aaron J. Seigo (<[email protected]>) * + * Copyright (C) 2006 by Patrice Tremblay * + * Copyright (C) 2007 by Kevin Ottens ([email protected]) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "kurlnavigator.h" + +#include "kfileplacesselector_p.h" +#include "kprotocolcombo_p.h" +#include "kurlnavigatorbutton_p.h" + +#include <assert.h> + +#include <kfileitem.h> +#include <kicon.h> +#include <klocale.h> +#include <kprotocolinfo.h> +#include <kurlcombobox.h> +#include <kurlcompletion.h> + +#include <QApplication> +#include <QClipboard> +#include <QDir> +#include <QHBoxLayout> +#include <QLabel> +#include <QLineEdit> +#include <QLinkedList> +#include <QMouseEvent> +#include <QToolButton> + +/** + * @brief Represents the history element of an URL. + * + * A history element contains the URL, the name of the current file + * (the 'current file' is the file where the cursor is located) and + * the x- and y-position of the content. + */ +class HistoryElem { +public: + HistoryElem(); + HistoryElem(const KUrl& url); + ~HistoryElem(); // non virtual + + const KUrl& url() const { return m_url; } + + void setCurrentFileName(const QString& name) { m_currentFileName = name; } + const QString& currentFileName() const { return m_currentFileName; } + + void setContentsX(int x) { m_contentsX = x; } + int contentsX() const { return m_contentsX; } + + void setContentsY(int y) { m_contentsY = y; } + int contentsY() const { return m_contentsY; } + +private: + KUrl m_url; + QString m_currentFileName; + int m_contentsX; + int m_contentsY; +}; + +HistoryElem::HistoryElem() : + m_url(), + m_currentFileName(), + m_contentsX(0), + m_contentsY(0) +{ +} + +HistoryElem::HistoryElem(const KUrl& url) : + m_url(url), + m_currentFileName(), + m_contentsX(0), + m_contentsY(0) +{ +} + +HistoryElem::~HistoryElem() +{ +} + +class KUrlNavigator::Private +{ +public: + Private(KUrlNavigator* q, KFilePlacesModel* placesModel); + + void slotReturnPressed(const QString&); + void slotRemoteHostActivated(); + void slotProtocolChanged(const QString&); + + /** + * 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); + + /** + * 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(); + + /** + * Updates the history element with the current file item + * and the contents position. + */ + void updateHistoryElem(); + void updateContent(); + + /** + * Updates all buttons to have one button for each part of the + * path \a path. Existing buttons, which are available by m_navButtons, + * are reused if possible. If the path is longer, new buttons will be + * created, if the path is shorter, the remaining buttons will be deleted. + * @param startIndex Start index of path part (/), where the buttons + * should be created for each following part. + */ + void updateButtons(const QString& path, int startIndex); + + /** + * Deletes all URL navigator buttons. m_navButtons is + * empty after this operation. + */ + void deleteButtons(); + + + bool m_active; + bool m_showHiddenFiles; + int m_historyIndex; + + QHBoxLayout* m_layout; + + QList<HistoryElem> m_history; + QToolButton* m_toggleButton; + KFilePlacesSelector* m_placesSelector; + KUrlComboBox* m_pathBox; + KProtocolCombo* m_protocols; + QLabel* m_protocolSeparator; + QLineEdit* m_host; + QLinkedList<KUrlNavigatorButton*> m_navButtons; + QWidget* m_filler; + QString m_homeUrl; + KUrlNavigator* q; +}; + + +KUrlNavigator::Private::Private(KUrlNavigator* q, KFilePlacesModel* placesModel) + : + m_active(true), + m_showHiddenFiles(false), + m_historyIndex(0), + m_layout(new QHBoxLayout), + m_protocols(0), + m_protocolSeparator(0), + m_host(0), + m_filler(0), + q(q) +{ + m_layout->setSpacing(0); + m_layout->setMargin(0); + + // initialize toggle button which switches between the breadcrumb view + // and the traditional view + m_toggleButton = new QToolButton(); + m_toggleButton->setCheckable(true); + m_toggleButton->setAutoRaise(true); + m_toggleButton->setIcon(KIcon("editinput")); // TODO: is just a placeholder icon (?) + m_toggleButton->setFocusPolicy(Qt::NoFocus); + m_toggleButton->setMinimumHeight(q->minimumHeight()); + connect(m_toggleButton, SIGNAL(clicked()), + q, SLOT(switchView())); + + // initialize the places selector + m_placesSelector = new KFilePlacesSelector(q, placesModel); + connect(m_placesSelector, SIGNAL(placeActivated(const KUrl&)), + q, SLOT(setUrl(const KUrl&))); + + // initialize the path box of the traditional view + m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q); + + KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion); + m_pathBox->setCompletionObject(kurlCompletion); + m_pathBox->setAutoDeleteCompletionObject(true); + + connect(m_pathBox, SIGNAL(returnPressed(QString)), + q, SLOT(slotReturnPressed(QString))); + connect(m_pathBox, SIGNAL(urlActivated(KUrl)), + q, SLOT(setUrl(KUrl))); + + // Append a filler widget at the end, which automatically resizes to the + // maximum available width. This assures that the URL navigator uses the + // whole width, so that the clipboard content can be dropped. + m_filler = new QWidget(); + m_filler->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + m_layout->addWidget(m_toggleButton); + m_layout->addWidget(m_placesSelector); + m_layout->addWidget(m_pathBox); + m_layout->addWidget(m_filler); +} + +void KUrlNavigator::Private::appendWidget(QWidget* widget) +{ + m_layout->insertWidget(m_layout->count() - 1, widget); +} + +void KUrlNavigator::Private::slotReturnPressed(const QString& text) +{ + // 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 <[email protected]> + // Copyright (C) 2001 Joseph Wenninger <[email protected]> + // Copyright (C) 2001 Anders Lund <[email protected]> + + KUrl typedUrl(text); + if (typedUrl.hasPass()) { + typedUrl.setPass(QString()); + } + + QStringList urls = m_pathBox->urls(); + urls.removeAll(typedUrl.url()); + urls.prepend(typedUrl.url()); + m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom); + + q->setUrl(typedUrl); + // The URL might have been adjusted by KUrlNavigator::setUrl(), hence + // synchronize the result in the path box. + m_pathBox->setUrl(q->url()); +} + +void KUrlNavigator::Private::slotRemoteHostActivated() +{ + KUrl u = q->url(); + + QString host = m_host->text(); + QString user; + + int marker = host.indexOf("@"); + if (marker != -1) + { + user = host.left(marker); + u.setUser(user); + host = host.right(host.length() - marker - 1); + } + + marker = host.indexOf("/"); + if (marker != -1) + { + u.setPath(host.right(host.length() - marker)); + host.truncate(marker); + } + else + { + u.setPath(""); + } + + if (m_protocols->currentProtocol() != u.protocol() || + host != u.host() || + user != u.user()) + { + u.setProtocol(m_protocols->currentProtocol()); + u.setHost(m_host->text()); + + //TODO: get rid of this HACK for file:///! + if (u.protocol() == "file") + { + u.setHost(""); + if (u.path().isEmpty()) + { + u.setPath("/"); + } + } + + q->setUrl(u); + } +} + +void KUrlNavigator::Private::slotProtocolChanged(const QString& protocol) +{ + KUrl url; + url.setProtocol(protocol); + //url.setPath(KProtocolInfo::protocolClass(protocol) == ":local" ? "/" : ""); + url.setPath("/"); + QLinkedList<KUrlNavigatorButton*>::const_iterator it = m_navButtons.begin(); + const QLinkedList<KUrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end(); + while (it != itEnd) { + (*it)->close(); + (*it)->deleteLater(); + ++it; + } + m_navButtons.clear(); + + if (KProtocolInfo::protocolClass(protocol) == ":local") { + q->setUrl(url); + } + else { + if (!m_host) { + m_protocolSeparator = new QLabel("://", q); + appendWidget(m_protocolSeparator); + m_host = new QLineEdit(q); + appendWidget(m_host); + + connect(m_host, SIGNAL(lostFocus()), + q, SLOT(slotRemoteHostActivated())); + connect(m_host, SIGNAL(returnPressed()), + q, SLOT(slotRemoteHostActivated())); + } + else { + m_host->setText(""); + } + m_protocolSeparator->show(); + m_host->show(); + m_host->setFocus(); + } +} + +#if 0 +void KUrlNavigator::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl) +{ +// kDebug() << "received redirection to " << newUrl << endl; +kDebug() << "received redirection from " << oldUrl << " to " << newUrl << endl; +/* UrlStack::iterator it = m_urls.find(oldUrl); + if (it != m_urls.end()) + { + m_urls.erase(++it, m_urls.end()); + } + + m_urls.append(newUrl);*/ +} +#endif + +void KUrlNavigator::Private::switchView() +{ + updateContent(); + if (q->isUrlEditable()) { + m_pathBox->setFocus(); + } else { + q->setUrl(m_pathBox->currentText()); + } + emit q->requestActivation(); +} + +void KUrlNavigator::Private::updateHistoryElem() +{ + assert(m_historyIndex >= 0); + const KFileItem* item = 0; // TODO: m_dolphinView->currentFileItem(); + if (item != 0) { + HistoryElem& hist = m_history[m_historyIndex]; + hist.setCurrentFileName(item->name()); + } +} + +void KUrlNavigator::Private::updateContent() +{ + m_placesSelector->updateSelection(q->url()); + + m_toggleButton->setToolTip(QString()); + QString path(q->url().pathOrUrl()); + + // TODO: prevent accessing the DolphinMainWindow out from this scope + //const QAction* action = dolphinView()->mainWindow()->actionCollection()->action("editable_location"); + // TODO: registry of default shortcuts + //QString shortcut = action? action->shortcut().toString() : "Ctrl+L"; + const QString shortcut = "Ctrl+L"; + + if (m_toggleButton->isChecked()) { + delete m_protocols; m_protocols = 0; + delete m_protocolSeparator; m_protocolSeparator = 0; + delete m_host; m_host = 0; + deleteButtons(); + m_filler->hide(); + + m_toggleButton->setToolTip(i18n("Browse (%1, Escape)", shortcut)); + + q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_pathBox->show(); + m_pathBox->setUrl(q->url()); + } + else { + m_toggleButton->setToolTip(i18n("Edit location (%1)", shortcut)); + + q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_pathBox->hide(); + m_filler->show(); + + // get the data from the currently selected place + KUrl placeUrl = m_placesSelector->selectedPlaceUrl(); + + QString placePath; + if (!placeUrl.isValid()) { + // No place is a part of the current Url. + // The following code tries to guess the place + // path. E. g. "fish://[email protected]/var/lib" writes + // "fish://[email protected]" to 'placePath', which leads to the + // navigation indication 'Custom Path > var > lib". + int idx = path.indexOf(QString("//")); + idx = path.indexOf("/", (idx < 0) ? 0 : idx + 2); + placePath = (idx < 0) ? path : path.left(idx); + } + else { + placePath = placeUrl.pathOrUrl(); + } + const uint len = placePath.length(); + + // calculate the start point for the URL navigator buttons by counting + // the slashs inside the place URL + int slashCount = 0; + for (uint i = 0; i < len; ++i) { + if (placePath.at(i) == QChar('/')) { + ++slashCount; + } + } + if ((len > 0) && placePath.at(len - 1) == QChar('/')) { + assert(slashCount > 0); + --slashCount; + } + + const KUrl currentUrl = q->url(); + if (!currentUrl.isLocalFile() && !placeUrl.isValid()) { + QString protocol = currentUrl.protocol(); + if (!m_protocols) { + deleteButtons(); + m_protocols = new KProtocolCombo(protocol, q); + appendWidget(m_protocols); + connect(m_protocols, SIGNAL(activated(QString)), + q, SLOT(slotProtocolChanged(QString))); + } + else { + m_protocols->setProtocol(protocol); + } + m_protocols->show(); + + if (KProtocolInfo::protocolClass(protocol) != ":local") { + QString hostText = currentUrl.host(); + + if (!currentUrl.user().isEmpty()) { + hostText = currentUrl.user() + '@' + hostText; + } + + if (!m_host) { + // ######### TODO: this code is duplicated from slotProtocolChanged! + m_protocolSeparator = new QLabel("://", q); + appendWidget(m_protocolSeparator); + m_host = new QLineEdit(hostText, q); + appendWidget(m_host); + + connect(m_host, SIGNAL(lostFocus()), + q, SLOT(slotRemoteHostActivated())); + connect(m_host, SIGNAL(returnPressed()), + q, SLOT(slotRemoteHostActivated())); + } + else { + m_host->setText(hostText); + } + m_protocolSeparator->show(); + m_host->show(); + } + else { + delete m_protocolSeparator; m_protocolSeparator = 0; + delete m_host; m_host = 0; + } + } + else if (m_protocols) { + m_protocols->hide(); + + if (m_host) { + m_protocolSeparator->hide(); + m_host->hide(); + } + } + + updateButtons(path, slashCount); + } +} + +void KUrlNavigator::Private::updateButtons(const QString& path, int startIndex) +{ + QLinkedList<KUrlNavigatorButton*>::iterator it = m_navButtons.begin(); + const QLinkedList<KUrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end(); + bool createButton = false; + const KUrl currentUrl = q->url(); + + int idx = startIndex; + bool hasNext = true; + do { + createButton = (it == itEnd); + + const QString dirName = path.section('/', idx, idx); + const bool isFirstButton = (idx == startIndex); + hasNext = isFirstButton || !dirName.isEmpty(); + if (hasNext) { + QString text; + if (isFirstButton) { + // the first URL navigator button should get the name of the + // place instead of the directory name + const KUrl placeUrl = m_placesSelector->selectedPlaceUrl(); + text = m_placesSelector->selectedPlaceText(); + if (text.isEmpty()) { + if (currentUrl.isLocalFile()) { + text = i18n("Custom Path"); + } + else { + ++idx; + continue; + } + } + } + + KUrlNavigatorButton* button = 0; + if (createButton) { + button = new KUrlNavigatorButton(idx, q); + appendWidget(button); + } + else { + button = *it; + button->setIndex(idx); + } + + if (isFirstButton) { + button->setText(text); + } + + if (createButton) { + button->show(); + m_navButtons.append(button); + } + else { + ++it; + } + ++idx; + } + } while (hasNext); + + // delete buttons which are not used anymore + QLinkedList<KUrlNavigatorButton*>::iterator itBegin = it; + while (it != itEnd) { + (*it)->close(); + (*it)->deleteLater(); + ++it; + } + m_navButtons.erase(itBegin, m_navButtons.end()); +} + +void KUrlNavigator::Private::deleteButtons() +{ + QLinkedList<KUrlNavigatorButton*>::iterator itBegin = m_navButtons.begin(); + QLinkedList<KUrlNavigatorButton*>::iterator itEnd = m_navButtons.end(); + QLinkedList<KUrlNavigatorButton*>::iterator it = itBegin; + while (it != itEnd) { + (*it)->close(); + (*it)->deleteLater(); + ++it; + } + m_navButtons.erase(itBegin, itEnd); +} + +//// + + +KUrlNavigator::KUrlNavigator(KFilePlacesModel* placesModel, + const KUrl& url, + QWidget* parent) : + QWidget(parent), + d( new Private(this, placesModel) ) +{ + d->m_history.prepend(HistoryElem(url)); + + QFontMetrics fontMetrics(font()); + setMinimumHeight(fontMetrics.height() + 10); + + setLayout(d->m_layout); + + d->updateContent(); +} + +KUrlNavigator::~KUrlNavigator() +{ + delete d; +} + +const KUrl& KUrlNavigator::url() const +{ + assert(!d->m_history.empty()); + return d->m_history[d->m_historyIndex].url(); +} + +KUrl KUrlNavigator::url(int index) const +{ + assert(index >= 0); + // keep scheme, hostname etc. maybe we will need this in the future + // for e.g. browsing ftp repositories. + KUrl newurl(url()); + newurl.setPath(QString()); + QString path(url().path()); + + if (!path.isEmpty()) { + if (index == 0) //prevent the last "/" from being stripped + path = "/"; //or we end up with an empty path + else + path = path.section('/', 0, index); + } + + newurl.setPath(path); + return newurl; +} + +QPoint KUrlNavigator::savedPosition() const +{ + const HistoryElem& histElem = d->m_history[d->m_historyIndex]; + return QPoint( histElem.contentsX(), histElem.contentsY() ); +} + +int KUrlNavigator::historySize() const +{ + return d->m_history.count(); +} + +void KUrlNavigator::goBack() +{ + d->updateHistoryElem(); + + const int count = d->m_history.count(); + if (d->m_historyIndex < count - 1) { + ++d->m_historyIndex; + d->updateContent(); + emit urlChanged(url()); + emit historyChanged(); + } +} + +void KUrlNavigator::goForward() +{ + if (d->m_historyIndex > 0) { + --d->m_historyIndex; + d->updateContent(); + emit urlChanged(url()); + emit historyChanged(); + } +} + +void KUrlNavigator::goUp() +{ + setUrl(url().upUrl()); +} + +void KUrlNavigator::goHome() +{ + if (d->m_homeUrl.isEmpty()) + setUrl(QDir::homePath()); + else + setUrl(d->m_homeUrl); +} + +bool KUrlNavigator::isUrlEditable() const +{ + return d->m_toggleButton->isChecked(); +} + +void KUrlNavigator::setUrlEditable(bool editable) +{ + if (isUrlEditable() != editable) { + d->m_toggleButton->toggle(); + d->switchView(); + } +} + +void KUrlNavigator::setActive(bool active) +{ + if (active != d->m_active) { + d->m_active = active; + update(); + if (active) { + emit activated(); + } + } +} + +void KUrlNavigator::setShowHiddenFiles( bool show ) +{ + d->m_showHiddenFiles = show; +} + +void KUrlNavigator::dropUrls(const KUrl::List& urls, + const KUrl& destination) +{ + emit urlsDropped(urls, destination); +} + +void KUrlNavigator::setUrl(const KUrl& url) +{ + QString urlStr(url.pathOrUrl()); + + // TODO: a patch has been submitted by Filip Brcic which adjusts + // the URL for tar and zip files. See https://bugs.kde.org/show_bug.cgi?id=142781 + // for details. The URL navigator part of the patch has not been committed yet, + // as the URL navigator will be subject of change and + // we might think of a more generic approach to check the protocol + MIME type for + // this use case. + + //kDebug() << "setUrl(" << url << ")" << endl; + if ( urlStr.length() > 0 && urlStr.at(0) == '~') { + // replace '~' by the home directory + urlStr.remove(0, 1); + urlStr.insert(0, QDir::homePath()); + } + + const KUrl transformedUrl(urlStr); + + if (d->m_historyIndex > 0) { + // Check whether the previous element of the history has the same Url. + // If yes, just go forward instead of inserting a duplicate history + // element. + HistoryElem& prevHistoryElem = d->m_history[d->m_historyIndex - 1]; + if (transformedUrl == prevHistoryElem.url()) { + goForward(); +// kDebug() << "goin' forward in history" << endl; + return; + } + } + + if (this->url() == transformedUrl) { + // don't insert duplicate history elements +// kDebug() << "current url == transformedUrl" << endl; + return; + } + + d->updateHistoryElem(); + d->m_history.insert(d->m_historyIndex, HistoryElem(transformedUrl)); + + d->updateContent(); + + emit urlChanged(transformedUrl); + emit historyChanged(); + + // Prevent an endless growing of the history: remembering + // the last 100 Urls should be enough... + if (d->m_historyIndex > 100) { + d->m_history.removeFirst(); + --d->m_historyIndex; + } + +/* kDebug() << "history starting ====================" << endl; + int i = 0; + for (QValueListIterator<KUrlNavigator::HistoryElem> it = d->m_history.begin(); + it != d->m_history.end(); + ++it, ++i) + { + kDebug() << i << ": " << (*it).url() << endl; + } + kDebug() << "history done ========================" << endl;*/ + + requestActivation(); +} + +void KUrlNavigator::requestActivation() +{ + setActive(true); +} + +void KUrlNavigator::storeContentsPosition(int x, int y) +{ + HistoryElem& hist = d->m_history[d->m_historyIndex]; + hist.setContentsX(x); + hist.setContentsY(y); +} + +void KUrlNavigator::keyReleaseEvent(QKeyEvent* event) +{ + QWidget::keyReleaseEvent(event); + if (isUrlEditable() && (event->key() == Qt::Key_Escape)) { + setUrlEditable(false); + } +} + +void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::MidButton) { + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if (mimeData->hasText()) { + const QString text = mimeData->text(); + setUrl(KUrl(text)); + } + } + QWidget::mouseReleaseEvent(event); +} + +bool KUrlNavigator::isActive() const +{ + return d->m_active; +} + +bool KUrlNavigator::showHiddenFiles() const +{ + return d->m_showHiddenFiles; +} + +void KUrlNavigator::setHomeUrl(const QString& homeUrl) +{ + d->m_homeUrl = homeUrl; +} + +#include "kurlnavigator.moc" |
