root/src/apps/fontdemo/ControlView.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 "ControlView.h"
#include "messages.h"

#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <Menu.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <PopUpMenu.h>
#include <Slider.h>
#include <String.h>
#include <TextControl.h>
#include <Window.h>

#include <stdio.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ControlView"

ControlView::ControlView()
        : BGroupView("ControlView", B_VERTICAL),
        fMessenger(NULL),
        fMessageRunner(NULL),
        fTextControl(NULL),
        fFontMenuField(NULL),
        fFontsizeSlider(NULL),
        fShearSlider(NULL),
        fRotationSlider(NULL),
        fSpacingSlider(NULL),
        fOutlineSlider(NULL),
        fAliasingCheckBox(NULL),
        fBoundingboxesCheckBox(NULL),
        fCyclingFontButton(NULL),
        fFontFamilyMenu(NULL),
        fDrawingModeMenu(NULL),
        fCycleFonts(false),
        fFontStyleindex(0)
{
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
        GroupLayout()->SetInsets(B_USE_WINDOW_SPACING);
}


ControlView::~ControlView()
{
        delete fMessenger;
        delete fMessageRunner;
}


void
ControlView::AttachedToWindow()
{
        fTextControl = new BTextControl("TextInput", B_TRANSLATE("Text:"),
                B_TRANSLATE("Haiku, Inc."), NULL);
        fTextControl->SetModificationMessage(new BMessage(TEXT_CHANGED_MSG));
        AddChild(fTextControl);

        _AddFontMenu();

        BString label;

        label.SetToFormat(B_TRANSLATE("Size: %d"), 50);
        fFontsizeSlider = new BSlider("Fontsize", label, NULL, 4, 360,
                B_HORIZONTAL);
        fFontsizeSlider->SetModificationMessage(new BMessage(FONTSIZE_MSG));
        fFontsizeSlider->SetValue(50);
        AddChild(fFontsizeSlider);

        label.SetToFormat(B_TRANSLATE("Shear: %d"), 90);
        fShearSlider = new BSlider("Shear", label, NULL, 45, 135, B_HORIZONTAL);
        fShearSlider->SetModificationMessage(new BMessage(FONTSHEAR_MSG));
        fShearSlider->SetValue(90);
        AddChild(fShearSlider);

        label.SetToFormat(B_TRANSLATE("Rotation: %d"), 0);
        fRotationSlider = new BSlider("Rotation", label, NULL, 0, 360,
                B_HORIZONTAL);
        fRotationSlider->SetModificationMessage( new BMessage(ROTATION_MSG));
        fRotationSlider->SetValue(0);
        AddChild(fRotationSlider);

        label.SetToFormat(B_TRANSLATE("Spacing: %d"), 0);
        fSpacingSlider = new BSlider("Spacing", label, NULL, -5, 50, B_HORIZONTAL);
        fSpacingSlider->SetModificationMessage(new BMessage(SPACING_MSG));
        fSpacingSlider->SetValue(0);
        AddChild(fSpacingSlider);

        label.SetToFormat(B_TRANSLATE("Outline: %d"), 0);
        fOutlineSlider = new BSlider("Outline", label, NULL, 0, 20, B_HORIZONTAL);
        fOutlineSlider->SetModificationMessage(new BMessage(OUTLINE_MSG));
        AddChild(fOutlineSlider);

        fAliasingCheckBox = new BCheckBox("Aliasing",
                B_TRANSLATE("Antialiased text"), new BMessage(ALIASING_MSG));
        fAliasingCheckBox->SetValue(B_CONTROL_ON);
        AddChild(fAliasingCheckBox);

        _AddDrawingModeMenu();

        fBoundingboxesCheckBox = new BCheckBox("BoundingBoxes",
                B_TRANSLATE("Bounding boxes"), new BMessage(BOUNDING_BOX_MSG));
        AddChild(fBoundingboxesCheckBox);

        fCyclingFontButton = new BButton("Cyclefonts",
                B_TRANSLATE("Cycle fonts"), new BMessage(CYCLING_FONTS_MSG));
        AddChild(fCyclingFontButton);

        fTextControl->SetTarget(this);
        fFontsizeSlider->SetTarget(this);
        fShearSlider->SetTarget(this);
        fRotationSlider->SetTarget(this);
        fSpacingSlider->SetTarget(this);
        fOutlineSlider->SetTarget(this);
        fAliasingCheckBox->SetTarget(this);
        fBoundingboxesCheckBox->SetTarget(this);
        fCyclingFontButton->SetTarget(this);
}


