settings.cpp

Engine/source/util/settings.cpp

More...

Public Functions

ConsoleDocClass(Settings , "@brief Class used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> writing out preferences and settings <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editors\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
DefineEngineMethod(Settings , beginGroup , void , (const char *groupName, bool includeDefaults) , (false) , "settingObj.beginGroup(groupName, fromStart = false);" )
DefineEngineMethod(Settings , clearGroups , void , () , "settingObj.clearGroups();" )
DefineEngineMethod(Settings , endGroup , void , () , "settingObj.endGroup();" )
DefineEngineMethod(Settings , findFirstValue , const char * , (const char *pattern, bool deepSearch, bool includeDefaults) , ("", false, false) , "settingObj.findFirstValue();" )
DefineEngineMethod(Settings , findNextValue , const char * , () , "settingObj.findNextValue();" )
DefineEngineMethod(Settings , getCurrentGroups , const char * , () , "settingObj.getCurrentGroups();" )
DefineEngineMethod(Settings , read , bool , () )
DefineEngineMethod(Settings , remove , void , (const char *settingName, bool includeDefaults) , (false) , "settingObj.remove(settingName, includeDefaults = false);" )
DefineEngineMethod(Settings , setDefaultValue , void , (const char *settingName, const char *value) , "settingObj.setDefaultValue(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a>);" )
DefineEngineMethod(Settings , setValue , void , (const char *settingName, const char *value) , ("") , "settingObj.setValue(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a>);" )
DefineEngineMethod(Settings , value , const char * , (const char *settingName, const char *defaultValue) , ("") , "settingObj.value(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a3db30c1795eb39d4452f8698f4a67f05">defaultValue</a>);" )
DefineEngineMethod(Settings , write , bool , () )

Detailed Description

Public Functions

ConsoleDocClass(Settings , "@brief Class used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> writing out preferences and settings <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editors\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )

DefineEngineMethod(Settings , beginGroup , void , (const char *groupName, bool includeDefaults) , (false) , "settingObj.beginGroup(groupName, fromStart = false);" )

DefineEngineMethod(Settings , clearGroups , void , () , "settingObj.clearGroups();" )

DefineEngineMethod(Settings , endGroup , void , () , "settingObj.endGroup();" )

DefineEngineMethod(Settings , findFirstValue , const char * , (const char *pattern, bool deepSearch, bool includeDefaults) , ("", false, false) , "settingObj.findFirstValue();" )

DefineEngineMethod(Settings , findNextValue , const char * , () , "settingObj.findNextValue();" )

DefineEngineMethod(Settings , getCurrentGroups , const char * , () , "settingObj.getCurrentGroups();" )

DefineEngineMethod(Settings , read , bool , () )

DefineEngineMethod(Settings , remove , void , (const char *settingName, bool includeDefaults) , (false) , "settingObj.remove(settingName, includeDefaults = false);" )

DefineEngineMethod(Settings , setDefaultValue , void , (const char *settingName, const char *value) , "settingObj.setDefaultValue(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a>);" )

DefineEngineMethod(Settings , setValue , void , (const char *settingName, const char *value) , ("") , "settingObj.setValue(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a32aff7c6c4cd253fdf6563677afab5ce">value</a>);" )

DefineEngineMethod(Settings , value , const char * , (const char *settingName, const char *defaultValue) , ("") , "settingObj.value(settingName, <a href="/coding/file/pointer_8h/#pointer_8h_1a3db30c1795eb39d4452f8698f4a67f05">defaultValue</a>);" )

DefineEngineMethod(Settings , write , bool , () )

IMPLEMENT_CONOBJECT(Settings )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "util/settings.h"
 25#include "console/engineAPI.h"
 26#include "console/consoleTypes.h"
 27#include "console/SimXMLDocument.h"
 28
 29IMPLEMENT_CONOBJECT(Settings);
 30
 31ConsoleDocClass( Settings,
 32            "@brief Class used for writing out preferences and settings for editors\n\n"
 33            "Not intended for game development, for editors or internal use only.\n\n "
 34            "@internal");
 35
 36Settings::Settings()
 37{
 38   mFile = "";
 39   mSearchPos = 0;
 40}
 41
 42Settings::~Settings()
 43{
 44   
 45}
 46
 47void Settings::initPersistFields()
 48{
 49   addField("file", TypeStringFilename, Offset(mFile, Settings), "The file path and name to be saved to and loaded from.");
 50
 51   Parent::initPersistFields();
 52}
 53
 54void Settings::setDefaultValue(const UTF8 *settingName, const UTF8 *settingValue, const UTF8 *settingType)
 55{
 56   String baseName;
 57   buildGroupString(baseName, settingName);
 58   String name = baseName + "_default";
 59   StringTableEntry nameEntry = StringTable->insert(name.c_str());
 60   String type = baseName + "_type";
 61   StringTableEntry typeEntry = StringTable->insert(type.c_str());
 62
 63   setModStaticFields(false);
 64   setDataField(nameEntry, NULL, settingValue);
 65   setDataField(typeEntry, NULL, settingType);
 66   setModStaticFields(true);
 67}
 68
 69void Settings::setValue(const UTF8 *settingName, const UTF8 *settingValue)
 70{
 71   String name;
 72   buildGroupString(name, settingName);
 73   StringTableEntry nameEntry = StringTable->insert(name.c_str());
 74
 75   setModStaticFields(false);
 76   setDataField(nameEntry, NULL, settingValue);
 77   setModStaticFields(true);
 78}
 79
 80const UTF8 *Settings::value(const UTF8 *settingName, const UTF8 *defaultValue)
 81{
 82   String name;
 83   buildGroupString(name, settingName);
 84
 85   StringTableEntry nameEntry = StringTable->insert(name.c_str());
 86   name += "_default";
 87   StringTableEntry defaultNameEntry = StringTable->insert(name.c_str());
 88
 89   // we do this setModStaticFields call to make sure our get/set calls
 90   // don't grab a regular field, don't want to stomp anything
 91   setModStaticFields(false);  
 92   const UTF8 *value = getDataField(nameEntry, NULL);
 93   const UTF8 *storedDefaultValue = getDataField(defaultNameEntry, NULL);
 94   setModStaticFields(true);
 95
 96   if(String::compare(value, "") != 0)
 97      return value;
 98   else if(String::compare(storedDefaultValue, "") != 0)
 99      return storedDefaultValue;
100   else
101     return defaultValue;
102}
103
104void Settings::remove(const UTF8 *settingName, bool includeDefaults)
105{
106    // Fetch Dynamic-Field Dictionary.
107    SimFieldDictionary* pFieldDictionary = getFieldDictionary();
108
109    // Any Field Dictionary?
110    if ( pFieldDictionary == NULL )
111    {
112        // No, so we're done.
113        return;
114    }
115
116    String name;
117    buildGroupString(name, settingName);
118    StringTableEntry nameEntry = StringTable->insert(name.c_str());
119    StringTableEntry nameEntryDefault = StringTable->insert( String::ToString("%s%s",name.c_str(), "_default") );
120
121    // Iterate fields.
122    for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
123    {
124        // Fetch Field Entry.
125        SimFieldDictionary::Entry* fieldEntry = *itr;
126
127        // is this a field of our current group
128        if ( (String::compare(nameEntry, "") == 0) || 
129               String::compare( nameEntry, fieldEntry->slotName ) == 0 ||
130               (includeDefaults && String::compare( nameEntryDefault, fieldEntry->slotName ) == 0) )
131        {
132            // Yes, so remove it.
133            pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
134        }
135    }
136}
137
138void Settings::buildGroupString(String &name, const UTF8 *settingName)
139{
140   // here we want to loop through the stack and build a "/" seperated string
141   // representing the entire current group stack that gets pre-pended to the
142   // setting name passed in
143   if(mGroupStack.size() > 0)
144   {
145      for(S32 i=0; i < mGroupStack.size(); i++)
146      {
147         S32 pos = 0;
148         if(name.size() > 0)
149            pos = name.size()-1;
150
151         // tack on the "/" in front if this isn't the first
152         if(i == 0)
153         {
154            name.insert(pos, mGroupStack[i]);
155         }
156         else
157         {
158            name.insert(pos, "/");
159            name.insert(pos+1, mGroupStack[i]);
160         }
161      }
162
163     // tack on a final "/"
164     name.insert(name.size()-1, "/");
165     if(dStrlen(settingName) > 0)
166        name.insert(name.size()-1, settingName);
167   }
168   else
169   {
170      name = settingName;
171   }
172}
173
174void Settings::clearAllFields()
175{
176    // Fetch Dynamic-Field Dictionary.
177    SimFieldDictionary* pFieldDictionary = getFieldDictionary();
178
179    // Any Field Dictionary?
180    if ( pFieldDictionary == NULL )
181    {
182        // No, so we're done.
183        return;
184    }
185
186    // Iterate fields.
187    for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
188    {
189        // Fetch Field Entry.
190        SimFieldDictionary::Entry* fieldEntry = *itr;
191
192        // don't remove default field values
193        if (dStrEndsWith(fieldEntry->slotName, "_default"))
194           continue;
195
196        // remove it.
197        pFieldDictionary->setFieldValue( fieldEntry->slotName, "" );
198    }
199}
200
201bool Settings::write()
202{
203   // Fetch Dynamic-Field Dictionary.
204   SimFieldDictionary* pFieldDictionary = getFieldDictionary();
205
206   // Any Field Dictionary?
207   if ( pFieldDictionary == NULL )
208   {
209      // No, so we're done.
210      return false;
211   }
212
213/*
214   // Iterate fields.
215   for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
216   {
217      // Fetch Field Entry.
218      SimFieldDictionary::Entry* fieldEntry = *itr;
219
220     String check(fieldEntry->slotName);
221     String::SizeType pos = check.find("_default");
222     if(pos != String::NPos)
223       continue;
224
225      // let's build our XML doc
226      document->pushNewElement("dynamicField");
227     document->setAttribute("name", fieldEntry->slotName);
228     document->addText(fieldEntry->value);
229      document->popElement();
230   }
231*/
232   SimXMLDocument *document = new SimXMLDocument();
233   document->registerObject();
234   document->addHeader();
235
236   document->pushNewElement(getName());
237
238   SettingSaveNode *node = new SettingSaveNode();
239   // Iterate fields.
240   for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr )
241   {
242      // Fetch Field Entry.
243      SimFieldDictionary::Entry* fieldEntry = *itr;
244
245      String check(fieldEntry->slotName);
246      if(check.find("_default") != String::NPos || check.find("_type") != String::NPos)
247        continue;
248
249     node->addValue(fieldEntry->slotName, fieldEntry->value);
250   }
251
252   node->buildDocument(document, true);
253   node->clear();
254   delete node;
255
256   bool saved = document->saveFile(mFile.c_str());
257   document->deleteObject();
258
259   if(saved)
260      return true;
261   else
262      return false;   
263}
264
265bool Settings::read()
266{
267   SimXMLDocument *document = new SimXMLDocument();
268   document->registerObject();
269
270   bool success = true;
271   if(document->loadFile(mFile.c_str()))
272   {
273      clearAllFields();
274
275      // set our base element
276      if(document->pushFirstChildElement(getName()))
277      {
278         setModStaticFields(false);
279         readLayer(document);
280         setModStaticFields(true);
281      }
282      else
283         success = false;
284   }
285   else
286      success = false;
287
288   document->deleteObject();
289
290   return success;
291}
292
293void Settings::readLayer(SimXMLDocument *document, String groupStack)
294{
295   for(S32 i=0; document->pushChildElement(i); i++)
296   {
297     const UTF8 *type = document->elementValue();
298     const UTF8 *name = document->attribute("name");
299     const UTF8 *value = document->getText();
300     
301     if(String::compare(type, "Group") == 0)
302     {
303       String newStack = groupStack;
304
305       if(!groupStack.isEmpty())
306         newStack += "/";
307
308       newStack += name;
309         readLayer(document, newStack);
310     } else if(String::compare(type, "Setting") == 0)
311     {
312       String nameString = groupStack;
313         
314       if(!groupStack.isEmpty())
315          nameString += "/";
316
317         nameString += name;
318         setDataField(StringTable->insert(nameString.c_str()), NULL, value);
319     }
320     
321     document->popElement();
322   }
323}
324
325void Settings::beginGroup(const UTF8 *groupName, bool fromStart)
326{
327   // check if we want to clear the stack
328   if(fromStart)
329      clearGroups();
330
331   mGroupStack.push_back(String(groupName));
332}
333
334void Settings::endGroup()
335{
336   if(mGroupStack.size() > 0)
337      mGroupStack.pop_back();
338}
339
340void Settings::clearGroups()
341{
342   mGroupStack.clear();
343}
344
345const UTF8 *Settings::getCurrentGroups()
346{
347   // we want to return a string with our group setup
348   String returnString;
349   for(S32 i=0; i<mGroupStack.size(); i++)
350   {
351     S32 pos = returnString.size() - 1;
352     if(pos < 0)
353       pos = 0;
354
355     if(i == 0)
356     {
357         returnString.insert(pos, mGroupStack[i]);
358     } else
359     {
360       returnString.insert(pos, "/");
361         returnString.insert(pos+1, mGroupStack[i]);
362     }
363   }
364
365   return StringTable->insert(returnString.c_str());
366}
367/*
368S32 Settings::buildSearchList( const char* pattern, bool deepSearch, bool includeDefaults )
369{
370   mSearchResults.clear();
371
372   SimFieldDictionary* fieldDictionary = getFieldDictionary();
373   // Get the dynamic field count
374   if ( !fieldDictionary )
375      return -1;
376
377   for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
378   {
379      // Fetch Field Entry.
380      SimFieldDictionary::Entry* fieldEntry = *itr;
381      
382      // Compare strings, store proper results in vector
383      String extendedPath = String::ToString(fieldEntry->slotName);
384      String::SizeType start(0);
385      String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
386      String shortPath = extendedPath.substr( start, slashPos );
387
388      if( deepSearch )
389      {
390         if( shortPath.find( pattern ) != -1 )
391         {
392            if( !includeDefaults && extendedPath.find("_default") != -1 )
393               continue;
394
395            String listMember = String::ToString(fieldEntry->value);
396            listMember.insert(start, " " );
397            listMember.insert(start, String::ToString(fieldEntry->slotName) );
398
399            mSearchResults.push_back( listMember );
400         }
401      }
402      else
403      {
404         if( shortPath.compare( pattern ) == 0 )
405         {
406            if( !includeDefaults && extendedPath.find("_default") != -1 )
407               continue;
408
409            String listMember = String::ToString(fieldEntry->value);
410            listMember.insert(start, " " );
411            listMember.insert(start, String::ToString(fieldEntry->slotName) );
412
413            mSearchResults.push_back( listMember );
414         }
415      }
416   }
417
418   return mSearchResults.size();
419}
420*/
421const char* Settings::findFirstValue( const char* pattern, bool deepSearch, bool includeDefaults )
422{
423   mSearchResults.clear();
424
425   SimFieldDictionary* fieldDictionary = getFieldDictionary();
426   // Get the dynamic field count
427   if ( !fieldDictionary )
428      return "";
429
430   for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
431   {
432      // Fetch Field Entry.
433      SimFieldDictionary::Entry* fieldEntry = *itr;
434      
435      // Compare strings, store proper results in vector
436      String extendedPath = String::ToString(fieldEntry->slotName);
437      String::SizeType start(0);
438      String::SizeType slashPos = extendedPath.find('/', 0, String::Right);
439      String shortPath = extendedPath.substr( start, slashPos );
440
441      if( deepSearch )
442      {
443         if( shortPath.find( pattern ) != -1 )
444         {
445            if( !includeDefaults && extendedPath.find("_default") != -1 )
446               continue;
447
448            String listMember = String::ToString(fieldEntry->slotName);
449            //listMember.insert(start, " " );
450            //listMember.insert(start, String::ToString(fieldEntry->slotName) );
451
452            mSearchResults.push_back( listMember );
453         }
454      }
455      else
456      {
457         if( shortPath.compare( pattern ) == 0 )
458         {
459            if( !includeDefaults && extendedPath.find("_default") != -1 )
460               continue;
461
462            String listMember = String::ToString(fieldEntry->slotName);
463            //listMember.insert(start, " " );
464            //listMember.insert(start, String::ToString(fieldEntry->slotName) );
465
466            mSearchResults.push_back( listMember );
467         }
468      }
469   }
470   
471   if( mSearchResults.size() < 1 )
472   {
473      Con::errorf("findFirstValue() : Pattern not found");
474      return "";
475   }
476
477   mSearchPos = 0;
478   return mSearchResults[mSearchPos];
479}
480
481const char* Settings::findNextValue()
482{
483   if ( mSearchPos + 1 >= mSearchResults.size() )
484      return "";
485   mSearchPos++;
486   return mSearchResults[mSearchPos];
487}
488
489// make sure to replace the strings
490DefineEngineMethod(Settings, findFirstValue, const char*, ( const char* pattern, bool deepSearch, bool includeDefaults ), ("", false, false), "settingObj.findFirstValue();")
491{
492   return object->findFirstValue( pattern, deepSearch, includeDefaults );
493}
494
495DefineEngineMethod(Settings, findNextValue, const char*, (), , "settingObj.findNextValue();")
496{
497   return object->findNextValue();
498}
499/*
500ConsoleMethod(Settings, buildSearchList, void, 2, 2, "settingObj.buildSearchList();")
501{
502   object->buildSearchList( "foobar" );
503}
504*/
505void SettingSaveNode::addValue(const UTF8 *name, const UTF8 *value)
506{
507   String nameString(name);
508   S32 groupCount = getGroupCount(nameString);
509   SettingSaveNode *parentNode = this;
510
511   // let's check to make sure all these groups exist already
512   for(S32 i=0; i<groupCount; i++)
513   {
514      String groupName = getGroup(nameString, i);
515     if(!groupName.isEmpty())
516     {
517       bool found = false;
518         // loop through all of our nodes to find if this one exists,
519       // if it does we want to use it
520         for(S32 j=0; j<parentNode->mGroupNodes.size(); j++)
521       {
522            SettingSaveNode *node = parentNode->mGroupNodes[j];
523
524            if(!node->mIsGroup)
525            continue;
526
527            if(node->mName.compare(groupName) == 0)
528         {
529            parentNode = node;
530            found = true;
531               break;
532         }
533       }
534         
535       // not found, so we create it
536       if(!found)
537       {   
538         SettingSaveNode *node = new SettingSaveNode(groupName, true);
539            parentNode->mGroupNodes.push_back(node);
540          parentNode = node;
541       }
542     }
543   }
544
545   // now we can properly set our actual value
546   String settingNameString = getSettingName(name);
547   String valueString(value);
548   SettingSaveNode *node = new SettingSaveNode(settingNameString, valueString);
549   parentNode->mSettingNodes.push_back(node);
550}
551
552S32 SettingSaveNode::getGroupCount(const String &name)
553{
554   String::SizeType pos = 0;
555   S32 count = 0;
556
557   // loop through and count our exiting groups
558   while(pos != String::NPos)
559   {
560     pos = name.find("/", pos + 1);
561     if(pos != String::NPos)
562        count++;
563   }
564
565   return count;
566}
567
568String SettingSaveNode::getGroup(const String &name, S32 num)
569{
570   String::SizeType pos = 0;
571   String::SizeType lastPos = 0;
572   S32 count = 0;
573
574   while(pos != String::NPos)
575   {
576     lastPos = pos;
577     pos = name.find("/", pos + 1);
578
579      if(count == num)
580     {
581       String::SizeType startPos = lastPos;
582
583         if(count > 0)
584         startPos++;
585
586       if(pos == String::NPos)
587          return name.substr(startPos, name.length() - (startPos));
588       else
589            return name.substr(startPos, pos - startPos);
590     }
591
592     count++;
593   }   
594
595   return String("");
596}
597
598String SettingSaveNode::getSettingName(const String &name)
599{
600   String::SizeType pos = name.find("/", 0, String::Right);
601
602   if(pos == String::NPos)
603     return String(name);
604   else
605     return name.substr(pos+1, name.length() - (pos+1));
606}
607
608void SettingSaveNode::clear()
609{
610   for( U32 i = 0, num = mGroupNodes.size(); i < num; ++ i )
611      delete mGroupNodes[ i ];
612   for( U32 i = 0, num = mSettingNodes.size(); i < num; ++ i )
613      delete mSettingNodes[ i ];
614
615   mGroupNodes.clear();
616   mSettingNodes.clear();
617}
618
619void SettingSaveNode::buildDocument(SimXMLDocument *document, bool skipWrite)
620{
621   // let's build our XML doc
622   if(mIsGroup && !skipWrite)
623   {
624      document->pushNewElement("Group");
625      document->setAttribute("name", mName);
626   }
627
628   if(!mIsGroup && !skipWrite)
629   {
630      document->pushNewElement("Setting");
631      document->setAttribute("name", mName);
632      document->addText(mValue);
633   }
634   else
635   {
636      mSettingNodes.sort(_NodeCompare);
637      mGroupNodes.sort(_NodeCompare);
638
639     for(S32 i=0; i<mSettingNodes.size(); i++)
640     {
641         SettingSaveNode *node = mSettingNodes[i];
642       node->buildDocument(document);
643     }
644
645      for(S32 i=0; i<mGroupNodes.size(); i++)
646     {
647         SettingSaveNode *node = mGroupNodes[i];
648       node->buildDocument(document);
649     }
650   }
651   
652   if(!skipWrite)
653      document->popElement();
654}
655
656S32 QSORT_CALLBACK SettingSaveNode::_NodeCompare(SettingSaveNode* const* a, SettingSaveNode* const* b)
657{
658   S32 result = dStrnatcasecmp((*a)->mName.c_str(), (*b)->mName.c_str());
659   return result;
660}
661
662DefineEngineMethod(Settings, setValue, void, (const char * settingName, const char * value), (""), "settingObj.setValue(settingName, value);")
663{
664   StringTableEntry fieldName = StringTable->insert( settingName );
665   
666   if (!String::isEmpty(value))
667      object->setValue( fieldName, value );
668   else
669      object->setValue( fieldName );
670}
671
672DefineEngineMethod(Settings, setDefaultValue, void, (const char * settingName, const char * value), , "settingObj.setDefaultValue(settingName, value);")
673{
674   StringTableEntry fieldName = StringTable->insert( settingName );
675   object->setDefaultValue( fieldName, value );
676}
677
678DefineEngineMethod(Settings, value, const char*, (const char * settingName, const char * defaultValue), (""), "settingObj.value(settingName, defaultValue);")
679{
680   StringTableEntry fieldName = StringTable->insert( settingName );
681   
682   if (String::compare(defaultValue, "") != 0)
683      return object->value( fieldName, defaultValue );
684   else if (String::compare(settingName, "") != 0)
685      return object->value( fieldName );
686
687   return "";
688}
689
690DefineEngineMethod(Settings, remove, void, (const char * settingName, bool includeDefaults), (false), "settingObj.remove(settingName, includeDefaults = false);")
691{
692   // there's a problem with some fields not being removed properly, but works if you run it twice,
693   // a temporary solution for now is simply to call the remove twice
694
695   object->remove( settingName, includeDefaults );
696   object->remove( settingName, includeDefaults );
697}
698
699DefineEngineMethod(Settings, write, bool, (),, "%success = settingObj.write();")
700{
701   return object->write();
702}
703
704DefineEngineMethod(Settings, read, bool, (), , "%success = settingObj.read();")
705{
706   return object->read();
707}
708
709DefineEngineMethod(Settings, beginGroup, void, (const char * groupName, bool includeDefaults), (false), "settingObj.beginGroup(groupName, fromStart = false);")
710{
711   object->beginGroup( groupName, includeDefaults );
712}
713
714DefineEngineMethod(Settings, endGroup, void, (), , "settingObj.endGroup();")
715{
716   object->endGroup();
717}
718
719DefineEngineMethod(Settings, clearGroups, void, (), , "settingObj.clearGroups();")
720{
721   object->clearGroups();
722}
723
724DefineEngineMethod(Settings, getCurrentGroups, const char*, (), , "settingObj.getCurrentGroups();")
725{
726   return object->getCurrentGroups();
727}
728