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 33static const float kMinSize = 8.0; 34static const float kMaxSize = 18.0; 35 36static const int32 kMsgSetFamily = 'fmly'; 37static const int32 kMsgSetStyle = 'styl'; 38static const int32 kMsgSetSize = 'size'; 39 40 41// #pragma mark - 42 43 44FontSelectionView::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 95FontSelectionView::~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 111void 112FontSelectionView::AttachedToLooper() 113{ 114 _BuildSizesMenu(); 115 UpdateFontsMenu(); 116} 117 118 119void 120FontSelectionView::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 198void 199FontSelectionView::SetMessage(BMessage* message) 200{ 201 delete fMessage; 202 fMessage = message; 203} 204 205 206void 207FontSelectionView::SetTarget(BHandler* target) 208{ 209 fTarget = target; 210} 211 212 213// #pragma mark - 214 215 216void 217FontSelectionView::SetFont(const BFont& font, float size) 218{ 219 BFont resizedFont(font); 220 resizedFont.SetSize(size); 221 SetFont(resizedFont); 222} 223 224 225void 226FontSelectionView::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 240void 241FontSelectionView::SetSize(float size) 242{ 243 SetFont(fCurrentFont, size); 244} 245 246 247const BFont& 248FontSelectionView::Font() const 249{ 250 return fCurrentFont; 251} 252 253 254void 255FontSelectionView::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 271void 272FontSelectionView::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 287bool 288FontSelectionView::IsDefaultable() 289{ 290 return fCurrentFont != _DefaultFont(); 291} 292 293 294bool 295FontSelectionView::IsRevertable() 296{ 297 return fCurrentFont != fSavedFont; 298} 299 300 301void 302FontSelectionView::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 354BLayoutItem* 355FontSelectionView::CreateSizesLabelLayoutItem() 356{ 357 return fSizesMenuField->CreateLabelLayoutItem(); 358} 359 360 361BLayoutItem* 362FontSelectionView::CreateSizesMenuBarLayoutItem() 363{ 364 return fSizesMenuField->CreateMenuBarLayoutItem(); 365} 366 367 368BLayoutItem* 369FontSelectionView::CreateFontsLabelLayoutItem() 370{ 371 return fFontsMenuField->CreateLabelLayoutItem(); 372} 373 374 375BLayoutItem* 376FontSelectionView::CreateFontsMenuBarLayoutItem() 377{ 378 return fFontsMenuField->CreateMenuBarLayoutItem(); 379} 380 381 382BLayoutItem* 383FontSelectionView::CreateStylesLabelLayoutItem() 384{ 385 if (fStylesMenuField) 386 return fStylesMenuField->CreateLabelLayoutItem(); 387 return NULL; 388} 389 390 391BLayoutItem* 392FontSelectionView::CreateStylesMenuBarLayoutItem() 393{ 394 if (fStylesMenuField) 395 return fStylesMenuField->CreateMenuBarLayoutItem(); 396 return NULL; 397} 398 399 400BView* 401FontSelectionView::PreviewBox() const 402{ 403 return fPreviewText; 404} 405 406 407// #pragma mark - private 408 409 410void 411FontSelectionView::_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 420BFont 421FontSelectionView::_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 432void 433FontSelectionView::_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 452void 453FontSelectionView::_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 464void 465FontSelectionView::_UpdateFontPreview() 466{ 467 fPreviewText->SetFont(&fCurrentFont); 468} 469 470 471void 472FontSelectionView::_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 499void 500FontSelectionView::_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