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