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

KFile

Skip menu "KFile"
  • Main Page
  • Namespace List
  • 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
  • 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.3
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