+ QJsonObject root = document.object();
+ foreach ( QString sectionName, root.keys() )
+ {
+ if ( sectionName == "import" )
+ {
+ QString impFile = root.value( sectionName ).toString();
+ if ( impFile.isEmpty() )
+ continue;
+ QString impPath = QDir::toNativeSeparators( Qtx::makeEnvVarSubst( impFile, Qtx::Always ) );
+ QFileInfo impFInfo( impPath );
+ if ( impFInfo.isRelative() )
+ impFInfo.setFile( aFinfo.absoluteDir(), impPath );
+ QMap<QString, Section> impMap;
+ if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) )
+ {
+ qDebug() << "QtxResourceMgr: Error with importing file:" << impPath;
+ }
+ else
+ {
+ QMap<QString, Section>::const_iterator it = impMap.constBegin();
+ for ( ; it != impMap.constEnd() ; ++it )
+ {
+ if ( !secMap.contains( it.key() ) )
+ {
+ // insert full section
+ secMap.insert( it.key(), it.value() );
+ }
+ else
+ {
+ // insert all parameters from the section
+ Section::ConstIterator paramIt = it.value().begin();
+ for ( ; paramIt != it.value().end() ; ++paramIt )
+ {
+ if ( !secMap[it.key()].contains( paramIt.key() ) )
+ secMap[it.key()].insert( paramIt.key(), paramIt.value() );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ QJsonObject section = root.value( sectionName ).toObject();
+ if ( !section.isEmpty() )
+ {
+ // case when a top-level item is a section
+ foreach ( QString parameterName, section.keys() )
+ {
+ // each value must be a string, number, or boolean
+ QJsonValue parameter = section.value( parameterName );
+ if ( parameter.isDouble() )
+ secMap[sectionName].insert( parameterName, QString::number( parameter.toDouble() ) );
+ else if ( parameter.isBool() )
+ secMap[sectionName].insert( parameterName, QString( parameter.toBool() ? "true" : "false" ) );
+ else if ( parameter.isString() )
+ secMap[sectionName].insert( parameterName, parameter.toString() );
+ }
+ }
+ else
+ {
+ QString parameterName = sectionName;
+ sectionName = "General"; // default section name for top-level items
+ // each value must be a string, number, or boolean
+ QJsonValue parameter = root.value( parameterName );
+ if ( parameter.isDouble() )
+ secMap[sectionName].insert( parameterName, QString::number( parameter.toDouble() ) );
+ else if ( parameter.isBool() )
+ secMap[sectionName].insert( parameterName, QString( parameter.toBool() ? "true" : "false" ) );
+ else if ( parameter.isString() )
+ secMap[sectionName].insert( parameterName, parameter.toString() );
+ }
+ }
+ }
+
+ if ( !secMap.isEmpty() )
+ qDebug() << "QtxResourceMgr: File" << fname << "is loaded successfully";
+ return true;
+}
+
+/*!
+ \brief Save resources to the json-file.
+ \param fname resources file name
+ \param secMap resources map
+ \return \c true on success and \c false on error
+*/
+bool QtxResourceMgr::JsonFormat::save( const QString& fname, const QMap<QString, Section>& secMap )
+{
+ if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) )
+ return false;
+
+ QFile file( fname );
+ if ( !file.open( QFile::WriteOnly ) )
+ return false;
+
+ QJsonObject root;
+ for ( QMap<QString, Section>::ConstIterator it = secMap.begin(); it != secMap.end(); ++it )
+ {
+ // note: we write all values as string, as it's enough to store resources as strings
+ // anyway resource manager converts values to strings when reading JSON file
+ QJsonObject section;
+ for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter )
+ section.insert( iter.key(), iter.value() );
+ root.insert( it.key(), section );
+ }
+
+ QJsonDocument document;
+ document.setObject( root );
+ file.write( document.toJson() );
+ file.close();
+ return true;
+}
+
+/*!
+ \class QtxResourceMgr::SalomexFormat
+ \internal
+ \brief Reader/writer for .salomex resources files. This is an alias for JSON format.
+*/
+
+class QtxResourceMgr::SalomexFormat : public JsonFormat
+{
+public:
+ SalomexFormat() : JsonFormat( "salomex" ) {}
+};
+
+
+/*!
+ \class QtxResourceMgr::Format
+ \brief Generic resources files reader/writer class.
+*/
+
+/*!
+ \brief Constructor.
+ \param fmt format name (for example, "xml" or "ini")
+*/
+QtxResourceMgr::Format::Format( const QString& fmt )
+: myFmt( fmt )
+{
+}
+
+/*!
+ \brief Destructor
+*/
+QtxResourceMgr::Format::~Format()
+{
+}
+
+/*!
+ \brief Get the format name.
+ \return format name
+*/
+QString QtxResourceMgr::Format::format() const
+{
+ return myFmt;
+}
+
+/*!
+ \brief Get options names.
+ \return list of the format options
+*/
+QStringList QtxResourceMgr::Format::options() const
+{
+ return myOpt.keys();
+}
+
+/*!
+ \brief Get the value of the option with specified name.
+
+ If option doesn't exist then null QString is returned.
+
+ \param opt option name
+ \return option value
+*/
+QString QtxResourceMgr::Format::option( const QString& opt ) const
+{
+ QString val;
+ if ( myOpt.contains( opt ) )
+ val = myOpt[opt];
+ return val;
+}
+
+/*!
+ \brief Set the value of the option with specified name.
+ \param opt option name
+ \param val option value
+*/
+void QtxResourceMgr::Format::setOption( const QString& opt, const QString& val )
+{
+ myOpt.insert( opt, val );
+}
+
+/*!
+ \brief Load resources from the resource file.
+ \param res resources object
+ \return \c true on success and \c false on error
+*/
+bool QtxResourceMgr::Format::load( Resources* res )
+{
+ if ( !res )
+ return false;
+
+ QMap<QString, Section> sections;
+ bool status = load( res->myFileName, sections );
+ if ( status )
+ res->mySections = sections;
+ else
+ qDebug() << "QtxResourceMgr: Can't load resource file:" << res->myFileName;
+
+ return status;
+}
+
+/*!
+ \brief Save resources to the resource file.
+ \param res resources object
+ \return \c true on success and \c false on error
+*/
+bool QtxResourceMgr::Format::save( Resources* res )
+{
+ if ( !res )
+ return false;
+
+ QtxResourceMgr* mgr = res->resMgr();
+
+ if ( mgr->appName().isEmpty() )
+ return false;
+
+ Qtx::mkDir( Qtx::dir( res->myFileName ) );
+
+ QString name = mgr ? mgr->userFileName( mgr->appName(), false ) : res->myFileName;
+ return save( name, res->mySections );
+}
+
+/*!
+ \fn virtual bool QtxResourceMgr::Format::load( const QString& fname,
+ QMap<QString, Section>& secMap )
+ \brief Load resources from the specified resources file.
+
+ Should be implemented in the successors.
+
+ \param fname resources file name
+ \param secMap resources map to be filled in
+ \return \c true on success and \c false on error
+*/
+
+/*!
+ \fn virtual bool QtxResourceMgr::Format::save( const QString& fname,
+ const QMap<QString, Section>& secMap )
+
+ \brief Save resources to the specified resources file.
+
+ Should be implemented in the successors.
+
+ \param fname resources file name
+ \param secMap resources map
+ \return \c true on success and \c false on error
+*/
+
+/*!
+ \class QtxResourceMgr
+ \brief Application resources manager.
+
+ This class can be used to define settings, save/load settings and
+ application preferences to the resource file(s), load translation files
+ (internationalization mechanism), load pixmaps and other resources from
+ external files, etc.
+
+ Currently it supports .ini, .xml, and .json resources file formats. To implement
+ own resources file format, inherit from the Format class and implement virtual
+ Format::load() and Format::save() methods.
+
+ Resources manager is initialized by the (symbolic) name of the application.
+ The parameter \a resVarTemplate specifies the template for the environment
+ variable which should point to the resource directory or list of directories.
+ Environment variable name is calculated by substitution of "%1" substring in
+ the \a resVarTemplate parameter (if it contains such substring) by the
+ application name (\a appName).
+ By default, \a resVarTemplate is set to "%1Resources". For example, if the application name
+ is "MyApp", the environment variable "MyAppResources" will be inspected in this case.
+
+ Resource manager can handle several global application configuration files and
+ one user configuration file. Location of global configuration files is defined
+ by the environment variable (see above) and these files are always read-only.
+ The name of the global configuration files is retrieved by calling virtual method
+ globalFileName() which can be redefined in the QtxResourceMgr class successors.
+ User configuration file always situated in the user's home directory. It's name
+ is defined by calling virtual method userFileName() which can be also redefined
+ in the QtxResourceMgr class successors. This is the only file which the preferences
+ changed by the user during the application session are written to (usually
+ when the application closes).
+
+ Resources environment variable should contain one or several resource directories
+ (separated by ";" symbol on Windows and ":" or ";" on Linux). Each resource directory
+ can contain application global configuration file. The user configuration file has
+ the highest priority, for the global configuration files the priority is decreasing from
+ left to right, i.e. the first directory in the directoris list, defined by the
+ resources environment variable has higher priority. Priority has the meaning when
+ searching requested resources (application preference, pixmap file name, translation
+ file, etc).
+
+ When retrieving preferences, it is sometimes helpful to ignore values coming from the
+ user preference file and take into account only global preferences.
+ To do this, use setWorkingMode() method passing QtxResourceMgr::IgnoreUserValues enumerator
+ as parameter.
+
+ Resources manager operates with such terms like options, sections and parameters.
+ Parametets are named application resources, for example, application preferences like
+ integer, double, boolean or string values, pictures, font and color definitions, etc.
+ Parameters are organized inside the resources files into the named groups - sections.
+ Options are special kind of resoures which allow customizing resource files interpreting.
+ For example, by default language settings are defined in the resource file in the
+ section "language". It is possible to change this section name by setting "language"
+ option to another value (see setOption()).
+
+ Retrieving preferences values can be done by using one of value() methods, each returns
+ \c true if the corresponding preference is found. Another way is to use integerValue(),
+ doubleValue(), etc methods, which allow specifying default value which is used if the
+ specified preference is not found. Removing of preferences or sections can be done using
+ remove(const QString& sect) or remove(const QString& sect, const QString& name) methods.
+ To add the preference or to change exiting preference value use setValue() methods family.
+ Methods hasSection() and hasValue() can be used to check existence of section or
+ preference (in the specified section). List of all sections can be retrieved with the
+ sections() method, and list of all settings names in some specified section can be
+ obtained with parameters() method.
+
+ Pixmaps can be loaded with the loadPixmap() methods. If the specified pixmap is not found,
+ the default one is returned. Default pixmap can be set by setDefaultPixmap().
+
+ One of the key feature of the resources manager is support of application
+ internationalization mechanism. Translation files for the specified language can be loaded
+ with loadLanguage() method.
+*/
+
+/*!
+ \brief Constructs the resource manager.
+ \param appName application name
+ \param resVarTemplate resource environment variable pattern
+*/
+QtxResourceMgr::QtxResourceMgr( const QString& appName, const QString& resVarTemplate )
+: myAppName( appName ),
+ myCheckExist( true ),
+ myDefaultPix( 0 ),
+ myIsPixmapCached( true ),
+ myHasUserValues( true ),
+ myWorkingMode( AllowUserValues )
+{
+ QString envVar = !resVarTemplate.isEmpty() ? resVarTemplate : QString( "%1Resources" );
+ if ( envVar.contains( "%1" ) )
+ envVar = envVar.arg( appName );