1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2004-2007 Torsten Rahn <tackat@kde.org>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5 //
6
7
8 #include "MarbleDirs.h"
9 #include "MarbleDebug.h"
10
11 #include <QFile>
12 #include <QString>
13 #include <QStringList>
14 #include <QCoreApplication>
15
16 #include <cstdlib>
17
18 #include <QStandardPaths>
19
20 #ifdef Q_OS_WIN
21 //for getting appdata path
22 //mingw-w64 Internet Explorer 5.01
23 #define _WIN32_IE 0x0501
24 #include <shlobj.h>
25 #endif
26
27 #ifdef Q_OS_MACX
28 //for getting app bundle path
29 #include <ApplicationServices/ApplicationServices.h>
30 #endif
31
32 #include <config-marble.h>
33
34 using namespace Marble;
35
36 namespace
37 {
38 QString runTimeMarbleDataPath;
39
40 QString runTimeMarblePluginPath;
41 }
42
MarbleDirs()43 MarbleDirs::MarbleDirs()
44 : d( nullptr )
45 {
46 }
47
48
path(const QString & relativePath)49 QString MarbleDirs::path( const QString& relativePath )
50 {
51 QString localpath = localPath() + QLatin1Char('/') + relativePath; // local path
52 QString systempath = systemPath() + QLatin1Char('/') + relativePath; // system path
53
54
55 QString fullpath = systempath;
56 if ( QFile::exists( localpath ) ) {
57 fullpath = localpath;
58 }
59 return QDir( fullpath ).canonicalPath();
60 }
61
62
pluginPath(const QString & relativePath)63 QString MarbleDirs::pluginPath( const QString& relativePath )
64 {
65 const QString localpath = pluginLocalPath() + QDir::separator() + relativePath; // local path
66 const QString systempath = pluginSystemPath() + QDir::separator() + relativePath; // system path
67
68
69 QString fullpath = systempath;
70 if ( QFile::exists( localpath ) ) {
71 fullpath = localpath;
72 }
73
74 return QDir( fullpath ).canonicalPath();
75 }
76
entryList(const QString & relativePath,QDir::Filters filters)77 QStringList MarbleDirs::entryList( const QString& relativePath, QDir::Filters filters )
78 {
79 QStringList filesLocal = QDir(MarbleDirs::localPath() + QLatin1Char('/') + relativePath).entryList(filters);
80 QStringList filesSystem = QDir(MarbleDirs::systemPath() + QLatin1Char('/') + relativePath).entryList(filters);
81 QStringList allFiles( filesLocal );
82 allFiles << filesSystem;
83
84 // remove duplicate entries
85 allFiles.sort();
86 for ( int i = 1; i < allFiles.size(); ++i ) {
87 if ( allFiles.at(i) == allFiles.at( i - 1 ) ) {
88 allFiles.removeAt(i);
89 --i;
90 }
91 }
92
93 return allFiles;
94 }
95
pluginEntryList(const QString & relativePath,QDir::Filters filters)96 QStringList MarbleDirs::pluginEntryList( const QString& relativePath, QDir::Filters filters )
97 {
98 QStringList allFiles = QDir(MarbleDirs::pluginLocalPath() + QLatin1Char('/') + relativePath).entryList(filters);
99 auto const pluginSystemPath = MarbleDirs::pluginSystemPath();
100 if (!pluginSystemPath.isEmpty()) {
101 allFiles << QDir(pluginSystemPath + QLatin1Char('/') + relativePath).entryList(filters);
102 }
103
104 // remove duplicate entries
105 allFiles.sort();
106 for ( int i = 1; i < allFiles.size(); ++i ) {
107 if ( allFiles.at(i) == allFiles.at( i - 1 ) ) {
108 allFiles.removeAt(i);
109 --i;
110 }
111 }
112
113 return allFiles;
114 }
115
systemPath()116 QString MarbleDirs::systemPath()
117 {
118 if (!runTimeMarbleDataPath.isEmpty()) {
119 return runTimeMarbleDataPath;
120 }
121
122 QString systempath;
123
124 #ifdef MARBLE_DATA_PATH
125 //MARBLE_DATA_PATH is a compiler define set by cmake
126 QString compileTimeMarbleDataPath(MARBLE_DATA_PATH);
127
128 if(QDir(compileTimeMarbleDataPath).exists())
129 return compileTimeMarbleDataPath;
130 #endif // MARBLE_DATA_PATH
131
132
133 #ifdef Q_OS_WIN
134 return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("data");
135 #endif
136
137 #ifdef Q_OS_MACX
138 //
139 // On OSX lets try to find any file first in the bundle
140 // before branching out to home and sys dirs
141 //
142 CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
143 CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle);
144 const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
145 CFRelease(myBundleRef);
146 QString myPath(mypPathPtr);
147 CFRelease(myMacPath);
148 //do some magick so that we can still find data dir if
149 //marble was not built as a bundle
150 if (myPath.contains(QLatin1String(".app"))) { //its a bundle!
151 systempath = myPath + QLatin1String("/Contents/Resources/data");
152 }
153
154 if ( QFile::exists( systempath ) ){
155 return systempath;
156 }
157 #endif // mac bundle
158
159 #ifdef Q_OS_ANDROID
160 systempath = "assets:/data";
161 return systempath;
162 #endif
163
164 // This is the preferred fallback location if no marbleDataPath is set.
165 systempath = QDir( QCoreApplication::applicationDirPath() + QLatin1String( "/data" ) ).canonicalPath();
166
167 if ( QFile::exists( systempath ) ){
168 return systempath;
169 }
170
171 // This fallback location is for compatibility with KDE installations.
172 return QDir( QCoreApplication::applicationDirPath()
173 + QLatin1String( "/../share/apps/marble/data" )
174 ).canonicalPath();
175 }
176
pluginSystemPath()177 QString MarbleDirs::pluginSystemPath()
178 {
179 if (!runTimeMarblePluginPath.isEmpty()) {
180 return runTimeMarblePluginPath;
181 }
182
183 QString systempath;
184
185 #ifdef MARBLE_PLUGIN_PATH
186 //MARBLE_PLUGIN_PATH is a compiler define set by cmake
187 QString compileTimeMarblePluginPath(MARBLE_PLUGIN_PATH);
188
189 if(QDir(compileTimeMarblePluginPath).exists())
190 return compileTimeMarblePluginPath;
191 #endif // MARBLE_PLUGIN_PATH
192
193 #ifdef Q_OS_MACX
194 //
195 // On OSX lets try to find any file first in the bundle
196 // before branching out to home and sys dirs
197 //
198 CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
199 CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle);
200 const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
201 CFRelease(myBundleRef);
202 CFRelease(myMacPath);
203 QString myPath(mypPathPtr);
204 //do some magick so that we can still find data dir if
205 //marble was not built as a bundle
206 if (myPath.contains(QLatin1String(".app"))) { //its a bundle!
207 systempath = myPath + QLatin1String("/Contents/Resources/plugins");
208 }
209
210 if ( QFile::exists( systempath ) ){
211 return systempath;
212 }
213 #endif // mac bundle
214
215 #ifdef Q_OS_WIN
216 return QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("plugins");
217 #endif
218
219 #ifdef Q_OS_ANDROID
220 return "assets:/plugins";
221 #endif
222
223 // This is the preferred fallback location if no marblePluginPath is set.
224 systempath = QDir( QCoreApplication::applicationDirPath() + QLatin1String( "/plugins" ) ).canonicalPath();
225
226 if ( QFile::exists( systempath ) ){
227 return systempath;
228 }
229
230 // This ultimate fallback location is for compatibility with KDE installations.
231 return QDir( QCoreApplication::applicationDirPath()
232 + QLatin1String( "/../lib/kde4/plugins/marble" )
233 ).canonicalPath();
234 }
235
localPath()236 QString MarbleDirs::localPath()
237 {
238 #ifndef Q_OS_WIN
239 QString dataHome = getenv( "XDG_DATA_HOME" );
240 if( dataHome.isEmpty() )
241 dataHome = QDir::homePath() + QLatin1String("/.local/share");
242
243 return dataHome + QLatin1String("/marble"); // local path
244 #else
245 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/data");
246 #endif
247 }
248
oldLocalPaths()249 QStringList MarbleDirs::oldLocalPaths()
250 {
251 QStringList possibleOldPaths;
252
253 #ifndef Q_OS_WIN
254 const QString oldDefault = QDir::homePath() + QLatin1String("/.marble/data");
255 possibleOldPaths.append( oldDefault );
256
257 const QString xdgDefault = QDir::homePath() + QLatin1String("/.local/share/marble");
258 possibleOldPaths.append( xdgDefault );
259
260 QString xdg = getenv( "XDG_DATA_HOME" );
261 xdg += QLatin1String("/marble/");
262 possibleOldPaths.append( xdg );
263 #endif
264
265 #ifdef Q_OS_WIN
266 HWND hwnd = 0;
267 WCHAR *appdata_path = new WCHAR[MAX_PATH + 1];
268
269 SHGetSpecialFolderPathW(hwnd, appdata_path, CSIDL_APPDATA, 0);
270 QString appdata = QString::fromUtf16(reinterpret_cast<ushort*>(appdata_path));
271 delete[] appdata_path;
272 possibleOldPaths << QString(QDir::fromNativeSeparators(appdata) + QLatin1String("/.marble/data")); // local path
273 #endif
274
275 QString currentLocalPath = QDir( MarbleDirs::localPath() ).canonicalPath();
276 QStringList oldPaths;
277 for( const QString& possibleOldPath: possibleOldPaths ) {
278 if( !QDir().exists( possibleOldPath ) ) {
279 continue;
280 }
281
282 QString canonicalPossibleOldPath = QDir( possibleOldPath ).canonicalPath();
283 if( canonicalPossibleOldPath == currentLocalPath ) {
284 continue;
285 }
286
287 oldPaths.append( canonicalPossibleOldPath );
288 }
289
290 return oldPaths;
291 }
292
pluginLocalPath()293 QString MarbleDirs::pluginLocalPath()
294 {
295 #ifndef Q_OS_WIN
296 return QDir::homePath() + QLatin1String("/.marble/plugins"); // local path
297 #else
298 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/.marble/plugins");
299 #endif
300 }
301
marbleDataPath()302 QString MarbleDirs::marbleDataPath()
303 {
304 return runTimeMarbleDataPath;
305 }
306
marblePluginPath()307 QString MarbleDirs::marblePluginPath()
308 {
309 return runTimeMarblePluginPath;
310 }
311
setMarbleDataPath(const QString & adaptedPath)312 void MarbleDirs::setMarbleDataPath( const QString& adaptedPath )
313 {
314 if ( !QDir::root().exists( adaptedPath ) )
315 {
316 qWarning() << QString( "Invalid MarbleDataPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, systemPath() );
317 return;
318 }
319
320 runTimeMarbleDataPath = adaptedPath;
321 }
322
setMarblePluginPath(const QString & adaptedPath)323 void MarbleDirs::setMarblePluginPath( const QString& adaptedPath )
324 {
325 if ( !QDir::root().exists( adaptedPath ) )
326 {
327 qWarning() << QString( "Invalid MarblePluginPath \"%1\". Using \"%2\" instead." ).arg( adaptedPath, pluginSystemPath() );
328 return;
329 }
330
331 runTimeMarblePluginPath = adaptedPath;
332 }
333
334
debug()335 void MarbleDirs::debug()
336 {
337 mDebug() << "=== MarbleDirs: ===";
338 mDebug() << "Local Path:" << localPath();
339 mDebug() << "Plugin Local Path:" << pluginLocalPath();
340 mDebug() << "";
341 mDebug() << "Marble Data Path (Run Time) :" << runTimeMarbleDataPath;
342 mDebug() << "Marble Data Path (Compile Time):" << QString(MARBLE_DATA_PATH);
343 mDebug() << "";
344 mDebug() << "Marble Plugin Path (Run Time) :" << runTimeMarblePluginPath;
345 mDebug() << "Marble Plugin Path (Compile Time):" << QString(MARBLE_PLUGIN_PATH);
346 mDebug() << "";
347 mDebug() << "System Path:" << systemPath();
348 mDebug() << "Plugin System Path:" << pluginSystemPath();
349 mDebug() << "===================";
350 }
351