1 // 2 // Copyright 2005 The Android Open Source Project 3 // 4 // Preferences file access. 5 // 6 7 // For compilers that support precompilation, include "wx/wx.h". 8 #include "wx/wxprec.h" 9 // Otherwise, include all standard headers 10 #ifndef WX_PRECOMP 11 //# include "wx/wx.h" 12 # include "wx/string.h" 13 #endif 14 15 #include "Preferences.h" 16 17 #include "utils.h" 18 #include "tinyxml.h" 19 20 static const char* kName = "name"; 21 static const char* kValue = "value"; 22 23 24 /* 25 * Load from a file. 26 */ 27 bool Preferences::Load(const char* fileName) 28 { 29 assert(fileName != NULL); 30 printf("SimPref: reading preferences file '%s'\n", fileName); 31 32 // throw out any existing stuff 33 delete mpDoc; 34 35 mpDoc = new TiXmlDocument; 36 if (mpDoc == NULL) 37 return false; 38 39 if (!mpDoc->LoadFile(fileName)) { 40 fprintf(stderr, "SimPref: ERROR: failed loading '%s'\n", fileName); 41 if (mpDoc->ErrorRow() != 0) 42 fprintf(stderr, " XML: %s (row=%d col=%d)\n", 43 mpDoc->ErrorDesc(), mpDoc->ErrorRow(), mpDoc->ErrorCol()); 44 else 45 fprintf(stderr, " XML: %s\n", mpDoc->ErrorDesc()); 46 goto fail; 47 } 48 49 TiXmlNode* pPrefs; 50 pPrefs = mpDoc->FirstChild("prefs"); 51 if (pPrefs == NULL) { 52 fprintf(stderr, "SimPref: ERROR: could not find <prefs> in '%s'\n", 53 fileName); 54 goto fail; 55 } 56 57 // set defaults for anything we haven't set explicitly 58 SetDefaults(); 59 60 return true; 61 62 fail: 63 delete mpDoc; 64 mpDoc = NULL; 65 return false; 66 } 67 68 /* 69 * Save to a file. 70 */ 71 bool Preferences::Save(const char* fileName) 72 { 73 assert(fileName != NULL); 74 75 if (mpDoc == NULL) 76 return false; 77 78 if (!mpDoc->SaveFile(fileName)) { 79 fprintf(stderr, "SimPref: ERROR: failed saving '%s': %s\n", 80 fileName, mpDoc->ErrorDesc()); 81 return false; 82 } 83 84 mDirty = false; 85 86 return true; 87 } 88 89 /* 90 * Create an empty collection of preferences. 91 */ 92 bool Preferences::Create(void) 93 { 94 static const char* docBase = 95 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" 96 "<!-- Android device simulator preferences -->\n" 97 "<!-- This file is updated by the simulator -->\n" 98 "<prefs>\n" 99 "</prefs>\n"; 100 101 // throw out any existing stuff 102 delete mpDoc; 103 104 // alloc and initialize 105 mpDoc = new TiXmlDocument; 106 if (mpDoc == NULL) 107 return false; 108 109 if (!mpDoc->Parse(docBase)) { 110 fprintf(stderr, "SimPref: bad docBase: %s\n", mpDoc->ErrorDesc()); 111 return false; 112 } 113 114 SetDefaults(); 115 mDirty = true; // should already be, mbut make sure 116 return true; 117 } 118 119 /* 120 * Add default values to XML doc. 121 * 122 * This isn't strictly necessary, because the functions that are interested 123 * in the preferences can set appropriate defaults themselves when the 124 * "get" function returns "false". However, in some cases a preference 125 * can be interesting to more than one function, and you either have to 126 * cut & paste the default value or write a "get default for xxx" function. 127 * 128 * We want this to work even if they already have an older config file, so 129 * this only sets values that don't already exist. 130 */ 131 void Preferences::SetDefaults(void) 132 { 133 /* table of default values */ 134 static const struct { 135 const char* type; 136 const char* name; 137 const char* value; 138 } kDefault[] = { 139 { "pref", "auto-power-on", "true" }, 140 { "pref", "debug", "false" }, 141 { "pref", "valgrind", "false" }, 142 { "pref", "check-jni", "true" }, 143 { "pref", "enable-sound", "true" }, 144 { "pref", "enable-fake-camera", "true" }, 145 { "pref", "java-vm", "Dalvik" }, 146 /* goobuntu dapper needed LD_ASSUME_KERNEL or gdb choked badly */ 147 { "pref", "ld-assume-kernel", "" /*2.4.19*/ }, 148 { "pref", "launch-command", 149 "xterm -geom 80x60+10+10 -sb -title Simulator -e" }, 150 { "pref", "launch-wrapper-args", "-wait" }, 151 }; 152 TiXmlNode* pPrefs; 153 154 assert(mpDoc != NULL); 155 156 pPrefs = mpDoc->FirstChild("prefs"); 157 158 /* 159 * Look up the name. If it doesn't exist, add it. 160 */ 161 for (int i = 0; i < NELEM(kDefault); i++) { 162 TiXmlNode* pNode = _FindNode(kDefault[i].type, kDefault[i].name); 163 164 if (pNode == NULL) { 165 TiXmlElement elem(kDefault[i].type); 166 elem.SetAttribute(kName, kDefault[i].name); 167 elem.SetAttribute(kValue, kDefault[i].value); 168 pPrefs->InsertEndChild(elem); 169 170 printf("SimPref: added default <%s> '%s'='%s'\n", 171 kDefault[i].type, kDefault[i].name, kDefault[i].value); 172 } else { 173 printf("SimPref: found existing <%s> '%s'\n", 174 kDefault[i].type, kDefault[i].name); 175 } 176 } 177 } 178 179 static TiXmlNode* get_next_node(TiXmlNode* pNode) 180 { 181 if (!pNode->NoChildren()) 182 { 183 pNode = pNode->FirstChild(); 184 } 185 else if (pNode->NoChildren() && 186 (pNode->NextSibling() == NULL)) 187 { 188 pNode = pNode->Parent()->NextSibling(); 189 } 190 else 191 { 192 pNode = pNode->NextSibling(); 193 } 194 return pNode; 195 } 196 197 /* 198 * Returns the node with element type and name specified 199 * 200 * WARNING: this searches through the tree and returns the first matching 201 * node. 202 */ 203 TiXmlNode* Preferences::_FindNode(const char* type, const char* str) const 204 { 205 assert((type != NULL) && (str != NULL)); 206 TiXmlNode* pRoot; 207 TiXmlNode* pNode; 208 209 pRoot = mpDoc->FirstChild("prefs"); 210 assert(pRoot != NULL); 211 212 for (pNode = pRoot->FirstChild(); pNode != NULL;) 213 { 214 if (pNode->Type() != TiXmlNode::ELEMENT || 215 strcasecmp(pNode->Value(), type) != 0) 216 { 217 pNode = get_next_node(pNode); 218 continue; 219 } 220 221 TiXmlElement* pElem = pNode->ToElement(); 222 assert(pElem != NULL); 223 224 const char* name = pElem->Attribute(kName); 225 226 /* 1. If the name is blank, something is wrong with the config file 227 * 2. If the name matches the passed in string, we found the node 228 * 3. If the node has children, descend another level 229 * 4. If there are no children and no siblings of the node, go up a level 230 * 5. Otherwise, grab the next sibling 231 */ 232 if (name == NULL) 233 { 234 fprintf(stderr, "WARNING: found <%s> without name\n", type); 235 continue; 236 } 237 else if (strcasecmp(name, str) == 0) 238 { 239 return pNode; 240 } 241 else 242 { 243 pNode = get_next_node(pNode); 244 } 245 } 246 247 return NULL; 248 } 249 250 /* 251 * Locate the specified preference. 252 */ 253 TiXmlNode* Preferences::FindPref(const char* str) const 254 { 255 TiXmlNode* pNode = _FindNode("pref", str); 256 return pNode; 257 } 258 259 /* 260 * Like FindPref(), but returns a TiXmlElement. 261 */ 262 TiXmlElement* Preferences::FindPrefElement(const char* str) const 263 { 264 TiXmlNode* pNode; 265 266 pNode = FindPref(str); 267 if (pNode != NULL) 268 return pNode->ToElement(); 269 return NULL; 270 } 271 272 /* 273 * Add a new preference entry with a blank entry for value. Returns a 274 * pointer to the new element. 275 */ 276 TiXmlElement* Preferences::AddPref(const char* str) 277 { 278 assert(FindPref(str) == NULL); 279 280 TiXmlNode* pPrefs; 281 282 pPrefs = mpDoc->FirstChild("prefs"); 283 assert(pPrefs != NULL); 284 285 TiXmlElement elem("pref"); 286 elem.SetAttribute(kName, str); 287 elem.SetAttribute(kValue, ""); 288 pPrefs->InsertEndChild(elem); 289 290 TiXmlNode* pNewPref = FindPref(str); 291 return pNewPref->ToElement(); 292 } 293 294 /* 295 * Remove a node from the tree 296 */ 297 bool Preferences::_RemoveNode(TiXmlNode* pNode) 298 { 299 if (pNode == NULL) 300 return false; 301 302 TiXmlNode* pParent = pNode->Parent(); 303 if (pParent == NULL) 304 return false; 305 306 pParent->RemoveChild(pNode); 307 mDirty = true; 308 return true; 309 } 310 311 /* 312 * Remove a preference entry. 313 */ 314 bool Preferences::RemovePref(const char* delName) 315 { 316 return _RemoveNode(FindPref(delName)); 317 } 318 319 /* 320 * Test for existence. 321 */ 322 bool Preferences::Exists(const char* name) const 323 { 324 TiXmlElement* pElem = FindPrefElement(name); 325 return (pElem != NULL); 326 } 327 328 /* 329 * Internal implemenations for getting values 330 */ 331 bool Preferences::_GetBool(TiXmlElement* pElem, bool* pVal) const 332 { 333 if (pElem != NULL) 334 { 335 const char* str = pElem->Attribute(kValue); 336 if (str != NULL) 337 { 338 if (strcasecmp(str, "true") == 0) 339 *pVal = true; 340 else if (strcasecmp(str, "false") == 0) 341 *pVal = false; 342 else 343 { 344 printf("SimPref: evaluating as bool name='%s' val='%s'\n", 345 pElem->Attribute(kName), str); 346 return false; 347 } 348 return true; 349 } 350 } 351 return false; 352 } 353 354 bool Preferences::_GetInt(TiXmlElement* pElem, int* pInt) const 355 { 356 int val; 357 if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) { 358 *pInt = val; 359 return true; 360 } 361 return false; 362 } 363 364 bool Preferences::_GetDouble(TiXmlElement* pElem, double* pDouble) const 365 { 366 double val; 367 if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) { 368 *pDouble = val; 369 return true; 370 } 371 return false; 372 } 373 374 bool Preferences::_GetString(TiXmlElement* pElem, wxString& str) const 375 { 376 const char* val; 377 if (pElem != NULL) { 378 val = pElem->Attribute(kValue); 379 if (val != NULL) { 380 str = wxString::FromAscii(val); 381 return true; 382 } 383 } 384 return false; 385 } 386 387 /* 388 * Get a value. Do not disturb "*pVal" unless we have something to return. 389 */ 390 bool Preferences::GetBool(const char* name, bool* pVal) const 391 { 392 return _GetBool(FindPrefElement(name), pVal); 393 } 394 395 bool Preferences::GetInt(const char* name, int* pInt) const 396 { 397 return _GetInt(FindPrefElement(name), pInt); 398 } 399 400 bool Preferences::GetDouble(const char* name, double* pDouble) const 401 { 402 return _GetDouble(FindPrefElement(name), pDouble); 403 } 404 405 bool Preferences::GetString(const char* name, char** pVal) const 406 { 407 wxString str = wxString::FromAscii(*pVal); 408 if (_GetString(FindPrefElement(name), str)) 409 { 410 *pVal = android::strdupNew(str.ToAscii()); 411 return true; 412 } 413 return false; 414 } 415 416 bool Preferences::GetString(const char* name, wxString& str) const 417 { 418 return _GetString(FindPrefElement(name), str); 419 } 420 421 /* 422 * Set a value. If the preference already exists, and the value hasn't 423 * changed, don't do anything. This avoids setting the "dirty" flag 424 * unnecessarily. 425 */ 426 void Preferences::SetBool(const char* name, bool val) 427 { 428 bool oldVal; 429 if (GetBool(name, &oldVal) && val == oldVal) 430 return; 431 432 SetString(name, val ? "true" : "false"); 433 mDirty = true; 434 } 435 436 void Preferences::SetInt(const char* name, int val) 437 { 438 int oldVal; 439 if (GetInt(name, &oldVal) && val == oldVal) 440 return; 441 442 TiXmlElement* pElem = FindPrefElement(name); 443 if (pElem == NULL) 444 pElem = AddPref(name); 445 pElem->SetAttribute(kValue, val); 446 mDirty = true; 447 } 448 449 void Preferences::SetDouble(const char* name, double val) 450 { 451 double oldVal; 452 if (GetDouble(name, &oldVal) && val == oldVal) 453 return; 454 455 TiXmlElement* pElem = FindPrefElement(name); 456 if (pElem == NULL) 457 pElem = AddPref(name); 458 pElem->SetDoubleAttribute(kValue, val); 459 mDirty = true; 460 } 461 462 void Preferences::SetString(const char* name, const char* val) 463 { 464 wxString oldVal; 465 if (GetString(name, /*ref*/oldVal) && strcmp(oldVal.ToAscii(), val) == 0) 466 return; 467 468 TiXmlElement* pElem = FindPrefElement(name); 469 if (pElem == NULL) 470 pElem = AddPref(name); 471 pElem->SetAttribute(kValue, val); 472 mDirty = true; 473 } 474 475 476