1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File: QtxResourceMgr.cxx
24 // Author: Alexander SOLOVYOV, Sergey TELKOV
26 #include "QtxResourceMgr.h"
27 #include "QtxTranslator.h"
34 #include <QTextStream>
35 #include <QApplication>
36 #include <QLibraryInfo>
39 #include <QDomDocument>
40 #include <QDomElement>
46 /* XPM for the default pixmap */
47 static const char* pixmap_not_found_xpm[] = {
70 \class QtxResourceMgr::Resources
72 \brief Represents container for settings read from the resource file.
75 class QtxResourceMgr::Resources
78 Resources( QtxResourceMgr*, const QString& );
82 void setFile( const QString& );
84 QString value( const QString&, const QString&, const bool ) const;
85 void setValue( const QString&, const QString&, const QString& );
87 bool hasSection( const QString& ) const;
88 bool hasValue( const QString&, const QString& ) const;
90 void removeSection( const QString& );
91 void removeValue( const QString&, const QString& );
93 QPixmap loadPixmap( const QString&, const QString&, const QString& ) const;
94 QTranslator* loadTranslator( const QString&, const QString&, const QString& ) const;
96 QString makeSubstitution( const QString&, const QString&, const QString& ) const;
100 QStringList sections() const;
101 QStringList parameters( const QString& ) const;
103 QString path( const QString&, const QString&, const QString& ) const;
106 QtxResourceMgr* resMgr() const;
109 Section section( const QString& );
110 const Section section( const QString& ) const;
112 QString fileName( const QString&, const QString&, const QString& ) const;
115 typedef QMap<QString, Section> SectionMap;
118 QtxResourceMgr* myMgr; //!< resources manager
119 SectionMap mySections; //!< sections map
120 QString myFileName; //!< resources file name
121 QMap<QString,QPixmap> myPixmapCache; //!< pixmaps cache
123 friend class QtxResourceMgr::Format;
128 \param mgr parent resources manager
129 \param fileName resources file name
131 QtxResourceMgr::Resources::Resources( QtxResourceMgr* mgr, const QString& fileName )
133 myFileName( fileName )
140 QtxResourceMgr::Resources::~Resources()
145 \brief Get resources file name.
147 This file is used to load/save operations.
152 QString QtxResourceMgr::Resources::file() const
158 \brief Set resources file name.
162 void QtxResourceMgr::Resources::setFile( const QString& fn )
168 \brief Get string representation of parameter value.
169 \param sect section name
170 \param name parameter name
171 \param subst if \c true, perform variables substitution
172 \return parameter value or null QString if there is no such parameter
173 \sa setValue(), makeSubstitution()
175 QString QtxResourceMgr::Resources::value( const QString& sect, const QString& name, const bool subst ) const
179 if ( hasValue( sect, name ) )
181 val = section( sect )[name];
183 val = makeSubstitution( val, sect, name );
189 \brief Set parameter value.
190 \param sect section name
191 \param name parameter name
192 \param val parameter value
193 \sa value(), makeSubstitution()
195 void QtxResourceMgr::Resources::setValue( const QString& sect, const QString& name, const QString& val )
197 if ( !mySections.contains( sect ) )
198 mySections.insert( sect, Section() );
200 mySections[sect].insert( name, val );
204 \brief Check section existence.
205 \param sect section name
206 \return \c true if section exists
208 bool QtxResourceMgr::Resources::hasSection( const QString& sect ) const
210 return mySections.contains( sect );
214 \brief Check parameter existence.
215 \param sect section name
216 \param name parameter name
217 \return \c true if parameter exists in specified section
219 bool QtxResourceMgr::Resources::hasValue( const QString& sect, const QString& name ) const
221 return hasSection( sect ) && section( sect ).contains( name );
225 \brief Remove resourcs section.
226 \param sect secton name
228 void QtxResourceMgr::Resources::removeSection( const QString& sect )
230 mySections.remove( sect );
234 \brief Remove parameter from the section.
235 \param sect section name
236 \param name parameter name
238 void QtxResourceMgr::Resources::removeValue( const QString& sect, const QString& name )
240 if ( !mySections.contains( sect ) )
243 mySections[sect].remove( name );
245 if ( mySections[sect].isEmpty() )
246 mySections.remove( sect );
250 \brief Remove all sections.
252 void QtxResourceMgr::Resources::clear()
258 \brief Get all sections names.
259 \return list of section names
261 QStringList QtxResourceMgr::Resources::sections() const
263 return mySections.keys();
267 \brief Get all parameters name in specified section.
268 \param sec section name
269 \return list of settings names
271 QStringList QtxResourceMgr::Resources::parameters( const QString& sec ) const
273 if ( !hasSection( sec ) )
274 return QStringList();
276 return section( sec ).keys();
280 \brief Get absolute path to the file which name is defined by the parameter.
282 The file name is defined by \a name argument, while directory name is retrieved
283 from resources parameter \a prefix of section \a sec. Both directory and file name
284 can be relative. If the directory is relative, it is calculated from the initial
285 resources file name (see file()). Directory parameter can contain environment
286 variables, which are substituted automatically.
288 \param sec section name
289 \param prefix parameter containing directory name
290 \param name file name
291 \return absolute file path or null QString if file does not exist
292 \sa fileName(), file(), makeSubstitution()
294 QString QtxResourceMgr::Resources::path( const QString& sec, const QString& prefix, const QString& name ) const
296 QString filePath = fileName( sec, prefix, name );
297 if ( !filePath.isEmpty() )
299 if ( !QFileInfo( filePath ).exists() )
300 filePath = QString();
306 \brief Get resource manager
307 \return resource manager pointer
309 QtxResourceMgr* QtxResourceMgr::Resources::resMgr() const
315 \brief Get resources section by specified name.
317 If section does not exist it is created (empty).
319 \param sn section name
320 \return resources section
322 QtxResourceMgr::Section QtxResourceMgr::Resources::section( const QString& sn )
324 if ( !mySections.contains( sn ) )
325 mySections.insert( sn, Section() );
327 return mySections[sn];
331 \brief Get resources section by specified name.
332 \param sn section name
333 \return resources section
335 const QtxResourceMgr::Section QtxResourceMgr::Resources::section( const QString& sn ) const
337 return mySections[sn];
341 \brief Get file path.
343 The file name is defined by \a name argument, while directory name is retrieved
344 from resources parameter \a prefix of section \a sec. Both directory and file name
345 can be relative. If the directory is relative, it is calculated from the initial
346 resources file name (see file()). Directory parameter can contain environment
347 variables, which are substituted automatically.
348 File existence is not checked.
350 \param sec section name
351 \param prefix parameter containing directory name
352 \param name file name
353 \return absolute file path or null QString if \a prefix parameter
354 does not exist in section \sec
355 \sa path(), file(), makeSubstitution()
357 QString QtxResourceMgr::Resources::fileName( const QString& sect, const QString& prefix, const QString& name ) const
360 if ( !QFileInfo( name ).isRelative() )
366 if ( hasValue( sect, prefix ) )
368 path = value( sect, prefix, true );
369 if ( !path.isEmpty() )
371 if ( QFileInfo( path ).isRelative() )
372 path = Qtx::addSlash( Qtx::dir( myFileName, true ) ) + path;
374 path = Qtx::addSlash( path ) + name;
378 if( !path.isEmpty() )
380 QString fname = QDir::toNativeSeparators( path );
381 QFileInfo inf( fname );
382 fname = inf.absoluteFilePath();
389 \brief Load and return pixmap from external file.
391 If QtxResourceMgr::isPixmapCached() is \c true then cached pixmap is returned
392 (if it is already loaded), otherwise it is loaded from file.
393 If the file name is invalid, null pixmap is returned.
395 \param sect section name
396 \param prefix parameter containing resources directory name
397 \param name pixmap file name
398 \return pixmap loaded from file
400 QPixmap QtxResourceMgr::Resources::loadPixmap( const QString& sect, const QString& prefix, const QString& name ) const
402 QString fname = fileName( sect, prefix, name );
403 bool toCache = resMgr() ? resMgr()->isPixmapCached() : false;
405 if( toCache && myPixmapCache.contains( fname ) )
406 p = myPixmapCache[fname];
411 ( ( QMap<QString,QPixmap>& )myPixmapCache ).insert( fname, p );
417 \brief Load translator.
418 \param sect section name
419 \param prefix parameter containing resources directory
420 \param name translation file name
421 \return just created and loaded translator or 0 in case of error
423 QTranslator* QtxResourceMgr::Resources::loadTranslator( const QString& sect, const QString& prefix, const QString& name ) const
425 QTranslator* trans = new QtxTranslator( 0 );
426 QString fname = QDir::toNativeSeparators( fileName( sect, prefix, name ) );
427 if ( !trans->load( Qtx::file( fname, false ), Qtx::dir( fname ) ) )
436 \brief Substitute variables by their values.
438 Environment variable is substituted by its value. For other variables resource
439 manager tries to find value among defined resources parameters.
441 \param str string to be processed
442 \param sect section, where variables are searched
443 \param name name of variable which must be ignored during substitution
444 \return processed string (with all substitutions made)
446 QString QtxResourceMgr::Resources::makeSubstitution( const QString& str, const QString& sect, const QString& name ) const
450 QMap<QString, int> ignoreMap;
451 ignoreMap.insert( name, 0 );
453 int start( 0 ), len( 0 );
456 QString envName = Qtx::findEnvVar( res, start, len );
457 if ( envName.isNull() )
461 if ( ::getenv( envName.toLatin1() ) )
462 newStr = QString( ::getenv( envName.toLatin1() ) );
464 if ( newStr.isNull() )
466 if ( ignoreMap.contains( envName ) )
472 if ( hasValue( sect, envName ) )
473 newStr = value( sect, envName, false );
474 ignoreMap.insert( envName, 0 );
476 res.replace( start, len, newStr );
479 res.replace( "$$", "$" );
480 res.replace( "%%", "%" );
486 \class QtxResourceMgr::IniFormat
488 \brief Reader/writer for .ini resources files.
491 class QtxResourceMgr::IniFormat : public Format
498 virtual bool load( const QString&, QMap<QString, Section>& );
499 virtual bool save( const QString&, const QMap<QString, Section>& );
502 bool load( const QString&, QMap<QString, Section>&, QSet<QString>& );
508 QtxResourceMgr::IniFormat::IniFormat()
516 QtxResourceMgr::IniFormat::~IniFormat()
521 \brief Load resources from ini-file.
522 \param fname resources file name
523 \param secMap resources map to be filled in
524 \return \c true on success and \c false on error
526 bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap<QString, Section>& secMap )
528 QSet<QString> importHistory;
529 return load( fname, secMap, importHistory );
534 \brief Load resources from xml-file.
535 \param fname resources file name
536 \param secMap resources map to be filled in
537 \param importHistory list of already imported resources files (to prevent import loops)
538 \return \c true on success or \c false on error
540 bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap<QString, Section>& secMap, QSet<QString>& importHistory )
542 QString aFName = fname.trimmed();
543 if ( !QFileInfo( aFName ).exists() )
545 if ( QFileInfo( aFName + ".ini" ).exists() )
547 else if ( QFileInfo( aFName + ".INI" ).exists() )
550 return false; // file does not exist
552 QFileInfo aFinfo( aFName );
553 aFName = aFinfo.canonicalFilePath();
555 if ( !importHistory.contains( aFName ) )
556 importHistory.insert( aFName );
558 return true; // already imported (prevent import loops)
560 QFile file( aFName );
561 if ( !file.open( QFile::ReadOnly ) )
562 return false; // file is not accessible
564 QTextStream ts( &file );
571 QString separator = option( "separator" );
572 if ( separator.isNull() )
573 separator = QString( "=" );
575 QString comment = option( "comment" );
576 if ( comment.isNull() )
577 comment = QString( "#" );
581 data = ts.readLine();
587 data = data.trimmed();
588 if ( data.isEmpty() )
591 if ( data.startsWith( comment ) )
594 QRegExp rx( "^\\[([\\w\\s\\._]*)\\]$" );
595 if ( rx.indexIn( data ) != -1 )
597 section = rx.cap( 1 );
598 if ( section.isEmpty() )
601 qWarning() << "QtxResourceMgr: Empty section in line:" << line;
604 else if ( data.contains( separator ) && !section.isEmpty() )
606 int pos = data.indexOf( separator );
607 QString key = data.left( pos ).trimmed();
608 QString val = data.mid( pos + 1 ).trimmed();
609 secMap[section].insert( key, val );
611 else if ( section == "import" )
613 QString impFile = QDir::toNativeSeparators( Qtx::makeEnvVarSubst( data, Qtx::Always ) );
614 QFileInfo impFInfo( impFile );
615 if ( impFInfo.isRelative() )
616 impFInfo.setFile( aFinfo.absoluteDir(), impFile );
618 QMap<QString, Section> impMap;
619 if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) )
621 qDebug() << "QtxResourceMgr: Error with importing file:" << data;
625 QMap<QString, Section>::const_iterator it = impMap.constBegin();
626 for ( ; it != impMap.constEnd() ; ++it )
628 if ( !secMap.contains( it.key() ) )
630 // insert full section
631 secMap.insert( it.key(), it.value() );
635 // insert all parameters from the section
636 Section::ConstIterator paramIt = it.value().begin();
637 for ( ; paramIt != it.value().end() ; ++paramIt )
639 if ( !secMap[it.key()].contains( paramIt.key() ) )
640 secMap[it.key()].insert( paramIt.key(), paramIt.value() );
649 if ( section.isEmpty() )
650 qWarning() << "QtxResourceMgr: Current section is empty";
652 qWarning() << "QtxResourceMgr: Error in line:" << line;
662 \brief Save resources to the ini-file.
663 \param fname resources file name
664 \param secMap resources map
665 \return \c true on success and \c false on error
667 bool QtxResourceMgr::IniFormat::save( const QString& fname, const QMap<QString, Section>& secMap )
669 if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) )
673 if ( !file.open( QFile::WriteOnly ) )
676 QTextStream ts( &file );
678 ts << "# This file is automatically created by SALOME application." << endl;
679 ts << "# Changes made in this file can be lost!" << endl;
683 for ( QMap<QString, Section>::ConstIterator it = secMap.begin(); it != secMap.end() && res; ++it )
685 QStringList data( QString( "[%1]" ).arg( it.key() ) );
686 for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter )
687 data.append( iter.key() + " = " + iter.value() );
690 for ( QStringList::ConstIterator itr = data.begin(); itr != data.end(); ++itr )
700 \class QtxResourceMgr::XmlFormat
702 \brief Reader/writer for .xml resources files.
705 class QtxResourceMgr::XmlFormat : public Format
712 virtual bool load( const QString&, QMap<QString, Section>& );
713 virtual bool save( const QString&, const QMap<QString, Section>& );
716 QString docTag() const;
717 QString sectionTag() const;
718 QString parameterTag() const;
719 QString importTag() const;
720 QString nameAttribute() const;
721 QString valueAttribute() const;
723 bool load( const QString&, QMap<QString, Section>&, QSet<QString>& );
729 QtxResourceMgr::XmlFormat::XmlFormat()
737 QtxResourceMgr::XmlFormat::~XmlFormat()
742 \brief Load resources from xml-file.
743 \param fname resources file name
744 \param secMap resources map to be filled in
745 \return \c true on success and \c false on error
747 bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap<QString, Section>& secMap )
749 QSet<QString> importHistory;
750 return load( fname, secMap, importHistory );
754 \brief Load resources from xml-file.
755 \param fname resources file name
756 \param secMap resources map to be filled in
757 \param importHistory list of already imported resources files (to prevent import loops)
758 \return \c true on success and \c false on error
760 bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap<QString, Section>& secMap, QSet<QString>& importHistory )
762 QString aFName = fname.trimmed();
763 if ( !QFileInfo( aFName ).exists() )
765 if ( QFileInfo( aFName + ".xml" ).exists() )
767 else if ( QFileInfo( aFName + ".XML" ).exists() )
770 return false; // file does not exist
772 QFileInfo aFinfo( aFName );
773 aFName = aFinfo.canonicalFilePath();
775 if ( !importHistory.contains( aFName ) )
776 importHistory.insert( aFName );
778 return true; // already imported (prevent import loops)
784 QFile file( aFName );
785 if ( !file.open( QFile::ReadOnly ) )
787 qDebug() << "QtxResourceMgr: File is not accessible:" << aFName;
793 res = doc.setContent( &file );
798 qDebug() << "QtxResourceMgr: File is empty:" << aFName;
802 QDomElement root = doc.documentElement();
803 if ( root.isNull() || root.tagName() != docTag() )
805 qDebug() << "QtxResourceMgr: Invalid root in file:" << aFName;
809 QDomNode sectNode = root.firstChild();
810 while ( res && !sectNode.isNull() )
812 res = sectNode.isElement();
815 QDomElement sectElem = sectNode.toElement();
816 if ( sectElem.tagName() == sectionTag() && sectElem.hasAttribute( nameAttribute() ) )
818 QString section = sectElem.attribute( nameAttribute() );
819 QDomNode paramNode = sectNode.firstChild();
820 while ( res && !paramNode.isNull() )
822 res = paramNode.isElement();
825 QDomElement paramElem = paramNode.toElement();
826 if ( paramElem.tagName() == parameterTag() &&
827 paramElem.hasAttribute( nameAttribute() ) && paramElem.hasAttribute( valueAttribute() ) )
829 QString paramName = paramElem.attribute( nameAttribute() );
830 QString paramValue = paramElem.attribute( valueAttribute() );
831 secMap[section].insert( paramName, paramValue );
835 qDebug() << "QtxResourceMgr: Invalid parameter element in file:" << aFName;
841 res = paramNode.isComment();
843 qDebug() << "QtxResourceMgr: Node is neither element nor comment in file:" << aFName;
846 paramNode = paramNode.nextSibling();
849 else if ( sectElem.tagName() == importTag() && sectElem.hasAttribute( nameAttribute() ) )
851 QString impFile = QDir::toNativeSeparators( Qtx::makeEnvVarSubst( sectElem.attribute( nameAttribute() ), Qtx::Always ) );
852 QFileInfo impFInfo( impFile );
853 if ( impFInfo.isRelative() )
854 impFInfo.setFile( aFinfo.absoluteDir(), impFile );
856 QMap<QString, Section> impMap;
857 if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) )
859 qDebug() << "QtxResourceMgr: Error with importing file:" << sectElem.attribute( nameAttribute() );
863 QMap<QString, Section>::const_iterator it = impMap.constBegin();
864 for ( ; it != impMap.constEnd() ; ++it )
866 if ( !secMap.contains( it.key() ) )
868 // insert full section
869 secMap.insert( it.key(), it.value() );
873 // insert all parameters from the section
874 Section::ConstIterator paramIt = it.value().begin();
875 for ( ; paramIt != it.value().end() ; ++paramIt )
877 if ( !secMap[it.key()].contains( paramIt.key() ) )
878 secMap[it.key()].insert( paramIt.key(), paramIt.value() );
886 qDebug() << "QtxResourceMgr: Invalid section in file:" << aFName;
892 res = sectNode.isComment(); // if it's a comment -- let it be, pass it..
894 qDebug() << "QtxResourceMgr: Node is neither element nor comment in file:" << aFName;
897 sectNode = sectNode.nextSibling();
903 qDebug() << "QtxResourceMgr: File" << fname << "is loaded successfully";
908 \brief Save resources to the xml-file.
909 \param fname resources file name
910 \param secMap resources map
911 \return \c true on success and \c false on error
913 bool QtxResourceMgr::XmlFormat::save( const QString& fname, const QMap<QString, Section>& secMap )
919 if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) )
923 if ( !file.open( QFile::WriteOnly ) )
926 QDomDocument doc( docTag() );
927 QDomComment comment = doc.createComment( "\nThis file is automatically created by SALOME application.\nChanges made in this file can be lost!\n" );
928 doc.appendChild( comment );
929 QDomElement root = doc.createElement( docTag() );
930 doc.appendChild( root );
932 for ( QMap<QString, Section>::ConstIterator it = secMap.begin(); it != secMap.end(); ++it )
934 QDomElement sect = doc.createElement( sectionTag() );
935 sect.setAttribute( nameAttribute(), it.key() );
936 root.appendChild( sect );
937 for ( Section::ConstIterator iter = it.value().begin(); iter != it.value().end(); ++iter )
939 QDomElement val = doc.createElement( parameterTag() );
940 val.setAttribute( nameAttribute(), iter.key() );
941 val.setAttribute( valueAttribute(), iter.value() );
942 sect.appendChild( val );
946 QTextStream ts( &file );
947 QStringList docStr = doc.toString().split( "\n" );
948 for ( QStringList::ConstIterator itr = docStr.begin(); itr != docStr.end(); ++itr )
959 \brief Get document tag name
960 \return XML document tag name
962 QString QtxResourceMgr::XmlFormat::docTag() const
964 QString tag = option( "doc_tag" );
966 tag = QString( "document" );
971 \brief Get section tag name
972 \return XML section tag name
974 QString QtxResourceMgr::XmlFormat::sectionTag() const
976 QString tag = option( "section_tag" );
978 tag = QString( "section" );
983 \brief Get parameter tag name
984 \return XML parameter tag name
986 QString QtxResourceMgr::XmlFormat::parameterTag() const
988 QString tag = option( "parameter_tag" );
990 tag = QString( "parameter" );
995 \brief Get import tag name
996 \return XML import tag name
998 QString QtxResourceMgr::XmlFormat::importTag() const
1000 QString tag = option( "import_tag" );
1001 if ( tag.isEmpty() )
1002 tag = QString( "import" );
1007 \brief Get parameter tag's "name" attribute name
1008 \return XML parameter tag's "name" attribute name
1010 QString QtxResourceMgr::XmlFormat::nameAttribute() const
1012 QString str = option( "name_attribute" );
1013 if ( str.isEmpty() )
1014 str = QString( "name" );
1019 \brief Get parameter tag's "value" attribute name
1020 \return XML parameter tag's "value" attribute name
1022 QString QtxResourceMgr::XmlFormat::valueAttribute() const
1024 QString str = option( "value_attribute" );
1025 if ( str.isEmpty() )
1026 str = QString( "value" );
1031 \class QtxResourceMgr::Format
1032 \brief Generic resources files reader/writer class.
1037 \param fmt format name (for example, "xml" or "ini")
1039 QtxResourceMgr::Format::Format( const QString& fmt )
1047 QtxResourceMgr::Format::~Format()
1052 \brief Get the format name.
1055 QString QtxResourceMgr::Format::format() const
1061 \brief Get options names.
1062 \return list of the format options
1064 QStringList QtxResourceMgr::Format::options() const
1066 return myOpt.keys();
1070 \brief Get the value of the option with specified name.
1072 If option doesn't exist then null QString is returned.
1074 \param opt option name
1075 \return option value
1077 QString QtxResourceMgr::Format::option( const QString& opt ) const
1080 if ( myOpt.contains( opt ) )
1086 \brief Set the value of the option with specified name.
1087 \param opt option name
1088 \param val option value
1090 void QtxResourceMgr::Format::setOption( const QString& opt, const QString& val )
1092 myOpt.insert( opt, val );
1096 \brief Load resources from the resource file.
1097 \param res resources object
1098 \return \c true on success and \c false on error
1100 bool QtxResourceMgr::Format::load( Resources* res )
1105 QMap<QString, Section> sections;
1106 bool status = load( res->myFileName, sections );
1108 res->mySections = sections;
1110 qDebug() << "QtxResourceMgr: Can't load resource file:" << res->myFileName;
1116 \brief Save resources to the resource file.
1117 \param res resources object
1118 \return \c true on success and \c false on error
1120 bool QtxResourceMgr::Format::save( Resources* res )
1125 Qtx::mkDir( Qtx::dir( res->myFileName ) );
1127 QtxResourceMgr* mgr = res->resMgr();
1128 QString name = mgr ? mgr->userFileName( mgr->appName(), false ) : res->myFileName;
1129 return save( name, res->mySections );
1133 \fn virtual bool QtxResourceMgr::Format::load( const QString& fname,
1134 QMap<QString, Section>& secMap )
1135 \brief Load resources from the specified resources file.
1137 Should be implemented in the successors.
1139 \param fname resources file name
1140 \param secMap resources map to be filled in
1141 \return \c true on success and \c false on error
1145 \fn virtual bool QtxResourceMgr::Format::save( const QString& fname,
1146 const QMap<QString, Section>& secMap )
1148 \brief Save resources to the specified resources file.
1150 Should be implemented in the successors.
1152 \param fname resources file name
1153 \param secMap resources map
1154 \return \c true on success and \c false on error
1158 \class QtxResourceMgr
1159 \brief Application resources manager.
1161 This class can be used to define settings, save/load settings and
1162 application preferences to the resource file(s), load translation files
1163 (internationalization mechanism), load pixmaps and other resources from
1164 external files, etc.
1166 Currently it supports .ini and .xml resources file formats. To implement
1167 own resources file format, inherit from the Format class and implement virtual
1168 Format::load() and Format::save() methods.
1170 Resources manager is initialized by the (symbolic) name of the application.
1171 The parameter \a resVarTemplate specifies the template for the environment
1172 variable which should point to the resource directory or list of directories.
1173 Environment variable name is calculated by substitution of "%1" substring in
1174 the \a resVarTemplate parameter (if it contains such substring) by the
1175 application name (\a appName).
1176 By default, \a resVarTemplate is set to "%1Resources". For example, if the application name
1177 is "MyApp", the environment variable "MyAppResources" will be inspected in this case.
1179 Resource manager can handle several global application configuration files and
1180 one user configuration file. Location of global configuration files is defined
1181 by the environment variable (see above) and these files are always read-only.
1182 The name of the global configuration files is retrieved by calling virtual method
1183 globalFileName() which can be redefined in the QtxResourceMgr class successors.
1184 User configuration file always situated in the user's home directory. It's name
1185 is defined by calling virtual method userFileName() which can be also redefined
1186 in the QtxResourceMgr class successors. This is the only file which the preferences
1187 changed by the user during the application session are written to (usually
1188 when the application closes).
1190 Resources environment variable should contain one or several resource directories
1191 (separated by ";" symbol on Windows and ":" or ";" on Linux). Each resource directory
1192 can contain application global configuration file. The user configuration file has
1193 the highest priority, for the global configuration files the priority is decreasing from
1194 left to right, i.e. the first directory in the directoris list, defined by the
1195 resources environment variable has higher priority. Priority has the meaning when
1196 searching requested resources (application preference, pixmap file name, translation
1199 When retrieving preferences, it is sometimes helpful to ignore values coming from the
1200 user preference file and take into account only global preferences.
1201 To do this, use setWorkingMode() method passing QtxResourceMgr::IgnoreUserValues enumerator
1204 Resources manager operates with such terms like options, sections and parameters.
1205 Parametets are named application resources, for example, application preferences like
1206 integer, double, boolean or string values, pictures, font and color definitions, etc.
1207 Parameters are organized inside the resources files into the named groups - sections.
1208 Options are special kind of resoures which allow customizing resource files interpreting.
1209 For example, by default language settings are defined in the resource file in the
1210 section "language". It is possible to change this section name by setting "language"
1211 option to another value (see setOption()).
1213 Retrieving preferences values can be done by using one of value() methods, each returns
1214 \c true if the corresponding preference is found. Another way is to use integerValue(),
1215 doubleValue(), etc methods, which allow specifying default value which is used if the
1216 specified preference is not found. Removing of preferences or sections can be done using
1217 remove(const QString& sect) or remove(const QString& sect, const QString& name) methods.
1218 To add the preference or to change exiting preference value use setValue() methods family.
1219 Methods hasSection() and hasValue() can be used to check existence of section or
1220 preference (in the specified section). List of all sections can be retrieved with the
1221 sections() method, and list of all settings names in some specified section can be
1222 obtained with parameters() method.
1224 Pixmaps can be loaded with the loadPixmap() methods. If the specified pixmap is not found,
1225 the default one is returned. Default pixmap can be set by setDefaultPixmap().
1227 One of the key feature of the resources manager is support of application
1228 internationalization mechanism. Translation files for the specified language can be loaded
1229 with loadLanguage() method.
1233 \brief Constructs the resource manager.
1234 \param appName application name
1235 \param resVarTemplate resource environment variable pattern
1237 QtxResourceMgr::QtxResourceMgr( const QString& appName, const QString& resVarTemplate )
1238 : myAppName( appName ),
1239 myCheckExist( true ),
1241 myIsPixmapCached( true ),
1242 myHasUserValues( true ),
1243 myWorkingMode( AllowUserValues )
1245 QString envVar = !resVarTemplate.isEmpty() ? resVarTemplate : QString( "%1Resources" );
1246 if ( envVar.contains( "%1" ) )
1247 envVar = envVar.arg( appName );
1250 if ( ::getenv( envVar.toLatin1() ) )
1251 dirs = ::getenv( envVar.toLatin1() );
1253 QString dirsep = ";"; // for Windows: ";" is used as directories separator
1255 QString dirsep = "[:|;]"; // for Linux: both ":" and ";" can be used
1257 setDirList( dirs.split( QRegExp( dirsep ), QString::SkipEmptyParts ) );
1259 installFormat( new XmlFormat() );
1260 installFormat( new IniFormat() );
1262 setOption( "translators", QString( "%P_msg_%L.qm|%P_images.qm" ) );
1268 Destroy the resource manager and free allocated memory.
1270 QtxResourceMgr::~QtxResourceMgr()
1272 QStringList prefList = myTranslator.keys();
1273 for ( QStringList::ConstIterator it = prefList.begin(); it != prefList.end(); ++it )
1274 removeTranslators( *it );
1276 qDeleteAll( myResources );
1277 qDeleteAll( myFormats );
1279 delete myDefaultPix;
1283 \brief Get the application name.
1284 \return application name
1286 QString QtxResourceMgr::appName() const
1292 \brief Get the "check existance" flag
1294 If this flag is \c true then preference can be set (with setValue() method)
1295 only if it doesn't exist or if the value is changed.
1297 \return \c true if "check existance" flag is set
1299 bool QtxResourceMgr::checkExisting() const
1301 return myCheckExist;
1305 \brief Set the "check existance" flag.
1306 \param on new flag value
1308 void QtxResourceMgr::setCheckExisting( const bool on )
1314 \brief Get the resource directories list.
1316 Home user directory (where the user application configuration file is situated)
1317 is not included. This is that directories list defined by the application
1318 resources environment variable.
1320 \return list of directories names
1322 QStringList QtxResourceMgr::dirList() const
1328 \brief Initialise resources manager.
1330 Prepare the resources containers and load resources (if \a autoLoad is \c true).
1332 \param autoLoad if \c true (default) then all resources are loaded
1334 void QtxResourceMgr::initialize( const bool autoLoad ) const
1336 if ( !myResources.isEmpty() )
1339 QtxResourceMgr* that = (QtxResourceMgr*)this;
1341 if ( !userFileName( appName() ).isEmpty() )
1342 that->myResources.append( new Resources( that, userFileName( appName() ) ) );
1344 that->myHasUserValues = myResources.count() > 0;
1346 for ( QStringList::ConstIterator it = myDirList.begin(); it != myDirList.end(); ++it )
1348 QString path = Qtx::addSlash( *it ) + globalFileName( appName() );
1349 that->myResources.append( new Resources( that, path ) );
1357 \brief Get "cached pixmaps" option value.
1359 Resources manager allows possibility to cache loaded pixmaps that allow to
1360 improve application performance. This feature is turned on by default - all
1361 loaded pixmaps are stored in the internal map. Switching of this feature on/off
1362 can be done by setIsPixmapCached() method.
1364 \return \c true if pixmap cache is turned on
1365 \sa setIsPixmapCached()
1367 bool QtxResourceMgr::isPixmapCached() const
1369 return myIsPixmapCached;
1373 \brief Switch "cached pixmaps" option on/off.
1374 \param on enable pixmap cache if \c true and disable it if \c false
1375 \sa isPixmapCached()
1377 void QtxResourceMgr::setIsPixmapCached( const bool on )
1379 myIsPixmapCached = on;
1383 \brief Remove all resources from the resources manager.
1385 void QtxResourceMgr::clear()
1387 for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it )
1392 \brief Get current working mode.
1394 \return current working mode
1395 \sa setWorkingMode(), value(), hasValue(), hasSection(), setValue()
1397 QtxResourceMgr::WorkingMode QtxResourceMgr::workingMode() const
1399 return myWorkingMode;
1403 \brief Set resource manager's working mode.
1405 The resource manager can operate in the following working modes:
1406 * AllowUserValues : methods values(), hasValue(), hasSection() take into account user values (default)
1407 * IgnoreUserValues : methods values(), hasValue(), hasSection() do not take into account user values
1409 Note, that setValue() method always put the value to the user settings file.
1411 \param mode new working mode
1412 \return previous working mode
1413 \sa workingMode(), value(), hasValue(), hasSection(), setValue()
1415 QtxResourceMgr::WorkingMode QtxResourceMgr::setWorkingMode( WorkingMode mode )
1417 WorkingMode m = myWorkingMode;
1418 myWorkingMode = mode;
1423 \brief Get interger parameter value.
1424 \param sect section name
1425 \param name parameter name
1426 \param iVal parameter to return resulting integer value
1427 \return \c true if parameter is found and \c false if parameter is not found
1428 (in this case \a iVal value is undefined)
1430 bool QtxResourceMgr::value( const QString& sect, const QString& name, int& iVal ) const
1433 if ( !value( sect, name, val, true ) )
1437 iVal = val.toInt( &ok );
1443 \brief Get double parameter value.
1444 \param sect section name
1445 \param name parameter name
1446 \param dVal parameter to return resulting double value
1447 \return \c true if parameter is found and \c false if parameter is not found
1448 (in this case \a dVal value is undefined)
1450 bool QtxResourceMgr::value( const QString& sect, const QString& name, double& dVal ) const
1453 if ( !value( sect, name, val, true ) )
1457 dVal = val.toDouble( &ok );
1463 \brief Get boolean parameter value.
1464 \param sect section name
1465 \param name parameter name
1466 \param bVal parameter to return resulting boolean value
1467 \return \c true if parameter is found and \c false if parameter is not found
1468 (in this case \a bVal value is undefined)
1470 bool QtxResourceMgr::value( const QString& sect, const QString& name, bool& bVal ) const
1473 if ( !value( sect, name, val, true ) )
1476 static QMap<QString, bool> boolMap;
1477 if ( boolMap.isEmpty() )
1479 boolMap["true"] = boolMap["yes"] = boolMap["on"] = true;
1480 boolMap["false"] = boolMap["no"] = boolMap["off"] = false;
1483 val = val.toLower();
1484 bool res = boolMap.contains( val );
1486 bVal = boolMap[val];
1489 double num = val.toDouble( &res );
1498 \brief Get color parameter value.
1499 \param sect section name
1500 \param name parameter name
1501 \param cVal parameter to return resulting color value
1502 \return \c true if parameter is found and \c false if parameter is not found
1503 (in this case \a cVal value is undefined)
1505 bool QtxResourceMgr::value( const QString& sect, const QString& name, QColor& cVal ) const
1508 if ( !value( sect, name, val, true ) )
1511 return Qtx::stringToColor( val, cVal );
1515 \brief Get font parameter value.
1516 \param sect section name
1517 \param name parameter name
1518 \param fVal parameter to return resulting font value
1519 \return \c true if parameter is found and \c false if parameter is not found
1520 (in this case \a fVal value is undefined)
1522 bool QtxResourceMgr::value( const QString& sect, const QString& name, QFont& fVal ) const
1525 if ( !value( sect, name, val, true ) )
1528 QStringList fontDescr = val.split( ",", QString::SkipEmptyParts );
1530 if ( fontDescr.count() < 2 )
1533 QString family = fontDescr[0];
1534 if ( family.isEmpty() )
1537 fVal = QFont( family );
1539 for ( int i = 1; i < (int)fontDescr.count(); i++ )
1541 QString curval = fontDescr[i].trimmed().toLower();
1542 if ( curval == QString( "bold" ) )
1543 fVal.setBold( true );
1544 else if ( curval == QString( "italic" ) )
1545 fVal.setItalic( true );
1546 else if ( curval == QString( "underline" ) )
1547 fVal.setUnderline( true );
1548 else if ( curval == QString( "shadow" ) || curval == QString( "overline" ) )
1549 fVal.setOverline( true );
1553 int ps = curval.toInt( &isOk );
1555 fVal.setPointSize( ps );
1563 \brief Get byte array parameter value.
1564 \param sect section name
1565 \param name parameter name
1566 \param baVal parameter to return resulting byte array value
1567 \return \c true if parameter is found and \c false if parameter is not found
1568 (in this case \a baVal value is undefined)
1570 bool QtxResourceMgr::value( const QString& sect, const QString& name, QByteArray& baVal ) const
1573 if ( !value( sect, name, val, true ) )
1577 QStringList lst = val.split( QRegExp( "[\\s|,]" ), QString::SkipEmptyParts );
1578 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
1582 if ( str.startsWith( "#" ) )
1588 int num = str.toInt( &ok, base );
1589 if ( !ok || num < 0 || num > 255 )
1592 baVal.append( (char)num );
1594 return !baVal.isEmpty();
1598 \brief Get linear gradient parameter value.
1599 \param sect section name
1600 \param name parameter name
1601 \param gVal parameter to return resulting linear gradient value
1602 \return \c true if parameter is found and \c false if parameter is not found
1603 (in this case \a gVal value is undefined)
1605 bool QtxResourceMgr::value( const QString& sect, const QString& name, QLinearGradient& gVal ) const
1608 if ( !value( sect, name, val, true ) )
1611 return Qtx::stringToLinearGradient( val, gVal );
1615 \brief Get radial gradient parameter value.
1616 \param sect section name
1617 \param name parameter name
1618 \param gVal parameter to return resulting radial gradient value
1619 \return \c true if parameter is found and \c false if parameter is not found
1620 (in this case \a gVal value is undefined)
1622 bool QtxResourceMgr::value( const QString& sect, const QString& name, QRadialGradient& gVal ) const
1625 if ( !value( sect, name, val, true ) )
1628 return Qtx::stringToRadialGradient( val, gVal );
1632 \brief Get conical gradient parameter value.
1633 \param sect section name
1634 \param name parameter name
1635 \param gVal parameter to return resulting conical gradient value
1636 \return \c true if parameter is found and \c false if parameter is not found
1637 (in this case \a gVal value is undefined)
1639 bool QtxResourceMgr::value( const QString& sect, const QString& name, QConicalGradient& gVal ) const
1642 if ( !value( sect, name, val, true ) )
1645 return Qtx::stringToConicalGradient( val, gVal );
1649 \brief Get background parameter value.
1650 \param sect section name
1651 \param name parameter name
1652 \param bgVal parameter to return resulting background value
1653 \return \c true if parameter is found and \c false if parameter is not found
1654 (in this case \a bgVal value is undefined)
1656 bool QtxResourceMgr::value( const QString& sect, const QString& name, Qtx::BackgroundData& bgVal ) const
1659 if ( !value( sect, name, val, true ) )
1662 bgVal = Qtx::stringToBackground( val );
1663 return bgVal.isValid();
1667 \brief Get string parameter value (native format).
1668 \param sect section name
1669 \param name parameter name
1670 \param val parameter to return resulting byte array value
1671 \param subst if \c true perform environment variables substitution
1672 \return \c true if parameter is found and \c false if parameter is not found
1673 (in this case \a val value is undefined)
1675 bool QtxResourceMgr::value( const QString& sect, const QString& name, QString& val, const bool subst ) const
1681 ResList::ConstIterator it = myResources.begin();
1682 if ( myHasUserValues && workingMode() == IgnoreUserValues )
1685 for ( ; it != myResources.end() && !ok; ++it )
1687 ok = (*it)->hasValue( sect, name );
1689 val = (*it)->value( sect, name, subst );
1696 \brief Get interger parameter value.
1698 If the specified parameter is not found or can not be converted to the integer value,
1699 the specified default value is returned instead.
1701 \param sect section name
1702 \param name parameter name
1703 \param def default value
1704 \return parameter value (or default value if parameter is not found)
1706 int QtxResourceMgr::integerValue( const QString& sect, const QString& name, const int def ) const
1709 if ( !value( sect, name, val ) )
1715 \brief Get double parameter value.
1717 If the specified parameter is not found or can not be converted to the double value,
1718 the specified default value is returned instead.
1720 \param sect section name
1721 \param name parameter name
1722 \param def default value
1723 \return parameter value (or default value if parameter is not found)
1725 double QtxResourceMgr::doubleValue( const QString& sect, const QString& name, const double def ) const
1728 if ( !value( sect, name, val ) )
1734 \brief Get boolean parameter value.
1736 If the specified parameter is not found or can not be converted to the boolean value,
1737 the specified default value is returned instead.
1739 \param sect section name
1740 \param name parameter name
1741 \param def default value
1742 \return parameter value (or default value if parameter is not found)
1744 bool QtxResourceMgr::booleanValue( const QString& sect, const QString& name, const bool def ) const
1747 if ( !value( sect, name, val ) )
1753 \brief Get font parameter value.
1755 If the specified parameter is not found or can not be converted to the font value,
1756 the specified default value is returned instead.
1758 \param sect section name
1759 \param name parameter name
1760 \param def default value
1761 \return parameter value (or default value if parameter is not found)
1763 QFont QtxResourceMgr::fontValue( const QString& sect, const QString& name, const QFont& def ) const
1766 if( !value( sect, name, font ) )
1772 \brief Get color parameter value.
1774 If the specified parameter is not found or can not be converted to the color value,
1775 the specified default value is returned instead.
1777 \param sect section name
1778 \param name parameter name
1779 \param def default value
1780 \return parameter value (or default value if parameter is not found)
1782 QColor QtxResourceMgr::colorValue( const QString& sect, const QString& name, const QColor& def ) const
1785 if ( !value( sect, name, val ) )
1791 \brief Get string parameter value.
1793 If the specified parameter is not found, the specified default value is returned instead.
1795 \param sect section name
1796 \param name parameter name
1797 \param def default value
1798 \return parameter value (or default value if parameter is not found)
1800 QString QtxResourceMgr::stringValue( const QString& sect, const QString& name, const QString& def ) const
1803 if ( !value( sect, name, val ) )
1809 \brief Get byte array parameter value.
1811 If the specified parameter is not found, the specified default value is returned instead.
1813 \param sect section name
1814 \param name parameter name
1815 \param def default value
1816 \return parameter value (or default value if parameter is not found)
1818 QByteArray QtxResourceMgr::byteArrayValue( const QString& sect, const QString& name, const QByteArray& def ) const
1821 if ( !value( sect, name, val ) )
1827 \brief Get linear gradient parameter value.
1829 If the specified parameter is not found, the specified default value is returned instead.
1831 \param sect section name
1832 \param name parameter name
1833 \param def default value
1834 \return parameter value (or default value if parameter is not found)
1836 QLinearGradient QtxResourceMgr::linearGradientValue( const QString& sect, const QString& name, const QLinearGradient& def ) const
1838 QLinearGradient val;
1839 if ( !value( sect, name, val ) )
1845 \brief Get radial gradient parameter value.
1847 If the specified parameter is not found, the specified default value is returned instead.
1849 \param sect section name
1850 \param name parameter name
1851 \param def default value
1852 \return parameter value (or default value if parameter is not found)
1854 QRadialGradient QtxResourceMgr::radialGradientValue( const QString& sect, const QString& name, const QRadialGradient& def ) const
1856 QRadialGradient val;
1857 if ( !value( sect, name, val ) )
1863 \brief Get conical gradient parameter value.
1865 If the specified parameter is not found, the specified default value is returned instead.
1867 \param sect section name
1868 \param name parameter name
1869 \param def default value
1870 \return parameter value (or default value if parameter is not found)
1872 QConicalGradient QtxResourceMgr::conicalGradientValue( const QString& sect, const QString& name, const QConicalGradient& def ) const
1874 QConicalGradient val;
1875 if ( !value( sect, name, val ) )
1881 \brief Get background parameter value.
1883 If the specified parameter is not found, the specified default value is returned instead.
1885 \param sect section name
1886 \param name parameter name
1887 \param def default value
1888 \return parameter value (or default value if parameter is not found)
1890 Qtx::BackgroundData QtxResourceMgr::backgroundValue( const QString& sect, const QString& name, const Qtx::BackgroundData& def ) const
1892 Qtx::BackgroundData val;
1893 if ( !value( sect, name, val ) )
1899 \brief Check parameter existence.
1900 \param sect section name
1901 \param name parameter name
1902 \return \c true if parameter exists in specified section
1904 bool QtxResourceMgr::hasValue( const QString& sect, const QString& name ) const
1910 ResList::ConstIterator it = myResources.begin();
1911 if ( myHasUserValues && workingMode() == IgnoreUserValues )
1914 for ( ; it != myResources.end() && !ok; ++it )
1915 ok = (*it)->hasValue( sect, name );
1921 \brief Check section existence.
1922 \param sect section name
1923 \return \c true if section exists
1925 bool QtxResourceMgr::hasSection( const QString& sect ) const
1931 ResList::ConstIterator it = myResources.begin();
1932 if ( myHasUserValues && workingMode() == IgnoreUserValues )
1935 for ( ; it != myResources.end() && !ok; ++it )
1936 ok = (*it)->hasSection( sect );
1942 \brief Set integer parameter value.
1943 \param sect section name
1944 \param name parameter name
1945 \param val parameter value
1947 void QtxResourceMgr::setValue( const QString& sect, const QString& name, int val )
1950 if ( checkExisting() && value( sect, name, res ) && res == val )
1953 setResource( sect, name, QString::number( val ) );
1957 \brief Set double parameter value.
1958 \param sect section name
1959 \param name parameter name
1960 \param val parameter value
1962 void QtxResourceMgr::setValue( const QString& sect, const QString& name, double val )
1965 if ( checkExisting() && value( sect, name, res ) && res == val )
1968 setResource( sect, name, QString::number( val, 'g', 12 ) );
1972 \brief Set boolean parameter value.
1973 \param sect section name
1974 \param name parameter name
1975 \param val parameter value
1977 void QtxResourceMgr::setValue( const QString& sect, const QString& name, bool val )
1980 if ( checkExisting() && value( sect, name, res ) && res == val )
1983 setResource( sect, name, QString( val ? "true" : "false" ) );
1987 \brief Set color parameter value.
1988 \param sect section name
1989 \param name parameter name
1990 \param val parameter value
1992 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QColor& val )
1995 if ( checkExisting() && value( sect, name, res ) && res == val )
1998 setResource( sect, name, Qtx::colorToString( val ) );
2002 \brief Set font parameter value.
2003 \param sect section name
2004 \param name parameter name
2005 \param val parameter value
2007 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QFont& val )
2010 if ( checkExisting() && value( sect, name, res ) && res == val )
2013 QStringList fontDescr;
2014 fontDescr.append( val.family() );
2016 fontDescr.append( "Bold" );
2018 fontDescr.append( "Italic" );
2019 if ( val.underline() )
2020 fontDescr.append( "Underline" );
2021 if ( val.overline() )
2022 fontDescr.append( "Overline" );
2023 fontDescr.append( QString( "%1" ).arg( val.pointSize() ) );
2025 setResource( sect, name, fontDescr.join( "," ) );
2029 \brief Set string parameter value.
2030 \param sect section name
2031 \param name parameter name
2032 \param val parameter value
2034 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QString& val )
2037 if ( checkExisting() && value( sect, name, res ) && res == val )
2040 setResource( sect, name, val );
2044 \brief Set byte array parameter value.
2045 \param sect section name
2046 \param name parameter name
2047 \param val parameter value
2049 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QByteArray& val )
2052 if ( checkExisting() && value( sect, name, res ) && res == val )
2057 for ( int i = 0; i < val.size(); i++ )
2059 ::sprintf( buf, "#%02X", (unsigned char)val.at( i ) );
2060 lst.append( QString( buf ) );
2062 setResource( sect, name, lst.join( " " ) );
2066 \brief Set linear gradient parameter value.
2067 \param sect section name
2068 \param name parameter name
2069 \param val parameter value
2071 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QLinearGradient& val )
2073 QLinearGradient res;
2074 if ( checkExisting() && value( sect, name, res ) && res == val )
2077 setResource( sect, name, Qtx::gradientToString( val ) );
2081 \brief Set radial gradient parameter value.
2082 \param sect section name
2083 \param name parameter name
2084 \param val parameter value
2086 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QRadialGradient& val )
2088 QRadialGradient res;
2089 if ( checkExisting() && value( sect, name, res ) && res == val )
2092 setResource( sect, name, Qtx::gradientToString( val ) );
2096 \brief Set conical gradient parameter value.
2097 \param sect section name
2098 \param name parameter name
2099 \param val parameter value
2101 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const QConicalGradient& val )
2103 QConicalGradient res;
2104 if ( checkExisting() && value( sect, name, res ) && res == val )
2107 setResource( sect, name, Qtx::gradientToString( val ) );
2111 \brief Set background parameter value.
2112 \param sect section name
2113 \param name parameter name
2114 \param val parameter value
2116 void QtxResourceMgr::setValue( const QString& sect, const QString& name, const Qtx::BackgroundData& val )
2118 Qtx::BackgroundData res;
2119 if ( checkExisting() && value( sect, name, res ) && res == val )
2122 setResource( sect, name, Qtx::backgroundToString( val ) );
2126 \brief Remove resources section.
2127 \param sect section name
2129 void QtxResourceMgr::remove( const QString& sect )
2133 for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it )
2134 (*it)->removeSection( sect );
2138 \brief Remove the specified parameter.
2139 \param sect section name
2140 \param name parameter name
2142 void QtxResourceMgr::remove( const QString& sect, const QString& name )
2146 for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it )
2147 (*it)->removeValue( sect, name );
2151 \brief Get current configuration files format.
2152 \return configuration files format name
2154 QString QtxResourceMgr::currentFormat() const
2157 if ( !myFormats.isEmpty() )
2158 fmt = myFormats[0]->format();
2163 \brief Set current configuration files format.
2164 \param fmt configuration files format name
2166 void QtxResourceMgr::setCurrentFormat( const QString& fmt )
2168 Format* form = format( fmt );
2172 myFormats.removeAll( form );
2173 myFormats.prepend( form );
2175 if ( myResources.isEmpty() )
2178 ResList::Iterator resIt = myResources.begin();
2179 if ( myResources.count() > myDirList.count() && resIt != myResources.end() )
2181 (*resIt)->setFile( userFileName( appName() ) );
2185 for ( QStringList::ConstIterator it = myDirList.begin(); it != myDirList.end() && resIt != myResources.end(); ++it, ++resIt )
2186 (*resIt)->setFile( Qtx::addSlash( *it ) + globalFileName( appName() ) );
2190 \brief Get configuration files format by specified format name.
2191 \param fmt configuration files format name
2192 \return format object or 0 if format is not defined
2194 QtxResourceMgr::Format* QtxResourceMgr::format( const QString& fmt ) const
2197 for ( FormatList::ConstIterator it = myFormats.begin(); it != myFormats.end() && !form; ++it )
2199 if ( (*it)->format() == fmt )
2207 \brief Install configuration files format.
2209 Added format becomes current.
2211 \param form format object to be installed
2213 void QtxResourceMgr::installFormat( QtxResourceMgr::Format* form )
2215 if ( !myFormats.contains( form ) )
2216 myFormats.prepend( form );
2220 \brief Remove configuration files format.
2221 \param form format object to be uninstalled
2223 void QtxResourceMgr::removeFormat( QtxResourceMgr::Format* form )
2225 myFormats.removeAll( form );
2229 \brief Get resource format options names.
2230 \return list of options names
2232 QStringList QtxResourceMgr::options() const
2234 return myOptions.keys();
2238 \brief Get the string value of the specified resources format option.
2240 If option does not exist, null QString is returned.
2242 \param opt option name
2243 \return option value
2244 \sa setOption(), options()
2246 QString QtxResourceMgr::option( const QString& opt ) const
2249 if ( myOptions.contains( opt ) )
2250 val = myOptions[opt];
2255 \brief Set the string value of the specified resources format option.
2256 \param opt option name
2257 \param val option value
2258 \sa option(), options()
2260 void QtxResourceMgr::setOption( const QString& opt, const QString& val )
2262 myOptions.insert( opt, val );
2266 \brief Load all resources from all resource files (global and user).
2267 \return \c true on success and \c false on error
2270 bool QtxResourceMgr::load()
2272 initialize( false );
2274 Format* fmt = format( currentFormat() );
2279 for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it )
2280 res = fmt->load( *it ) && res;
2286 \brief Import resources from specified resource file.
2287 \param fname resources file name
2288 \return \c true on success and \c false on error
2290 bool QtxResourceMgr::import( const QString& fname )
2292 Format* fmt = format( currentFormat() );
2296 if ( myResources.isEmpty() || !myHasUserValues )
2299 Resources* r = myResources[0];
2303 QString old = r->file();
2304 r->setFile( fname );
2305 bool res = fmt->load( r );
2311 \brief Save all resources to the user resource files.
2312 \return \c true on success and \c false on error
2314 bool QtxResourceMgr::save()
2316 initialize( false );
2318 Format* fmt = format( currentFormat() );
2322 if ( myResources.isEmpty() || !myHasUserValues )
2325 bool result = fmt->save( myResources[0] );
2333 \brief Get all sections names.
2334 \return list of section names
2336 QStringList QtxResourceMgr::sections() const
2340 QMap<QString, int> map;
2342 ResList::ConstIterator it = myResources.begin();
2343 if ( myHasUserValues && workingMode() == IgnoreUserValues )
2346 for ( ; it != myResources.end(); ++it )
2348 QStringList lst = (*it)->sections();
2349 for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr )
2350 map.insert( *itr, 0 );
2357 \brief Get all sections names matching specified regular expression.
2358 \param re searched regular expression
2359 \return list of sections names
2361 QStringList QtxResourceMgr::sections(const QRegExp& re) const
2363 return sections().filter( re );
2367 \brief Get all sections names with the prefix specified by passed
2368 list of parent sections names.
2370 Sub-sections are separated inside the section name by the sections
2371 separator token, for example "splash:color:label".
2373 \param names parent sub-sections names
2374 \return list of sections names
2376 QStringList QtxResourceMgr::sections(const QStringList& names) const
2378 QStringList nm = names;
2380 QRegExp re( QString( "^%1$" ).arg( nm.join( sectionsToken() ) ) );
2381 return sections( re );
2385 \brief Get list of sub-sections names for the specified parent section name.
2387 Sub-sections are separated inside the section name by the sections
2388 separator token, for example "splash:color:label".
2390 \param section parent sub-section name
2391 \param full if \c true return full names of child sub-sections, if \c false,
2392 return only top-level sub-sections names
2393 \return list of sub-sections names
2395 QStringList QtxResourceMgr::subSections(const QString& section, const bool full) const
2397 QStringList names = sections( QStringList() << section );
2398 QMutableListIterator<QString> it( names );
2399 while ( it.hasNext() ) {
2400 QString name = it.next().mid( section.size() + 1 ).trimmed();
2401 if ( name.isEmpty() ) {
2405 if ( !full ) name = name.split( sectionsToken() ).first();
2406 it.setValue( name );
2408 names.removeDuplicates();
2414 \brief Get all parameters name in specified section.
2415 \param sec section name
2416 \return list of settings names
2418 QStringList QtxResourceMgr::parameters( const QString& sec ) const
2422 #if defined(QTX_NO_INDEXED_MAP)
2423 typedef QMap<QString, int> PMap;
2425 typedef IMap<QString, int> PMap;
2429 Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0;
2431 QListIterator<Resources*> it( myResources );
2433 while ( it.hasPrevious() )
2435 Resources* r = it.previous();
2436 if ( r == ur ) break;
2437 QStringList lst = r->parameters( sec );
2438 for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr )
2439 #if defined(QTX_NO_INDEXED_MAP)
2440 if ( !pmap.contains( *itr ) ) pmap.insert( *itr, 0 );
2442 pmap.insert( *itr, 0, false );
2450 \brief Get all parameters name in specified
2451 list of sub-sections names.
2453 Sub-sections are separated inside the section name by the sections
2454 separator token, for example "splash:color:label".
2456 \param names parent sub-sections names
2457 \return list of settings names
2459 QStringList QtxResourceMgr::parameters( const QStringList& names ) const
2461 return parameters( names.join( sectionsToken() ) );
2465 \brief Get absolute path to the file which name is defined by the parameter.
2467 The file name is defined by \a name argument, while directory name is retrieved
2468 from resources parameter \a prefix of section \a sec. Both directory and file name
2469 can be relative. If the directory is relative, it is calculated from the initial
2470 resources file name. Directory parameter can contain environment
2471 variables, which are substituted automatically.
2473 \param sec section name
2474 \param prefix parameter containing directory name
2475 \param name file name
2476 \return absolute file path or null QString if file does not exist
2478 QString QtxResourceMgr::path( const QString& sect, const QString& prefix, const QString& name ) const
2482 ResList::ConstIterator it = myResources.begin();
2483 if ( myHasUserValues && workingMode() == IgnoreUserValues )
2486 for ( ; it != myResources.end() && res.isEmpty(); ++it )
2487 res = (*it)->path( sect, prefix, name );
2492 \brief Get application resources section name.
2494 By default, application resources section name is "resources" but
2495 it can be changed by setting the "res_section_name" resources manager option.
2497 \return section corresponding to the resources directories
2498 \sa option(), setOption()
2500 QString QtxResourceMgr::resSection() const
2502 QString res = option( "res_section_name" );
2503 if ( res.isEmpty() )
2504 res = QString( "resources" );
2509 \brief Get application language section name.
2511 By default, application language section name is "language" but
2512 it can be changed by setting the "lang_section_name" resources manager option.
2514 \return section corresponding to the application language settings
2515 \sa option(), setOption()
2517 QString QtxResourceMgr::langSection() const
2519 QString res = option( "lang_section_name" );
2520 if ( res.isEmpty() )
2521 res = QString( "language" );
2526 \brief Get sections separator token.
2528 By default, sections separator token is colon symbol ":" but
2529 it can be changed by setting the "section_token" resources manager option.
2531 \return string corresponding to the current section separator token
2532 \sa option(), setOption()
2534 QString QtxResourceMgr::sectionsToken() const
2536 QString res = option( "section_token" );
2537 if ( res.isEmpty() )
2538 res = QString( ":" );
2543 \brief Get default pixmap.
2545 Default pixmap is used when requested pixmap resource is not found.
2547 \return default pixmap
2548 \sa setDefaultPixmap(), loadPixmap()
2550 QPixmap QtxResourceMgr::defaultPixmap() const
2552 static QPixmap* defpx = 0;
2554 defpx = new QPixmap( pixmap_not_found_xpm );
2556 return myDefaultPix ? *myDefaultPix : *defpx;
2560 \brief Set default pixmap.
2562 Default pixmap is used when requested pixmap resource is not found.
2564 \param pix default pixmap
2565 \sa defaultPixmap(), loadPixmap()
2567 void QtxResourceMgr::setDefaultPixmap( const QPixmap& pix )
2569 delete myDefaultPix;
2573 myDefaultPix = new QPixmap( pix );
2577 \brief Load pixmap resource.
2578 \param prefix parameter which refers to the resources directory (directories)
2579 \param name pixmap file name
2580 \return pixmap loaded from the file
2581 \sa defaultPixmap(), setDefaultPixmap()
2583 QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name ) const
2585 return loadPixmap( prefix, name, true );
2589 \brief Load pixmap resource.
2591 \param prefix parameter which refers to the resources directory (directories)
2592 \param name pixmap file name
2593 \param useDef if \c false, default pixmap is not returned if resource is not found,
2594 in this case null pixmap is returned instead
2595 \return pixmap loaded from the file
2596 \sa defaultPixmap(), setDefaultPixmap()
2598 QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name, const bool useDef ) const
2600 return loadPixmap( prefix, name, useDef ? defaultPixmap() : QPixmap() );
2604 \brief Load pixmap resource.
2606 \param prefix parameter which refers to the resources directory (directories)
2607 \param name pixmap file name
2608 \param defPix default which should be used if the resource file doesn't exist
2609 \return pixmap loaded from the file
2610 \sa defaultPixmap(), setDefaultPixmap()
2612 QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name, const QPixmap& defPix ) const
2618 ResList::ConstIterator it = myResources.begin();
2619 if ( myHasUserValues && workingMode() == IgnoreUserValues )
2622 for ( ; it != myResources.end() && pix.isNull(); ++it )
2623 pix = (*it)->loadPixmap( resSection(), prefix, name );
2630 \brief Load translation files according to the specified language.
2632 Names of the translation files are calculated according to the pattern specified
2633 by the "translators" option (this option is read from the section "language" of resources files).
2634 By default, "%P_msg_%L.qm" pattern is used.
2635 Keywords \%A, \%P, \%L in the pattern are substituted by the application name, prefix and language name
2637 For example, for prefix "SUIT" and language "en", all translation files "SUIT_msg_en.qm" are searched and
2640 If prefix is empty or null string, all translation files specified in the "resources" section of resources
2641 files are loaded (actually, the section is retrieved from resSection() method).
2642 If language is not specified, it is retrieved from the langSection() method, and if the latest is also empty,
2643 by default "en" (English) language is used.
2644 By default, settings from the user preferences file are also loaded (if user resource file is valid,
2645 see userFileName()). To avoid loading user settings, pass \c false as first parameter.
2647 \param pref parameter which defines translation context (for example, package name)
2648 \param l language name
2650 \sa resSection(), langSection(), loadTranslators()
2652 void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l )
2656 QMap<QChar, QString> substMap;
2657 substMap.insert( 'A', appName() );
2660 if ( lang.isEmpty() )
2661 value( langSection(), "language", lang );
2663 if ( lang.isEmpty() )
2665 lang = QString( "en" );
2666 qWarning() << "QtxResourceMgr: Language not specified. Assumed:" << lang;
2669 substMap.insert( 'L', lang );
2672 if ( value( langSection(), "translators", trs, false ) && !trs.isEmpty() )
2674 QStringList translators = option( "translators" ).split( "|", QString::SkipEmptyParts );
2675 QStringList newTranslators = trs.split( "|", QString::SkipEmptyParts );
2676 for ( int i = 0; i < (int)newTranslators.count(); i++ )
2678 if ( translators.indexOf( newTranslators[i] ) < 0 )
2679 translators += newTranslators[i];
2681 setOption( "translators", translators.join( "|" ) );
2684 QStringList trList = option( "translators" ).split( "|", QString::SkipEmptyParts );
2685 if ( trList.isEmpty() )
2687 trList.append( "%P_msg_%L.qm" );
2688 qWarning() << "QtxResourceMgr: Translators not defined. Assumed:" << trList[0];
2691 QStringList prefixList;
2692 if ( !pref.isEmpty() )
2693 prefixList.append( pref );
2695 prefixList = parameters( resSection() );
2697 if ( pref.isEmpty() && lang != "en" ) {
2698 // load Qt resources
2699 QString qt_translations = QLibraryInfo::location( QLibraryInfo::TranslationsPath );
2700 QString qt_dir_trpath = Qtx::qtDir( "translations" );
2701 QTranslator* trans = new QtxTranslator( 0 );
2702 if ( trans->load( QString("qt_%1").arg( lang ), qt_translations ) || trans->load( QString("qt_%1").arg( lang ), qt_dir_trpath ) ) {
2703 if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans );
2707 for ( QStringList::ConstIterator iter = prefixList.begin(); iter != prefixList.end(); ++iter )
2709 QString prefix = *iter;
2710 substMap.insert( 'P', prefix );
2713 for ( QStringList::ConstIterator it = trList.begin(); it != trList.end(); ++it )
2714 trs.append( substMacro( *it, substMap ).trimmed() );
2716 loadTranslators( prefix, trs );
2721 \brief Load translation files for the specified translation context.
2722 \param prefix parameter which defines translation context (for example, package name)
2723 \param translators list of translation files
2726 void QtxResourceMgr::loadTranslators( const QString& prefix, const QStringList& translators )
2732 ResList::ConstIterator iter = myResources.begin();
2733 if ( myHasUserValues && workingMode() == IgnoreUserValues )
2736 for ( ; iter != myResources.end(); ++iter )
2737 lst.prepend( *iter );
2739 QTranslator* trans = 0;
2741 for ( ResList::Iterator it = lst.begin(); it != lst.end(); ++it )
2743 for ( QStringList::ConstIterator itr = translators.begin(); itr != translators.end(); ++itr )
2745 trans = (*it)->loadTranslator( resSection(), prefix, *itr );
2748 if ( !myTranslator[prefix].contains( trans ) )
2749 myTranslator[prefix].append( trans );
2750 if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans );
2757 \brief Load translation file.
2758 \param prefix parameter which defines translation context (for example, package name)
2759 \param name translator file name
2760 \sa loadLanguage(), loadTranslators()
2762 void QtxResourceMgr::loadTranslator( const QString& prefix, const QString& name )
2766 QTranslator* trans = 0;
2768 Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0;
2770 QListIterator<Resources*> it( myResources );
2772 while ( it.hasPrevious() )
2774 Resources* r = it.previous();
2775 if ( r == ur ) break;
2777 trans = r->loadTranslator( resSection(), prefix, name );
2780 if ( !myTranslator[prefix].contains( trans ) )
2781 myTranslator[prefix].append( trans );
2782 if ( QApplication::instance() ) QApplication::instance()->installTranslator( trans );
2788 \brief Add custom translator.
2789 \param prefix parameter which defines translation context (for example, package name)
2790 \param translator translator being installed
2791 \sa loadLanguage(), loadTranslators()
2793 void QtxResourceMgr::addTranslator( const QString& prefix, QTranslator* translator )
2797 if ( !myTranslator[prefix].contains( translator ) ) {
2798 myTranslator[prefix].append( translator );
2799 if ( QApplication::instance() )
2800 QApplication::instance()->installTranslator( translator );
2806 \brief Remove all translators corresponding to the specified translation context.
2807 \param prefix parameter which defines translation context (for example, package name)
2809 void QtxResourceMgr::removeTranslators( const QString& prefix )
2811 if ( !myTranslator.contains( prefix ) )
2814 for ( TransList::Iterator it = myTranslator[prefix].begin(); it != myTranslator[prefix].end(); ++it )
2816 if ( QApplication::instance() ) QApplication::instance()->removeTranslator( *it );
2820 myTranslator.remove( prefix );
2824 \brief Move all translators corresponding to the specified translation context
2825 to the top of translators stack (increase their priority).
2826 \param prefix parameter which defines translation context (for example, package name)
2828 void QtxResourceMgr::raiseTranslators( const QString& prefix )
2830 if ( !myTranslator.contains( prefix ) )
2833 for ( TransList::Iterator it = myTranslator[prefix].begin(); it != myTranslator[prefix].end(); ++it )
2835 if ( QApplication::instance() ) {
2836 QApplication::instance()->removeTranslator( *it );
2837 QApplication::instance()->installTranslator( *it );
2843 \brief Copy all parameters to the user resources in order to
2844 saved them lately in the user home folder.
2846 void QtxResourceMgr::refresh()
2848 QStringList sl = sections();
2849 for ( QStringList::ConstIterator it = sl.begin(); it != sl.end(); ++it )
2851 QStringList pl = parameters( *it );
2852 for ( QStringList::ConstIterator itr = pl.begin(); itr != pl.end(); ++itr )
2853 setResource( *it, *itr, stringValue( *it, *itr ) );
2858 \brief Set the resource directories (where global confguration files are searched).
2860 This function also clears all currently set resources.
2862 \param dl directories list
2864 void QtxResourceMgr::setDirList( const QStringList& dl )
2867 for ( ResList::Iterator it = myResources.begin(); it != myResources.end(); ++it )
2870 myResources.clear();
2874 \brief Set parameter value.
2875 \param sect section name
2876 \param name parameter name
2877 \param val parameter value
2879 void QtxResourceMgr::setResource( const QString& sect, const QString& name, const QString& val )
2883 if ( !myResources.isEmpty() && myHasUserValues )
2884 myResources.first()->setValue( sect, name, val );
2888 \brief Get user configuration file name.
2890 This method can be redefined in the successor class to customize the user configuration file name.
2891 User configuration file is always situated in the user's home directory. By default .<appName>rc
2892 file is used on Linux (e.g. .MyApprc) and <appName>.<format> under Windows (e.g. MyApp.xml).
2894 Parameter \a for_load (not used in default implementation) specifies the usage mode, i.e. if
2895 user configuration file is opened for reading or writing. This allows customizing a way of application
2896 resources initializing (for example, if the user configuraion file includes version number and there is
2897 no file corresponding to this version in the user's home directory, it could be good idea to try
2898 the configuration file from the previous versions of the application).
2900 \param appName application name
2901 \param for_load boolean flag indicating that file is opened for loading or saving (not used in default implementation)
2902 \return user configuration file name
2903 \sa globalFileName()
2905 QString QtxResourceMgr::userFileName( const QString& appName, const bool /*for_load*/ ) const
2908 QString pathName = QDir::homePath();
2909 QString cfgAppName = QApplication::applicationName();
2910 if ( !cfgAppName.isEmpty() )
2911 pathName = Qtx::addSlash( Qtx::addSlash( pathName ) + QString( ".config" ) ) + cfgAppName;
2914 fileName = QString( "%1.%2" ).arg( appName ).arg( currentFormat() );
2916 fileName = QString( "%1rc" ).arg( appName );
2917 // VSR 24/09/2012: issue 0021781: do not prepend filename with "."
2918 // when user file is stored in ~/.config/<appname> directory
2919 if ( cfgAppName.isEmpty() )
2920 fileName.prepend( "." );
2923 if ( !fileName.isEmpty() )
2924 pathName = Qtx::addSlash( pathName ) + fileName;
2930 \brief Get global configuration file name.
2932 This method can be redefined in the successor class to customize the global configuration file name.
2933 Global configuration files are searched in the directories specified by the application resources
2934 environment variable (e.g. MyAppResources). By default <appName>.<format> file name is used
2937 \param appName application name
2938 \return global configuration file name
2941 QString QtxResourceMgr::globalFileName( const QString& appName ) const
2943 return QString( "%1.%2" ).arg( appName ).arg( currentFormat() );
2947 \brief This function is called after user configuration file is saved.
2948 Can be redefined in the successor classes, default implementation does nothing.
2950 void QtxResourceMgr::saved()
2955 \brief Perform substitution of the patterns like \%A, \%B, etc by values from the map.
2957 Used by loadLanguage().
2959 \param src sring to be processed
2960 \param substMap map of values for replacing
2961 \return processed string
2963 QString QtxResourceMgr::substMacro( const QString& src, const QMap<QChar, QString>& substMap ) const
2967 QRegExp rx( "%[A-Za-z%]" );
2970 while ( ( idx = rx.indexIn( trg, idx ) ) >= 0 )
2972 QChar spec = trg.at( idx + 1 );
2976 else if ( substMap.contains( spec ) )
2977 subst = substMap[spec];
2979 if ( !subst.isEmpty() )
2981 trg.replace( idx, rx.matchedLength(), subst );
2982 idx += subst.length();
2985 idx += rx.matchedLength();