* Copyright 2001-2010, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Mark Hogben
* DarkWyrm <bpmagic@columbus.rr.com>
* Axel Dörfler, axeld@pinc-software.de
* Philippe Saint-Pierre, stpere@gmail.com
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "FontSelectionView.h"
#include "private/interface/FontPrivate.h"
#include <Box.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <GroupLayoutBuilder.h>
#include <LayoutItem.h>
#include <Locale.h>
#include <Looper.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <String.h>
#include <TextView.h>
#include <stdio.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Font Selection view"
static const float kMinSize = 8.0;
static const float kMaxSize = 18.0;
static const char* kPreviewText = B_TRANSLATE_COMMENT(
"The quick brown fox jumps over the lazy dog.",
"Don't translate this literally ! Use a phrase showing all "
"chars from A to Z.");
static const int32 kMsgSetFamily = 'fmly';
static const int32 kMsgSetStyle = 'styl';
static const int32 kMsgSetSize = 'size';
FontSelectionView::FontSelectionView(const char* name, const char* label,
bool separateStyles, const BFont* currentFont)
:
BHandler(name),
fMessage(NULL),
fTarget(NULL)
{
if (currentFont == NULL)
fCurrentFont = _DefaultFont();
else
fCurrentFont = *currentFont;
fSavedFont = fCurrentFont;
fSizesMenu = new BPopUpMenu("size menu");
fFontsMenu = new BPopUpMenu("font menu");
fFontsMenuField = new BMenuField("fonts", label, fFontsMenu, B_WILL_DRAW);
fFontsMenuField->SetAlignment(B_ALIGN_RIGHT);
if (separateStyles) {
fStylesMenu = new BPopUpMenu("styles menu");
fStylesMenuField = new BMenuField("styles", B_TRANSLATE("Style:"),
fStylesMenu, B_WILL_DRAW);
} else {
fStylesMenu = NULL;
fStylesMenuField = NULL;
}
fSizesMenuField = new BMenuField("size", B_TRANSLATE("Size:"), fSizesMenu,
B_WILL_DRAW);
fSizesMenuField->SetAlignment(B_ALIGN_RIGHT);
fPreviewTextView = new BTextView("preview text");
fPreviewTextView->SetFontAndColor(&fCurrentFont);
fPreviewTextView->SetText(kPreviewText);
fPreviewTextView->MakeResizable(false);
fPreviewTextView->SetWordWrap(true);
fPreviewTextView->MakeEditable(false);
fPreviewTextView->MakeSelectable(false);
fPreviewTextView->SetInsets(0, 0, 0, 0);
fPreviewTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fPreviewTextView->SetHighUIColor(B_PANEL_TEXT_COLOR);
fPreviewTextWidth = be_control_look->DefaultLabelSpacing() * 58.0f;
float lineCount = ceilf(fCurrentFont.StringWidth(kPreviewText) / fPreviewTextWidth);
fPreviewTextView->SetExplicitSize(
BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * lineCount));
fPreviewBox = new BBox("preview box", B_WILL_DRAW | B_FRAME_EVENTS);
fPreviewBox->AddChild(BGroupLayoutBuilder(B_VERTICAL)
.AddGroup(B_HORIZONTAL, 0)
.Add(fPreviewTextView)
.AddGlue()
.End()
.SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING,
B_USE_SMALL_SPACING, B_USE_SMALL_SPACING)
.TopView()
);
_UpdateFontPreview();
}
FontSelectionView::~FontSelectionView()
{
if (!fPreviewTextView->Window())
delete fPreviewTextView;
if (!fSizesMenuField->Window())
delete fSizesMenuField;
if (fStylesMenuField && !fStylesMenuField->Window())
delete fStylesMenuField;
if (!fFontsMenuField->Window())
delete fFontsMenuField;
delete fMessage;
}
void
FontSelectionView::AttachedToLooper()
{
_BuildSizesMenu();
UpdateFontsMenu();
}
void
FontSelectionView::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_COLORS_UPDATED:
{
if (message->HasColor(ui_color_name(B_PANEL_TEXT_COLOR))) {
rgb_color textColor;
if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &textColor) == B_OK)
fPreviewTextView->SetFontAndColor(&fCurrentFont, B_FONT_ALL, &textColor);
}
break;
}
case kMsgSetSize:
{
int32 size;
if (message->FindInt32("size", &size) != B_OK
|| size == fCurrentFont.Size())
break;
fCurrentFont.SetSize(size);
_UpdateFontPreview();
_Invoke();
break;
}
case kMsgSetFamily:
{
const char* family;
if (message->FindString("family", &family) != B_OK)
break;
font_style style;
fCurrentFont.GetFamilyAndStyle(NULL, &style);
BMenuItem* familyItem = fFontsMenu->FindItem(family);
if (familyItem != NULL) {
_SelectCurrentFont(false);
BMenuItem* styleItem;
if (fStylesMenuField != NULL)
styleItem = fStylesMenuField->Menu()->FindMarked();
else {
styleItem = familyItem->Submenu()->FindItem(style);
if (styleItem == NULL)
styleItem = familyItem->Submenu()->ItemAt(0);
}
if (styleItem != NULL) {
styleItem->SetMarked(true);
fCurrentFont.SetFamilyAndStyle(family, styleItem->Label());
_UpdateFontPreview();
}
if (fStylesMenuField != NULL)
_AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
}
_Invoke();
break;
}
case kMsgSetStyle:
{
const char* family;
const char* style;
if (message->FindString("family", &family) != B_OK
|| message->FindString("style", &style) != B_OK)
break;
BMenuItem *familyItem = fFontsMenu->FindItem(family);
if (!familyItem)
break;
_SelectCurrentFont(false);
familyItem->SetMarked(true);
fCurrentFont.SetFamilyAndStyle(family, style);
_UpdateFontPreview();
_Invoke();
break;
}
default:
BHandler::MessageReceived(message);
}
}
void
FontSelectionView::SetMessage(BMessage* message)
{
delete fMessage;
fMessage = message;
}
void
FontSelectionView::SetTarget(BHandler* target)
{
fTarget = target;
}
void
FontSelectionView::SetFont(const BFont& font, float size)
{
BFont resizedFont(font);
resizedFont.SetSize(size);
SetFont(resizedFont);
}
void
FontSelectionView::SetFont(const BFont& font)
{
if (font == fCurrentFont && font == fSavedFont)
return;
_SelectCurrentFont(false);
fSavedFont = fCurrentFont = font;
_UpdateFontPreview();
_SelectCurrentFont(true);
_SelectCurrentSize(true);
}
void
FontSelectionView::SetSize(float size)
{
SetFont(fCurrentFont, size);
}
const BFont&
FontSelectionView::Font() const
{
return fCurrentFont;
}
void
FontSelectionView::SetDefaults()
{
BFont defaultFont = _DefaultFont();
if (defaultFont == fCurrentFont)
return;
_SelectCurrentFont(false);
fCurrentFont = defaultFont;
_UpdateFontPreview();
_SelectCurrentFont(true);
_SelectCurrentSize(true);
}
void
FontSelectionView::Revert()
{
if (!IsRevertable())
return;
_SelectCurrentFont(false);
fCurrentFont = fSavedFont;
_UpdateFontPreview();
_SelectCurrentFont(true);
_SelectCurrentSize(true);
}
bool
FontSelectionView::IsDefaultable()
{
return fCurrentFont != _DefaultFont();
}
bool
FontSelectionView::IsRevertable()
{
return fCurrentFont != fSavedFont;
}
void
FontSelectionView::UpdateFontsMenu()
{
int32 numFamilies = count_font_families();
fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true);
BFont font = fCurrentFont;
font_family currentFamily;
font_style currentStyle;
font.GetFamilyAndStyle(¤tFamily, ¤tStyle);
for (int32 i = 0; i < numFamilies; i++) {
font_family family;
uint32 flags;
if (get_font_family(i, &family, &flags) != B_OK)
continue;
if (strcmp(Name(), "fixed") == 0
&& (flags & (B_IS_FIXED | B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED)) == 0) {
continue;
}
font.SetFamilyAndFace(family, B_REGULAR_FACE);
BMessage* message = new BMessage(kMsgSetFamily);
message->AddString("family", family);
message->AddString("name", Name());
BMenuItem* familyItem;
if (fStylesMenuField != NULL) {
familyItem = new BMenuItem(family, message);
} else {
BMenu* stylesMenu = new BMenu(family);
_AddStylesToMenu(font, stylesMenu);
familyItem = new BMenuItem(stylesMenu, message);
}
familyItem->SetMarked(strcmp(family, currentFamily) == 0);
fFontsMenu->AddItem(familyItem);
familyItem->SetTarget(this);
}
if (fStylesMenuField != NULL)
_AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
}
BLayoutItem*
FontSelectionView::CreateSizesLabelLayoutItem()
{
return fSizesMenuField->CreateLabelLayoutItem();
}
BLayoutItem*
FontSelectionView::CreateSizesMenuBarLayoutItem()
{
return fSizesMenuField->CreateMenuBarLayoutItem();
}
BLayoutItem*
FontSelectionView::CreateFontsLabelLayoutItem()
{
return fFontsMenuField->CreateLabelLayoutItem();
}
BLayoutItem*
FontSelectionView::CreateFontsMenuBarLayoutItem()
{
return fFontsMenuField->CreateMenuBarLayoutItem();
}
BLayoutItem*
FontSelectionView::CreateStylesLabelLayoutItem()
{
if (fStylesMenuField)
return fStylesMenuField->CreateLabelLayoutItem();
return NULL;
}
BLayoutItem*
FontSelectionView::CreateStylesMenuBarLayoutItem()
{
if (fStylesMenuField)
return fStylesMenuField->CreateMenuBarLayoutItem();
return NULL;
}
BView*
FontSelectionView::PreviewBox() const
{
return fPreviewBox;
}
void
FontSelectionView::_Invoke()
{
if (fTarget != NULL && fTarget->Looper() != NULL && fMessage != NULL) {
BMessage message(*fMessage);
fTarget->Looper()->PostMessage(&message, fTarget);
}
}
BFont
FontSelectionView::_DefaultFont() const
{
if (strcmp(Name(), "bold") == 0)
return *be_bold_font;
if (strcmp(Name(), "fixed") == 0)
return *be_fixed_font;
else
return *be_plain_font;
}
void
FontSelectionView::_SelectCurrentFont(bool select)
{
font_family family;
font_style style;
fCurrentFont.GetFamilyAndStyle(&family, &style);
BMenuItem *item = fFontsMenu->FindItem(family);
if (item != NULL) {
item->SetMarked(select);
if (item->Submenu() != NULL) {
item = item->Submenu()->FindItem(style);
if (item != NULL)
item->SetMarked(select);
}
}
}
void
FontSelectionView::_SelectCurrentSize(bool select)
{
char label[16];
snprintf(label, sizeof(label), "%" B_PRId32, (int32)fCurrentFont.Size());
BMenuItem* item = fSizesMenu->FindItem(label);
if (item != NULL)
item->SetMarked(select);
}
void
FontSelectionView::_UpdateFontPreview()
{
fPreviewTextView->SetFontAndColor(&fCurrentFont);
fPreviewTextView->SetExplicitSize(
BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * fPreviewTextView->CountLines()));
}
void
FontSelectionView::_BuildSizesMenu()
{
const int32 sizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 18, 21, 24, 0};
for (int32 i = 0; sizes[i]; i++) {
int32 size = sizes[i];
if (size < kMinSize || size > kMaxSize)
continue;
char label[32];
snprintf(label, sizeof(label), "%" B_PRId32, size);
BMessage* message = new BMessage(kMsgSetSize);
message->AddInt32("size", size);
message->AddString("name", Name());
BMenuItem* item = new BMenuItem(label, message);
if (size == fCurrentFont.Size())
item->SetMarked(true);
fSizesMenu->AddItem(item);
item->SetTarget(this);
}
}
void
FontSelectionView::_AddStylesToMenu(const BFont& font, BMenu* stylesMenu) const
{
stylesMenu->RemoveItems(0, stylesMenu->CountItems(), true);
stylesMenu->SetRadioMode(true);
font_family family;
font_style style;
font.GetFamilyAndStyle(&family, &style);
BString currentStyle(style);
int32 numStyles = count_font_styles(family);
for (int32 j = 0; j < numStyles; j++) {
if (get_font_style(family, j, &style) != B_OK)
continue;
BMessage* message = new BMessage(kMsgSetStyle);
message->AddString("family", (char*)family);
message->AddString("style", (char*)style);
BMenuItem* item = new BMenuItem(style, message);
item->SetMarked(currentStyle == style);
stylesMenu->AddItem(item);
item->SetTarget(this);
}
}