void
ControlView::Draw(BRect updateRect)
{
        BRect rect(Bounds());
        SetHighColor(tint_color(ViewColor(), B_LIGHTEN_2_TINT));
        StrokeLine(rect.LeftTop(), rect.RightTop());
        StrokeLine(rect.LeftTop(), rect.LeftBottom());

        SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT));
        StrokeLine(rect.LeftBottom(), rect.RightBottom());
        StrokeLine(rect.RightBottom(), rect.RightTop());
}


void
ControlView::MessageReceived(BMessage* msg)
{
        if (!fMessenger) {
                BView::MessageReceived(msg);
                return;
        }

        switch (msg->what) {
                case TEXT_CHANGED_MSG:
                {
                        BMessage fontMsg(TEXT_CHANGED_MSG);
                        fontMsg.AddString("_text", fTextControl->Text());
                        fMessenger->SendMessage(&fontMsg);
                        break;
                }

                case FONTSTYLE_CHANGED_MSG:
                        _UpdateAndSendStyle(msg);
                        break;

                case FONTFAMILY_CHANGED_MSG:
                        _UpdateAndSendFamily(msg);
                        break;

                case FONTSIZE_MSG:
                {
                        BString label;
                        label.SetToFormat(B_TRANSLATE("Size: %d"),
                                static_cast<int>(fFontsizeSlider->Value()));
                        fFontsizeSlider->SetLabel(label);

                        BMessage msg(FONTSIZE_MSG);
                        msg.AddFloat("_size", static_cast<float>(fFontsizeSlider->Value()));
                        fMessenger->SendMessage(&msg);
                        break;
                }

                case FONTSHEAR_MSG:
                {
                        BString label;
                        label.SetToFormat(B_TRANSLATE("Shear: %d"),
                                static_cast<int>(fShearSlider->Value()));
                        fShearSlider->SetLabel(label);

                        BMessage msg(FONTSHEAR_MSG);
                        msg.AddFloat("_shear", static_cast<float>(fShearSlider->Value()));
                        fMessenger->SendMessage(&msg);
                        break;
                }

                case ROTATION_MSG:
                {
                        BString label;
                        label.SetToFormat(B_TRANSLATE("Rotation: %d"),
                                static_cast<int>(fRotationSlider->Value()));
                        fRotationSlider->SetLabel(label);

                        BMessage msg(ROTATION_MSG);
                        msg.AddFloat("_rotation", static_cast<float>(fRotationSlider->Value()));
                        fMessenger->SendMessage(&msg);
                        break;
                }

                case SPACING_MSG:
                {
                        BString label;
                        label.SetToFormat(B_TRANSLATE("Spacing: %d"),
                                (int)fSpacingSlider->Value());
                        fSpacingSlider->SetLabel(label);

                        BMessage msg(SPACING_MSG);
                        msg.AddFloat("_spacing", static_cast<float>(fSpacingSlider->Value()));
                        fMessenger->SendMessage(&msg);
                        break;
                }

                case ALIASING_MSG:
                {
                        BMessage msg(ALIASING_MSG);
                        msg.AddBool("_aliased", static_cast<bool>(fAliasingCheckBox->Value()));
                        fMessenger->SendMessage(&msg);
                        if (static_cast<bool>(fAliasingCheckBox->Value()) == true)
                                printf("Aliasing: true\n");
                        else
                                printf("Aliasing: false\n");
                        break;
                }

                case BOUNDING_BOX_MSG:
                {
                        BMessage msg(BOUNDING_BOX_MSG);
                        msg.AddBool("_boundingbox", static_cast<bool>(fBoundingboxesCheckBox->Value()));
                        fMessenger->SendMessage(&msg);
                        if (static_cast<bool>(fBoundingboxesCheckBox->Value()))
                                printf("Bounding: true\n");
                        else
                                printf("Bounding: false\n");
                        break;
                }

                case OUTLINE_MSG:
                {
                        int8 outlineVal = (int8)fOutlineSlider->Value();

                        BString label;
                        label.SetToFormat(B_TRANSLATE("Outline: %d"), outlineVal);
                        fOutlineSlider->SetLabel(label);

                        fAliasingCheckBox->SetEnabled(outlineVal < 1);
                        fBoundingboxesCheckBox->SetEnabled(outlineVal < 1);

                        BMessage msg(OUTLINE_MSG);
                        msg.AddInt8("_outline", outlineVal);
                        fMessenger->SendMessage(&msg);
                        break;
                }

                case CYCLING_FONTS_MSG:
                {
                        fCyclingFontButton->SetLabel(fCycleFonts ? \
                                B_TRANSLATE("Cycle fonts") : B_TRANSLATE("Stop cycling"));
                        fCycleFonts = !fCycleFonts;

                        if (fCycleFonts) {
                                delete fMessageRunner;
                                fMessageRunner = new BMessageRunner(this,
                                        new BMessage(CYCLING_FONTS_UPDATE_MSG), 360000*2, -1);
                                printf("Cycle fonts enabled\n");
                        } else {
                                // Delete our MessageRunner and reset the style index
                                delete fMessageRunner;
                                fMessageRunner = NULL;
                                fFontStyleindex = 0;
                                printf("Cycle fonts disabled\n");
                        }
                        break;
                }

                case CYCLING_FONTS_UPDATE_MSG:
                {
                        int32 familyindex = -1;
                        BMenuItem* currentFamilyItem = fFontFamilyMenu->FindMarked();

                        if (currentFamilyItem) {
                                familyindex = fFontFamilyMenu->IndexOf(currentFamilyItem);
                                const int32 installedStyles = count_font_styles(
                                        const_cast<char*>(currentFamilyItem->Label()));

                                BMenu* submenu = currentFamilyItem->Submenu();
                                if (submenu == NULL) {
                                        printf("Failed to get style.\n");
                                        return;
                                }
                                BMenuItem* markedStyle = submenu->FindMarked();
                                fFontStyleindex = submenu->IndexOf(markedStyle);

                                if (fFontStyleindex < installedStyles - 1)
                                        fFontStyleindex++;
                                else {
                                        fFontStyleindex = 0;

                                        if (familyindex < count_font_families() - 1)
                                                familyindex++;
                                        else
                                                familyindex = 0;
                                }

                                BMenuItem* newFontFamilyItem = fFontFamilyMenu->ItemAt(familyindex);
                                BMenuItem* newstyleitem = submenu->ItemAt(fFontStyleindex);

                                if (newFontFamilyItem && newstyleitem) {
                                        if (msg->AddString("_style", newstyleitem->Label()) != B_OK
                                                || msg->AddString("_family", newFontFamilyItem->Label()) != B_OK) {
                                                printf("Failed to add style or family to the message\n");
                                                return;
                                        }
                                        printf("InstalledStyles(%" B_PRId32 "), Font(%s), Style(%s)\n",
                                                installedStyles, newFontFamilyItem->Label(),
                                                newstyleitem->Label());
                                        _UpdateAndSendStyle(msg);
                                }
                        }
                        break;
                }

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


void
ControlView::SetTarget(BHandler* handler)
{
        delete fMessenger;
        fMessenger = new BMessenger(handler);
        fDrawingModeMenu->SetTargetForItems(handler);
}


void
ControlView::_UpdateFontmenus(bool setInitialfont)
{
        BFont font;
        BMenu* stylemenu = NULL;

        font_family fontFamilyName, currentFamily;
        font_style fontStyleName, currentStyle;

        GetFont(&font);
        font.GetFamilyAndStyle(&currentFamily, &currentStyle);

        const int32 fontfamilies = count_font_families();

        fFontFamilyMenu->RemoveItems(0, fFontFamilyMenu->CountItems(), true);

        for (int32 i = 0; i < fontfamilies; i++) {
                if (get_font_family(i, &fontFamilyName) == B_OK) {
                        stylemenu = new BMenu(fontFamilyName);
                        stylemenu->SetLabelFromMarked(false);
                        const int32 styles = count_font_styles(fontFamilyName);

                        BMessage* familyMsg = new BMessage(FONTFAMILY_CHANGED_MSG);
                        familyMsg->AddString("_family", fontFamilyName);
                        BMenuItem* familyItem = new BMenuItem(stylemenu, familyMsg);
                        fFontFamilyMenu->AddItem(familyItem);

                        for (int32 j = 0; j < styles; j++) {
                                if (get_font_style(fontFamilyName, j, &fontStyleName) == B_OK) {
                                        BMessage* fontMsg = new BMessage(FONTSTYLE_CHANGED_MSG);
                                        fontMsg->AddString("_family", fontFamilyName);
                                        fontMsg->AddString("_style", fontStyleName);

                                        BMenuItem* styleItem = new BMenuItem(fontStyleName, fontMsg);
                                        styleItem->SetMarked(false);

                                        // setInitialfont is used when we attach the FontField
                                        if (!strcmp(fontStyleName, currentStyle)
                                                && !strcmp(fontFamilyName, currentFamily)
                                                && setInitialfont) {
                                                styleItem->SetMarked(true);
                                                familyItem->SetMarked(true);

                                                BString string;
                                                string << currentFamily << " " << currentStyle;

                                                if (fFontMenuField)
                                                        fFontMenuField->MenuItem()->SetLabel(string.String());
                                        }
                                        stylemenu->AddItem(styleItem);
                                }
                        }

                        stylemenu->SetRadioMode(true);
                        stylemenu->SetTargetForItems(this);
                }
        }

        fFontFamilyMenu->SetLabelFromMarked(false);
        fFontFamilyMenu->SetTargetForItems(this);
}


void
ControlView::_AddFontMenu()
{
        fFontFamilyMenu = new BPopUpMenu("fontfamlilymenu");

        fFontMenuField = new BMenuField("FontMenuField",
                B_TRANSLATE("Font:"), fFontFamilyMenu);
        AddChild(fFontMenuField);

        _UpdateFontmenus(true);
}


void
ControlView::_AddDrawingModeMenu()
{
        fDrawingModeMenu = new BPopUpMenu("drawingmodemenu");

        BMessage* drawingMsg = NULL;
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_COPY);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_COPY", drawingMsg));
        fDrawingModeMenu->ItemAt(0)->SetMarked(true);
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_OVER);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_OVER", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_ERASE);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_ERASE", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_INVERT);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_INVERT", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_ADD);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_ADD", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_SUBTRACT);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_SUBTRACT", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_BLEND);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_BLEND", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_MIN);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_MIN", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_MAX);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_MAX", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_SELECT);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_SELECT", drawingMsg));
        drawingMsg = new BMessage(DRAWINGMODE_CHANGED_MSG);
        drawingMsg->AddInt32("_mode", B_OP_ALPHA);
        fDrawingModeMenu->AddItem(new BMenuItem("B_OP_ALPHA", drawingMsg));

        fDrawingModeMenu->SetLabelFromMarked(true);

        BMenuField* drawingModeMenuField = new BMenuField("FontMenuField",
                B_TRANSLATE("Drawing mode:"), fDrawingModeMenu);
        AddChild(drawingModeMenuField);
}


