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