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"
KDE 4.7 API Reference