void
ControlView::_UpdateAndSendFamily(const BMessage* message)
{
        _DeselectOldItems();

        const char* family;
        font_style style;

        if (message->FindString("_family", &family) == B_OK) {
                char* name;
                type_code typeFound = 0;
                int32 countFound = 0;
                if (message->GetInfo(B_ANY_TYPE, 0, &name, &typeFound,
                                &countFound) == B_OK) {
                        if (typeFound == B_STRING_TYPE) {
                                BString string;
                                if (message->FindString(name, 0, &string) == B_OK)
                                        printf("Family: %s\n", string.String());
                        }
                }

                BMenuItem* markedItem = fFontFamilyMenu->FindItem(family);
                if (!markedItem)
                        return;

                markedItem->SetMarked(true);

                get_font_style((char*)family, 0, &style);

                BString string;
                string << family << " " << style;

                if (fFontMenuField)
                        fFontMenuField->MenuItem()->SetLabel(string.String());

                BMenu* submenu = markedItem->Submenu();

                if (submenu) {
                        BMenuItem* styleItem = submenu->FindItem(style);
                        if (styleItem && !styleItem->IsMarked())
                                styleItem->SetMarked(true);
                }

                BMessage fontMsg(FONTFAMILY_CHANGED_MSG);
                if (fontMsg.AddMessage("_fontMessage", message) == B_OK)
                        fMessenger->SendMessage(&fontMsg);
        }
}


