ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

config-manager.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/config-manager.h"
00024 #include "common/debug.h"
00025 #include "common/file.h"
00026 #include "common/fs.h"
00027 #include "common/system.h"
00028 #include "common/textconsole.h"
00029 
00030 static bool isValidDomainName(const Common::String &domName) {
00031     const char *p = domName.c_str();
00032     while (*p && (Common::isAlnum(*p) || *p == '-' || *p == '_'))
00033         p++;
00034     return *p == 0;
00035 }
00036 
00037 namespace Common {
00038 
00039 DECLARE_SINGLETON(ConfigManager);
00040 
00041 char const *const ConfigManager::kApplicationDomain = "residualvm";
00042 char const *const ConfigManager::kTransientDomain = "__TRANSIENT";
00043 
00044 #ifdef ENABLE_KEYMAPPER
00045 char const *const ConfigManager::kKeymapperDomain = "keymapper";
00046 #endif
00047 
00048 #ifdef USE_CLOUD
00049 char const *const ConfigManager::kCloudDomain = "cloud";
00050 #endif
00051 
00052 #pragma mark -
00053 
00054 
00055 ConfigManager::ConfigManager() : _activeDomain(nullptr) {
00056 }
00057 
00058 void ConfigManager::defragment() {
00059     ConfigManager *newInstance = new ConfigManager();
00060     newInstance->copyFrom(*_singleton);
00061     delete _singleton;
00062     _singleton = newInstance;
00063 }
00064 
00065 void ConfigManager::copyFrom(ConfigManager &source) {
00066     _transientDomain = source._transientDomain;
00067     _gameDomains = source._gameDomains;
00068     _miscDomains = source._miscDomains;
00069     _appDomain = source._appDomain;
00070     _defaultsDomain = source._defaultsDomain;
00071 #ifdef ENABLE_KEYMAPPER
00072     _keymapperDomain = source._keymapperDomain;
00073 #endif
00074 #ifdef USE_CLOUD
00075     _cloudDomain = source._cloudDomain;
00076 #endif
00077     _domainSaveOrder = source._domainSaveOrder;
00078     _activeDomainName = source._activeDomainName;
00079     _activeDomain = &_gameDomains[_activeDomainName];
00080     _filename = source._filename;
00081 }
00082 
00083 
00084 void ConfigManager::loadDefaultConfigFile() {
00085     // Open the default config file
00086     assert(g_system);
00087     SeekableReadStream *stream = g_system->createConfigReadStream();
00088     _filename.clear(); // clear the filename to indicate that we are using the default config file
00089 
00090     // ... load it, if available ...
00091     if (stream) {
00092         loadFromStream(*stream);
00093 
00094         // ... and close it again.
00095         delete stream;
00096 
00097     } else {
00098         // No config file -> create new one!
00099         debug("Default configuration file missing, creating a new one");
00100 
00101         flushToDisk();
00102     }
00103 }
00104 
00105 void ConfigManager::loadConfigFile(const String &filename) {
00106     _filename = filename;
00107 
00108     FSNode node(filename);
00109     File cfg_file;
00110     if (!cfg_file.open(node)) {
00111         debug("Creating configuration file: %s", filename.c_str());
00112     } else {
00113         debug("Using configuration file: %s", _filename.c_str());
00114         loadFromStream(cfg_file);
00115     }
00116 }
00117 
00122 void ConfigManager::addDomain(const String &domainName, const ConfigManager::Domain &domain) {
00123     if (domainName.empty())
00124         return;
00125     if (domainName == kApplicationDomain) {
00126         _appDomain = domain;
00127 #ifdef ENABLE_KEYMAPPER
00128     } else if (domainName == kKeymapperDomain) {
00129         _keymapperDomain = domain;
00130 #endif
00131 #ifdef USE_CLOUD
00132     } else if (domainName == kCloudDomain) {
00133         _cloudDomain = domain;
00134 #endif
00135     } else if (domain.contains("gameid")) {
00136         // If the domain contains "gameid" we assume it's a game domain
00137         if (_gameDomains.contains(domainName))
00138             warning("Game domain %s already exists in ConfigManager", domainName.c_str());
00139 
00140         _gameDomains[domainName] = domain;
00141 
00142         _domainSaveOrder.push_back(domainName);
00143 
00144         // Check if we have the same misc domain. For older config files
00145         // we could have 'ghost' domains with the same name, so delete
00146         // the ghost domain
00147         if (_miscDomains.contains(domainName))
00148             _miscDomains.erase(domainName);
00149     } else {
00150         // Otherwise it's a miscellaneous domain
00151         if (_miscDomains.contains(domainName))
00152             warning("Misc domain %s already exists in ConfigManager", domainName.c_str());
00153 
00154         _miscDomains[domainName] = domain;
00155     }
00156 }
00157 
00158 
00159 void ConfigManager::loadFromStream(SeekableReadStream &stream) {
00160     String domainName;
00161     String comment;
00162     Domain domain;
00163     int lineno = 0;
00164 
00165     _appDomain.clear();
00166     _gameDomains.clear();
00167     _miscDomains.clear();
00168     _transientDomain.clear();
00169     _domainSaveOrder.clear();
00170 
00171 #ifdef ENABLE_KEYMAPPER
00172     _keymapperDomain.clear();
00173 #endif
00174 #ifdef USE_CLOUD
00175     _cloudDomain.clear();
00176 #endif
00177 
00178     // TODO: Detect if a domain occurs multiple times (or likewise, if
00179     // a key occurs multiple times inside one domain).
00180 
00181     while (!stream.eos() && !stream.err()) {
00182         lineno++;
00183 
00184         // Read a line
00185         String line = stream.readLine();
00186 
00187         if (line.size() == 0) {
00188             // Do nothing
00189         } else if (line[0] == '#') {
00190             // Accumulate comments here. Once we encounter either the start
00191             // of a new domain, or a key-value-pair, we associate the value
00192             // of the 'comment' variable with that entity.
00193             comment += line;
00194             comment += "\n";
00195         } else if (line[0] == '[') {
00196             // It's a new domain which begins here.
00197             // Determine where the previously accumulated domain goes, if we accumulated anything.
00198             addDomain(domainName, domain);
00199             domain.clear();
00200             const char *p = line.c_str() + 1;
00201             // Get the domain name, and check whether it's valid (that
00202             // is, verify that it only consists of alphanumerics,
00203             // dashes and underscores).
00204             while (*p && (isAlnum(*p) || *p == '-' || *p == '_'))
00205                 p++;
00206 
00207             if (*p == '\0')
00208                 error("Config file buggy: missing ] in line %d", lineno);
00209             else if (*p != ']')
00210                 error("Config file buggy: Invalid character '%c' occurred in section name in line %d", *p, lineno);
00211 
00212             domainName = String(line.c_str() + 1, p);
00213 
00214             domain.setDomainComment(comment);
00215             comment.clear();
00216 
00217         } else {
00218             // This line should be a line with a 'key=value' pair, or an empty one.
00219 
00220             // Skip leading whitespaces
00221             const char *t = line.c_str();
00222             while (isSpace(*t))
00223                 t++;
00224 
00225             // Skip empty lines / lines with only whitespace
00226             if (*t == 0)
00227                 continue;
00228 
00229             // If no domain has been set, this config file is invalid!
00230             if (domainName.empty()) {
00231                 error("Config file buggy: Key/value pair found outside a domain in line %d", lineno);
00232             }
00233 
00234             // Split string at '=' into 'key' and 'value'. First, find the "=" delimeter.
00235             const char *p = strchr(t, '=');
00236             if (!p)
00237                 error("Config file buggy: Junk found in line line %d: '%s'", lineno, t);
00238 
00239             // Extract the key/value pair
00240             String key(t, p);
00241             String value(p + 1);
00242 
00243             // Trim of spaces
00244             key.trim();
00245             value.trim();
00246 
00247             // Finally, store the key/value pair in the active domain
00248             domain[key] = value;
00249 
00250             // Store comment
00251             domain.setKVComment(key, comment);
00252             comment.clear();
00253         }
00254     }
00255 
00256     addDomain(domainName, domain); // Add the last domain found
00257 }
00258 
00259 void ConfigManager::flushToDisk() {
00260 #ifndef __DC__
00261     WriteStream *stream;
00262 
00263     if (_filename.empty()) {
00264         // Write to the default config file
00265         assert(g_system);
00266         stream = g_system->createConfigWriteStream();
00267         if (!stream)    // If writing to the config file is not possible, do nothing
00268             return;
00269     } else {
00270         DumpFile *dump = new DumpFile();
00271         assert(dump);
00272 
00273         if (!dump->open(_filename)) {
00274             warning("Unable to write configuration file: %s", _filename.c_str());
00275             delete dump;
00276             return;
00277         }
00278 
00279         stream = dump;
00280     }
00281 
00282     // Write the application domain
00283     writeDomain(*stream, kApplicationDomain, _appDomain);
00284 
00285 #ifdef ENABLE_KEYMAPPER
00286     // Write the keymapper domain
00287     writeDomain(*stream, kKeymapperDomain, _keymapperDomain);
00288 #endif
00289 #ifdef USE_CLOUD
00290     // Write the cloud domain
00291     writeDomain(*stream, kCloudDomain, _cloudDomain);
00292 #endif
00293 
00294     DomainMap::const_iterator d;
00295 
00296     // Write the miscellaneous domains next
00297     for (d = _miscDomains.begin(); d != _miscDomains.end(); ++d) {
00298         writeDomain(*stream, d->_key, d->_value);
00299     }
00300 
00301     // First write the domains in _domainSaveOrder, in that order.
00302     // Note: It's possible for _domainSaveOrder to list domains which
00303     // are not present anymore, so we validate each name.
00304     Array<String>::const_iterator i;
00305     for (i = _domainSaveOrder.begin(); i != _domainSaveOrder.end(); ++i) {
00306         if (_gameDomains.contains(*i)) {
00307             writeDomain(*stream, *i, _gameDomains[*i]);
00308         }
00309     }
00310 
00311     // Now write the domains which haven't been written yet
00312     for (d = _gameDomains.begin(); d != _gameDomains.end(); ++d) {
00313         if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), d->_key) == _domainSaveOrder.end())
00314             writeDomain(*stream, d->_key, d->_value);
00315     }
00316 
00317     delete stream;
00318 
00319 #endif // !__DC__
00320 }
00321 
00322 void ConfigManager::writeDomain(WriteStream &stream, const String &name, const Domain &domain) {
00323     if (domain.empty())
00324         return; // Don't bother writing empty domains.
00325 
00326     // WORKAROUND: Fix for bug #1972625 "ALL: On-the-fly targets are
00327     // written to the config file": Do not save domains that came from
00328     // the command line
00329     if (domain.contains("id_came_from_command_line"))
00330         return;
00331 
00332     String comment;
00333 
00334     // Write domain comment (if any)
00335     comment = domain.getDomainComment();
00336     if (!comment.empty())
00337         stream.writeString(comment);
00338 
00339     // Write domain start
00340     stream.writeByte('[');
00341     stream.writeString(name);
00342     stream.writeByte(']');
00343     stream.writeByte('\n');
00344 
00345     // Write all key/value pairs in this domain, including comments
00346     Domain::const_iterator x;
00347     for (x = domain.begin(); x != domain.end(); ++x) {
00348         if (!x->_value.empty()) {
00349             // Write comment (if any)
00350             if (domain.hasKVComment(x->_key)) {
00351                 comment = domain.getKVComment(x->_key);
00352                 stream.writeString(comment);
00353             }
00354             // Write the key/value pair
00355             stream.writeString(x->_key);
00356             stream.writeByte('=');
00357             stream.writeString(x->_value);
00358             stream.writeByte('\n');
00359         }
00360     }
00361     stream.writeByte('\n');
00362 }
00363 
00364 
00365 #pragma mark -
00366 
00367 
00368 const ConfigManager::Domain *ConfigManager::getDomain(const String &domName) const {
00369     assert(!domName.empty());
00370     assert(isValidDomainName(domName));
00371 
00372     if (domName == kTransientDomain)
00373         return &_transientDomain;
00374     if (domName == kApplicationDomain)
00375         return &_appDomain;
00376 #ifdef ENABLE_KEYMAPPER
00377     if (domName == kKeymapperDomain)
00378         return &_keymapperDomain;
00379 #endif
00380 #ifdef USE_CLOUD
00381     if (domName == kCloudDomain)
00382         return &_cloudDomain;
00383 #endif
00384     if (_gameDomains.contains(domName))
00385         return &_gameDomains[domName];
00386     if (_miscDomains.contains(domName))
00387         return &_miscDomains[domName];
00388 
00389     return nullptr;
00390 }
00391 
00392 ConfigManager::Domain *ConfigManager::getDomain(const String &domName) {
00393     assert(!domName.empty());
00394     assert(isValidDomainName(domName));
00395 
00396     if (domName == kTransientDomain)
00397         return &_transientDomain;
00398     if (domName == kApplicationDomain)
00399         return &_appDomain;
00400 #ifdef ENABLE_KEYMAPPER
00401     if (domName == kKeymapperDomain)
00402         return &_keymapperDomain;
00403 #endif
00404 #ifdef USE_CLOUD
00405     if (domName == kCloudDomain)
00406         return &_cloudDomain;
00407 #endif
00408     if (_gameDomains.contains(domName))
00409         return &_gameDomains[domName];
00410     if (_miscDomains.contains(domName))
00411         return &_miscDomains[domName];
00412 
00413     return nullptr;
00414 }
00415 
00416 
00417 #pragma mark -
00418 
00419 
00420 bool ConfigManager::hasKey(const String &key) const {
00421     // Search the domains in the following order:
00422     // 1) the transient domain,
00423     // 2) the active game domain (if any),
00424     // 3) the application domain.
00425     // The defaults domain is explicitly *not* checked.
00426 
00427     if (_transientDomain.contains(key))
00428         return true;
00429 
00430     if (_activeDomain && _activeDomain->contains(key))
00431         return true;
00432 
00433     if (_appDomain.contains(key))
00434         return true;
00435 
00436     return false;
00437 }
00438 
00439 bool ConfigManager::hasKey(const String &key, const String &domName) const {
00440     // FIXME: For now we continue to allow empty domName to indicate
00441     // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
00442     // and should be removed ASAP.
00443     if (domName.empty())
00444         return hasKey(key);
00445 
00446     const Domain *domain = getDomain(domName);
00447 
00448     if (!domain)
00449         return false;
00450     return domain->contains(key);
00451 }
00452 
00453 void ConfigManager::removeKey(const String &key, const String &domName) {
00454     Domain *domain = getDomain(domName);
00455 
00456     if (!domain)
00457         error("ConfigManager::removeKey(%s, %s) called on non-existent domain",
00458               key.c_str(), domName.c_str());
00459 
00460     domain->erase(key);
00461 }
00462 
00463 
00464 #pragma mark -
00465 
00466 
00467 const String &ConfigManager::get(const String &key) const {
00468     if (_transientDomain.contains(key))
00469         return _transientDomain[key];
00470     else if (_activeDomain && _activeDomain->contains(key))
00471         return (*_activeDomain)[key];
00472     else if (_appDomain.contains(key))
00473         return _appDomain[key];
00474 
00475     return _defaultsDomain.getVal(key);
00476 }
00477 
00478 const String &ConfigManager::get(const String &key, const String &domName) const {
00479     // FIXME: For now we continue to allow empty domName to indicate
00480     // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
00481     // and should be removed ASAP.
00482     if (domName.empty())
00483         return get(key);
00484 
00485     const Domain *domain = getDomain(domName);
00486 
00487     if (!domain)
00488         error("ConfigManager::get(%s,%s) called on non-existent domain",
00489               key.c_str(), domName.c_str());
00490 
00491     if (domain->contains(key))
00492         return (*domain)[key];
00493 
00494     return _defaultsDomain.getVal(key);
00495 }
00496 
00497 int ConfigManager::getInt(const String &key, const String &domName) const {
00498     String value(get(key, domName));
00499     char *errpos;
00500 
00501     // For now, be tolerant against missing config keys. Strictly spoken, it is
00502     // a bug in the calling code to retrieve an int for a key which isn't even
00503     // present... and a default value of 0 seems rather arbitrary.
00504     if (value.empty())
00505         return 0;
00506 
00507     // We use the special value '0' for the base passed to strtol. Doing that
00508     // makes it possible to enter hex values as "0x1234", but also decimal
00509     // values ("123") are still valid.
00510     int ivalue = (int)strtol(value.c_str(), &errpos, 0);
00511     if (value.c_str() == errpos)
00512         error("ConfigManager::getInt(%s,%s): '%s' is not a valid integer",
00513               key.c_str(), domName.c_str(), errpos);
00514 
00515     return ivalue;
00516 }
00517 
00518 bool ConfigManager::getBool(const String &key, const String &domName) const {
00519     String value(get(key, domName));
00520     bool val;
00521     if (parseBool(value, val))
00522         return val;
00523 
00524     error("ConfigManager::getBool(%s,%s): '%s' is not a valid bool",
00525           key.c_str(), domName.c_str(), value.c_str());
00526 }
00527 
00528 
00529 #pragma mark -
00530 
00531 
00532 void ConfigManager::set(const String &key, const String &value) {
00533     // Remove the transient domain value, if any.
00534     _transientDomain.erase(key);
00535 
00536     // Write the new key/value pair into the active domain, resp. into
00537     // the application domain if no game domain is active.
00538     if (_activeDomain)
00539         (*_activeDomain)[key] = value;
00540     else
00541         _appDomain[key] = value;
00542 }
00543 
00544 void ConfigManager::set(const String &key, const String &value, const String &domName) {
00545     // FIXME: For now we continue to allow empty domName to indicate
00546     // "use 'default' domain". This is mainly needed for the SCUMM ConfigDialog
00547     // and should be removed ASAP.
00548     if (domName.empty()) {
00549         set(key, value);
00550         return;
00551     }
00552 
00553     Domain *domain = getDomain(domName);
00554 
00555     if (!domain)
00556         error("ConfigManager::set(%s,%s,%s) called on non-existent domain",
00557               key.c_str(), value.c_str(), domName.c_str());
00558 
00559     (*domain)[key] = value;
00560 
00561     // TODO/FIXME: We used to erase the given key from the transient domain
00562     // here. Do we still want to do that?
00563     // It was probably there to simplify the options dialogs code:
00564     // Imagine you are editing the current options (via the SCUMM ConfigDialog,
00565     // for example). If you edit the game domain for that, but a matching
00566     // entry in the transient domain is present, than your changes may not take
00567     // effect. So you want to remove the key from the transient domain before
00568     // adding it to the active domain.
00569     // But doing this here seems rather evil... need to comb the options dialog
00570     // code to find out if it's still necessary, and if that's the case, how
00571     // to replace it in a clean fashion...
00572 #if 0
00573     if (domName == kTransientDomain)
00574         _transientDomain[key] = value;
00575     else {
00576         if (domName == kApplicationDomain) {
00577             _appDomain[key] = value;
00578             if (_activeDomainName.empty() || !_gameDomains[_activeDomainName].contains(key))
00579                 _transientDomain.erase(key);
00580         } else {
00581             _gameDomains[domName][key] = value;
00582             if (domName == _activeDomainName)
00583                 _transientDomain.erase(key);
00584         }
00585     }
00586 #endif
00587 }
00588 
00589 void ConfigManager::setInt(const String &key, int value, const String &domName) {
00590     set(key, String::format("%i", value), domName);
00591 }
00592 
00593 void ConfigManager::setBool(const String &key, bool value, const String &domName) {
00594     set(key, String(value ? "true" : "false"), domName);
00595 }
00596 
00597 
00598 #pragma mark -
00599 
00600 
00601 void ConfigManager::registerDefault(const String &key, const String &value) {
00602     _defaultsDomain[key] = value;
00603 }
00604 
00605 void ConfigManager::registerDefault(const String &key, const char *value) {
00606     registerDefault(key, String(value));
00607 }
00608 
00609 void ConfigManager::registerDefault(const String &key, int value) {
00610     registerDefault(key, String::format("%i", value));
00611 }
00612 
00613 void ConfigManager::registerDefault(const String &key, bool value) {
00614     registerDefault(key, value ? "true" : "false");
00615 }
00616 
00617 
00618 #pragma mark -
00619 
00620 
00621 void ConfigManager::setActiveDomain(const String &domName) {
00622     if (domName.empty()) {
00623         _activeDomain = nullptr;
00624     } else {
00625         assert(isValidDomainName(domName));
00626         _activeDomain = &_gameDomains[domName];
00627     }
00628     _activeDomainName = domName;
00629 }
00630 
00631 void ConfigManager::addGameDomain(const String &domName) {
00632     assert(!domName.empty());
00633     assert(isValidDomainName(domName));
00634 
00635     // TODO: Do we want to generate an error/warning if a domain with
00636     // the given name already exists?
00637 
00638     _gameDomains[domName];
00639 
00640     // Add it to the _domainSaveOrder, if it's not already in there
00641     if (find(_domainSaveOrder.begin(), _domainSaveOrder.end(), domName) == _domainSaveOrder.end())
00642         _domainSaveOrder.push_back(domName);
00643 }
00644 
00645 void ConfigManager::addMiscDomain(const String &domName) {
00646     assert(!domName.empty());
00647     assert(isValidDomainName(domName));
00648 
00649     _miscDomains[domName];
00650 }
00651 
00652 void ConfigManager::removeGameDomain(const String &domName) {
00653     assert(!domName.empty());
00654     assert(isValidDomainName(domName));
00655     if (domName == _activeDomainName) {
00656         _activeDomainName.clear();
00657         _activeDomain = nullptr;
00658     }
00659     _gameDomains.erase(domName);
00660 }
00661 
00662 void ConfigManager::removeMiscDomain(const String &domName) {
00663     assert(!domName.empty());
00664     assert(isValidDomainName(domName));
00665     _miscDomains.erase(domName);
00666 }
00667 
00668 
00669 void ConfigManager::renameGameDomain(const String &oldName, const String &newName) {
00670     renameDomain(oldName, newName, _gameDomains);
00671     if (_activeDomainName == oldName) {
00672         _activeDomainName = newName;
00673         _activeDomain = &_gameDomains[newName];
00674     }
00675 }
00676 
00677 void ConfigManager::renameMiscDomain(const String &oldName, const String &newName) {
00678     renameDomain(oldName, newName, _miscDomains);
00679 }
00680 
00684 void ConfigManager::renameDomain(const String &oldName, const String &newName, DomainMap &map) {
00685     if (oldName == newName)
00686         return;
00687 
00688     assert(!oldName.empty());
00689     assert(!newName.empty());
00690     assert(isValidDomainName(oldName));
00691     assert(isValidDomainName(newName));
00692 
00693 //  _gameDomains[newName].merge(_gameDomains[oldName]);
00694     Domain &oldDom = map[oldName];
00695     Domain &newDom = map[newName];
00696     Domain::const_iterator iter;
00697     for (iter = oldDom.begin(); iter != oldDom.end(); ++iter)
00698         newDom[iter->_key] = iter->_value;
00699 
00700     map.erase(oldName);
00701 }
00702 
00703 bool ConfigManager::hasGameDomain(const String &domName) const {
00704     assert(!domName.empty());
00705     return isValidDomainName(domName) && _gameDomains.contains(domName);
00706 }
00707 
00708 bool ConfigManager::hasMiscDomain(const String &domName) const {
00709     assert(!domName.empty());
00710     return isValidDomainName(domName) && _miscDomains.contains(domName);
00711 }
00712 
00713 #pragma mark -
00714 
00715 void ConfigManager::Domain::setDomainComment(const String &comment) {
00716     _domainComment = comment;
00717 }
00718 const String &ConfigManager::Domain::getDomainComment() const {
00719     return _domainComment;
00720 }
00721 
00722 void ConfigManager::Domain::setKVComment(const String &key, const String &comment) {
00723     _keyValueComments[key] = comment;
00724 }
00725 const String &ConfigManager::Domain::getKVComment(const String &key) const {
00726     return _keyValueComments[key];
00727 }
00728 bool ConfigManager::Domain::hasKVComment(const String &key) const {
00729     return _keyValueComments.contains(key);
00730 }
00731 
00732 } // End of namespace Common


Generated on Sat Feb 23 2019 05:00:59 for ResidualVM by doxygen 1.7.1
curved edge   curved edge