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