root/src/apps/fontdemo/FontDemoView.cpp
/*
 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Mikael Konradson, mikael.konradson@gmail.com
 */


#include "FontDemoView.h"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <Catalog.h>
#include <Bitmap.h>
#include <Font.h>
#include <Message.h>
#include <Shape.h>
#include <String.h>
#include <StackOrHeapArray.h>

#include "messages.h"

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FontDemoView"

FontDemoView::FontDemoView(BRect rect)
        : BView(rect, "FontDemoView", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS),
        fFontSize(50.0),
        fSpacing(0.0),
        fOutLineLevel(0),
        fDrawingMode(B_OP_COPY),
        fBoundingBoxes(false),
        fDrawShapes(false),
        fShapes(NULL)
{
        SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
        SetHighUIColor(B_DOCUMENT_TEXT_COLOR);

        BString setStr = B_TRANSLATE("Haiku, Inc.");
        SetString(setStr);
        SetFontSize(fFontSize);
        SetAntialiasing(true);
}


FontDemoView::~FontDemoView()
{
        free(fShapes);
}


void
FontDemoView::FrameResized(float width, float height)
{
        // TODO: We shouldnt invalidate the whole view when bounding boxes are
        // working as wanted
        Invalidate(/*fBoxRegion.Frame()*/);
        BView::FrameResized(width, height);
}


void
FontDemoView::Draw(BRect updateRect)
{
        SetDrawingMode(B_OP_COPY);

        BRect rect = Bounds();

        if (!fString)
                return;

        SetFont(&fFont, B_FONT_ALL);

        const size_t size = fString.CountChars();
        BStackOrHeapArray<BRect, 64> boundBoxes(size);

        if (OutLineLevel())
                fFont.GetGlyphShapes(fString, size, fShapes);
        else
                fFont.GetBoundingBoxesAsGlyphs(fString, size, B_SCREEN_METRIC, boundBoxes);

        float escapementArray[size];
        //struct escapement_delta escapeDeltas[size];
        struct edge_info edgeInfo[size];
/*
        for (size_t j = 0; j < size; j++) {
                escapeDeltas[j].nonspace = 0.0f;
                escapeDeltas[j].space = 0.0f;
        }
*/
        fFont.GetEdges(fString.String(), size, edgeInfo);
        fFont.GetEscapements(fString.String(), size, /*escapeDeltas,*/ escapementArray);

        font_height fh;
        fFont.GetHeight(&fh);

        float xCoordArray[size];
        float yCoordArray[size];

        float yCoord = (rect.Height() + fh.ascent - fh.descent) / 2;
        float xCoord = -rect.Width() / 2;
        const float xCenter = xCoord * -1;
        const float r = Rotation() * (M_PI / 180.0);
        const float cosinus = cos(r);
        const float sinus = -sin(r);

        // When the bounding boxes workes properly we will invalidate only the
        // region area instead of the whole view.

        fBoxRegion.MakeEmpty();

        for (size_t i = 0; i < size; i++) {
                xCoordArray[i] = 0.0f;
                yCoordArray[i] = 0.0f;

                yCoordArray[i] = sinus * (xCoord - xCoordArray[i]);
                xCoordArray[i] = cosinus * xCoord;

                xCoordArray[i] += xCenter;
                yCoordArray[i] += yCoord;

                boundBoxes[i].OffsetBy(xCoordArray[i], yCoordArray[i]);

                if (OutLineLevel()) {
                        MovePenTo(xCoordArray[i], yCoordArray[i]);
                        FillShape(fShapes[i]);
                        SetPenSize(OutLineLevel());
                        StrokeShape(fShapes[i]);
                } else {
                        SetDrawingMode(fDrawingMode);
                        int32 charLength;
                        const char* charAt = fString.CharAt(i, &charLength);
                        DrawString(charAt, charLength,
                                BPoint(xCoordArray[i], yCoordArray[i]));
                }

                if (BoundingBoxes() && !OutLineLevel()) {
                        if (i % 2)
                                SetHighColor(0, 255, 0);
                        else
                                SetHighColor(255, 0, 0);
                        SetDrawingMode(B_OP_COPY);
                        StrokeRect(boundBoxes[i]);
                }

                // add the bounding to the region.
                fBoxRegion.Include(boundBoxes[i]);

                xCoord += (escapementArray[i] /*+ escapeDeltas[i].nonspace + escapeDeltas[i].space*/)
                        * FontSize() + Spacing();
                //printf("xCoord %f\n", xCoord);
        }
}


