• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KFile

kurlnavigator.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>                      *
00003  * Copyright (C) 2006 by Aaron J. Seigo <aseigo@kde.org>                     *
00004  * Copyright (C) 2007 by Kevin Ottens <ervin@kde.org>                        *
00005  * Copyright (C) 2007 by Urs Wolfer <uwolfer @ kde.org>                      *
00006  *                                                                           *
00007  * This library is free software; you can redistribute it and/or             *
00008  * modify it under the terms of the GNU Library General Public               *
00009  * License version 2 as published by the Free Software Foundation.           *
00010  *                                                                           *
00011  * This library is distributed in the hope that it will be useful,           *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00014  * Library General Public License for more details.                          *
00015  *                                                                           *
00016  * You should have received a copy of the GNU Library General Public License *
00017  * along with this library; see the file COPYING.LIB.  If not, write to      *
00018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      *
00019  * Boston, MA 02110-1301, USA.                                               *
00020  *****************************************************************************/
00021 
00022 #include "kurlnavigator.h"
00023 
00024 #include "kfileplacesselector_p.h"
00025 #include "kprotocolcombo_p.h"
00026 #include "kurldropdownbutton_p.h"
00027 #include "kurlnavigatorbutton_p.h"
00028 #include "kurltogglebutton_p.h"
00029 
00030 #include <kfileitem.h>
00031 #include <kfileplacesmodel.h>
00032 #include <kglobalsettings.h>
00033 #include <kicon.h>
00034 #include <klineedit.h>
00035 #include <klocale.h>
00036 #include <kmenu.h>
00037 #include <kprotocolinfo.h>
00038 #include <kurlcombobox.h>
00039 #include <kurlcompletion.h>
00040 
00041 #include <QtCore/QDir>
00042 #include <QtCore/QLinkedList>
00043 #include <QtCore/QTimer>
00044 #include <QtGui/QApplication>
00045 #include <QtGui/QClipboard>
00046 #include <QtGui/QDropEvent>
00047 #include <QtGui/QKeyEvent>
00048 #include <QtGui/QBoxLayout>
00049 #include <QtGui/QLabel>
00050 
00057 class HistoryElem
00058 {
00059 public:
00060     HistoryElem();
00061     HistoryElem(const KUrl& url);
00062     ~HistoryElem(); // non virtual
00063 
00064     const KUrl& url() const;
00065 
00066     void setRootUrl(const KUrl& url);
00067     const KUrl& rootUrl() const;
00068 
00069     void setContentsX(int x);
00070     int contentsX() const;
00071 
00072     void setContentsY(int y);
00073     int contentsY() const;
00074 
00075 private:
00076     KUrl m_url;
00077     KUrl m_rootUrl;
00078     int m_contentsX;
00079     int m_contentsY;
00080 };
00081 
00082 HistoryElem::HistoryElem() :
00083     m_url(),
00084     m_rootUrl(),
00085     m_contentsX(0),
00086     m_contentsY(0)
00087 {
00088 }
00089 
00090 HistoryElem::HistoryElem(const KUrl& url) :
00091     m_url(url),
00092     m_rootUrl(),
00093     m_contentsX(0),
00094     m_contentsY(0)
00095 {
00096 }
00097 
00098 HistoryElem::~HistoryElem()
00099 {
00100 }
00101 
00102 inline const KUrl& HistoryElem::url() const
00103 {
00104     return m_url;
00105 }
00106 
00107 inline void HistoryElem::setRootUrl(const KUrl& url)
00108 {
00109     m_rootUrl = url;
00110 }
00111 
00112 inline const KUrl& HistoryElem::rootUrl() const
00113 {
00114     return m_rootUrl;
00115 }
00116 
00117 inline void HistoryElem::setContentsX(int x)
00118 {
00119     m_contentsX = x;
00120 }
00121 
00122 inline int HistoryElem::contentsX() const
00123 {
00124     return m_contentsX;
00125 }
00126 
00127 inline void HistoryElem::setContentsY(int y)
00128 {
00129     m_contentsY = y;
00130 }
00131 
00132 inline int HistoryElem::contentsY() const
00133 {
00134     return m_contentsY;
00135 }
00136 
00138 
00139 class KUrlNavigator::Private
00140 {
00141 public:
00142     Private(KUrlNavigator* q, KFilePlacesModel* placesModel);
00143 
00144     void slotReturnPressed(const QString&);
00145     void slotReturnPressed();
00146     void slotRemoteHostActivated();
00147     void slotProtocolChanged(const QString&);
00148     void openPathSelectorMenu();
00149 
00155     void appendWidget(QWidget* widget, int stretch = 0);
00156 
00162     void switchView();
00163 
00165     void dropUrls(const KUrl& destination, QDropEvent* event);
00166 
00167     void updateContent();
00168 
00177     void updateButtons(const QString& path, int startIndex);
00178 
00184     void updateButtonVisibility();
00185 
00186     void switchToBreadcrumbMode();
00187 
00192     void deleteButtons();
00193 
00201     QString retrievePlacePath(const QString& path) const;
00202 
00207     bool isCompressedPath(const KUrl& path) const;
00208     
00209     void removeTrailingSlash(QString& url);
00210 
00211     bool m_editable : 1;
00212     bool m_active : 1;
00213     bool m_showPlacesSelector : 1;
00214     bool m_showFullPath : 1;
00215     int m_historyIndex;
00216 
00217     QHBoxLayout* m_layout;
00218 
00219     QList<HistoryElem> m_history;
00220     KFilePlacesSelector* m_placesSelector;
00221     KUrlComboBox* m_pathBox;
00222     KProtocolCombo* m_protocols;
00223     KLineEdit* m_host;
00224     KUrlDropDownButton* m_dropDownButton;
00225     QLinkedList<KUrlNavigatorButton*> m_navButtons;
00226     KUrlButton* m_toggleEditableMode;
00227     QString m_homeUrl;
00228     QStringList m_customProtocols;
00229     KUrlNavigator* q;
00230 };
00231 
00232 
00233 KUrlNavigator::Private::Private(KUrlNavigator* q, KFilePlacesModel* placesModel) :
00234     m_editable(false),
00235     m_active(true),
00236     m_showPlacesSelector(placesModel != 0),
00237     m_showFullPath(false),
00238     m_historyIndex(0),
00239     m_layout(new QHBoxLayout),
00240     m_placesSelector(0),
00241     m_pathBox(0),
00242     m_protocols(0),
00243     m_host(0),
00244     m_dropDownButton(0),
00245     m_toggleEditableMode(0),
00246     m_customProtocols(QStringList()),
00247     q(q)
00248 {
00249     m_layout->setSpacing(0);
00250     m_layout->setMargin(0);
00251 
00252     // initialize the places selector
00253     q->setAutoFillBackground(false);
00254 
00255     if (placesModel != 0) {
00256         m_placesSelector = new KFilePlacesSelector(q, placesModel);
00257         connect(m_placesSelector, SIGNAL(placeActivated(const KUrl&)),
00258                 q, SLOT(setUrl(const KUrl&)));
00259 
00260         connect(placesModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
00261                 q, SLOT(updateContent()));
00262         connect(placesModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
00263                 q, SLOT(updateContent()));
00264         connect(placesModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
00265                 q, SLOT(updateContent()));
00266     }
00267 
00268     // create protocol combo
00269     m_protocols = new KProtocolCombo(QString(), q);
00270     connect(m_protocols, SIGNAL(activated(QString)),
00271             q, SLOT(slotProtocolChanged(QString)));
00272 
00273     // create editor for editing the host
00274     m_host = new KLineEdit(QString(), q);
00275     m_host->setClearButtonShown(true);
00276     connect(m_host, SIGNAL(editingFinished()),
00277             q, SLOT(slotRemoteHostActivated()));
00278     connect(m_host, SIGNAL(returnPressed()),
00279             q, SIGNAL(returnPressed()));
00280             
00281     // create drop down button for accessing all paths of the URL
00282     m_dropDownButton = new KUrlDropDownButton(q);
00283     connect(m_dropDownButton, SIGNAL(clicked()),
00284             q, SLOT(openPathSelectorMenu()));
00285 
00286     // initialize the path box of the traditional view
00287     m_pathBox = new KUrlComboBox(KUrlComboBox::Both, true, q);
00288     m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
00289     m_pathBox->installEventFilter(q);
00290 
00291     KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
00292     kurlCompletion->setIgnoreCase(true);
00293     m_pathBox->setCompletionObject(kurlCompletion);
00294     m_pathBox->setAutoDeleteCompletionObject(true);
00295 
00296     connect(m_pathBox, SIGNAL(returnPressed(QString)),
00297             q, SLOT(slotReturnPressed(QString)));
00298     connect(m_pathBox, SIGNAL(returnPressed()),
00299             q, SLOT(slotReturnPressed()));
00300     connect(m_pathBox, SIGNAL(urlActivated(KUrl)),
00301             q, SLOT(setUrl(KUrl)));
00302 
00303     m_toggleEditableMode = new KUrlToggleButton(q);
00304     m_toggleEditableMode->setMinimumWidth(20);
00305     connect(m_toggleEditableMode, SIGNAL(clicked()),
00306             q, SLOT(switchView()));
00307 
00308     if (m_placesSelector != 0) {
00309         m_layout->addWidget(m_placesSelector);
00310     }
00311     m_layout->addWidget(m_protocols);
00312     m_layout->addWidget(m_dropDownButton);
00313     m_layout->addWidget(m_host);
00314     m_layout->setStretchFactor(m_host, 1);
00315     m_layout->addWidget(m_pathBox, 1);
00316     m_layout->addWidget(m_toggleEditableMode);
00317 }
00318 
00319 void KUrlNavigator::Private::appendWidget(QWidget* widget, int stretch)
00320 {
00321     m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
00322 }
00323 
00324 void KUrlNavigator::Private::slotReturnPressed(const QString& text)
00325 {
00326     // Parts of the following code have been taken
00327     // from the class KateFileSelector located in
00328     // kate/app/katefileselector.hpp of Kate.
00329     // Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
00330     // Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
00331     // Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
00332 
00333     KUrl typedUrl(text);
00334     if (typedUrl.hasPass()) {
00335         typedUrl.setPass(QString());
00336     }
00337 
00338     QStringList urls = m_pathBox->urls();
00339     urls.removeAll(typedUrl.url());
00340     urls.prepend(typedUrl.url());
00341     m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
00342 
00343     q->setUrl(typedUrl);
00344     // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
00345     // synchronize the result in the path box.
00346     m_pathBox->setUrl(q->url());
00347 
00348     emit q->returnPressed();
00349 
00350     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
00351         // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
00352         // The switch must be done asynchronously, as we are in the context of the
00353         // editor.
00354         QMetaObject::invokeMethod(q, "switchToBreadcrumbMode", Qt::QueuedConnection);
00355     }
00356 }
00357 
00358 void KUrlNavigator::Private::slotReturnPressed()
00359 {
00360     const QString text = q->uncommittedUrl().prettyUrl();
00361     slotReturnPressed(text);
00362 }
00363 
00364 void KUrlNavigator::Private::slotRemoteHostActivated()
00365 {
00366     KUrl u = q->url();
00367 
00368     KUrl n(m_protocols->currentProtocol() + "://" + m_host->text());
00369 
00370     if (n.scheme() != u.scheme() ||
00371             n.host() != u.host() ||
00372             n.user() != u.user() ||
00373             n.port() != u.port()) {
00374         u.setScheme(n.scheme());
00375         u.setHost(n.host());
00376         u.setUser(n.user());
00377         u.setPort(n.port());
00378 
00379         //TODO: get rid of this HACK for file:///!
00380         if (u.scheme() == "file") {
00381             u.setHost("");
00382             if (u.path().isEmpty()) {
00383                 u.setPath("/");
00384             }
00385         }
00386 
00387         q->setUrl(u);
00388     }
00389 }
00390 
00391 void KUrlNavigator::Private::slotProtocolChanged(const QString& protocol)
00392 {
00393     KUrl url;
00394     url.setScheme(protocol);
00395     url.setPath("/");
00396     QLinkedList<KUrlNavigatorButton*>::const_iterator it = m_navButtons.begin();
00397     const QLinkedList<KUrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end();
00398     while (it != itEnd) {
00399         (*it)->hide();
00400         (*it)->deleteLater();
00401         ++it;
00402     }
00403     m_navButtons.clear();
00404 
00405     if (KProtocolInfo::protocolClass(protocol) == ":local") {
00406         q->setUrl(url);
00407     } else {
00408         m_host->setText(QString());
00409         m_host->show();
00410         m_host->setFocus();
00411     }
00412 }
00413 
00414 void KUrlNavigator::Private::openPathSelectorMenu()
00415 {
00416     if (m_navButtons.count() <= 0) {
00417         return;
00418     }
00419     
00420     const KUrl firstVisibleUrl = q->url(m_navButtons.first()->index());
00421     
00422     QString spacer;
00423     KMenu* popup = new KMenu(q); 
00424     popup->setLayoutDirection(Qt::LeftToRight);
00425     
00426     const QString path = q->url().pathOrUrl();
00427     QString placePath = retrievePlacePath(path);
00428     removeTrailingSlash(placePath);
00429     int idx = placePath.count('/'); // idx points to the first directory
00430                                     // after the place path
00431 
00432     QString dirName = path.section('/', idx, idx);
00433     if (dirName.isEmpty()) {
00434         dirName = QChar('/');
00435     }
00436     do {   
00437         const QString text = spacer + dirName;
00438 
00439         QAction* action = new QAction(text, popup);
00440         const KUrl currentUrl = q->url(idx);
00441         if (currentUrl == firstVisibleUrl) {
00442             popup->addSeparator();
00443         }
00444         action->setData(QVariant(currentUrl.prettyUrl()));
00445         popup->addAction(action);      
00446         
00447         ++idx;
00448         spacer.append("  ");
00449         dirName = path.section('/', idx, idx);
00450     } while (!dirName.isEmpty());
00451 
00452     const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
00453     const QAction* activatedAction = popup->exec(pos);
00454     if (activatedAction != 0) {
00455         const KUrl url = KUrl(activatedAction->data().toString());
00456         q->setUrl(url);
00457     }
00458 
00459     popup->deleteLater();
00460 }
00461 
00462 void KUrlNavigator::Private::switchView()
00463 {
00464     m_toggleEditableMode->setFocus();
00465     m_editable = !m_editable;
00466     m_toggleEditableMode->setChecked(m_editable);
00467     updateContent();
00468     if (q->isUrlEditable()) {
00469         m_pathBox->setFocus();
00470     }
00471 
00472     emit q->requestActivation();
00473     emit q->editableStateChanged(m_editable);
00474 }
00475 
00476 void KUrlNavigator::Private::dropUrls(const KUrl& destination, QDropEvent* event)
00477 {
00478     const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00479     if (!urls.isEmpty()) {
00480         emit q->urlsDropped(destination, event);
00481     
00482         // KDE5: remove, as the signal has been replaced by
00483         // urlsDropped(const KUrl& destination, QDropEvent* event)
00484         emit q->urlsDropped(urls, destination);
00485     }
00486 }
00487 
00488 void KUrlNavigator::Private::updateContent()
00489 {
00490     if (m_placesSelector != 0) {
00491         m_placesSelector->updateSelection(q->url());
00492     }
00493 
00494     if (m_editable) {
00495         m_protocols->hide();
00496         m_host->hide();
00497         m_dropDownButton->hide();
00498         
00499         deleteButtons();
00500         m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
00501         q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
00502         
00503         m_pathBox->show();
00504         m_pathBox->setUrl(q->url());
00505     } else {
00506         m_dropDownButton->setVisible(!m_showFullPath);
00507         m_pathBox->hide();
00508 
00509         QString path = q->url().pathOrUrl();
00510         removeTrailingSlash(path);
00511 
00512         m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
00513         q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
00514 
00515         // get the data from the currently selected place
00516         KUrl placeUrl;
00517         if ((m_placesSelector != 0) && !m_showFullPath) {
00518             placeUrl = m_placesSelector->selectedPlaceUrl();
00519         }
00520 
00521         QString placePath = placeUrl.isValid() ? placeUrl.pathOrUrl() : retrievePlacePath(path);
00522         removeTrailingSlash(placePath);
00523 
00524         // calculate the start point for the URL navigator buttons by counting
00525         // the slashs inside the place URL
00526         const int slashCount = placePath.count('/');
00527 
00528         const KUrl currentUrl = q->url();
00529         if (currentUrl.isLocalFile() || placeUrl.isValid()) {
00530             m_protocols->hide();
00531             m_host->hide();
00532         } else {
00533             // The URL is invalid or is a non local file. In this
00534             // case the protocol combo is shown. 
00535             const QString protocol = currentUrl.scheme();
00536             m_protocols->setProtocol(protocol);
00537             m_protocols->show();
00538             
00539             // set the text for the host widget
00540             QString hostText = currentUrl.host();
00541             if (!currentUrl.user().isEmpty()) {
00542                 hostText = currentUrl.user() + '@' + hostText;
00543             }
00544             if (currentUrl.port() != -1) {
00545                 hostText = hostText + ':' + QString::number(currentUrl.port());
00546             }
00547             m_host->setText(hostText);
00548             m_host->setVisible((placePath == path) &&
00549                                (KProtocolInfo::protocolClass(protocol) != ":local"));
00550         }
00551         
00552         updateButtons(path, slashCount);
00553     }
00554 }
00555 
00556 void KUrlNavigator::Private::updateButtons(const QString& path, int startIndex)
00557 {
00558     QLinkedList<KUrlNavigatorButton*>::iterator it = m_navButtons.begin();
00559     const QLinkedList<KUrlNavigatorButton*>::const_iterator itEnd = m_navButtons.end();
00560     bool createButton = false;
00561     const KUrl currentUrl = q->url();
00562 
00563     int idx = startIndex;
00564     bool hasNext = true;
00565     do {
00566         createButton = (it == itEnd);
00567 
00568         const QString dirName = path.section('/', idx, idx);
00569         const bool isFirstButton = (idx == startIndex);
00570         hasNext = isFirstButton || !dirName.isEmpty();
00571         if (hasNext) {
00572             QString text;
00573             if (isFirstButton) {
00574                 // the first URL navigator button should get the name of the
00575                 // place instead of the directory name
00576                 if ((m_placesSelector != 0) && !m_showFullPath) {
00577                     const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
00578                     text = m_placesSelector->selectedPlaceText();
00579                 }
00580                 if (text.isEmpty()) {
00581                     if (currentUrl.isLocalFile()) {
00582                         text = m_showFullPath ? "/" : i18n("Custom Path");
00583                     } else if (!m_host->isVisible() && !m_host->text().isEmpty()) {
00584                         text = m_host->text();
00585                     } else {
00586                         // The host is already displayed by the m_host widget,
00587                         // no button may be added for this index.
00588                         ++idx;
00589                         continue;
00590                     }
00591                 }
00592             }
00593 
00594             KUrlNavigatorButton* button = 0;
00595             if (createButton) {
00596                 button = new KUrlNavigatorButton(idx, q);
00597                 connect(button, SIGNAL(urlsDropped(const KUrl&, QDropEvent*)),
00598                         q, SLOT(dropUrls(const KUrl&, QDropEvent*)));
00599                 appendWidget(button);
00600             } else {
00601                 button = *it;
00602                 button->setIndex(idx);
00603             }
00604 
00605             if (isFirstButton) {
00606                 button->setText(text);
00607             }
00608 
00609             if (createButton) {
00610                 m_navButtons.append(button);
00611             } else {
00612                 ++it;
00613             }
00614             ++idx;
00615         }
00616     } while (hasNext);
00617     
00618     // delete buttons which are not used anymore
00619     QLinkedList<KUrlNavigatorButton*>::iterator itBegin = it;
00620     while (it != itEnd) {
00621         (*it)->hide();
00622         (*it)->deleteLater();
00623         ++it;
00624     }
00625     m_navButtons.erase(itBegin, m_navButtons.end());
00626     
00627     updateButtonVisibility();
00628 }
00629 
00630 void KUrlNavigator::Private::updateButtonVisibility()
00631 {
00632     if (m_editable) {
00633         return;
00634     }
00635 
00636     const int buttonsCount = m_navButtons.count();
00637     if (buttonsCount == 0) {
00638         m_dropDownButton->hide();
00639         return;
00640     }
00641 
00642     // subtract all widgets from the available width, that must be shown anyway
00643     int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
00644 
00645     if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
00646         availableWidth -= m_placesSelector->width();
00647     }
00648 
00649     if ((m_protocols != 0) && m_protocols->isVisible()) {
00650         availableWidth -= m_protocols->width();
00651     }
00652 
00653     if (m_host->isVisible()) {
00654         availableWidth -= m_host->width();
00655     }
00656 
00657     // check whether buttons must be hidden at all...
00658     int requiredButtonWidth = 0;
00659     foreach (KUrlNavigatorButton* button, m_navButtons) {
00660         requiredButtonWidth += button->minimumWidth();
00661     }
00662     if (requiredButtonWidth > availableWidth) {
00663         // At least one button must be hidden. This implies that the
00664         // drop-down button must get visible, which again decreases the
00665         // available width.
00666         availableWidth -= m_dropDownButton->width();
00667     }
00668 
00669     // hide buttons...
00670     QLinkedList<KUrlNavigatorButton*>::iterator it = m_navButtons.end();
00671     const QLinkedList<KUrlNavigatorButton*>::const_iterator itBegin = m_navButtons.begin();
00672     bool isLastButton = true;
00673     bool hasHiddenButtons = false;
00674     
00675     QLinkedList<KUrlNavigatorButton*> buttonsToShow;
00676     while (it != itBegin) {
00677         --it;
00678         KUrlNavigatorButton* button = (*it);
00679         availableWidth -= button->minimumWidth();
00680         if ((availableWidth <= 0) && !isLastButton) {
00681             button->hide();
00682             hasHiddenButtons = true;
00683         }
00684         else {
00685             button->setActive(isLastButton);
00686             // Don't show the button immediately, as setActive()
00687             // might change the size and a relayout gets triggered
00688             // after showing the button. So the showing of all buttons
00689             // is postponed until all buttons have the correct
00690             // activation state.
00691             buttonsToShow.append(button);
00692         }
00693         isLastButton = false;
00694     }
00695 
00696     // all buttons have the correct activation state and
00697     // can be shown now
00698     foreach (KUrlNavigatorButton* button, buttonsToShow) {
00699         button->show();
00700     }
00701 
00702     if (m_showFullPath) {
00703         m_dropDownButton->setVisible(hasHiddenButtons);
00704     }
00705 }
00706 
00707 void KUrlNavigator::Private::switchToBreadcrumbMode()
00708 {
00709     q->setUrlEditable(false);
00710 }
00711 
00712 void KUrlNavigator::Private::deleteButtons()
00713 {
00714     foreach (KUrlNavigatorButton* button, m_navButtons) {
00715         button->hide();
00716         button->deleteLater();
00717     }
00718     m_navButtons.clear();
00719 }
00720 
00721 QString KUrlNavigator::Private::retrievePlacePath(const QString& path) const
00722 {
00723     int idx = path.indexOf(QLatin1String("///"));
00724     if (idx >= 0) {
00725         idx += 3;
00726     } else {
00727         idx = path.indexOf(QLatin1String("//"));
00728         idx = path.indexOf(QLatin1Char('/'), (idx < 0) ? 0 : idx + 2);
00729     }
00730     return (idx < 0) ? path : path.left(idx);
00731 }
00732 
00733 bool KUrlNavigator::Private::isCompressedPath(const KUrl& url) const
00734 {
00735     const KMimeType::Ptr mime = KMimeType::findByPath(url.path(KUrl::RemoveTrailingSlash));
00736     // Note: this list of MIME types depends on the protocols implemented by kio_archive
00737     return  mime->is("application/x-compressed-tar") ||
00738             mime->is("application/x-bzip-compressed-tar") ||
00739             mime->is("application/x-tar") ||
00740             mime->is("application/x-tarz") ||
00741             mime->is("application/x-tzo") || // (not sure KTar supports those?)
00742             mime->is("application/zip") ||
00743             mime->is("application/x-archive");
00744 }
00745 
00746 void KUrlNavigator::Private::removeTrailingSlash(QString& url)
00747 {
00748     const int length = url.length();
00749     if ((length > 0) && (url.at(length - 1) == QChar('/'))) {
00750         url.remove(length -1, 1);
00751     }
00752 }
00753 
00755 
00756 KUrlNavigator::KUrlNavigator(KFilePlacesModel* placesModel,
00757                              const KUrl& url,
00758                              QWidget* parent) :
00759     QWidget(parent),
00760     d(new Private(this, placesModel))
00761 {
00762     d->m_history.prepend(HistoryElem(url));
00763     setLayoutDirection(Qt::LeftToRight);
00764 
00765     const QFont font = KGlobalSettings::generalFont();
00766     setFont(font);
00767 
00768     const int minHeight = d->m_pathBox->sizeHint().height();
00769     setMinimumHeight(minHeight);
00770 
00771     setLayout(d->m_layout);
00772     setMinimumWidth(100);
00773 
00774     d->updateContent();
00775 }
00776 
00777 KUrlNavigator::~KUrlNavigator()
00778 {
00779     delete d;
00780 }
00781 
00782 const KUrl& KUrlNavigator::url() const
00783 {
00784     Q_ASSERT(!d->m_history.empty());
00785     return d->m_history[d->m_historyIndex].url();
00786 }
00787 
00788 KUrl KUrlNavigator::uncommittedUrl() const
00789 {
00790     if (isUrlEditable()) {
00791         return KUrl(d->m_pathBox->currentText());
00792     } else {
00793         return KUrl(d->m_protocols->currentProtocol() + "://" + d->m_host->text());
00794     }
00795 }
00796 
00797 KUrl KUrlNavigator::url(int index) const
00798 {
00799     if (index < 0) {
00800         index = 0;
00801     }
00802 
00803     // keep scheme, hostname etc. as this is needed for e. g. browsing
00804     // FTP directories
00805     KUrl newUrl = url();
00806     newUrl.setPath(QString());
00807 
00808     QString pathOrUrl = url().pathOrUrl();
00809     if (!pathOrUrl.isEmpty()) {
00810         if (index == 0) {
00811             // prevent the last "/" from being stripped
00812             // or we end up with an empty path
00813 #ifdef Q_OS_WIN
00814             pathOrUrl = pathOrUrl.length() > 2 ? pathOrUrl.left(3) : QDir::rootPath();
00815 #else
00816             pathOrUrl = QLatin1String("/");
00817 #endif
00818         } else {
00819             pathOrUrl = pathOrUrl.section('/', 0, index);
00820         }
00821     }
00822 
00823     newUrl.setPath(KUrl(pathOrUrl).path());
00824     return newUrl;
00825 }
00826 
00827 bool KUrlNavigator::goBack()
00828 {
00829     const int count = d->m_history.count();
00830     if (d->m_historyIndex < count - 1) {
00831         ++d->m_historyIndex;
00832         d->updateContent();
00833         emit historyChanged();
00834         emit urlChanged(url());
00835         return true;
00836     }
00837 
00838     return false;
00839 }
00840 
00841 bool KUrlNavigator::goForward()
00842 {
00843     if (d->m_historyIndex > 0) {
00844         --d->m_historyIndex;
00845         d->updateContent();
00846         emit historyChanged();
00847         emit urlChanged(url());
00848         return true;
00849     }
00850 
00851     return false;
00852 }
00853 
00854 bool KUrlNavigator::goUp()
00855 {
00856     const KUrl& currentUrl = url();
00857     const KUrl upUrl = currentUrl.upUrl();
00858     if (upUrl != currentUrl) {
00859         setUrl(upUrl);
00860         return true;
00861     }
00862 
00863     return false;
00864 }
00865 
00866 void KUrlNavigator::goHome()
00867 {
00868     if (d->m_homeUrl.isEmpty()) {
00869         setUrl(QDir::homePath());
00870     } else {
00871         setUrl(d->m_homeUrl);
00872     }
00873 }
00874 
00875 void KUrlNavigator::setHomeUrl(const QString& homeUrl)
00876 {
00877     d->m_homeUrl = homeUrl;
00878 }
00879 
00880 void KUrlNavigator::setUrlEditable(bool editable)
00881 {
00882     if (d->m_editable != editable) {
00883         d->switchView();
00884     }
00885 }
00886 
00887 bool KUrlNavigator::isUrlEditable() const
00888 {
00889     return d->m_editable;
00890 }
00891 
00892 void KUrlNavigator::setShowFullPath(bool show)
00893 {
00894     if (d->m_showFullPath != show) {
00895         d->m_showFullPath = show;
00896         d->updateContent();
00897     }
00898 }
00899 
00900 bool KUrlNavigator::showFullPath() const
00901 {
00902     return d->m_showFullPath;
00903 }
00904 
00905 
00906 void KUrlNavigator::setActive(bool active)
00907 {
00908     if (active != d->m_active) {
00909         d->m_active = active;
00910         update();
00911         if (active) {
00912             emit activated();
00913         }
00914     }
00915 }
00916 
00917 bool KUrlNavigator::isActive() const
00918 {
00919     return d->m_active;
00920 }
00921 
00922 void KUrlNavigator::setPlacesSelectorVisible(bool visible)
00923 {
00924     if (visible == d->m_showPlacesSelector) {
00925         return;
00926     }
00927 
00928     if (visible  && (d->m_placesSelector == 0)) {
00929         // the places selector cannot get visible as no
00930         // places model is available
00931         return;
00932     }
00933 
00934     d->m_showPlacesSelector = visible;
00935     d->m_placesSelector->setVisible(visible);
00936 }
00937 
00938 bool KUrlNavigator::isPlacesSelectorVisible() const
00939 {
00940     return d->m_showPlacesSelector;
00941 }
00942 
00943 void KUrlNavigator::setUrl(const KUrl& url)
00944 {
00945     QString urlStr(KUrlCompletion::replacedPath(url.pathOrUrl(), true, true));
00946 
00947     if (urlStr.length() > 0 && urlStr.at(0) == '~') {
00948         // replace '~' by the home directory
00949         urlStr.remove(0, 1);
00950         urlStr.insert(0, QDir::homePath());
00951     }
00952 
00953     if ((url.protocol() == "tar") || (url.protocol() == "zip")) {
00954         // The URL represents a tar- or zip-file. Check whether
00955         // the URL is really part of the tar- or zip-file, otherwise
00956         // replace it by the local path again.
00957         bool insideCompressedPath = d->isCompressedPath(url);
00958         if (!insideCompressedPath) {
00959             KUrl prevUrl = url;
00960             KUrl parentUrl = url.upUrl();
00961             while (parentUrl != prevUrl) {
00962                 if (d->isCompressedPath(parentUrl)) {
00963                     insideCompressedPath = true;
00964                     break;
00965                 }
00966                 prevUrl = parentUrl;
00967                 parentUrl = parentUrl.upUrl();
00968             }
00969         }
00970         if (!insideCompressedPath) {
00971             // drop the tar: or zip: protocol since we are not
00972             // inside the compressed path anymore
00973             urlStr = url.path();
00974         }
00975     }
00976 
00977     const KUrl transformedUrl(urlStr);
00978 
00979     // Check whether current history element has the same URL.
00980     // If this is the case, just ignore setting the URL.
00981     const HistoryElem& historyElem = d->m_history[d->m_historyIndex];
00982     const bool isUrlEqual = transformedUrl.equals(historyElem.url(), KUrl::CompareWithoutTrailingSlash) ||
00983                             (!transformedUrl.isValid() && (urlStr == historyElem.url().url()));
00984     if (isUrlEqual) {
00985         return;
00986     }
00987 
00988     if (d->m_historyIndex > 0) {
00989         // If an URL is set when the history index is not at the end (= 0),
00990         // then clear all previous history elements so that a new history
00991         // tree is started from the current position.
00992         QList<HistoryElem>::iterator begin = d->m_history.begin();
00993         QList<HistoryElem>::iterator end = begin + d->m_historyIndex;
00994         d->m_history.erase(begin, end);
00995         d->m_historyIndex = 0;
00996     }
00997 
00998     Q_ASSERT(d->m_historyIndex == 0);
00999     d->m_history.insert(0, HistoryElem(transformedUrl));
01000 
01001     // Prevent an endless growing of the history: remembering
01002     // the last 100 Urls should be enough...
01003     const int historyMax = 100;
01004     if (d->m_history.size() > historyMax) {
01005         QList<HistoryElem>::iterator begin = d->m_history.begin() + historyMax;
01006         QList<HistoryElem>::iterator end = d->m_history.end();
01007         d->m_history.erase(begin, end);
01008     }
01009 
01010     emit historyChanged();
01011     emit urlChanged(transformedUrl);
01012 
01013     d->updateContent();
01014 
01015     requestActivation();
01016 }
01017 
01018 void KUrlNavigator::requestActivation()
01019 {
01020     setActive(true);
01021 }
01022 
01023 void KUrlNavigator::saveRootUrl(const KUrl& url)
01024 {
01025     HistoryElem& hist = d->m_history[d->m_historyIndex];
01026     hist.setRootUrl(url);
01027 }
01028 
01029 void KUrlNavigator::savePosition(int x, int y)
01030 {
01031     HistoryElem& hist = d->m_history[d->m_historyIndex];
01032     hist.setContentsX(x);
01033     hist.setContentsY(y);
01034 }
01035 
01036 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
01037 {
01038     QWidget::keyReleaseEvent(event);
01039     if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
01040         setUrlEditable(false);
01041     }
01042 }
01043 
01044 void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
01045 {
01046     if (event->button() == Qt::MidButton) {
01047         QClipboard* clipboard = QApplication::clipboard();
01048         const QMimeData* mimeData = clipboard->mimeData();
01049         if (mimeData->hasText()) {
01050             const QString text = mimeData->text();
01051             setUrl(KUrl(text));
01052         }
01053     }
01054     QWidget::mouseReleaseEvent(event);
01055 }
01056 
01057 void KUrlNavigator::resizeEvent(QResizeEvent* event)
01058 {
01059     QTimer::singleShot(0, this, SLOT(updateButtonVisibility()));
01060     QWidget::resizeEvent(event);
01061 }
01062 
01063 bool KUrlNavigator::eventFilter(QObject* watched, QEvent* event)
01064 {
01065     if ((watched == d->m_pathBox) && (event->type() == QEvent::FocusIn)) {
01066         requestActivation();
01067         setFocus();
01068     }
01069 
01070     return QWidget::eventFilter(watched, event);
01071 }
01072 
01073 int KUrlNavigator::historySize() const
01074 {
01075     return d->m_history.count();
01076 }
01077 
01078 int KUrlNavigator::historyIndex() const
01079 {
01080     return d->m_historyIndex;
01081 }
01082 
01083 const KUrl& KUrlNavigator::savedRootUrl() const
01084 {
01085     const HistoryElem& histElem = d->m_history[d->m_historyIndex];
01086     return histElem.rootUrl();
01087 }
01088 
01089 QPoint KUrlNavigator::savedPosition() const
01090 {
01091     const HistoryElem& histElem = d->m_history[d->m_historyIndex];
01092     return QPoint(histElem.contentsX(), histElem.contentsY());
01093 }
01094 
01095 KUrlComboBox* KUrlNavigator::editor() const
01096 {
01097     return d->m_pathBox;
01098 }
01099 
01100 void