File indexing completed on 2025-01-19 03:41:33
0001 /* 0002 SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at> 0003 SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org> 0004 SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org> 0005 SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "kcoreurlnavigator.h" 0011 0012 #include "urlutil_p.h" 0013 0014 #include <KIO/StatJob> 0015 #include <KLocalizedString> 0016 #include <kfileitem.h> 0017 #include <kprotocolinfo.h> 0018 0019 #include <QDir> 0020 #include <QMimeData> 0021 #include <QMimeDatabase> 0022 0023 #include <algorithm> 0024 #include <numeric> 0025 0026 struct LocationData { 0027 QUrl url; 0028 QVariant state; 0029 }; 0030 0031 class KCoreUrlNavigatorPrivate 0032 { 0033 public: 0034 KCoreUrlNavigatorPrivate(KCoreUrlNavigator *qq); 0035 0036 ~KCoreUrlNavigatorPrivate() 0037 { 0038 } 0039 0040 /** 0041 * Returns true, if the MIME type of the path represents a 0042 * compressed file like TAR or ZIP, as listed in @p archiveMimetypes 0043 */ 0044 bool isCompressedPath(const QUrl &path, const QStringList &archiveMimetypes) const; 0045 0046 /** 0047 * Returns the current history index, if \a historyIndex is 0048 * smaller than 0. If \a historyIndex is greater or equal than 0049 * the number of available history items, the largest possible 0050 * history index is returned. For the other cases just \a historyIndex 0051 * is returned. 0052 */ 0053 int adjustedHistoryIndex(int historyIndex) const; 0054 0055 KCoreUrlNavigator *const q; 0056 0057 QList<LocationData> m_history; 0058 int m_historyIndex = 0; 0059 }; 0060 0061 KCoreUrlNavigatorPrivate::KCoreUrlNavigatorPrivate(KCoreUrlNavigator *qq) 0062 : q(qq) 0063 { 0064 } 0065 0066 bool KCoreUrlNavigatorPrivate::isCompressedPath(const QUrl &url, const QStringList &archiveMimetypes) const 0067 { 0068 QMimeDatabase db; 0069 const QMimeType mime = db.mimeTypeForUrl(QUrl(url.toString(QUrl::StripTrailingSlash))); 0070 return std::any_of(archiveMimetypes.begin(), archiveMimetypes.end(), [mime](const QString &archiveType) { 0071 return mime.inherits(archiveType); 0072 }); 0073 } 0074 0075 int KCoreUrlNavigatorPrivate::adjustedHistoryIndex(int historyIndex) const 0076 { 0077 const int historySize = m_history.size(); 0078 if (historyIndex < 0) { 0079 historyIndex = m_historyIndex; 0080 } else if (historyIndex >= historySize) { 0081 historyIndex = historySize - 1; 0082 Q_ASSERT(historyIndex >= 0); // m_history.size() must always be > 0 0083 } 0084 return historyIndex; 0085 } 0086 0087 // ------------------------------------------------------------------------------------------------ 0088 0089 KCoreUrlNavigator::KCoreUrlNavigator(const QUrl &url, QObject *parent) 0090 : QObject(parent) 0091 , d(new KCoreUrlNavigatorPrivate(this)) 0092 { 0093 d->m_history.prepend(LocationData{url.adjusted(QUrl::NormalizePathSegments), {}}); 0094 } 0095 0096 KCoreUrlNavigator::~KCoreUrlNavigator() 0097 { 0098 } 0099 0100 QUrl KCoreUrlNavigator::locationUrl(int historyIndex) const 0101 { 0102 historyIndex = d->adjustedHistoryIndex(historyIndex); 0103 return d->m_history.at(historyIndex).url; 0104 } 0105 0106 void KCoreUrlNavigator::saveLocationState(const QVariant &state) 0107 { 0108 d->m_history[d->m_historyIndex].state = state; 0109 } 0110 0111 QVariant KCoreUrlNavigator::locationState(int historyIndex) const 0112 { 0113 historyIndex = d->adjustedHistoryIndex(historyIndex); 0114 return d->m_history.at(historyIndex).state; 0115 } 0116 0117 bool KCoreUrlNavigator::goBack() 0118 { 0119 const int count = d->m_history.size(); 0120 if (d->m_historyIndex < count - 1) { 0121 const QUrl newUrl = locationUrl(d->m_historyIndex + 1); 0122 Q_EMIT currentUrlAboutToChange(newUrl); 0123 0124 ++d->m_historyIndex; 0125 0126 Q_EMIT historyIndexChanged(); 0127 Q_EMIT historyChanged(); 0128 Q_EMIT currentLocationUrlChanged(); 0129 return true; 0130 } 0131 0132 return false; 0133 } 0134 0135 bool KCoreUrlNavigator::goForward() 0136 { 0137 if (d->m_historyIndex > 0) { 0138 const QUrl newUrl = locationUrl(d->m_historyIndex - 1); 0139 Q_EMIT currentUrlAboutToChange(newUrl); 0140 0141 --d->m_historyIndex; 0142 0143 Q_EMIT historyIndexChanged(); 0144 Q_EMIT historyChanged(); 0145 Q_EMIT currentLocationUrlChanged(); 0146 return true; 0147 } 0148 0149 return false; 0150 } 0151 0152 bool KCoreUrlNavigator::goUp() 0153 { 0154 const QUrl currentUrl = locationUrl(); 0155 const QUrl upUrl = KIO::upUrl(currentUrl); 0156 if (!currentUrl.matches(upUrl, QUrl::StripTrailingSlash)) { 0157 setCurrentLocationUrl(upUrl); 0158 return true; 0159 } 0160 0161 return false; 0162 } 0163 0164 QUrl KCoreUrlNavigator::currentLocationUrl() const 0165 { 0166 return locationUrl(); 0167 } 0168 0169 void KCoreUrlNavigator::setCurrentLocationUrl(const QUrl &newUrl) 0170 { 0171 if (newUrl == locationUrl()) { 0172 return; 0173 } 0174 0175 QUrl url = newUrl.adjusted(QUrl::NormalizePathSegments); 0176 0177 // This will be used below; we define it here because in the lower part of the 0178 // code locationUrl() and url become the same URLs 0179 QUrl firstChildUrl = KIO::UrlUtil::firstChildUrl(locationUrl(), url); 0180 0181 const QString scheme = url.scheme(); 0182 if (!scheme.isEmpty()) { 0183 // Check if the URL represents a tar-, zip- or 7z-file, or an archive file supported by krarc. 0184 const QStringList archiveMimetypes = KProtocolInfo::archiveMimetypes(scheme); 0185 0186 if (!archiveMimetypes.isEmpty()) { 0187 // Check whether the URL is really part of the archive file, otherwise 0188 // replace it by the local path again. 0189 bool insideCompressedPath = d->isCompressedPath(url, archiveMimetypes); 0190 if (!insideCompressedPath) { 0191 QUrl prevUrl = url; 0192 QUrl parentUrl = KIO::upUrl(url); 0193 while (parentUrl != prevUrl) { 0194 if (d->isCompressedPath(parentUrl, archiveMimetypes)) { 0195 insideCompressedPath = true; 0196 break; 0197 } 0198 prevUrl = parentUrl; 0199 parentUrl = KIO::upUrl(parentUrl); 0200 } 0201 } 0202 if (!insideCompressedPath) { 0203 // drop the tar:, zip:, sevenz: or krarc: protocol since we are not 0204 // inside the compressed path 0205 url.setScheme(QStringLiteral("file")); 0206 firstChildUrl.setScheme(QStringLiteral("file")); 0207 } 0208 } 0209 } 0210 0211 // Check whether current history element has the same URL. 0212 // If this is the case, just ignore setting the URL. 0213 const LocationData &data = d->m_history.at(d->m_historyIndex); 0214 const bool isUrlEqual = url.matches(locationUrl(), QUrl::StripTrailingSlash) || (!url.isValid() && url.matches(data.url, QUrl::StripTrailingSlash)); 0215 if (isUrlEqual) { 0216 return; 0217 } 0218 0219 Q_EMIT currentUrlAboutToChange(url); 0220 0221 if (d->m_historyIndex > 0) { 0222 // If an URL is set when the history index is not at the end (= 0), 0223 // then clear all previous history elements so that a new history 0224 // tree is started from the current position. 0225 auto begin = d->m_history.begin(); 0226 auto end = begin + d->m_historyIndex; 0227 d->m_history.erase(begin, end); 0228 d->m_historyIndex = 0; 0229 } 0230 0231 Q_ASSERT(d->m_historyIndex == 0); 0232 d->m_history.insert(0, LocationData{url, {}}); 0233 0234 // Prevent an endless growing of the history: remembering 0235 // the last 100 Urls should be enough... 0236 const int historyMax = 100; 0237 if (d->m_history.size() > historyMax) { 0238 auto begin = d->m_history.begin() + historyMax; 0239 auto end = d->m_history.end(); 0240 d->m_history.erase(begin, end); 0241 } 0242 0243 Q_EMIT historyIndexChanged(); 0244 Q_EMIT historySizeChanged(); 0245 Q_EMIT historyChanged(); 0246 Q_EMIT currentLocationUrlChanged(); 0247 if (firstChildUrl.isValid()) { 0248 Q_EMIT urlSelectionRequested(firstChildUrl); 0249 } 0250 } 0251 0252 int KCoreUrlNavigator::historySize() const 0253 { 0254 return d->m_history.count(); 0255 } 0256 0257 int KCoreUrlNavigator::historyIndex() const 0258 { 0259 return d->m_historyIndex; 0260 } 0261 0262 #include "moc_kcoreurlnavigator.cpp"