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