root/src/apps/haikudepot/textview/CharacterStyle.cpp
/*
 * Copyright 2013, Stephan Aßmus <superstippi@gmx.de>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */

#include "CharacterStyle.h"


CharacterStyle::CharacterStyle()
        :
        fStyleData(new CharacterStyleData(), true)
{
}


CharacterStyle::CharacterStyle(const CharacterStyle& other)
        :
        fStyleData(other.fStyleData)
{
}


CharacterStyle&
CharacterStyle::operator=(const CharacterStyle& other)
{
        if (this == &other)
                return *this;

        fStyleData = other.fStyleData;
        return *this;
}


bool
CharacterStyle::operator==(const CharacterStyle& other) const
{
        if (this == &other)
                return true;

        if (fStyleData == other.fStyleData)
                return true;

        if (fStyleData.IsSet() && other.fStyleData.IsSet())
                return *fStyleData == *other.fStyleData;

        return false;
}


bool
CharacterStyle::operator!=(const CharacterStyle& other) const
{
        return !(*this == other);
}


bool
CharacterStyle::SetFont(const BFont& font)
{
        CharacterStyleDataRef data = fStyleData->SetFont(font);
        if (data == fStyleData)
                return data->Font() == font;

        fStyleData = data;
        return true;
}


const BFont&
CharacterStyle::Font() const
{
        return fStyleData->Font();
}


bool
CharacterStyle::SetFontSize(float size)
{
        BFont font(Font());
        font.SetSize(size);
        return SetFont(font);
}


float
CharacterStyle::FontSize() const
{
        return Font().Size();
}


bool
CharacterStyle::SetBold(bool bold)
{
        uint16 face = Font().Face();
        if ((bold && (face & B_BOLD_FACE) != 0)
                || (!bold && (face & B_BOLD_FACE) == 0)) {
                return true;
        }

        uint16 neededFace = face;
        if (bold) {
                neededFace |= B_BOLD_FACE;
                neededFace &= ~B_REGULAR_FACE;
        } else {
                neededFace &= ~B_BOLD_FACE;
                if (neededFace == 0)
                        neededFace |= B_REGULAR_FACE;
        }

        return SetFont(_FindFontForFace(neededFace));
}


bool
CharacterStyle::IsBold() const
{
        return (Font().Face() & B_BOLD_FACE) != 0;
}


bool
CharacterStyle::SetItalic(bool italic)
{
        uint16 face = Font().Face();
        if ((italic && (face & B_ITALIC_FACE) != 0)
                || (!italic && (face & B_ITALIC_FACE) == 0)) {
                return true;
        }

        uint16 neededFace = face;
        if (italic) {
                neededFace |= B_ITALIC_FACE;
                neededFace &= ~B_REGULAR_FACE;
        } else {
                neededFace &= ~B_ITALIC_FACE;
                if (neededFace == 0)
                        neededFace |= B_REGULAR_FACE;
        }

        return SetFont(_FindFontForFace(neededFace));
}


bool
CharacterStyle::IsItalic() const
{
        return (Font().Face() & B_ITALIC_FACE) != 0;
}


bool
CharacterStyle::SetAscent(float ascent)
{
        CharacterStyleDataRef data = fStyleData->SetAscent(ascent);
        if (data == fStyleData)
                return data->Ascent() == ascent;

        fStyleData = data;
        return true;
}


float
CharacterStyle::Ascent() const
{
        return fStyleData->Ascent();
}


bool
CharacterStyle::SetDescent(float descent)
{
        CharacterStyleDataRef data = fStyleData->SetDescent(descent);
        if (data == fStyleData)
                return data->Descent() == descent;

        fStyleData = data;
        return true;
}


float
CharacterStyle::Descent() const
{
        return fStyleData->Descent();
}


bool
CharacterStyle::SetWidth(float width)
{
        CharacterStyleDataRef data = fStyleData->SetWidth(width);
        if (data == fStyleData)
                return data->Width() == width;

        fStyleData = data;
        return true;
}


float
CharacterStyle::Width() const
{
        return fStyleData->Width();
}


bool
CharacterStyle::SetGlyphSpacing(float spacing)
{
        CharacterStyleDataRef data = fStyleData->SetGlyphSpacing(spacing);
        if (data == fStyleData)
                return data->GlyphSpacing() == spacing;

        fStyleData = data;
        return true;
}


float
CharacterStyle::GlyphSpacing() const
{
        return fStyleData->GlyphSpacing();
}


bool
CharacterStyle::SetForegroundColor(uint8 r, uint8 g, uint8 b, uint8 a)
{
        return SetForegroundColor((rgb_color){ r, g, b, a });
}


bool
CharacterStyle::SetForegroundColor(color_which which)
{
        CharacterStyleDataRef data = fStyleData->SetForegroundColor(which);
        if (data == fStyleData)
                return data->WhichForegroundColor() == which;

        fStyleData = data;
        return true;
}


