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 // --------------------------------------------------------------- 54 LiveSettings::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 // --------------------------------------------------------------- 94 LiveSettings * 95 LiveSettings::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 // --------------------------------------------------------------- 124 LiveSettings * 125 LiveSettings::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 // --------------------------------------------------------------- 155 LiveSettings::~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 162 bool 163 LiveSettings::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 187 bool 188 LiveSettings::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 210 void 211 LiveSettings::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 // --------------------------------------------------------------- 239 status_t 240 LiveSettings::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 // --------------------------------------------------------------- 282 status_t 283 LiveSettings::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 // --------------------------------------------------------------- 316 status_t 317 LiveSettings::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 // --------------------------------------------------------------- 356 status_t 357 LiveSettings::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 // --------------------------------------------------------------- 400 const LiveSetting * 401 LiveSettings::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 410 bool 411 LiveSettings::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 421 int32 422 LiveSettings::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 432 void 433 LiveSettings::SetString(const char *name, const BString &str) 434 { 435 SetValue<BString>(name, str); 436 } 437 438 void 439 LiveSettings::GetString(const char *name, BString &str) 440 { 441 GetValue<BString>(name, str); 442 } 443 444 template <class T> 445 bool 446 LiveSettings::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 465 template <class T> 466 bool 467 LiveSettings::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