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 35static const float kMinSize = 8.0; 36static const float kMaxSize = 18.0; 37 38static const int32 kMsgSetFamily = 'fmly'; 39static const int32 kMsgSetStyle = 'styl'; 40static const int32 kMsgSetSize = 'size'; 41 42 43// #pragma mark - 44 45 46FontSelectionView::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 103FontSelectionView::~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 119void 120FontSelectionView::AttachedToLooper() 121{ 122 _BuildSizesMenu(); 123 UpdateFontsMenu(); 124} 125 126 127void 128FontSelectionView::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 206void 207FontSelectionView::SetMessage(BMessage* message) 208{ 209 delete fMessage; 210 fMessage = message; 211} 212 213 214void 215FontSelectionView::SetTarget(BHandler* target) 216{ 217 fTarget = target; 218} 219 220 221// #pragma mark - 222 223 224void 225FontSelectionView::SetFont(const BFont& font, float size) 226{ 227 BFont resizedFont(font); 228 resizedFont.SetSize(size); 229 SetFont(resizedFont); 230} 231 232 233void 234FontSelectionView::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 248void 249FontSelectionView::SetSize(float size) 250{ 251 SetFont(fCurrentFont, size); 252} 253 254 255const BFont& 256FontSelectionView::Font() const 257{ 258 return fCurrentFont; 259} 260 261 262void 263FontSelectionView::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 279void 280FontSelectionView::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 295bool 296FontSelectionView::IsDefaultable() 297{ 298 return fCurrentFont != _DefaultFont(); 299} 300 301 302bool 303FontSelectionView::IsRevertable() 304{ 305 return fCurrentFont != fSavedFont; 306} 307 308 309void 310FontSelectionView::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 365BLayoutItem* 366FontSelectionView::CreateSizesLabelLayoutItem() 367{ 368 return fSizesMenuField->CreateLabelLayoutItem(); 369} 370 371 372BLayoutItem* 373FontSelectionView::CreateSizesMenuBarLayoutItem() 374{ 375 return fSizesMenuField->CreateMenuBarLayoutItem(); 376} 377 378 379BLayoutItem* 380FontSelectionView::CreateFontsLabelLayoutItem() 381{ 382 return fFontsMenuField->CreateLabelLayoutItem(); 383} 384 385 386BLayoutItem* 387FontSelectionView::CreateFontsMenuBarLayoutItem() 388{ 389 return fFontsMenuField->CreateMenuBarLayoutItem(); 390} 391 392 393BLayoutItem* 394FontSelectionView::CreateStylesLabelLayoutItem() 395{ 396 if (fStylesMenuField) 397 return fStylesMenuField->CreateLabelLayoutItem(); 398 return NULL; 399} 400 401 402BLayoutItem* 403FontSelectionView::CreateStylesMenuBarLayoutItem() 404{ 405 if (fStylesMenuField) 406 return fStylesMenuField->CreateMenuBarLayoutItem(); 407 return NULL; 408} 409 410 411BView* 412FontSelectionView::PreviewBox() const 413{ 414 return fPreviewBox; 415} 416 417 418// #pragma mark - private 419 420 421void 422FontSelectionView::_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 431BFont 432FontSelectionView::_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 443void 444FontSelectionView::_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 463void 464FontSelectionView::_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 475void 476FontSelectionView::_UpdateFontPreview() 477{ 478 fPreviewText->SetFont(&fCurrentFont); 479} 480 481 482void 483FontSelectionView::_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 510void 511FontSelectionView::_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