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 43 static const float kMinSize = 8.0; 44 static const float kMaxSize = 72.0; 45 46 static 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 53 extern void _set_system_font_(const char *which, font_family family, 54 font_style style, float size); 55 extern 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 61 void 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) 71 status_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 84 FontSelectionView::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 163 FontSelectionView::~FontSelectionView() 164 { 165 #ifndef INSTANT_UPDATE 166 _UpdateSystemFont(); 167 #endif 168 } 169 170 171 void 172 FontSelectionView::SetTarget(BHandler* messageTarget) 173 { 174 fMessageTarget = messageTarget; 175 fFontSizeSpinner->SetTarget(messageTarget); 176 } 177 178 179 void 180 FontSelectionView::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 246 BView* 247 FontSelectionView::GetPreviewBox() const 248 { 249 return fPreviewBox; 250 } 251 252 253 BView* 254 FontSelectionView::GetFontSizeSpinner() const 255 { 256 return fFontSizeSpinner; 257 } 258 259 260 BLayoutItem* 261 FontSelectionView::CreateFontsLabelLayoutItem() const 262 { 263 return fFontsMenuField->CreateLabelLayoutItem(); 264 } 265 266 267 BLayoutItem* 268 FontSelectionView::CreateFontsMenuBarLayoutItem() const 269 { 270 return fFontsMenuField->CreateMenuBarLayoutItem(); 271 } 272 273 274 void 275 FontSelectionView::_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 294 void 295 FontSelectionView::_SelectCurrentSize() 296 { 297 fFontSizeSpinner->SetValue((int32)fCurrentFont.Size()); 298 } 299 300 301 void 302 FontSelectionView::_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 314 void 315 FontSelectionView::_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 336 void 337 FontSelectionView::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 371 void 372 FontSelectionView::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 387 bool 388 FontSelectionView::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 418 bool 419 FontSelectionView::IsRevertable() 420 { 421 return fCurrentFont != fSavedFont; 422 } 423 424 425 void 426 FontSelectionView::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