* Copyright 2001-2022 Haiku, Inc. All rights reserved.
* 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>
* John Scipione, jscipione@gmail.com
*/
#include "FontSelectionView.h"
#include <Box.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <GroupLayoutBuilder.h>
#include <LayoutItem.h>
#include <Locale.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <String.h>
#include <TextView.h>
#include <Spinner.h>
#include <FontPrivate.h>
#include <stdio.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Font Selection view"
#define INSTANT_UPDATE
// if defined, the system font will be updated immediately, and not
// only on exit
static const float kMinSize = 8.0;
static const float kMaxSize = 72.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.");
extern void _set_system_font_(const char *which, font_family family,
font_style style, float size);
extern status_t _get_system_default_font_(const char* which,
font_family family, font_style style, float* _size);
#ifdef B_BEOS_VERSION_DANO
void
_set_system_font_(const char *which, font_family family,
font_style style, float size)
{
puts("you don't have _set_system_font_()");
}
#endif
#if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
status_t
_get_system_default_font_(const char* which, font_family family,
font_style style, float* _size)
{
puts("you don't have _get_system_default_font_()");
return B_ERROR;
}
#endif
FontSelectionView::FontSelectionView(const char* name,
const char* label, const BFont* currentFont)
:
BView(name, B_WILL_DRAW),
fMessageTarget(this)
{
if (currentFont == NULL) {
if (!strcmp(Name(), "plain"))
fCurrentFont = *be_plain_font;
else if (!strcmp(Name(), "bold"))
fCurrentFont = *be_bold_font;
else if (!strcmp(Name(), "fixed"))
fCurrentFont = *be_fixed_font;
else if (!strcmp(Name(), "menu")) {
menu_info info;
get_menu_info(&info);
fCurrentFont.SetFamilyAndStyle(info.f_family, info.f_style);
fCurrentFont.SetSize(info.font_size);
}
} else
fCurrentFont = *currentFont;
fSavedFont = fCurrentFont;
fFontsMenu = new BPopUpMenu("font menu");
fFontsMenuField = new BMenuField("fonts", label, fFontsMenu);
fFontsMenuField->SetAlignment(B_ALIGN_RIGHT);
BMessage* fontSizeMessage = new BMessage(kMsgSetSize);
fontSizeMessage->AddString("name", Name());
fFontSizeSpinner = new BSpinner("font size", B_TRANSLATE("Size:"),
fontSizeMessage);
fFontSizeSpinner->SetRange(kMinSize, kMaxSize);
fFontSizeSpinner->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
B_SIZE_UNSET));
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(ViewUIColor());
fPreviewTextView->SetLowUIColor(LowUIColor());
fPreviewTextView->SetHighUIColor(HighUIColor());
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()
);
_SelectCurrentSize();
}
FontSelectionView::~FontSelectionView()
{
#ifndef INSTANT_UPDATE
_UpdateSystemFont();
#endif
}
void
FontSelectionView::SetTarget(BHandler* messageTarget)
{
fMessageTarget = messageTarget;
fFontSizeSpinner->SetTarget(messageTarget);
}
void
FontSelectionView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kMsgSetSize:
{
int32 size = fFontSizeSpinner->Value();
if (size == fCurrentFont.Size())
break;
fCurrentFont.SetSize(size);
_UpdateFontPreview();
break;
}
case kMsgSetFamily:
{
const char* family;
if (msg->FindString("family", &family) != B_OK)
break;
font_style style;
fCurrentFont.GetFamilyAndStyle(NULL, &style);
BMenuItem* familyItem = fFontsMenu->FindItem(family);
if (familyItem != NULL) {
_SelectCurrentFont(false);
BMenuItem* item = familyItem->Submenu()->FindItem(style);
if (item == NULL)
item = familyItem->Submenu()->ItemAt(0);
if (item != NULL) {
item->SetMarked(true);
fCurrentFont.SetFamilyAndStyle(family, item->Label());
_UpdateFontPreview();
}
}
break;
}
case kMsgSetStyle:
{
const char* family;
const char* style;
if (msg->FindString("family", &family) != B_OK
|| msg->FindString("style", &style) != B_OK)
break;
BMenuItem *familyItem = fFontsMenu->FindItem(family);
if (!familyItem)
break;
_SelectCurrentFont(false);
familyItem->SetMarked(true);
fCurrentFont.SetFamilyAndStyle(family, style);
_UpdateFontPreview();
break;
}
default:
BView::MessageReceived(msg);
}
}
BView*
FontSelectionView::GetPreviewBox() const
{
return fPreviewBox;
}
BView*
FontSelectionView::GetFontSizeSpinner() const
{
return fFontSizeSpinner;
}
BLayoutItem*
FontSelectionView::CreateFontsLabelLayoutItem() const
{
return fFontsMenuField->CreateLabelLayoutItem();
}
BLayoutItem*
FontSelectionView::CreateFontsMenuBarLayoutItem() const
{
return fFontsMenuField->CreateMenuBarLayoutItem();
}
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()
{
fFontSizeSpinner->SetValue((int32)fCurrentFont.Size());
}
void
FontSelectionView::_UpdateFontPreview()
{
#ifdef INSTANT_UPDATE
_UpdateSystemFont();
#endif
fPreviewTextView->SetFontAndColor(&fCurrentFont);
fPreviewTextView->SetExplicitSize(BSize(fPreviewTextWidth,
fPreviewTextView->LineHeight(0) * fPreviewTextView->CountLines()));
}
void
FontSelectionView::_UpdateSystemFont()
{
font_family family;
font_style style;
fCurrentFont.GetFamilyAndStyle(&family, &style);
if (strcmp(Name(), "menu") == 0) {
menu_info info;
get_menu_info(&info);
strlcpy(info.f_family, (const char*)family, B_FONT_FAMILY_LENGTH);
strlcpy(info.f_style, (const char*)style, B_FONT_STYLE_LENGTH);
info.font_size = fCurrentFont.Size();
set_menu_info(&info);
} else
_set_system_font_(Name(), family, style, fCurrentFont.Size());
}
void
FontSelectionView::SetDefaults()
{
font_family family;
font_style style;
float size;
const char* fontName;
if (strcmp(Name(), "menu") == 0)
fontName = "plain";
else
fontName = Name();
if (_get_system_default_font_(fontName, family, style, &size) != B_OK) {
Revert();
return;
}
BFont defaultFont;
defaultFont.SetFamilyAndStyle(family, style);
defaultFont.SetSize(size);
if (defaultFont == fCurrentFont)
return;
_SelectCurrentFont(false);
fCurrentFont = defaultFont;
_UpdateFontPreview();
_SelectCurrentFont(true);
_SelectCurrentSize();
}
void
FontSelectionView::Revert()
{
if (!IsRevertable())
return;
_SelectCurrentFont(false);
fCurrentFont = fSavedFont;
_UpdateFontPreview();
_SelectCurrentFont(true);
_SelectCurrentSize();
}
bool
FontSelectionView::IsDefaultable()
{
font_family defaultFamily;
font_style defaultStyle;
float defaultSize;
const char* fontName;
if (strcmp(Name(), "menu") == 0)
fontName = "plain";
else
fontName = Name();
if (_get_system_default_font_(fontName, defaultFamily, defaultStyle,
&defaultSize) != B_OK) {
return false;
}
font_family currentFamily;
font_style currentStyle;
float currentSize;
fCurrentFont.GetFamilyAndStyle(¤tFamily, ¤tStyle);
currentSize = fCurrentFont.Size();
return strcmp(currentFamily, defaultFamily) != 0
|| strcmp(currentStyle, defaultStyle) != 0
|| currentSize != defaultSize;
}
bool
FontSelectionView::IsRevertable()
{
return fCurrentFont != fSavedFont;
}
void
FontSelectionView::UpdateFontsMenu()
{
int32 numFamilies = count_font_families();
fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true);
BFont font;
fFontsMenu->GetFont(&font);
font_family currentFamily;
font_style currentStyle;
fCurrentFont.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;
}
BMenu* stylesMenu = new BMenu(family);
stylesMenu->SetRadioMode(true);
stylesMenu->SetFont(&font);
BMessage* message = new BMessage(kMsgSetFamily);
message->AddString("family", family);
message->AddString("name", Name());
BMenuItem* familyItem = new BMenuItem(stylesMenu, message);
fFontsMenu->AddItem(familyItem);
int32 numStyles = count_font_styles(family);
for (int32 j = 0; j < numStyles; j++) {
font_style style;
if (get_font_style(family, j, &style, &flags) != B_OK)
continue;
message = new BMessage(kMsgSetStyle);
message->AddString("family", (char*)family);
message->AddString("style", (char*)style);
message->AddString("name", Name());
BMenuItem* item = new BMenuItem(style, message);
if (!strcmp(style, currentStyle)
&& !strcmp(family, currentFamily)) {
item->SetMarked(true);
familyItem->SetMarked(true);
}
stylesMenu->AddItem(item);
}
stylesMenu->SetTargetForItems(fMessageTarget);
}
fFontsMenu->SetTargetForItems(fMessageTarget);
}