1 /* 2 * Copyright 2001-2010, 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 #include "FontSelectionView.h" 14 15 #include "private/interface/FontPrivate.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 <Looper.h> 24 #include <MenuField.h> 25 #include <MenuItem.h> 26 #include <PopUpMenu.h> 27 #include <String.h> 28 #include <TextView.h> 29 30 #include <stdio.h> 31 32 #undef B_TRANSLATION_CONTEXT 33 #define B_TRANSLATION_CONTEXT "Font Selection view" 34 35 36 static const float kMinSize = 8.0; 37 static const float kMaxSize = 18.0; 38 39 static const char* kPreviewText = B_TRANSLATE_COMMENT( 40 "The quick brown fox jumps over the lazy dog.", 41 "Don't translate this literally ! Use a phrase showing all " 42 "chars from A to Z."); 43 44 static const int32 kMsgSetFamily = 'fmly'; 45 static const int32 kMsgSetStyle = 'styl'; 46 static const int32 kMsgSetSize = 'size'; 47 48 49 // #pragma mark - 50 51 52 FontSelectionView::FontSelectionView(const char* name, const char* label, 53 bool separateStyles, const BFont* currentFont) 54 : 55 BHandler(name), 56 fMessage(NULL), 57 fTarget(NULL) 58 { 59 if (currentFont == NULL) 60 fCurrentFont = _DefaultFont(); 61 else 62 fCurrentFont = *currentFont; 63 64 fSavedFont = fCurrentFont; 65 66 fSizesMenu = new BPopUpMenu("size menu"); 67 fFontsMenu = new BPopUpMenu("font menu"); 68 69 // font menu 70 fFontsMenuField = new BMenuField("fonts", label, fFontsMenu, B_WILL_DRAW); 71 fFontsMenuField->SetAlignment(B_ALIGN_RIGHT); 72 73 // styles menu, if desired 74 if (separateStyles) { 75 fStylesMenu = new BPopUpMenu("styles menu"); 76 fStylesMenuField = new BMenuField("styles", B_TRANSLATE("Style:"), 77 fStylesMenu, B_WILL_DRAW); 78 } else { 79 fStylesMenu = NULL; 80 fStylesMenuField = NULL; 81 } 82 83 // size menu 84 fSizesMenuField = new BMenuField("size", B_TRANSLATE("Size:"), fSizesMenu, 85 B_WILL_DRAW); 86 fSizesMenuField->SetAlignment(B_ALIGN_RIGHT); 87 88 // preview 89 // A string view would be enough if only it handled word-wrap. 90 fPreviewTextView = new BTextView("preview text"); 91 fPreviewTextView->SetFontAndColor(&fCurrentFont); 92 fPreviewTextView->SetText(kPreviewText); 93 fPreviewTextView->MakeResizable(false); 94 fPreviewTextView->SetWordWrap(true); 95 fPreviewTextView->MakeEditable(false); 96 fPreviewTextView->MakeSelectable(false); 97 fPreviewTextView->SetInsets(0, 0, 0, 0); 98 fPreviewTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 99 fPreviewTextView->SetHighUIColor(B_PANEL_TEXT_COLOR); 100 101 // determine initial line count using fCurrentFont 102 fPreviewTextWidth = be_control_look->DefaultLabelSpacing() * 58.0f; 103 float lineCount = ceilf(fCurrentFont.StringWidth(kPreviewText) / fPreviewTextWidth); 104 fPreviewTextView->SetExplicitSize( 105 BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * lineCount)); 106 107 // box around preview 108 fPreviewBox = new BBox("preview box", B_WILL_DRAW | B_FRAME_EVENTS); 109 fPreviewBox->AddChild(BGroupLayoutBuilder(B_VERTICAL) 110 .AddGroup(B_HORIZONTAL, 0) 111 .Add(fPreviewTextView) 112 .AddGlue() 113 .End() 114 .SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING, 115 B_USE_SMALL_SPACING, B_USE_SMALL_SPACING) 116 .TopView() 117 ); 118 _UpdateFontPreview(); 119 } 120 121 122 FontSelectionView::~FontSelectionView() 123 { 124 // Some controls may not have been attached... 125 if (!fPreviewTextView->Window()) 126 delete fPreviewTextView; 127 if (!fSizesMenuField->Window()) 128 delete fSizesMenuField; 129 if (fStylesMenuField && !fStylesMenuField->Window()) 130 delete fStylesMenuField; 131 if (!fFontsMenuField->Window()) 132 delete fFontsMenuField; 133 134 delete fMessage; 135 } 136 137 138 void 139 FontSelectionView::AttachedToLooper() 140 { 141 _BuildSizesMenu(); 142 UpdateFontsMenu(); 143 } 144 145 146 void 147 FontSelectionView::MessageReceived(BMessage* message) 148 { 149 switch (message->what) { 150 case B_COLORS_UPDATED: 151 { 152 if (message->HasColor(ui_color_name(B_PANEL_TEXT_COLOR))) { 153 rgb_color textColor; 154 if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &textColor) == B_OK) 155 fPreviewTextView->SetFontAndColor(&fCurrentFont, B_FONT_ALL, &textColor); 156 } 157 break; 158 } 159 160 case kMsgSetSize: 161 { 162 int32 size; 163 if (message->FindInt32("size", &size) != B_OK 164 || size == fCurrentFont.Size()) 165 break; 166 167 fCurrentFont.SetSize(size); 168 _UpdateFontPreview(); 169 _Invoke(); 170 break; 171 } 172 173 case kMsgSetFamily: 174 { 175 const char* family; 176 if (message->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* styleItem; 187 if (fStylesMenuField != NULL) 188 styleItem = fStylesMenuField->Menu()->FindMarked(); 189 else { 190 styleItem = familyItem->Submenu()->FindItem(style); 191 if (styleItem == NULL) 192 styleItem = familyItem->Submenu()->ItemAt(0); 193 } 194 195 if (styleItem != NULL) { 196 styleItem->SetMarked(true); 197 fCurrentFont.SetFamilyAndStyle(family, styleItem->Label()); 198 _UpdateFontPreview(); 199 } 200 if (fStylesMenuField != NULL) 201 _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu()); 202 } 203 204 _Invoke(); 205 break; 206 } 207 208 case kMsgSetStyle: 209 { 210 const char* family; 211 const char* style; 212 if (message->FindString("family", &family) != B_OK 213 || message->FindString("style", &style) != B_OK) 214 break; 215 216 BMenuItem *familyItem = fFontsMenu->FindItem(family); 217 if (!familyItem) 218 break; 219 220 _SelectCurrentFont(false); 221 familyItem->SetMarked(true); 222 223 fCurrentFont.SetFamilyAndStyle(family, style); 224 _UpdateFontPreview(); 225 _Invoke(); 226 break; 227 } 228 229 default: 230 BHandler::MessageReceived(message); 231 } 232 } 233 234 235 void 236 FontSelectionView::SetMessage(BMessage* message) 237 { 238 delete fMessage; 239 fMessage = message; 240 } 241 242 243 void 244 FontSelectionView::SetTarget(BHandler* target) 245 { 246 fTarget = target; 247 } 248 249 250 // #pragma mark - 251 252 253 void 254 FontSelectionView::SetFont(const BFont& font, float size) 255 { 256 BFont resizedFont(font); 257 resizedFont.SetSize(size); 258 SetFont(resizedFont); 259 } 260 261 262 void 263 FontSelectionView::SetFont(const BFont& font) 264 { 265 if (font == fCurrentFont && font == fSavedFont) 266 return; 267 268 _SelectCurrentFont(false); 269 fSavedFont = fCurrentFont = font; 270 _UpdateFontPreview(); 271 272 _SelectCurrentFont(true); 273 _SelectCurrentSize(true); 274 } 275 276 277 void 278 FontSelectionView::SetSize(float size) 279 { 280 SetFont(fCurrentFont, size); 281 } 282 283 284 const BFont& 285 FontSelectionView::Font() const 286 { 287 return fCurrentFont; 288 } 289 290 291 void 292 FontSelectionView::SetDefaults() 293 { 294 BFont defaultFont = _DefaultFont(); 295 if (defaultFont == fCurrentFont) 296 return; 297 298 _SelectCurrentFont(false); 299 300 fCurrentFont = defaultFont; 301 _UpdateFontPreview(); 302 303 _SelectCurrentFont(true); 304 _SelectCurrentSize(true); 305 } 306 307 308 void 309 FontSelectionView::Revert() 310 { 311 if (!IsRevertable()) 312 return; 313 314 _SelectCurrentFont(false); 315 316 fCurrentFont = fSavedFont; 317 _UpdateFontPreview(); 318 319 _SelectCurrentFont(true); 320 _SelectCurrentSize(true); 321 } 322 323 324 bool 325 FontSelectionView::IsDefaultable() 326 { 327 return fCurrentFont != _DefaultFont(); 328 } 329 330 331 bool 332 FontSelectionView::IsRevertable() 333 { 334 return fCurrentFont != fSavedFont; 335 } 336 337 338 void 339 FontSelectionView::UpdateFontsMenu() 340 { 341 int32 numFamilies = count_font_families(); 342 343 fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true); 344 345 BFont font = fCurrentFont; 346 347 font_family currentFamily; 348 font_style currentStyle; 349 font.GetFamilyAndStyle(¤tFamily, ¤tStyle); 350 351 for (int32 i = 0; i < numFamilies; i++) { 352 font_family family; 353 uint32 flags; 354 if (get_font_family(i, &family, &flags) != B_OK) 355 continue; 356 357 // if we're setting the fixed font, we only want to show fixed and 358 // full-and-half-fixed fonts 359 if (strcmp(Name(), "fixed") == 0 360 && (flags & (B_IS_FIXED | B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED)) == 0) { 361 continue; 362 } 363 364 font.SetFamilyAndFace(family, B_REGULAR_FACE); 365 366 BMessage* message = new BMessage(kMsgSetFamily); 367 message->AddString("family", family); 368 message->AddString("name", Name()); 369 370 BMenuItem* familyItem; 371 if (fStylesMenuField != NULL) { 372 familyItem = new BMenuItem(family, message); 373 } else { 374 // Each family item has a submenu with all styles for that font. 375 BMenu* stylesMenu = new BMenu(family); 376 _AddStylesToMenu(font, stylesMenu); 377 familyItem = new BMenuItem(stylesMenu, message); 378 } 379 380 familyItem->SetMarked(strcmp(family, currentFamily) == 0); 381 fFontsMenu->AddItem(familyItem); 382 familyItem->SetTarget(this); 383 } 384 385 // Separate styles menu for only the current font. 386 if (fStylesMenuField != NULL) 387 _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu()); 388 } 389 390 391 // #pragma mark - private 392 393 394 BLayoutItem* 395 FontSelectionView::CreateSizesLabelLayoutItem() 396 { 397 return fSizesMenuField->CreateLabelLayoutItem(); 398 } 399 400 401 BLayoutItem* 402 FontSelectionView::CreateSizesMenuBarLayoutItem() 403 { 404 return fSizesMenuField->CreateMenuBarLayoutItem(); 405 } 406 407 408 BLayoutItem* 409 FontSelectionView::CreateFontsLabelLayoutItem() 410 { 411 return fFontsMenuField->CreateLabelLayoutItem(); 412 } 413 414 415 BLayoutItem* 416 FontSelectionView::CreateFontsMenuBarLayoutItem() 417 { 418 return fFontsMenuField->CreateMenuBarLayoutItem(); 419 } 420 421 422 BLayoutItem* 423 FontSelectionView::CreateStylesLabelLayoutItem() 424 { 425 if (fStylesMenuField) 426 return fStylesMenuField->CreateLabelLayoutItem(); 427 return NULL; 428 } 429 430 431 BLayoutItem* 432 FontSelectionView::CreateStylesMenuBarLayoutItem() 433 { 434 if (fStylesMenuField) 435 return fStylesMenuField->CreateMenuBarLayoutItem(); 436 return NULL; 437 } 438 439 440 BView* 441 FontSelectionView::PreviewBox() const 442 { 443 return fPreviewBox; 444 } 445 446 447 // #pragma mark - private 448 449 450 void 451 FontSelectionView::_Invoke() 452 { 453 if (fTarget != NULL && fTarget->Looper() != NULL && fMessage != NULL) { 454 BMessage message(*fMessage); 455 fTarget->Looper()->PostMessage(&message, fTarget); 456 } 457 } 458 459 460 BFont 461 FontSelectionView::_DefaultFont() const 462 { 463 if (strcmp(Name(), "bold") == 0) 464 return *be_bold_font; 465 if (strcmp(Name(), "fixed") == 0) 466 return *be_fixed_font; 467 else 468 return *be_plain_font; 469 } 470 471 472 void 473 FontSelectionView::_SelectCurrentFont(bool select) 474 { 475 font_family family; 476 font_style style; 477 fCurrentFont.GetFamilyAndStyle(&family, &style); 478 479 BMenuItem *item = fFontsMenu->FindItem(family); 480 if (item != NULL) { 481 item->SetMarked(select); 482 483 if (item->Submenu() != NULL) { 484 item = item->Submenu()->FindItem(style); 485 if (item != NULL) 486 item->SetMarked(select); 487 } 488 } 489 } 490 491 492 void 493 FontSelectionView::_SelectCurrentSize(bool select) 494 { 495 char label[16]; 496 snprintf(label, sizeof(label), "%" B_PRId32, (int32)fCurrentFont.Size()); 497 498 BMenuItem* item = fSizesMenu->FindItem(label); 499 if (item != NULL) 500 item->SetMarked(select); 501 } 502 503 504 void 505 FontSelectionView::_UpdateFontPreview() 506 { 507 fPreviewTextView->SetFontAndColor(&fCurrentFont); 508 fPreviewTextView->SetExplicitSize( 509 BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * fPreviewTextView->CountLines())); 510 } 511 512 513 void 514 FontSelectionView::_BuildSizesMenu() 515 { 516 const int32 sizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 18, 21, 24, 0}; 517 518 // build size menu 519 for (int32 i = 0; sizes[i]; i++) { 520 int32 size = sizes[i]; 521 if (size < kMinSize || size > kMaxSize) 522 continue; 523 524 char label[32]; 525 snprintf(label, sizeof(label), "%" B_PRId32, size); 526 527 BMessage* message = new BMessage(kMsgSetSize); 528 message->AddInt32("size", size); 529 message->AddString("name", Name()); 530 531 BMenuItem* item = new BMenuItem(label, message); 532 if (size == fCurrentFont.Size()) 533 item->SetMarked(true); 534 535 fSizesMenu->AddItem(item); 536 item->SetTarget(this); 537 } 538 } 539 540 541 void 542 FontSelectionView::_AddStylesToMenu(const BFont& font, BMenu* stylesMenu) const 543 { 544 stylesMenu->RemoveItems(0, stylesMenu->CountItems(), true); 545 stylesMenu->SetRadioMode(true); 546 547 font_family family; 548 font_style style; 549 font.GetFamilyAndStyle(&family, &style); 550 BString currentStyle(style); 551 552 int32 numStyles = count_font_styles(family); 553 554 for (int32 j = 0; j < numStyles; j++) { 555 if (get_font_style(family, j, &style) != B_OK) 556 continue; 557 558 BMessage* message = new BMessage(kMsgSetStyle); 559 message->AddString("family", (char*)family); 560 message->AddString("style", (char*)style); 561 562 BMenuItem* item = new BMenuItem(style, message); 563 item->SetMarked(currentStyle == style); 564 565 stylesMenu->AddItem(item); 566 item->SetTarget(this); 567 } 568 } 569 570