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