bool
CharacterStyle::SetForegroundColor(rgb_color color)
{
        CharacterStyleDataRef data = fStyleData->SetForegroundColor(color);
        if (data == fStyleData)
                return data->ForegroundColor() == color;

        fStyleData = data;
        return true;
}


rgb_color
CharacterStyle::ForegroundColor() const
{
        return fStyleData->ForegroundColor();
}


color_which
CharacterStyle::WhichForegroundColor() const
{
        return fStyleData->WhichForegroundColor();
}


bool
CharacterStyle::SetBackgroundColor(uint8 r, uint8 g, uint8 b, uint8 a)
{
        return SetBackgroundColor((rgb_color){ r, g, b, a });
}


bool
CharacterStyle::SetBackgroundColor(color_which which)
{
        CharacterStyleDataRef data = fStyleData->SetBackgroundColor(which);
        if (data == fStyleData)
                return data->WhichBackgroundColor() == which;

        fStyleData = data;
        return true;
}


bool
CharacterStyle::SetBackgroundColor(rgb_color color)
{
        CharacterStyleDataRef data = fStyleData->SetBackgroundColor(color);
        if (data == fStyleData)
                return data->BackgroundColor() == color;

        fStyleData = data;
        return true;
}


rgb_color
CharacterStyle::BackgroundColor() const
{
        return fStyleData->BackgroundColor();
}


color_which
CharacterStyle::WhichBackgroundColor() const
{
        return fStyleData->WhichBackgroundColor();
}


bool
CharacterStyle::SetStrikeOutColor(color_which which)
{
        CharacterStyleDataRef data = fStyleData->SetStrikeOutColor(which);
        if (data == fStyleData)
                return data->WhichStrikeOutColor() == which;

        fStyleData = data;
        return true;
}


bool
CharacterStyle::SetStrikeOutColor(rgb_color color)
{
        CharacterStyleDataRef data = fStyleData->SetStrikeOutColor(color);
        if (data == fStyleData)
                return data->StrikeOutColor() == color;

        fStyleData = data;
        return true;
}


rgb_color
CharacterStyle::StrikeOutColor() const
{
        return fStyleData->StrikeOutColor();
}


color_which
CharacterStyle::WhichStrikeOutColor() const
{
        return fStyleData->WhichStrikeOutColor();
}


bool
CharacterStyle::SetUnderlineColor(color_which which)
{
        CharacterStyleDataRef data = fStyleData->SetUnderlineColor(which);
        if (data == fStyleData)
                return data->WhichUnderlineColor() == which;

        fStyleData = data;
        return true;
}


bool
CharacterStyle::SetUnderlineColor(rgb_color color)
{
        CharacterStyleDataRef data = fStyleData->SetUnderlineColor(color);
        if (data == fStyleData)
                return data->UnderlineColor() == color;

        fStyleData = data;
        return true;
}


rgb_color
CharacterStyle::UnderlineColor() const
{
        return fStyleData->UnderlineColor();
}


color_which
CharacterStyle::WhichUnderlineColor() const
{
        return fStyleData->WhichUnderlineColor();
}


bool
CharacterStyle::SetStrikeOut(uint8 strikeOut)
{
        CharacterStyleDataRef data = fStyleData->SetStrikeOut(strikeOut);
        if (data == fStyleData)
                return data->StrikeOut() == strikeOut;

        fStyleData = data;
        return true;
}


uint8
CharacterStyle::StrikeOut() const
{
        return fStyleData->StrikeOut();
}


bool
CharacterStyle::SetUnderline(uint8 underline)
{
#if 1
        uint16 face = Font().Face();
        if ((underline && (face & B_UNDERSCORE_FACE) != 0)
                || (!underline && (face & B_UNDERSCORE_FACE) == 0)) {
                return true;
        }

        uint16 neededFace = face;
        if (underline) {
                neededFace |= B_UNDERSCORE_FACE;
                neededFace &= ~B_REGULAR_FACE;
        } else {
                neededFace &= ~B_UNDERSCORE_FACE;
                if (neededFace == 0)
                        neededFace |= B_REGULAR_FACE;
        }

        return SetFont(_FindFontForFace(neededFace));
#else
        // TODO: re-enable this instead of using B_UNDERSCORE_FACE when TextDocumentView actually
        // implements drawing the fancy underline styles. Until then, we can at least get simple
        // underlines.
        CharacterStyleDataRef data = fStyleData->SetUnderline(underline);
        if (data == fStyleData)
                return data->Underline() == underline;

        fStyleData = data;
        return true;
#endif
}


uint8
CharacterStyle::Underline() const
{
        return fStyleData->Underline();
}


// #pragma mark - private


BFont
CharacterStyle::_FindFontForFace(uint16 face) const
{
        BFont font(Font());
        font.SetFamilyAndFace(NULL, face);

        return font;
}