root/src/kits/shared/SettingsMessage.cpp
/*
 * Copyright 2008-2010, Stephan Aßmus <superstippi@gmx.de>.
 * Copyright 1998, Eric Shepherd.
 * All rights reserved. Distributed under the terms of the Be Sample Code
 * license.
 */

//! Be Newsletter Volume II, Issue 35; September 2, 1998 (Eric Shepherd)

#include "SettingsMessage.h"

#include <new>

#include <Autolock.h>
#include <Entry.h>
#include <File.h>
#include <Messenger.h>
#include <String.h>


SettingsMessage::SettingsMessage(directory_which directory,
                const char* filename)
        :
        BMessage('pref'),
        fListeners(0)
{
        fStatus = find_directory(directory, &fPath);

        if (fStatus == B_OK)
                fStatus = fPath.Append(filename);

        if (fStatus == B_OK)
                fStatus = Load();
}


SettingsMessage::~SettingsMessage()
{
        Save();

        for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
                delete reinterpret_cast<BMessenger*>(fListeners.ItemAtFast(i));
}


status_t
SettingsMessage::InitCheck() const
{
        return fStatus;
}


status_t
SettingsMessage::Load()
{
        BAutolock _(this);

        BFile file(fPath.Path(), B_READ_ONLY);
        status_t status = file.InitCheck();

        if (status == B_OK)
                status = Unflatten(&file);

        return status;
}


status_t
SettingsMessage::Save() const
{
        BAutolock _(const_cast<SettingsMessage*>(this));

        BFile file(fPath.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
        status_t status = file.InitCheck();

        if (status == B_OK)
                status = Flatten(&file);

        return status;
}


bool
SettingsMessage::AddListener(const BMessenger& listener)
{
        BAutolock _(this);

        BMessenger* listenerCopy = new(std::nothrow) BMessenger(listener);
        if (listenerCopy && fListeners.AddItem(listenerCopy))
                return true;
        delete listenerCopy;
        return false;
}


void
SettingsMessage::RemoveListener(const BMessenger& listener)
{
        BAutolock _(this);

        for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
                BMessenger* listenerItem = reinterpret_cast<BMessenger*>(
                        fListeners.ItemAtFast(i));
                if (*listenerItem == listener) {
                        fListeners.RemoveItem(i);
                        delete listenerItem;
                        return;
                }
        }
}


// #pragma mark -


