1/*****************************************************************************/ 2// LiveSettings 3// Written by Michael Wilber 4// 5// LiveSettings.cpp 6// 7// This class manages (saves/loads/locks/unlocks) a collection of BMessage 8// based settings. This class allows you to share settings between different 9// classes in different threads and receive notifications when the settings 10// change. This class makes it easy to share settings between a Translator 11// and its config panel or a Screen Saver and its config panel. 12// 13// 14// Copyright (C) Haiku 15// 16// Permission is hereby granted, free of charge, to any person obtaining a 17// copy of this software and associated documentation files (the "Software"), 18// to deal in the Software without restriction, including without limitation 19// the rights to use, copy, modify, merge, publish, distribute, sublicense, 20// and/or sell copies of the Software, and to permit persons to whom the 21// Software is furnished to do so, subject to the following conditions: 22// 23// The above copyright notice and this permission notice shall be included 24// in all copies or substantial portions of the Software. 25// 26// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 27// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 29// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 32// DEALINGS IN THE SOFTWARE. 33/*****************************************************************************/ 34 35#include <string.h> 36#include <File.h> 37#include <FindDirectory.h> 38#include "LiveSettings.h" 39 40// --------------------------------------------------------------- 41// Constructor 42// 43// Sets the default settings, location for the settings file 44// and sets the reference count to 1 45// 46// Preconditions: 47// 48// Parameters: 49// 50// Postconditions: 51// 52// Returns: 53// --------------------------------------------------------------- 54LiveSettings::LiveSettings(const char *settingsFile, 55 LiveSetting *defaults, int32 defCount) 56 : fLock("LiveSettings Lock") 57{ 58 if (find_directory(B_USER_SETTINGS_DIRECTORY, &fSettingsPath)) 59 fSettingsPath.SetTo("/tmp"); 60 fSettingsPath.Append(settingsFile); 61 62 fRefCount = 1; 63 64 if (defCount > 0) { 65 fDefaults = defaults; 66 fDefCount = defCount; 67 } else { 68 fDefaults = NULL; 69 fDefCount = 0; 70 } 71 72 // Add Default Settings 73 // (Used when loading from the settings file or from 74 // a BMessage fails) 75 const LiveSetting *defs = fDefaults; 76 for (int32 i = 0; i < fDefCount; i++) 77 defs[i].AddReplaceValue(&fSettingsMsg); 78} 79 80// --------------------------------------------------------------- 81// Acquire 82// 83// Returns a pointer to the LiveSettings and increments 84// the reference count. 85// 86// Preconditions: 87// 88// Parameters: 89// 90// Postconditions: 91// 92// Returns: pointer to this LiveSettings object 93// --------------------------------------------------------------- 94LiveSettings * 95LiveSettings::Acquire() 96{ 97 LiveSettings *psettings = NULL; 98 99 fLock.Lock(); 100 fRefCount++; 101 psettings = this; 102 fLock.Unlock(); 103 104 return psettings; 105} 106 107// --------------------------------------------------------------- 108// Release 109// 110// Decrements the reference count and deletes the 111// LiveSettings if the reference count is zero. 112// 113// Preconditions: 114// 115// Parameters: 116// 117// Postconditions: 118// 119// Returns: pointer to this LiveSettings object if 120// the reference count is greater than zero, returns NULL 121// if the reference count is zero and the LiveSettings 122// object has been deleted 123// --------------------------------------------------------------- 124LiveSettings * 125LiveSettings::Release() 126{ 127 LiveSettings *psettings = NULL; 128 129 fLock.Lock(); 130 fRefCount--; 131 if (fRefCount > 0) { 132 psettings = this; 133 fLock.Unlock(); 134 } else 135 delete this; 136 // delete this object and 137 // release locks 138 139 return psettings; 140} 141 142// --------------------------------------------------------------- 143// Destructor 144// 145// Does nothing! 146// 147// Preconditions: 148// 149// Parameters: 150// 151// Postconditions: 152// 153// Returns: 154// --------------------------------------------------------------- 155LiveSettings::~LiveSettings() 156{ 157 fObservers.clear(); 158} 159 160// returns true if observer was added sucessfully, 161// false if observer already in the list or error 162bool 163LiveSettings::AddObserver(LiveSettingsObserver *observer) 164{ 165 fLock.Lock(); 166 167 bool bAdd = true; 168 ObserverList::iterator I = fObservers.begin(); 169 while (I != fObservers.end()) { 170 if (*I == observer) { 171 bAdd = false; 172 break; 173 } 174 I++; 175 } 176 177 if (bAdd == true) 178 fObservers.push_back(observer); 179 180 fLock.Unlock(); 181 182 return bAdd; 183} 184 185// returns true if observer was removed successfully, 186// false if observer not found or error 187bool 188LiveSettings::RemoveObserver(LiveSettingsObserver *observer) 189{ 190 fLock.Lock(); 191 192 bool bRemove = false; 193 ObserverList::iterator I = fObservers.begin(); 194 while (I != fObservers.end()) { 195 if (*I == observer) { 196 bRemove = true; 197 break; 198 } 199 I++; 200 } 201 202 if (bRemove == true) 203 fObservers.erase(I); 204 205 fLock.Unlock(); 206 207 return bRemove; 208} 209 210void 211LiveSettings::NotifySettingChanged(uint32 setting) 212{ 213 fLock.Lock(); 214 ObserverList listCopy = fObservers; 215 fLock.Unlock(); 216 217 ObserverList::iterator I = listCopy.begin(); 218 while (I != listCopy.end()) { 219 (*I)->SettingChanged(setting); 220 I++; 221 } 222} 223 224// --------------------------------------------------------------- 225// LoadSettings 226// 227// Loads the settings by reading them from the default 228// settings file. 229// 230// Preconditions: 231// 232// Parameters: 233// 234// Postconditions: 235// 236// Returns: B_OK if there were no errors or an error code from 237// BFile::SetTo() or BMessage::Unflatten() if there were errors 238// --------------------------------------------------------------- 239status_t 240LiveSettings::LoadSettings() 241{ 242 status_t result; 243 244 fLock.Lock(); 245 246 // Don't try to open the settings file if there are 247 // no settings that need to be loaded 248 if (fDefCount > 0) { 249 BFile settingsFile; 250 result = settingsFile.SetTo(fSettingsPath.Path(), B_READ_ONLY); 251 if (result == B_OK) { 252 BMessage msg; 253 result = msg.Unflatten(&settingsFile); 254 if (result == B_OK) 255 result = LoadSettings(&msg); 256 } 257 } else 258 result = B_OK; 259 260 fLock.Unlock(); 261 262 return result; 263} 264 265// --------------------------------------------------------------- 266// LoadSettings 267// 268// Loads the settings from a BMessage passed to the function. 269// 270// Preconditions: 271// 272// Parameters: pmsg pointer to BMessage that contains the 273// settings 274// 275// Postconditions: 276// 277// Returns: B_BAD_VALUE if pmsg is NULL or invalid options 278// have been found, B_OK if there were no 279// errors or an error code from BMessage::FindBool() or 280// BMessage::ReplaceBool() if there were other errors 281// --------------------------------------------------------------- 282status_t 283LiveSettings::LoadSettings(BMessage *pmsg) 284{ 285 status_t result = B_OK; 286 287 if (pmsg) { 288 289 fLock.Lock(); 290 291 const LiveSetting *defs = fDefaults; 292 for (int32 i = 0; i < fDefCount; i++) 293 defs[i].AddReplaceValue(&fSettingsMsg, pmsg); 294 295 fLock.Unlock(); 296 } 297 298 return result; 299} 300 301// --------------------------------------------------------------- 302// SaveSettings 303// 304// Saves the settings as a flattened BMessage to the default 305// settings file 306// 307// Preconditions: 308// 309// Parameters: 310// 311// Postconditions: 312// 313// Returns: B_OK if no errors or an error code from BFile::SetTo() 314// or BMessage::Flatten() if there were errors 315// --------------------------------------------------------------- 316status_t 317LiveSettings::SaveSettings() 318{ 319 status_t result; 320 321 fLock.Lock(); 322 323 // Only write out settings file if there are 324 // actual settings stored by this object 325 if (fDefCount > 0) { 326 BFile settingsFile; 327 result = settingsFile.SetTo(fSettingsPath.Path(), 328 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 329 if (result == B_OK) 330 result = fSettingsMsg.Flatten(&settingsFile); 331 } else 332 result = B_OK; 333 334 fLock.Unlock(); 335 336 return result; 337} 338 339// --------------------------------------------------------------- 340// GetConfigurationMessage 341// 342// Saves the current settings to the BMessage passed to the 343// function 344// 345// Preconditions: 346// 347// Parameters: pmsg pointer to BMessage where the settings 348// will be stored 349// 350// Postconditions: 351// 352// Returns: B_OK if there were no errors or an error code from 353// BMessage::RemoveName() or BMessage::AddBool() if there were 354// errors 355// --------------------------------------------------------------- 356status_t 357LiveSettings::GetConfigurationMessage(BMessage *pmsg) 358{ 359 status_t result = B_BAD_VALUE; 360 361 if (pmsg) { 362 int32 i; 363 for (i = 0; i < fDefCount; i++) { 364 result = pmsg->RemoveName(fDefaults[i].GetName()); 365 if (result != B_OK && result != B_NAME_NOT_FOUND) 366 break; 367 } 368 if (i == fDefCount) { 369 fLock.Lock(); 370 result = B_OK; 371 372 BString tempStr; 373 const LiveSetting *defs = fDefaults; 374 for (i = 0; i < fDefCount && result >= B_OK; i++) 375 defs[i].AddReplaceValue(pmsg, &fSettingsMsg); 376 377 fLock.Unlock(); 378 } 379 } 380 381 return result; 382} 383 384// --------------------------------------------------------------- 385// FindLiveSetting 386// 387// Returns a pointer to the LiveSetting with the given name 388// 389// 390// Preconditions: 391// 392// Parameters: name name of the LiveSetting to find 393// 394// 395// Postconditions: 396// 397// Returns: NULL if the LiveSetting cannot be found, or a pointer 398// to the desired LiveSetting if it is found 399// --------------------------------------------------------------- 400const LiveSetting * 401LiveSettings::FindLiveSetting(const char *name) 402{ 403 for (int32 i = 0; i < fDefCount; i++) { 404 if (!strcmp(fDefaults[i].GetName(), name)) 405 return fDefaults + i; 406 } 407 return NULL; 408} 409 410bool 411LiveSettings::SetGetBool(const char *name, bool *pVal /*= NULL*/) 412{ 413 bool bPrevVal = false; 414 GetValue<bool>(name, bPrevVal); 415 if (pVal != NULL) 416 SetValue<bool>(name, *pVal); 417 418 return bPrevVal; 419} 420 421int32 422LiveSettings::SetGetInt32(const char *name, int32 *pVal /*= NULL*/) 423{ 424 int32 iPrevVal = 0; 425 GetValue<int32>(name, iPrevVal); 426 if (pVal != NULL) 427 SetValue<int32>(name, *pVal); 428 429 return iPrevVal; 430} 431 432void 433LiveSettings::SetString(const char *name, const BString &str) 434{ 435 SetValue<BString>(name, str); 436} 437 438void 439LiveSettings::GetString(const char *name, BString &str) 440{ 441 GetValue<BString>(name, str); 442} 443 444template <class T> 445bool 446LiveSettings::SetValue(const char *name, const T &val) 447{ 448 bool bResult = false, bChanged = false; 449 fLock.Lock(); 450 451 const LiveSetting *def = FindLiveSetting(name); 452 if (def) { 453 bResult = def->AddReplaceValue(&fSettingsMsg, val); 454 bChanged = true; 455 } 456 457 fLock.Unlock(); 458 459 if (bChanged == true) 460 NotifySettingChanged(def->GetId()); 461 462 return bResult; 463} 464 465template <class T> 466bool 467LiveSettings::GetValue(const char *name, T &val) 468{ 469 fLock.Lock(); 470 471 bool bResult = false; 472 const LiveSetting *def = FindLiveSetting(name); 473 if (def) 474 bResult = def->GetValue(&fSettingsMsg, val); 475 476 fLock.Unlock(); 477 478 return bResult; 479} 480