X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FQtx%2FQtxResourceMgr.cxx;h=68511cf35c7703a6cada46f0a6917b91bba74f4f;hb=2400f3659b65727d5a776dccd3d5dd506d755a88;hp=917037bb47a1b457793d47197e23939d23afa50e;hpb=c38bd41c3e1c73ad990ed3a64a53ad0ed89190d0;p=modules%2Fgui.git diff --git a/src/Qtx/QtxResourceMgr.cxx b/src/Qtx/QtxResourceMgr.cxx index 917037bb4..68511cf35 100644 --- a/src/Qtx/QtxResourceMgr.cxx +++ b/src/Qtx/QtxResourceMgr.cxx @@ -1,42 +1,177 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File: QtxResourceMgr.cxx +// Author: Alexander SOLOVYOV, Sergey TELKOV +// #include "QtxResourceMgr.h" - -#include -#include -#include -#include -#include -#include - +#include "QtxTranslator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifndef QT_NO_DOM -#include +#include +#include +#include #endif #include +/* XPM for the default pixmap */ +static const char* pixmap_not_found_xpm[] = { +"16 16 3 1", +" c None", +". c #000000", +"+ c #A80000", +" ", +" ", +" . . ", +" .+. .+. ", +" .+++. .+++. ", +" .+++.+++. ", +" .+++++. ", +" .+++. ", +" .+++++. ", +" .+++.+++. ", +" .+++. .+++. ", +" .+. .+. ", +" . . ", +" ", +" ", +" "}; + /*! - Class: QtxResourceMgr::Resources - Level: Internal + \class QtxResourceMgr::Resources + \internal + \brief Represents container for settings read from the resource file. */ -QtxResourceMgr::Resources::Resources( const QString& fileName ) -: myFileName( fileName ) +class QtxResourceMgr::Resources +{ +public: + Resources( QtxResourceMgr*, const QString& ); + virtual ~Resources(); + + QString file() const; + void setFile( const QString& ); + + QString value( const QString&, const QString&, const bool ) const; + void setValue( const QString&, const QString&, const QString& ); + + bool hasSection( const QString& ) const; + bool hasValue( const QString&, const QString& ) const; + + void removeSection( const QString& ); + void removeValue( const QString&, const QString& ); + + QPixmap loadPixmap( const QString&, const QString&, const QString& ) const; + QTranslator* loadTranslator( const QString&, const QString&, const QString& ) const; + + QString makeSubstitution( const QString&, const QString&, const QString& ) const; + + void clear(); + + QStringList sections() const; + QStringList parameters( const QString& ) const; + + QString path( const QString&, const QString&, const QString& ) const; + +protected: + QtxResourceMgr* resMgr() const; + +private: + Section section( const QString& ); + const Section section( const QString& ) const; + + QString fileName( const QString&, const QString&, const QString& ) const; + +private: + typedef QMap SectionMap; + +private: + QtxResourceMgr* myMgr; //!< resources manager + SectionMap mySections; //!< sections map + QString myFileName; //!< resources file name + QMap myPixmapCache; //!< pixmaps cache + + friend class QtxResourceMgr::Format; +}; + +/*! + \brief Constructor. + \param mgr parent resources manager + \param fileName resources file name +*/ +QtxResourceMgr::Resources::Resources( QtxResourceMgr* mgr, const QString& fileName ) +: myMgr( mgr ), + myFileName( fileName ) { } +/*! + \brief Destructor. +*/ QtxResourceMgr::Resources::~Resources() { } +/*! + \brief Get resources file name. + + This file is used to load/save operations. + + \return file name + \sa setFile() +*/ QString QtxResourceMgr::Resources::file() const { return myFileName; } +/*! + \brief Set resources file name. + \param fn file name + \sa file() +*/ void QtxResourceMgr::Resources::setFile( const QString& fn ) { myFileName = fn; } +/*! + \brief Get string representation of parameter value. + \param sect section name + \param name parameter name + \param subst if \c true, perform variables substitution + \return parameter value or null QString if there is no such parameter + \sa setValue(), makeSubstitution() +*/ QString QtxResourceMgr::Resources::value( const QString& sect, const QString& name, const bool subst ) const { QString val; @@ -50,49 +185,89 @@ QString QtxResourceMgr::Resources::value( const QString& sect, const QString& na return val; } +/*! + \brief Set parameter value. + \param sect section name + \param name parameter name + \param val parameter value + \sa value(), makeSubstitution() +*/ void QtxResourceMgr::Resources::setValue( const QString& sect, const QString& name, const QString& val ) { - Section& s = section( sect ); - s.insert( name, val ); + if ( !mySections.contains( sect ) ) + mySections.insert( sect, Section() ); + + mySections[sect].insert( name, val ); } +/*! + \brief Check section existence. + \param sect section name + \return \c true if section exists +*/ bool QtxResourceMgr::Resources::hasSection( const QString& sect ) const { return mySections.contains( sect ); } +/*! + \brief Check parameter existence. + \param sect section name + \param name parameter name + \return \c true if parameter exists in specified section +*/ bool QtxResourceMgr::Resources::hasValue( const QString& sect, const QString& name ) const { return hasSection( sect ) && section( sect ).contains( name ); } +/*! + \brief Remove resourcs section. + \param sect secton name +*/ void QtxResourceMgr::Resources::removeSection( const QString& sect ) { mySections.remove( sect ); } +/*! + \brief Remove parameter from the section. + \param sect section name + \param name parameter name +*/ void QtxResourceMgr::Resources::removeValue( const QString& sect, const QString& name ) { - if ( !hasSection( sect ) ) + if ( !mySections.contains( sect ) ) return; - Section& s = section( sect ); - s.remove( name ); + mySections[sect].remove( name ); - if ( s.isEmpty() ) + if ( mySections[sect].isEmpty() ) mySections.remove( sect ); } +/*! + \brief Remove all sections. +*/ void QtxResourceMgr::Resources::clear() { mySections.clear(); } +/*! + \brief Get all sections names. + \return list of section names +*/ QStringList QtxResourceMgr::Resources::sections() const { return mySections.keys(); } +/*! + \brief Get all parameters name in specified section. + \param sec section name + \return list of settings names +*/ QStringList QtxResourceMgr::Resources::parameters( const QString& sec ) const { if ( !hasSection( sec ) ) @@ -101,18 +276,50 @@ QStringList QtxResourceMgr::Resources::parameters( const QString& sec ) const return section( sec ).keys(); } +/*! + \brief Get absolute path to the file which name is defined by the parameter. + + The file name is defined by \a name argument, while directory name is retrieved + from resources parameter \a prefix of section \a sec. Both directory and file name + can be relative. If the directory is relative, it is calculated from the initial + resources file name (see file()). Directory parameter can contain environment + variables, which are substituted automatically. + + \param sec section name + \param prefix parameter containing directory name + \param name file name + \return absolute file path or null QString if file does not exist + \sa fileName(), file(), makeSubstitution() +*/ QString QtxResourceMgr::Resources::path( const QString& sec, const QString& prefix, const QString& name ) const { QString filePath = fileName( sec, prefix, name ); if ( !filePath.isEmpty() ) { if ( !QFileInfo( filePath ).exists() ) - filePath = QString::null; + filePath = QString(); } return filePath; } -QtxResourceMgr::Section& QtxResourceMgr::Resources::section( const QString& sn ) +/*! + \brief Get resource manager + \return resource manager pointer +*/ +QtxResourceMgr* QtxResourceMgr::Resources::resMgr() const +{ + return myMgr; +} + +/*! + \brief Get resources section by specified name. + + If section does not exist it is created (empty). + + \param sn section name + \return resources section +*/ +QtxResourceMgr::Section QtxResourceMgr::Resources::section( const QString& sn ) { if ( !mySections.contains( sn ) ) mySections.insert( sn, Section() ); @@ -120,37 +327,104 @@ QtxResourceMgr::Section& QtxResourceMgr::Resources::section( const QString& sn ) return mySections[sn]; } -const QtxResourceMgr::Section& QtxResourceMgr::Resources::section( const QString& sn ) const +/*! + \brief Get resources section by specified name. + \param sn section name + \return resources section +*/ +const QtxResourceMgr::Section QtxResourceMgr::Resources::section( const QString& sn ) const { return mySections[sn]; } +/*! + \brief Get file path. + + The file name is defined by \a name argument, while directory name is retrieved + from resources parameter \a prefix of section \a sec. Both directory and file name + can be relative. If the directory is relative, it is calculated from the initial + resources file name (see file()). Directory parameter can contain environment + variables, which are substituted automatically. + File existence is not checked. + + \param sec section name + \param prefix parameter containing directory name + \param name file name + \return absolute file path or null QString if \a prefix parameter + does not exist in section \sec + \sa path(), file(), makeSubstitution() +*/ QString QtxResourceMgr::Resources::fileName( const QString& sect, const QString& prefix, const QString& name ) const { QString path; - if ( hasValue( sect, prefix ) ) + if ( !QFileInfo( name ).isRelative() ) + { + path = name; + } + else { - path = value( sect, prefix, true ); - if ( !path.isEmpty() ) + if ( hasValue( sect, prefix ) ) { - if ( QFileInfo( path ).isRelative() ) - path = Qtx::addSlash( QFileInfo( myFileName ).dirPath( true ) ) + path; - - path = Qtx::addSlash( path ) + name; + path = value( sect, prefix, true ); + if ( !path.isEmpty() ) + { + if ( QFileInfo( path ).isRelative() ) + path = Qtx::addSlash( Qtx::dir( myFileName, true ) ) + path; + + path = Qtx::addSlash( path ) + name; + } } } - return QDir::convertSeparators( path ); + if( !path.isEmpty() ) + { + QString fname = QDir::convertSeparators( path ); + QFileInfo inf( fname ); + fname = inf.absoluteFilePath(); + return fname; + } + return QString(); } +/*! + \brief Load and return pixmap from external file. + + If QtxResourceMgr::isPixmapCached() is \c true then cached pixmap is returned + (if it is already loaded), otherwise it is loaded from file. + If the file name is invalid, null pixmap is returned. + + \param sect section name + \param prefix parameter containing resources directory name + \param name pixmap file name + \return pixmap loaded from file +*/ QPixmap QtxResourceMgr::Resources::loadPixmap( const QString& sect, const QString& prefix, const QString& name ) const { - return QPixmap( fileName( sect, prefix, name ) ); + QString fname = fileName( sect, prefix, name ); + bool toCache = resMgr() ? resMgr()->isPixmapCached() : false; + QPixmap p; + if( toCache && myPixmapCache.contains( fname ) ) + p = myPixmapCache[fname]; + else + { + p.load( fname ); + if( toCache ) + ( ( QMap& )myPixmapCache ).insert( fname, p ); + } + return p; } +/*! + \brief Load translator. + \param sect section name + \param prefix parameter containing resources directory + \param name translation file name + \return just created and loaded translator or 0 in case of error +*/ QTranslator* QtxResourceMgr::Resources::loadTranslator( const QString& sect, const QString& prefix, const QString& name ) const { - QTranslator* trans = new QTranslator( 0 ); - if ( !trans->load( fileName( sect, prefix, name ) ) ) + QTranslator* trans = new QtxTranslator( 0 ); + QString fname = QDir::convertSeparators( fileName( sect, prefix, name ) ); + if ( !trans->load( Qtx::file( fname, false ), Qtx::dir( fname ) ) ) { delete trans; trans = 0; @@ -158,25 +432,17 @@ QTranslator* QtxResourceMgr::Resources::loadTranslator( const QString& sect, con return trans; } -QString QtxResourceMgr::Resources::environmentVariable( const QString& str, int& start, int& len ) const -{ - QString varName = QString::null; - len = 0; - - QRegExp rx( "\\$\\{([a-zA-Z]+[a-zA-Z0-9_]*)\\}|\\$\\(([a-zA-Z]+[a-zA-Z0-9_]*)\\)|\\$([a-zA-Z]+[a-zA-Z0-9_]*)|\\%([a-zA-Z]+[a-zA-Z0-9_]*)\\%" ); +/*! + \brief Substitute variables by their values. - int pos = rx.search( str, start ); - if ( pos != -1 ) - { - start = pos; - len = rx.matchedLength(); - QStringList caps = rx.capturedTexts(); - for ( uint i = 1; i <= caps.count() && varName.isEmpty(); i++ ) - varName = *caps.at( i ); - } - return varName; -} + Environment variable is substituted by its value. For other variables resource + manager tries to find value among defined resources parameters. + \param str string to be processed + \param sect section, where variables are searched + \param name name of variable which must be ignored during substitution + \return processed string (with all substitutions made) +*/ QString QtxResourceMgr::Resources::makeSubstitution( const QString& str, const QString& sect, const QString& name ) const { QString res = str; @@ -187,13 +453,13 @@ QString QtxResourceMgr::Resources::makeSubstitution( const QString& str, const Q int start( 0 ), len( 0 ); while ( true ) { - QString envName = environmentVariable( res, start, len ); + QString envName = Qtx::findEnvVar( res, start, len ); if ( envName.isNull() ) break; - QString newStr = QString::null; - if ( ::getenv( envName ) ) - newStr = QString( ::getenv( envName ) ); + QString newStr; + if ( ::getenv( envName.toLatin1() ) ) + newStr = QString( ::getenv( envName.toLatin1() ) ); if ( newStr.isNull() ) { @@ -210,12 +476,16 @@ QString QtxResourceMgr::Resources::makeSubstitution( const QString& str, const Q res.replace( start, len, newStr ); } + res.replace( "$$", "$" ); + res.replace( "%%", "%" ); + return res; } /*! - Class: QtxResourceMgr::IniFormat - Level: Internal + \class QtxResourceMgr::IniFormat + \internal + \brief Reader/writer for .ini resources files. */ class QtxResourceMgr::IniFormat : public Format @@ -227,22 +497,69 @@ public: protected: virtual bool load( const QString&, QMap& ); virtual bool save( const QString&, const QMap& ); + +private: + bool load( const QString&, QMap&, QSet& ); }; +/*! + \brief Constructor. +*/ QtxResourceMgr::IniFormat::IniFormat() : Format( "ini" ) { } +/*! + \brief Destructor. +*/ QtxResourceMgr::IniFormat::~IniFormat() { } +/*! + \brief Load resources from ini-file. + \param fname resources file name + \param secMap resources map to be filled in + \return \c true on success and \c false on error +*/ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap& secMap ) { - QFile file( fname ); - if ( !file.open( IO_ReadOnly ) ) - return false; + QSet importHistory; + return load( fname, secMap, importHistory ); +} + + +/*! + \brief Load resources from xml-file. + \param fname resources file name + \param secMap resources map to be filled in + \param importHistory list of already imported resources files (to prevent import loops) + \return \c true on success or \c false on error +*/ +bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap& secMap, QSet& importHistory ) +{ + QString aFName = fname.trimmed(); + if ( !QFileInfo( aFName ).exists() ) + { + if ( QFileInfo( aFName + ".ini" ).exists() ) + aFName += ".ini"; + else if ( QFileInfo( aFName + ".INI" ).exists() ) + aFName += ".INI"; + else + return false; // file does not exist + } + QFileInfo aFinfo( aFName ); + aFName = aFinfo.canonicalFilePath(); + + if ( !importHistory.contains( aFName ) ) + importHistory.insert( aFName ); + else + return true; // already imported (prevent import loops) + + QFile file( aFName ); + if ( !file.open( QFile::ReadOnly ) ) + return false; // file is not accessible QTextStream ts( &file ); @@ -267,58 +584,111 @@ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap impMap; + if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) + { + qDebug() << "QtxResourceMgr: Error with importing file:" << data; + } + else + { + QMap::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 { res = false; - section.isEmpty() ? qWarning( "Current section is empty" ) : - qWarning( QString( "Error in line: %1" ).arg( line ) ); + if ( section.isEmpty() ) + qWarning() << "QtxResourceMgr: Current section is empty"; + else + qWarning() << "QtxResourceMgr: Error in line:" << line; } } file.close(); - return res; + return res; } +/*! + \brief Save resources to the ini-file. + \param fname resources file name + \param secMap resources map + \return \c true on success and \c false on error +*/ bool QtxResourceMgr::IniFormat::save( const QString& fname, const QMap& secMap ) { + if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) ) + return false; + QFile file( fname ); - if ( !file.open( IO_WriteOnly ) ) + if ( !file.open( QFile::WriteOnly ) ) return false; + QTextStream ts( &file ); + + ts << "# This file is automatically created by SALOME application." << endl; + ts << "# Changes made in this file can be lost!" << endl; + ts << endl; + bool res = true; for ( QMap::ConstIterator it = secMap.begin(); it != secMap.end() && res; ++it ) { - QString data = QString( "[%1]\n" ).arg( it.key() ); - for ( Section::ConstIterator iter = it.data().begin(); iter != it.data().end(); ++iter ) - data += iter.key() + " = " + iter.data() + "\n"; - data += "\n"; + QStringList data( QString( "[%1]" ).arg( it.key() ) ); + for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter ) + data.append( iter.key() + " = " + iter.value() ); + data.append( "" ); - res = file.writeBlock( data.latin1(), data.length() ) == (int)data.length(); + for ( QStringList::ConstIterator itr = data.begin(); itr != data.end(); ++itr ) + ts << *itr << endl; } file.close(); @@ -327,8 +697,9 @@ bool QtxResourceMgr::IniFormat::save( const QString& fname, const QMap&, QSet& ); }; +/*! + \brief Constructor. +*/ QtxResourceMgr::XmlFormat::XmlFormat() : Format( "xml" ) { } +/*! + \brief Destructor. +*/ QtxResourceMgr::XmlFormat::~XmlFormat() { } +/*! + \brief Load resources from xml-file. + \param fname resources file name + \param secMap resources map to be filled in + \return \c true on success and \c false on error +*/ bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap& secMap ) { + QSet importHistory; + return load( fname, secMap, importHistory ); +} + +/*! + \brief Load resources from xml-file. + \param fname resources file name + \param secMap resources map to be filled in + \param importHistory list of already imported resources files (to prevent import loops) + \return \c true on success and \c false on error +*/ +bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap& secMap, QSet& importHistory ) +{ + QString aFName = fname.trimmed(); + if ( !QFileInfo( aFName ).exists() ) + { + if ( QFileInfo( aFName + ".xml" ).exists() ) + aFName += ".xml"; + else if ( QFileInfo( aFName + ".XML" ).exists() ) + aFName += ".XML"; + else + return false; // file does not exist + } + QFileInfo aFinfo( aFName ); + aFName = aFinfo.canonicalFilePath(); + + if ( !importHistory.contains( aFName ) ) + importHistory.insert( aFName ); + else + return true; // already imported (prevent import loops) + bool res = false; #ifndef QT_NO_DOM - QFile file( fname ); - if ( !file.open( IO_ReadOnly ) ) + QFile file( aFName ); + if ( !file.open( QFile::ReadOnly ) ) + { + qDebug() << "QtxResourceMgr: File is not accessible:" << aFName; return false; + } QDomDocument doc; @@ -374,11 +794,17 @@ bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap impMap; + if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) + { + qDebug() << "QtxResourceMgr: Error with importing file:" << sectElem.attribute( nameAttribute() ); + } + else + { + QMap::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 - res = false; + { + qDebug() << "QtxResourceMgr: Invalid section in file:" << aFName; + res = false; + } } else + { res = sectNode.isComment(); // if it's a comment -- let it be, pass it.. + if ( !res ) + qDebug() << "QtxResourceMgr: Node is neither element nor comment in file:" << aFName; + } sectNode = sectNode.nextSibling(); } #endif - + + if ( res ) + qDebug() << "QtxResourceMgr: File" << fname << "is loaded successfully"; return res; } +/*! + \brief Save resources to the xml-file. + \param fname resources file name + \param secMap resources map + \return \c true on success and \c false on error +*/ bool QtxResourceMgr::XmlFormat::save( const QString& fname, const QMap& secMap ) { bool res = false; #ifndef QT_NO_DOM + if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) ) + return false; + QFile file( fname ); - if ( !file.open( IO_WriteOnly ) ) + if ( !file.open( QFile::WriteOnly ) ) return false; QDomDocument doc( docTag() ); + QDomComment comment = doc.createComment( "\nThis file is automatically created by SALOME application.\nChanges made in this file can be lost!\n" ); + doc.appendChild( comment ); QDomElement root = doc.createElement( docTag() ); doc.appendChild( root ); @@ -447,17 +934,20 @@ bool QtxResourceMgr::XmlFormat::save( const QString& fname, const QMap::ConstIterator iter = it.data().begin(); iter != it.data().end(); ++iter ) + for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter ) { QDomElement val = doc.createElement( parameterTag() ); val.setAttribute( nameAttribute(), iter.key() ); - val.setAttribute( valueAttribute(), iter.data() ); + val.setAttribute( valueAttribute(), iter.value() ); sect.appendChild( val ); } } - QString docStr = doc.toString(); - res = file.writeBlock( docStr.latin1(), docStr.length() ) == (int)docStr.length(); + QTextStream ts( &file ); + QStringList docStr = doc.toString().split( "\n" ); + for ( QStringList::ConstIterator itr = docStr.begin(); itr != docStr.end(); ++itr ) + ts << *itr << endl; + file.close(); #endif @@ -465,6 +955,10 @@ bool QtxResourceMgr::XmlFormat::save( const QString& fname, const QMapmySections = sections; else - qDebug( "QtxResourceMgr: Could not load resource file \"%s\"", res->myFileName.latin1() ); + 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 ) @@ -563,26 +1124,137 @@ bool QtxResourceMgr::Format::save( Resources* res ) Qtx::mkDir( Qtx::dir( res->myFileName ) ); - return save( res->myFileName, res->mySections ); + QtxResourceMgr* mgr = res->resMgr(); + QString name = mgr ? mgr->userFileName( mgr->appName(), false ) : res->myFileName; + return save( name, res->mySections ); } /*! - Class: QtxResourceMgr - Level: Public + \fn virtual bool QtxResourceMgr::Format::load( const QString& fname, + QMap& 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& 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 and .xml 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 ) +: 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 ); QString dirs; - if ( ::getenv( envVar ) ) - dirs = ::getenv( envVar ); - - setDirList( QStringList::split( ";", dirs ) ); + if ( ::getenv( envVar.toLatin1() ) ) + dirs = ::getenv( envVar.toLatin1() ); +#ifdef WIN32 + QString dirsep = ";"; // for Windows: ";" is used as directories separator +#else + QString dirsep = "[:|;]"; // for Linux: both ":" and ";" can be used +#endif + setDirList( dirs.split( QRegExp( dirsep ), QString::SkipEmptyParts ) ); installFormat( new XmlFormat() ); installFormat( new IniFormat() ); @@ -590,96 +1262,171 @@ QtxResourceMgr::QtxResourceMgr( const QString& appName, const QString& resVarTem setOption( "translators", QString( "%P_msg_%L.qm|%P_images.qm" ) ); } +/*! + \brief Destructor. + + Destroy the resource manager and free allocated memory. +*/ QtxResourceMgr::~QtxResourceMgr() { QStringList prefList = myTranslator.keys(); - for ( QStringList::const_iterator it = prefList.begin(); it != prefList.end(); ++it ) + for ( QStringList::ConstIterator it = prefList.begin(); it != prefList.end(); ++it ) removeTranslators( *it ); + + qDeleteAll( myResources ); + qDeleteAll( myFormats ); + + delete myDefaultPix; } +/*! + \brief Get the application name. + \return application name +*/ QString QtxResourceMgr::appName() const { return myAppName; } -QStringList QtxResourceMgr::dirList() const +/*! + \brief Get the "check existance" flag + + If this flag is \c true then preference can be set (with setValue() method) + only if it doesn't exist or if the value is changed. + + \return \c true if "check existance" flag is set +*/ +bool QtxResourceMgr::checkExisting() const { - return myDirList; + return myCheckExist; } -void QtxResourceMgr::setDirList( const QStringList& dl ) +/*! + \brief Set the "check existance" flag. + \param on new flag value +*/ +void QtxResourceMgr::setCheckExisting( const bool on ) { - myDirList = dl; - for ( ResListIterator it( myResources ); it.current(); ++it ) - delete it.current(); - - myResources.clear(); + myCheckExist = on; } -void QtxResourceMgr::initialize( const bool autoLoad ) const -{ - if ( !myResources.isEmpty() ) - return; +/*! + \brief Get the resource directories list. + + Home user directory (where the user application configuration file is situated) + is not included. This is that directories list defined by the application + resources environment variable. + + \return list of directories names +*/ +QStringList QtxResourceMgr::dirList() const +{ + return myDirList; +} + +/*! + \brief Initialise resources manager. + + Prepare the resources containers and load resources (if \a autoLoad is \c true). + + \param autoLoad if \c true (default) then all resources are loaded +*/ +void QtxResourceMgr::initialize( const bool autoLoad ) const +{ + if ( !myResources.isEmpty() ) + return; QtxResourceMgr* that = (QtxResourceMgr*)this; - that->myResources.append( new Resources( userFileName( appName() ) ) ); - for ( QStringList::const_iterator it = myDirList.begin(); it != myDirList.end(); ++it ) + if ( !userFileName( appName() ).isEmpty() ) + that->myResources.append( new Resources( that, userFileName( appName() ) ) ); + + that->myHasUserValues = myResources.count() > 0; + + for ( QStringList::ConstIterator it = myDirList.begin(); it != myDirList.end(); ++it ) { QString path = Qtx::addSlash( *it ) + globalFileName( appName() ); - that->myResources.append( new Resources( path ) ); + that->myResources.append( new Resources( that, path ) ); } if ( autoLoad ) that->load(); } -void QtxResourceMgr::clear() -{ - for ( ResListIterator it( myResources ); it.current(); ++it ) - it.current()->clear(); -} +/*! + \brief Get "cached pixmaps" option value. -QString QtxResourceMgr::currentSection() const -{ - return myCurSection; -} + Resources manager allows possibility to cache loaded pixmaps that allow to + improve application performance. This feature is turned on by default - all + loaded pixmaps are stored in the internal map. Switching of this feature on/off + can be done by setIsPixmapCached() method. -void QtxResourceMgr::setCurrentSection( const QString& str ) + \return \c true if pixmap cache is turned on + \sa setIsPixmapCached() +*/ +bool QtxResourceMgr::isPixmapCached() const { - myCurSection = str; + return myIsPixmapCached; } -bool QtxResourceMgr::value( const QString& name, int& val ) const +/*! + \brief Switch "cached pixmaps" option on/off. + \param on enable pixmap cache if \c true and disable it if \c false + \sa isPixmapCached() +*/ +void QtxResourceMgr::setIsPixmapCached( const bool on ) { - return value( currentSection(), name, val ); + myIsPixmapCached = on; } -bool QtxResourceMgr::value( const QString& name, double& val ) const +/*! + \brief Remove all resources from the resources manager. +*/ +void QtxResourceMgr::clear() { - return value( currentSection(), name, val ); + for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it ) + (*it)->clear(); } -bool QtxResourceMgr::value( const QString& name, bool& val ) const +/*! + \brief Get current working mode. + + \return current working mode + \sa setWorkingMode(), value(), hasValue(), hasSection(), setValue() +*/ +QtxResourceMgr::WorkingMode QtxResourceMgr::workingMode() const { - return value( currentSection(), name, val ); + return myWorkingMode; } -bool QtxResourceMgr::value( const QString& name, QColor& val ) const -{ - return value( currentSection(), name, val ); -} +/*! + \brief Set resource manager's working mode. -bool QtxResourceMgr::value( const QString& name, QFont& val ) const -{ - return value( currentSection(), name, val ); -} + The resource manager can operate in the following working modes: + * AllowUserValues : methods values(), hasValue(), hasSection() take into account user values (default) + * IgnoreUserValues : methods values(), hasValue(), hasSection() do not take into account user values -bool QtxResourceMgr::value( const QString& name, QString& val, const bool subst ) const + Note, that setValue() method always put the value to the user settings file. + + \param mode new working mode + \return previous working mode + \sa workingMode(), value(), hasValue(), hasSection(), setValue() +*/ +QtxResourceMgr::WorkingMode QtxResourceMgr::setWorkingMode( WorkingMode mode ) { - return value( currentSection(), name, val, subst ); + WorkingMode m = myWorkingMode; + myWorkingMode = mode; + return m; } +/*! + \brief Get interger parameter value. + \param sect section name + \param name parameter name + \param iVal parameter to return resulting integer value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a iVal value is undefined) +*/ bool QtxResourceMgr::value( const QString& sect, const QString& name, int& iVal ) const { QString val; @@ -692,6 +1439,14 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, int& iVal return ok; } +/*! + \brief Get double parameter value. + \param sect section name + \param name parameter name + \param dVal parameter to return resulting double value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a dVal value is undefined) +*/ bool QtxResourceMgr::value( const QString& sect, const QString& name, double& dVal ) const { QString val; @@ -704,6 +1459,14 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, double& dV return ok; } +/*! + \brief Get boolean parameter value. + \param sect section name + \param name parameter name + \param bVal parameter to return resulting boolean value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a bVal value is undefined) +*/ bool QtxResourceMgr::value( const QString& sect, const QString& name, bool& bVal ) const { QString val; @@ -717,7 +1480,7 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, bool& bVal boolMap["false"] = boolMap["no"] = boolMap["off"] = false; } - val = val.lower(); + val = val.toLower(); bool res = boolMap.contains( val ); if ( res ) bVal = boolMap[val]; @@ -731,111 +1494,215 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, bool& bVal return res; } +/*! + \brief Get color parameter value. + \param sect section name + \param name parameter name + \param cVal parameter to return resulting color value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a cVal value is undefined) +*/ bool QtxResourceMgr::value( const QString& sect, const QString& name, QColor& cVal ) const { QString val; if ( !value( sect, name, val, true ) ) return false; - bool res = true; - QStringList vals = QStringList::split( ",", val, true ); - - QIntList nums; - for ( QStringList::const_iterator it = vals.begin(); it != vals.end() && res; ++it ) - nums.append( (*it).toInt( &res ) ); - - if ( res && nums.count() >= 3 ) - cVal.setRgb( nums[0], nums[1], nums[2] ); - else - { - int pack = val.toInt( &res ); - if ( res ) - Qtx::rgbSet( pack, cVal ); - } - - return res; + return Qtx::stringToColor( val, cVal ); } +/*! + \brief Get font parameter value. + \param sect section name + \param name parameter name + \param fVal parameter to return resulting font value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a fVal value is undefined) +*/ bool QtxResourceMgr::value( const QString& sect, const QString& name, QFont& fVal ) const { - QString val = stringValue( sect, name, "" ).stripWhiteSpace(); - QStringList font_values = QStringList::split( val, "," ); - if( font_values.count()<2 || font_values.count()>4 ) + QString val; + if ( !value( sect, name, val, true ) ) return false; - - QString family = font_values[0]; - bool isBold = false, isItalic = false, isOk = false; - int pSize = -1; - for( int i=1, n=font_values.count(); i0 && !family.isEmpty() ) - { - fVal = QFont( family, pSize ); - fVal.setBold( isBold ); - fVal.setItalic( isItalic ); - return true; - } - else - return false; + return true; } -bool QtxResourceMgr::value( const QString& sect, const QString& name, QString& val, const bool subst ) const +/*! + \brief Get byte array parameter value. + \param sect section name + \param name parameter name + \param baVal parameter to return resulting byte array value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a baVal value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, QByteArray& baVal ) const { - initialize(); + QString val; + if ( !value( sect, name, val, true ) ) + return false; - bool ok = false; - for ( ResListIterator it( myResources ); it.current() && !ok; ++it ) + baVal.clear(); + QStringList lst = val.split( QRegExp( "[\\s|,]" ), QString::SkipEmptyParts ); + for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { - ok = it.current()->hasValue( sect, name ); - if ( ok ) - val = it.current()->value( sect, name, subst ); - } + int base = 10; + QString str = *it; + if ( str.startsWith( "#" ) ) + { + base = 16; + str = str.mid( 1 ); + } + bool ok = false; + int num = str.toInt( &ok, base ); + if ( !ok || num < 0 || num > 255 ) + continue; - return ok; + baVal.append( (char)num ); + } + return !baVal.isEmpty(); } -int QtxResourceMgr::integerValue( const QString& name, const int def ) const +/*! + \brief Get linear gradient parameter value. + \param sect section name + \param name parameter name + \param gVal parameter to return resulting linear gradient value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a gVal value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, QLinearGradient& gVal ) const { - return integerValue( currentSection(), name, def ); -} + QString val; + if ( !value( sect, name, val, true ) ) + return false; -double QtxResourceMgr::doubleValue( const QString& name, const double def ) const -{ - return doubleValue( currentSection(), name, def ); + return Qtx::stringToLinearGradient( val, gVal ); } -bool QtxResourceMgr::booleanValue( const QString& name, const bool def ) const +/*! + \brief Get radial gradient parameter value. + \param sect section name + \param name parameter name + \param gVal parameter to return resulting radial gradient value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a gVal value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, QRadialGradient& gVal ) const { - return booleanValue( currentSection(), name, def ); + QString val; + if ( !value( sect, name, val, true ) ) + return false; + + return Qtx::stringToRadialGradient( val, gVal ); } -QFont QtxResourceMgr::fontValue( const QString& name, const QFont& def ) const +/*! + \brief Get conical gradient parameter value. + \param sect section name + \param name parameter name + \param gVal parameter to return resulting conical gradient value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a gVal value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, QConicalGradient& gVal ) const { - return fontValue( currentSection(), name, def ); + QString val; + if ( !value( sect, name, val, true ) ) + return false; + + return Qtx::stringToConicalGradient( val, gVal ); } - -QColor QtxResourceMgr::colorValue( const QString& name, const QColor& def ) const + +/*! + \brief Get background parameter value. + \param sect section name + \param name parameter name + \param bgVal parameter to return resulting background value + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a bgVal value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, Qtx::BackgroundData& bgVal ) const { - return colorValue( currentSection(), name, def ); + QString val; + if ( !value( sect, name, val, true ) ) + return false; + + bgVal = Qtx::stringToBackground( val ); + return bgVal.isValid(); } -QString QtxResourceMgr::stringValue( const QString& name, const char* def ) const +/*! + \brief Get string parameter value (native format). + \param sect section name + \param name parameter name + \param val parameter to return resulting byte array value + \param subst if \c true perform environment variables substitution + \return \c true if parameter is found and \c false if parameter is not found + (in this case \a val value is undefined) +*/ +bool QtxResourceMgr::value( const QString& sect, const QString& name, QString& val, const bool subst ) const { - return stringValue( currentSection(), name, def ); + initialize(); + + bool ok = false; + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && !ok; ++it ) + { + ok = (*it)->hasValue( sect, name ); + if ( ok ) + val = (*it)->value( sect, name, subst ); + } + + return ok; } +/*! + \brief Get interger parameter value. + + If the specified parameter is not found or can not be converted to the integer value, + the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ int QtxResourceMgr::integerValue( const QString& sect, const QString& name, const int def ) const { int val; @@ -844,6 +1711,17 @@ int QtxResourceMgr::integerValue( const QString& sect, const QString& name, cons return val; } +/*! + \brief Get double parameter value. + + If the specified parameter is not found or can not be converted to the double value, + the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ double QtxResourceMgr::doubleValue( const QString& sect, const QString& name, const double def ) const { double val; @@ -852,6 +1730,17 @@ double QtxResourceMgr::doubleValue( const QString& sect, const QString& name, co return val; } +/*! + \brief Get boolean parameter value. + + If the specified parameter is not found or can not be converted to the boolean value, + the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ bool QtxResourceMgr::booleanValue( const QString& sect, const QString& name, const bool def ) const { bool val; @@ -860,6 +1749,17 @@ bool QtxResourceMgr::booleanValue( const QString& sect, const QString& name, con return val; } +/*! + \brief Get font parameter value. + + If the specified parameter is not found or can not be converted to the font value, + the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ QFont QtxResourceMgr::fontValue( const QString& sect, const QString& name, const QFont& def ) const { QFont font; @@ -868,6 +1768,17 @@ QFont QtxResourceMgr::fontValue( const QString& sect, const QString& name, const return font; } +/*! + \brief Get color parameter value. + + If the specified parameter is not found or can not be converted to the color value, + the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ QColor QtxResourceMgr::colorValue( const QString& sect, const QString& name, const QColor& def ) const { QColor val; @@ -876,7 +1787,17 @@ QColor QtxResourceMgr::colorValue( const QString& sect, const QString& name, con return val; } -QString QtxResourceMgr::stringValue( const QString& sect, const QString& name, const char* def ) const +/*! + \brief Get string parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +QString QtxResourceMgr::stringValue( const QString& sect, const QString& name, const QString& def ) const { QString val; if ( !value( sect, name, val ) ) @@ -884,182 +1805,444 @@ QString QtxResourceMgr::stringValue( const QString& sect, const QString& name, c return val; } -bool QtxResourceMgr::hasValue( const QString& name ) const +/*! + \brief Get byte array parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +QByteArray QtxResourceMgr::byteArrayValue( const QString& sect, const QString& name, const QByteArray& def ) const +{ + QByteArray val; + if ( !value( sect, name, val ) ) + val = def; + return val; +} + +/*! + \brief Get linear gradient parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +QLinearGradient QtxResourceMgr::linearGradientValue( const QString& sect, const QString& name, const QLinearGradient& def ) const +{ + QLinearGradient val; + if ( !value( sect, name, val ) ) + val = def; + return val; +} + +/*! + \brief Get radial gradient parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +QRadialGradient QtxResourceMgr::radialGradientValue( const QString& sect, const QString& name, const QRadialGradient& def ) const +{ + QRadialGradient val; + if ( !value( sect, name, val ) ) + val = def; + return val; +} + +/*! + \brief Get conical gradient parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +QConicalGradient QtxResourceMgr::conicalGradientValue( const QString& sect, const QString& name, const QConicalGradient& def ) const +{ + QConicalGradient val; + if ( !value( sect, name, val ) ) + val = def; + return val; +} + +/*! + \brief Get background parameter value. + + If the specified parameter is not found, the specified default value is returned instead. + + \param sect section name + \param name parameter name + \param def default value + \return parameter value (or default value if parameter is not found) +*/ +Qtx::BackgroundData QtxResourceMgr::backgroundValue( const QString& sect, const QString& name, const Qtx::BackgroundData& def ) const { - return hasValue( currentSection(), name ); + Qtx::BackgroundData val; + if ( !value( sect, name, val ) ) + val = def; + return val; } +/*! + \brief Check parameter existence. + \param sect section name + \param name parameter name + \return \c true if parameter exists in specified section +*/ bool QtxResourceMgr::hasValue( const QString& sect, const QString& name ) const { initialize(); bool ok = false; - for ( ResListIterator it( myResources ); it.current() && !ok; ++it ) - ok = it.current()->hasValue( sect, name ); + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && !ok; ++it ) + ok = (*it)->hasValue( sect, name ); return ok; } +/*! + \brief Check section existence. + \param sect section name + \return \c true if section exists +*/ bool QtxResourceMgr::hasSection( const QString& sect ) const { initialize(); bool ok = false; - for ( ResListIterator it( myResources ); it.current() && !ok; ++it ) - ok = it.current()->hasSection( sect ); + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && !ok; ++it ) + ok = (*it)->hasSection( sect ); return ok; } -void QtxResourceMgr::setValue( const QString& name, int val ) +/*! + \brief Set integer parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, int val ) { - setValue( currentSection(), name, val ); -} + int res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; -void QtxResourceMgr::setValue( const QString& name, double val ) -{ - setValue( currentSection(), name, val ); + setResource( sect, name, QString::number( val ) ); } -void QtxResourceMgr::setValue( const QString& name, bool val ) +/*! + \brief Set double parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, double val ) { - setValue( currentSection(), name, val ); -} + double res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; -void QtxResourceMgr::setValue( const QString& name, const QColor& val ) -{ - setValue( currentSection(), name, val ); + setResource( sect, name, QString::number( val, 'g', 12 ) ); } -void QtxResourceMgr::setValue( const QString& name, const QFont& val ) +/*! + \brief Set boolean parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, bool val ) { - setValue( currentSection(), name, val ); + bool res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, QString( val ? "true" : "false" ) ); } -void QtxResourceMgr::setValue( const QString& name, const QString& val ) +/*! + \brief Set color parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QColor& val ) { - setValue( currentSection(), name, val ); + QColor res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, Qtx::colorToString( val ) ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, int val ) +/*! + \brief Set font parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QFont& val ) { - setValue( sect, name, QString::number( val ) ); + QFont res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + QStringList fontDescr; + fontDescr.append( val.family() ); + if ( val.bold() ) + fontDescr.append( "Bold" ); + if ( val.italic() ) + fontDescr.append( "Italic" ); + if ( val.underline() ) + fontDescr.append( "Underline" ); + if ( val.overline() ) + fontDescr.append( "Overline" ); + fontDescr.append( QString( "%1" ).arg( val.pointSize() ) ); + + setResource( sect, name, fontDescr.join( "," ) ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, double val ) +/*! + \brief Set string parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QString& val ) { - setValue( sect, name, QString::number( val, 'g', 12 ) ); + QString res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, val ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, bool val ) +/*! + \brief Set byte array parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QByteArray& val ) { - setValue( sect, name, QString( val ? "true" : "false" ) ); + QByteArray res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + char buf[8]; + QStringList lst; + for ( int i = 0; i < val.size(); i++ ) + { + ::sprintf( buf, "#%02X", (unsigned char)val.at( i ) ); + lst.append( QString( buf ) ); + } + setResource( sect, name, lst.join( " " ) ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QColor& val ) +/*! + \brief Set linear gradient parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QLinearGradient& val ) { - setValue( sect, name, QString( "%1, %2, %3").arg( val.red() ).arg( val.green() ).arg( val.blue() ) ); + QLinearGradient res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, Qtx::gradientToString( val ) ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QFont& f ) +/*! + \brief Set radial gradient parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QRadialGradient& val ) { - QStringList val; - val.append( f.family() ); - if( f.bold() ) - val.append( "Bold" ); - if( f.italic() ) - val.append( "Italic" ); - val.append( QString( "%1" ).arg( f.pointSize() ) ); - - setValue( sect, name, val.join( "," ) ); + QRadialGradient res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, Qtx::gradientToString( val ) ); } -void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QString& val ) +/*! + \brief Set conical gradient parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QConicalGradient& val ) { - initialize(); + QConicalGradient res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; - if ( !myResources.isEmpty() ) - myResources.first()->setValue( sect, name, val ); + setResource( sect, name, Qtx::gradientToString( val ) ); } -void QtxResourceMgr::remove( const QString& name ) +/*! + \brief Set background parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setValue( const QString& sect, const QString& name, const Qtx::BackgroundData& val ) { - remove( currentSection(), name ); + Qtx::BackgroundData res; + if ( checkExisting() && value( sect, name, res ) && res == val ) + return; + + setResource( sect, name, Qtx::backgroundToString( val ) ); } -void QtxResourceMgr::remove( const QString& sect, const QString& name ) +/*! + \brief Remove resources section. + \param sect section name +*/ +void QtxResourceMgr::remove( const QString& sect ) { initialize(); - for ( ResListIterator it( myResources ); it.current(); ++it ) - it.current()->removeValue( sect, name ); + for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it ) + (*it)->removeSection( sect ); } -void QtxResourceMgr::removeSection( const QString& sect ) +/*! + \brief Remove the specified parameter. + \param sect section name + \param name parameter name +*/ +void QtxResourceMgr::remove( const QString& sect, const QString& name ) { initialize(); - for ( ResListIterator it( myResources ); it.current(); ++it ) - it.current()->removeSection( sect ); + for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it ) + (*it)->removeValue( sect, name ); } +/*! + \brief Get current configuration files format. + \return configuration files format name +*/ QString QtxResourceMgr::currentFormat() const { QString fmt; if ( !myFormats.isEmpty() ) - fmt = myFormats.getFirst()->format(); + fmt = myFormats[0]->format(); return fmt; } +/*! + \brief Set current configuration files format. + \param fmt configuration files format name +*/ void QtxResourceMgr::setCurrentFormat( const QString& fmt ) { Format* form = format( fmt ); if ( !form ) return; - myFormats.remove( form ); + myFormats.removeAll( form ); myFormats.prepend( form ); if ( myResources.isEmpty() ) return; - ResListIterator resIt( myResources ); - if ( resIt.current() ) - resIt.current()->setFile( userFileName( appName() ) ); - ++resIt; + ResList::Iterator resIt = myResources.begin(); + if ( myResources.count() > myDirList.count() && resIt != myResources.end() ) + { + (*resIt)->setFile( userFileName( appName() ) ); + ++resIt; + } - for ( QStringList::const_iterator it = myDirList.begin(); it != myDirList.end() && resIt.current(); ++it, ++resIt ) - resIt.current()->setFile( Qtx::addSlash( *it ) + globalFileName( appName() ) ); + for ( QStringList::ConstIterator it = myDirList.begin(); it != myDirList.end() && resIt != myResources.end(); ++it, ++resIt ) + (*resIt)->setFile( Qtx::addSlash( *it ) + globalFileName( appName() ) ); } +/*! + \brief Get configuration files format by specified format name. + \param fmt configuration files format name + \return format object or 0 if format is not defined +*/ QtxResourceMgr::Format* QtxResourceMgr::format( const QString& fmt ) const { Format* form = 0; - for ( FormatListIterator it( myFormats ); it.current() && !form; ++it ) + for ( FormatList::ConstIterator it = myFormats.begin(); it != myFormats.end() && !form; ++it ) { - if ( it.current()->format() == fmt ) - form = it.current(); + if ( (*it)->format() == fmt ) + form = *it; } return form; } +/*! + \brief Install configuration files format. + + Added format becomes current. + + \param form format object to be installed +*/ void QtxResourceMgr::installFormat( QtxResourceMgr::Format* form ) { if ( !myFormats.contains( form ) ) myFormats.prepend( form ); } +/*! + \brief Remove configuration files format. + \param form format object to be uninstalled +*/ void QtxResourceMgr::removeFormat( QtxResourceMgr::Format* form ) { - myFormats.remove( form ); + myFormats.removeAll( form ); } +/*! + \brief Get resource format options names. + \return list of options names +*/ QStringList QtxResourceMgr::options() const { return myOptions.keys(); } +/*! + \brief Get the string value of the specified resources format option. + + If option does not exist, null QString is returned. + + \param opt option name + \return option value + \sa setOption(), options() +*/ QString QtxResourceMgr::option( const QString& opt ) const { QString val; @@ -1068,11 +2251,22 @@ QString QtxResourceMgr::option( const QString& opt ) const return val; } +/*! + \brief Set the string value of the specified resources format option. + \param opt option name + \param val option value + \sa option(), options() +*/ void QtxResourceMgr::setOption( const QString& opt, const QString& val ) { myOptions.insert( opt, val ); } +/*! + \brief Load all resources from all resource files (global and user). + \return \c true on success and \c false on error + \sa save() +*/ bool QtxResourceMgr::load() { initialize( false ); @@ -1082,12 +2276,41 @@ bool QtxResourceMgr::load() return false; bool res = true; - for ( ResListIterator it( myResources ); it.current(); ++it ) - res = fmt->load( it.current() ) && res; + for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it ) + res = fmt->load( *it ) && res; + + return res; +} + +/*! + \brief Import resources from specified resource file. + \param fname resources file name + \return \c true on success and \c false on error +*/ +bool QtxResourceMgr::import( const QString& fname ) +{ + Format* fmt = format( currentFormat() ); + if ( !fmt ) + return false; + + if ( myResources.isEmpty() || !myHasUserValues ) + return false; + + Resources* r = myResources[0]; + if ( !r ) + return false; + QString old = r->file(); + r->setFile( fname ); + bool res = fmt->load( r ); + r->setFile( old ); return res; } +/*! + \brief Save all resources to the user resource files. + \return \c true on success and \c false on error +*/ bool QtxResourceMgr::save() { initialize( false ); @@ -1096,58 +2319,184 @@ bool QtxResourceMgr::save() if ( !fmt ) return false; - if ( myResources.isEmpty() ) + if ( myResources.isEmpty() || !myHasUserValues ) return true; - return fmt->save( myResources.getFirst() ); + bool result = fmt->save( myResources[0] ); + + saved(); + + return result; } +/*! + \brief Get all sections names. + \return list of section names +*/ QStringList QtxResourceMgr::sections() const { initialize(); QMap map; - for ( ResListIterator it( myResources ); it.current(); ++it ) + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end(); ++it ) { - QStringList lst = it.current()->sections(); - for ( QStringList::const_iterator itr = lst.begin(); itr != lst.end(); ++itr ) + QStringList lst = (*it)->sections(); + for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr ) map.insert( *itr, 0 ); } - QStringList res; - for ( QMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter ) - res.append( iter.key() ); + return map.keys(); +} - return res; +/*! + \brief Get all sections names matching specified regular expression. + \param re searched regular expression + \return list of sections names +*/ +QStringList QtxResourceMgr::sections(const QRegExp& re) const +{ + return sections().filter( re ); +} + +/*! + \brief Get all sections names with the prefix specified by passed + list of parent sections names. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param names parent sub-sections names + \return list of sections names +*/ +QStringList QtxResourceMgr::sections(const QStringList& names) const +{ + QStringList nm = names; + nm << ".+"; + QRegExp re( QString( "^%1$" ).arg( nm.join( sectionsToken() ) ) ); + return sections( re ); +} + +/*! + \brief Get list of sub-sections names for the specified parent section name. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param section parent sub-section name + \param full if \c true return full names of child sub-sections, if \c false, + return only top-level sub-sections names + \return list of sub-sections names +*/ +QStringList QtxResourceMgr::subSections(const QString& section, const bool full) const +{ + QStringList names = sections( QStringList() << section ); + QMutableListIterator it( names ); + while ( it.hasNext() ) { + QString name = it.next().mid( section.size() + 1 ).trimmed(); + if ( name.isEmpty() ) { + it.remove(); + continue; + } + if ( !full ) name = name.split( sectionsToken() ).first(); + it.setValue( name ); + } + names.removeDuplicates(); + names.sort(); + return names; } +/*! + \brief Get all parameters name in specified section. + \param sec section name + \return list of settings names +*/ QStringList QtxResourceMgr::parameters( const QString& sec ) const { initialize(); - QMap map; - for ( ResListIterator it( myResources ); it.current(); ++it ) +#if defined(QTX_NO_INDEXED_MAP) + typedef QMap PMap; +#else + typedef IMap PMap; +#endif + PMap pmap; + + Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0; + + QListIterator it( myResources ); + it.toBack(); + while ( it.hasPrevious() ) { - QStringList lst = it.current()->parameters( sec ); - for ( QStringList::const_iterator itr = lst.begin(); itr != lst.end(); ++itr ) - map.insert( *itr, 0 ); + Resources* r = it.previous(); + if ( r == ur ) break; + QStringList lst = r->parameters( sec ); + for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr ) +#if defined(QTX_NO_INDEXED_MAP) + if ( !pmap.contains( *itr ) ) pmap.insert( *itr, 0 ); +#else + pmap.insert( *itr, 0, false ); +#endif } - QStringList res; - for ( QMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter ) - res.append( iter.key() ); + return pmap.keys(); +} - return res; +/*! + \brief Get all parameters name in specified + list of sub-sections names. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param names parent sub-sections names + \return list of settings names +*/ +QStringList QtxResourceMgr::parameters( const QStringList& names ) const +{ + return parameters( names.join( sectionsToken() ) ); } +/*! + \brief Get absolute path to the file which name is defined by the parameter. + + The file name is defined by \a name argument, while directory name is retrieved + from resources parameter \a prefix of section \a sec. Both directory and file name + can be relative. If the directory is relative, it is calculated from the initial + resources file name. Directory parameter can contain environment + variables, which are substituted automatically. + + \param sec section name + \param prefix parameter containing directory name + \param name file name + \return absolute file path or null QString if file does not exist +*/ QString QtxResourceMgr::path( const QString& sect, const QString& prefix, const QString& name ) const { QString res; - for ( ResListIterator it( myResources ); it.current() && res.isEmpty(); ++it ) - res = it.current()->path( sect, prefix, name ); + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && res.isEmpty(); ++it ) + res = (*it)->path( sect, prefix, name ); return res; } +/*! + \brief Get application resources section name. + + By default, application resources section name is "resources" but + it can be changed by setting the "res_section_name" resources manager option. + + \return section corresponding to the resources directories + \sa option(), setOption() +*/ QString QtxResourceMgr::resSection() const { QString res = option( "res_section_name" ); @@ -1156,6 +2505,15 @@ QString QtxResourceMgr::resSection() const return res; } +/*! + \brief Get application language section name. + + By default, application language section name is "language" but + it can be changed by setting the "lang_section_name" resources manager option. + + \return section corresponding to the application language settings + \sa option(), setOption() +*/ QString QtxResourceMgr::langSection() const { QString res = option( "lang_section_name" ); @@ -1164,41 +2522,136 @@ QString QtxResourceMgr::langSection() const return res; } +/*! + \brief Get sections separator token. + + By default, sections separator token is colon symbol ":" but + it can be changed by setting the "section_token" resources manager option. + + \return string corresponding to the current section separator token + \sa option(), setOption() +*/ +QString QtxResourceMgr::sectionsToken() const +{ + QString res = option( "section_token" ); + if ( res.isEmpty() ) + res = QString( ":" ); + return res; +} + +/*! + \brief Get default pixmap. + + Default pixmap is used when requested pixmap resource is not found. + + \return default pixmap + \sa setDefaultPixmap(), loadPixmap() +*/ QPixmap QtxResourceMgr::defaultPixmap() const { - return myDefaultPix; + static QPixmap* defpx = 0; + if ( !defpx ) + defpx = new QPixmap( pixmap_not_found_xpm ); + + return myDefaultPix ? *myDefaultPix : *defpx; } +/*! + \brief Set default pixmap. + + Default pixmap is used when requested pixmap resource is not found. + + \param pix default pixmap + \sa defaultPixmap(), loadPixmap() +*/ void QtxResourceMgr::setDefaultPixmap( const QPixmap& pix ) { - myDefaultPix = pix; + delete myDefaultPix; + if ( pix.isNull() ) + myDefaultPix = 0; + else + myDefaultPix = new QPixmap( pix ); } +/*! + \brief Load pixmap resource. + \param prefix parameter which refers to the resources directory (directories) + \param name pixmap file name + \return pixmap loaded from the file + \sa defaultPixmap(), setDefaultPixmap() +*/ QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name ) const { return loadPixmap( prefix, name, true ); } +/*! + \brief Load pixmap resource. + \overload + \param prefix parameter which refers to the resources directory (directories) + \param name pixmap file name + \param useDef if \c false, default pixmap is not returned if resource is not found, + in this case null pixmap is returned instead + \return pixmap loaded from the file + \sa defaultPixmap(), setDefaultPixmap() +*/ QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name, const bool useDef ) const { return loadPixmap( prefix, name, useDef ? defaultPixmap() : QPixmap() ); } +/*! + \brief Load pixmap resource. + \overload + \param prefix parameter which refers to the resources directory (directories) + \param name pixmap file name + \param defPix default which should be used if the resource file doesn't exist + \return pixmap loaded from the file + \sa defaultPixmap(), setDefaultPixmap() +*/ QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name, const QPixmap& defPix ) const { initialize(); QPixmap pix; - for ( ResListIterator it( myResources ); it.current() && pix.isNull(); ++it ) - pix = it.current()->loadPixmap( resSection(), prefix, name ); + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && pix.isNull(); ++it ) + pix = (*it)->loadPixmap( resSection(), prefix, name ); if ( pix.isNull() ) pix = defPix; return pix; } +/*! + \brief Load translation files according to the specified language. + + Names of the translation files are calculated according to the pattern specified + by the "translators" option (this option is read from the section "language" of resources files). + By default, "%P_msg_%L.qm" pattern is used. + Keywords \%A, \%P, \%L in the pattern are substituted by the application name, prefix and language name + correspondingly. + For example, for prefix "SUIT" and language "en", all translation files "SUIT_msg_en.qm" are searched and + loaded. + + If prefix is empty or null string, all translation files specified in the "resources" section of resources + files are loaded (actually, the section is retrieved from resSection() method). + If language is not specified, it is retrieved from the langSection() method, and if the latest is also empty, + by default "en" (English) language is used. + By default, settings from the user preferences file are also loaded (if user resource file is valid, + see userFileName()). To avoid loading user settings, pass \c false as first parameter. + + \param pref parameter which defines translation context (for example, package name) + \param l language name + + \sa resSection(), langSection(), loadTranslators() +*/ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) { - initialize(); + initialize( true ); QMap substMap; substMap.insert( 'A', appName() ); @@ -1210,7 +2663,7 @@ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) if ( lang.isEmpty() ) { lang = QString( "en" ); - qWarning( QString( "Language not specified. Assumed: %1" ).arg( lang ) ); + qWarning() << "QtxResourceMgr: Language not specified. Assumed:" << lang; } substMap.insert( 'L', lang ); @@ -1218,19 +2671,21 @@ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) QString trs; if ( value( langSection(), "translators", trs, false ) && !trs.isEmpty() ) { - QStringList translators = QStringList::split( "|", option( "translators" ) ); - QStringList newTranslators = QStringList::split( "|", trs ); - for ( uint i = 0; i < newTranslators.count(); i++ ) - if ( translators.find( newTranslators[i] ) == translators.end() ) + QStringList translators = option( "translators" ).split( "|", QString::SkipEmptyParts ); + QStringList newTranslators = trs.split( "|", QString::SkipEmptyParts ); + for ( int i = 0; i < (int)newTranslators.count(); i++ ) + { + if ( translators.indexOf( newTranslators[i] ) < 0 ) translators += newTranslators[i]; + } setOption( "translators", translators.join( "|" ) ); } - QStringList trList = QStringList::split( "|", option( "translators" ) ); + QStringList trList = option( "translators" ).split( "|", QString::SkipEmptyParts ); if ( trList.isEmpty() ) { trList.append( "%P_msg_%L.qm" ); - qWarning( QString( "Translators not defined. Assumed: %1" ).arg( trList.first() ) ); + qWarning() << "QtxResourceMgr: Translators not defined. Assumed:" << trList[0]; } QStringList prefixList; @@ -1239,82 +2694,217 @@ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) else prefixList = parameters( resSection() ); - for ( QStringList::const_iterator iter = prefixList.begin(); iter != prefixList.end(); ++iter ) + if ( pref.isEmpty() && lang != "en" ) { + // load Qt resources + QString qt_translations = QLibraryInfo::location( QLibraryInfo::TranslationsPath ); + QString qt_dir_trpath = qgetenv( "QT_ROOT_DIR" ); + if ( qt_dir_trpath.isEmpty() ) + qt_dir_trpath = qgetenv( "QTDIR" ); + if ( !qt_dir_trpath.isEmpty() ) + qt_dir_trpath = QDir( qt_dir_trpath ).absoluteFilePath( "translations" ); + + QTranslator* trans = new QtxTranslator( 0 ); + if ( trans->load( QString("qt_%1").arg( lang ), qt_translations ) || trans->load( QString("qt_%1").arg( lang ), qt_dir_trpath ) ) { + if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans ); + } + } + + for ( QStringList::ConstIterator iter = prefixList.begin(); iter != prefixList.end(); ++iter ) { QString prefix = *iter; substMap.insert( 'P', prefix ); QStringList trs; - for ( QStringList::const_iterator it = trList.begin(); it != trList.end(); ++it ) - trs.append( substMacro( *it, substMap ).stripWhiteSpace() ); + for ( QStringList::ConstIterator it = trList.begin(); it != trList.end(); ++it ) + trs.append( substMacro( *it, substMap ).trimmed() ); - for ( QStringList::const_iterator itr = trs.begin(); itr != trs.end(); ++itr ) - loadTranslator( prefix, *itr ); + loadTranslators( prefix, trs ); } } +/*! + \brief Load translation files for the specified translation context. + \param prefix parameter which defines translation context (for example, package name) + \param translators list of translation files + \sa loadLanguage() +*/ +void QtxResourceMgr::loadTranslators( const QString& prefix, const QStringList& translators ) +{ + initialize(); + + ResList lst; + + ResList::ConstIterator iter = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++iter; + + for ( ; iter != myResources.end(); ++iter ) + lst.prepend( *iter ); + + QTranslator* trans = 0; + + for ( ResList::Iterator it = lst.begin(); it != lst.end(); ++it ) + { + for ( QStringList::ConstIterator itr = translators.begin(); itr != translators.end(); ++itr ) + { + trans = (*it)->loadTranslator( resSection(), prefix, *itr ); + if ( trans ) + { + if ( !myTranslator[prefix].contains( trans ) ) + myTranslator[prefix].append( trans ); + if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans ); + } + } + } +} + +/*! + \brief Load translation file. + \param prefix parameter which defines translation context (for example, package name) + \param name translator file name + \sa loadLanguage(), loadTranslators() +*/ void QtxResourceMgr::loadTranslator( const QString& prefix, const QString& name ) { initialize(); QTranslator* trans = 0; - for ( ResListIterator it( myResources ); it.current() && !trans; ++it ) - trans = it.current()->loadTranslator( resSection(), prefix, name ); - if ( !trans ) - return; + Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0; + + QListIterator it( myResources ); + it.toBack(); + while ( it.hasPrevious() ) + { + Resources* r = it.previous(); + if ( r == ur ) break; - if ( !myTranslator[prefix].contains( trans ) ) - myTranslator[prefix].append( trans ); - qApp->installTranslator( trans ); + trans = r->loadTranslator( resSection(), prefix, name ); + if ( trans ) + { + if ( !myTranslator[prefix].contains( trans ) ) + myTranslator[prefix].append( trans ); + if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans ); + } + } } +/*! + \brief Remove all translators corresponding to the specified translation context. + \param prefix parameter which defines translation context (for example, package name) +*/ void QtxResourceMgr::removeTranslators( const QString& prefix ) { if ( !myTranslator.contains( prefix ) ) return; - for ( TransListIterator it( myTranslator[prefix] ); it.current(); ++it ) + for ( TransList::Iterator it = myTranslator[prefix].begin(); it != myTranslator[prefix].end(); ++it ) { - qApp->removeTranslator( it.current() ); - delete it.current(); + if ( QApplication::instance() ) QApplication::instance()->removeTranslator( *it ); + delete *it; } myTranslator.remove( prefix ); } +/*! + \brief Move all translators corresponding to the specified translation context + to the top of translators stack (increase their priority). + \param prefix parameter which defines translation context (for example, package name) +*/ void QtxResourceMgr::raiseTranslators( const QString& prefix ) { if ( !myTranslator.contains( prefix ) ) return; - for ( TransListIterator it( myTranslator[prefix] ); it.current(); ++it ) + for ( TransList::Iterator it = myTranslator[prefix].begin(); it != myTranslator[prefix].end(); ++it ) { - qApp->removeTranslator( it.current() ); - qApp->installTranslator( it.current() ); + if ( QApplication::instance() ) { + QApplication::instance()->removeTranslator( *it ); + QApplication::instance()->installTranslator( *it ); + } } } +/*! + \brief Copy all parameters to the user resources in order to + saved them lately in the user home folder. +*/ void QtxResourceMgr::refresh() { QStringList sl = sections(); - for ( QStringList::const_iterator it = sl.begin(); it != sl.end(); ++it ) + for ( QStringList::ConstIterator it = sl.begin(); it != sl.end(); ++it ) { QStringList pl = parameters( *it ); - for ( QStringList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr ) - setValue( *it, *itr, stringValue( *it, *itr ) ); + for ( QStringList::ConstIterator itr = pl.begin(); itr != pl.end(); ++itr ) + setResource( *it, *itr, stringValue( *it, *itr ) ); } } -QString QtxResourceMgr::userFileName( const QString& appName ) const +/*! + \brief Set the resource directories (where global confguration files are searched). + + This function also clears all currently set resources. + + \param dl directories list +*/ +void QtxResourceMgr::setDirList( const QStringList& dl ) +{ + myDirList = dl; + for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it ) + delete *it; + + myResources.clear(); +} + +/*! + \brief Set parameter value. + \param sect section name + \param name parameter name + \param val parameter value +*/ +void QtxResourceMgr::setResource( const QString& sect, const QString& name, const QString& val ) +{ + initialize(); + + if ( !myResources.isEmpty() && myHasUserValues ) + myResources.first()->setValue( sect, name, val ); +} + +/*! + \brief Get user configuration file name. + + This method can be redefined in the successor class to customize the user configuration file name. + User configuration file is always situated in the user's home directory. By default .rc + file is used on Linux (e.g. .MyApprc) and . under Windows (e.g. MyApp.xml). + + Parameter \a for_load (not used in default implementation) specifies the usage mode, i.e. if + user configuration file is opened for reading or writing. This allows customizing a way of application + resources initializing (for example, if the user configuraion file includes version number and there is + no file corresponding to this version in the user's home directory, it could be good idea to try + the configuration file from the previous versions of the application). + + \param appName application name + \param for_load boolean flag indicating that file is opened for loading or saving (not used in default implementation) + \return user configuration file name + \sa globalFileName() +*/ +QString QtxResourceMgr::userFileName( const QString& appName, const bool /*for_load*/ ) const { QString fileName; - QString pathName = QDir::homeDirPath(); + QString pathName = QDir::homePath(); + QString cfgAppName = QApplication::applicationName(); + if ( !cfgAppName.isEmpty() ) + pathName = Qtx::addSlash( Qtx::addSlash( pathName ) + QString( ".config" ) ) + cfgAppName; #ifdef WIN32 fileName = QString( "%1.%2" ).arg( appName ).arg( currentFormat() ); #else - fileName = QString( ".%1rc" ).arg( appName ); + fileName = QString( "%1rc" ).arg( appName ); + // VSR 24/09/2012: issue 0021781: do not prepend filename with "." + // when user file is stored in ~/.config/ directory + if ( cfgAppName.isEmpty() ) + fileName.prepend( "." ); #endif if ( !fileName.isEmpty() ) @@ -1323,11 +2913,40 @@ QString QtxResourceMgr::userFileName( const QString& appName ) const return pathName; } +/*! + \brief Get global configuration file name. + + This method can be redefined in the successor class to customize the global configuration file name. + Global configuration files are searched in the directories specified by the application resources + environment variable (e.g. MyAppResources). By default . file name is used + (e.g. MyApp.xml). + + \param appName application name + \return global configuration file name + \sa userFileName() +*/ QString QtxResourceMgr::globalFileName( const QString& appName ) const { return QString( "%1.%2" ).arg( appName ).arg( currentFormat() ); } +/*! + \brief This function is called after user configuration file is saved. + Can be redefined in the successor classes, default implementation does nothing. +*/ +void QtxResourceMgr::saved() +{ +} + +/*! + \brief Perform substitution of the patterns like \%A, \%B, etc by values from the map. + + Used by loadLanguage(). + + \param src sring to be processed + \param substMap map of values for replacing + \return processed string +*/ QString QtxResourceMgr::substMacro( const QString& src, const QMap& substMap ) const { QString trg = src; @@ -1335,7 +2954,7 @@ QString QtxResourceMgr::substMacro( const QString& src, const QMap= 0 ) + while ( ( idx = rx.indexIn( trg, idx ) ) >= 0 ) { QChar spec = trg.at( idx + 1 ); QString subst;