1/* 2 * Copyright 2001-2022 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Mark Hogben 7 * DarkWyrm <bpmagic@columbus.rr.com> 8 * Axel D��rfler, axeld@pinc-software.de 9 * Philippe Saint-Pierre, stpere@gmail.com 10 * Stephan A��mus <superstippi@gmx.de> 11 * John Scipione, jscipione@gmail.com 12 */ 13 14 15#include "FontSelectionView.h" 16 17#include <Box.h> 18#include <Catalog.h> 19#include <ControlLook.h> 20#include <GroupLayoutBuilder.h> 21#include <LayoutItem.h> 22#include <Locale.h> 23#include <MenuField.h> 24#include <MenuItem.h> 25#include <PopUpMenu.h> 26#include <String.h> 27#include <TextView.h> 28#include <Spinner.h> 29 30#include <FontPrivate.h> 31 32#include <stdio.h> 33 34 35#undef B_TRANSLATION_CONTEXT 36#define B_TRANSLATION_CONTEXT "Font Selection view" 37 38 39#define INSTANT_UPDATE 40 // if defined, the system font will be updated immediately, and not 41 // only on exit 42 43static const float kMinSize = 8.0; 44static const float kMaxSize = 72.0; 45 46static const char* kPreviewText = B_TRANSLATE_COMMENT( 47 "The quick brown fox jumps over the lazy dog.", 48 "Don't translate this literally ! Use a phrase showing all chars " 49 "from A to Z."); 50 51 52// private font API 53extern void _set_system_font_(const char *which, font_family family, 54 font_style style, float size); 55extern status_t _get_system_default_font_(const char* which, 56 font_family family, font_style style, float* _size); 57 58 59#ifdef B_BEOS_VERSION_DANO 60// this call only exists under R5 61void 62_set_system_font_(const char *which, font_family family, 63 font_style style, float size) 64{ 65 puts("you don't have _set_system_font_()"); 66} 67#endif 68 69#if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 70// this call only exists under Haiku (and the test environment) 71status_t 72_get_system_default_font_(const char* which, font_family family, 73 font_style style, float* _size) 74{ 75 puts("you don't have _get_system_default_font_()"); 76 return B_ERROR; 77} 78#endif 79 80 81// #pragma mark - 82 83 84FontSelectionView::FontSelectionView(const char* name, 85 const char* label, const BFont* currentFont) 86 : 87 BView(name, B_WILL_DRAW), 88 fMessageTarget(this) 89{ 90 if (currentFont == NULL) { 91 if (!strcmp(Name(), "plain")) 92 fCurrentFont = *be_plain_font; 93 else if (!strcmp(Name(), "bold")) 94 fCurrentFont = *be_bold_font; 95 else if (!strcmp(Name(), "fixed")) 96 fCurrentFont = *be_fixed_font; 97 else if (!strcmp(Name(), "menu")) { 98 menu_info info; 99 get_menu_info(&info); 100 101 fCurrentFont.SetFamilyAndStyle(info.f_family, info.f_style); 102 fCurrentFont.SetSize(info.font_size); 103 } 104 } else 105 fCurrentFont = *currentFont; 106 107 fSavedFont = fCurrentFont; 108 109 fFontsMenu = new BPopUpMenu("font menu"); 110 111 // font menu 112 fFontsMenuField = new BMenuField("fonts", label, fFontsMenu); 113 fFontsMenuField->SetAlignment(B_ALIGN_RIGHT); 114 115 // font size 116 BMessage* fontSizeMessage = new BMessage(kMsgSetSize); 117 fontSizeMessage->AddString("name", Name()); 118 119 fFontSizeSpinner = new BSpinner("font size", B_TRANSLATE("Size:"), 120 fontSizeMessage); 121 122 fFontSizeSpinner->SetRange(kMinSize, kMaxSize); 123 fFontSizeSpinner->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 124 B_SIZE_UNSET)); 125 126 // preview 127 // A string view would be enough if only it handled word-wrap. 128 fPreviewTextView = new BTextView("preview text"); 129 fPreviewTextView->SetFontAndColor(&fCurrentFont); 130 fPreviewTextView->SetText(kPreviewText); 131 fPreviewTextView->MakeResizable(false); 132 fPreviewTextView->SetWordWrap(true); 133 fPreviewTextView->MakeEditable(false); 134 fPreviewTextView->MakeSelectable(false); 135 fPreviewTextView->SetInsets(0, 0, 0, 0); 136 fPreviewTextView->SetViewUIColor(ViewUIColor()); 137 fPreviewTextView->SetLowUIColor(LowUIColor()); 138 fPreviewTextView->SetHighUIColor(HighUIColor()); 139 140 // determine initial line count using fCurrentFont 141 fPreviewTextWidth = be_control_look->DefaultLabelSpacing() * 58.0f; 142 float lineCount = ceilf(fCurrentFont.StringWidth(kPreviewText) 143 / fPreviewTextWidth); 144 fPreviewTextView->SetExplicitSize(BSize(fPreviewTextWidth, 145 fPreviewTextView->LineHeight(0) * lineCount)); 146 147 // box around preview 148 fPreviewBox = new BBox("preview box", B_WILL_DRAW | B_FRAME_EVENTS); 149 fPreviewBox->AddChild(BGroupLayoutBuilder(B_VERTICAL) 150 .AddGroup(B_HORIZONTAL, 0) 151 .Add(fPreviewTextView) 152 .AddGlue() 153 .End() 154 .SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING, 155 B_USE_SMALL_SPACING, B_USE_SMALL_SPACING) 156 .TopView() 157 ); 158 159 _SelectCurrentSize(); 160} 161 162 163FontSelectionView::~FontSelectionView() 164{ 165#ifndef INSTANT_UPDATE 166 _UpdateSystemFont(); 167#endif 168} 169 170 171void 172FontSelectionView::SetTarget(BHandler* messageTarget) 173{ 174 fMessageTarget = messageTarget; 175 fFontSizeSpinner->SetTarget(messageTarget); 176} 177 178 179void 180FontSelectionView::MessageReceived(BMessage* msg) 181{ 182 switch (msg->what) { 183 case kMsgSetSize: 184 { 185 int32 size = fFontSizeSpinner->Value(); 186 if (size == fCurrentFont.Size()) 187 break; 188 189 fCurrentFont.SetSize(size); 190 _UpdateFontPreview(); 191 break; 192 } 193 194 case kMsgSetFamily: 195 { 196 const char* family; 197 if (msg->FindString("family", &family) != B_OK) 198 break; 199 200 font_style style; 201 fCurrentFont.GetFamilyAndStyle(NULL, &style); 202 203 BMenuItem* familyItem = fFontsMenu->FindItem(family); 204 if (familyItem != NULL) { 205 _SelectCurrentFont(false); 206 207 BMenuItem* item = familyItem->Submenu()->FindItem(style); 208 if (item == NULL) 209 item = familyItem->Submenu()->ItemAt(0); 210 211 if (item != NULL) { 212 item->SetMarked(true); 213 fCurrentFont.SetFamilyAndStyle(family, item->Label()); 214 _UpdateFontPreview(); 215 } 216 } 217 break; 218 } 219 220 case kMsgSetStyle: 221 { 222 const char* family; 223 const char* style; 224 if (msg->FindString("family", &family) != B_OK 225 || msg->FindString("style", &style) != B_OK) 226 break; 227 228 BMenuItem *familyItem = fFontsMenu->FindItem(family); 229 if (!familyItem) 230 break; 231 232 _SelectCurrentFont(false); 233 familyItem->SetMarked(true); 234 235 fCurrentFont.SetFamilyAndStyle(family, style); 236 _UpdateFontPreview(); 237 break; 238 } 239 240 default: 241 BView::MessageReceived(msg); 242 } 243} 244 245 246BView* 247FontSelectionView::GetPreviewBox() const 248{ 249 return fPreviewBox; 250} 251 252 253BView* 254FontSelectionView::GetFontSizeSpinner() const 255{ 256 return fFontSizeSpinner; 257} 258 259 260BLayoutItem* 261FontSelectionView::CreateFontsLabelLayoutItem() const 262{ 263 return fFontsMenuField->CreateLabelLayoutItem(); 264} 265 266 267BLayoutItem* 268FontSelectionView::CreateFontsMenuBarLayoutItem() const 269{ 270 return fFontsMenuField->CreateMenuBarLayoutItem(); 271} 272 273 274void 275FontSelectionView::_SelectCurrentFont(bool select) 276{ 277 font_family family; 278 font_style style; 279 fCurrentFont.GetFamilyAndStyle(&family, &style); 280 281 BMenuItem *item = fFontsMenu->FindItem(family); 282 if (item != NULL) { 283 item->SetMarked(select); 284 285 if (item->Submenu() != NULL) { 286 item = item->Submenu()->FindItem(style); 287 if (item != NULL) 288 item->SetMarked(select); 289 } 290 } 291} 292 293 294void 295FontSelectionView::_SelectCurrentSize() 296{ 297 fFontSizeSpinner->SetValue((int32)fCurrentFont.Size()); 298} 299 300 301void 302FontSelectionView::_UpdateFontPreview() 303{ 304#ifdef INSTANT_UPDATE 305 _UpdateSystemFont(); 306#endif 307 308 fPreviewTextView->SetFontAndColor(&fCurrentFont); 309 fPreviewTextView->SetExplicitSize(BSize(fPreviewTextWidth, 310 fPreviewTextView->LineHeight(0) * fPreviewTextView->CountLines())); 311} 312 313 314void 315FontSelectionView::_UpdateSystemFont() 316{ 317 font_family family; 318 font_style style; 319 fCurrentFont.GetFamilyAndStyle(&family, &style); 320 321 if (strcmp(Name(), "menu") == 0) { 322 // The menu font is not handled as a system font 323 menu_info info; 324 get_menu_info(&info); 325 326 strlcpy(info.f_family, (const char*)family, B_FONT_FAMILY_LENGTH); 327 strlcpy(info.f_style, (const char*)style, B_FONT_STYLE_LENGTH); 328 info.font_size = fCurrentFont.Size(); 329 330 set_menu_info(&info); 331 } else 332 _set_system_font_(Name(), family, style, fCurrentFont.Size()); 333} 334 335 336void 337FontSelectionView::SetDefaults() 338{ 339 font_family family; 340 font_style style; 341 float size; 342 const char* fontName; 343 344 if (strcmp(Name(), "menu") == 0) 345 fontName = "plain"; 346 else 347 fontName = Name(); 348 349 if (_get_system_default_font_(fontName, family, style, &size) != B_OK) { 350 Revert(); 351 return; 352 } 353 354 BFont defaultFont; 355 defaultFont.SetFamilyAndStyle(family, style); 356 defaultFont.SetSize(size); 357 358 if (defaultFont == fCurrentFont) 359 return; 360 361 _SelectCurrentFont(false); 362 363 fCurrentFont = defaultFont; 364 _UpdateFontPreview(); 365 366 _SelectCurrentFont(true); 367 _SelectCurrentSize(); 368} 369 370 371void 372FontSelectionView::Revert() 373{ 374 if (!IsRevertable()) 375 return; 376 377 _SelectCurrentFont(false); 378 379 fCurrentFont = fSavedFont; 380 _UpdateFontPreview(); 381 382 _SelectCurrentFont(true); 383 _SelectCurrentSize(); 384} 385 386 387bool 388FontSelectionView::IsDefaultable() 389{ 390 font_family defaultFamily; 391 font_style defaultStyle; 392 float defaultSize; 393 const char* fontName; 394 395 if (strcmp(Name(), "menu") == 0) 396 fontName = "plain"; 397 else 398 fontName = Name(); 399 400 if (_get_system_default_font_(fontName, defaultFamily, defaultStyle, 401 &defaultSize) != B_OK) { 402 return false; 403 } 404 405 font_family currentFamily; 406 font_style currentStyle; 407 float currentSize; 408 409 fCurrentFont.GetFamilyAndStyle(¤tFamily, ¤tStyle); 410 currentSize = fCurrentFont.Size(); 411 412 return strcmp(currentFamily, defaultFamily) != 0 413 || strcmp(currentStyle, defaultStyle) != 0 414 || currentSize != defaultSize; 415} 416 417 418bool 419FontSelectionView::IsRevertable() 420{ 421 return fCurrentFont != fSavedFont; 422} 423 424 425void 426FontSelectionView::UpdateFontsMenu() 427{ 428 int32 numFamilies = count_font_families(); 429 430 fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true); 431 BFont font; 432 fFontsMenu->GetFont(&font); 433 434 font_family currentFamily; 435 font_style currentStyle; 436 fCurrentFont.GetFamilyAndStyle(¤tFamily, ¤tStyle); 437 438 for (int32 i = 0; i < numFamilies; i++) { 439 font_family family; 440 uint32 flags; 441 if (get_font_family(i, &family, &flags) != B_OK) 442 continue; 443 444 // if we're setting the fixed font, we only want to show fixed and 445 // full-and-half-fixed fonts 446 if (strcmp(Name(), "fixed") == 0 447 && (flags 448 & (B_IS_FIXED | B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED)) == 0) { 449 continue; 450 } 451 452 BMenu* stylesMenu = new BMenu(family); 453 stylesMenu->SetRadioMode(true); 454 stylesMenu->SetFont(&font); 455 456 BMessage* message = new BMessage(kMsgSetFamily); 457 message->AddString("family", family); 458 message->AddString("name", Name()); 459 460 BMenuItem* familyItem = new BMenuItem(stylesMenu, message); 461 fFontsMenu->AddItem(familyItem); 462 463 int32 numStyles = count_font_styles(family); 464 465 for (int32 j = 0; j < numStyles; j++) { 466 font_style style; 467 if (get_font_style(family, j, &style, &flags) != B_OK) 468 continue; 469 470 message = new BMessage(kMsgSetStyle); 471 message->AddString("family", (char*)family); 472 message->AddString("style", (char*)style); 473 message->AddString("name", Name()); 474 475 BMenuItem* item = new BMenuItem(style, message); 476 477 if (!strcmp(style, currentStyle) 478 && !strcmp(family, currentFamily)) { 479 item->SetMarked(true); 480 familyItem->SetMarked(true); 481 } 482 stylesMenu->AddItem(item); 483 } 484 485 stylesMenu->SetTargetForItems(fMessageTarget); 486 } 487 488 fFontsMenu->SetTargetForItems(fMessageTarget); 489} 490