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