void
ControlView::_UpdateAndSendStyle(const BMessage* message)
{
        _DeselectOldItems();

        const char* style;
        const char* family;
        if (message->FindString("_style", &style) == B_OK
                && message->FindString("_family", &family) == B_OK) {
                BMenuItem* familyItem = fFontFamilyMenu->FindItem(family);

                if (familyItem && !familyItem->IsMarked()) {
                        familyItem->SetMarked(true);

                        BMenu* submenu = familyItem->Submenu();
                        if (submenu) {
                                BMenuItem* styleItem = submenu->FindItem(style);
                                if (styleItem && !styleItem->IsMarked())
                                        styleItem->SetMarked(true);
                        }
                        printf("Family: %s, Style: %s\n", family, style);
                }

                BString string;
                string << family << " " << style;

                if (fFontMenuField)
                        fFontMenuField->MenuItem()->SetLabel(string.String());
        }

        BMessage fontMsg(FONTSTYLE_CHANGED_MSG);
        if (fontMsg.AddMessage("_fontMessage", message) == B_OK)
                fMessenger->SendMessage(&fontMsg);
}


void
ControlView::_DeselectOldItems()
{
        BMenuItem* oldItem = fFontFamilyMenu->FindMarked();
        if (oldItem) {
                oldItem->SetMarked(false);

                BMenu* submenu = oldItem->Submenu();
                if (submenu) {
                        BMenuItem* marked = submenu->FindMarked();
                        if (marked)
                                marked->SetMarked(false);
                }
        }
}