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