QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsapplication.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsapplication.cpp - Accessors for application-wide data
3 --------------------------------------
4 Date : 02-Jan-2006
5 Copyright : (C) 2006 by Tom Elwertowski
6 Email : telwertowski at users dot sourceforge dot net
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsapplication.h"
17#include "qgsauthmanager.h"
20#include "qgsexception.h"
21#include "qgsgeometry.h"
24#include "qgslayout.h"
26#include "qgslogger.h"
27#include "qgsproject.h"
30#include "qgsnetworkreply.h"
31#include "qgsproviderregistry.h"
32#include "qgsexpression.h"
34#include "qgsruntimeprofiler.h"
35#include "qgstaskmanager.h"
39#include "qgssvgcache.h"
40#include "qgsimagecache.h"
41#include "qgssourcecache.h"
47#include "qgsrendererregistry.h"
51#include "qgssymbollayerutils.h"
52#include "qgscalloutsregistry.h"
55#include "qgsmessagelog.h"
57#include "qgssettings.h"
61#include "qgsunittypes.h"
62#include "qgsuserprofile.h"
66#include "qgs3dsymbolregistry.h"
68#include "qgssqliteutils.h"
69#include "qgsstyle.h"
70#include "qgsprojutils.h"
72#include "qgsnewsfeedparser.h"
73#include "qgsbookmarkmanager.h"
74#include "qgsstylemodel.h"
77#include "qgsmeshlayer.h"
78#include "qgsfeaturestore.h"
79#include "qgslocator.h"
80#include "qgsreadwritelocker.h"
82#include "qgsdbquerylog.h"
83#include "qgsfontmanager.h"
85#include "qgscolorrampimpl.h"
86#include "qgsinterval.h"
87#include "qgsgpsconnection.h"
88#include "qgssensorregistry.h"
89
94
98
99#include <QDir>
100#include <QFile>
101#include <QFileInfo>
102#include <QFileOpenEvent>
103#include <QMessageBox>
104#include <QPalette>
105#include <QProcess>
106#include <QProcessEnvironment>
107#include <QIcon>
108#include <QPixmap>
109#include <QThreadPool>
110#include <QLocale>
111#include <QStyle>
112#include <QLibraryInfo>
113#include <QStandardPaths>
114#include <QRegularExpression>
115#include <QTextStream>
116#include <QScreen>
117#include <QAuthenticator>
118#include <QRecursiveMutex>
119
121
123
125
127
129
130#ifndef Q_OS_WIN
131#include <netinet/in.h>
132#include <pwd.h>
133#else
134#include <winsock.h>
135#include <windows.h>
136#include <lmcons.h>
137#define SECURITY_WIN32
138#include <security.h>
139#ifdef _MSC_VER
140#pragma comment( lib, "Secur32.lib" )
141#endif
142#endif
143
144#include "qgsconfig.h"
145
146#include <gdal.h>
147#include <ogr_api.h>
148#include <cpl_conv.h> // for setting gdal options
149#include <sqlite3.h>
150#include <mutex>
151
152#include <proj.h>
153
154#if defined(Q_OS_LINUX)
155#include <sys/sysinfo.h>
156#endif
157
158#define CONN_POOL_MAX_CONCURRENT_CONNS 4
159
160QObject *ABISYM( QgsApplication::mFileOpenEventReceiver ) = nullptr;
161bool ABISYM( QgsApplication::mInitialized ) = false;
162bool ABISYM( QgsApplication::mRunningFromBuildDir ) = false;
163const char *QgsApplication::QGIS_ORGANIZATION_NAME = "QGIS";
164const char *QgsApplication::QGIS_ORGANIZATION_DOMAIN = "qgis.org";
165const char *QgsApplication::QGIS_APPLICATION_NAME = "QGIS3";
166QgsApplication::ApplicationMembers *QgsApplication::sApplicationMembers = nullptr;
167QgsAuthManager *QgsApplication::sAuthManager = nullptr;
168int ABISYM( QgsApplication::sMaxThreads ) = -1;
169
170Q_GLOBAL_STATIC( QStringList, sFileOpenEventList )
171Q_GLOBAL_STATIC( QString, sPrefixPath )
172Q_GLOBAL_STATIC( QString, sPluginPath )
173Q_GLOBAL_STATIC( QString, sPkgDataPath )
174Q_GLOBAL_STATIC( QString, sLibraryPath )
175Q_GLOBAL_STATIC( QString, sLibexecPath )
176Q_GLOBAL_STATIC( QString, sQmlImportPath )
177Q_GLOBAL_STATIC( QString, sThemeName )
178Q_GLOBAL_STATIC( QString, sProfilePath )
179
180Q_GLOBAL_STATIC( QStringList, sDefaultSvgPaths )
181Q_GLOBAL_STATIC( QgsStringMap, sSystemEnvVars )
182Q_GLOBAL_STATIC( QString, sConfigPath )
183
184Q_GLOBAL_STATIC( QString, sBuildSourcePath )
185#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
186Q_GLOBAL_STATIC( QString, sCfgIntDir )
187#endif
188Q_GLOBAL_STATIC( QString, sBuildOutputPath )
189Q_GLOBAL_STATIC( QStringList, sGdalSkipList )
190Q_GLOBAL_STATIC( QStringList, sDeferredSkippedGdalDrivers )
191Q_GLOBAL_STATIC( QString, sAuthDbDirPath )
192
193Q_GLOBAL_STATIC( QString, sUserName )
194Q_GLOBAL_STATIC( QString, sUserFullName )
195Q_GLOBAL_STATIC_WITH_ARGS( QString, sPlatformName, ( "external" ) )
196Q_GLOBAL_STATIC( QString, sApplicationFullName )
197Q_GLOBAL_STATIC( QString, sTranslation )
198
199Q_GLOBAL_STATIC( QTemporaryDir, sIconCacheDir )
200
201QgsApplication::QgsApplication( int &argc, char **argv, bool GUIenabled, const QString &profileFolder, const QString &platformName )
202 : QApplication( argc, argv, GUIenabled )
203{
204 *sPlatformName() = platformName;
205
207
208 // Delay application members initialization in desktop app (In desktop app, profile folder is not known at this point)
209 if ( platformName != QLatin1String( "desktop" ) )
210 {
211 mApplicationMembers = new ApplicationMembers();
212 mApplicationMembers->mSettingsRegistryCore->migrateOldSettings();
213 }
214 else
215 {
216 *sProfilePath() = profileFolder;
217 }
218
219}
220
221void QgsApplication::init( QString profileFolder )
222{
223 // Initialize application members in desktop app (at this point, profile folder is known)
224 if ( platform() == QLatin1String( "desktop" ) )
225 {
226 instance()->mApplicationMembers = new ApplicationMembers();
227 instance()->mApplicationMembers->mSettingsRegistryCore->migrateOldSettings();
228 }
229
230 if ( profileFolder.isEmpty() )
231 {
232 if ( getenv( "QGIS_CUSTOM_CONFIG_PATH" ) )
233 {
234 profileFolder = getenv( "QGIS_CUSTOM_CONFIG_PATH" );
235 }
236 else
237 {
238 profileFolder = QStandardPaths::standardLocations( QStandardPaths::AppDataLocation ).value( 0 );
239 }
240 // This will normally get here for custom scripts that use QgsApplication.
241 // This doesn't get this hit for QGIS Desktop because we setup the profile via main
242 QString rootProfileFolder = QgsUserProfileManager::resolveProfilesFolder( profileFolder );
243 QgsUserProfileManager manager( rootProfileFolder );
244 QgsUserProfile *profile = manager.getProfile();
245 profileFolder = profile->folder();
246 delete profile;
247 }
248
249 *sProfilePath() = profileFolder;
250
251 static std::once_flag sMetaTypesRegistered;
252 std::call_once( sMetaTypesRegistered, []
253 {
254 qRegisterMetaType<QgsGeometry::Error>( "QgsGeometry::Error" );
255 qRegisterMetaType<QgsDatabaseQueryLogEntry>( "QgsDatabaseQueryLogEntry" );
256 qRegisterMetaType<QgsProcessingFeatureSourceDefinition>( "QgsProcessingFeatureSourceDefinition" );
257 qRegisterMetaType<QgsProcessingOutputLayerDefinition>( "QgsProcessingOutputLayerDefinition" );
258 qRegisterMetaType<Qgis::LayoutUnit>( "Qgis::LayoutUnit" );
259 qRegisterMetaType<QgsUnsetAttributeValue>( "QgsUnsetAttributeValue" );
260 qRegisterMetaType<QgsFeatureId>( "QgsFeatureId" );
261 qRegisterMetaType<QgsFields>( "QgsFields" );
262 qRegisterMetaType<QgsFeatureIds>( "QgsFeatureIds" );
263 qRegisterMetaType<QgsProperty>( "QgsProperty" );
264 qRegisterMetaType<QgsFeatureStoreList>( "QgsFeatureStoreList" );
265 qRegisterMetaType<Qgis::MessageLevel>( "Qgis::MessageLevel" );
266 qRegisterMetaType<Qgis::BrowserItemState>( "Qgis::BrowserItemState" );
267 qRegisterMetaType<Qgis::GpsFixStatus>( "Qgis::GpsFixStatus" );
268 qRegisterMetaType<QgsReferencedRectangle>( "QgsReferencedRectangle" );
269 qRegisterMetaType<QgsReferencedPointXY>( "QgsReferencedPointXY" );
270 qRegisterMetaType<QgsReferencedGeometry>( "QgsReferencedGeometry" );
271 qRegisterMetaType<QgsLayoutRenderContext::Flags>( "QgsLayoutRenderContext::Flags" );
272 qRegisterMetaType<QgsStyle::StyleEntity>( "QgsStyle::StyleEntity" );
273 qRegisterMetaType<QgsCoordinateReferenceSystem>( "QgsCoordinateReferenceSystem" );
274 qRegisterMetaType<QgsAuthManager::MessageLevel>( "QgsAuthManager::MessageLevel" );
275 qRegisterMetaType<QgsNetworkRequestParameters>( "QgsNetworkRequestParameters" );
276 qRegisterMetaType<QgsNetworkReplyContent>( "QgsNetworkReplyContent" );
277 qRegisterMetaType<QgsFeature>( "QgsFeature" );
278 qRegisterMetaType<QgsGeometry>( "QgsGeometry" );
279 qRegisterMetaType<QgsInterval>( "QgsInterval" );
280 qRegisterMetaType<QgsRectangle>( "QgsRectangle" );
281 qRegisterMetaType<QgsPointXY>( "QgsPointXY" );
282 qRegisterMetaType<QgsPoint>( "QgsPoint" );
283 qRegisterMetaType<QgsDatumTransform::GridDetails>( "QgsDatumTransform::GridDetails" );
284 qRegisterMetaType<QgsDatumTransform::TransformDetails>( "QgsDatumTransform::TransformDetails" );
285 qRegisterMetaType<QgsNewsFeedParser::Entry>( "QgsNewsFeedParser::Entry" );
286 qRegisterMetaType<QgsRectangle>( "QgsRectangle" );
287 qRegisterMetaType<QgsLocatorResult>( "QgsLocatorResult" );
288 qRegisterMetaType<QgsGradientColorRamp>( "QgsGradientColorRamp" );
289 qRegisterMetaType<QgsProcessingModelChildParameterSource>( "QgsProcessingModelChildParameterSource" );
290#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
291 // Qt6 documentation says these are not needed anymore (https://www.qt.io/blog/whats-new-in-qmetatype-qvariant) #spellok
292 // TODO: when tests can run against Qt6 builds, check for any regressions
293 qRegisterMetaTypeStreamOperators<QgsProcessingModelChildParameterSource>( "QgsProcessingModelChildParameterSource" );
294#endif
295 qRegisterMetaType<QgsRemappingSinkDefinition>( "QgsRemappingSinkDefinition" );
296 qRegisterMetaType<QgsProcessingModelChildDependency>( "QgsProcessingModelChildDependency" );
297 qRegisterMetaType<QgsTextFormat>( "QgsTextFormat" );
298#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
299 QMetaType::registerComparators<QgsProcessingModelChildDependency>();
300 QMetaType::registerEqualsComparator<QgsProcessingFeatureSourceDefinition>();
301 QMetaType::registerEqualsComparator<QgsProperty>();
302 QMetaType::registerEqualsComparator<QgsDateTimeRange>();
303 QMetaType::registerEqualsComparator<QgsDateRange>();
304 QMetaType::registerEqualsComparator<QgsUnsetAttributeValue>();
305#endif
306 qRegisterMetaType<QPainter::CompositionMode>( "QPainter::CompositionMode" );
307 qRegisterMetaType<QgsDateTimeRange>( "QgsDateTimeRange" );
308 qRegisterMetaType<QList<QgsMapLayer *>>( "QList<QgsMapLayer*>" );
309 qRegisterMetaType<QMap<QNetworkRequest::Attribute, QVariant>>( "QMap<QNetworkRequest::Attribute,QVariant>" );
310 qRegisterMetaType<QMap<QNetworkRequest::KnownHeaders, QVariant>>( "QMap<QNetworkRequest::KnownHeaders,QVariant>" );
311 qRegisterMetaType<QList<QNetworkReply::RawHeaderPair>>( "QList<QNetworkReply::RawHeaderPair>" );
312 qRegisterMetaType< QAuthenticator * >( "QAuthenticator*" );
313 qRegisterMetaType< QgsGpsInformation >( "QgsGpsInformation" );
314 } );
315
316 ( void ) resolvePkgPath();
317
318 if ( ABISYM( mRunningFromBuildDir ) )
319 {
320 // we run from source directory - not installed to destination (specified prefix)
321 *sPrefixPath() = QString(); // set invalid path
322#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
323 setPluginPath( *sBuildOutputPath() + '/' + QString( QGIS_PLUGIN_SUBDIR ) + '/' + *sCfgIntDir() );
324#else
325 setPluginPath( *sBuildOutputPath() + '/' + QStringLiteral( QGIS_PLUGIN_SUBDIR ) );
326#endif
327 setPkgDataPath( *sBuildOutputPath() + QStringLiteral( "/data" ) ); // in buildDir/data - used for: doc, resources, svg
328 *sLibraryPath() = *sBuildOutputPath() + '/' + QGIS_LIB_SUBDIR + '/';
329#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
330 *sLibexecPath() = *sBuildOutputPath() + '/' + QGIS_LIBEXEC_SUBDIR + '/' + *sCfgIntDir() + '/';
331#else
332 *sLibexecPath() = *sBuildOutputPath() + '/' + QGIS_LIBEXEC_SUBDIR + '/';
333#endif
334#if defined( HAVE_QUICK )
335 *sQmlImportPath() = *sBuildOutputPath() + '/' + QGIS_QML_SUBDIR + '/';
336#endif
337 }
338 else
339 {
340 char *prefixPath = getenv( "QGIS_PREFIX_PATH" );
341 if ( !prefixPath )
342 {
343 if ( sPrefixPath()->isNull() )
344 {
345#if defined(Q_OS_MACX) || defined(Q_OS_WIN)
346 setPrefixPath( applicationDirPath(), true );
347#elif defined(ANDROID)
348 // this is "/data/data/org.qgis.qgis" in android
349 QDir myDir( QDir::homePath() );
350 myDir.cdUp();
351 QString myPrefix = myDir.absolutePath();
352 setPrefixPath( myPrefix, true );
353#else
354 QDir myDir( applicationDirPath() );
355 // Fix for server which is one level deeper in /usr/lib/cgi-bin
356 if ( applicationDirPath().contains( QStringLiteral( "cgi-bin" ) ) )
357 {
358 myDir.cdUp();
359 }
360 myDir.cdUp(); // Go from /usr/bin or /usr/lib (for server) to /usr
361 QString myPrefix = myDir.absolutePath();
362 setPrefixPath( myPrefix, true );
363#endif
364 }
365 }
366 else
367 {
368 setPrefixPath( prefixPath, true );
369 }
370 }
371
372 *sConfigPath() = profileFolder + '/'; // make sure trailing slash is included
373 *sDefaultSvgPaths() << qgisSettingsDirPath() + QStringLiteral( "svg/" );
374
375 *sAuthDbDirPath() = qgisSettingsDirPath();
376 if ( getenv( "QGIS_AUTH_DB_DIR_PATH" ) )
377 {
378 setAuthDatabaseDirPath( getenv( "QGIS_AUTH_DB_DIR_PATH" ) );
379 }
380
381 // force use of OpenGL renderer for Qt3d.
382 qputenv( "QT3D_RENDERER", "opengl" );
383
384 // store system environment variables passed to application, before they are adjusted
385 QMap<QString, QString> systemEnvVarMap;
386 QString passfile( QStringLiteral( "QGIS_AUTH_PASSWORD_FILE" ) ); // QString, for comparison
387
388 const auto systemEnvironment = QProcessEnvironment::systemEnvironment().toStringList();
389 for ( const QString &varStr : systemEnvironment )
390 {
391 int pos = varStr.indexOf( QLatin1Char( '=' ) );
392 if ( pos == -1 )
393 continue;
394 QString varStrName = varStr.left( pos );
395 QString varStrValue = varStr.mid( pos + 1 );
396 if ( varStrName != passfile )
397 {
398 systemEnvVarMap.insert( varStrName, varStrValue );
399 }
400 }
401 *sSystemEnvVars() = systemEnvVarMap;
402
403 // append local user-writable folder as a proj search path
404 QStringList currentProjSearchPaths = QgsProjUtils::searchPaths();
405 currentProjSearchPaths.append( qgisSettingsDirPath() + QStringLiteral( "proj" ) );
406#ifdef Q_OS_MACX
407 // append bundled proj lib for MacOS
408 QString projLib( QDir::cleanPath( pkgDataPath().append( "/proj" ) ) );
409 if ( QFile::exists( projLib ) )
410 {
411 currentProjSearchPaths.append( projLib );
412 }
413#endif // Q_OS_MACX
414
415 char **newPaths = new char *[currentProjSearchPaths.length()];
416 for ( int i = 0; i < currentProjSearchPaths.count(); ++i )
417 {
418 newPaths[i] = CPLStrdup( currentProjSearchPaths.at( i ).toUtf8().constData() );
419 }
420 proj_context_set_search_paths( nullptr, currentProjSearchPaths.count(), newPaths );
421 for ( int i = 0; i < currentProjSearchPaths.count(); ++i )
422 {
423 CPLFree( newPaths[i] );
424 }
425 delete [] newPaths;
426
427 // allow Qt to search for Qt plugins (e.g. sqldrivers) in our plugin directory
428 QCoreApplication::addLibraryPath( pluginPath() );
429
430 {
431 QgsScopedRuntimeProfile profile( tr( "Load user fonts" ) );
433 }
434
435 // set max. thread count to -1
436 // this should be read from QgsSettings but we don't know where they are at this point
437 // so we read actual value in main.cpp
438 ABISYM( sMaxThreads ) = -1;
439
440 {
441 QgsScopedRuntimeProfile profile( tr( "Load color schemes" ) );
444 }
445
446 {
447 QgsScopedRuntimeProfile profile( tr( "Load bookmarks" ) );
449 }
450
451 // trigger creation of default style
452 QgsStyle *defaultStyle = QgsStyle::defaultStyle();
453 if ( !members()->mStyleModel )
454 members()->mStyleModel = new QgsStyleModel( defaultStyle );
455
456 ABISYM( mInitialized ) = true;
457}
458
459
460void QgsApplication::installTranslators()
461{
462 if ( *sTranslation() != QLatin1String( "C" ) )
463 {
464 mQgisTranslator = new QTranslator( this );
465 if ( mQgisTranslator->load( QStringLiteral( "qgis_" ) + *sTranslation(), i18nPath() ) )
466 {
467 installTranslator( mQgisTranslator );
468 }
469 else
470 {
471 QgsDebugMsgLevel( QStringLiteral( "loading of qgis translation failed %1/qgis_%2" ).arg( i18nPath(), *sTranslation() ), 2 );
472 }
473
474 /* Translation file for Qt.
475 * The strings from the QMenuBar context section are used by Qt/Mac to shift
476 * the About, Preferences and Quit items to the Mac Application menu.
477 * These items must be translated identically in both qt_ and qgis_ files.
478 */
479 QString qtTranslationsPath = QLibraryInfo::location( QLibraryInfo::TranslationsPath );
480#ifdef __MINGW32__
481 QString prefix = QDir( QString( "%1/../" ).arg( QApplication::applicationDirPath() ) ).absolutePath();
482 qtTranslationsPath = prefix + qtTranslationsPath.mid( QLibraryInfo::location( QLibraryInfo::PrefixPath ).length() );
483#endif
484
485 mQtTranslator = new QTranslator( this );
486 if ( mQtTranslator->load( QStringLiteral( "qt_" ) + *sTranslation(), qtTranslationsPath ) )
487 {
488 installTranslator( mQtTranslator );
489 }
490 else
491 {
492 QgsDebugMsgLevel( QStringLiteral( "loading of qt translation failed %1/qt_%2" ).arg( qtTranslationsPath, *sTranslation() ), 2 );
493 }
494
495 mQtBaseTranslator = new QTranslator( this );
496 if ( mQtBaseTranslator->load( QStringLiteral( "qtbase_" ) + *sTranslation(), qtTranslationsPath ) )
497 {
498 installTranslator( mQtBaseTranslator );
499 }
500 else
501 {
502 QgsDebugMsgLevel( QStringLiteral( "loading of qtbase translation failed %1/qt_%2" ).arg( qtTranslationsPath, *sTranslation() ), 2 );
503 }
504 }
505}
506
508{
509 if ( mApplicationMembers )
510 mApplicationMembers->mSettingsRegistryCore->backwardCompatibility();
511
512 delete mDataItemProviderRegistry;
513 delete mApplicationMembers;
514 delete mQgisTranslator;
515 delete mQtTranslator;
516 delete mQtBaseTranslator;
517
518 // we do this here as well as in exitQgis() -- it's safe to call as often as we want,
519 // and there's just a *chance* that someone hasn't properly called exitQgis prior to
520 // this destructor...
521 invalidateCaches();
522}
523
524void QgsApplication::invalidateCaches()
525{
526 // invalidate coordinate cache while the PROJ context held by the thread-locale
527 // QgsProjContextStore object is still alive. Otherwise if this later object
528 // is destroyed before the static variables of the cache, we might use freed memory.
532}
533
535{
536 return qobject_cast<QgsApplication *>( QCoreApplication::instance() );
537}
538
539bool QgsApplication::event( QEvent *event )
540{
541 bool done = false;
542 if ( event->type() == QEvent::FileOpen )
543 {
544 // handle FileOpen event (double clicking a file icon in Mac OS X Finder)
545 if ( ABISYM( mFileOpenEventReceiver ) )
546 {
547 // Forward event to main window.
548 done = notify( ABISYM( mFileOpenEventReceiver ), event );
549 }
550 else
551 {
552 // Store filename because receiver has not registered yet.
553 // If QGIS has been launched by double clicking a file icon, FileOpen will be
554 // the first event; the main window is not yet ready to handle the event.
555 sFileOpenEventList()->append( static_cast<QFileOpenEvent *>( event )->file() );
556 done = true;
557 }
558 }
559 else
560 {
561 // pass other events to base class
562 done = QApplication::event( event );
563 }
564 return done;
565}
566
567bool QgsApplication::notify( QObject *receiver, QEvent *event )
568{
569 bool done = false;
570 // Crashes in customization (especially on Mac), if we're not in the main/UI thread, see #5597
571 if ( thread() == receiver->thread() )
572 emit preNotify( receiver, event, &done );
573
574 if ( done )
575 return true;
576
577 // Send event to receiver and catch unhandled exceptions
578 done = true;
579 try
580 {
581 done = QApplication::notify( receiver, event );
582 }
583 catch ( QgsException &e )
584 {
585 qCritical() << "Caught unhandled QgsException: " << e.what();
586 if ( qApp->thread() == QThread::currentThread() )
587 QMessageBox::critical( activeWindow(), tr( "Exception" ), e.what() );
588 }
589 catch ( std::exception &e )
590 {
591 qCritical() << "Caught unhandled std::exception: " << e.what();
592 if ( qApp->thread() == QThread::currentThread() )
593 QMessageBox::critical( activeWindow(), tr( "Exception" ), e.what() );
594 }
595 catch ( ... )
596 {
597 qCritical() << "Caught unhandled unknown exception";
598 if ( qApp->thread() == QThread::currentThread() )
599 QMessageBox::critical( activeWindow(), tr( "Exception" ), tr( "unknown exception" ) );
600 }
601
602 return done;
603}
604
606{
607 return QgsRuntimeProfiler::threadLocalInstance();
608}
609
611{
612 // Set receiver for FileOpen events
613 ABISYM( mFileOpenEventReceiver ) = receiver;
614 // Propagate any events collected before the receiver has registered.
615 if ( sFileOpenEventList()->count() > 0 )
616 {
617 const QStringList fileOpenEventList = *sFileOpenEventList();
618 for ( const QString &file : fileOpenEventList )
619 {
620 QFileOpenEvent foe( file );
621 QgsApplication::sendEvent( ABISYM( mFileOpenEventReceiver ), &foe );
622 }
623 sFileOpenEventList()->clear();
624 }
625}
626
627void QgsApplication::setPrefixPath( const QString &prefixPath, bool useDefaultPaths )
628{
629 *sPrefixPath() = prefixPath;
630#if defined(Q_OS_WIN)
631 if ( sPrefixPath()->endsWith( "/bin" ) )
632 {
633 sPrefixPath()->chop( 4 );
634 }
635#endif
636 if ( useDefaultPaths && !ABISYM( mRunningFromBuildDir ) )
637 {
638 setPluginPath( *sPrefixPath() + '/' + QStringLiteral( QGIS_PLUGIN_SUBDIR ) );
639 setPkgDataPath( *sPrefixPath() + '/' + QStringLiteral( QGIS_DATA_SUBDIR ) );
640 }
641 *sLibraryPath() = *sPrefixPath() + '/' + QGIS_LIB_SUBDIR + '/';
642 *sLibexecPath() = *sPrefixPath() + '/' + QGIS_LIBEXEC_SUBDIR + '/';
643#if defined( HAVE_QUICK )
644 *sQmlImportPath() = *sPrefixPath() + '/' + QGIS_QML_SUBDIR + '/';
645#endif
646}
647
648void QgsApplication::setPluginPath( const QString &pluginPath )
649{
650 *sPluginPath() = pluginPath;
651}
652
653void QgsApplication::setPkgDataPath( const QString &pkgDataPath )
654{
655 *sPkgDataPath() = pkgDataPath;
656
657 QString mySvgPath = pkgDataPath + QStringLiteral( "/svg/" );
658
659 // avoid duplicate entries
660 if ( !sDefaultSvgPaths()->contains( mySvgPath ) )
661 *sDefaultSvgPaths() << mySvgPath;
662}
663
664void QgsApplication::setDefaultSvgPaths( const QStringList &pathList )
665{
666 *sDefaultSvgPaths() = pathList;
667}
668
669void QgsApplication::setAuthDatabaseDirPath( const QString &authDbDirPath )
670{
671 QFileInfo fi( authDbDirPath );
672 if ( fi.exists() && fi.isDir() && fi.isWritable() )
673 {
674 *sAuthDbDirPath() = fi.canonicalFilePath() + QDir::separator();
675 }
676}
677
679{
680#if 0
681 if ( ABISYM( mRunningFromBuildDir ) )
682 {
683 static bool sOnce = true;
684 if ( sOnce )
685 {
686 QgsMessageLogNotifyBlocker blockNotifications;
687 ( void ) blockNotifications;
688 qWarning( "!!! prefix path was requested, but it is not valid - we do not run from installed path !!!" );
689 }
690 sOnce = false;
691 }
692#endif
693
694 return *sPrefixPath();
695}
697{
698 return *sPluginPath();
699}
700
702{
703 if ( sPkgDataPath()->isNull() )
704 return resolvePkgPath();
705 else
706 return *sPkgDataPath();
707}
708
710{
711 return QStringLiteral( ":/images/themes/default/" );
712}
714{
715 QString usersThemes = userThemesFolder() + QDir::separator() + themeName() + QDir::separator() + "icons/";
716 QDir dir( usersThemes );
717 if ( dir.exists() )
718 {
719 return usersThemes;
720 }
721 else
722 {
723 QString defaultThemes = defaultThemesFolder() + QDir::separator() + themeName() + QDir::separator() + "icons/";
724 return defaultThemes;
725 }
726}
727
729{
730 return iconsPath() + QStringLiteral( "qgis-icon-60x60.png" );
731}
732
734{
735 return ABISYM( sMaxThreads );
736}
737
738QString QgsApplication::iconPath( const QString &iconFile )
739{
740 // try active theme
741 QString path = activeThemePath();
742 if ( QFile::exists( path + iconFile ) )
743 return path + iconFile;
744
745 // use default theme
746 return defaultThemePath() + iconFile;
747}
748
749QIcon QgsApplication::getThemeIcon( const QString &name, const QColor &fillColor, const QColor &strokeColor )
750{
751 const QString cacheKey = ( name.startsWith( '/' ) ? name.mid( 1 ) : name )
752 + ( fillColor.isValid() ? QStringLiteral( "_%1" ).arg( fillColor.name( QColor::HexArgb ).mid( 1 ) ) : QString() )
753 + ( strokeColor.isValid() ? QStringLiteral( "_%1" ).arg( strokeColor.name( QColor::HexArgb ).mid( 1 ) ) : QString() );
754 QgsApplication *app = instance();
755 if ( app && app->mIconCache.contains( cacheKey ) )
756 return app->mIconCache.value( cacheKey );
757
758 QIcon icon;
759 const bool colorBased = fillColor.isValid() || strokeColor.isValid();
760
761 auto iconFromColoredSvg = [ = ]( const QString & path ) -> QIcon
762 {
763 // sizes are unused here!
764 const QByteArray svgContent = QgsApplication::svgCache()->svgContent( path, 16, fillColor, strokeColor, 1, 1 );
765
766 const QString iconPath = sIconCacheDir()->filePath( cacheKey + QStringLiteral( ".svg" ) );
767 QFile f( iconPath );
768 if ( f.open( QFile::WriteOnly | QFile::Truncate ) )
769 {
770 f.write( svgContent );
771 f.close();
772 }
773 else
774 {
775 QgsDebugError( QStringLiteral( "Could not create colorized icon svg at %1" ).arg( iconPath ) );
776 return QIcon();
777 }
778
779 return QIcon( f.fileName() );
780 };
781
782 QString preferredPath = activeThemePath() + QDir::separator() + name;
783 QString defaultPath = defaultThemePath() + QDir::separator() + name;
784 if ( QFile::exists( preferredPath ) )
785 {
786 if ( colorBased )
787 {
788 icon = iconFromColoredSvg( preferredPath );
789 }
790 else
791 {
792 icon = QIcon( preferredPath );
793 }
794 }
795 else if ( QFile::exists( defaultPath ) )
796 {
797 //could still return an empty icon if it
798 //doesn't exist in the default theme either!
799 if ( colorBased )
800 {
801 icon = iconFromColoredSvg( defaultPath );
802 }
803 else
804 {
805 icon = QIcon( defaultPath );
806 }
807 }
808 else
809 {
810 icon = QIcon();
811 }
812
813 if ( app )
814 app->mIconCache.insert( cacheKey, icon );
815 return icon;
816}
817
819{
820 QgsApplication *app = instance();
821 if ( app && app->mCursorCache.contains( cursor ) )
822 return app->mCursorCache.value( cursor );
823
824 // All calculations are done on 32x32 icons
825 // Defaults to center, individual cursors may override
826 int activeX = 16;
827 int activeY = 16;
828
829 QString name;
830 switch ( cursor )
831 {
832 case ZoomIn:
833 name = QStringLiteral( "mZoomIn.svg" );
834 activeX = 13;
835 activeY = 13;
836 break;
837 case ZoomOut:
838 name = QStringLiteral( "mZoomOut.svg" );
839 activeX = 13;
840 activeY = 13;
841 break;
842 case Identify:
843 activeX = 3;
844 activeY = 6;
845 name = QStringLiteral( "mIdentify.svg" );
846 break;
847 case CrossHair:
848 name = QStringLiteral( "mCrossHair.svg" );
849 break;
850 case CapturePoint:
851 name = QStringLiteral( "mCapturePoint.svg" );
852 break;
853 case Select:
854 name = QStringLiteral( "mSelect.svg" );
855 activeX = 6;
856 activeY = 6;
857 break;
858 case Sampler:
859 activeX = 5;
860 activeY = 5;
861 name = QStringLiteral( "mSampler.svg" );
862 break;
863 // No default
864 }
865 // It should never get here!
866 Q_ASSERT( ! name.isEmpty( ) );
867
868 QIcon icon = getThemeIcon( QStringLiteral( "cursors" ) + QDir::separator() + name );
869 QCursor cursorIcon;
870 // Check if an icon exists for this cursor (the O.S. default cursor will be used if it does not)
871 if ( ! icon.isNull( ) )
872 {
873 // Apply scaling
874 float scale = Qgis::UI_SCALE_FACTOR * QgsApplication::fontMetrics().height() / 32.0;
875 cursorIcon = QCursor( icon.pixmap( std::ceil( scale * 32 ), std::ceil( scale * 32 ) ), std::ceil( scale * activeX ), std::ceil( scale * activeY ) );
876 }
877 if ( app )
878 app->mCursorCache.insert( cursor, cursorIcon );
879 return cursorIcon;
880}
881
882// TODO: add some caching mechanism ?
883QPixmap QgsApplication::getThemePixmap( const QString &name, const QColor &foreColor, const QColor &backColor, const int size )
884{
885 const QString preferredPath = activeThemePath() + QDir::separator() + name;
886 const QString defaultPath = defaultThemePath() + QDir::separator() + name;
887 const QString path = QFile::exists( preferredPath ) ? preferredPath : defaultPath;
888 if ( foreColor.isValid() || backColor.isValid() )
889 {
890 bool fitsInCache = false;
891 const QImage image = svgCache()->svgAsImage( path, size, backColor, foreColor, 1, 1, fitsInCache );
892 return QPixmap::fromImage( image );
893 }
894
895 return QPixmap( path );
896}
897
898void QgsApplication::setThemeName( const QString &themeName )
899{
900 *sThemeName() = themeName;
901}
902
904{
905 static QString appPath;
906 if ( appPath.isNull() )
907 {
908 if ( QCoreApplication::instance() )
909 {
910 appPath = applicationDirPath();
911 }
912 else
913 {
914 qWarning( "Application path not initialized" );
915 }
916 }
917
918 if ( !appPath.isNull() || getenv( "QGIS_PREFIX_PATH" ) )
919 {
920 QString prefix = getenv( "QGIS_PREFIX_PATH" ) ? getenv( "QGIS_PREFIX_PATH" ) : appPath;
921
922 // check if QGIS is run from build directory (not the install directory)
923 QFile f;
924 // "/../../.." is for Mac bundled app in build directory
925 static const QStringList paths { QStringList() << QString() << QStringLiteral( "/.." ) << QStringLiteral( "/bin" ) << QStringLiteral( "/../../.." ) };
926 for ( const QString &path : paths )
927 {
928 f.setFileName( prefix + path + "/qgisbuildpath.txt" );
929 if ( f.exists() )
930 break;
931 }
932 if ( f.exists() && f.open( QIODevice::ReadOnly ) )
933 {
934 ABISYM( mRunningFromBuildDir ) = true;
935 *sBuildSourcePath() = f.readLine().trimmed();
936 *sBuildOutputPath() = f.readLine().trimmed();
937 QgsDebugMsgLevel( QStringLiteral( "Running from build directory!" ), 4 );
938 QgsDebugMsgLevel( QStringLiteral( "- source directory: %1" ).arg( sBuildSourcePath()->toUtf8().constData() ), 4 );
939 QgsDebugMsgLevel( QStringLiteral( "- output directory of the build: %1" ).arg( sBuildOutputPath()->toUtf8().constData() ), 4 );
940#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
941#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
942 *sCfgIntDir() = prefix.split( '/', QString::SkipEmptyParts ).last();
943#else
944 *sCfgIntDir() = prefix.split( '/', Qt::SkipEmptyParts ).last();
945#endif
946 qDebug( "- cfg: %s", sCfgIntDir()->toUtf8().constData() );
947#endif
948 }
949 }
950
951 QString prefixPath;
952 if ( getenv( "QGIS_PREFIX_PATH" ) )
953 prefixPath = getenv( "QGIS_PREFIX_PATH" );
954 else
955 {
956#if defined(ANDROID)
957 // this is "/data/data/org.qgis.qgis" in android
958 QDir dir( QDir::homePath() );
959 dir.cdUp();
960 prefixPath = dir.absolutePath();
961#else
962
963#if defined(Q_OS_MACX)
964 prefixPath = appPath;
965#elif defined(Q_OS_WIN)
966 prefixPath = appPath;
967 if ( prefixPath.endsWith( "/bin" ) )
968 prefixPath.chop( 4 );
969#else
970 QDir dir( appPath );
971 // Fix for server which is one level deeper in /usr/lib/cgi-bin
972 if ( appPath.contains( QStringLiteral( "cgi-bin" ) ) )
973 {
974 dir.cdUp();
975 }
976 dir.cdUp(); // Go from /usr/bin or /usr/lib (for server) to /usr
977 prefixPath = dir.absolutePath();
978#endif
979#endif
980 }
981
982 if ( ABISYM( mRunningFromBuildDir ) )
983 return *sBuildOutputPath() + QStringLiteral( "/data" );
984 else
985 return prefixPath + '/' + QStringLiteral( QGIS_DATA_SUBDIR );
986}
987
989{
990 return *sThemeName();
991}
992
993void QgsApplication::setUITheme( const QString &themeName )
994{
995 // Loop all style sheets, find matching name, load it.
996 QHash<QString, QString> themes = QgsApplication::uiThemes();
997 if ( themeName == QLatin1String( "default" ) || !themes.contains( themeName ) )
998 {
999 setThemeName( QStringLiteral( "default" ) );
1000 qApp->setStyleSheet( QString() );
1001 return;
1002 }
1003
1004 QString path = themes.value( themeName );
1005 QString stylesheetname = path + "/style.qss";
1006
1007 QFile file( stylesheetname );
1008 QFile variablesfile( path + "/variables.qss" );
1009
1010 QFileInfo variableInfo( variablesfile );
1011
1012 if ( !file.open( QIODevice::ReadOnly ) || ( variableInfo.exists() && !variablesfile.open( QIODevice::ReadOnly ) ) )
1013 {
1014 return;
1015 }
1016
1017 QString styledata = file.readAll();
1018 styledata.replace( QLatin1String( "@theme_path" ), path );
1019
1020 if ( variableInfo.exists() )
1021 {
1022 QTextStream in( &variablesfile );
1023 while ( !in.atEnd() )
1024 {
1025 QString line = in.readLine();
1026 // This is a variable
1027 if ( line.startsWith( '@' ) )
1028 {
1029 int index = line.indexOf( ':' );
1030 QString name = line.mid( 0, index );
1031 QString value = line.mid( index + 1, line.length() );
1032 styledata.replace( name, value );
1033 }
1034 }
1035 variablesfile.close();
1036 }
1037 file.close();
1038
1039 if ( Qgis::UI_SCALE_FACTOR != 1.0 )
1040 {
1041 // apply OS-specific UI scale factor to stylesheet's em values
1042 int index = 0;
1043 const static QRegularExpression regex( QStringLiteral( "(?<=[\\s:])([0-9\\.]+)(?=em)" ) );
1044 QRegularExpressionMatch match = regex.match( styledata, index );
1045 while ( match.hasMatch() )
1046 {
1047 index = match.capturedStart();
1048 styledata.remove( index, match.captured( 0 ).length() );
1049 QString number = QString::number( match.captured( 0 ).toDouble() * Qgis::UI_SCALE_FACTOR );
1050 styledata.insert( index, number );
1051 index += number.length();
1052 match = regex.match( styledata, index );
1053 }
1054 }
1055
1056 qApp->setStyleSheet( styledata );
1057
1058 QFile palettefile( path + "/palette.txt" );
1059 QFileInfo paletteInfo( palettefile );
1060 if ( paletteInfo.exists() && palettefile.open( QIODevice::ReadOnly ) )
1061 {
1062 QPalette pal = qApp->palette();
1063 QTextStream in( &palettefile );
1064 while ( !in.atEnd() )
1065 {
1066 QString line = in.readLine();
1067 QStringList parts = line.split( ':' );
1068 if ( parts.count() == 2 )
1069 {
1070 int role = parts.at( 0 ).trimmed().toInt();
1071 QColor color = QgsSymbolLayerUtils::decodeColor( parts.at( 1 ).trimmed() );
1072 pal.setColor( static_cast< QPalette::ColorRole >( role ), color );
1073 }
1074 }
1075 palettefile.close();
1076 qApp->setPalette( pal );
1077 }
1078
1080}
1081
1082QHash<QString, QString> QgsApplication::uiThemes()
1083{
1084 QStringList paths = QStringList() << userThemesFolder() << defaultThemesFolder();
1085 QHash<QString, QString> mapping;
1086 mapping.insert( QStringLiteral( "default" ), QString() );
1087 const auto constPaths = paths;
1088 for ( const QString &path : constPaths )
1089 {
1090 QDir folder( path );
1091 QFileInfoList styleFiles = folder.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot );
1092 const auto constStyleFiles = styleFiles;
1093 for ( const QFileInfo &info : constStyleFiles )
1094 {
1095 QFileInfo styleFile( info.absoluteFilePath() + "/style.qss" );
1096 if ( !styleFile.exists() )
1097 continue;
1098
1099 QString name = info.baseName();
1100 QString path = info.absoluteFilePath();
1101 mapping.insert( name, path );
1102 }
1103 }
1104 return mapping;
1105}
1106
1108{
1109 return pkgDataPath() + QStringLiteral( "/doc/AUTHORS" );
1110}
1111
1113{
1114 return pkgDataPath() + QStringLiteral( "/doc/CONTRIBUTORS" );
1115}
1117{
1118 return pkgDataPath() + QStringLiteral( "/doc/developersmap.html" );
1119}
1120
1122{
1123 return pkgDataPath() + QStringLiteral( "/doc/SPONSORS" );
1124}
1125
1127{
1128 return pkgDataPath() + QStringLiteral( "/doc/DONORS" );
1129}
1130
1132{
1133 return pkgDataPath() + QStringLiteral( "/doc/TRANSLATORS" );
1134}
1135
1137{
1138 return pkgDataPath() + QStringLiteral( "/doc/LICENSE" );
1139}
1140
1142{
1143 if ( ABISYM( mRunningFromBuildDir ) )
1144 return *sBuildOutputPath() + QStringLiteral( "/i18n/" );
1145 else
1146 return pkgDataPath() + QStringLiteral( "/i18n/" );
1147}
1148
1150{
1151 return pkgDataPath() + QStringLiteral( "/resources/metadata-ISO/" );
1152}
1153
1155{
1156 return pkgDataPath() + QStringLiteral( "/resources/qgis.db" );
1157}
1158
1160{
1161 return *sConfigPath();
1162}
1163
1165{
1166 return qgisSettingsDirPath() + QStringLiteral( "qgis.db" );
1167}
1168
1170{
1171 return *sAuthDbDirPath() + QStringLiteral( "qgis-auth.db" );
1172}
1173
1175{
1176 return QStringLiteral( ":/images/splash/" );
1177}
1178
1180{
1181 return pkgDataPath() + QStringLiteral( "/images/icons/" );
1182}
1183
1185{
1186 if ( ABISYM( mRunningFromBuildDir ) )
1187 {
1188 QString tempCopy = QDir::tempPath() + "/srs6.db";
1189
1190 if ( !QFile( tempCopy ).exists() )
1191 {
1192 QFile f( buildSourcePath() + "/resources/srs6.db" );
1193 if ( !f.copy( tempCopy ) )
1194 {
1195 qFatal( "Could not create temporary copy" );
1196 }
1197 }
1198
1199 return tempCopy;
1200 }
1201 else
1202 {
1203 return pkgDataPath() + QStringLiteral( "/resources/srs.db" );
1204 }
1205}
1206
1207void QgsApplication::setSvgPaths( const QStringList &svgPaths )
1208{
1210 members()->mSvgPathCacheValid = false;
1211}
1212
1214{
1215 static QReadWriteLock lock;
1216
1218
1219 if ( members()->mSvgPathCacheValid )
1220 {
1221 return members()->mSvgPathCache;
1222 }
1223 else
1224 {
1226 //local directories to search when looking for an SVG with a given basename
1227 //defined by user in options dialog
1228 const QStringList pathList = settingsSearchPathsForSVG->value();
1229
1230 // maintain user set order while stripping duplicates
1231 QStringList paths;
1232 for ( const QString &path : pathList )
1233 {
1234 if ( !paths.contains( path ) )
1235 paths.append( path );
1236 }
1237 for ( const QString &path : std::as_const( *sDefaultSvgPaths() ) )
1238 {
1239 if ( !paths.contains( path ) )
1240 paths.append( path );
1241 }
1242 members()->mSvgPathCache = paths;
1243
1244 return paths;
1245 }
1246}
1247
1249{
1250 //local directories to search when looking for an template with a given basename
1251 //defined by user in options dialog
1253}
1254
1255QMap<QString, QString> QgsApplication::systemEnvVars()
1256{
1257 return *sSystemEnvVars();
1258}
1259
1261{
1262 return qgisSettingsDirPath() + QStringLiteral( "symbology-style.db" );
1263}
1264
1266{
1267 const thread_local QRegularExpression regexp( QRegularExpression::anchoredPattern( QStringLiteral( "^[A-Za-z][A-Za-z0-9\\._-]*" ) ) );
1268 return regexp;
1269}
1270
1272{
1273 if ( !sUserName()->isEmpty() )
1274 return *sUserName();
1275
1276#ifdef _MSC_VER
1277 TCHAR name [ UNLEN + 1 ];
1278 DWORD size = UNLEN + 1;
1279
1280 if ( GetUserName( ( TCHAR * )name, &size ) )
1281 {
1282#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1283 *sUserName() = QString::fromLocal8Bit( name );
1284#else
1285 *sUserName() = QString::fromWCharArray( name );
1286#endif
1287 }
1288
1289
1290#elif QT_CONFIG(process)
1291 QProcess process;
1292
1293 process.start( QStringLiteral( "whoami" ), QStringList() );
1294 process.waitForFinished();
1295 *sUserName() = process.readAllStandardOutput().trimmed();
1296#endif
1297
1298 if ( !sUserName()->isEmpty() )
1299 return *sUserName();
1300
1301 //backup plan - use environment variables
1302 *sUserName() = qgetenv( "USER" );
1303 if ( !sUserName()->isEmpty() )
1304 return *sUserName();
1305
1306 //last resort
1307 *sUserName() = qgetenv( "USERNAME" );
1308 return *sUserName();
1309}
1310
1312{
1313 if ( !sUserFullName()->isEmpty() )
1314 return *sUserFullName();
1315
1316#ifdef _MSC_VER
1317 TCHAR name [ UNLEN + 1 ];
1318 DWORD size = UNLEN + 1;
1319
1320 //note - this only works for accounts connected to domain
1321 if ( GetUserNameEx( NameDisplay, ( TCHAR * )name, &size ) )
1322 {
1323#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1324 *sUserFullName() = QString::fromLocal8Bit( name );
1325#else
1326 *sUserFullName() = QString::fromWCharArray( name );
1327#endif
1328 }
1329
1330 //fall back to login name
1331 if ( sUserFullName()->isEmpty() )
1332 *sUserFullName() = userLoginName();
1333#elif defined(Q_OS_ANDROID) || defined(__MINGW32__)
1334 *sUserFullName() = QStringLiteral( "Not available" );
1335#else
1336 struct passwd *p = getpwuid( getuid() );
1337
1338 if ( p )
1339 {
1340 QString gecosName = QString( p->pw_gecos );
1341 *sUserFullName() = gecosName.left( gecosName.indexOf( ',', 0 ) );
1342 }
1343
1344#endif
1345
1346 return *sUserFullName();
1347}
1348
1350{
1351#if defined(Q_OS_ANDROID)
1352 return QLatin1String( "android" );
1353#elif defined(Q_OS_MAC)
1354 return QLatin1String( "osx" );
1355#elif defined(Q_OS_WIN)
1356 return QLatin1String( "windows" );
1357#elif defined(Q_OS_LINUX)
1358 return QStringLiteral( "linux" );
1359#elif defined(Q_OS_FREEBSD)
1360 return QStringLiteral( "freebsd" );
1361#elif defined(Q_OS_OPENBSD)
1362 return QStringLiteral( "openbsd" );
1363#elif defined(Q_OS_NETBSD)
1364 return QStringLiteral( "netbsd" );
1365#elif defined(Q_OS_UNIX)
1366 return QLatin1String( "unix" );
1367#else
1368 return QLatin1String( "unknown" );
1369#endif
1370}
1371
1373{
1374#if defined(Q_OS_ANDROID)
1375 return -1;
1376#elif defined(Q_OS_MAC)
1377 return -1;
1378#elif defined(Q_OS_WIN)
1379 MEMORYSTATUSEX memoryStatus;
1380 ZeroMemory( &memoryStatus, sizeof( MEMORYSTATUSEX ) );
1381 memoryStatus.dwLength = sizeof( MEMORYSTATUSEX );
1382 if ( GlobalMemoryStatusEx( &memoryStatus ) )
1383 {
1384 return memoryStatus.ullTotalPhys / ( 1024 * 1024 );
1385 }
1386 else
1387 {
1388 return -1;
1389 }
1390#elif defined(Q_OS_LINUX)
1391 constexpr int megabyte = 1024 * 1024;
1392 struct sysinfo si;
1393 sysinfo( &si );
1394 return si.totalram / megabyte;
1395#elif defined(Q_OS_FREEBSD)
1396 return -1;
1397#elif defined(Q_OS_OPENBSD)
1398 return -1;
1399#elif defined(Q_OS_NETBSD)
1400 return -1;
1401#elif defined(Q_OS_UNIX)
1402 return -1;
1403#else
1404 return -1;
1405#endif
1406}
1407
1409{
1410 return *sPlatformName();
1411}
1412
1414{
1415 if ( !sApplicationFullName()->isEmpty() )
1416 return *sApplicationFullName();
1417
1418 //use environment variables
1419 *sApplicationFullName() = qgetenv( "QGIS_APPLICATION_FULL_NAME" );
1420 if ( !sApplicationFullName()->isEmpty() )
1421 return *sApplicationFullName();
1422
1423 //last resort
1424 QgsSettings settings;
1425 *sApplicationFullName() = settings.value(
1426 QStringLiteral( "/qgis/application_full_name" ),
1427 QStringLiteral( "%1 %2" ).arg( applicationName(), platform() )
1428 ).toString();
1429 return *sApplicationFullName();
1430}
1431
1433{
1435 {
1437 // don't differentiate en_US and en_GB
1438 if ( locale.startsWith( QLatin1String( "en" ), Qt::CaseInsensitive ) )
1439 {
1440 return locale.left( 2 );
1441 }
1442
1443 return locale;
1444 }
1445 else
1446 {
1447 return QLocale().name().left( 2 );
1448 }
1449}
1450
1451void QgsApplication::setLocale( const QLocale &locale )
1452{
1453 QLocale::setDefault( locale );
1454 emit instance()->localeChanged();
1455}
1456
1458{
1459 return qgisSettingsDirPath() + QStringLiteral( "/themes" );
1460}
1461
1463{
1464 return pkgDataPath() + QStringLiteral( "/resources/symbology-style.xml" );
1465}
1466
1468{
1469 return pkgDataPath() + QStringLiteral( "/resources/themes" );
1470}
1471
1473{
1474 return pkgDataPath() + QStringLiteral( "/resources/server/" );
1475}
1476
1478{
1479 return *sLibraryPath();
1480}
1481
1483{
1484 return *sLibexecPath();
1485}
1486
1488{
1489 return *sQmlImportPath();
1490}
1491
1493{
1494 return ( htonl( 1 ) == 1 ) ? XDR : NDR;
1495}
1496
1498{
1499 if ( !ABISYM( mInitialized ) && QgsApplication::instance() )
1500 {
1501 init( *sProfilePath() );
1502 }
1503
1504 // set the provider plugin path (this creates provider registry)
1506
1507 // create data item provider registry
1509
1510 // create project instance if doesn't exist
1512
1513 // Initialize authentication manager and connect to database
1515
1516 // Make sure we have a NAM created on the main thread.
1517 // Note that this might call QgsApplication::authManager to
1518 // setup the proxy configuration that's why it needs to be
1519 // called after the QgsAuthManager instance has been created
1521
1522}
1523
1525{
1526 if ( auto *lInstance = instance() )
1527 {
1528 if ( !lInstance->mAuthManager )
1529 {
1530 lInstance->mAuthManager = QgsAuthManager::instance();
1531 }
1532 return lInstance->mAuthManager;
1533 }
1534 else
1535 {
1536 // no QgsApplication instance
1537 if ( !sAuthManager )
1538 sAuthManager = QgsAuthManager::instance();
1539 return sAuthManager;
1540 }
1541}
1542
1543
1545{
1546 // make sure all threads are done before exiting
1547 QThreadPool::globalInstance()->waitForDone();
1548
1549 // don't create to delete
1550 if ( auto *lInstance = instance() )
1551 delete lInstance->mAuthManager;
1552 else
1553 delete sAuthManager;
1554
1555 //Ensure that all remaining deleteLater QObjects are actually deleted before we exit.
1556 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1557
1558 //delete all registered functions from expression engine (see above comment)
1560
1561 // avoid creating instance just to delete it!
1562 if ( QgsProject::sProject )
1563 delete QgsProject::instance();
1564
1565 //Ensure that providers/layers which called deleteLater on objects as part of their cleanup
1566 //result in fully deleted objects before we do the provider registry cleanup.
1567 //E.g. the QgsOgrConnPool instance has deleteLater calls when unrefing layers, so clearing
1568 //the project above has not yet fully cleaned up OGR objects, which we MUST do before
1569 //cleaning up the provider
1570 QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
1571
1572 // avoid creating instance just to delete it!
1573 if ( QgsProviderRegistry::exists() )
1575
1576 invalidateCaches();
1577
1579
1580 // tear-down GDAL/OGR
1581 OGRCleanupAll();
1582 GDALDestroyDriverManager();
1583}
1584
1586{
1587 QString myEnvironmentVar( getenv( "QGIS_PREFIX_PATH" ) );
1588 QString myState = tr( "Application state:\n"
1589 "QGIS_PREFIX_PATH env var:\t\t%1\n"
1590 "Prefix:\t\t%2\n"
1591 "Plugin Path:\t\t%3\n"
1592 "Package Data Path:\t%4\n"
1593 "Active Theme Name:\t%5\n"
1594 "Active Theme Path:\t%6\n"
1595 "Default Theme Path:\t%7\n"
1596 "SVG Search Paths:\t%8\n"
1597 "User DB Path:\t%9\n"
1598 "Auth DB Path:\t%10\n" )
1599 .arg( myEnvironmentVar,
1600 prefixPath(),
1601 pluginPath(),
1602 pkgDataPath(),
1603 themeName(),
1606 svgPaths().join( tr( "\n\t\t", "match indentation of application state" ) ),
1608 .arg( qgisAuthDatabaseFilePath() );
1609 return myState;
1610}
1611
1613{
1614 //
1615 // Make the style sheet desktop preferences aware by using qapplication
1616 // palette as a basis for colors where appropriate
1617 //
1618 // QColor myColor1 = palette().highlight().color();
1619 QColor myColor1( Qt::lightGray );
1620 QColor myColor2 = myColor1;
1621 myColor2 = myColor2.lighter( 110 ); //10% lighter
1622 QString myStyle;
1623 myStyle = QStringLiteral( ".overview{"
1624 " font: 1.82em;"
1625 " font-weight: bold;"
1626 "}"
1627 "body{"
1628 " background: white;"
1629 " color: black;"
1630 " font-family: 'Lato', 'Open Sans', 'Lucida Grande', 'Segoe UI', 'Arial', sans-serif;"
1631 " width: 100%;"
1632 "}"
1633 "h1{ background-color: #F6F6F6;"
1634 " color: #589632; " // from http://qgis.org/en/site/getinvolved/styleguide.html
1635 " font-size: x-large; "
1636 " font-weight: normal;"
1637 " background: none;"
1638 " padding: 0.75em 0 0;"
1639 " margin: 0;"
1640 " line-height: 3em;"
1641 "}"
1642 "h2{ background-color: #F6F6F6;"
1643 " color: #589632; " // from http://qgis.org/en/site/getinvolved/styleguide.html
1644 " font-size: medium; "
1645 " font-weight: normal;"
1646 " background: none;"
1647 " padding: 0.75em 0 0;"
1648 " margin: 0;"
1649 " line-height: 1.1em;"
1650 "}"
1651 "h3{ background-color: #F6F6F6;"
1652 " color: #93b023;" // from http://qgis.org/en/site/getinvolved/styleguide.html
1653 " font-weight: bold;"
1654 " font-size: large;"
1655 " text-align: left;"
1656 " border-bottom: 5px solid #DCEB5C;"
1657 "}"
1658 "h4{ background-color: #F6F6F6;"
1659 " color: #93b023;" // from http://qgis.org/en/site/getinvolved/styleguide.html
1660 " font-weight: bold;"
1661 " font-size: medium;"
1662 " text-align: left;"
1663 "}"
1664 "h5{ background-color: #F6F6F6;"
1665 " color: #93b023;" // from http://qgis.org/en/site/getinvolved/styleguide.html
1666 " font-weight: bold;"
1667 " font-size: small;"
1668 " text-align: left;"
1669 "}"
1670 "a{ color: #729FCF;"
1671 " font-family: arial,sans-serif;"
1672 "}"
1673 "label{ background-color: #FFFFCC;"
1674 " border: 1px solid black;"
1675 " margin: 1px;"
1676 " padding: 0px 3px; "
1677 " font-size: small;"
1678 "}"
1679 "th .strong {"
1680 " font-weight: bold;"
1681 "}"
1682 "hr {"
1683 " border: 0;"
1684 " height: 0;"
1685 " border-top: 1px solid black;"
1686 "}"
1687 ".list-view .highlight {"
1688 " text-align: left;"
1689 " border: 0px;"
1690 " width: 20%;"
1691 " padding-right: 15px;"
1692 " padding-left: 20px;"
1693 " font-weight: bold;"
1694 "}"
1695 ".tabular-view .odd-row {"
1696 " background-color: #f9f9f9;"
1697 "}"
1698 ".section {"
1699 " font-weight: bold;"
1700 " padding-top:25px;"
1701 "}" );
1702
1703 // We have some subtle differences between Qt based style and QWebKit style
1704 switch ( styleSheetType )
1705 {
1706 case StyleSheetType::Qt:
1707 myStyle += QStringLiteral(
1708 ".tabular-view{ "
1709 " border-collapse: collapse;"
1710 " width: 95%;"
1711 "}"
1712 ".tabular-view th, .tabular-view td { "
1713 " border:1px solid black;"
1714 "}" );
1715 break;
1716
1718 myStyle += QStringLiteral(
1719 "body { "
1720 " margin: auto;"
1721 " width: 97%;"
1722 "}"
1723 "table.tabular-view, table.list-view { "
1724 " border-collapse: collapse;"
1725 " table-layout:fixed;"
1726 " width: 100% !important;"
1727 " font-size: 90%;"
1728 "}"
1729 // Override
1730 "h1 { "
1731 " line-height: inherit;"
1732 "}"
1733 "td, th {"
1734 " word-wrap: break-word; "
1735 " vertical-align: top;"
1736 "}"
1737 // Set first column width
1738 ".list-view th:first-child, .list-view td:first-child {"
1739 " width: 20%;"
1740 "}"
1741 ".list-view.highlight { "
1742 " padding-left: inherit; "
1743 "}"
1744 // Set first column width for inner tables
1745 ".tabular-view th:first-child, .tabular-view td:first-child { "
1746 " width: 20%; "
1747 "}"
1748 // Makes titles bg stand up
1749 ".tabular-view th.strong { "
1750 " background-color: #eee; "
1751 "}"
1752 // Give some visual appearance to those ugly nested tables
1753 ".tabular-view th, .tabular-view td { "
1754 " border: 1px solid #eee;"
1755 "}"
1756 );
1757 break;
1758 }
1759
1760 return myStyle;
1761}
1762
1764{
1765 if ( 0 >= OGRGetDriverCount() )
1766 {
1767 OGRRegisterAll();
1768 }
1769}
1770
1771QString QgsApplication::absolutePathToRelativePath( const QString &aPath, const QString &targetPath )
1772{
1773 QString aPathUrl = aPath;
1774 QString tPathUrl = targetPath;
1775#if defined( Q_OS_WIN )
1776 const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1777
1778 aPathUrl.replace( '\\', '/' );
1779 if ( aPathUrl.startsWith( "//" ) )
1780 {
1781 // keep UNC prefix
1782 aPathUrl = "\\\\" + aPathUrl.mid( 2 );
1783 }
1784
1785 tPathUrl.replace( '\\', '/' );
1786 if ( tPathUrl.startsWith( "//" ) )
1787 {
1788 // keep UNC prefix
1789 tPathUrl = "\\\\" + tPathUrl.mid( 2 );
1790 }
1791#else
1792 const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1793#endif
1794
1795#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1796 QStringList targetElems = tPathUrl.split( '/', QString::SkipEmptyParts );
1797 QStringList aPathElems = aPathUrl.split( '/', QString::SkipEmptyParts );
1798#else
1799 QStringList targetElems = tPathUrl.split( '/', Qt::SkipEmptyParts );
1800 QStringList aPathElems = aPathUrl.split( '/', Qt::SkipEmptyParts );
1801#endif
1802
1803 targetElems.removeAll( QStringLiteral( "." ) );
1804 aPathElems.removeAll( QStringLiteral( "." ) );
1805
1806 // remove common part
1807 int n = 0;
1808 while ( !aPathElems.isEmpty() &&
1809 !targetElems.isEmpty() &&
1810 aPathElems[0].compare( targetElems[0], cs ) == 0 )
1811 {
1812 aPathElems.removeFirst();
1813 targetElems.removeFirst();
1814 n++;
1815 }
1816
1817 if ( n == 0 )
1818 {
1819 // no common parts; might not even be a file
1820 return aPathUrl;
1821 }
1822
1823 if ( !targetElems.isEmpty() )
1824 {
1825 // go up to the common directory
1826 for ( int i = 0; i < targetElems.size(); i++ )
1827 {
1828 aPathElems.insert( 0, QStringLiteral( ".." ) );
1829 }
1830 }
1831 else
1832 {
1833 // let it start with . nevertheless,
1834 // so relative path always start with either ./ or ../
1835 aPathElems.insert( 0, QStringLiteral( "." ) );
1836 }
1837
1838 return aPathElems.join( QLatin1Char( '/' ) );
1839}
1840
1841QString QgsApplication::relativePathToAbsolutePath( const QString &rpath, const QString &targetPath )
1842{
1843 // relative path should always start with ./ or ../
1844 if ( !rpath.startsWith( QLatin1String( "./" ) ) && !rpath.startsWith( QLatin1String( "../" ) ) )
1845 {
1846 return rpath;
1847 }
1848
1849 QString rPathUrl = rpath;
1850 QString targetPathUrl = targetPath;
1851
1852#if defined(Q_OS_WIN)
1853 rPathUrl.replace( '\\', '/' );
1854 targetPathUrl.replace( '\\', '/' );
1855
1856 bool uncPath = targetPathUrl.startsWith( "//" );
1857#endif
1858
1859#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1860 QStringList srcElems = rPathUrl.split( '/', QString::SkipEmptyParts );
1861 QStringList targetElems = targetPathUrl.split( '/', QString::SkipEmptyParts );
1862#else
1863 QStringList srcElems = rPathUrl.split( '/', Qt::SkipEmptyParts );
1864 QStringList targetElems = targetPathUrl.split( '/', Qt::SkipEmptyParts );
1865#endif
1866
1867#if defined(Q_OS_WIN)
1868 if ( uncPath )
1869 {
1870 targetElems.insert( 0, "" );
1871 targetElems.insert( 0, "" );
1872 }
1873#endif
1874
1875 // append source path elements
1876 targetElems << srcElems;
1877 targetElems.removeAll( QStringLiteral( "." ) );
1878
1879 // resolve ..
1880 int pos;
1881 while ( ( pos = targetElems.indexOf( QLatin1String( ".." ) ) ) > 0 )
1882 {
1883 // remove preceding element and ..
1884 targetElems.removeAt( pos - 1 );
1885 targetElems.removeAt( pos - 1 );
1886 }
1887
1888#if !defined(Q_OS_WIN)
1889 // make path absolute
1890 targetElems.prepend( QString() );
1891#endif
1892
1893 return targetElems.join( QLatin1Char( '/' ) );
1894}
1895
1897{
1898 return *sBuildSourcePath();
1899}
1900
1902{
1903 return *sBuildOutputPath();
1904}
1905
1906#if defined(_MSC_VER) && !defined(USING_NMAKE) && !defined(USING_NINJA)
1907QString QgsApplication::cfgIntDir()
1908{
1909 return *sCfgIntDir();
1910}
1911#endif
1912
1913void QgsApplication::skipGdalDriver( const QString &driver )
1914{
1915 if ( sGdalSkipList()->contains( driver ) || driver.isEmpty() )
1916 {
1917 return;
1918 }
1919 *sGdalSkipList() << driver;
1921}
1922
1923void QgsApplication::restoreGdalDriver( const QString &driver )
1924{
1925 if ( !sGdalSkipList()->contains( driver ) )
1926 {
1927 return;
1928 }
1929 int myPos = sGdalSkipList()->indexOf( driver );
1930 if ( myPos >= 0 )
1931 {
1932 sGdalSkipList()->removeAt( myPos );
1933 }
1935}
1936
1938{
1939 return *sGdalSkipList();
1940}
1941
1942void QgsApplication::setSkippedGdalDrivers( const QStringList &skippedGdalDrivers,
1943 const QStringList &deferredSkippedGdalDrivers )
1944{
1945 *sGdalSkipList() = skippedGdalDrivers;
1946 *sDeferredSkippedGdalDrivers() = deferredSkippedGdalDrivers;
1947
1948 QgsSettings settings;
1949 settings.setValue( QStringLiteral( "gdal/skipDrivers" ), skippedGdalDrivers.join( QLatin1Char( ',' ) ) );
1950
1952}
1953
1955{
1956 QgsSettings settings;
1957 QString joinedList, delimiter;
1958 if ( settings.contains( QStringLiteral( "gdal/skipDrivers" ) ) )
1959 {
1960 joinedList = settings.value( QStringLiteral( "gdal/skipDrivers" ), QString() ).toString();
1961 delimiter = QStringLiteral( "," );
1962 }
1963 else
1964 {
1965 joinedList = settings.value( QStringLiteral( "gdal/skipList" ), QString() ).toString();
1966 delimiter = QStringLiteral( " " );
1967 }
1968 QStringList myList;
1969 if ( !joinedList.isEmpty() )
1970 {
1971 myList = joinedList.split( delimiter );
1972 }
1973 *sGdalSkipList() = myList;
1975}
1976
1978{
1979 return *sDeferredSkippedGdalDrivers();
1980}
1981
1983{
1984 sGdalSkipList()->removeDuplicates();
1985 QStringList realDisabledDriverList;
1986 for ( const auto &driverName : *sGdalSkipList() )
1987 {
1988 if ( !sDeferredSkippedGdalDrivers()->contains( driverName ) )
1989 realDisabledDriverList << driverName;
1990 }
1991 QString myDriverList = realDisabledDriverList.join( ',' );
1992 QgsDebugMsgLevel( QStringLiteral( "Gdal Skipped driver list set to:" ), 2 );
1993 QgsDebugMsgLevel( myDriverList, 2 );
1994 CPLSetConfigOption( "GDAL_SKIP", myDriverList.toUtf8() );
1995 GDALAllRegister(); //to update driver list and skip missing ones
1996}
1997
1999{
2000 QString folder = userThemesFolder();
2001 QDir myDir( folder );
2002 if ( !myDir.exists() )
2003 {
2004 myDir.mkpath( folder );
2005 }
2006
2007 return true;
2008}
2009
2010void QgsApplication::copyPath( const QString &src, const QString &dst )
2011{
2012 QDir dir( src );
2013 if ( ! dir.exists() )
2014 return;
2015
2016 const auto subDirectories = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
2017 for ( const QString &d : subDirectories )
2018 {
2019 QString dst_path = dst + QDir::separator() + d;
2020 dir.mkpath( dst_path );
2021 copyPath( src + QDir::separator() + d, dst_path );
2022 }
2023
2024 const auto files = dir.entryList( QDir::Files );
2025 for ( const QString &f : files )
2026 {
2027 QFile::copy( src + QDir::separator() + f, dst + QDir::separator() + f );
2028 }
2029}
2030
2032{
2033 //read values from QgsSettings
2034 QgsSettings settings;
2035
2036 QVariantMap variables;
2037
2038 //check if settings contains any variables
2039 settings.beginGroup( "variables" );
2040 QStringList childKeys = settings.childKeys();
2041 for ( QStringList::const_iterator it = childKeys.constBegin(); it != childKeys.constEnd(); ++it )
2042 {
2043 QString name = *it;
2044 variables.insert( name, settings.value( name ) );
2045 }
2046
2047 return variables;
2048}
2049
2050void QgsApplication::setCustomVariables( const QVariantMap &variables )
2051{
2052 QgsSettings settings;
2053
2054 QVariantMap::const_iterator it = variables.constBegin();
2055 settings.beginGroup( "variables" );
2056 settings.remove( "" );
2057 for ( ; it != variables.constEnd(); ++it )
2058 {
2059 settings.setValue( it.key(), it.value() );
2060 }
2061
2063}
2064
2065void QgsApplication::setCustomVariable( const QString &name, const QVariant &value )
2066{
2067 // save variable to settings
2068 QgsSettings settings;
2069
2070 settings.setValue( QStringLiteral( "variables/" ) + name, value );
2071
2073}
2074
2075int QgsApplication::scaleIconSize( int standardSize, bool applyDevicePixelRatio )
2076{
2077 QFontMetrics fm( ( QFont() ) );
2078 const double scale = 1.1 * standardSize / 24;
2079 int scaledIconSize = static_cast< int >( std::floor( std::max( Qgis::UI_SCALE_FACTOR * fm.height() * scale, static_cast< double >( standardSize ) ) ) );
2080 if ( applyDevicePixelRatio )
2081 {
2082 if ( QWidget *activeWindow = QApplication::activeWindow() )
2083 scaledIconSize *= ( activeWindow->screen() ? QApplication::activeWindow()->screen()->devicePixelRatio() : 1 );
2084 }
2085 return scaledIconSize;
2086}
2087
2092
2093void QgsApplication::setTranslation( const QString &translation )
2094{
2095 *sTranslation() = translation;
2096 if ( auto app = QgsApplication::instance() )
2097 {
2098 app->installTranslators();
2099 }
2100}
2101
2103{
2104 return *sTranslation();
2105}
2106
2108{
2109 emit requestForTranslatableObjects( translationContext );
2110}
2111
2113{
2114 ApplicationMembers *appMembers = members();
2115 if ( appMembers->mNullRepresentation.isNull() )
2116 {
2117 appMembers->mNullRepresentation = QgsSettings().value( QStringLiteral( "qgis/nullValue" ), QStringLiteral( "NULL" ) ).toString();
2118 }
2119 return appMembers->mNullRepresentation;
2120}
2121
2122void QgsApplication::setNullRepresentation( const QString &nullRepresentation )
2123{
2124 ApplicationMembers *appMembers = members();
2125 if ( !appMembers || appMembers->mNullRepresentation == nullRepresentation )
2126 return;
2127
2128 appMembers->mNullRepresentation = nullRepresentation;
2129 QgsSettings().setValue( QStringLiteral( "qgis/nullValue" ), nullRepresentation );
2130
2131 QgsApplication *app = instance();
2132 if ( app )
2133 emit app->nullRepresentationChanged();
2134}
2135
2137{
2138 return members()->mActionScopeRegistry;
2139}
2140
2141bool QgsApplication::createDatabase( QString *errorMessage )
2142{
2143 // set a working directory up for gdal to write .aux.xml files into
2144 // for cases where the raster dir is read only to the user
2145 // if the env var is already set it will be used preferentially
2146 QString myPamPath = qgisSettingsDirPath() + QStringLiteral( "gdal_pam/" );
2147 QDir myDir( myPamPath );
2148 if ( !myDir.exists() )
2149 {
2150 myDir.mkpath( myPamPath ); //fail silently
2151 }
2152
2153#if defined(Q_OS_WIN)
2154 CPLSetConfigOption( "GDAL_PAM_PROXY_DIR", myPamPath.toUtf8() );
2155#else
2156 //under other OS's we use an environment var so the user can
2157 //override the path if he likes
2158 int myChangeFlag = 0; //whether we want to force the env var to change
2159 setenv( "GDAL_PAM_PROXY_DIR", myPamPath.toUtf8(), myChangeFlag );
2160#endif
2161
2162 // Check qgis.db and make private copy if necessary
2163 QFile qgisPrivateDbFile( QgsApplication::qgisUserDatabaseFilePath() );
2164
2165 // first we look for ~/.qgis/qgis.db
2166 if ( !qgisPrivateDbFile.exists() )
2167 {
2168 // if it doesn't exist we copy it in from the global resources dir
2169 QString qgisMasterDbFileName = QgsApplication::qgisMasterDatabaseFilePath();
2170 QFile masterFile( qgisMasterDbFileName );
2171
2172 // Must be sure there is destination directory ~/.qgis
2173 QDir().mkpath( QgsApplication::qgisSettingsDirPath() );
2174
2175 //now copy the master file into the users .qgis dir
2176 bool isDbFileCopied = masterFile.copy( qgisPrivateDbFile.fileName() );
2177
2178 if ( !isDbFileCopied )
2179 {
2180 if ( errorMessage )
2181 {
2182 *errorMessage = tr( "[ERROR] Can not make qgis.db private copy" );
2183 }
2184 return false;
2185 }
2186
2187 QFile::Permissions perms = QFile( qgisPrivateDbFile.fileName() ).permissions();
2188 if ( !( perms & QFile::WriteOwner ) )
2189 {
2190 if ( !qgisPrivateDbFile.setPermissions( perms | QFile::WriteOwner ) )
2191 {
2192 if ( errorMessage )
2193 {
2194 *errorMessage = tr( "Can not make '%1' user writable" ).arg( qgisPrivateDbFile.fileName() );
2195 }
2196 return false;
2197 }
2198 }
2199 }
2200 else
2201 {
2202 // migrate if necessary
2204 if ( database.open( QgsApplication::qgisUserDatabaseFilePath() ) != SQLITE_OK )
2205 {
2206 if ( errorMessage )
2207 {
2208 *errorMessage = tr( "Could not open qgis.db" );
2209 }
2210 return false;
2211 }
2212
2213 char *errmsg = nullptr;
2214 int res = sqlite3_exec( database.get(), "SELECT srs_id FROM tbl_srs LIMIT 0", nullptr, nullptr, &errmsg );
2215 if ( res != SQLITE_OK )
2216 {
2217 sqlite3_free( errmsg );
2218
2219 // qgis.db is missing tbl_srs, create it
2220 if ( sqlite3_exec( database.get(),
2221 "DROP INDEX IF EXISTS idx_srsauthid;"
2222 "CREATE TABLE tbl_srs ("
2223 "srs_id INTEGER PRIMARY KEY,"
2224 "description text NOT NULL,"
2225 "projection_acronym text NOT NULL,"
2226 "ellipsoid_acronym NOT NULL,"
2227 "parameters text NOT NULL,"
2228 "srid integer,"
2229 "auth_name varchar,"
2230 "auth_id varchar,"
2231 "is_geo integer NOT NULL,"
2232 "deprecated boolean,"
2233 "wkt text);"
2234 "CREATE INDEX idx_srsauthid on tbl_srs(auth_name,auth_id);", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2235 {
2236 if ( errorMessage )
2237 {
2238 *errorMessage = tr( "Creation of missing tbl_srs in the private qgis.db failed.\n%1" ).arg( QString::fromUtf8( errmsg ) );
2239 }
2240 sqlite3_free( errmsg );
2241 return false;
2242 }
2243 }
2244 else
2245 {
2246 // test if wkt column exists in database
2247 res = sqlite3_exec( database.get(), "SELECT wkt FROM tbl_srs LIMIT 0", nullptr, nullptr, &errmsg );
2248 if ( res != SQLITE_OK )
2249 {
2250 // need to add wkt column
2251 sqlite3_free( errmsg );
2252 if ( sqlite3_exec( database.get(),
2253 "DROP INDEX IF EXISTS idx_srsauthid;"
2254 "DROP TABLE IF EXISTS tbl_srs_bak;"
2255 "ALTER TABLE tbl_srs RENAME TO tbl_srs_bak;"
2256 "CREATE TABLE tbl_srs ("
2257 "srs_id INTEGER PRIMARY KEY,"
2258 "description text NOT NULL,"
2259 "projection_acronym text NOT NULL,"
2260 "ellipsoid_acronym NOT NULL,"
2261 "parameters text NOT NULL,"
2262 "srid integer,"
2263 "auth_name varchar,"
2264 "auth_id varchar,"
2265 "is_geo integer NOT NULL,"
2266 "deprecated boolean,"
2267 "wkt text);"
2268 "CREATE INDEX idx_srsauthid on tbl_srs(auth_name,auth_id);"
2269 "INSERT INTO tbl_srs(srs_id,description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) SELECT srs_id,description,projection_acronym,ellipsoid_acronym,parameters,srid,'','',is_geo,0 FROM tbl_srs_bak;"
2270 "DROP TABLE tbl_srs_bak", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2271 {
2272 if ( errorMessage )
2273 {
2274 *errorMessage = tr( "Migration of private qgis.db failed.\n%1" ).arg( QString::fromUtf8( errmsg ) );
2275 }
2276 sqlite3_free( errmsg );
2277 return false;
2278 }
2279 }
2280 }
2281
2282 res = sqlite3_exec( database.get(), "SELECT acronym FROM tbl_projection LIMIT 0", nullptr, nullptr, &errmsg );
2283 if ( res != SQLITE_OK )
2284 {
2285 sqlite3_free( errmsg );
2286
2287 // qgis.db is missing tbl_projection, create it
2288 if ( sqlite3_exec( database.get(),
2289 "CREATE TABLE tbl_projection ("
2290 "acronym varchar(20) NOT NULL PRIMARY KEY,"
2291 "name varchar(255) NOT NULL default '',"
2292 "notes varchar(255) NOT NULL default '',"
2293 "parameters varchar(255) NOT NULL default ''"
2294 ")", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2295 {
2296 if ( errorMessage )
2297 {
2298 *errorMessage = tr( "Creation of missing tbl_projection in the private qgis.db failed.\n%1" ).arg( QString::fromUtf8( errmsg ) );
2299 }
2300 sqlite3_free( errmsg );
2301 return false;
2302 }
2303 }
2304
2305 res = sqlite3_exec( database.get(), "SELECT epsg FROM tbl_srs LIMIT 0", nullptr, nullptr, &errmsg );
2306 if ( res == SQLITE_OK )
2307 {
2308 // epsg column exists => need migration
2309 if ( sqlite3_exec( database.get(),
2310 "DROP INDEX IF EXISTS idx_srsauthid;"
2311 "DROP TABLE IF EXISTS tbl_srs_bak;"
2312 "ALTER TABLE tbl_srs RENAME TO tbl_srs_bak;"
2313 "CREATE TABLE tbl_srs ("
2314 "srs_id INTEGER PRIMARY KEY,"
2315 "description text NOT NULL,"
2316 "projection_acronym text NOT NULL,"
2317 "ellipsoid_acronym NOT NULL,"
2318 "parameters text NOT NULL,"
2319 "srid integer,"
2320 "auth_name varchar,"
2321 "auth_id varchar,"
2322 "is_geo integer NOT NULL,"
2323 "deprecated boolean,"
2324 "wkt text);"
2325 "CREATE INDEX idx_srsauthid on tbl_srs(auth_name,auth_id);"
2326 "INSERT INTO tbl_srs(srs_id,description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) SELECT srs_id,description,projection_acronym,ellipsoid_acronym,parameters,srid,'','',is_geo,0 FROM tbl_srs_bak;"
2327 "DROP TABLE tbl_srs_bak", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2328 {
2329 if ( errorMessage )
2330 {
2331 *errorMessage = tr( "Migration of private qgis.db failed.\n%1" ).arg( QString::fromUtf8( errmsg ) );
2332 }
2333 sqlite3_free( errmsg );
2334 return false;
2335 }
2336 }
2337 else
2338 {
2339 sqlite3_free( errmsg );
2340 }
2341
2342 if ( sqlite3_exec( database.get(), "DROP VIEW vw_srs", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2343 {
2344 QgsDebugError( QStringLiteral( "vw_srs didn't exists in private qgis.db: %1" ).arg( errmsg ) );
2345 }
2346
2347 if ( sqlite3_exec( database.get(),
2348 "CREATE VIEW vw_srs AS"
2349 " SELECT"
2350 " a.description AS description"
2351 ",a.srs_id AS srs_id"
2352 ",a.is_geo AS is_geo"
2353 ",coalesce(b.name,a.projection_acronym) AS name"
2354 ",a.parameters AS parameters"
2355 ",a.auth_name AS auth_name"
2356 ",a.auth_id AS auth_id"
2357 ",a.deprecated AS deprecated"
2358 " FROM tbl_srs a"
2359 " LEFT OUTER JOIN tbl_projection b ON a.projection_acronym=b.acronym"
2360 " ORDER BY coalesce(b.name,a.projection_acronym),a.description", nullptr, nullptr, &errmsg ) != SQLITE_OK )
2361 {
2362 if ( errorMessage )
2363 {
2364 *errorMessage = tr( "Update of view in private qgis.db failed.\n%1" ).arg( QString::fromUtf8( errmsg ) );
2365 }
2366 sqlite3_free( errmsg );
2367 return false;
2368 }
2369 }
2370 return true;
2371}
2372
2373void QgsApplication::setMaxThreads( int maxThreads )
2374{
2375 QgsDebugMsgLevel( QStringLiteral( "maxThreads: %1" ).arg( maxThreads ), 2 );
2376
2377 // make sure value is between 1 and #cores, if not set to -1 (use #cores)
2378 if ( maxThreads < 1 || maxThreads > QThread::idealThreadCount() )
2379 maxThreads = -1;
2380
2381 // force at least 2 threads -- anything less risks deadlocks within Qt itself (e.g in QImage internal mutexes)
2382 if ( maxThreads > 0 && maxThreads < 2 )
2383 maxThreads = 2;
2384
2385 // save value
2386 ABISYM( sMaxThreads ) = maxThreads;
2387
2388 // if -1 use #cores
2389 if ( maxThreads == -1 )
2390 maxThreads = QThread::idealThreadCount();
2391
2392 // set max thread count in QThreadPool
2393 QThreadPool::globalInstance()->setMaxThreadCount( maxThreads );
2394 QgsDebugMsgLevel( QStringLiteral( "set QThreadPool max thread count to %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ), 2 );
2395}
2396
2398{
2399 return members()->mTaskManager;
2400}
2401
2403{
2404 return members()->mSettingsRegistryCore;
2405}
2406
2408{
2409 return members()->mColorSchemeRegistry;
2410}
2411
2413{
2414 return members()->mPaintEffectRegistry;
2415}
2416
2418{
2419 return members()->mRendererRegistry;
2420}
2421
2423{
2424 return members()->mRasterRendererRegistry;
2425}
2426
2428{
2429 return members()->mPointCloudRendererRegistry;
2430}
2431
2433{
2434 return members()->mTiledSceneRendererRegistry;
2435}
2436
2438{
2439 if ( auto *lInstance = instance() )
2440 {
2441 if ( !instance()->mDataItemProviderRegistry )
2442 {
2443 lInstance->mDataItemProviderRegistry = new QgsDataItemProviderRegistry();
2444 }
2445 return lInstance->mDataItemProviderRegistry;
2446 }
2447 else
2448 {
2449 // no QgsApplication instance
2450 static QgsDataItemProviderRegistry *sDataItemProviderRegistry = nullptr;
2451 if ( !sDataItemProviderRegistry )
2452 sDataItemProviderRegistry = new QgsDataItemProviderRegistry();
2453 return sDataItemProviderRegistry;
2454 }
2455}
2456
2461
2463{
2464 return members()->mSvgCache;
2465}
2466
2468{
2469 return members()->mImageCache;
2470}
2471
2473{
2474 return members()->mSourceCache;
2475}
2476
2478{
2479 return members()->mNetworkContentFetcherRegistry;
2480}
2481
2483{
2484 return members()->mValidityCheckRegistry;
2485}
2486
2488{
2489 return members()->mSymbolLayerRegistry;
2490}
2491
2493{
2494 return members()->mCalloutRegistry;
2495}
2496
2498{
2499 return members()->mLayoutItemRegistry;
2500}
2501
2503{
2504 return members()->mAnnotationItemRegistry;
2505}
2506
2508{
2509 return members()->mSensorRegistry;
2510}
2511
2513{
2514 return members()->mGpsConnectionRegistry;
2515}
2516
2518{
2519 return members()->mGpsBabelFormatRegistry;
2520}
2521
2523{
2524 return members()->mPluginLayerRegistry;
2525}
2526
2528{
2529 return members()->mClassificationMethodRegistry;
2530}
2531
2533{
2534 return members()->mBookmarkManager;
2535}
2536
2538{
2539 return members()->mTileDownloadManager;
2540}
2541
2543{
2544 return members()->mRecentStyleHandler;
2545}
2546
2548{
2549 return members()->mQueryLogger;
2550}
2551
2553{
2554 return members()->mStyleModel;
2555}
2556
2558{
2559 return members()->mFontManager;
2560}
2561
2563{
2564 return members()->mMessageLog;
2565}
2566
2568{
2569 return members()->mProcessingRegistry;
2570}
2571
2573{
2574 return members()->mConnectionRegistry;
2575}
2576
2578{
2579 return members()->mLayerMetadataProviderRegistry;
2580}
2581
2583{
2584 return members()->mPageSizeRegistry;
2585}
2586
2588{
2589 return members()->mAnnotationRegistry;
2590}
2591
2593{
2594 return members()->mNumericFormatRegistry;
2595}
2596
2598{
2599 return members()->mFieldFormatterRegistry;
2600}
2601
2603{
2604 return members()->m3DRendererRegistry;
2605}
2606
2608{
2609 return members()->m3DSymbolRegistry;
2610}
2611
2613{
2614 return members()->mScaleBarRendererRegistry;
2615}
2616
2618{
2619 return members()->mProjectStorageRegistry;
2620}
2621
2623{
2624 return members()->mExternalStorageRegistry;
2625}
2626
2628{
2629 return members()->mLocalizedDataPathRegistry;
2630}
2631
2632QgsApplication::ApplicationMembers::ApplicationMembers()
2633{
2634 // don't use initializer lists or scoped pointers - as more objects are added here we
2635 // will need to be careful with the order of creation/destruction
2636 mSettingsRegistryCore = new QgsSettingsRegistryCore();
2637 mLocalizedDataPathRegistry = new QgsLocalizedDataPathRegistry();
2638 mMessageLog = new QgsMessageLog();
2639 QgsRuntimeProfiler *profiler = QgsRuntimeProfiler::threadLocalInstance();
2640
2641 {
2642 profiler->start( tr( "Create query logger" ) );
2643 mQueryLogger = new QgsDatabaseQueryLog();
2644 profiler->end();
2645 }
2646 {
2647 profiler->start( tr( "Setup coordinate reference system registry" ) );
2648 mCrsRegistry = new QgsCoordinateReferenceSystemRegistry();
2649 profiler->end();
2650 }
2651 {
2652 profiler->start( tr( "Create connection registry" ) );
2653 mConnectionRegistry = new QgsConnectionRegistry();
2654 profiler->end();
2655 }
2656 {
2657 profiler->start( tr( "Create project storage registry" ) );
2658 mProjectStorageRegistry = new QgsProjectStorageRegistry();
2659 profiler->end();
2660 }
2661 {
2662 profiler->start( tr( "Create layer metadata provider registry" ) );
2663 mLayerMetadataProviderRegistry = new QgsLayerMetadataProviderRegistry();
2664 profiler->end();
2665 }
2666 {
2667 profiler->start( tr( "Create font manager" ) );
2668 mFontManager = new QgsFontManager();
2669 profiler->end();
2670 }
2671 {
2672 profiler->start( tr( "Setup task manager" ) );
2673 mTaskManager = new QgsTaskManager();
2674 profiler->end();
2675 }
2676 {
2677 profiler->start( tr( "Setup action scope registry" ) );
2678 mActionScopeRegistry = new QgsActionScopeRegistry();
2679 profiler->end();
2680 }
2681 {
2682 profiler->start( tr( "Setup numeric formats" ) );
2683 mNumericFormatRegistry = new QgsNumericFormatRegistry();
2684 profiler->end();
2685 }
2686 {
2687 profiler->start( tr( "Setup field formats" ) );
2688 mFieldFormatterRegistry = new QgsFieldFormatterRegistry();
2689 profiler->end();
2690 }
2691 {
2692 profiler->start( tr( "Setup SVG cache" ) );
2693 mSvgCache = new QgsSvgCache();
2694 profiler->end();
2695 }
2696 {
2697 profiler->start( tr( "Setup image cache" ) );
2698 mImageCache = new QgsImageCache();
2699 profiler->end();
2700 }
2701 {
2702 profiler->start( tr( "Setup source cache" ) );
2703 mSourceCache = new QgsSourceCache();
2704 profiler->end();
2705 }
2706 {
2707 profiler->start( tr( "Setup color scheme registry" ) );
2708 mColorSchemeRegistry = new QgsColorSchemeRegistry();
2709 profiler->end();
2710 }
2711 {
2712 profiler->start( tr( "Setup paint effect" ) );
2713 mPaintEffectRegistry = new QgsPaintEffectRegistry();
2714 profiler->end();
2715 }
2716 {
2717 profiler->start( tr( "Setup symbol layer registry" ) );
2718 mSymbolLayerRegistry = new QgsSymbolLayerRegistry();
2719 profiler->end();
2720 }
2721 {
2722 profiler->start( tr( "Recent style handler" ) );
2723 mRecentStyleHandler = new QgsRecentStyleHandler();
2724 profiler->end();
2725 }
2726 {
2727 profiler->start( tr( "Setup callout registry" ) );
2728 mCalloutRegistry = new QgsCalloutRegistry();
2729 profiler->end();
2730 }
2731 {
2732 profiler->start( tr( "Setup renderer registry" ) );
2733 mRendererRegistry = new QgsRendererRegistry();
2734 profiler->end();
2735 }
2736 {
2737 profiler->start( tr( "Setup raster renderer registry" ) );
2738 mRasterRendererRegistry = new QgsRasterRendererRegistry();
2739 profiler->end();
2740 }
2741 {
2742 profiler->start( tr( "Setup point cloud renderer registry" ) );
2743 mPointCloudRendererRegistry = new QgsPointCloudRendererRegistry();
2744 profiler->end();
2745 }
2746 {
2747 profiler->start( tr( "Setup tiled scene renderer registry" ) );
2748 mTiledSceneRendererRegistry = new QgsTiledSceneRendererRegistry();
2749 profiler->end();
2750 }
2751 {
2752 profiler->start( tr( "Setup GPS registry" ) );
2753 mGpsConnectionRegistry = new QgsGpsConnectionRegistry();
2754 profiler->end();
2755 }
2756 {
2757 profiler->start( tr( "Setup GPSBabel format registry" ) );
2758 mGpsBabelFormatRegistry = new QgsBabelFormatRegistry();
2759 profiler->end();
2760 }
2761 {
2762 profiler->start( tr( "Setup plugin layer registry" ) );
2763 mPluginLayerRegistry = new QgsPluginLayerRegistry();
2764 profiler->end();
2765 }
2766 {
2767 profiler->start( tr( "Setup Processing registry" ) );
2768 mProcessingRegistry = new QgsProcessingRegistry();
2769 profiler->end();
2770 }
2771 mPageSizeRegistry = new QgsPageSizeRegistry();
2772 {
2773 profiler->start( tr( "Setup layout item registry" ) );
2774 mLayoutItemRegistry = new QgsLayoutItemRegistry();
2775 mLayoutItemRegistry->populate();
2776 profiler->end();
2777 }
2778 {
2779 profiler->start( tr( "Setup annotation registry" ) );
2780 mAnnotationRegistry = new QgsAnnotationRegistry();
2781 profiler->end();
2782 }
2783 {
2784 profiler->start( tr( "Setup annotation item registry" ) );
2785 mAnnotationItemRegistry = new QgsAnnotationItemRegistry();
2786 mAnnotationItemRegistry->populate();
2787 profiler->end();
2788 }
2789 {
2790 profiler->start( tr( "Setup sensor registry" ) );
2791 mSensorRegistry = new QgsSensorRegistry();
2792 mSensorRegistry->populate();
2793 profiler->end();
2794 }
2795 {
2796 profiler->start( tr( "Setup 3D symbol registry" ) );
2797 m3DSymbolRegistry = new Qgs3DSymbolRegistry();
2798 profiler->end();
2799 }
2800 {
2801 profiler->start( tr( "Setup 3D renderer registry" ) );
2802 m3DRendererRegistry = new Qgs3DRendererRegistry();
2803 profiler->end();
2804 }
2805 {
2806 profiler->start( tr( "Setup external storage registry" ) );
2807 mExternalStorageRegistry = new QgsExternalStorageRegistry();
2808 profiler->end();
2809 }
2810 {
2811 profiler->start( tr( "Setup network content cache" ) );
2812 mNetworkContentFetcherRegistry = new QgsNetworkContentFetcherRegistry();
2813 profiler->end();
2814 }
2815 {
2816 profiler->start( tr( "Setup layout check registry" ) );
2817 mValidityCheckRegistry = new QgsValidityCheckRegistry();
2818 profiler->end();
2819 }
2820 {
2821 profiler->start( tr( "Setup classification registry" ) );
2822 mClassificationMethodRegistry = new QgsClassificationMethodRegistry();
2823 profiler->end();
2824 }
2825 {
2826 profiler->start( tr( "Setup bookmark manager" ) );
2827 mBookmarkManager = new QgsBookmarkManager( nullptr );
2828 profiler->end();
2829 }
2830 {
2831 profiler->start( tr( "Setup tile download manager" ) );
2832 mTileDownloadManager = new QgsTileDownloadManager();
2833 profiler->end();
2834 }
2835 {
2836 profiler->start( tr( "Setup scalebar registry" ) );
2837 mScaleBarRendererRegistry = new QgsScaleBarRendererRegistry();
2838 profiler->end();
2839 }
2840}
2841
2842QgsApplication::ApplicationMembers::~ApplicationMembers()
2843{
2844 delete mStyleModel;
2845 delete mTileDownloadManager;
2846 delete mScaleBarRendererRegistry;
2847 delete mValidityCheckRegistry;
2848 delete mActionScopeRegistry;
2849 delete m3DRendererRegistry;
2850 delete m3DSymbolRegistry;
2851 delete mAnnotationRegistry;
2852 delete mColorSchemeRegistry;
2853 delete mFieldFormatterRegistry;
2854 delete mGpsConnectionRegistry;
2855 delete mGpsBabelFormatRegistry;
2856 delete mMessageLog;
2857 delete mPaintEffectRegistry;
2858 delete mPluginLayerRegistry;
2859 delete mProcessingRegistry;
2860 delete mPageSizeRegistry;
2861 delete mAnnotationItemRegistry;
2862 delete mSensorRegistry;
2863 delete mLayoutItemRegistry;
2864 delete mPointCloudRendererRegistry;
2865 delete mTiledSceneRendererRegistry;
2866 delete mRasterRendererRegistry;
2867 delete mRendererRegistry;
2868 delete mSvgCache;
2869 delete mImageCache;
2870 delete mSourceCache;
2871 delete mCalloutRegistry;
2872 delete mRecentStyleHandler;
2873 delete mSymbolLayerRegistry;
2874 delete mExternalStorageRegistry;
2875 delete mTaskManager;
2876 delete mNetworkContentFetcherRegistry;
2877 delete mClassificationMethodRegistry;
2878 delete mNumericFormatRegistry;
2879 delete mBookmarkManager;
2880 delete mConnectionRegistry;
2881 delete mProjectStorageRegistry;
2882 delete mLayerMetadataProviderRegistry;
2883 delete mFontManager;
2884 delete mLocalizedDataPathRegistry;
2885 delete mCrsRegistry;
2886 delete mQueryLogger;
2887 delete mSettingsRegistryCore;
2888}
2889
2890QgsApplication::ApplicationMembers *QgsApplication::members()
2891{
2892 if ( auto *lInstance = instance() )
2893 {
2894 return lInstance->mApplicationMembers;
2895 }
2896 else
2897 {
2898 static QRecursiveMutex sMemberMutex;
2899 QMutexLocker lock( &sMemberMutex );
2900 if ( !sApplicationMembers )
2901 sApplicationMembers = new ApplicationMembers();
2902 return sApplicationMembers;
2903 }
2904}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:4086
Keeps track of available 3D renderers.
Registry of available 3D symbol classes.
The action scope registry is an application wide registry that contains a list of available action sc...
Registry of available annotation item types.
Extends QApplication to provide access to QGIS specific resources such as theme paths,...
static QString resolvePkgPath()
Calculate the application pkg path.
static int scaleIconSize(int standardSize, bool applyDevicePixelRatio=false)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
static void restoreGdalDriver(const QString &driver)
Sets the GDAL_SKIP environment variable to exclude the specified driver and then calls GDALDriverMana...
static void setCustomVariables(const QVariantMap &customVariables)
Custom expression variables for this application.
QString translation() const
Returns the current application translation locale code.
static QString i18nPath()
Returns the path to the translation directory.
static QgsAnnotationItemRegistry * annotationItemRegistry()
Returns the application's annotation item registry, used for annotation item types.
static QString osName()
Returns a string name of the operating system QGIS is running on.
static void registerOgrDrivers()
Register OGR drivers ensuring this only happens once.
static QString sponsorsFilePath()
Returns the path to the sponsors file.
static QgsRecentStyleHandler * recentStyleHandler()
Returns the handler for recently used style items.
endian_t
Constants for endian-ness.
static QString qgisMasterDatabaseFilePath()
Returns the path to the master qgis.db file.
static void skipGdalDriver(const QString &driver)
Sets the GDAL_SKIP environment variable to include the specified driver and then calls GDALDriverMana...
static QString defaultThemePath()
Returns the path to the default theme directory.
static QgsPageSizeRegistry * pageSizeRegistry()
Returns the application's page size registry, used for managing layout page sizes.
static QgsValidityCheckRegistry * validityCheckRegistry()
Returns the application's validity check registry, used for managing validity checks.
static QgsDataItemProviderRegistry * dataItemProviderRegistry()
Returns the application's data item provider registry, which keeps a list of data item providers that...
static QString userStylePath()
Returns the path to user's style.
static QString platform()
Returns the QGIS platform name, e.g., "desktop", "server", "qgis_process" or "external" (for external...
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QgsLayerMetadataProviderRegistry * layerMetadataProviderRegistry()
Returns registry of available layer metadata provider implementations.
static QgsConnectionRegistry * connectionRegistry()
Returns the application's connection registry, used for managing saved data provider connections.
static void exitQgis()
deletes provider registry and map layer registry
static void setPluginPath(const QString &pluginPath)
Alters plugin path - used by 3rd party apps.
static const QgsSettingsEntryStringList * settingsSearchPathsForSVG
Settings entry search path for SVG.
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QVariantMap customVariables()
Custom expression variables for this application.
static QgsPointCloudRendererRegistry * pointCloudRendererRegistry()
Returns the application's point cloud renderer registry, used for managing point cloud layer 2D rende...
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
static QgsSensorRegistry * sensorRegistry()
Returns the application's sensor registry, used for sensor types.
static QString pluginPath()
Returns the path to the application plugin directory.
static void setUITheme(const QString &themeName)
Set the current UI theme used to style the interface.
static bool createDatabase(QString *errorMessage=nullptr)
initialize qgis.db
static const QgsSettingsEntryBool * settingsLocaleOverrideFlag
Settings entry locale override flag.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
static int systemMemorySizeMb()
Returns the size of the system memory (RAM) in megabytes.
static void setLocale(const QLocale &locale)
Sets the QGIS locale - used mainly by 3rd party apps and tests.
static void init(QString profileFolder=QString())
This method initializes paths etc for QGIS.
static void setThemeName(const QString &themeName)
Set the active theme to the specified theme.
void customVariablesChanged()
Emitted whenever a custom global variable changes.
static QString buildSourcePath()
Returns path to the source directory. Valid only when running from build directory.
static QString buildOutputPath()
Returns path to the build output directory. Valid only when running from build directory.
bool notify(QObject *receiver, QEvent *event) override
Catch exceptions when sending event to receiver.
static int maxThreads()
Gets maximum concurrent thread count.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QgsScaleBarRendererRegistry * scaleBarRendererRegistry()
Gets the registry of available scalebar renderers.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
static void setFileOpenEventReceiver(QObject *receiver)
Sets the FileOpen event receiver.
static QgsSymbolLayerRegistry * symbolLayerRegistry()
Returns the application's symbol layer registry, used for managing symbol layers.
static QgsRasterRendererRegistry * rasterRendererRegistry()
Returns the application's raster renderer registry, used for managing raster layer renderers.
static void applyGdalSkippedDrivers()
Apply the skipped drivers list to gdal.
static void setMaxThreads(int maxThreads)
Set maximum concurrent thread count.
static QgsNumericFormatRegistry * numericFormatRegistry()
Gets the registry of available numeric formats.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static QString licenceFilePath()
Returns the path to the licence file.
static QString libexecPath()
Returns the path with utility executables (help viewer, crssync, ...)
static QStringList skippedGdalDrivers()
Returns the list of gdal drivers that should be skipped (based on GDAL_SKIP environment variable)
StyleSheetType
The StyleSheetType enum represents the stylesheet type that a widget supports.
@ WebBrowser
StyleSheet for Qt GUI widgets (based on QLabel or QTextBrowser), supports basic CSS and Qt extensions...
static QString translatorsFilePath()
Returns the path to the sponsors file.
static const QgsSettingsEntryString * settingsLocaleGlobalLocale
Settings entry locale global locale.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static void setNullRepresentation(const QString &nullRepresentation)
This string is used to represent the value NULL throughout QGIS.
static QString applicationFullName()
Returns the QGIS application full name.
static QgsGpsConnectionRegistry * gpsConnectionRegistry()
Returns the application's GPS connection registry, used for managing GPS connections.
static QString locale()
Returns the QGIS locale.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QStringList svgPaths()
Returns the paths to svg directories.
static void initQgis()
loads providers
static QString showSettings()
Convenience function to get a summary of the paths used in this application instance useful for debug...
bool event(QEvent *event) override
Watch for QFileOpenEvent.
static void setPkgDataPath(const QString &pkgDataPath)
Alters pkg data path - used by 3rd party apps.
static QString absolutePathToRelativePath(const QString &apath, const QString &targetPath)
Converts absolute path to path relative to target.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
~QgsApplication() override
static QgsLocalizedDataPathRegistry * localizedDataPathRegistry()
Returns the registry of data repositories These are used as paths for basemaps, logos,...
static const char * QGIS_APPLICATION_NAME
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
static const char * QGIS_ORGANIZATION_DOMAIN
static QMap< QString, QString > systemEnvVars()
Returns the system environment variables passed to application.
static void setAuthDatabaseDirPath(const QString &authDbDirPath)
Alters authentication data base directory path - used by 3rd party apps.
static QString prefixPath()
Returns the path to the application prefix directory.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
static QgsDatabaseQueryLog * databaseQueryLog()
Returns the database query log.
static QgsMessageLog * messageLog()
Returns the application's message log.
void preNotify(QObject *receiver, QEvent *event, bool *done)
static bool createThemeFolder()
Create the users theme folder.
static QString metadataPath()
Returns the path to the metadata directory.
void localeChanged()
Emitted when project locale has been changed.
static QgsActionScopeRegistry * actionScopeRegistry()
Returns the action scope registry.
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
static const char * QGIS_ORGANIZATION_NAME
static QString contributorsFilePath()
Returns the path to the contributors file.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsSourceCache * sourceCache()
Returns the application's source cache, used for caching embedded and remote source strings as local ...
static QRegularExpression shortNameRegularExpression()
Returns the short name regular expression for line edit validator.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
static QgsAnnotationRegistry * annotationRegistry()
Returns the application's annotation registry, used for managing annotation types.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
static QgsClassificationMethodRegistry * classificationMethodRegistry()
Returns the application's classification methods registry, used in graduated renderer.
static QStringList deferredSkippedGdalDrivers()
Returns the list of gdal drivers that have been disabled in the current session, and thus,...
static QString defaultStylePath()
Returns the path to default style (works as a starting point).
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QString qmlImportPath()
Returns the path where QML components are installed for QGIS Quick library.
Cursor
The Cursor enum defines constants for QGIS custom cursors.
@ ZoomOut
Zoom out.
@ CrossHair
Precisely identify a point on the canvas.
@ Identify
Identify: obtain information about the object.
@ Select
Select a rectangle.
@ CapturePoint
Select and capture a point or a feature.
@ Sampler
Color/Value picker.
static QString qgisAuthDatabaseFilePath()
Returns the path to the user authentication database file: qgis-auth.db.
static QString authorsFilePath()
Returns the path to the authors file.
static QgsBookmarkManager * bookmarkManager()
Returns the application's bookmark manager, used for storing installation-wide bookmarks.
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
static QString activeThemePath()
Returns the path to the currently active theme directory.
static QString defaultThemesFolder()
Returns the path to default themes folder from install (works as a starting point).
static void setSkippedGdalDrivers(const QStringList &skippedGdalDrivers, const QStringList &deferredSkippedGdalDrivers)
Sets the list of gdal drivers that should be disabled (skippedGdalDrivers), but excludes for now the ...
static QgsRendererRegistry * rendererRegistry()
Returns the application's renderer registry, used for managing vector layer renderers.
static void setTranslation(const QString &translation)
Set translation locale code.
static QgsCalloutRegistry * calloutRegistry()
Returns the application's callout registry, used for managing callout types.
static void setPrefixPath(const QString &prefixPath, bool useDefaultPaths=false)
Alters prefix path - used by 3rd party apps.
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
static QString relativePathToAbsolutePath(const QString &rpath, const QString &targetPath)
Converts path relative to target to an absolute path.
static void setSvgPaths(const QStringList &svgPaths)
Sets the paths to svg directories and invalidates the svg path list cache.
static QString developersMapFilePath()
Returns the path to the developers map file.
static QgsBabelFormatRegistry * gpsBabelFormatRegistry()
Returns the application's GPSBabel format registry, used for managing GPSBabel formats.
static endian_t endian()
Returns whether this machine uses big or little endian.
int maxConcurrentConnectionsPerPool() const
The maximum number of concurrent connections per connections pool.
static void setCustomVariable(const QString &name, const QVariant &value)
Set a single custom expression variable.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString iconsPath()
Returns the path to the icons image directory.
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
static QgsExternalStorageRegistry * externalStorageRegistry()
Returns registry of available external storage implementations.
static QHash< QString, QString > uiThemes()
All themes found in ~/.qgis3/themes folder.
static QString splashPath()
Returns the path to the splash screen image directory.
static QString donorsFilePath()
Returns the path to the donors file.
static QString themeName()
Set the active theme to the specified theme.
void nullRepresentationChanged()
This string is used to represent the value NULL throughout QGIS.
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
static QString userThemesFolder()
Returns the path to user's themes folder.
static void registerGdalDriversFromSettings()
Register gdal drivers, excluding the ones mentioned in "gdal/skipList" setting.
static Qgs3DRendererRegistry * renderer3DRegistry()
Returns registry of available 3D renderers.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
static QgsTiledSceneRendererRegistry * tiledSceneRendererRegistry()
Returns the application's tiled scene renderer registry, used for managing tiled scene layer 2D rende...
static void setDefaultSvgPaths(const QStringList &pathList)
Alters default svg paths - used by 3rd party apps.
static QString libraryPath()
Returns the path containing qgis_core, qgis_gui, qgispython (and other) libraries.
static QStringList layoutTemplatePaths()
Returns the paths to layout template directories.
static const QgsSettingsEntryBool * settingsLocaleShowGroupSeparator
Settings entry locale show group separator.
static QString userFullName()
Returns the user's operating system login account full display name.
static Q_DECL_DEPRECATED QgsSettingsRegistryCore * settingsRegistryCore()
Returns the application's settings registry, used for managing application settings.
static QString serverResourcesPath()
Returns the path to the server resources directory.
static QString appIconPath()
Gets application icon.
static QString userLoginName()
Returns the user's operating system login account name.
Singleton offering an interface to manage the authentication configuration database and to utilize co...
bool init(const QString &pluginPath=QString(), const QString &authDatabasePath=QString())
init initialize QCA, prioritize qca-ossl plugin and optionally set up the authentication database
static QgsAuthManager * instance()
Enforce singleton pattern.
A registry for QgsAbstractBabelFormat GPSBabel formats.
Manages storage of a set of bookmarks.
void initialize(const QString &filePath)
Initializes the bookmark manager.
Registry of available callout classes.
This class manages all known classification methods.
Registry of color schemes.
void addDefaultSchemes()
Adds all default color schemes to this color scheme.
void initStyleScheme()
Initializes the default random style color scheme for the user.
A registry for saved data provider connections, allowing retrieval of saved connections by name and p...
A registry for known coordinate reference system (CRS) definitions, including any user-defined CRSes.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateTransform objects.
This class keeps a list of data item providers that may add items to the browser tree.
Handles logging of database queries.
static void applyLocaleChange()
Adjusts the date time display formats according to locale.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used.
Defines a QGIS exception class.
QString what() const
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Registry of external storage backends used by QgsExternalResourceWidget.
The QgsFieldFormatterRegistry manages registered classes of QgsFieldFormatter.
Manages available fonts and font installation for a QGIS instance.
void installUserFonts()
Installs user fonts from the profile/fonts directory as application fonts.
A class to register / unregister existing GPS connections such that the information is available to a...
A cache for images derived from raster files.
Registry of layer metadata provider backends.
Registry of available layout item types.
static const QgsSettingsEntryStringList * settingsSearchPathForTemplates
Settings entry search path for templates.
Definition qgslayout.h:664
A registry class to hold localized data paths which can be used for basemaps, logos,...
Temporarily blocks the application QgsMessageLog (see QgsApplication::messageLog()) from emitting the...
Interface for logging messages from QGIS in GUI independent way.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
Registry for temporary fetched files.
The QgsNumericFormatRegistry manages registered classes of QgsNumericFormat.
A registry for known page sizes.
Registry of available paint effects.
A registry of plugin layers types.
Registry of 2D renderers for point clouds.
Registry for various processing components, including providers, algorithms and various parameters an...
static QStringList searchPaths()
Returns the current list of Proj file search paths.
Registry of storage backends that QgsProject may use.
static QgsProject * instance()
Returns the QgsProject singleton instance.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Registry for raster renderers.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
void changeMode(Mode mode)
Change the mode of the lock to mode.
Handles and tracks style items recently used in the QGIS GUI.
Registry of renderers.
Provides a method of recording run time profiles of operations, allowing easy recording of their over...
void start(const QString &name, const QString &group="startup", const QString &id=QString())
Start a profile event with the given name.
void end(const QString &group="startup")
End the current profile event.
The QgsScaleBarRendererRegistry manages registered scalebar renderers.
Scoped object for logging of the runtime for a single operation or group of operations.
Registry of available sensor types.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
A boolean settings entry.
A string list settings entry.
A string settings entry.
QgsSettingsRegistryCore is used for settings introspection and collects all QgsSettingsEntry instance...
static QgsSettingsTreeNode * sTreeLocale
static QgsSettingsTreeNode * sTreeSvg
This class is a composition of two QSettings instances:
Definition qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool contains(const QString &key, QgsSettings::Section section=QgsSettings::NoSection) const
Returns true if there exists a setting called key; returns false otherwise.
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
QStringList childKeys() const
Returns a list of all top-level keys that can be read using the QSettings object.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A cache for source strings that returns a local file path containing the source content.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
static void cleanDefaultStyle()
Deletes the default style. Only to be used by QgsApplication::exitQgis()
Definition qgsstyle.cpp:176
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition qgsstyle.cpp:145
A cache for images / pictures derived from SVG files.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
Registry of available symbol layer classes.
static QColor decodeColor(const QString &str)
Task manager for managing a set of long-running QgsTask tasks.
Tile download manager handles downloads of map tiles for the purpose of map rendering.
Registry of 2D renderers for tiled scenes.
Used for the collecting of strings from projects for translation and creation of ts files.
User profile manager is used to manager list, and manage user profiles on the users machine.
QgsUserProfile * getProfile(const QString &defaultProfile="default", bool createNew=true, bool initSettings=true)
Returns the profile from the given root profile location.
static QString resolveProfilesFolder(const QString &basePath=QString())
Resolves the profiles folder for the given path.
User profile contains information about the user profile folders on the machine.
const QString folder() const
The base folder for the user profile.
This class keeps a list of QgsAbstractValidityCheck checks which can be used when performing validity...
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
int open(const QString &path)
Opens the database at the specified file path.
CORE_EXPORT const QStringList files(const QString &zip)
Returns the list of files within a zip file.
QMap< QString, QString > QgsStringMap
Definition qgis.h:4877
QObject * ABISYM(QgsApplication::mFileOpenEventReceiver)
#define CONN_POOL_MAX_CONCURRENT_CONNS
Q_GLOBAL_STATIC_WITH_ARGS(PalPropertyList, palHiddenProperties,({ QgsPalLayerSettings::PositionX, QgsPalLayerSettings::PositionY, QgsPalLayerSettings::Show, QgsPalLayerSettings::LabelRotation, QgsPalLayerSettings::Family, QgsPalLayerSettings::FontStyle, QgsPalLayerSettings::Size, QgsPalLayerSettings::Bold, QgsPalLayerSettings::Italic, QgsPalLayerSettings::Underline, QgsPalLayerSettings::Color, QgsPalLayerSettings::Strikeout, QgsPalLayerSettings::MultiLineAlignment, QgsPalLayerSettings::BufferSize, QgsPalLayerSettings::BufferDraw, QgsPalLayerSettings::BufferColor, QgsPalLayerSettings::LabelDistance, QgsPalLayerSettings::Hali, QgsPalLayerSettings::Vali, QgsPalLayerSettings::ScaleVisibility, QgsPalLayerSettings::MinScale, QgsPalLayerSettings::MaxScale, QgsPalLayerSettings::AlwaysShow, QgsPalLayerSettings::CalloutDraw, QgsPalLayerSettings::LabelAllParts })) Q_GLOBAL_STATIC_WITH_ARGS(SymbolPropertyList
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38