KIO

kcoreurlnavigator.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2010 Peter Penz <[email protected]>
3  SPDX-FileCopyrightText: 2006 Aaron J. Seigo <[email protected]>
4  SPDX-FileCopyrightText: 2007 Kevin Ottens <[email protected]>
5  SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "kcoreurlnavigator.h"
11 
12 #include "urlutil_p.h"
13 
14 #include <KIO/StatJob>
15 #include <KLocalizedString>
16 #include <kfileitem.h>
17 #include <kprotocolinfo.h>
18 
19 #include <QDir>
20 #include <QMimeData>
21 #include <QMimeDatabase>
22 
23 #include <algorithm>
24 #include <numeric>
25 
26 struct LocationData {
27  QUrl url;
28  QVariant state;
29 };
30 
31 class KCoreUrlNavigatorPrivate
32 {
33 public:
34  KCoreUrlNavigatorPrivate(KCoreUrlNavigator *qq);
35 
36  ~KCoreUrlNavigatorPrivate()
37  {
38  }
39 
40  /**
41  * Returns true, if the MIME type of the path represents a
42  * compressed file like TAR or ZIP, as listed in @p archiveMimetypes
43  */
44  bool isCompressedPath(const QUrl &path, const QStringList &archiveMimetypes) const;
45 
46  /**
47  * Returns the current history index, if \a historyIndex is
48  * smaller than 0. If \a historyIndex is greater or equal than
49  * the number of available history items, the largest possible
50  * history index is returned. For the other cases just \a historyIndex
51  * is returned.
52  */
53  int adjustedHistoryIndex(int historyIndex) const;
54 
55  KCoreUrlNavigator *const q;
56 
57  QList<LocationData> m_history;
58  int m_historyIndex = 0;
59 };
60 
61 KCoreUrlNavigatorPrivate::KCoreUrlNavigatorPrivate(KCoreUrlNavigator *qq)
62  : q(qq)
63 {
64 }
65 
66 bool KCoreUrlNavigatorPrivate::isCompressedPath(const QUrl &url, const QStringList &archiveMimetypes) const
67 {
68  QMimeDatabase db;
70  return std::any_of(archiveMimetypes.begin(), archiveMimetypes.end(), [mime](const QString &archiveType) {
71  return mime.inherits(archiveType);
72  });
73 }
74 
75 int KCoreUrlNavigatorPrivate::adjustedHistoryIndex(int historyIndex) const
76 {
77  const int historySize = m_history.size();
78  if (historyIndex < 0) {
79  historyIndex = m_historyIndex;
80  } else if (historyIndex >= historySize) {
81  historyIndex = historySize - 1;
82  Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0
83  }
84  return historyIndex;
85 }
86 
87 // ------------------------------------------------------------------------------------------------
88 
89 KCoreUrlNavigator::KCoreUrlNavigator(const QUrl &url, QObject *parent)
90  : QObject(parent)
91  , d(new KCoreUrlNavigatorPrivate(this))
92 {
93  d->m_history.prepend(LocationData{url.adjusted(QUrl::NormalizePathSegments), {}});
94 }
95 
96 KCoreUrlNavigator::~KCoreUrlNavigator()
97 {
98 }
99 
100 QUrl KCoreUrlNavigator::locationUrl(int historyIndex) const
101 {
102  historyIndex = d->adjustedHistoryIndex(historyIndex);
103  return d->m_history.at(historyIndex).url;
104 }
105 
107 {
108  d->m_history[d->m_historyIndex].state = state;
109 }
110 
112 {
113  historyIndex = d->adjustedHistoryIndex(historyIndex);
114  return d->m_history.at(historyIndex).state;
115 }
116 
118 {
119  const int count = d->m_history.size();
120  if (d->m_historyIndex < count - 1) {
121  const QUrl newUrl = locationUrl(d->m_historyIndex + 1);
123 
124  ++d->m_historyIndex;
125 
126  Q_EMIT historyIndexChanged();
128  Q_EMIT currentLocationUrlChanged();
129  return true;
130  }
131 
132  return false;
133 }
134 
136 {
137  if (d->m_historyIndex > 0) {
138  const QUrl newUrl = locationUrl(d->m_historyIndex - 1);
140 
141  --d->m_historyIndex;
142 
143  Q_EMIT historyIndexChanged();
145  Q_EMIT currentLocationUrlChanged();
146  return true;
147  }
148 
149  return false;
150 }
151 
153 {
154  const QUrl currentUrl = locationUrl();
155  const QUrl upUrl = KIO::upUrl(currentUrl);
156  if (!currentUrl.matches(upUrl, QUrl::StripTrailingSlash)) {
157  setCurrentLocationUrl(upUrl);
158  return true;
159  }
160 
161  return false;
162 }
163 
164 QUrl KCoreUrlNavigator::currentLocationUrl() const
165 {
166  return locationUrl();
167 }
168 
169 void KCoreUrlNavigator::setCurrentLocationUrl(const QUrl &newUrl)
170 {
171  if (newUrl == locationUrl()) {
172  return;
173  }
174 
176 
177  // This will be used below; we define it here because in the lower part of the
178  // code locationUrl() and url become the same URLs
179  QUrl firstChildUrl = KIO::UrlUtil::firstChildUrl(locationUrl(), url);
180 
181  const QString scheme = url.scheme();
182  if (!scheme.isEmpty()) {
183  // Check if the URL represents a tar-, zip- or 7z-file, or an archive file supported by krarc.
184  const QStringList archiveMimetypes = KProtocolInfo::archiveMimetypes(scheme);
185 
186  if (!archiveMimetypes.isEmpty()) {
187  // Check whether the URL is really part of the archive file, otherwise
188  // replace it by the local path again.
189  bool insideCompressedPath = d->isCompressedPath(url, archiveMimetypes);
190  if (!insideCompressedPath) {
191  QUrl prevUrl = url;
192  QUrl parentUrl = KIO::upUrl(url);
193  while (parentUrl != prevUrl) {
194  if (d->isCompressedPath(parentUrl, archiveMimetypes)) {
195  insideCompressedPath = true;
196  break;
197  }
198  prevUrl = parentUrl;
199  parentUrl = KIO::upUrl(parentUrl);
200  }
201  }
202  if (!insideCompressedPath) {
203  // drop the tar:, zip:, sevenz: or krarc: protocol since we are not
204  // inside the compressed path
205  url.setScheme(QStringLiteral("file"));
206  firstChildUrl.setScheme(QStringLiteral("file"));
207  }
208  }
209  }
210 
211  // Check whether current history element has the same URL.
212  // If this is the case, just ignore setting the URL.
213  const LocationData &data = d->m_history.at(d->m_historyIndex);
214  const bool isUrlEqual = url.matches(locationUrl(), QUrl::StripTrailingSlash) || (!url.isValid() && url.matches(data.url, QUrl::StripTrailingSlash));
215  if (isUrlEqual) {
216  return;
217  }
218 
220 
221  if (d->m_historyIndex > 0) {
222  // If an URL is set when the history index is not at the end (= 0),
223  // then clear all previous history elements so that a new history
224  // tree is started from the current position.
225  auto begin = d->m_history.begin();
226  auto end = begin + d->m_historyIndex;
227  d->m_history.erase(begin, end);
228  d->m_historyIndex = 0;
229  }
230 
231  Q_ASSERT(d->m_historyIndex == 0);
232  d->m_history.insert(0, LocationData{url, {}});
233 
234  // Prevent an endless growing of the history: remembering
235  // the last 100 Urls should be enough...
236  const int historyMax = 100;
237  if (d->m_history.size() > historyMax) {
238  auto begin = d->m_history.begin() + historyMax;
239  auto end = d->m_history.end();
240  d->m_history.erase(begin, end);
241  }
242 
243  Q_EMIT historyIndexChanged();
244  Q_EMIT historySizeChanged();
246  Q_EMIT currentLocationUrlChanged();
247  if (firstChildUrl.isValid()) {
248  Q_EMIT urlSelectionRequested(firstChildUrl);
249  }
250 }
251 
253 {
254  return d->m_history.count();
255 }
256 
258 {
259  return d->m_historyIndex;
260 }
261 
262 #include "moc_kcoreurlnavigator.cpp"
Q_INVOKABLE bool goBack()
Goes back one step in the URL history.
Q_SIGNAL void urlSelectionRequested(const QUrl &url)
When the URL is changed and the new URL (e.g. /home/user1/) is a parent of the previous URL (e....
Q_SIGNAL void historyChanged()
Is emitted, if the history has been changed.
QMimeType mimeTypeForUrl(const QUrl &url) const const
Q_EMITQ_EMIT
QString scheme() const const
Q_INVOKABLE bool goForward()
Goes forward one step in the URL history.
const QList< QKeySequence > & begin()
bool matches(const QUrl &url, QUrl::FormattingOptions options) const const
void setScheme(const QString &scheme)
bool isValid() const const
Object that helps with keeping track of URLs in file-manager like interfaces.
QString toString(QUrl::FormattingOptions options) const const
int historyIndex
The history index of the current location, where 0 <= history index < KCoreUrlNavigator::historySize(...
StripTrailingSlash
Q_INVOKABLE QVariant locationState(int historyIndex=-1) const
bool isEmpty() const const
int historySize
The amount of locations in the history.
bool isEmpty() const const
KIOCORE_EXPORT QUrl upUrl(const QUrl &url)
This function is useful to implement the "Up" button in a file manager for example.
Definition: global.cpp:261
Q_SIGNAL void currentUrlAboutToChange(const QUrl &newUrl)
Is emitted, before the location URL is going to be changed to newUrl.
QUrl url() const
Returns the top level URL that is listed by this KCoreDirLister.
Q_INVOKABLE bool goUp()
Goes up one step of the URL path and remembers the old path in the history.
QUrl adjusted(QUrl::FormattingOptions options) const const
Q_INVOKABLE void saveLocationState(const QVariant &state)
Saves the location state described by state for the current location.
static QStringList archiveMimetypes(const QString &protocol)
Returns the list of archive MIME types handled by the KIO worker implementing this protocol.
QList::iterator begin()
QList::iterator end()
const QList< QKeySequence > & end()
Q_INVOKABLE QUrl locationUrl(int historyIndex=-1) const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Dec 6 2023 03:52:41 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.