1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5 //
6 
7 #include <QApplication>
8 #include <QFile>
9 #include <QDir>
10 #include <QLocale>
11 #include <QTranslator>
12 #include <QStandardPaths>
13 
14 #include "QtMainWindow.h"
15 
16 #include "MapThemeManager.h"
17 #include "MarbleWidgetInputHandler.h"
18 #include "MarbleDirs.h"
19 #include "MarbleDebug.h"
20 #include "MarbleTest.h"
21 #include "MarbleLocale.h"
22 #include "GeoUriParser.h"
23 
24 #ifdef STATIC_BUILD
25  #include <QtPlugin>
26  Q_IMPORT_PLUGIN(qjpeg)
27  Q_IMPORT_PLUGIN(qsvg)
28 #endif
29 
30 #ifdef Q_OS_MACX
31 //for getting app bundle path
32 #include <ApplicationServices/ApplicationServices.h>
33 #endif
34 
35 using namespace Marble;
36 
37 // load translation file from normal "KDE Applications" packaging installation
loadTranslation(const QString & localeDirName,QApplication & app)38 static bool loadTranslation(const QString &localeDirName, QApplication &app)
39 {
40     const QString subPath = QLatin1String("locale/") + localeDirName + QLatin1String("/LC_MESSAGES/marble_qt.qm");
41     const QString fullPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, subPath);
42     if (fullPath.isEmpty()) {
43         return false;
44     }
45 
46     QTranslator* translator = new QTranslator(&app);
47     if (!translator->load(fullPath)) {
48         delete translator;
49         return false;
50     }
51 
52     app.installTranslator(translator);
53 
54     return true;
55 }
56 
57 // load KDE translators system based translations
58 // TODO: document other possible supported translation systems, if any, and where their catalog files are
loadTranslations(QApplication & app)59 static void loadTranslations(QApplication &app)
60 {
61     // Quote from ecm_create_qm_loader created code:
62     // The way Qt translation system handles plural forms makes it necessary to
63     // have a translation file which contains only plural forms for `en`.
64     // That's why we load the `en` translation unconditionally, then load the
65     // translation for the current locale to overload it.
66     const QString en(QStringLiteral("en"));
67 
68     loadTranslation(en, app);
69 
70     QLocale locale = QLocale::system();
71     if (locale.name() != en) {
72         if (!loadTranslation(locale.name(), app)) {
73             loadTranslation(locale.bcp47Name(), app);
74         }
75     }
76 }
77 
78 
main(int argc,char * argv[])79 int main(int argc, char *argv[])
80 {
81     QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
82 
83     QApplication app(argc, argv);
84     app.setApplicationName( "Marble Virtual Globe" );
85     app.setOrganizationName( "KDE" );
86     app.setOrganizationDomain( "kde.org" );
87 #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
88     app.setDesktopFileName(QStringLiteral("org.kde.marble-qt"));
89 #endif
90 
91     // Load Qt translation system catalog for libmarblewidget, the plugins and this app
92     loadTranslations(app);
93 
94     app.setApplicationDisplayName(MainWindow::tr("Marble - Virtual Globe"));
95 
96     // For non static builds on mac and win
97     // we need to be sure we can find the qt image
98     // plugins. In mac be sure to look in the
99     // application bundle...
100 
101 #ifdef Q_WS_WIN
102     QApplication::addLibraryPath( QApplication::applicationDirPath()
103         + QDir::separator() + QLatin1String("plugins"));
104 #endif
105 #ifdef Q_OS_MACX
106     QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus);
107     qDebug("Adding qt image plugins to plugin search path...");
108     CFURLRef myBundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
109     CFStringRef myMacPath = CFURLCopyFileSystemPath(myBundleRef, kCFURLPOSIXPathStyle);
110     const char *mypPathPtr = CFStringGetCStringPtr(myMacPath,CFStringGetSystemEncoding());
111     CFRelease(myBundleRef);
112     CFRelease(myMacPath);
113     QString myPath(mypPathPtr);
114     // if we are not in a bundle assume that the app is built
115     // as a non bundle app and that image plugins will be
116     // in system Qt frameworks. If the app is a bundle
117     // lets try to set the qt plugin search path...
118     if (myPath.contains(".app"))
119     {
120       myPath += QLatin1String("/Contents/plugins");
121       QApplication::addLibraryPath( myPath );
122       qDebug( "Added %s to plugin search path", qPrintable( myPath ) );
123     }
124 #endif
125 
126     QString marbleDataPath;
127     int dataPathIndex=0;
128     QString mapThemeId;
129     QString tour;
130     QString coordinatesString;
131     QString distanceString;
132     QString geoUriString;
133     MarbleGlobal::Profiles profiles = MarbleGlobal::getInstance()->profiles();
134 
135     QStringList args = QApplication::arguments();
136 
137     if ( args.contains( "-h" ) || args.contains( "--help" ) ) {
138         qWarning() << "Usage: marble [options] [files]";
139         qWarning();
140         qWarning() << "[files] can be zero, one or more .kml and/or .gpx files to load and show.";
141         qWarning();
142         qWarning() << "general options:";
143         qWarning() << "  --marbledatapath=<path> .... Overwrite the compile-time path to map themes and other data";
144         qWarning() << "  --geo-uri=<uri> ............ Show map at given geo uri";
145         qWarning() << "  --latlon=<coordinates> ..... Show map at given lat lon coordinates";
146         qWarning() << "  --distance=<value> ......... Set the distance of the observer to the globe (in km)";
147         qWarning() << "  --map=<id> ................. Use map id (e.g. \"earth/openstreetmap/openstreetmap.dgml\")";
148         qWarning() << "  --tour=<file> .............. Load a KML tour from the given file and play it";
149         qWarning();
150         qWarning() << "debug options:";
151         qWarning() << "  --debug-info ............... write (more) debugging information to the console";
152         qWarning() << "  --fps ...................... Show the paint performance (paint rate) in the top left corner";
153         qWarning() << "  --runtimeTrace.............. Show the time spent and other debug info of each layer";
154         qWarning() << "  --tile-id................... Write the identifier of texture tiles on top of them";
155         qWarning() << "  --timedemo ................. Measure the paint performance while moving the map and quit";
156         qWarning() << "  --debug-polygons ........... Display the polygon nodes and their index for debugging";
157         qWarning() << "  --debug-levels ............. Display OSM placemarks according to the level selected";
158         qWarning();
159         qWarning() << "profile options (note that marble should automatically detect which profile to use. Override that with the options below):";
160         qWarning() << "  --highresolution ........... Enforce the profile for devices with high resolution (e.g. desktop computers)";
161         qWarning() << "  --nohighresolution ......... Deactivate the profile for devices with high resolution (e.g. desktop computers)";
162 
163         return 0;
164     }
165 
166     for ( int i = 1; i < args.count(); ++i ) {
167         const QString arg = args.at(i);
168 
169         if ( arg == QLatin1String( "--debug-info" ) )
170         {
171             MarbleDebug::setEnabled( true );
172         }
173         else if ( arg.startsWith( QLatin1String( "--marbledatapath=" ), Qt::CaseInsensitive ) )
174         {
175             marbleDataPath = args.at(i).mid(17);
176         }
177         else if ( arg.compare( QLatin1String( "--marbledatapath" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) {
178             dataPathIndex = i + 1;
179             marbleDataPath = args.value( dataPathIndex );
180             ++i;
181         }
182         else if ( arg == QLatin1String( "--highresolution" ) ) {
183             profiles |= MarbleGlobal::HighResolution;
184         }
185         else if ( arg == QLatin1String( "--nohighresolution" ) ) {
186             profiles &= ~MarbleGlobal::HighResolution;
187         }
188         else if ( arg.startsWith( QLatin1String( "--latlon=" ), Qt::CaseInsensitive ) )
189         {
190             coordinatesString = arg.mid(9);
191         }
192         else if ( arg.compare( QLatin1String( "--latlon" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) {
193             ++i;
194             coordinatesString = args.value( i );
195         }
196         else if ( arg.compare( QLatin1String( "--geo-uri=" ), Qt::CaseInsensitive ) == 0 ) {
197             geoUriString = arg.mid(10);
198         }
199         else if ( arg.compare( QLatin1String( "--geo-uri" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() )
200         {
201             ++i;
202             geoUriString = args.value(i);
203         }
204         else if ( arg.startsWith( QLatin1String( "--distance=" ), Qt::CaseInsensitive ) )
205         {
206             distanceString = arg.mid(11);
207         }
208         else if ( arg.compare( QLatin1String( "--distance" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) {
209             ++i;
210             distanceString = args.value( i );
211         }
212         else if ( arg.startsWith( QLatin1String( "--map=" ), Qt::CaseInsensitive ) )
213         {
214             mapThemeId = arg.mid(6);
215         }
216         else if ( arg.compare( QLatin1String( "--map" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) {
217             ++i;
218             mapThemeId = args.value( i );
219         }
220         else if ( arg.startsWith( QLatin1String( "--tour=" ), Qt::CaseInsensitive ) )
221         {
222             tour = arg.mid(7);
223         }
224         else if ( arg.compare( QLatin1String( "--tour" ), Qt::CaseInsensitive ) == 0 && i+1 < args.size() ) {
225             ++i;
226             tour = args.value( i );
227         }
228     }
229     MarbleGlobal::getInstance()->setProfiles( profiles );
230 
231     MarbleLocale::MeasurementSystem const measurement =
232             (MarbleLocale::MeasurementSystem)QLocale::system().measurementSystem();
233     MarbleGlobal::getInstance()->locale()->setMeasurementSystem( measurement );
234 
235     QVariantMap cmdLineSettings;
236     if ( !mapThemeId.isEmpty() ) {
237         cmdLineSettings.insert( QLatin1String("mapTheme"), QVariant(mapThemeId) );
238     }
239 
240     if ( !coordinatesString.isEmpty() ) {
241         bool success = false;
242         const GeoDataCoordinates coordinates = GeoDataCoordinates::fromString(coordinatesString, success);
243         if ( success ) {
244             QVariantList lonLat;
245             lonLat << QVariant( coordinates.longitude(GeoDataCoordinates::Degree) )
246                    << QVariant( coordinates.latitude(GeoDataCoordinates::Degree) );
247             cmdLineSettings.insert( QLatin1String("lonlat"), QVariant(lonLat) );
248         }
249     }
250     if ( !distanceString.isEmpty() ) {
251         bool success = false;
252         const qreal distance = distanceString.toDouble(&success);
253         if ( success ) {
254             cmdLineSettings.insert( QLatin1String("distance"), QVariant(distance) );
255         }
256     }
257     if ( !tour.isEmpty() ) {
258         cmdLineSettings.insert( QLatin1String("tour"), QVariant(tour) );
259     }
260 
261     cmdLineSettings.insert( QLatin1String("geo-uri"), QVariant(geoUriString) );
262 
263     MainWindow window( marbleDataPath, cmdLineSettings );
264 
265 //    window.marbleWidget()->rotateTo( 0, 0, -90 );
266 //    window.show();
267 
268     for ( int i = 1; i < args.count(); ++i ) {
269         const QString arg = args.at(i);
270         if (arg == QLatin1String("--timedemo")) {
271             window.resize(900, 640);
272             MarbleTest marbleTest( window.marbleWidget() );
273             marbleTest.timeDemo();
274             return 0;
275         }
276 
277         if (arg == QLatin1String("--fps")) {
278             window.marbleControl()->marbleWidget()->setShowFrameRate( true );
279         }
280         else if (arg == QLatin1String("--tile-id")) {
281             window.marbleControl()->marbleWidget()->setShowTileId(true);
282         }
283         else if (arg == QLatin1String("--runtimeTrace")) {
284             window.marbleControl()->marbleWidget()->setShowRuntimeTrace( true );
285         }
286         else if (arg == QLatin1String("--debug-polygons")) {
287             window.marbleControl()->marbleWidget()->setShowDebugPolygons( true );
288         }
289         else if ( i != dataPathIndex && QFile::exists( arg ) ) {
290             window.addGeoDataFile(arg);
291         }
292         else if (arg == QLatin1String("--debug-levels")) {
293             window.marbleWidget()->setDebugLevelTags(true);
294         }
295     }
296 
297     auto const marbleWidget = window.marbleControl()->marbleWidget();
298     bool const debugModeEnabled = marbleWidget->showRuntimeTrace() || marbleWidget->showDebugPolygons() ||
299             marbleWidget->debugLevelTags() || MarbleDebug::isEnabled();
300     marbleWidget->inputHandler()->setDebugModeEnabled(debugModeEnabled);
301 
302     return app.exec();
303 }
304