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

KFile

Skip menu "KFile"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal