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 // ---------------------------------------------------------------
LiveSettings(const char * settingsFile,LiveSetting * defaults,int32 defCount)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 *
Acquire()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 *
Release()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 // ---------------------------------------------------------------
~LiveSettings()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
AddObserver(LiveSettingsObserver * observer)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
RemoveObserver(LiveSettingsObserver * observer)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
NotifySettingChanged(uint32 setting)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
LoadSettings()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
LoadSettings(BMessage * pmsg)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
SaveSettings()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
GetConfigurationMessage(BMessage * pmsg)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 *
FindLiveSetting(const char * name)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
SetGetBool(const char * name,bool * pVal)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
SetGetInt32(const char * name,int32 * pVal)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
SetString(const char * name,const BString & str)433 LiveSettings::SetString(const char *name, const BString &str)
434 {
435 SetValue<BString>(name, str);
436 }
437
438 void
GetString(const char * name,BString & str)439 LiveSettings::GetString(const char *name, BString &str)
440 {
441 GetValue<BString>(name, str);
442 }
443
444 template <class T>
445 bool
SetValue(const char * name,const T & val)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
GetValue(const char * name,T & val)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