| // |
| // Copyright 2005 The Android Open Source Project |
| // |
| // Preferences file access. |
| // |
| |
| // For compilers that support precompilation, include "wx/wx.h". |
| #include "wx/wxprec.h" |
| // Otherwise, include all standard headers |
| #ifndef WX_PRECOMP |
| //# include "wx/wx.h" |
| # include "wx/string.h" |
| #endif |
| |
| #include "Preferences.h" |
| |
| #include "utils.h" |
| #include "tinyxml.h" |
| |
| static const char* kName = "name"; |
| static const char* kValue = "value"; |
| |
| |
| /* |
| * Load from a file. |
| */ |
| bool Preferences::Load(const char* fileName) |
| { |
| assert(fileName != NULL); |
| printf("SimPref: reading preferences file '%s'\n", fileName); |
| |
| // throw out any existing stuff |
| delete mpDoc; |
| |
| mpDoc = new TiXmlDocument; |
| if (mpDoc == NULL) |
| return false; |
| |
| if (!mpDoc->LoadFile(fileName)) { |
| fprintf(stderr, "SimPref: ERROR: failed loading '%s'\n", fileName); |
| if (mpDoc->ErrorRow() != 0) |
| fprintf(stderr, " XML: %s (row=%d col=%d)\n", |
| mpDoc->ErrorDesc(), mpDoc->ErrorRow(), mpDoc->ErrorCol()); |
| else |
| fprintf(stderr, " XML: %s\n", mpDoc->ErrorDesc()); |
| goto fail; |
| } |
| |
| TiXmlNode* pPrefs; |
| pPrefs = mpDoc->FirstChild("prefs"); |
| if (pPrefs == NULL) { |
| fprintf(stderr, "SimPref: ERROR: could not find <prefs> in '%s'\n", |
| fileName); |
| goto fail; |
| } |
| |
| // set defaults for anything we haven't set explicitly |
| SetDefaults(); |
| |
| return true; |
| |
| fail: |
| delete mpDoc; |
| mpDoc = NULL; |
| return false; |
| } |
| |
| /* |
| * Save to a file. |
| */ |
| bool Preferences::Save(const char* fileName) |
| { |
| assert(fileName != NULL); |
| |
| if (mpDoc == NULL) |
| return false; |
| |
| if (!mpDoc->SaveFile(fileName)) { |
| fprintf(stderr, "SimPref: ERROR: failed saving '%s': %s\n", |
| fileName, mpDoc->ErrorDesc()); |
| return false; |
| } |
| |
| mDirty = false; |
| |
| return true; |
| } |
| |
| /* |
| * Create an empty collection of preferences. |
| */ |
| bool Preferences::Create(void) |
| { |
| static const char* docBase = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" |
| "<!-- Android device simulator preferences -->\n" |
| "<!-- This file is updated by the simulator -->\n" |
| "<prefs>\n" |
| "</prefs>\n"; |
| |
| // throw out any existing stuff |
| delete mpDoc; |
| |
| // alloc and initialize |
| mpDoc = new TiXmlDocument; |
| if (mpDoc == NULL) |
| return false; |
| |
| if (!mpDoc->Parse(docBase)) { |
| fprintf(stderr, "SimPref: bad docBase: %s\n", mpDoc->ErrorDesc()); |
| return false; |
| } |
| |
| SetDefaults(); |
| mDirty = true; // should already be, mbut make sure |
| return true; |
| } |
| |
| /* |
| * Add default values to XML doc. |
| * |
| * This isn't strictly necessary, because the functions that are interested |
| * in the preferences can set appropriate defaults themselves when the |
| * "get" function returns "false". However, in some cases a preference |
| * can be interesting to more than one function, and you either have to |
| * cut & paste the default value or write a "get default for xxx" function. |
| * |
| * We want this to work even if they already have an older config file, so |
| * this only sets values that don't already exist. |
| */ |
| void Preferences::SetDefaults(void) |
| { |
| /* table of default values */ |
| static const struct { |
| const char* type; |
| const char* name; |
| const char* value; |
| } kDefault[] = { |
| { "pref", "auto-power-on", "true" }, |
| { "pref", "debug", "false" }, |
| { "pref", "valgrind", "false" }, |
| { "pref", "check-jni", "true" }, |
| { "pref", "enable-sound", "true" }, |
| { "pref", "enable-fake-camera", "true" }, |
| { "pref", "java-vm", "Dalvik" }, |
| /* goobuntu dapper needed LD_ASSUME_KERNEL or gdb choked badly */ |
| { "pref", "ld-assume-kernel", "" /*2.4.19*/ }, |
| { "pref", "launch-command", |
| "xterm -geom 80x60+10+10 -sb -title Simulator -e" }, |
| { "pref", "launch-wrapper-args", "-wait" }, |
| }; |
| TiXmlNode* pPrefs; |
| |
| assert(mpDoc != NULL); |
| |
| pPrefs = mpDoc->FirstChild("prefs"); |
| |
| /* |
| * Look up the name. If it doesn't exist, add it. |
| */ |
| for (int i = 0; i < NELEM(kDefault); i++) { |
| TiXmlNode* pNode = _FindNode(kDefault[i].type, kDefault[i].name); |
| |
| if (pNode == NULL) { |
| TiXmlElement elem(kDefault[i].type); |
| elem.SetAttribute(kName, kDefault[i].name); |
| elem.SetAttribute(kValue, kDefault[i].value); |
| pPrefs->InsertEndChild(elem); |
| |
| printf("SimPref: added default <%s> '%s'='%s'\n", |
| kDefault[i].type, kDefault[i].name, kDefault[i].value); |
| } else { |
| printf("SimPref: found existing <%s> '%s'\n", |
| kDefault[i].type, kDefault[i].name); |
| } |
| } |
| } |
| |
| static TiXmlNode* get_next_node(TiXmlNode* pNode) |
| { |
| if (!pNode->NoChildren()) |
| { |
| pNode = pNode->FirstChild(); |
| } |
| else if (pNode->NoChildren() && |
| (pNode->NextSibling() == NULL)) |
| { |
| pNode = pNode->Parent()->NextSibling(); |
| } |
| else |
| { |
| pNode = pNode->NextSibling(); |
| } |
| return pNode; |
| } |
| |
| /* |
| * Returns the node with element type and name specified |
| * |
| * WARNING: this searches through the tree and returns the first matching |
| * node. |
| */ |
| TiXmlNode* Preferences::_FindNode(const char* type, const char* str) const |
| { |
| assert((type != NULL) && (str != NULL)); |
| TiXmlNode* pRoot; |
| TiXmlNode* pNode; |
| |
| pRoot = mpDoc->FirstChild("prefs"); |
| assert(pRoot != NULL); |
| |
| for (pNode = pRoot->FirstChild(); pNode != NULL;) |
| { |
| if (pNode->Type() != TiXmlNode::ELEMENT || |
| strcasecmp(pNode->Value(), type) != 0) |
| { |
| pNode = get_next_node(pNode); |
| continue; |
| } |
| |
| TiXmlElement* pElem = pNode->ToElement(); |
| assert(pElem != NULL); |
| |
| const char* name = pElem->Attribute(kName); |
| |
| /* 1. If the name is blank, something is wrong with the config file |
| * 2. If the name matches the passed in string, we found the node |
| * 3. If the node has children, descend another level |
| * 4. If there are no children and no siblings of the node, go up a level |
| * 5. Otherwise, grab the next sibling |
| */ |
| if (name == NULL) |
| { |
| fprintf(stderr, "WARNING: found <%s> without name\n", type); |
| continue; |
| } |
| else if (strcasecmp(name, str) == 0) |
| { |
| return pNode; |
| } |
| else |
| { |
| pNode = get_next_node(pNode); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * Locate the specified preference. |
| */ |
| TiXmlNode* Preferences::FindPref(const char* str) const |
| { |
| TiXmlNode* pNode = _FindNode("pref", str); |
| return pNode; |
| } |
| |
| /* |
| * Like FindPref(), but returns a TiXmlElement. |
| */ |
| TiXmlElement* Preferences::FindPrefElement(const char* str) const |
| { |
| TiXmlNode* pNode; |
| |
| pNode = FindPref(str); |
| if (pNode != NULL) |
| return pNode->ToElement(); |
| return NULL; |
| } |
| |
| /* |
| * Add a new preference entry with a blank entry for value. Returns a |
| * pointer to the new element. |
| */ |
| TiXmlElement* Preferences::AddPref(const char* str) |
| { |
| assert(FindPref(str) == NULL); |
| |
| TiXmlNode* pPrefs; |
| |
| pPrefs = mpDoc->FirstChild("prefs"); |
| assert(pPrefs != NULL); |
| |
| TiXmlElement elem("pref"); |
| elem.SetAttribute(kName, str); |
| elem.SetAttribute(kValue, ""); |
| pPrefs->InsertEndChild(elem); |
| |
| TiXmlNode* pNewPref = FindPref(str); |
| return pNewPref->ToElement(); |
| } |
| |
| /* |
| * Remove a node from the tree |
| */ |
| bool Preferences::_RemoveNode(TiXmlNode* pNode) |
| { |
| if (pNode == NULL) |
| return false; |
| |
| TiXmlNode* pParent = pNode->Parent(); |
| if (pParent == NULL) |
| return false; |
| |
| pParent->RemoveChild(pNode); |
| mDirty = true; |
| return true; |
| } |
| |
| /* |
| * Remove a preference entry. |
| */ |
| bool Preferences::RemovePref(const char* delName) |
| { |
| return _RemoveNode(FindPref(delName)); |
| } |
| |
| /* |
| * Test for existence. |
| */ |
| bool Preferences::Exists(const char* name) const |
| { |
| TiXmlElement* pElem = FindPrefElement(name); |
| return (pElem != NULL); |
| } |
| |
| /* |
| * Internal implemenations for getting values |
| */ |
| bool Preferences::_GetBool(TiXmlElement* pElem, bool* pVal) const |
| { |
| if (pElem != NULL) |
| { |
| const char* str = pElem->Attribute(kValue); |
| if (str != NULL) |
| { |
| if (strcasecmp(str, "true") == 0) |
| *pVal = true; |
| else if (strcasecmp(str, "false") == 0) |
| *pVal = false; |
| else |
| { |
| printf("SimPref: evaluating as bool name='%s' val='%s'\n", |
| pElem->Attribute(kName), str); |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool Preferences::_GetInt(TiXmlElement* pElem, int* pInt) const |
| { |
| int val; |
| if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) { |
| *pInt = val; |
| return true; |
| } |
| return false; |
| } |
| |
| bool Preferences::_GetDouble(TiXmlElement* pElem, double* pDouble) const |
| { |
| double val; |
| if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) { |
| *pDouble = val; |
| return true; |
| } |
| return false; |
| } |
| |
| bool Preferences::_GetString(TiXmlElement* pElem, wxString& str) const |
| { |
| const char* val; |
| if (pElem != NULL) { |
| val = pElem->Attribute(kValue); |
| if (val != NULL) { |
| str = wxString::FromAscii(val); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * Get a value. Do not disturb "*pVal" unless we have something to return. |
| */ |
| bool Preferences::GetBool(const char* name, bool* pVal) const |
| { |
| return _GetBool(FindPrefElement(name), pVal); |
| } |
| |
| bool Preferences::GetInt(const char* name, int* pInt) const |
| { |
| return _GetInt(FindPrefElement(name), pInt); |
| } |
| |
| bool Preferences::GetDouble(const char* name, double* pDouble) const |
| { |
| return _GetDouble(FindPrefElement(name), pDouble); |
| } |
| |
| bool Preferences::GetString(const char* name, char** pVal) const |
| { |
| wxString str = wxString::FromAscii(*pVal); |
| if (_GetString(FindPrefElement(name), str)) |
| { |
| *pVal = android::strdupNew(str.ToAscii()); |
| return true; |
| } |
| return false; |
| } |
| |
| bool Preferences::GetString(const char* name, wxString& str) const |
| { |
| return _GetString(FindPrefElement(name), str); |
| } |
| |
| /* |
| * Set a value. If the preference already exists, and the value hasn't |
| * changed, don't do anything. This avoids setting the "dirty" flag |
| * unnecessarily. |
| */ |
| void Preferences::SetBool(const char* name, bool val) |
| { |
| bool oldVal; |
| if (GetBool(name, &oldVal) && val == oldVal) |
| return; |
| |
| SetString(name, val ? "true" : "false"); |
| mDirty = true; |
| } |
| |
| void Preferences::SetInt(const char* name, int val) |
| { |
| int oldVal; |
| if (GetInt(name, &oldVal) && val == oldVal) |
| return; |
| |
| TiXmlElement* pElem = FindPrefElement(name); |
| if (pElem == NULL) |
| pElem = AddPref(name); |
| pElem->SetAttribute(kValue, val); |
| mDirty = true; |
| } |
| |
| void Preferences::SetDouble(const char* name, double val) |
| { |
| double oldVal; |
| if (GetDouble(name, &oldVal) && val == oldVal) |
| return; |
| |
| TiXmlElement* pElem = FindPrefElement(name); |
| if (pElem == NULL) |
| pElem = AddPref(name); |
| pElem->SetDoubleAttribute(kValue, val); |
| mDirty = true; |
| } |
| |
| void Preferences::SetString(const char* name, const char* val) |
| { |
| wxString oldVal; |
| if (GetString(name, /*ref*/oldVal) && strcmp(oldVal.ToAscii(), val) == 0) |
| return; |
| |
| TiXmlElement* pElem = FindPrefElement(name); |
| if (pElem == NULL) |
| pElem = AddPref(name); |
| pElem->SetAttribute(kValue, val); |
| mDirty = true; |
| } |
| |
| |