• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KFile

kurlnavigator.cpp
Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 2006-2010 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 as published by the Free Software Foundation; either              *
00010  * version 2 of the License, or (at your option) any later version.          *
00011  *                                                                           *
00012  * This library is distributed in the hope that it will be useful,           *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00015  * Library General Public License for more details.                          *
00016  *                                                                           *
00017  * You should have received a copy of the GNU Library General Public License *
00018  * along with this library; see the file COPYING.LIB.  If not, write to      *
00019  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      *
00020  * Boston, MA 02110-1301, USA.                                               *
00021  *****************************************************************************/
00022 
00023 #include "kurlnavigator.h"
00024 
00025 #include "kurlnavigatorplacesselector_p.h"
00026 #include "kurlnavigatorprotocolcombo_p.h"
00027 #include "kurlnavigatordropdownbutton_p.h"
00028 #include "kurlnavigatorbutton_p.h"
00029 #include "kurlnavigatortogglebutton_p.h"
00030 
00031 #include <kfileitem.h>
00032 #include <kfileplacesmodel.h>
00033 #include <kglobalsettings.h>
00034 #include <kicon.h>
00035 #include <klocale.h>
00036 #include <kmenu.h>
00037 #include <kprotocolinfo.h>
00038 #include <kurlcombobox.h>
00039 #include <kurlcompletion.h>
00040 #include <kurifilter.h>
00041 
00042 #include <QtCore/QDir>
00043 #include <QtCore/QLinkedList>
00044 #include <QtCore/QTimer>
00045 #include <QtGui/QApplication>
00046 #include <QtGui/QBoxLayout>
00047 #include <QtGui/QClipboard>
00048 #include <QtGui/QDropEvent>
00049 #include <QtGui/QKeyEvent>
00050 #include <QtGui/QLabel>
00051 #include <QtGui/QPainter>
00052 #include <QtGui/QStyleOption>
00053 
00054 #include <fixx11h.h>
00055 
00056 using namespace KDEPrivate;
00057 
00058 struct LocationData
00059 {
00060     KUrl url;
00061 #ifndef KDE_NO_DEPRECATED
00062     KUrl rootUrl;      // KDE5: remove after the deprecated methods have been removed
00063     QPoint pos;        // KDE5: remove after the deprecated methods have been removed
00064 #endif
00065     QByteArray state;
00066 };
00067 
00068 class KUrlNavigator::Private
00069 {
00070 public:
00071     Private(KUrlNavigator* q, KFilePlacesModel* placesModel);
00072 
00073     void initialize(const KUrl& url);
00074 
00075     void slotReturnPressed();
00076     void slotProtocolChanged(const QString&);
00077     void openPathSelectorMenu();
00078 
00084     void appendWidget(QWidget* widget, int stretch = 0);
00085 
00091     void switchView();
00092 
00094     void dropUrls(const KUrl& destination, QDropEvent* event);
00095 
00101     void slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button);
00102 
00103     void openContextMenu();
00104 
00105     void slotPathBoxChanged(const QString& text);
00106 
00107     void updateContent();
00108 
00117     void updateButtons(int startIndex);
00118 
00124     void updateButtonVisibility();
00125 
00129     QString firstButtonText() const;
00130 
00134     KUrl buttonUrl(int index) const;
00135 
00136     void switchToBreadcrumbMode();
00137 
00142     void deleteButtons();
00143 
00151     QString retrievePlacePath() const;
00152 
00157     bool isCompressedPath(const KUrl& path) const;
00158 
00159     void removeTrailingSlash(QString& url) const;
00160 
00168     int adjustedHistoryIndex(int historyIndex) const;
00169 
00170     bool m_editable : 1;
00171     bool m_active : 1;
00172     bool m_showPlacesSelector : 1;
00173     bool m_showFullPath : 1;
00174     int m_historyIndex;
00175 
00176     QHBoxLayout* m_layout;
00177 
00178     QList<LocationData> m_history;
00179     KUrlNavigatorPlacesSelector* m_placesSelector;
00180     KUrlComboBox* m_pathBox;
00181     KUrlNavigatorProtocolCombo* m_protocols;
00182     KUrlNavigatorDropDownButton* m_dropDownButton;
00183     QList<KUrlNavigatorButton*> m_navButtons;
00184     KUrlNavigatorButtonBase* m_toggleEditableMode;
00185     KUrl m_homeUrl;
00186     QStringList m_customProtocols;
00187     KUrlNavigator* q;
00188 };
00189 
00190 
00191 KUrlNavigator::Private::Private(KUrlNavigator* q, KFilePlacesModel* placesModel) :
00192     m_editable(false),
00193     m_active(true),
00194     m_showPlacesSelector(placesModel != 0),
00195     m_showFullPath(false),
00196     m_historyIndex(0),
00197     m_layout(new QHBoxLayout),
00198     m_placesSelector(0),
00199     m_pathBox(0),
00200     m_protocols(0),
00201     m_dropDownButton(0),
00202     m_navButtons(),
00203     m_toggleEditableMode(0),
00204     m_homeUrl(),
00205     m_customProtocols(QStringList()),
00206     q(q)
00207 {
00208     m_layout->setSpacing(0);
00209     m_layout->setMargin(0);
00210 
00211     // initialize the places selector
00212     q->setAutoFillBackground(false);
00213 
00214     if (placesModel != 0) {
00215         m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
00216         connect(m_placesSelector, SIGNAL(placeActivated(const KUrl&)),
00217                 q, SLOT(setLocationUrl(const KUrl&)));
00218 
00219         connect(placesModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
00220                 q, SLOT(updateContent()));
00221         connect(placesModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
00222                 q, SLOT(updateContent()));
00223         connect(placesModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
00224                 q, SLOT(updateContent()));
00225     }
00226 
00227     // create protocol combo
00228     m_protocols = new KUrlNavigatorProtocolCombo(QString(), q);
00229     connect(m_protocols, SIGNAL(activated(QString)),
00230             q, SLOT(slotProtocolChanged(QString)));
00231 
00232     // create drop down button for accessing all paths of the URL
00233     m_dropDownButton = new KUrlNavigatorDropDownButton(q);
00234     m_dropDownButton->installEventFilter(q);
00235     connect(m_dropDownButton, SIGNAL(clicked()),
00236             q, SLOT(openPathSelectorMenu()));
00237 
00238     // initialize the path box of the traditional view
00239     m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
00240     m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
00241     m_pathBox->installEventFilter(q);
00242 
00243     KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
00244     m_pathBox->setCompletionObject(kurlCompletion);
00245     m_pathBox->setAutoDeleteCompletionObject(true);
00246 
00247     connect(m_pathBox, SIGNAL(returnPressed()),
00248             q, SLOT(slotReturnPressed()));
00249     connect(m_pathBox, SIGNAL(urlActivated(KUrl)),
00250             q, SLOT(setLocationUrl(KUrl)));
00251     connect(m_pathBox, SIGNAL(editTextChanged(QString)),
00252             q, SLOT(slotPathBoxChanged(QString)));
00253 
00254     // create toggle button which allows to switch between
00255     // the breadcrumb and traditional view
00256     m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
00257     m_toggleEditableMode->installEventFilter(q);
00258     m_toggleEditableMode->setMinimumWidth(20);
00259     connect(m_toggleEditableMode, SIGNAL(clicked()),
00260             q, SLOT(switchView()));
00261 
00262     if (m_placesSelector != 0) {
00263         m_layout->addWidget(m_placesSelector);
00264     }
00265     m_layout->addWidget(m_protocols);
00266     m_layout->addWidget(m_dropDownButton);
00267     m_layout->addWidget(m_pathBox, 1);
00268     m_layout->addWidget(m_toggleEditableMode);
00269 
00270     q->setContextMenuPolicy(Qt::CustomContextMenu);
00271     connect(q, SIGNAL(customContextMenuRequested(QPoint)),
00272             q, SLOT(openContextMenu()));
00273 }
00274 
00275 void KUrlNavigator::Private::initialize(const KUrl& url)
00276 {
00277     LocationData data;
00278     data.url = url;
00279     m_history.prepend(data);
00280 
00281     q->setLayoutDirection(Qt::LeftToRight);
00282 
00283     const int minHeight = m_pathBox->sizeHint().height();
00284     q->setMinimumHeight(minHeight);
00285 
00286     q->setLayout(m_layout);
00287     q->setMinimumWidth(100);
00288 
00289     updateContent();
00290 }
00291 
00292 void KUrlNavigator::Private::appendWidget(QWidget* widget, int stretch)
00293 {
00294     m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
00295 }
00296 
00297 void KUrlNavigator::Private::slotReturnPressed()
00298 {
00299     // Parts of the following code have been taken
00300     // from the class KateFileSelector located in
00301     // kate/app/katefileselector.hpp of Kate.
00302     // Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
00303     // Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
00304     // Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
00305 
00306     const KUrl typedUrl = q->uncommittedUrl();
00307     QStringList urls = m_pathBox->urls();
00308     urls.removeAll(typedUrl.url());
00309     urls.prepend(typedUrl.url());
00310     m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
00311 
00312     q->setLocationUrl(typedUrl);
00313     // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
00314     // synchronize the result in the path box.
00315     const KUrl currentUrl = q->locationUrl();
00316     m_pathBox->setUrl(currentUrl);
00317 
00318     emit q->returnPressed();
00319 
00320     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
00321         // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
00322         // The switch must be done asynchronously, as we are in the context of the
00323         // editor.
00324         QMetaObject::invokeMethod(q, "switchToBreadcrumbMode", Qt::QueuedConnection);
00325     }
00326 }
00327 
00328 void KUrlNavigator::Private::slotProtocolChanged(const QString& protocol)
00329 {
00330     Q_ASSERT(m_editable);
00331 
00332     KUrl url;
00333     url.setProtocol(protocol);
00334     url.setPath((protocol == QLatin1String("file")) ? QLatin1String("/") : QLatin1String("//"));
00335 
00336     m_pathBox->setEditUrl(url);
00337 }
00338 
00339 void KUrlNavigator::Private::openPathSelectorMenu()
00340 {
00341     if (m_navButtons.count() <= 0) {
00342         return;
00343     }
00344 
00345     const KUrl firstVisibleUrl = m_navButtons.first()->url();
00346 
00347     QString spacer;
00348     KMenu* popup = new KMenu(q);
00349     popup->setLayoutDirection(Qt::LeftToRight);
00350 
00351     const QString placePath = retrievePlacePath();
00352     int idx = placePath.count(QLatin1Char('/')); // idx points to the first directory
00353                                                  // after the place path
00354 
00355     const QString path = m_history[m_historyIndex].url.pathOrUrl();
00356     QString dirName = path.section(QLatin1Char('/'), idx, idx);
00357     if (dirName.isEmpty()) {
00358         dirName = QLatin1Char('/');
00359     }
00360     do {
00361         const QString text = spacer + dirName;
00362 
00363         QAction* action = new QAction(text, popup);
00364         const KUrl currentUrl = buttonUrl(idx);
00365         if (currentUrl == firstVisibleUrl) {
00366             popup->addSeparator();
00367         }
00368         action->setData(QVariant(currentUrl.prettyUrl()));
00369         popup->addAction(action);
00370 
00371         ++idx;
00372         spacer.append("  ");
00373         dirName = path.section('/', idx, idx);
00374     } while (!dirName.isEmpty());
00375 
00376     const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
00377     const QAction* activatedAction = popup->exec(pos);
00378     if (activatedAction != 0) {
00379         const KUrl url = KUrl(activatedAction->data().toString());
00380         q->setLocationUrl(url);
00381     }
00382 
00383     popup->deleteLater();
00384 }
00385 
00386 void KUrlNavigator::Private::switchView()
00387 {
00388     m_toggleEditableMode->setFocus();
00389     m_editable = !m_editable;
00390     m_toggleEditableMode->setChecked(m_editable);
00391     updateContent();
00392     if (q->isUrlEditable()) {
00393         m_pathBox->setFocus();
00394     }
00395 
00396     emit q->requestActivation();
00397     emit q->editableStateChanged(m_editable);
00398 }
00399 
00400 void KUrlNavigator::Private::dropUrls(const KUrl& destination, QDropEvent* event)
00401 {
00402     const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
00403     if (!urls.isEmpty()) {
00404         emit q->urlsDropped(destination, event);
00405 
00406 #ifndef KDE_NO_DEPRECATED
00407         // KDE5: remove, as the signal has been replaced by
00408         // urlsDropped(const KUrl& destination, QDropEvent* event)
00409         emit q->urlsDropped(urls, destination);
00410 #endif
00411     }
00412 }
00413 
00414 void KUrlNavigator::Private::slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button)
00415 {
00416     if (button & Qt::LeftButton) {
00417         q->setLocationUrl(url);
00418     } else if (button & Qt::MidButton) {
00419         emit q->tabRequested(url);
00420     }
00421 }
00422 
00423 void KUrlNavigator::Private::openContextMenu()
00424 {
00425     q->setActive(true);
00426 
00427     KMenu popup(q);
00428 
00429     // provide 'Copy' action, which copies the current URL of
00430     // the URL navigator into the clipboard
00431     QAction* copyAction = popup.addAction(KIcon("edit-copy"), i18n("Copy"));
00432 
00433     // provide 'Paste' action, which copies the current clipboard text
00434     // into the URL navigator
00435     QAction* pasteAction = popup.addAction(KIcon("edit-paste"), i18n("Paste"));
00436     QClipboard* clipboard = QApplication::clipboard();
00437     pasteAction->setEnabled(!clipboard->text().isEmpty());
00438 
00439     popup.addSeparator();
00440 
00441     // provide radiobuttons for toggling between the edit and the navigation mode
00442     QAction* editAction = popup.addAction(i18n("Edit"));
00443     editAction->setCheckable(true);
00444 
00445     QAction* navigateAction = popup.addAction(i18n("Navigate"));
00446     navigateAction->setCheckable(true);
00447 
00448     QActionGroup* modeGroup = new QActionGroup(&popup);
00449     modeGroup->addAction(editAction);
00450     modeGroup->addAction(navigateAction);
00451     if (q->isUrlEditable()) {
00452         editAction->setChecked(true);
00453     } else {
00454         navigateAction->setChecked(true);
00455     }
00456 
00457     popup.addSeparator();
00458 
00459     // allow showing of the full path
00460     QAction* showFullPathAction = popup.addAction(i18n("Show Full Path"));
00461     showFullPathAction->setCheckable(true);
00462     showFullPathAction->setChecked(q->showFullPath());
00463 
00464     QAction* activatedAction = popup.exec(QCursor::pos());
00465     if (activatedAction == copyAction) {
00466         QMimeData* mimeData = new QMimeData();
00467         mimeData->setText(q->locationUrl().pathOrUrl());
00468         clipboard->setMimeData(mimeData);
00469     } else if (activatedAction == pasteAction) {
00470         q->setLocationUrl(KUrl(clipboard->text()));
00471     } else if (activatedAction == editAction) {
00472         q->setUrlEditable(true);
00473     } else if (activatedAction == navigateAction) {
00474         q->setUrlEditable(false);
00475     } else if (activatedAction == showFullPathAction) {
00476         q->setShowFullPath(showFullPathAction->isChecked());
00477     }
00478 }
00479 
00480 void KUrlNavigator::Private::slotPathBoxChanged(const QString& text)
00481 {
00482     if (text.isEmpty()) {
00483         const QString protocol = q->locationUrl().protocol();
00484         m_protocols->setProtocol(protocol);
00485         m_protocols->show();
00486     } else {
00487         m_protocols->hide();
00488     }
00489 }
00490 
00491 void KUrlNavigator::Private::updateContent()
00492 {
00493     const KUrl currentUrl = q->locationUrl();
00494     if (m_placesSelector != 0) {
00495         m_placesSelector->updateSelection(currentUrl);
00496     }
00497 
00498     if (m_editable) {
00499         m_protocols->hide();
00500         m_dropDownButton->hide();
00501 
00502         deleteButtons();
00503         m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
00504         q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
00505 
00506         m_pathBox->show();
00507         m_pathBox->setUrl(currentUrl);
00508     } else {
00509         m_pathBox->hide();
00510 
00511         m_protocols->hide();
00512 
00513         m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
00514         q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
00515 
00516         // Calculate the start index for the directories that should be shown as buttons
00517         // and create the buttons
00518         KUrl placeUrl;
00519         if ((m_placesSelector != 0) && !m_showFullPath) {
00520             placeUrl = m_placesSelector->selectedPlaceUrl();
00521         }
00522 
00523         QString placePath = placeUrl.isValid() ? placeUrl.pathOrUrl() : retrievePlacePath();
00524         removeTrailingSlash(placePath);
00525 
00526         const int startIndex = placePath.count('/');
00527         updateButtons(startIndex);
00528     }
00529 }
00530 
00531 void KUrlNavigator::Private::updateButtons(int startIndex)
00532 {
00533     KUrl currentUrl = q->locationUrl();
00534 
00535     const QString path = currentUrl.pathOrUrl();
00536 
00537     bool createButton = false;
00538     const int oldButtonCount = m_navButtons.count();
00539 
00540     int idx = startIndex;
00541     bool hasNext = true;
00542     do {
00543         createButton = (idx - startIndex >= oldButtonCount);
00544         const bool isFirstButton = (idx == startIndex);
00545         const QString dirName = path.section(QLatin1Char('/'), idx, idx);
00546         hasNext = isFirstButton || !dirName.isEmpty();
00547         if (hasNext) {
00548             KUrlNavigatorButton* button = 0;
00549             if (createButton) {
00550                 button = new KUrlNavigatorButton(buttonUrl(idx), q);
00551                 button->installEventFilter(q);
00552                 button->setForegroundRole(QPalette::WindowText);
00553                 connect(button, SIGNAL(urlsDropped(const KUrl&, QDropEvent*)),
00554                         q, SLOT(dropUrls(const KUrl&, QDropEvent*)));
00555                 connect(button, SIGNAL(clicked(KUrl, Qt::MouseButton)),
00556                         q, SLOT(slotNavigatorButtonClicked(KUrl, Qt::MouseButton)));
00557                 connect(button, SIGNAL(finishedTextResolving()),
00558                         q, SLOT(updateButtonVisibility()));
00559                 appendWidget(button);
00560             } else {
00561                 button = m_navButtons[idx - startIndex];
00562                 button->setUrl(buttonUrl(idx));
00563             }
00564 
00565             if (isFirstButton) {
00566                 button->setText(firstButtonText());
00567             }
00568             button->setActive(q->isActive());
00569 
00570             if (createButton) {
00571                 if (!isFirstButton) {
00572                     setTabOrder(m_navButtons.last(), button);
00573                 }
00574                 m_navButtons.append(button);
00575             }
00576 
00577             ++idx;
00578             button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
00579         }
00580     } while (hasNext);
00581 
00582     // delete buttons which are not used anymore
00583     const int newButtonCount = idx - startIndex;
00584     if (newButtonCount < oldButtonCount) {
00585         const QList<KUrlNavigatorButton*>::iterator itBegin = m_navButtons.begin() + newButtonCount;
00586         const QList<KUrlNavigatorButton*>::iterator itEnd = m_navButtons.end();
00587         QList<KUrlNavigatorButton*>::iterator it = itBegin;
00588         while (it != itEnd) {
00589             (*it)->hide();
00590             (*it)->deleteLater();
00591             ++it;
00592         }
00593         m_navButtons.erase(itBegin, itEnd);
00594     }
00595 
00596     setTabOrder(m_dropDownButton, m_navButtons.first());
00597     setTabOrder(m_navButtons.last(), m_toggleEditableMode);
00598 
00599     updateButtonVisibility();
00600 }
00601 
00602 void KUrlNavigator::Private::updateButtonVisibility()
00603 {
00604     if (m_editable) {
00605         return;
00606     }
00607 
00608     const int buttonsCount = m_navButtons.count();
00609     if (buttonsCount == 0) {
00610         m_dropDownButton->hide();
00611         return;
00612     }
00613 
00614     // Subtract all widgets from the available width, that must be shown anyway
00615     int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
00616 
00617     if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
00618         availableWidth -= m_placesSelector->width();
00619     }
00620 
00621     if ((m_protocols != 0) && m_protocols->isVisible()) {
00622         availableWidth -= m_protocols->width();
00623     }
00624 
00625     // Check whether buttons must be hidden at all...
00626     int requiredButtonWidth = 0;
00627     foreach (const KUrlNavigatorButton* button, m_navButtons) {
00628         requiredButtonWidth += button->minimumWidth();
00629     }
00630 
00631     if (requiredButtonWidth > availableWidth) {
00632         // At least one button must be hidden. This implies that the
00633         // drop-down button must get visible, which again decreases the
00634         // available width.
00635         availableWidth -= m_dropDownButton->width();
00636     }
00637 
00638     // Hide buttons...
00639     QList<KUrlNavigatorButton*>::const_iterator it = m_navButtons.constEnd();
00640     const QList<KUrlNavigatorButton*>::const_iterator itBegin = m_navButtons.constBegin();
00641     bool isLastButton = true;
00642     bool hasHiddenButtons = false;
00643 
00644     QLinkedList<KUrlNavigatorButton*> buttonsToShow;
00645     while (it != itBegin) {
00646         --it;
00647         KUrlNavigatorButton* button = (*it);
00648         availableWidth -= button->minimumWidth();
00649         if ((availableWidth <= 0) && !isLastButton) {
00650             button->hide();
00651             hasHiddenButtons = true;
00652         }
00653         else {
00654             // Don't show the button immediately, as setActive()
00655             // might change the size and a relayout gets triggered
00656             // after showing the button. So the showing of all buttons
00657             // is postponed until all buttons have the correct
00658             // activation state.
00659             buttonsToShow.append(button);
00660         }
00661         isLastButton = false;
00662     }
00663 
00664     // All buttons have the correct activation state and
00665     // can be shown now
00666     foreach (KUrlNavigatorButton* button, buttonsToShow) {
00667         button->show();
00668     }
00669 
00670     if (hasHiddenButtons) {
00671         m_dropDownButton->show();
00672     } else {
00673         // Check whether going upwards is possible. If this is the case, show the drop-down button.
00674         KUrl url = m_navButtons.front()->url();
00675         url.adjustPath(KUrl::AddTrailingSlash);
00676         const bool visible = !url.equals(url.upUrl()) && (url.protocol() != "nepomuksearch");
00677         m_dropDownButton->setVisible(visible);
00678     }
00679 }
00680 
00681 QString KUrlNavigator::Private::firstButtonText() const
00682 {
00683     QString text;
00684 
00685     // The first URL navigator button should get the name of the
00686     // place instead of the directory name
00687     if ((m_placesSelector != 0) && !m_showFullPath) {
00688         const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
00689         text = m_placesSelector->selectedPlaceText();
00690     }
00691 
00692     if (text.isEmpty()) {
00693         const KUrl currentUrl = q->locationUrl();
00694         if (currentUrl.isLocalFile()) {
00695 #ifdef Q_OS_WIN
00696             text = currentUrl.path().length() > 1 ? currentUrl.path().left(2) : QDir::rootPath();
00697 #else
00698             text = m_showFullPath ? QLatin1String("/") : i18n("Custom Path");
00699 #endif
00700         } else {
00701             text = currentUrl.protocol() + QLatin1Char(':');
00702             if (!currentUrl.host().isEmpty()) {
00703                 text += QLatin1Char(' ') + currentUrl.host();
00704             }
00705         }
00706     }
00707 
00708     return text;
00709 }
00710 
00711 KUrl KUrlNavigator::Private::buttonUrl(int index) const
00712 {
00713     if (index < 0) {
00714         index = 0;
00715     }
00716 
00717     // Keep scheme, hostname etc. as this is needed for e. g. browsing
00718     // FTP directories
00719     const KUrl currentUrl = q->locationUrl();
00720     KUrl newUrl = currentUrl;
00721     newUrl.setPath(QString());
00722 
00723     QString pathOrUrl = currentUrl.pathOrUrl();
00724     if (!pathOrUrl.isEmpty()) {
00725         if (index == 0) {
00726             // prevent the last "/" from being stripped
00727             // or we end up with an empty path
00728 #ifdef Q_OS_WIN
00729             pathOrUrl = pathOrUrl.length() > 1 ? pathOrUrl.left(2) : QDir::rootPath();
00730 #else
00731             pathOrUrl = QLatin1String("/");
00732 #endif
00733         } else {
00734             pathOrUrl = pathOrUrl.section('/', 0, index);
00735         }
00736     }
00737 
00738     newUrl.setPath(KUrl(pathOrUrl).path());
00739     return newUrl;
00740 }
00741 
00742 void KUrlNavigator::Private::switchToBreadcrumbMode()
00743 {
00744     q->setUrlEditable(false);
00745 }
00746 
00747 void KUrlNavigator::Private::deleteButtons()
00748 {
00749     foreach (KUrlNavigatorButton* button, m_navButtons) {
00750         button->hide();
00751         button->deleteLater();
00752     }
00753     m_navButtons.clear();
00754 }
00755 
00756 QString KUrlNavigator::Private::retrievePlacePath() const
00757 {
00758     const KUrl currentUrl = q->locationUrl();
00759     const QString path = currentUrl.pathOrUrl();
00760     int idx = path.indexOf(QLatin1String("///"));
00761     if (idx >= 0) {
00762         idx += 3;
00763     } else {
00764         idx = path.indexOf(QLatin1String("//"));
00765         idx = path.indexOf(QLatin1Char('/'), (idx < 0) ? 0 : idx + 2);
00766     }
00767 
00768     QString placePath = (idx < 0) ? path : path.left(idx);
00769     removeTrailingSlash(placePath);
00770     return placePath;
00771 }
00772 
00773 bool KUrlNavigator::Private::isCompressedPath(const KUrl& url) const
00774 {
00775     const KMimeType::Ptr mime = KMimeType::findByPath(url.path(KUrl::RemoveTrailingSlash));
00776     // Note: this list of MIME types depends on the protocols implemented by kio_archive
00777     return  mime->is("application/x-compressed-tar") ||
00778             mime->is("application/x-bzip-compressed-tar") ||
00779             mime->is("application/x-lzma-compressed-tar") ||
00780             mime->is("application/x-xz-compressed-tar") ||
00781             mime->is("application/x-tar") ||
00782             mime->is("application/x-tarz") ||
00783             mime->is("application/x-tzo") || // (not sure KTar supports those?)
00784             mime->is("application/zip") ||
00785             mime->is("application/x-archive");
00786 }
00787 
00788 void KUrlNavigator::Private::removeTrailingSlash(QString& url) const
00789 {
00790     const int length = url.length();
00791     if ((length > 0) && (url.at(length - 1) == QChar('/'))) {
00792         url.remove(length - 1, 1);
00793     }
00794 }
00795 
00796 int KUrlNavigator::Private::adjustedHistoryIndex(int historyIndex) const
00797 {
00798     if (historyIndex < 0) {
00799         historyIndex = m_historyIndex;
00800     } else if (historyIndex >= m_history.size()) {
00801         historyIndex = m_history.size() - 1;
00802         Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0
00803     }
00804     return historyIndex;
00805 }
00806 
00807 // ------------------------------------------------------------------------------------------------
00808 
00809 KUrlNavigator::KUrlNavigator(QWidget* parent) :
00810     QWidget(parent),
00811     d(new Private(this, 0))
00812 {
00813     d->initialize(KUrl());
00814 }
00815 
00816 KUrlNavigator::KUrlNavigator(KFilePlacesModel* placesModel,
00817                              const KUrl& url,
00818                              QWidget* parent) :
00819     QWidget(parent),
00820     d(new Private(this, placesModel))
00821 {
00822     d->initialize(url);
00823 }
00824 
00825 KUrlNavigator::~KUrlNavigator()
00826 {
00827     delete d;
00828 }
00829 
00830 KUrl KUrlNavigator::locationUrl(int historyIndex) const
00831 {
00832     historyIndex = d->adjustedHistoryIndex(historyIndex);
00833     return d->m_history[historyIndex].url;
00834 }
00835 
00836 void KUrlNavigator::saveLocationState(const QByteArray& state)
00837 {
00838     d->m_history[d->m_historyIndex].state = state;
00839 }
00840 
00841 QByteArray KUrlNavigator::locationState(int historyIndex) const
00842 {
00843     historyIndex = d->adjustedHistoryIndex(historyIndex);
00844     return d->m_history[historyIndex].state;
00845 }
00846 
00847 bool KUrlNavigator::goBack()
00848 {
00849     const int count = d->m_history.count();
00850     if (d->m_historyIndex < count - 1) {
00851         const KUrl newUrl = locationUrl(d->m_historyIndex + 1);
00852         emit urlAboutToBeChanged(newUrl);
00853 
00854         ++d->m_historyIndex;
00855         d->updateContent();
00856 
00857         emit historyChanged();
00858         emit urlChanged(locationUrl());
00859         return true;
00860     }
00861 
00862     return false;
00863 }
00864 
00865 bool KUrlNavigator::goForward()
00866 {
00867     if (d->m_historyIndex > 0) {
00868         const KUrl newUrl = locationUrl(d->m_historyIndex - 1);
00869         emit urlAboutToBeChanged(newUrl);
00870 
00871         --d->m_historyIndex;
00872         d->updateContent();
00873 
00874         emit historyChanged();
00875         emit urlChanged(locationUrl());
00876         return true;
00877     }
00878 
00879     return false;
00880 }
00881 
00882 bool KUrlNavigator::goUp()
00883 {
00884     const KUrl currentUrl = locationUrl();
00885     const KUrl upUrl = currentUrl.upUrl();
00886     if (upUrl != currentUrl) {
00887         setLocationUrl(upUrl);
00888         return true;
00889     }
00890 
00891     return false;
00892 }
00893 
00894 void KUrlNavigator::goHome()
00895 {
00896     if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
00897         setLocationUrl(KUrl(QDir::homePath()));
00898     } else {
00899         setLocationUrl(d->m_homeUrl);
00900     }
00901 }
00902 
00903 void KUrlNavigator::setHomeUrl(const KUrl& url)
00904 {
00905     d->m_homeUrl = url;
00906 }
00907 
00908 KUrl KUrlNavigator::homeUrl() const
00909 {
00910     return d->m_homeUrl;
00911 }
00912 
00913 void KUrlNavigator::setUrlEditable(bool editable)
00914 {
00915     if (d->m_editable != editable) {
00916         d->switchView();
00917     }
00918 }
00919 
00920 bool KUrlNavigator::isUrlEditable() const
00921 {
00922     return d->m_editable;
00923 }
00924 
00925 void KUrlNavigator::setShowFullPath(bool show)
00926 {
00927     if (d->m_showFullPath != show) {
00928         d->m_showFullPath = show;
00929         d->updateContent();
00930     }
00931 }
00932 
00933 bool KUrlNavigator::showFullPath() const
00934 {
00935     return d->m_showFullPath;
00936 }
00937 
00938 
00939 void KUrlNavigator::setActive(bool active)
00940 {
00941     if (active != d->m_active) {
00942         d->m_active = active;
00943 
00944         d->m_dropDownButton->setActive(active);
00945         foreach(KUrlNavigatorButton* button, d->m_navButtons) {
00946             button->setActive(active);
00947         }
00948 
00949         update();
00950         if (active) {
00951             emit activated();
00952         }
00953     }
00954 }
00955 
00956 bool KUrlNavigator::isActive() const
00957 {
00958     return d->m_active;
00959 }
00960 
00961 void KUrlNavigator::setPlacesSelectorVisible(bool visible)
00962 {
00963     if (visible == d->m_showPlacesSelector) {
00964         return;
00965     }
00966 
00967     if (visible  && (d->m_placesSelector == 0)) {
00968         // the places selector cannot get visible as no
00969         // places model is available
00970         return;
00971     }
00972 
00973     d->m_showPlacesSelector = visible;
00974     d->m_placesSelector->setVisible(visible);
00975 }
00976 
00977 bool KUrlNavigator::isPlacesSelectorVisible() const
00978 {
00979     return d->m_showPlacesSelector;
00980 }
00981 
00982 KUrl KUrlNavigator::uncommittedUrl() const
00983 {
00984     KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
00985     filteredData.setCheckForExecutables(false);
00986     if (KUriFilter::self()->filterUri(filteredData, QStringList() << "kshorturifilter" << "kurisearchfilter")) {
00987         return filteredData.uri();
00988     }
00989     else {
00990         return KUrl(filteredData.typedString());
00991     }
00992 }
00993 
00994 void KUrlNavigator::setLocationUrl(const KUrl& newUrl)
00995 {
00996     if (newUrl == locationUrl()) {
00997         return;
00998     }
00999 
01000     KUrl url = newUrl;
01001     url.cleanPath();
01002 
01003     if ((url.protocol() == QLatin1String("tar")) || (url.protocol() == QLatin1String("zip"))) {
01004         // The URL represents a tar- or zip-file. Check whether
01005         // the URL is really part of the tar- or zip-file, otherwise
01006         // replace it by the local path again.
01007         bool insideCompressedPath = d->isCompressedPath(url);
01008         if (!insideCompressedPath) {
01009             KUrl prevUrl = url;
01010             KUrl parentUrl = url.upUrl();
01011             while (parentUrl != prevUrl) {
01012                 if (d->isCompressedPath(parentUrl)) {
01013                     insideCompressedPath = true;
01014                     break;
01015                 }
01016                 prevUrl = parentUrl;
01017                 parentUrl = parentUrl.upUrl();
01018             }
01019         }
01020         if (!insideCompressedPath) {
01021             // drop the tar: or zip: protocol since we are not
01022             // inside the compressed path
01023             url.setProtocol("file");
01024         }
01025     }
01026 
01027     // Check whether current history element has the same URL.
01028     // If this is the case, just ignore setting the URL.
01029     const LocationData& data = d->m_history[d->m_historyIndex];
01030     const bool isUrlEqual = url.equals(locationUrl(), KUrl::CompareWithoutTrailingSlash) ||
01031                             (!url.isValid() && url.equals(data.url, KUrl::CompareWithoutTrailingSlash));
01032     if (isUrlEqual) {
01033         return;
01034     }
01035 
01036     emit urlAboutToBeChanged(url);
01037 
01038     if (d->m_historyIndex > 0) {
01039         // If an URL is set when the history index is not at the end (= 0),
01040         // then clear all previous history elements so that a new history
01041         // tree is started from the current position.
01042         QList<LocationData>::iterator begin = d->m_history.begin();
01043         QList<LocationData>::iterator end = begin + d->m_historyIndex;
01044         d->m_history.erase(begin, end);
01045         d->m_historyIndex = 0;
01046     }
01047 
01048     Q_ASSERT(d->m_historyIndex == 0);
01049     LocationData newData;
01050     newData.url = url;
01051     d->m_history.insert(0, newData);
01052 
01053     // Prevent an endless growing of the history: remembering
01054     // the last 100 Urls should be enough...
01055     const int historyMax = 100;
01056     if (d->m_history.size() > historyMax) {
01057         QList<LocationData>::iterator begin = d->m_history.begin() + historyMax;
01058         QList<LocationData>::iterator end = d->m_history.end();
01059         d->m_history.erase(begin, end);
01060     }
01061 
01062     emit historyChanged();
01063     emit urlChanged(url);
01064 
01065     d->updateContent();
01066 
01067     requestActivation();
01068 }
01069 
01070 void KUrlNavigator::requestActivation()
01071 {
01072     setActive(true);
01073 }
01074 
01075 void KUrlNavigator::setFocus()
01076 {
01077     if (isUrlEditable()) {
01078         d->m_pathBox->setFocus();
01079     } else {
01080         QWidget::setFocus();
01081     }
01082 }
01083 
01084 #ifndef KDE_NO_DEPRECATED
01085 void KUrlNavigator::setUrl(const KUrl& url)
01086 {
01087     // deprecated
01088     setLocationUrl(url);
01089 }
01090 #endif
01091 
01092 #ifndef KDE_NO_DEPRECATED
01093 void KUrlNavigator::saveRootUrl(const KUrl& url)
01094 {
01095     // deprecated
01096     d->m_history[d->m_historyIndex].rootUrl = url;
01097 }
01098 #endif
01099 
01100 #ifndef KDE_NO_DEPRECATED
01101 void KUrlNavigator::savePosition(int x, int y)
01102 {
01103     // deprecated
01104     d->m_history[d->m_historyIndex].pos = QPoint(x, y);
01105 }
01106 #endif
01107 
01108 void KUrlNavigator::keyPressEvent(QKeyEvent* event)
01109 {
01110     if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
01111         setUrlEditable(false);
01112     } else {
01113         QWidget::keyPressEvent(event);
01114     }
01115 }
01116 
01117 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
01118 {
01119     QWidget::keyReleaseEvent(event);
01120 }
01121 
01122 void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
01123 {
01124     if (event->button() == Qt::MidButton) {
01125         const QRect bounds = d->m_toggleEditableMode->geometry();
01126         if (bounds.contains(event->pos())) {
01127             // The middle mouse button has been clicked above the
01128             // toggle-editable-mode-button. Paste the clipboard content
01129             // as location URL.
01130             QClipboard* clipboard = QApplication::clipboard();
01131             const QMimeData* mimeData = clipboard->mimeData();
01132             if (mimeData->hasText()) {
01133                 const QString text = mimeData->text();
01134                 setLocationUrl(KUrl(text));
01135             }
01136         }
01137     }
01138     QWidget::mouseReleaseEvent(event);
01139 }
01140 
01141 void KUrlNavigator::resizeEvent(QResizeEvent* event)
01142 {
01143     QTimer::singleShot(0, this, SLOT(updateButtonVisibility()));
01144     QWidget::resizeEvent(event);
01145 }
01146 
01147 void KUrlNavigator::wheelEvent(QWheelEvent* event)
01148 {
01149     setActive(true);
01150     QWidget::wheelEvent(event);
01151 }
01152 
01153 bool KUrlNavigator::eventFilter(QObject* watched, QEvent* event)
01154 {
01155     switch (event->type()) {
01156     case QEvent::FocusIn:
01157         if (watched == d->m_pathBox) {
01158             requestActivation();
01159             setFocus();
01160         }
01161         foreach (KUrlNavigatorButton* button, d->m_navButtons) {
01162             button->setShowMnemonic(true);
01163         }
01164         break;
01165 
01166     case QEvent::FocusOut:
01167         foreach (KUrlNavigatorButton* button, d->m_navButtons) {
01168             button->setShowMnemonic(false);
01169         }
01170         break;
01171 
01172     default:
01173         break;
01174     }
01175 
01176     return QWidget::eventFilter(watched, event);
01177 }
01178 
01179 int KUrlNavigator::historySize() const
01180 {
01181     return d->m_history.count();
01182 }
01183 
01184 int KUrlNavigator::historyIndex() const
01185 {
01186     return d->m_historyIndex;
01187 }
01188 
01189 KUrlComboBox* KUrlNavigator::editor() const
01190 {
01191     return d->m_pathBox;
01192 }
01193 
01194 void KUrlNavigator::setCustomProtocols(const QStringList &protocols)
01195 {
01196     d->m_customProtocols = protocols;
01197     d->m_protocols->setCustomProtocols(d->m_customProtocols);
01198 }
01199 
01200 QStringList KUrlNavigator::customProtocols() const
01201 {
01202     return d->m_customProtocols;
01203 }
01204 
01205 #ifndef KDE_NO_DEPRECATED
01206 const KUrl& KUrlNavigator::url() const
01207 {
01208     // deprecated
01209 
01210     // Workaround required because of flawed interface ('const KUrl&' is returned
01211     // instead of 'KUrl'): remember the URL to prevent a dangling pointer
01212     static KUrl url;
01213     url = locationUrl();
01214     return url;
01215 }
01216 #endif
01217 
01218 #ifndef KDE_NO_DEPRECATED
01219 KUrl KUrlNavigator::url(int index) const
01220 {
01221     // deprecated
01222     return d->buttonUrl(index);
01223 }
01224 #endif
01225 
01226 #ifndef KDE_NO_DEPRECATED
01227 KUrl KUrlNavigator::historyUrl(int historyIndex) const
01228 {
01229     // deprecated
01230     return locationUrl(historyIndex);
01231 }
01232 #endif
01233 
01234 #ifndef KDE_NO_DEPRECATED
01235 const KUrl& KUrlNavigator::savedRootUrl() const
01236 {
01237     // deprecated
01238 
01239     // Workaround required because of flawed interface ('const KUrl&' is returned
01240     // instead of 'KUrl'): remember the root URL to prevent a dangling pointer
01241     static KUrl rootUrl;
01242     rootUrl = d->m_history[d->m_historyIndex].rootUrl;
01243     return rootUrl;
01244 }
01245 #endif
01246 
01247 #ifndef KDE_NO_DEPRECATED
01248 QPoint KUrlNavigator::savedPosition() const
01249 {
01250     // deprecated
01251     return d->m_history[d->m_historyIndex].pos;
01252 }
01253 #endif
01254 
01255 #ifndef KDE_NO_DEPRECATED
01256 void KUrlNavigator::setHomeUrl(const QString& homeUrl)
01257 {
01258     // deprecated
01259     setLocationUrl(KUrl(homeUrl));
01260 }
01261 #endif
01262 
01263 #include "kurlnavigator.moc"

KFile

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
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