void
FontDemoView::MessageReceived(BMessage* msg)
{
        switch (msg->what) {
                case TEXT_CHANGED_MSG:
                {
                        BString text;
                        if (msg->FindString("_text", &text) == B_OK) {
                                SetString(text);
                                Invalidate(/*&fBoxRegion*/);
                        }
                        break;
                }

                case FONTSTYLE_CHANGED_MSG:
                {
                        BMessage fontMessage;
                        if (msg->FindMessage("_fontMessage", &fontMessage) != B_OK)
                                return;

                        const char* family;
                        const char* style;
                        if (fontMessage.FindString("_family", &family) != B_OK
                                || fontMessage.FindString("_style", &style) != B_OK)
                                return;

                        fFont.SetFamilyAndStyle(family, style);
                        Invalidate();
                        break;
                }

                case FONTFAMILY_CHANGED_MSG:
                {
                        BMessage fontMessage;
                        if (msg->FindMessage("_fontMessage", &fontMessage) != B_OK)
                                return;

                        const char* family;
                        if (fontMessage.FindString("_family", &family) != B_OK)
                                return;

                        font_style style;
                        if (get_font_style(const_cast<char*>(family), 0, &style) == B_OK) {
                                fFont.SetFamilyAndStyle(family, style);
                                Invalidate(/*&fBoxRegion*/);
                        }
                        break;
                }

                case FONTSIZE_MSG:
                {
                        float size = 0.0;
                        if (msg->FindFloat("_size", &size) == B_OK) {
                                SetFontSize(size);
                                Invalidate(/*&fBoxRegion*/);
                        }
                        break;
                }

                case FONTSHEAR_MSG:
                {
                        float shear = 90.0;
                        if (msg->FindFloat("_shear", &shear) == B_OK) {
                                SetFontShear(shear);
                                Invalidate(/*&fBoxRegion*/);
                         }
                        break;
                }

                case ROTATION_MSG:
                {
                        float rotation = 0.0;
                        if (msg->FindFloat("_rotation", &rotation) == B_OK) {
                                SetFontRotation(rotation);
                                Invalidate(/*&fBoxRegion*/);
                         }
                        break;
                }

                case SPACING_MSG:
                {
                        float space = 0.0;
                        if (msg->FindFloat("_spacing", &space) == B_OK) {
                                SetSpacing(space);
                                Invalidate(/*&fBoxRegion*/);
                         }
                        break;
                }

                case OUTLINE_MSG:
                {
                        int8 outline = 0;
                        if (msg->FindInt8("_outline", &outline) == B_OK) {
                                SetOutlineLevel(outline);
                                Invalidate(/*&fBoxRegion*/);
                         }
                        break;
                }

                case ALIASING_MSG:
                {
                        bool aliased = false;
                        if (msg->FindBool("_aliased", &aliased) == B_OK) {
                                SetAntialiasing(aliased);
                                Invalidate(/*&fBoxRegion*/);
                        }
                        break;
                }

                case DRAWINGMODE_CHANGED_MSG:
                {
                        if (msg->FindInt32("_mode", (int32 *)&fDrawingMode) == B_OK) {
                                Invalidate(/*&fBoxRegion*/);
                                switch (fDrawingMode) {
                                        case B_OP_COPY:
                                                printf("Drawing mode: B_OP_COPY\n");
                                                break;
                                        case B_OP_OVER:
                                                printf("Drawing mode: B_OP_OVER\n");
                                                break;
                                        case B_OP_ERASE:
                                                printf("Drawing mode: B_OP_ERASE\n");
                                                break;
                                        case B_OP_INVERT:
                                                printf("Drawing mode: B_OP_INVERT\n");
                                                break;
                                        case B_OP_ADD:
                                                printf("Drawing mode: B_OP_ADD\n");
                                                break;
                                        case B_OP_SUBTRACT:
                                                printf("Drawing mode: B_OP_SUBTRACT\n");
                                                break;
                                        case B_OP_BLEND:
                                                printf("Drawing mode: B_OP_BLEND\n");
                                                break;
                                        case B_OP_MIN:
                                                printf("Drawing mode: B_OP_MIN\n");
                                                break;
                                        case B_OP_MAX:
                                                printf("Drawing mode: B_OP_MAX\n");
                                                break;
                                        case B_OP_SELECT:
                                                printf("Drawing mode: B_OP_SELECT\n");
                                                break;
                                        case B_OP_ALPHA:
                                                printf("Drawing mode: B_OP_ALPHA\n");
                                                break;
                                        default:
                                                printf("Drawing mode: %d\n", fDrawingMode);
                                }
                        }
                        break;
                }

                case BOUNDING_BOX_MSG:
                {
                        bool boundingbox = false;
                        if (msg->FindBool("_boundingbox", &boundingbox) == B_OK) {
                                SetDrawBoundingBoxes(boundingbox);
                                Invalidate(/*&fBoxRegion*/);
                        }
                        break;
                }

                default:
                        BView::MessageReceived(msg);
                        break;
        }
}


void
FontDemoView::SetString(BString string)
{
        fString = string;
        free(fShapes);
        _AddShapes(fString);
}


BString
FontDemoView::String() const
{
        return fString;
}


void
FontDemoView::SetFontSize(float size)
{
        fFont.SetSize(size);
}


void
FontDemoView::SetFontShear(float shear)
{
        fFont.SetShear(shear);
}


void
FontDemoView::SetFontRotation(float rotation)
{
        fFont.SetRotation(rotation);
}


void
FontDemoView::SetDrawBoundingBoxes(bool state)
{
        fBoundingBoxes = state;
}


void
FontDemoView::SetAntialiasing(bool state)
{
        fFont.SetFlags(state ? B_FORCE_ANTIALIASING : B_DISABLE_ANTIALIASING);
}


void
FontDemoView::SetSpacing(float space)
{
        fSpacing = space;
}


void
FontDemoView::SetOutlineLevel(int8 outline)
{
        fOutLineLevel = outline;
}


void
FontDemoView::_AddShapes(BString string)
{
        const size_t size = string.CountChars();
        fShapes = (BShape**)malloc(sizeof(BShape*) * size);

        for (size_t i = 0; i < size; i++) {
                fShapes[i] = new BShape();
        }
}