status_t
SettingsMessage::SetValue(const char* name, bool value)
{
        status_t ret = ReplaceBool(name, value);
        if (ret != B_OK)
                ret = AddBool(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, int8 value)
{
        status_t ret = ReplaceInt8(name, value);
        if (ret != B_OK)
                ret = AddInt8(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, int16 value)
{
        status_t ret = ReplaceInt16(name, value);
        if (ret != B_OK)
                ret = AddInt16(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, uint16 value)
{
        status_t ret = ReplaceUInt16(name, value);
        if (ret != B_OK)
                ret = AddUInt16(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, int32 value)
{
        status_t ret = ReplaceInt32(name, value);
        if (ret != B_OK)
                ret = AddInt32(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, uint32 value)
{
        status_t ret = ReplaceUInt32(name, value);
        if (ret != B_OK)
                ret = AddUInt32(name, value);
        if (ret == B_BAD_TYPE && HasData(name, B_INT32_TYPE)) {
                // For compatibility with older versions of this class, replace an int32
                RemoveData(name);
                ret = AddUInt32(name, value);
        }
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, int64 value)
{
        status_t ret = ReplaceInt64(name, value);
        if (ret != B_OK)
                ret = AddInt64(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, uint64 value)
{
        status_t ret = ReplaceUInt64(name, value);
        if (ret != B_OK)
                ret = AddUInt64(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, float value)
{
        status_t ret = ReplaceFloat(name, value);
        if (ret != B_OK)
                ret = AddFloat(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, double value)
{
        status_t ret = ReplaceDouble(name, value);
        if (ret != B_OK)
                ret = AddDouble(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const char* value)
{
        status_t ret = ReplaceString(name, value);
        if (ret != B_OK)
                ret = AddString(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BString& value)
{
        status_t ret = ReplaceString(name, value);
        if (ret != B_OK)
                ret = AddString(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BPoint& value)
{
        status_t ret = ReplacePoint(name, value);
        if (ret != B_OK)
                ret = AddPoint(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BRect& value)
{
        status_t ret = ReplaceRect(name, value);
        if (ret != B_OK)
                ret = AddRect(name, value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const entry_ref& value)
{
        status_t ret = ReplaceRef(name, &value);
        if (ret != B_OK)
                ret = AddRef(name, &value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BMessage& value)
{
        status_t ret = ReplaceMessage(name, &value);
        if (ret != B_OK)
                ret = AddMessage(name, &value);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BFlattenable* value)
{
        status_t ret = ReplaceFlat(name, const_cast<BFlattenable*>(value));
        if (ret != B_OK)
                ret = AddFlat(name, const_cast<BFlattenable*>(value));
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, type_code type, const void* data,
        ssize_t numBytes)
{
        status_t ret = ReplaceData(name, type, data, numBytes);
        if (ret != B_OK)
                ret = AddData(name, type, data, numBytes);
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


status_t
SettingsMessage::SetValue(const char* name, const BFont& value)
{
        font_family family;
        font_style style;
        value.GetFamilyAndStyle(&family, &style);

        BMessage fontMessage;
        status_t ret = fontMessage.AddString("family", family);
        if (ret == B_OK)
                ret = fontMessage.AddString("style", style);
        if (ret == B_OK)
                ret = fontMessage.AddFloat("size", value.Size());

        if (ret == B_OK) {
                if (ReplaceMessage(name, &fontMessage) != B_OK)
                        ret = AddMessage(name, &fontMessage);
        }
        if (ret == B_OK)
                _NotifyValueChanged(name);
        return ret;
}


// #pragma mark -


bool
SettingsMessage::GetValue(const char* name, bool defaultValue) const
{
        bool value;
        if (FindBool(name, &value) != B_OK)
                return defaultValue;
        return value;
}


int8
SettingsMessage::GetValue(const char* name, int8 defaultValue) const
{
        int8 value;
        if (FindInt8(name, &value) != B_OK)
                return defaultValue;
        return value;
}


int16
SettingsMessage::GetValue(const char* name, int16 defaultValue) const
{
        int16 value;
        if (FindInt16(name, &value) != B_OK)
                return defaultValue;
        return value;
}


uint16
SettingsMessage::GetValue(const char* name, uint16 defaultValue) const
{
        uint16 value;
        if (FindUInt16(name, &value) != B_OK)
                return defaultValue;
        return value;
}


int32
SettingsMessage::GetValue(const char* name, int32 defaultValue) const
{
        int32 value;
        if (FindInt32(name, &value) != B_OK)
                return defaultValue;
        return value;
}


uint32
SettingsMessage::GetValue(const char* name, uint32 defaultValue) const
{
        uint32 value;
        if (FindUInt32(name, &value) == B_OK)
                return value;
        // For compatibility with older versions of this class, also accept an int32
        int32 signedValue;
        if (FindInt32(name, &signedValue) == B_OK && signedValue >= 0)
                return signedValue;
        return defaultValue;
}


int64
SettingsMessage::GetValue(const char* name, int64 defaultValue) const
{
        int64 value;
        if (FindInt64(name, &value) != B_OK)
                return defaultValue;
        return value;
}


uint64
SettingsMessage::GetValue(const char* name, uint64 defaultValue) const
{
        uint64 value;
        if (FindUInt64(name, &value) != B_OK)
                return defaultValue;
        return value;
}


float
SettingsMessage::GetValue(const char* name, float defaultValue) const
{
        float value;
        if (FindFloat(name, &value) != B_OK)
                return defaultValue;
        return value;
}


double
SettingsMessage::GetValue(const char* name, double defaultValue) const
{
        double value;
        if (FindDouble(name, &value) != B_OK)
                return defaultValue;
        return value;
}


BString
SettingsMessage::GetValue(const char* name, const BString& defaultValue) const
{
        BString value;
        if (FindString(name, &value) != B_OK)
                return defaultValue;
        return value;
}


const char*
SettingsMessage::GetValue(const char* name, const char* defaultValue) const
{
        const char* value;
        if (FindString(name, &value) != B_OK)
                return defaultValue;
        return value;
}


BPoint
SettingsMessage::GetValue(const char *name, BPoint defaultValue) const
{
        BPoint value;
        if (FindPoint(name, &value) != B_OK)
                return defaultValue;
        return value;
}


BRect
SettingsMessage::GetValue(const char* name, BRect defaultValue) const
{
        BRect value;
        if (FindRect(name, &value) != B_OK)
                return defaultValue;
        return value;
}


entry_ref
SettingsMessage::GetValue(const char* name, const entry_ref& defaultValue) const
{
        entry_ref value;
        if (FindRef(name, &value) != B_OK)
                return defaultValue;
        return value;
}


BMessage
SettingsMessage::GetValue(const char* name, const BMessage& defaultValue) const
{
        BMessage value;
        if (FindMessage(name, &value) != B_OK)
                return defaultValue;
        return value;
}


BFont
SettingsMessage::GetValue(const char* name, const BFont& defaultValue) const
{
        BMessage fontMessage;
        if (FindMessage(name, &fontMessage) != B_OK)
                return defaultValue;

        const char* family;
        const char* style;
        float size;
        if (fontMessage.FindString("family", &family) != B_OK
                || fontMessage.FindString("style", &style) != B_OK
                || fontMessage.FindFloat("size", &size) != B_OK) {
                return defaultValue;
        }

        BFont value;
        if (value.SetFamilyAndStyle(family, style) != B_OK)
                return defaultValue;

        value.SetSize(size);

        return value;
}


void*
SettingsMessage::GetValue(const char* name, type_code type, ssize_t numBytes,
                const void** defaultValue) const
{
        void* value;
        if (FindData(name, type, (const void**)&value, &numBytes) != B_OK)
                return defaultValue;
        return value;
}


// #pragma mark - private

void
SettingsMessage::_NotifyValueChanged(const char* name) const
{
        BMessage message(SETTINGS_VALUE_CHANGED);
        message.AddString("name", name);

        // Add the value of that name to the notification.
        type_code type;
        if (GetInfo(name, &type) == B_OK) {
                const void* data;
                ssize_t numBytes;
                if (FindData(name, type, &data, &numBytes) == B_OK)
                        message.AddData("value", type, data, numBytes);
        }

        int32 count = fListeners.CountItems();
        for (int32 i = 0; i < count; i++) {
                BMessenger* listener = reinterpret_cast<BMessenger*>(
                        fListeners.ItemAtFast(i));
                listener->SendMessage(&message);
        }
}