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

KFile

  • kfile
kurlnavigator.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright (C) 2006-2010 by Peter Penz <peter.penz@gmx.at> *
3  * Copyright (C) 2006 by Aaron J. Seigo <aseigo@kde.org> *
4  * Copyright (C) 2007 by Kevin Ottens <ervin@kde.org> *
5  * Copyright (C) 2007 by Urs Wolfer <uwolfer @ kde.org> *
6  * *
7  * This library is free software; you can redistribute it and/or *
8  * modify it under the terms of the GNU Library General Public *
9  * License as published by the Free Software Foundation; either *
10  * version 2 of the License, or (at your option) any later version. *
11  * *
12  * This library is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
15  * Library General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU Library General Public License *
18  * along with this library; see the file COPYING.LIB. If not, write to *
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
20  * Boston, MA 02110-1301, USA. *
21  *****************************************************************************/
22 
23 #include "kurlnavigator.h"
24 
25 #include "kurlnavigatorplacesselector_p.h"
26 #include "kurlnavigatorprotocolcombo_p.h"
27 #include "kurlnavigatordropdownbutton_p.h"
28 #include "kurlnavigatorbutton_p.h"
29 #include "kurlnavigatortogglebutton_p.h"
30 
31 #include <kfileitem.h>
32 #include <kfileplacesmodel.h>
33 #include <kglobalsettings.h>
34 #include <kicon.h>
35 #include <klocale.h>
36 #include <kmenu.h>
37 #include <kprotocolinfo.h>
38 #include <kurlcombobox.h>
39 #include <kurlcompletion.h>
40 #include <kurifilter.h>
41 
42 #include <QtCore/QDir>
43 #include <QtCore/QLinkedList>
44 #include <QtCore/QTimer>
45 #include <QtGui/QApplication>
46 #include <QtGui/QBoxLayout>
47 #include <QtGui/QClipboard>
48 #include <QtGui/QDropEvent>
49 #include <QtGui/QKeyEvent>
50 #include <QtGui/QLabel>
51 #include <QtGui/QPainter>
52 #include <QtGui/QStyleOption>
53 
54 #include <fixx11h.h>
55 
56 using namespace KDEPrivate;
57 
58 struct LocationData
59 {
60  KUrl url;
61 #ifndef KDE_NO_DEPRECATED
62  KUrl rootUrl; // KDE5: remove after the deprecated methods have been removed
63  QPoint pos; // KDE5: remove after the deprecated methods have been removed
64 #endif
65  QByteArray state;
66 };
67 
68 class KUrlNavigator::Private
69 {
70 public:
71  Private(KUrlNavigator* q, KFilePlacesModel* placesModel);
72 
73  void initialize(const KUrl& url);
74 
75  void slotReturnPressed();
76  void slotProtocolChanged(const QString&);
77  void openPathSelectorMenu();
78 
84  void appendWidget(QWidget* widget, int stretch = 0);
85 
91  void switchView();
92 
94  void dropUrls(const KUrl& destination, QDropEvent* event);
95 
101  void slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button);
102 
103  void openContextMenu();
104 
105  void slotPathBoxChanged(const QString& text);
106 
107  void updateContent();
108 
117  void updateButtons(int startIndex);
118 
124  void updateButtonVisibility();
125 
129  QString firstButtonText() const;
130 
134  KUrl buttonUrl(int index) const;
135 
136  void switchToBreadcrumbMode();
137 
142  void deleteButtons();
143 
151  QString retrievePlacePath() const;
152 
157  bool isCompressedPath(const KUrl& path) const;
158 
159  void removeTrailingSlash(QString& url) const;
160 
168  int adjustedHistoryIndex(int historyIndex) const;
169 
170  bool m_editable : 1;
171  bool m_active : 1;
172  bool m_showPlacesSelector : 1;
173  bool m_showFullPath : 1;
174  int m_historyIndex;
175 
176  QHBoxLayout* m_layout;
177 
178  QList<LocationData> m_history;
179  KUrlNavigatorPlacesSelector* m_placesSelector;
180  KUrlComboBox* m_pathBox;
181  KUrlNavigatorProtocolCombo* m_protocols;
182  KUrlNavigatorDropDownButton* m_dropDownButton;
183  QList<KUrlNavigatorButton*> m_navButtons;
184  KUrlNavigatorButtonBase* m_toggleEditableMode;
185  KUrl m_homeUrl;
186  QStringList m_customProtocols;
187  KUrlNavigator* q;
188 };
189 
190 
191 KUrlNavigator::Private::Private(KUrlNavigator* q, KFilePlacesModel* placesModel) :
192  m_editable(false),
193  m_active(true),
194  m_showPlacesSelector(placesModel != 0),
195  m_showFullPath(false),
196  m_historyIndex(0),
197  m_layout(new QHBoxLayout),
198  m_placesSelector(0),
199  m_pathBox(0),
200  m_protocols(0),
201  m_dropDownButton(0),
202  m_navButtons(),
203  m_toggleEditableMode(0),
204  m_homeUrl(),
205  m_customProtocols(QStringList()),
206  q(q)
207 {
208  m_layout->setSpacing(0);
209  m_layout->setMargin(0);
210 
211  // initialize the places selector
212  q->setAutoFillBackground(false);
213 
214  if (placesModel != 0) {
215  m_placesSelector = new KUrlNavigatorPlacesSelector(q, placesModel);
216  connect(m_placesSelector, SIGNAL(placeActivated(KUrl)),
217  q, SLOT(setLocationUrl(KUrl)));
218 
219  connect(placesModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
220  q, SLOT(updateContent()));
221  connect(placesModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
222  q, SLOT(updateContent()));
223  connect(placesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
224  q, SLOT(updateContent()));
225  }
226 
227  // create protocol combo
228  m_protocols = new KUrlNavigatorProtocolCombo(QString(), q);
229  connect(m_protocols, SIGNAL(activated(QString)),
230  q, SLOT(slotProtocolChanged(QString)));
231 
232  // create drop down button for accessing all paths of the URL
233  m_dropDownButton = new KUrlNavigatorDropDownButton(q);
234  m_dropDownButton->installEventFilter(q);
235  connect(m_dropDownButton, SIGNAL(clicked()),
236  q, SLOT(openPathSelectorMenu()));
237 
238  // initialize the path box of the traditional view
239  m_pathBox = new KUrlComboBox(KUrlComboBox::Directories, true, q);
240  m_pathBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
241  m_pathBox->installEventFilter(q);
242 
243  KUrlCompletion* kurlCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion);
244  m_pathBox->setCompletionObject(kurlCompletion);
245  m_pathBox->setAutoDeleteCompletionObject(true);
246 
247  connect(m_pathBox, SIGNAL(returnPressed()),
248  q, SLOT(slotReturnPressed()));
249  connect(m_pathBox, SIGNAL(urlActivated(KUrl)),
250  q, SLOT(setLocationUrl(KUrl)));
251  connect(m_pathBox, SIGNAL(editTextChanged(QString)),
252  q, SLOT(slotPathBoxChanged(QString)));
253 
254  // create toggle button which allows to switch between
255  // the breadcrumb and traditional view
256  m_toggleEditableMode = new KUrlNavigatorToggleButton(q);
257  m_toggleEditableMode->installEventFilter(q);
258  m_toggleEditableMode->setMinimumWidth(20);
259  connect(m_toggleEditableMode, SIGNAL(clicked()),
260  q, SLOT(switchView()));
261 
262  if (m_placesSelector != 0) {
263  m_layout->addWidget(m_placesSelector);
264  }
265  m_layout->addWidget(m_protocols);
266  m_layout->addWidget(m_dropDownButton);
267  m_layout->addWidget(m_pathBox, 1);
268  m_layout->addWidget(m_toggleEditableMode);
269 
270  q->setContextMenuPolicy(Qt::CustomContextMenu);
271  connect(q, SIGNAL(customContextMenuRequested(QPoint)),
272  q, SLOT(openContextMenu()));
273 }
274 
275 void KUrlNavigator::Private::initialize(const KUrl& url)
276 {
277  LocationData data;
278  data.url = url;
279  m_history.prepend(data);
280 
281  q->setLayoutDirection(Qt::LeftToRight);
282 
283  const int minHeight = m_pathBox->sizeHint().height();
284  q->setMinimumHeight(minHeight);
285 
286  q->setLayout(m_layout);
287  q->setMinimumWidth(100);
288 
289  updateContent();
290 }
291 
292 void KUrlNavigator::Private::appendWidget(QWidget* widget, int stretch)
293 {
294  m_layout->insertWidget(m_layout->count() - 1, widget, stretch);
295 }
296 
297 void KUrlNavigator::Private::slotReturnPressed()
298 {
299  // Parts of the following code have been taken
300  // from the class KateFileSelector located in
301  // kate/app/katefileselector.hpp of Kate.
302  // Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
303  // Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
304  // Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
305 
306  const KUrl typedUrl = q->uncommittedUrl();
307  QStringList urls = m_pathBox->urls();
308  urls.removeAll(typedUrl.url());
309  urls.prepend(typedUrl.url());
310  m_pathBox->setUrls(urls, KUrlComboBox::RemoveBottom);
311 
312  q->setLocationUrl(typedUrl);
313  // The URL might have been adjusted by KUrlNavigator::setUrl(), hence
314  // synchronize the result in the path box.
315  const KUrl currentUrl = q->locationUrl();
316  m_pathBox->setUrl(currentUrl);
317 
318  emit q->returnPressed();
319 
320  if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
321  // Pressing Ctrl+Return automatically switches back to the breadcrumb mode.
322  // The switch must be done asynchronously, as we are in the context of the
323  // editor.
324  QMetaObject::invokeMethod(q, "switchToBreadcrumbMode", Qt::QueuedConnection);
325  }
326 }
327 
328 void KUrlNavigator::Private::slotProtocolChanged(const QString& protocol)
329 {
330  Q_ASSERT(m_editable);
331 
332  KUrl url;
333  url.setProtocol(protocol);
334  url.setPath((protocol == QLatin1String("file")) ? QLatin1String("/") : QLatin1String("//"));
335 
336  m_pathBox->setEditUrl(url);
337 }
338 
339 void KUrlNavigator::Private::openPathSelectorMenu()
340 {
341  if (m_navButtons.count() <= 0) {
342  return;
343  }
344 
345  const KUrl firstVisibleUrl = m_navButtons.first()->url();
346 
347  QString spacer;
348  KMenu* popup = new KMenu(q);
349  popup->setLayoutDirection(Qt::LeftToRight);
350 
351  const QString placePath = retrievePlacePath();
352  int idx = placePath.count(QLatin1Char('/')); // idx points to the first directory
353  // after the place path
354 
355  const QString path = m_history[m_historyIndex].url.pathOrUrl();
356  QString dirName = path.section(QLatin1Char('/'), idx, idx);
357  if (dirName.isEmpty()) {
358  dirName = QLatin1Char('/');
359  }
360  do {
361  const QString text = spacer + dirName;
362 
363  QAction* action = new QAction(text, popup);
364  const KUrl currentUrl = buttonUrl(idx);
365  if (currentUrl == firstVisibleUrl) {
366  popup->addSeparator();
367  }
368  action->setData(QVariant(currentUrl.prettyUrl()));
369  popup->addAction(action);
370 
371  ++idx;
372  spacer.append(" ");
373  dirName = path.section('/', idx, idx);
374  } while (!dirName.isEmpty());
375 
376  const QPoint pos = q->mapToGlobal(m_dropDownButton->geometry().bottomRight());
377  const QAction* activatedAction = popup->exec(pos);
378  if (activatedAction != 0) {
379  const KUrl url = KUrl(activatedAction->data().toString());
380  q->setLocationUrl(url);
381  }
382 
383  popup->deleteLater();
384 }
385 
386 void KUrlNavigator::Private::switchView()
387 {
388  m_toggleEditableMode->setFocus();
389  m_editable = !m_editable;
390  m_toggleEditableMode->setChecked(m_editable);
391  updateContent();
392  if (q->isUrlEditable()) {
393  m_pathBox->setFocus();
394  }
395 
396  emit q->requestActivation();
397  emit q->editableStateChanged(m_editable);
398 }
399 
400 void KUrlNavigator::Private::dropUrls(const KUrl& destination, QDropEvent* event)
401 {
402  const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
403  if (!urls.isEmpty()) {
404  emit q->urlsDropped(destination, event);
405 
406 #ifndef KDE_NO_DEPRECATED
407  // KDE5: remove, as the signal has been replaced by
408  // urlsDropped(const KUrl& destination, QDropEvent* event)
409  emit q->urlsDropped(urls, destination);
410 #endif
411  }
412 }
413 
414 void KUrlNavigator::Private::slotNavigatorButtonClicked(const KUrl& url, Qt::MouseButton button)
415 {
416  if (button & Qt::LeftButton) {
417  q->setLocationUrl(url);
418  } else if (button & Qt::MidButton) {
419  emit q->tabRequested(url);
420  }
421 }
422 
423 void KUrlNavigator::Private::openContextMenu()
424 {
425  q->setActive(true);
426 
427  KMenu popup(q);
428 
429  // provide 'Copy' action, which copies the current URL of
430  // the URL navigator into the clipboard
431  QAction* copyAction = popup.addAction(KIcon("edit-copy"), i18n("Copy"));
432 
433  // provide 'Paste' action, which copies the current clipboard text
434  // into the URL navigator
435  QAction* pasteAction = popup.addAction(KIcon("edit-paste"), i18n("Paste"));
436  QClipboard* clipboard = QApplication::clipboard();
437  pasteAction->setEnabled(!clipboard->text().isEmpty());
438 
439  popup.addSeparator();
440 
441  // provide radiobuttons for toggling between the edit and the navigation mode
442  QAction* editAction = popup.addAction(i18n("Edit"));
443  editAction->setCheckable(true);
444 
445  QAction* navigateAction = popup.addAction(i18n("Navigate"));
446  navigateAction->setCheckable(true);
447 
448  QActionGroup* modeGroup = new QActionGroup(&popup);
449  modeGroup->addAction(editAction);
450  modeGroup->addAction(navigateAction);
451  if (q->isUrlEditable()) {
452  editAction->setChecked(true);
453  } else {
454  navigateAction->setChecked(true);
455  }
456 
457  popup.addSeparator();
458 
459  // allow showing of the full path
460  QAction* showFullPathAction = popup.addAction(i18n("Show Full Path"));
461  showFullPathAction->setCheckable(true);
462  showFullPathAction->setChecked(q->showFullPath());
463 
464  QAction* activatedAction = popup.exec(QCursor::pos());
465  if (activatedAction == copyAction) {
466  QMimeData* mimeData = new QMimeData();
467  mimeData->setText(q->locationUrl().pathOrUrl());
468  clipboard->setMimeData(mimeData);
469  } else if (activatedAction == pasteAction) {
470  q->setLocationUrl(KUrl(clipboard->text()));
471  } else if (activatedAction == editAction) {
472  q->setUrlEditable(true);
473  } else if (activatedAction == navigateAction) {
474  q->setUrlEditable(false);
475  } else if (activatedAction == showFullPathAction) {
476  q->setShowFullPath(showFullPathAction->isChecked());
477  }
478 }
479 
480 void KUrlNavigator::Private::slotPathBoxChanged(const QString& text)
481 {
482  if (text.isEmpty()) {
483  const QString protocol = q->locationUrl().protocol();
484  m_protocols->setProtocol(protocol);
485  m_protocols->show();
486  } else {
487  m_protocols->hide();
488  }
489 }
490 
491 void KUrlNavigator::Private::updateContent()
492 {
493  const KUrl currentUrl = q->locationUrl();
494  if (m_placesSelector != 0) {
495  m_placesSelector->updateSelection(currentUrl);
496  }
497 
498  if (m_editable) {
499  m_protocols->hide();
500  m_dropDownButton->hide();
501 
502  deleteButtons();
503  m_toggleEditableMode->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
504  q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
505 
506  m_pathBox->show();
507  m_pathBox->setUrl(currentUrl);
508  } else {
509  m_pathBox->hide();
510 
511  m_protocols->hide();
512 
513  m_toggleEditableMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
514  q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
515 
516  // Calculate the start index for the directories that should be shown as buttons
517  // and create the buttons
518  KUrl placeUrl;
519  if ((m_placesSelector != 0) && !m_showFullPath) {
520  placeUrl = m_placesSelector->selectedPlaceUrl();
521  }
522 
523  QString placePath = placeUrl.isValid() ? placeUrl.pathOrUrl() : retrievePlacePath();
524  removeTrailingSlash(placePath);
525 
526  const int startIndex = placePath.count('/');
527  updateButtons(startIndex);
528  }
529 }
530 
531 void KUrlNavigator::Private::updateButtons(int startIndex)
532 {
533  KUrl currentUrl = q->locationUrl();
534 
535  const QString path = currentUrl.pathOrUrl();
536 
537  bool createButton = false;
538  const int oldButtonCount = m_navButtons.count();
539 
540  int idx = startIndex;
541  bool hasNext = true;
542  do {
543  createButton = (idx - startIndex >= oldButtonCount);
544  const bool isFirstButton = (idx == startIndex);
545  const QString dirName = path.section(QLatin1Char('/'), idx, idx);
546  hasNext = isFirstButton || !dirName.isEmpty();
547  if (hasNext) {
548  KUrlNavigatorButton* button = 0;
549  if (createButton) {
550  button = new KUrlNavigatorButton(buttonUrl(idx), q);
551  button->installEventFilter(q);
552  button->setForegroundRole(QPalette::WindowText);
553  connect(button, SIGNAL(urlsDropped(KUrl,QDropEvent*)),
554  q, SLOT(dropUrls(KUrl,QDropEvent*)));
555  connect(button, SIGNAL(clicked(KUrl,Qt::MouseButton)),
556  q, SLOT(slotNavigatorButtonClicked(KUrl,Qt::MouseButton)));
557  connect(button, SIGNAL(finishedTextResolving()),
558  q, SLOT(updateButtonVisibility()));
559  appendWidget(button);
560  } else {
561  button = m_navButtons[idx - startIndex];
562  button->setUrl(buttonUrl(idx));
563  }
564 
565  if (isFirstButton) {
566  button->setText(firstButtonText());
567  }
568  button->setActive(q->isActive());
569 
570  if (createButton) {
571  if (!isFirstButton) {
572  setTabOrder(m_navButtons.last(), button);
573  }
574  m_navButtons.append(button);
575  }
576 
577  ++idx;
578  button->setActiveSubDirectory(path.section(QLatin1Char('/'), idx, idx));
579  }
580  } while (hasNext);
581 
582  // delete buttons which are not used anymore
583  const int newButtonCount = idx - startIndex;
584  if (newButtonCount < oldButtonCount) {
585  const QList<KUrlNavigatorButton*>::iterator itBegin = m_navButtons.begin() + newButtonCount;
586  const QList<KUrlNavigatorButton*>::iterator itEnd = m_navButtons.end();
587  QList<KUrlNavigatorButton*>::iterator it = itBegin;
588  while (it != itEnd) {
589  (*it)->hide();
590  (*it)->deleteLater();
591  ++it;
592  }
593  m_navButtons.erase(itBegin, itEnd);
594  }
595 
596  setTabOrder(m_dropDownButton, m_navButtons.first());
597  setTabOrder(m_navButtons.last(), m_toggleEditableMode);
598 
599  updateButtonVisibility();
600 }
601 
602 void KUrlNavigator::Private::updateButtonVisibility()
603 {
604  if (m_editable) {
605  return;
606  }
607 
608  const int buttonsCount = m_navButtons.count();
609  if (buttonsCount == 0) {
610  m_dropDownButton->hide();
611  return;
612  }
613 
614  // Subtract all widgets from the available width, that must be shown anyway
615  int availableWidth = q->width() - m_toggleEditableMode->minimumWidth();
616 
617  if ((m_placesSelector != 0) && m_placesSelector->isVisible()) {
618  availableWidth -= m_placesSelector->width();
619  }
620 
621  if ((m_protocols != 0) && m_protocols->isVisible()) {
622  availableWidth -= m_protocols->width();
623  }
624 
625  // Check whether buttons must be hidden at all...
626  int requiredButtonWidth = 0;
627  foreach (const KUrlNavigatorButton* button, m_navButtons) {
628  requiredButtonWidth += button->minimumWidth();
629  }
630 
631  if (requiredButtonWidth > availableWidth) {
632  // At least one button must be hidden. This implies that the
633  // drop-down button must get visible, which again decreases the
634  // available width.
635  availableWidth -= m_dropDownButton->width();
636  }
637 
638  // Hide buttons...
639  QList<KUrlNavigatorButton*>::const_iterator it = m_navButtons.constEnd();
640  const QList<KUrlNavigatorButton*>::const_iterator itBegin = m_navButtons.constBegin();
641  bool isLastButton = true;
642  bool hasHiddenButtons = false;
643 
644  QLinkedList<KUrlNavigatorButton*> buttonsToShow;
645  while (it != itBegin) {
646  --it;
647  KUrlNavigatorButton* button = (*it);
648  availableWidth -= button->minimumWidth();
649  if ((availableWidth <= 0) && !isLastButton) {
650  button->hide();
651  hasHiddenButtons = true;
652  }
653  else {
654  // Don't show the button immediately, as setActive()
655  // might change the size and a relayout gets triggered
656  // after showing the button. So the showing of all buttons
657  // is postponed until all buttons have the correct
658  // activation state.
659  buttonsToShow.append(button);
660  }
661  isLastButton = false;
662  }
663 
664  // All buttons have the correct activation state and
665  // can be shown now
666  foreach (KUrlNavigatorButton* button, buttonsToShow) {
667  button->show();
668  }
669 
670  if (hasHiddenButtons) {
671  m_dropDownButton->show();
672  } else {
673  // Check whether going upwards is possible. If this is the case, show the drop-down button.
674  KUrl url = m_navButtons.front()->url();
675  url.adjustPath(KUrl::AddTrailingSlash);
676  const bool visible = !url.equals(url.upUrl()) && (url.protocol() != "nepomuksearch");
677  m_dropDownButton->setVisible(visible);
678  }
679 }
680 
681 QString KUrlNavigator::Private::firstButtonText() const
682 {
683  QString text;
684 
685  // The first URL navigator button should get the name of the
686  // place instead of the directory name
687  if ((m_placesSelector != 0) && !m_showFullPath) {
688  const KUrl placeUrl = m_placesSelector->selectedPlaceUrl();
689  text = m_placesSelector->selectedPlaceText();
690  }
691 
692  if (text.isEmpty()) {
693  const KUrl currentUrl = q->locationUrl();
694  if (currentUrl.isLocalFile()) {
695 #ifdef Q_OS_WIN
696  text = currentUrl.path().length() > 1 ? currentUrl.path().left(2) : QDir::rootPath();
697 #else
698  text = m_showFullPath ? QLatin1String("/") : i18n("Custom Path");
699 #endif
700  } else {
701  text = currentUrl.protocol() + QLatin1Char(':');
702  if (!currentUrl.host().isEmpty()) {
703  text += QLatin1Char(' ') + currentUrl.host();
704  }
705  }
706  }
707 
708  return text;
709 }
710 
711 KUrl KUrlNavigator::Private::buttonUrl(int index) const
712 {
713  if (index < 0) {
714  index = 0;
715  }
716 
717  // Keep scheme, hostname etc. as this is needed for e. g. browsing
718  // FTP directories
719  const KUrl currentUrl = q->locationUrl();
720  KUrl newUrl = currentUrl;
721  newUrl.setPath(QString());
722 
723  QString pathOrUrl = currentUrl.pathOrUrl();
724  if (!pathOrUrl.isEmpty()) {
725  if (index == 0) {
726  // prevent the last "/" from being stripped
727  // or we end up with an empty path
728 #ifdef Q_OS_WIN
729  pathOrUrl = pathOrUrl.length() > 1 ? pathOrUrl.left(2) : QDir::rootPath();
730 #else
731  pathOrUrl = QLatin1String("/");
732 #endif
733  } else {
734  pathOrUrl = pathOrUrl.section('/', 0, index);
735  }
736  }
737 
738  newUrl.setPath(KUrl(pathOrUrl).path());
739  return newUrl;
740 }
741 
742 void KUrlNavigator::Private::switchToBreadcrumbMode()
743 {
744  q->setUrlEditable(false);
745 }
746 
747 void KUrlNavigator::Private::deleteButtons()
748 {
749  foreach (KUrlNavigatorButton* button, m_navButtons) {
750  button->hide();
751  button->deleteLater();
752  }
753  m_navButtons.clear();
754 }
755 
756 QString KUrlNavigator::Private::retrievePlacePath() const
757 {
758  const KUrl currentUrl = q->locationUrl();
759  const QString path = currentUrl.pathOrUrl();
760  int idx = path.indexOf(QLatin1String("///"));
761  if (idx >= 0) {
762  idx += 3;
763  } else {
764  idx = path.indexOf(QLatin1String("//"));
765  idx = path.indexOf(QLatin1Char('/'), (idx < 0) ? 0 : idx + 2);
766  }
767 
768  QString placePath = (idx < 0) ? path : path.left(idx);
769  removeTrailingSlash(placePath);
770  return placePath;
771 }
772 
773 bool KUrlNavigator::Private::isCompressedPath(const KUrl& url) const
774 {
775  const KMimeType::Ptr mime = KMimeType::findByPath(url.path(KUrl::RemoveTrailingSlash));
776  // Note: this list of MIME types depends on the protocols implemented by kio_archive
777  return mime->is("application/x-compressed-tar") ||
778  mime->is("application/x-bzip-compressed-tar") ||
779  mime->is("application/x-lzma-compressed-tar") ||
780  mime->is("application/x-xz-compressed-tar") ||
781  mime->is("application/x-tar") ||
782  mime->is("application/x-tarz") ||
783  mime->is("application/x-tzo") || // (not sure KTar supports those?)
784  mime->is("application/zip") ||
785  mime->is("application/x-archive");
786 }
787 
788 void KUrlNavigator::Private::removeTrailingSlash(QString& url) const
789 {
790  const int length = url.length();
791  if ((length > 0) && (url.at(length - 1) == QChar('/'))) {
792  url.remove(length - 1, 1);
793  }
794 }
795 
796 int KUrlNavigator::Private::adjustedHistoryIndex(int historyIndex) const
797 {
798  if (historyIndex < 0) {
799  historyIndex = m_historyIndex;
800  } else if (historyIndex >= m_history.size()) {
801  historyIndex = m_history.size() - 1;
802  Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0
803  }
804  return historyIndex;
805 }
806 
807 // ------------------------------------------------------------------------------------------------
808 
809 KUrlNavigator::KUrlNavigator(QWidget* parent) :
810  QWidget(parent),
811  d(new Private(this, 0))
812 {
813  d->initialize(KUrl());
814 }
815 
816 KUrlNavigator::KUrlNavigator(KFilePlacesModel* placesModel,
817  const KUrl& url,
818  QWidget* parent) :
819  QWidget(parent),
820  d(new Private(this, placesModel))
821 {
822  d->initialize(url);
823 }
824 
825 KUrlNavigator::~KUrlNavigator()
826 {
827  delete d;
828 }
829 
830 KUrl KUrlNavigator::locationUrl(int historyIndex) const
831 {
832  historyIndex = d->adjustedHistoryIndex(historyIndex);
833  return d->m_history[historyIndex].url;
834 }
835 
836 void KUrlNavigator::saveLocationState(const QByteArray& state)
837 {
838  d->m_history[d->m_historyIndex].state = state;
839 }
840 
841 QByteArray KUrlNavigator::locationState(int historyIndex) const
842 {
843  historyIndex = d->adjustedHistoryIndex(historyIndex);
844  return d->m_history[historyIndex].state;
845 }
846 
847 bool KUrlNavigator::goBack()
848 {
849  const int count = d->m_history.count();
850  if (d->m_historyIndex < count - 1) {
851  const KUrl newUrl = locationUrl(d->m_historyIndex + 1);
852  emit urlAboutToBeChanged(newUrl);
853 
854  ++d->m_historyIndex;
855  d->updateContent();
856 
857  emit historyChanged();
858  emit urlChanged(locationUrl());
859  return true;
860  }
861 
862  return false;
863 }
864 
865 bool KUrlNavigator::goForward()
866 {
867  if (d->m_historyIndex > 0) {
868  const KUrl newUrl = locationUrl(d->m_historyIndex - 1);
869  emit urlAboutToBeChanged(newUrl);
870 
871  --d->m_historyIndex;
872  d->updateContent();
873 
874  emit historyChanged();
875  emit urlChanged(locationUrl());
876  return true;
877  }
878 
879  return false;
880 }
881 
882 bool KUrlNavigator::goUp()
883 {
884  const KUrl currentUrl = locationUrl();
885  const KUrl upUrl = currentUrl.upUrl();
886  if (upUrl != currentUrl) {
887  setLocationUrl(upUrl);
888  return true;
889  }
890 
891  return false;
892 }
893 
894 void KUrlNavigator::goHome()
895 {
896  if (d->m_homeUrl.isEmpty() || !d->m_homeUrl.isValid()) {
897  setLocationUrl(KUrl(QDir::homePath()));
898  } else {
899  setLocationUrl(d->m_homeUrl);
900  }
901 }
902 
903 void KUrlNavigator::setHomeUrl(const KUrl& url)
904 {
905  d->m_homeUrl = url;
906 }
907 
908 KUrl KUrlNavigator::homeUrl() const
909 {
910  return d->m_homeUrl;
911 }
912 
913 void KUrlNavigator::setUrlEditable(bool editable)
914 {
915  if (d->m_editable != editable) {
916  d->switchView();
917  }
918 }
919 
920 bool KUrlNavigator::isUrlEditable() const
921 {
922  return d->m_editable;
923 }
924 
925 void KUrlNavigator::setShowFullPath(bool show)
926 {
927  if (d->m_showFullPath != show) {
928  d->m_showFullPath = show;
929  d->updateContent();
930  }
931 }
932 
933 bool KUrlNavigator::showFullPath() const
934 {
935  return d->m_showFullPath;
936 }
937 
938 
939 void KUrlNavigator::setActive(bool active)
940 {
941  if (active != d->m_active) {
942  d->m_active = active;
943 
944  d->m_dropDownButton->setActive(active);
945  foreach(KUrlNavigatorButton* button, d->m_navButtons) {
946  button->setActive(active);
947  }
948 
949  update();
950  if (active) {
951  emit activated();
952  }
953  }
954 }
955 
956 bool KUrlNavigator::isActive() const
957 {
958  return d->m_active;
959 }
960 
961 void KUrlNavigator::setPlacesSelectorVisible(bool visible)
962 {
963  if (visible == d->m_showPlacesSelector) {
964  return;
965  }
966 
967  if (visible && (d->m_placesSelector == 0)) {
968  // the places selector cannot get visible as no
969  // places model is available
970  return;
971  }
972 
973  d->m_showPlacesSelector = visible;
974  d->m_placesSelector->setVisible(visible);
975 }
976 
977 bool KUrlNavigator::isPlacesSelectorVisible() const
978 {
979  return d->m_showPlacesSelector;
980 }
981 
982 KUrl KUrlNavigator::uncommittedUrl() const
983 {
984  KUriFilterData filteredData(d->m_pathBox->currentText().trimmed());
985  filteredData.setCheckForExecutables(false);
986  if (KUriFilter::self()->filterUri(filteredData, QStringList() << "kshorturifilter" << "kurisearchfilter")) {
987  return filteredData.uri();
988  }
989  else {
990  return KUrl(filteredData.typedString());
991  }
992 }
993 
994 void KUrlNavigator::setLocationUrl(const KUrl& newUrl)
995 {
996  if (newUrl == locationUrl()) {
997  return;
998  }
999 
1000  KUrl url = newUrl;
1001  url.cleanPath();
1002 
1003  if ((url.protocol() == QLatin1String("tar")) || (url.protocol() == QLatin1String("zip"))) {
1004  // The URL represents a tar- or zip-file. Check whether
1005  // the URL is really part of the tar- or zip-file, otherwise
1006  // replace it by the local path again.
1007  bool insideCompressedPath = d->isCompressedPath(url);
1008  if (!insideCompressedPath) {
1009  KUrl prevUrl = url;
1010  KUrl parentUrl = url.upUrl();
1011  while (parentUrl != prevUrl) {
1012  if (d->isCompressedPath(parentUrl)) {
1013  insideCompressedPath = true;
1014  break;
1015  }
1016  prevUrl = parentUrl;
1017  parentUrl = parentUrl.upUrl();
1018  }
1019  }
1020  if (!insideCompressedPath) {
1021  // drop the tar: or zip: protocol since we are not
1022  // inside the compressed path
1023  url.setProtocol("file");
1024  }
1025  }
1026 
1027  // Check whether current history element has the same URL.
1028  // If this is the case, just ignore setting the URL.
1029  const LocationData& data = d->m_history[d->m_historyIndex];
1030  const bool isUrlEqual = url.equals(locationUrl(), KUrl::CompareWithoutTrailingSlash) ||
1031  (!url.isValid() && url.equals(data.url, KUrl::CompareWithoutTrailingSlash));
1032  if (isUrlEqual) {
1033  return;
1034  }
1035 
1036  emit urlAboutToBeChanged(url);
1037 
1038  if (d->m_historyIndex > 0) {
1039  // If an URL is set when the history index is not at the end (= 0),
1040  // then clear all previous history elements so that a new history
1041  // tree is started from the current position.
1042  QList<LocationData>::iterator begin = d->m_history.begin();
1043  QList<LocationData>::iterator end = begin + d->m_historyIndex;
1044  d->m_history.erase(begin, end);
1045  d->m_historyIndex = 0;
1046  }
1047 
1048  Q_ASSERT(d->m_historyIndex == 0);
1049  LocationData newData;
1050  newData.url = url;
1051  d->m_history.insert(0, newData);
1052 
1053  // Prevent an endless growing of the history: remembering
1054  // the last 100 Urls should be enough...
1055  const int historyMax = 100;
1056  if (d->m_history.size() > historyMax) {
1057  QList<LocationData>::iterator begin = d->m_history.begin() + historyMax;
1058  QList<LocationData>::iterator end = d->m_history.end();
1059  d->m_history.erase(begin, end);
1060  }
1061 
1062  emit historyChanged();
1063  emit urlChanged(url);
1064 
1065  d->updateContent();
1066 
1067  requestActivation();
1068 }
1069 
1070 void KUrlNavigator::requestActivation()
1071 {
1072  setActive(true);
1073 }
1074 
1075 void KUrlNavigator::setFocus()
1076 {
1077  if (isUrlEditable()) {
1078  d->m_pathBox->setFocus();
1079  } else {
1080  QWidget::setFocus();
1081  }
1082 }
1083 
1084 #ifndef KDE_NO_DEPRECATED
1085 void KUrlNavigator::setUrl(const KUrl& url)
1086 {
1087  // deprecated
1088  setLocationUrl(url);
1089 }
1090 #endif
1091 
1092 #ifndef KDE_NO_DEPRECATED
1093 void KUrlNavigator::saveRootUrl(const KUrl& url)
1094 {
1095  // deprecated
1096  d->m_history[d->m_historyIndex].rootUrl = url;
1097 }
1098 #endif
1099 
1100 #ifndef KDE_NO_DEPRECATED
1101 void KUrlNavigator::savePosition(int x, int y)
1102 {
1103  // deprecated
1104  d->m_history[d->m_historyIndex].pos = QPoint(x, y);
1105 }
1106 #endif
1107 
1108 void KUrlNavigator::keyPressEvent(QKeyEvent* event)
1109 {
1110  if (isUrlEditable() && (event->key() == Qt::Key_Escape)) {
1111  setUrlEditable(false);
1112  } else {
1113  QWidget::keyPressEvent(event);
1114  }
1115 }
1116 
1117 void KUrlNavigator::keyReleaseEvent(QKeyEvent* event)
1118 {
1119  QWidget::keyReleaseEvent(event);
1120 }
1121 
1122 void KUrlNavigator::mouseReleaseEvent(QMouseEvent* event)
1123 {
1124  if (event->button() == Qt::MidButton) {
1125  const QRect bounds = d->m_toggleEditableMode->geometry();
1126  if (bounds.contains(event->pos())) {
1127  // The middle mouse button has been clicked above the
1128  // toggle-editable-mode-button. Paste the clipboard content
1129  // as location URL.
1130  QClipboard* clipboard = QApplication::clipboard();
1131  const QMimeData* mimeData = clipboard->mimeData();
1132  if (mimeData->hasText()) {
1133  const QString text = mimeData->text();
1134  setLocationUrl(KUrl(text));
1135  }
1136  }
1137  }
1138  QWidget::mouseReleaseEvent(event);
1139 }
1140 
1141 void KUrlNavigator::resizeEvent(QResizeEvent* event)
1142 {
1143  QTimer::singleShot(0, this, SLOT(updateButtonVisibility()));
1144  QWidget::resizeEvent(event);
1145 }
1146 
1147 void KUrlNavigator::wheelEvent(QWheelEvent* event)
1148 {
1149  setActive(true);
1150  QWidget::wheelEvent(event);
1151 }
1152 
1153 bool KUrlNavigator::eventFilter(QObject* watched, QEvent* event)
1154 {
1155  switch (event->type()) {
1156  case QEvent::FocusIn:
1157  if (watched == d->m_pathBox) {
1158  requestActivation();
1159  setFocus();
1160  }
1161  foreach (KUrlNavigatorButton* button, d->m_navButtons) {
1162  button->setShowMnemonic(true);
1163  }
1164  break;
1165 
1166  case QEvent::FocusOut:
1167  foreach (KUrlNavigatorButton* button, d->m_navButtons) {
1168  button->setShowMnemonic(false);
1169  }
1170  break;
1171 
1172  default:
1173  break;
1174  }
1175 
1176  return QWidget::eventFilter(watched, event);
1177 }
1178 
1179 int KUrlNavigator::historySize() const
1180 {
1181  return d->m_history.count();
1182 }
1183 
1184 int KUrlNavigator::historyIndex() const
1185 {
1186  return d->m_historyIndex;
1187 }
1188 
1189 KUrlComboBox* KUrlNavigator::editor() const
1190 {
1191  return d->m_pathBox;
1192 }
1193 
1194 void KUrlNavigator::setCustomProtocols(const QStringList &protocols)
1195 {
1196  d->m_customProtocols = protocols;
1197  d->m_protocols->setCustomProtocols(d->m_customProtocols);
1198 }
1199 
1200 QStringList KUrlNavigator::customProtocols() const
1201 {
1202  return d->m_customProtocols;
1203 }
1204 
1205 #ifndef KDE_NO_DEPRECATED
1206 const KUrl& KUrlNavigator::url() const
1207 {
1208  // deprecated
1209 
1210  // Workaround required because of flawed interface ('const KUrl&' is returned
1211  // instead of 'KUrl'): remember the URL to prevent a dangling pointer
1212  static KUrl url;
1213  url = locationUrl();
1214  return url;
1215 }
1216 #endif
1217 
1218 #ifndef KDE_NO_DEPRECATED
1219 KUrl KUrlNavigator::url(int index) const
1220 {
1221  // deprecated
1222  return d->buttonUrl(index);
1223 }
1224 #endif
1225 
1226 #ifndef KDE_NO_DEPRECATED
1227 KUrl KUrlNavigator::historyUrl(int historyIndex) const
1228 {
1229  // deprecated
1230  return locationUrl(historyIndex);
1231 }
1232 #endif
1233 
1234 #ifndef KDE_NO_DEPRECATED
1235 const KUrl& KUrlNavigator::savedRootUrl() const
1236 {
1237  // deprecated
1238 
1239  // Workaround required because of flawed interface ('const KUrl&' is returned
1240  // instead of 'KUrl'): remember the root URL to prevent a dangling pointer
1241  static KUrl rootUrl;
1242  rootUrl = d->m_history[d->m_historyIndex].rootUrl;
1243  return rootUrl;
1244 }
1245 #endif
1246 
1247 #ifndef KDE_NO_DEPRECATED
1248 QPoint KUrlNavigator::savedPosition() const
1249 {
1250  // deprecated
1251  return d->m_history[d->m_historyIndex].pos;
1252 }
1253 #endif
1254 
1255 #ifndef KDE_NO_DEPRECATED
1256 void KUrlNavigator::setHomeUrl(const QString& homeUrl)
1257 {
1258  // deprecated
1259  setLocationUrl(KUrl(homeUrl));
1260 }
1261 #endif
1262 
1263 #include "kurlnavigator.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Thu Feb 21 2013 11:20:53 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KFile

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

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal