476 lines
12 KiB
C++
476 lines
12 KiB
C++
//
|
|
// 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;
|
|
}
|
|
|
|
|