aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/PreferenceMgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/PreferenceMgr.cpp')
-rw-r--r--SrcShared/PreferenceMgr.cpp2564
1 files changed, 2564 insertions, 0 deletions
diff --git a/SrcShared/PreferenceMgr.cpp b/SrcShared/PreferenceMgr.cpp
new file mode 100644
index 0000000..f15bab3
--- /dev/null
+++ b/SrcShared/PreferenceMgr.cpp
@@ -0,0 +1,2564 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 1999-2001 Palm, Inc. or its subsidiaries.
+ All rights reserved.
+
+ This file is part of the Palm OS Emulator.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+\* ===================================================================== */
+
+#include "EmCommon.h"
+#include "PreferenceMgr.h"
+
+#include "EmCPU.h" // gCPU
+#include "EmHAL.h" // EmHAL::GetLineDriverState
+#include "EmMapFile.h" // EmMapFile
+#include "EmSession.h" // EmSessionStopper
+#include "EmTransportSerial.h" // EmTransportSerial
+#include "EmTransportSocket.h" // EmTransportSocket
+#include "EmTransportUSB.h" // EmTransportUSB
+#include "Platform.h" // _stricmp
+#include "StringConversions.h" // ToString, FromString
+
+#include <algorithm> // find()
+#include <ctype.h> // isdigit
+
+Preferences* gPrefs;
+EmulatorPreferences* gEmuPrefs;
+omni_mutex Preferences::fgPrefsMutex;
+
+
+// Define all the keys
+
+#define DEFINE_PREF_KEYS(name, type, init) const char* kPrefKey##name = #name;
+FOR_EACH_PREF(DEFINE_PREF_KEYS)
+
+static void PrvFilterFileRefList (PrefKeyType key);
+static EmTransportType PrvGetTransportTypeFromPortName (const char* portName);
+
+static Bool PrvFirstBeginsWithSecond (const string& first, const string& second)
+{
+ // Return true if the first string starts with the second string, and
+ // if the point where they both end in common is either the end of
+ // the first string or a delimiter indicating that a sub-part follows.
+
+ return
+ (first.size () >= second.size ()) &&
+ (memcmp (first.c_str (), second.c_str (), second.size ()) == 0) &&
+ ((first.size () == second.size ()) ||
+ (first[second.size ()] == '.') ||
+ (first[second.size ()] == '['));
+}
+
+
+// ----------------------------------------------------------------------
+// * BasePreference
+//
+// Clients of the prefernces sub-system access the data via Preference
+// objects. These objects are created from the Preference class,
+// which the client templatizes based on the data type in which they
+// want the data returned.
+//
+// Preference descends from BasePreference, which implements generic
+// functionality. If we were to put this functionality into Preference
+// itself, we'd have massive unnecessary code bloat.
+// ----------------------------------------------------------------------
+
+/***********************************************************************
+ *
+ * FUNCTION: BasePreference constructor
+ *
+ * DESCRIPTION: Initializes all the data members.
+ *
+ * PARAMETERS: name - name of the key used to fetch the data.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+BasePreference::BasePreference (PrefKeyType name, bool acquireLock) :
+ fName (name),
+ fLoaded (false),
+ fChanged (false),
+ fAcquireLock (acquireLock)
+{
+}
+
+BasePreference::BasePreference (long index, bool acquireLock) :
+ fName (::ToString (index)),
+ fLoaded (false),
+ fChanged (false),
+ fAcquireLock (acquireLock)
+{
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: BasePreference destructor
+ *
+ * DESCRIPTION: Destroys all the data members.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+BasePreference::~BasePreference (void)
+{
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: BasePreference::Load
+ *
+ * DESCRIPTION: Lock the preference data and attempt to load the item
+ * specified by the key (specified to the constructor).
+ * If the data was able to be loaded (which pretty much
+ * means that we were able to convert it into the correct
+ * type, set fLoaded to true.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void BasePreference::Load (void)
+{
+ if (fAcquireLock)
+ {
+ // Lock this here. In case DoLoad sets the prefix, we don't want
+ // any other threads accidently using or changing that prefix.
+
+ omni_mutex_lock lock (Preferences::fgPrefsMutex);
+
+ if (this->DoLoad ())
+ {
+ fLoaded = true;
+ }
+ }
+ else
+ {
+ if (this->DoLoad ())
+ {
+ fLoaded = true;
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: BasePreference::Save
+ *
+ * DESCRIPTION: If the data has been change (via operator=()), lock the
+ * preference data and store the entry back out to it.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void BasePreference::Save (void)
+{
+ if (fChanged)
+ {
+ // Lock this here. In case DoSave sets the prefix, we don't want
+ // any other threads accidently using or chaning that prefix.
+
+ if (fAcquireLock)
+ {
+ omni_mutex_lock lock (Preferences::fgPrefsMutex);
+
+ this->DoSave ();
+ fChanged = false;
+ }
+ else
+ {
+ this->DoSave ();
+ fChanged = false;
+ }
+ }
+}
+
+
+// ----------------------------------------------------------------------
+// * Preference<T>
+//
+// Clients of the prefernces sub-system access the data via Preference
+// objects. These objects are created from the Preference class,
+// which the client templatizes based on the data type in which they
+// want the data returned.
+//
+// Once an object has been created, the preference value can be fetched
+// with the * operator:
+//
+// Preference<bool> pref(kPrefKeySomeSetting);
+// if (*pref)
+// ...
+//
+// The value can also be fetched with the -> operator:
+//
+// Preference<RGBType> pref(kPrefKeySomeColor);
+// if (pref->fRed == 0 && pref->fGreen == 0 && pref->fBlue == 0)
+// ...
+//
+// The value can be changed by simply assigning to the Preference object:
+//
+// Preference<string> pref(kPrefKeySomeString);
+// pref = string ("new string value");
+//
+// You can find out if there is a valid value for the desired preference
+// by calling Loaded():
+//
+// Preference<bool> pref1(kPrefKeySomeSetting);
+// pref1 = true;
+//
+// Preference<GremlinInfo> pref2(kPrefKeySomeSetting);
+// if (pref2.Loaded ())
+// ... fails because we can't convert a bool to a GremlinInfo...
+//
+// Preference<bool> pref3("bogus undefined key);
+// if (pref3.Loaded ())
+// ... fails because we key was not found...
+//
+// ----------------------------------------------------------------------
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preference<T> constructor
+ *
+ * DESCRIPTION: Calls BasePreference to initialize the base members,
+ * then calls Load() to load the data.
+ *
+ * PARAMETERS: name - name of the key used to fetch the data.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+template <class T>
+Preference<T>::Preference (PrefKeyType name, bool acquireLock) :
+ BasePreference (name, acquireLock),
+ fValue (T())
+{
+ this->Load ();
+}
+
+template <class T>
+Preference<T>::Preference (long index, bool acquireLock) :
+ BasePreference (index, acquireLock),
+ fValue (T())
+{
+ this->Load ();
+}
+
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preference<T> destructor
+ *
+ * DESCRIPTION: Ensures that any changes are flushed back to the
+ * preference collection before destructing the base class.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+template <class T>
+Preference<T>::~Preference (void)
+{
+ this->Save ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preference<T>::DoLoad
+ *
+ * DESCRIPTION: Virtual function responsible for the actual conversion
+ * of string information into natural data types.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: True if the function could be loaded and converted.
+ * False otherwise.
+ *
+ ***********************************************************************/
+
+template <class T>
+bool Preference<T>::DoLoad (void)
+{
+ string value;
+
+ if (gPrefs->GetPref (fName, value) && ::FromString (value, fValue))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preference<T>::DoSave
+ *
+ * DESCRIPTION: Virtual function responsible for the actual conversion
+ * of our natural data types into strings.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+template <class T>
+void Preference<T>::DoSave (void)
+{
+ gPrefs->SetPref (fName, ::ToString (fValue));
+}
+
+
+// ----------------------------------------------------------------------
+// Instantiations of Preference class for all non-compound types.
+// ----------------------------------------------------------------------
+
+template class Preference<bool>;
+template class Preference<char>;
+template class Preference<signed char>;
+template class Preference<unsigned char>;
+template class Preference<signed short>;
+template class Preference<unsigned short>;
+template class Preference<signed int>;
+template class Preference<unsigned int>;
+template class Preference<signed long>;
+template class Preference<unsigned long>;
+
+//template class Preference<EmDirRef>;
+template class Preference<EmFileRef>;
+template class Preference<string>;
+
+//template class Preference<Bool>;
+template class Preference<CloseActionType>;
+template class Preference<EmDevice>;
+template class Preference<EmErrorHandlingOption>;
+
+
+// ----------------------------------------------------------------------
+// Specializations of Preference class for compound types.
+//
+// template Preference<Configuration>;
+// template Preference<DatabaseInfo>;
+// template Preference<DatabaseInfoList>;
+// template Preference<EmFileRefList>;
+// template Preference<GremlinInfo>;
+// template Preference<PointType>;
+// template Preference<EmPoint>;
+// template Preference<RGBType>;
+// template Preference<SkinNameList>;
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<Configuration>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("fDeviceType", value) && ::FromString (value, fValue.fDevice) &&
+ gPrefs->GetPref ("fRAMSize", value) && ::FromString (value, fValue.fRAMSize) &&
+ gPrefs->GetPref ("fROMFile", value) && ::FromString (value, fValue.fROMFile))
+ {
+ loaded = true;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<Configuration>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("fDeviceType", ::ToString (fValue.fDevice));
+ gPrefs->SetPref ("fRAMSize", ::ToString (fValue.fRAMSize));
+ gPrefs->SetPref ("fROMFile", ::ToString (fValue.fROMFile));
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<DatabaseInfo>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("creator", value) && ::FromString (value, fValue.creator) &&
+ gPrefs->GetPref ("type", value) && ::FromString (value, fValue.type) &&
+ gPrefs->GetPref ("version", value) && ::FromString (value, fValue.version) &&
+ gPrefs->GetPref ("dbID", value) && ::FromString (value, fValue.dbID) &&
+ gPrefs->GetPref ("cardNo", value) && ::FromString (value, fValue.cardNo) &&
+ gPrefs->GetPref ("modDate", value) && ::FromString (value, fValue.modDate) &&
+ gPrefs->GetPref ("dbAttrs", value) && ::FromString (value, fValue.dbAttrs) &&
+ gPrefs->GetPref ("name", value) && ::FromString (value, fValue.name))
+ {
+ loaded = true;
+
+ // Workaround a bug in older Posers. I forgot to write out the dbName,
+ // so not all preference files have that field. If it's there, read it.
+ // Otherwise, use application name.
+ //
+ // !!! May instead want to set dbName to "\0" and try to fix it up later.
+ if (gPrefs->GetPref ("dbName", value) && ::FromString (value, fValue.dbName))
+ {
+ }
+ else
+ {
+ strcpy (fValue.dbName, fValue.name);
+ }
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<DatabaseInfo>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("creator", ::ToString (fValue.creator));
+ gPrefs->SetPref ("type", ::ToString (fValue.type));
+ gPrefs->SetPref ("version", ::ToString (fValue.version));
+ gPrefs->SetPref ("dbID", ::ToString (fValue.dbID));
+ gPrefs->SetPref ("cardNo", ::ToString (fValue.cardNo));
+ gPrefs->SetPref ("modDate", ::ToString (fValue.modDate));
+ gPrefs->SetPref ("dbAttrs", ::ToString (fValue.dbAttrs));
+ gPrefs->SetPref ("name", ::ToString (fValue.name));
+ gPrefs->SetPref ("dbName", ::ToString (fValue.dbName));
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<DatabaseInfoList>::DoLoad (void)
+{
+ bool loaded = true;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ while (1)
+ {
+ Preference<DatabaseInfo> pref (ii++, false);
+ if (!pref.Loaded ())
+ break;
+
+ fValue.push_back (*pref);
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<DatabaseInfoList>::DoSave (void)
+{
+ gPrefs->DeletePref (fName);
+
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ DatabaseInfoList::iterator iter = fValue.begin ();
+ while (iter != fValue.end ())
+ {
+ Preference<DatabaseInfo> pref (ii++, false);
+ pref = *iter;
+
+ ++iter;
+ }
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<EmFileRefList>::DoLoad (void)
+{
+ bool loaded = true;
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ while (1)
+ {
+ Preference<EmFileRef> pref (ii++, false);
+ if (!pref.Loaded ())
+ break;
+
+ fValue.push_back (*pref);
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<EmFileRefList>::DoSave (void)
+{
+ gPrefs->DeletePref (fName);
+
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ EmFileRefList::iterator iter = fValue.begin ();
+ while (iter != fValue.end ())
+ {
+ Preference<EmFileRef> pref (ii++, false);
+ pref = *iter;
+
+ ++iter;
+ }
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<GremlinInfo>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("fNumber", value) && ::FromString (value, fValue.fNumber) &&
+ gPrefs->GetPref ("fSteps", value) && ::FromString (value, fValue.fSteps) &&
+ gPrefs->GetPref ("fSaveFrequency", value) && ::FromString (value, fValue.fSaveFrequency))
+ {
+ loaded = true;
+ }
+
+ if (loaded)
+ {
+ Preference<DatabaseInfoList> pref ("fAppList", false);
+ if (pref.Loaded ())
+ {
+ fValue.fAppList = *pref;
+ }
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<GremlinInfo>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("fNumber", ::ToString (fValue.fNumber));
+ gPrefs->SetPref ("fSteps", ::ToString (fValue.fSteps));
+ gPrefs->SetPref ("fSaveFrequency", ::ToString (fValue.fSaveFrequency));
+
+ // Save the fAppList collection.
+ {
+ Preference<DatabaseInfoList> pref ("fAppList", false);
+ pref = fValue.fAppList;
+ }
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<HordeInfo>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ // If the old fields exist, convert them to new fields and delete them.
+ if (gPrefs->GetPref ("fDepthBound", value) && ::FromString (value, fValue.fSwitchDepth) &&
+ gPrefs->GetPref ("fSaveFrequency", value) && ::FromString (value, fValue.fSaveFrequency) &&
+ gPrefs->GetPref ("fMaxDepth", value) && ::FromString (value, fValue.fMaxDepth))
+ {
+ fValue.OldToNew (); // Transfer the old fields to the new fields.
+
+ gPrefs->SetPref ("fDepthSwitch", ::ToString (fValue.fDepthSwitch));
+ gPrefs->SetPref ("fDepthSave", ::ToString (fValue.fDepthSave));
+ gPrefs->SetPref ("fDepthStop", ::ToString (fValue.fDepthStop));
+ gPrefs->SetPref ("fCanSwitch", ::ToString (fValue.fCanSwitch));
+ gPrefs->SetPref ("fCanSave", ::ToString (fValue.fCanSave));
+ gPrefs->SetPref ("fCanStop", ::ToString (fValue.fCanStop));
+
+ gPrefs->DeletePref ("fDepthBound");
+ gPrefs->DeletePref ("fSaveFrequency");
+ gPrefs->DeletePref ("fMaxDepth");
+ }
+
+ if (gPrefs->GetPref ("fStartNumber", value) && ::FromString (value, fValue.fStartNumber) &&
+ gPrefs->GetPref ("fStopNumber", value) && ::FromString (value, fValue.fStopNumber) &&
+ gPrefs->GetPref ("fDepthSwitch", value) && ::FromString (value, fValue.fDepthSwitch) &&
+ gPrefs->GetPref ("fDepthSave", value) && ::FromString (value, fValue.fDepthSave) &&
+ gPrefs->GetPref ("fDepthStop", value) && ::FromString (value, fValue.fDepthStop) &&
+ gPrefs->GetPref ("fCanSwitch", value) && ::FromString (value, fValue.fCanSwitch) &&
+ gPrefs->GetPref ("fCanSave", value) && ::FromString (value, fValue.fCanSave) &&
+ gPrefs->GetPref ("fCanStop", value) && ::FromString (value, fValue.fCanStop) &&
+ gPrefs->GetPref ("fFirstLaunchedAppName", value) && ::FromString (value, fValue.fFirstLaunchedAppName))
+ {
+ loaded = true;
+ fValue.NewToOld (); // Transfer the new fields to the old fields.
+ }
+
+ if (loaded)
+ {
+ Preference<DatabaseInfoList> pref ("fAppList", false);
+ if (pref.Loaded ())
+ {
+ fValue.fAppList = *pref;
+ }
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<HordeInfo>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("fStartNumber", ::ToString (fValue.fStartNumber));
+ gPrefs->SetPref ("fStopNumber", ::ToString (fValue.fStopNumber));
+ gPrefs->SetPref ("fDepthSwitch", ::ToString (fValue.fDepthSwitch));
+ gPrefs->SetPref ("fDepthSave", ::ToString (fValue.fDepthSave));
+ gPrefs->SetPref ("fDepthStop", ::ToString (fValue.fDepthStop));
+ gPrefs->SetPref ("fCanSwitch", ::ToString (fValue.fCanSwitch));
+ gPrefs->SetPref ("fCanSave", ::ToString (fValue.fCanSave));
+ gPrefs->SetPref ("fCanStop", ::ToString (fValue.fCanStop));
+ gPrefs->SetPref ("fFirstLaunchedAppName", ::ToString (fValue.fFirstLaunchedAppName));
+
+ // Save the fAppList collection.
+ {
+ Preference<DatabaseInfoList> pref ("fAppList", false);
+ pref = fValue.fAppList;
+ }
+
+
+ gPrefs->PopPrefix ();
+}
+
+
+// ----------------------------------------------------------------------
+
+ // It's odd that we have to provide explicit specializations of the
+ // constructor and destructor for Preference<PointType>. I think it
+ // has something to do with the fact that PointType doesn't have a
+ // constructor of its own.
+
+template <>
+Preference<PointType>::Preference (PrefKeyType name, bool acquireLock) :
+ BasePreference (name, acquireLock)
+ //, fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<PointType>::Preference (long index, bool acquireLock) :
+ BasePreference (index, acquireLock)
+ //, fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<PointType>::~Preference (void)
+{
+ this->Save ();
+}
+
+template <>
+bool Preference<PointType>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("x", value) && ::FromString (value, fValue.x) &&
+ gPrefs->GetPref ("y", value) && ::FromString (value, fValue.y))
+ {
+ loaded = true;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<PointType>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("x", ::ToString (fValue.x));
+ gPrefs->SetPref ("y", ::ToString (fValue.y));
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+ // It's odd that we have to provide explicit specializations of the
+ // constructor and destructor for Preference<EmPoint>.
+
+template <>
+Preference<EmPoint>::Preference (PrefKeyType name, bool acquireLock) :
+ BasePreference (name, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<EmPoint>::Preference (long index, bool acquireLock) :
+ BasePreference (index, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<EmPoint>::~Preference (void)
+{
+ this->Save ();
+}
+
+template <>
+bool Preference<EmPoint>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("x", value) && ::FromString (value, fValue.fX) &&
+ gPrefs->GetPref ("y", value) && ::FromString (value, fValue.fY))
+ {
+ loaded = true;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<EmPoint>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("x", ::ToString (fValue.fX));
+ gPrefs->SetPref ("y", ::ToString (fValue.fY));
+
+ gPrefs->PopPrefix ();
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+Preference<RGBType>::Preference (PrefKeyType name, bool acquireLock) :
+ BasePreference (name, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<RGBType>::Preference (long index, bool acquireLock) :
+ BasePreference (index, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<RGBType>::~Preference (void)
+{
+ this->Save ();
+}
+
+template <>
+bool Preference<RGBType>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("red", value) && ::FromString (value, fValue.fRed) &&
+ gPrefs->GetPref ("green", value) && ::FromString (value, fValue.fGreen) &&
+ gPrefs->GetPref ("blue", value) && ::FromString (value, fValue.fBlue))
+ {
+ loaded = true;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<RGBType>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("red", ::ToString (fValue.fRed));
+ gPrefs->SetPref ("green", ::ToString (fValue.fGreen));
+ gPrefs->SetPref ("blue", ::ToString (fValue.fBlue));
+
+ gPrefs->PopPrefix ();
+}
+
+
+// ----------------------------------------------------------------------
+// Also SkinNameList and PortNameList.
+
+template <>
+void Preference<StringList>::DoSave (void)
+{
+// gPrefs->DeletePref (fName);
+
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ StringList::iterator iter = fValue.begin ();
+ while (iter != fValue.end ())
+ {
+ Preference<string> pref (ii++, false);
+ pref = *iter;
+
+ ++iter;
+ }
+
+ gPrefs->PopPrefix ();
+}
+
+
+template <>
+bool Preference<StringList>::DoLoad (void)
+{
+ bool loaded = true;
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ while (1)
+ {
+ Preference<string> pref (ii, false);
+ if (!pref.Loaded ())
+ break;
+
+ fValue.push_back (*pref);
+
+ ++ii;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+// ----------------------------------------------------------------------
+
+template <>
+Preference<SlotInfoType>::Preference (PrefKeyType name, bool acquireLock) :
+ BasePreference (name, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<SlotInfoType>::Preference (long index, bool acquireLock) :
+ BasePreference (index, acquireLock),
+ fValue ()
+{
+ this->Load ();
+}
+
+template <>
+Preference<SlotInfoType>::~Preference (void)
+{
+ this->Save ();
+}
+
+template <>
+bool Preference<SlotInfoType>::DoLoad (void)
+{
+ bool loaded = false;
+ string value;
+
+ gPrefs->PushPrefix (fName);
+
+ if (gPrefs->GetPref ("fSlotNumber", value) && ::FromString (value, fValue.fSlotNumber) &&
+ gPrefs->GetPref ("fSlotOccupied", value) && ::FromString (value, fValue.fSlotOccupied) &&
+ gPrefs->GetPref ("fSlotRoot", value) && ::FromString (value, fValue.fSlotRoot))
+ {
+ loaded = true;
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<SlotInfoType>::DoSave (void)
+{
+ gPrefs->PushPrefix (fName);
+
+ gPrefs->SetPref ("fSlotNumber", ::ToString (fValue.fSlotNumber));
+ gPrefs->SetPref ("fSlotOccupied", ::ToString (fValue.fSlotOccupied));
+ gPrefs->SetPref ("fSlotRoot", ::ToString (fValue.fSlotRoot));
+
+ gPrefs->PopPrefix ();
+}
+
+
+// ----------------------------------------------------------------------
+
+template <>
+bool Preference<SlotInfoList>::DoLoad (void)
+{
+ bool loaded = true;
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ while (1)
+ {
+ Preference<SlotInfoType> pref (ii++, false);
+ if (!pref.Loaded ())
+ break;
+
+ fValue.push_back (*pref);
+ }
+
+ gPrefs->PopPrefix ();
+
+ return loaded;
+}
+
+template <>
+void Preference<SlotInfoList>::DoSave (void)
+{
+ gPrefs->DeletePref (fName);
+
+ gPrefs->PushPrefix (fName);
+
+ long ii = 0;
+ SlotInfoList::iterator iter = fValue.begin ();
+ while (iter != fValue.end ())
+ {
+ Preference<SlotInfoType> pref (ii++, false);
+ pref = *iter;
+
+ ++iter;
+ }
+
+ gPrefs->PopPrefix ();
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::Preferences
+ *
+ * DESCRIPTION: Constructor. Does nothing but construct data members.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+Preferences::Preferences (void) :
+ fPreferences ()
+{
+ if (gPrefs == NULL)
+ gPrefs = this;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::~Preferences
+ *
+ * DESCRIPTION: Destructor. Does nothing but destruct data members.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+Preferences::~Preferences (void)
+{
+ if (gPrefs == this)
+ gPrefs = NULL;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::Load
+ *
+ * DESCRIPTION: Loads the preferences from the storage medium.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::Load (void)
+{
+ StringStringMap mapData;
+
+ if (!this->ReadPreferences (mapData))
+ return;
+
+ StringStringMap::iterator iter = mapData.begin ();
+ while (iter != mapData.end ())
+ {
+ string key = iter->first;
+ string value = iter->second;
+
+ this->SetPref (key, value);
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::Save
+ *
+ * DESCRIPTION: Saves the preferences to the storage medium.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::Save (void)
+{
+ this->StripUnused ();
+
+ this->WritePreferences (fPreferences);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::GetPref
+ *
+ * DESCRIPTION: Returns the specified preference. The given key is
+ * prepended with any prefixes, and the resulting key is
+ * used to look up the value.
+ *
+ * PARAMETERS: key - the key used to look up the preference's value.
+ *
+ * value - the found value (if any).
+ *
+ * RETURNED: True if the key/value could be found, false otherwise.
+ *
+ ***********************************************************************/
+
+bool Preferences::GetPref (const string& key, string& value)
+{
+ // Locking done in Preference::DoLoad.
+// omni_mutex_lock lock (fgPrefsMutex);
+
+ string fullKey = gPrefs->ExpandKey (key);
+
+ iterator iter = fPreferences.find (fullKey);
+ if (iter == fPreferences.end ())
+ return false;
+
+ value = iter->second;
+ return true;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::SetPref
+ *
+ * DESCRIPTION: Store the given key/value pair into our collection. If
+ * an entry with the given key already exists, its value
+ * is updated with the specified one.
+ *
+ * If the value is changed or newly added, then send out
+ * notification to anyone registered for this preference.
+ *
+ * PARAMETERS: key - the key used to look up the preference.
+ *
+ * value - the value to associate with the key.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::SetPref (const string& key, const string& value)
+{
+ // Locking done in Preference::DoSave.
+// omni_mutex_lock lock (fgPrefsMutex);
+
+ string fullKey = gPrefs->ExpandKey (key);
+ Bool doNotify = false;
+
+ iterator iter = fPreferences.find(fullKey);
+ if (iter == fPreferences.end ())
+ {
+ doNotify = true;
+ fPreferences[fullKey] = value;
+ }
+ else
+ {
+ doNotify = (iter->second != value);
+ iter->second = value;
+ }
+
+ if (doNotify)
+ {
+ this->DoNotify (fullKey);
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::DeletePref
+ *
+ * DESCRIPTION: Delete a preference from our collection. The delete
+ * operation works on the given key and all sub-keys (that
+ * is, all keys that have "key" as its prefix).
+ *
+ * PARAMETERS: key - the key for the value or value to delete.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::DeletePref (const string& key)
+{
+ // Locking done in Preference::DoSave.
+// omni_mutex_lock lock (fgPrefsMutex);
+
+ string deleteKey = gPrefs->ExpandKey (key);
+
+ // Iterate over all the entries in the map.
+
+ iterator iter = fPreferences.begin ();
+ while (iter != fPreferences.end ())
+ {
+ // For each entry, get the key.
+
+ string thisKey (iter->first.c_str());
+
+ if (::PrvFirstBeginsWithSecond (thisKey, deleteKey))
+ {
+ fPreferences.erase (iter);
+ iter = fPreferences.begin ();
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ this->DoNotify (deleteKey);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::PushPrefix
+ *
+ * DESCRIPTION: Push a prefix onto our internal stack. These prefixes
+ * are used later to build a full key with which to fetch
+ * and store preference values.
+ *
+ * PARAMETERS: prefix - the subkey to push onto our stack.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::PushPrefix (const string& prefix)
+{
+ // Locking done in Preference::DoSave/DoLoad.
+// omni_mutex_lock lock (fgPrefsMutex);
+
+ fPrefixes.push_back (prefix);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::PushPrefix
+ *
+ * DESCRIPTION: Push a prefix onto our internal stack. These prefixes
+ * are used later to build a full key with which to fetch
+ * and store preference values.
+ *
+ * PARAMETERS: index - the subkey to push onto our stack.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::PushPrefix (long index)
+{
+ this->PushPrefix (::ToString (index));
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::PopPrefix
+ *
+ * DESCRIPTION: Pop a prefix off of our internal stack.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::PopPrefix (void)
+{
+ // Locking done in Preference::DoSave/DoLoad.
+// omni_mutex_lock lock (fgPrefsMutex);
+
+ fPrefixes.pop_back ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::ExpandKey
+ *
+ * DESCRIPTION: Prepend the given key with the prefixes pushed onto
+ * our stack. Prefixes are prepended in such as way as to
+ * look like a "C" expression. "Fields" are seperated with
+ * dots, and array subscripts are surround with brackets.
+ *
+ * PARAMETERS: name - the part of the key that will appear at the
+ * very end of the expanded key. If this name is
+ * empty, the expanded key will end with the last
+ * prefix pushed onto the stack.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+string Preferences::ExpandKey (const string& name)
+{
+ string key;
+
+ PrefixType::iterator iter = fPrefixes.begin ();
+
+ if (iter == fPrefixes.end ())
+ {
+ key = name;
+ }
+ else
+ {
+ key = *iter++;
+
+ while (iter != fPrefixes.end ())
+ {
+ if (!iter->empty ())
+ key = this->AppendName (key, *iter);
+
+ ++iter;
+ }
+
+ if (!name.empty ())
+ {
+ key = this->AppendName (key, name);
+ }
+ }
+
+ return key;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::AppendName
+ *
+ * DESCRIPTION: Append a single prefix to the full expanded key as it
+ * is built up from left to right.
+ *
+ * PARAMETERS: key - the expanded key so far.
+ *
+ * name - the subkey to append to it.
+ *
+ * RETURNED: The catenated value. If name looks like a number, the
+ * catenated result is:
+ *
+ * key[name]
+ *
+ * Otherwise, we return:
+ *
+ * key.name
+ *
+ ***********************************************************************/
+
+string Preferences::AppendName (string key, const string& name)
+{
+ if (isdigit (name[0]))
+ {
+ key = key + "[" + name + "]";
+ }
+ else
+ {
+ key = key + "." + name;
+ }
+
+ return key;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::AddNotification
+ *
+ * DESCRIPTION: DESCRIPTION
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::AddNotification (PrefNotifyFunc fn, PrefKeyType key, PrefRefCon refCon)
+{
+ PrefKeyList prefList;
+
+ prefList.push_back (key);
+ this->AddNotification (fn, prefList, refCon);
+}
+
+
+void Preferences::AddNotification (PrefNotifyFunc fn, const PrefKeyList& keyList, PrefRefCon refCon)
+{
+ PrefNotifyList::iterator iter = fNotifications.begin ();
+ while (iter != fNotifications.end ())
+ {
+ if (iter->fFunc == fn)
+ {
+ break;
+ }
+
+ ++iter;
+ }
+
+ if (iter == fNotifications.end ())
+ {
+ PrefNotifyType entry;
+
+ entry.fFunc = fn;
+ entry.fRefCon = refCon;
+
+ fNotifications.push_back (entry);
+
+ iter = fNotifications.end () - 1;
+ }
+
+ // Add the list of keys for which this function wants notification.
+ // (Hmmm...doesn't check for duplicates...should it?)
+
+ iter->fKeyList.insert (iter->fKeyList.end (), keyList.begin (), keyList.end ());
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::RemoveNotification
+ *
+ * DESCRIPTION: DESCRIPTION
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::RemoveNotification (PrefNotifyFunc fn)
+{
+ PrefNotifyList::iterator iter = fNotifications.begin ();
+ while (iter != fNotifications.end ())
+ {
+ if (iter->fFunc == fn)
+ {
+ fNotifications.erase (iter);
+ break;
+ }
+
+ ++iter;
+ }
+}
+
+
+void Preferences::RemoveNotification (PrefNotifyFunc fn, PrefKeyType key)
+{
+ PrefKeyList prefList;
+
+ prefList.push_back (key);
+ this->RemoveNotification (fn, prefList);
+}
+
+
+void Preferences::RemoveNotification (PrefNotifyFunc /*fn*/, const PrefKeyList& /*keyList*/)
+{
+#if 0
+ PrefNotifyList::iterator iter = fNotifications.find(fn);
+
+ if (iter != fNotifications.end ())
+ {
+ // !!! TBD
+ }
+#endif
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: DoNotify
+ *
+ * DESCRIPTION: DESCRIPTION
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::DoNotify (const string& key)
+{
+ PrefNotifyList::iterator listIter = fNotifications.begin ();
+ while (listIter != fNotifications.end ())
+ {
+ // For each key in the list, see if it matches the specified key.
+
+ PrefKeyList::iterator keyIter = listIter->fKeyList.begin ();
+ while (keyIter != listIter->fKeyList.end ())
+ {
+ if (::PrvFirstBeginsWithSecond (key, *keyIter))
+ {
+ // It does, so call the notification function.
+
+ (listIter->fFunc) (keyIter->c_str (), listIter->fRefCon);
+ }
+
+ ++keyIter;
+ }
+
+
+ ++listIter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::ReadPreferences
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+Bool Preferences::ReadPreferences (StringStringMap& prefData)
+{
+ EmFileRef prefRef (this->GetPrefRef ());
+ return EmMapFile::Read (prefRef, prefData);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::WritePreferences
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::WritePreferences (const StringStringMap& prefData)
+{
+ EmFileRef prefRef (this->GetPrefRef ());
+ EmMapFile::Write (prefRef, prefData);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::GetPrefRef
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+EmFileRef Preferences::GetPrefRef (void)
+{
+ EmDirRef poserDir (EmDirRef::GetEmulatorDirectory ());
+ EmDirRef prefDir (EmDirRef::GetPrefsDirectory ());
+
+#if PLATFORM_MAC
+
+ string name ("Palm OS Emulator Prefs");
+
+#elif PLATFORM_UNIX
+
+ string name (".poserrc");
+
+#elif PLATFORM_WINDOWS
+
+ string name ("Palm OS Emulator.ini");
+
+#else
+
+ #error "Undefined platform"
+
+#endif
+
+ EmFileRef poserResult (poserDir, name);
+ if (poserResult.Exists ())
+ return poserResult;
+
+ EmFileRef prefResult (prefDir, name);
+
+ return prefResult;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::WriteBanner
+ *
+ * DESCRIPTION: Write a descriptive banner to the preference file.
+ * Useful so that people will know what the contents of
+ * the file are for (in case they can't glean that from
+ * the name of the file).
+ *
+ * PARAMETERS: f - the open FILE to write the banner to.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::WriteBanner (FILE*)
+{
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::ReadBanner
+ *
+ * DESCRIPTION: Read the banner in the given file, possibly validating
+ * it.
+ *
+ * PARAMETERS: f - the open FILE containing the banner.
+ *
+ * RETURNED: True if it looks like this is our file, false if it
+ * looks like this file contains something else.
+ *
+ ***********************************************************************/
+
+Bool Preferences::ReadBanner (FILE*)
+{
+ return true;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Preferences::StripUnused
+ *
+ * DESCRIPTION: Called to remove any obsolete or otherwise stray
+ * settings from our collection. Usually called some time
+ * before the updated settings are rewritten back to disk.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void Preferences::StripUnused (void)
+{
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::EmulatorPreferences
+ *
+ * DESCRIPTION: Constructor. Establishes default values for all the
+ * preferences we'll be using.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+EmulatorPreferences::EmulatorPreferences (void) :
+ Preferences (),
+ fTransports ()
+{
+ if (gEmuPrefs == NULL)
+ gEmuPrefs = this;
+
+ // Set up default values for all the keys.
+ //
+ // Warning: this method works implicitly off of "gPrefs", and so will
+ // fail when trying to create additional preference objects.
+
+ #define INIT_PREF_KEYS(name, type, init) \
+ { \
+ Preference<type> pref (kPrefKey##name); \
+ pref = type init; \
+ }
+
+ FOR_EACH_INIT_PREF(INIT_PREF_KEYS)
+
+ for (EmUARTDeviceType ii = kUARTBegin; ii < kUARTEnd; ++ii)
+ {
+ fTransports[ii] = NULL;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::~EmulatorPreferences
+ *
+ * DESCRIPTION: Destructor.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+EmulatorPreferences::~EmulatorPreferences (void)
+{
+ for (EmUARTDeviceType ii = kUARTBegin; ii < kUARTEnd; ++ii)
+ {
+ delete fTransports[ii];
+ }
+
+ if (gEmuPrefs == this)
+ gEmuPrefs = NULL;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::Load
+ *
+ * DESCRIPTION: Install the correct set of logging preferences after the
+ * settings have been loaded from the file. Also, make
+ * sure the list of skin preferences is large enough so
+ * that we can look up the skin for any device without
+ * having to check the array size all over the place.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::Load (void)
+{
+ Preferences::Load ();
+
+ // Migrate over any old prefs to any new way of handling them.
+
+ this->MigrateOldPrefs ();
+
+ // Let's set up some default configuration in case there isn't
+ // one in the prefs file (or it was invalid).
+
+ Preference<Configuration> prefConfig (kPrefKeyLastConfiguration);
+ if (!prefConfig.Loaded())
+ {
+ prefConfig = Configuration (EmDevice ("PalmIII"), 1024, EmFileRef());
+ }
+
+ // Create the transports used by the UARTs.
+
+ this->SetTransports ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::GetIndMRU
+ *
+ * DESCRIPTION: Return a PRC or PSF file from the corresponding list of
+ * files, based on the given index.
+ *
+ * PARAMETERS: The index of the file to return.
+ *
+ * RETURNED: The nth file in the list. If index is off the end of
+ * the list, a non-specified EmFileRef (that is, one
+ * for which the IsSpecified method returns false) is
+ * returned.
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::GetDatabaseMRU (EmFileRefList& files)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPRC_MRU);
+ files = *pref;
+}
+
+
+void EmulatorPreferences::GetSessionMRU (EmFileRefList& files)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPSF_MRU);
+ files = *pref;
+}
+
+
+void EmulatorPreferences::GetROMMRU (EmFileRefList& files)
+{
+ Preference<EmFileRefList> pref (kPrefKeyROM_MRU);
+ files = *pref;
+}
+
+
+EmFileRef EmulatorPreferences::GetIndPRCMRU (int index)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPRC_MRU);
+ EmFileRefList mru = *pref;
+ return this->GetIndMRU (mru, index);
+}
+
+
+EmFileRef EmulatorPreferences::GetIndRAMMRU (int index)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPSF_MRU);
+ EmFileRefList mru = *pref;
+ return this->GetIndMRU (mru, index);
+}
+
+
+EmFileRef EmulatorPreferences::GetIndROMMRU (int index)
+{
+ Preference<EmFileRefList> pref (kPrefKeyROM_MRU);
+ EmFileRefList mru = *pref;
+ return this->GetIndMRU (mru, index);
+}
+
+
+EmFileRef EmulatorPreferences::GetIndMRU (const EmFileRefList& fileList, int index)
+{
+ if ((EmFileRefList::size_type) index < fileList.size ())
+ return fileList [index];
+
+ return EmFileRef();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::UpdateMRU
+ *
+ * DESCRIPTION: Update the MRU list for PRC and PSF files. The given
+ * file is inserted at the beginning of the list. If
+ * the file previously existed in the list, it is removed
+ * from its old location. The length of the list is
+ * limited to MRU_COUNT
+ *
+ * PARAMETERS: newFile - the file to move to the beginning of the list.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::UpdatePRCMRU (const EmFileRef& newFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPRC_MRU);
+ EmFileRefList mru = *pref;
+ this->UpdateMRU (mru, newFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::UpdateRAMMRU (const EmFileRef& newFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPSF_MRU);
+ EmFileRefList mru = *pref;
+ this->UpdateMRU (mru, newFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::UpdateROMMRU (const EmFileRef& newFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyROM_MRU);
+ EmFileRefList mru = *pref;
+ this->UpdateMRU (mru, newFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::UpdateMRU (EmFileRefList& fileList, const EmFileRef& newFile)
+{
+ this->RemoveMRU (fileList, newFile);
+
+ fileList.insert (fileList.begin (), newFile);
+
+ while (fileList.size () > MRU_COUNT)
+ fileList.pop_back ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::RemoveMRU
+ *
+ * DESCRIPTION: Remove the given PRC or PSF file from its MRU list.
+ *
+ * PARAMETERS: oldFile - the file to remove.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::RemovePRCMRU (const EmFileRef& oldFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPRC_MRU);
+ EmFileRefList mru = *pref;
+ this->RemoveMRU (mru, oldFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::RemoveRAMMRU (const EmFileRef& oldFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyPSF_MRU);
+ EmFileRefList mru = *pref;
+ this->RemoveMRU (mru, oldFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::RemoveROMMRU (const EmFileRef& oldFile)
+{
+ Preference<EmFileRefList> pref (kPrefKeyROM_MRU);
+ EmFileRefList mru = *pref;
+ this->RemoveMRU (mru, oldFile);
+ pref = mru;
+}
+
+
+void EmulatorPreferences::RemoveMRU (EmFileRefList& fileList, const EmFileRef& oldFile)
+{
+ EmFileRefList::iterator iter = find (fileList.begin (), fileList.end (), oldFile);
+
+ if (iter != fileList.end ())
+ fileList.erase (iter);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::SetTransports
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::SetTransports (void)
+{
+ {
+ Preference<EmTransportDescriptor> pref (kPrefKeyPortSerial);
+ this->SetTransportForDevice (kUARTSerial, pref->CreateTransport ());
+ }
+
+ {
+ Preference<EmTransportDescriptor> pref (kPrefKeyPortIR);
+ this->SetTransportForDevice (kUARTIR, pref->CreateTransport ());
+ }
+
+ {
+ Preference<EmTransportDescriptor> pref (kPrefKeyPortMystery);
+ this->SetTransportForDevice (kUARTMystery, pref->CreateTransport ());
+ }
+}
+
+
+void EmulatorPreferences::SetTransportForDevice (EmUARTDeviceType type,
+ EmTransport* transport)
+{
+ EmSessionStopper stopper (gSession, kStopNow);
+
+ delete fTransports[type];
+ fTransports[type] = transport;
+
+ // If the transport exists and needs to be opened, open it.
+
+ if (transport && gCPU && EmHAL::GetLineDriverState (type))
+ {
+ transport->Open ();
+ }
+}
+
+
+EmTransport* EmulatorPreferences::GetTransportForDevice (EmUARTDeviceType type)
+{
+ return fTransports[type];
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ Errors::LogMessage
+// ---------------------------------------------------------------------------
+// Return whether or not the message should be logged.
+
+Bool EmulatorPreferences::LogMessage (Bool isFatal)
+{
+ {
+ if (isFatal && LogErrorMessages ())
+ return true;
+ }
+
+ {
+ if (!isFatal && LogWarningMessages ())
+ return true;
+ }
+
+ {
+ Preference<Bool> pref ("SilentRunning");
+ if (pref.Loaded () && *pref)
+ return true;
+ }
+
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ PrvGetPrefKey
+// ---------------------------------------------------------------------------
+// Return the key for the preference that tells us to handle the error or
+// warning in this situation.
+
+static PrefKeyType PrvGetPrefKey (Bool isFatal)
+{
+ PrefKeyType prefKey = NULL;
+
+ if (Hordes::IsOn ())
+ {
+ prefKey = isFatal ? kPrefKeyErrorOn : kPrefKeyWarningOn;
+ }
+ else
+ {
+ prefKey = isFatal ? kPrefKeyErrorOff : kPrefKeyWarningOff;
+ }
+
+ return prefKey;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmulatorPreferences::ShouldQuit
+// ---------------------------------------------------------------------------
+// Return whether or not this error should cause us to quit.
+
+Bool EmulatorPreferences::ShouldQuit (Bool isFatal)
+{
+ // If SilentRunning is set, then we quit on fatal errors.
+
+ if (isFatal)
+ {
+ Preference<Bool> pref ("SilentRunning");
+ if (pref.Loaded () && *pref)
+ return true;
+ }
+
+ // If the old ExitOnErrors preference is set, then we
+ // quit on fatal errors.
+
+ if (isFatal)
+ {
+ Preference<Bool> pref ("ExitOnErrors");
+ if (pref.Loaded () && *pref)
+ return true;
+ }
+
+ // Otherwise, get the setting for this situation and
+ // see if it tells us to quit.
+
+ PrefKeyType prefKey = ::PrvGetPrefKey (isFatal);
+ Preference<EmErrorHandlingOption> pref (prefKey);
+ return (*pref == kQuit);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmulatorPreferences::ShouldContinue
+// ---------------------------------------------------------------------------
+// Return whether or not this error should be logged but not displayed.
+
+Bool EmulatorPreferences::ShouldContinue (Bool isFatal)
+{
+ // If SilentRunning is set, then we continue on warnings.
+
+ if (!isFatal)
+ {
+ Preference<Bool> pref ("SilentRunning");
+ if (pref.Loaded () && *pref)
+ return true;
+ }
+
+ // If the old ContinueOnWarnings preference is set, then we
+ // continue on warnings.
+
+ if (!isFatal)
+ {
+ Preference<Bool> pref ("ContinueOnWarnings");
+ if (pref.Loaded () && *pref)
+ return true;
+ }
+
+ // Otherwise, get the setting for this situation and
+ // see if it tells us to quit.
+
+ PrefKeyType prefKey = ::PrvGetPrefKey (isFatal);
+ Preference<EmErrorHandlingOption> pref (prefKey);
+ return (*pref == kContinue);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmulatorPreferences::ShouldNextGremlin
+// ---------------------------------------------------------------------------
+// Return whether or not this error should cause us to switch to the next
+// Gremlin in a Horde.
+
+Bool EmulatorPreferences::ShouldNextGremlin (Bool isFatal)
+{
+ // Otherwise, get the setting for this situation and
+ // see if it tells us to quit.
+
+ PrefKeyType prefKey = ::PrvGetPrefKey (isFatal);
+ Preference<EmErrorHandlingOption> pref (prefKey);
+ return (*pref == kSwitch);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::WriteBanner
+ *
+ * DESCRIPTION: Write out a banner indicating that that is a Poser
+ * preference file.
+ *
+ * PARAMETERS: f - the open FILE to write the banner to.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::WriteBanner (FILE* f)
+{
+ fprintf (f, "# Palm OS Emulator Preferences\n\n");
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::ReadBanner
+ *
+ * DESCRIPTION: Do nothing.
+ *
+ * PARAMETERS: f - the open FILE containing the banner.
+ *
+ * RETURNED: True if it looks like this is our file, false if it
+ * looks like this file contains something else.
+ *
+ ***********************************************************************/
+
+Bool EmulatorPreferences::ReadBanner (FILE*)
+{
+ return true;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::StripUnused
+ *
+ * DESCRIPTION: Called to remove any obsolete or otherwise stray
+ * settings from our collection. Usually called some time
+ * before the updated settings are rewritten back to disk.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+static bool PrvCheckKey (PrefKeyType key)
+{
+ #define REMOVE_UNUSED(name, type, init) \
+ if (::PrefKeysEqual (key, kPrefKey##name)) \
+ return true;
+ FOR_EACH_PREF(REMOVE_UNUSED)
+
+ return false;
+}
+
+void EmulatorPreferences::StripUnused (void)
+{
+ // Iterate over all the entries in the map.
+
+ iterator iter = fPreferences.begin ();
+ while (iter != fPreferences.end ())
+ {
+ // For each entry, get the key, and then just the root part of
+ // the key (everything before the first '.' or '[', if any).
+
+ string keyRoot (iter->first);
+
+ string::size_type dotPos = keyRoot.find ('.');
+ if (dotPos != string::npos)
+ keyRoot.erase (dotPos);
+
+ string::size_type bracketPos = keyRoot.find ('[');
+ if (bracketPos != string::npos)
+ keyRoot.erase (bracketPos);
+
+ // See if this key (root) is one that we still support.
+ // If not, then remove it.
+
+ if (!::PrvCheckKey (keyRoot.c_str ()))
+ {
+ // According to MSDN's documentation, erase is supposed to
+ // return an iterator to the next element. MSL doesn't
+ // seem to follow that description.
+// iter = fPreferences.erase (iter);
+
+ fPreferences.erase (iter);
+ iter = fPreferences.begin ();
+ continue;
+ }
+
+ // If this is one of the old Skins preferences, remove it.
+
+ keyRoot = iter->first;
+ bracketPos = keyRoot.find ('[');
+
+ if (bracketPos != string::npos)
+ {
+ keyRoot.erase (bracketPos + 1);
+
+ if (keyRoot == "Skins[")
+ {
+ fPreferences.erase (iter);
+ iter = fPreferences.begin ();
+ continue;
+ }
+ }
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmulatorPreferences::MigrateOldPrefs
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmulatorPreferences::MigrateOldPrefs (void)
+{
+ // Migrate any old skin preferences to the current format.
+
+ {
+ const char* kDeviceName[] =
+ {
+ NULL,
+ "Pilot",
+ "Pilot",
+ "PalmPilot",
+ "PalmPilot",
+ NULL,
+ "PalmIII",
+ "PalmVII",
+ NULL,
+ NULL,
+ "PalmV",
+ "PalmIIIx",
+ NULL,
+ "PalmIIIc",
+ "PalmVIIEZ",
+ "PalmIIIe",
+ "PalmVx",
+ "Symbol1700",
+ "TRGpro",
+ "Visor",
+ "PalmM100",
+ "PalmVIIx",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "VZDevice",
+ NULL,
+ NULL,
+ "VisorPrism",
+ "VisorPlatinum",
+ NULL
+ };
+
+ for (size_t index = 0; index < countof (kDeviceName); ++index)
+ {
+ if (kDeviceName[index])
+ {
+ char oldPrefKey[20];
+ sprintf (oldPrefKey, "%s[%d]", kPrefKeySkins, index);
+
+ Preference<SkinName> oldSkinPref (oldPrefKey);
+ if (!oldSkinPref.Loaded ())
+ break;
+
+ char newPrefKey[20];
+ sprintf (newPrefKey, "%s.%s", kPrefKeySkins, kDeviceName[index]);
+
+ Preference<SkinName> newSkinPref (newPrefKey);
+ newSkinPref = *oldSkinPref;
+ }
+ }
+ }
+
+ // Filter the MRU lists, removing references to items no longer there.
+
+ ::PrvFilterFileRefList (kPrefKeyPRC_MRU);
+ ::PrvFilterFileRefList (kPrefKeyPSF_MRU);
+ ::PrvFilterFileRefList (kPrefKeyROM_MRU);
+
+ // If this is the first time Poser 3.3 is being run, then set the
+ // logging options for errors and warnings to true in Gremlin mode.
+
+ StringStringMap mapData;
+
+ if (this->ReadPreferences (mapData))
+ {
+ if (mapData.find ("DialogBeep") == mapData.end ())
+ {
+ Preference<uint8> prefLogWarnings (kPrefKeyLogWarningMessages);
+ Preference<uint8> prefLogErrors (kPrefKeyLogErrorMessages);
+
+ prefLogWarnings = *prefLogWarnings | kGremlinLogging;
+ prefLogErrors = *prefLogErrors | kGremlinLogging;
+ }
+ }
+
+ // If there's a CommPortList preference, migrate that over
+ // to the CommPort preference.
+
+ Preference<string> prefCommPort ("CommPort");
+ Preference<StringList> prefCommPortList ("CommPortList");
+
+ if (prefCommPortList.Loaded () && prefCommPortList->size () > 0)
+ {
+ prefCommPort = (*prefCommPortList)[0];
+ }
+
+ // Migrate forward old port redirection settings to new ones.
+ // (Yes, this is done in addition to the migration done previously.
+ // Each migration represents different changes in Poser's evolution.)
+
+ // Convert CommPort, SerialTargetHost, and SerialTargetPort.
+
+ {
+ Preference<string> oldHost ("SerialTargetHost");
+ Preference<string> oldPort ("SerialTargetPort");
+
+ if (oldHost.Loaded ())
+ {
+ Preference<string> newHost (kPrefKeyPortSerialSocket);
+
+ newHost = *oldHost + ":" + *oldPort;
+ }
+ }
+
+ {
+ Preference<string> oldPref ("CommPort");
+ if (oldPref.Loaded ())
+ {
+ Preference<EmTransportDescriptor> newPref (kPrefKeyPortSerial);
+ EmTransportType type = PrvGetTransportTypeFromPortName (oldPref->c_str ());
+
+ if (type == kTransportSerial)
+ {
+ newPref = EmTransportDescriptor (kTransportSerial, *oldPref);
+ }
+ else if (type == kTransportSocket)
+ {
+ Preference<string> newHost (kPrefKeyPortSerialSocket);
+
+ newPref = EmTransportDescriptor (kTransportSocket, *newHost);
+ }
+ else
+ {
+ newPref = EmTransportDescriptor (kTransportNull);
+ }
+ }
+ }
+
+ // Convert IRPort, IRTargetHost, and IRTargetPort.
+
+ {
+ Preference<string> oldHost ("IRTargetHost");
+ Preference<string> oldPort ("IRTargetPort");
+
+ if (oldHost.Loaded ())
+ {
+ Preference<string> newHost (kPrefKeyPortIRSocket);
+
+ newHost = *oldHost + ":" + *oldPort;
+ }
+ }
+
+ {
+ Preference<string> oldPref ("IRPort");
+ if (oldPref.Loaded ())
+ {
+ Preference<EmTransportDescriptor> newPref (kPrefKeyPortIR);
+ EmTransportType type = PrvGetTransportTypeFromPortName (oldPref->c_str ());
+
+ if (type == kTransportSerial)
+ {
+ newPref = EmTransportDescriptor (kTransportSerial, *oldPref);
+ }
+ else if (type == kTransportSocket)
+ {
+ Preference<string> newHost (kPrefKeyPortIRSocket);
+
+ newPref = EmTransportDescriptor (kTransportSocket, *newHost);
+ }
+ else
+ {
+ newPref = EmTransportDescriptor (kTransportNull);
+ }
+ }
+ }
+
+ // Establish Mystery Port
+
+ {
+ Preference<EmTransportDescriptor> pref (kPrefKeyPortMystery);
+
+ if (!pref.Loaded ())
+ {
+ pref = EmTransportDescriptor (kTransportNull);
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvFilterFileRefList
+ *
+ * DESCRIPTION: Remove files from the given list that no longer appear
+ * to exist.
+ *
+ * PARAMETERS: key - the key of the preference containing the list
+ * of files to check.
+ *
+ * RETURNED: Nothing, but the preference is altered.
+ *
+ ***********************************************************************/
+
+void PrvFilterFileRefList (PrefKeyType key)
+{
+ Preference<EmFileRefList> pref (key);
+ EmFileRefList mruList = *pref;
+
+ EmFileRefList::iterator iter = mruList.begin ();
+
+ while (iter != mruList.end ())
+ {
+ if (!iter->Exists ())
+ {
+ EmFileRefList::difference_type index = iter - mruList.begin ();
+ mruList.erase (iter);
+ iter = mruList.begin () + index;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ pref = mruList;
+}
+
+
+/**********************************************************************
+ *
+ * FUNCTION: PrvGetTransportTypeFromPortName
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+EmTransportType PrvGetTransportTypeFromPortName (const char* portName)
+{
+#if PLATFORM_UNIX
+ if (portName && strlen (portName) > 0)
+ {
+ if (portName[0] == '/')
+ {
+ return kTransportSerial;
+ }
+
+ return kTransportSocket;
+ }
+#else
+ {
+ EmTransportDescriptorList names;
+ EmTransportSerial::GetDescriptorList (names);
+ EmTransportDescriptorList::iterator iter = names.begin ();
+
+ while (iter != names.end ())
+ {
+ if (!strcmp (portName, iter->GetSchemeSpecific ().c_str ()))
+ {
+ return kTransportSerial;
+ }
+
+ ++iter;
+ }
+ }
+
+ {
+ EmTransportDescriptorList names;
+ EmTransportSocket::GetDescriptorList (names);
+ EmTransportDescriptorList::iterator iter = names.begin ();
+
+ while (iter != names.end ())
+ {
+ if (!strcmp (portName, iter->GetSchemeSpecific ().c_str ()))
+ {
+ return kTransportSocket;
+ }
+
+ ++iter;
+ }
+ }
+
+ {
+ EmTransportDescriptorList names;
+ EmTransportUSB::GetDescriptorList (names);
+ EmTransportDescriptorList::iterator iter = names.begin ();
+
+ while (iter != names.end ())
+ {
+ if (!strcmp (portName, iter->GetSchemeSpecific ().c_str ()))
+ {
+ return kTransportUSB;
+ }
+
+ ++iter;
+ }
+ }
+#endif
+
+ return kTransportNull;
+}
+
+bool PrefKeysEqual (PrefKeyType key1, PrefKeyType key2)
+{
+ return _stricmp (key1, key2) == 0;
+}