1 /* 2 * Copyright 2001-2015, 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 14 #include "FontSelectionView.h" 15 16 #include <Box.h> 17 #include <Catalog.h> 18 #include <GroupLayoutBuilder.h> 19 #include <LayoutItem.h> 20 #include <Locale.h> 21 #include <MenuField.h> 22 #include <MenuItem.h> 23 #include <PopUpMenu.h> 24 #include <String.h> 25 #include <StringView.h> 26 #include <Spinner.h> 27 28 #include <FontPrivate.h> 29 30 #include <stdio.h> 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "Font Selection view" 35 36 37 #define INSTANT_UPDATE 38 // if defined, the system font will be updated immediately, and not 39 // only on exit 40 41 static const float kMinSize = 8.0; 42 static const float kMaxSize = 72.0; 43 44 static const char *kPreviewText = B_TRANSLATE_COMMENT( 45 "The quick brown fox jumps over the lazy dog.", 46 "Don't translate this literally ! Use a phrase showing all chars " 47 "from A to Z."); 48 49 50 // private font API 51 extern void _set_system_font_(const char *which, font_family family, 52 font_style style, float size); 53 extern status_t _get_system_default_font_(const char* which, 54 font_family family, font_style style, float* _size); 55 56 57 #ifdef B_BEOS_VERSION_DANO 58 // this call only exists under R5 59 void 60 _set_system_font_(const char *which, font_family family, 61 font_style style, float size) 62 { 63 puts("you don't have _set_system_font_()"); 64 } 65 #endif 66 67 #if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 68 // this call only exists under Haiku (and the test environment) 69 status_t 70 _get_system_default_font_(const char* which, font_family family, 71 font_style style, float* _size) 72 { 73 puts("you don't have _get_system_default_font_()"); 74 return B_ERROR; 75 } 76 #endif 77 78 79 // #pragma mark - 80 81 82 FontSelectionView::FontSelectionView(const char* name, 83 const char* label, const BFont* currentFont) 84 : 85 BView(name, B_WILL_DRAW), 86 fMessageTarget(this) 87 { 88 if (currentFont == NULL) { 89 if (!strcmp(Name(), "plain")) 90 fCurrentFont = *be_plain_font; 91 else if (!strcmp(Name(), "bold")) 92 fCurrentFont = *be_bold_font; 93 else if (!strcmp(Name(), "fixed")) 94 fCurrentFont = *be_fixed_font; 95 else if (!strcmp(Name(), "menu")) { 96 menu_info info; 97 get_menu_info(&info); 98 99 fCurrentFont.SetFamilyAndStyle(info.f_family, info.f_style); 100 fCurrentFont.SetSize(info.font_size); 101 } 102 } else 103 fCurrentFont = *currentFont; 104 105 fSavedFont = fCurrentFont; 106 107 fFontsMenu = new BPopUpMenu("font menu"); 108 109 // font menu 110 fFontsMenuField = new BMenuField("fonts", label, fFontsMenu); 111 fFontsMenuField->SetAlignment(B_ALIGN_RIGHT); 112 113 // font size 114 BMessage* fontSizeMessage = new BMessage(kMsgSetSize); 115 fontSizeMessage->AddString("name", Name()); 116 117 fFontSizeSpinner = new BSpinner("font size", B_TRANSLATE("Size:"), 118 fontSizeMessage); 119 120 fFontSizeSpinner->SetRange(kMinSize, kMaxSize); 121 fFontSizeSpinner->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 122 123 // preview 124 fPreviewTextView = new BStringView("preview text", kPreviewText); 125 126 fPreviewTextView->SetFont(&fCurrentFont); 127 fPreviewTextView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 128 129 // box around preview 130 fPreviewBox = new BBox("preview box", B_WILL_DRAW | B_FRAME_EVENTS); 131 fPreviewBox->AddChild(BGroupLayoutBuilder(B_HORIZONTAL) 132 .Add(fPreviewTextView) 133 .SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING, 134 B_USE_SMALL_SPACING, B_USE_SMALL_SPACING) 135 .TopView() 136 ); 137 138 _SelectCurrentSize(); 139 } 140 141 142 FontSelectionView::~FontSelectionView() 143 { 144 #ifndef INSTANT_UPDATE 145 _UpdateSystemFont(); 146 #endif 147 } 148 149 150 void 151 FontSelectionView::SetTarget(BHandler* messageTarget) 152 { 153 fMessageTarget = messageTarget; 154 fFontSizeSpinner->SetTarget(messageTarget); 155 } 156 157 158 void 159 FontSelectionView::MessageReceived(BMessage* msg) 160 { 161 switch (msg->what) { 162 case kMsgSetSize: 163 { 164 int32 size = fFontSizeSpinner->Value(); 165 if (size == fCurrentFont.Size()) 166 break; 167 168 fCurrentFont.SetSize(size); 169 _UpdateFontPreview(); 170 break; 171 } 172 173 case kMsgSetFamily: 174 { 175 const char* family; 176 if (msg->FindString("family", &family) != B_OK) 177 break; 178 179 font_style style; 180 fCurrentFont.GetFamilyAndStyle(NULL, &style); 181 182 BMenuItem* familyItem = fFontsMenu->FindItem(family); 183 if (familyItem != NULL) { 184 _SelectCurrentFont(false); 185 186 BMenuItem* item = familyItem->Submenu()->FindItem(style); 187 if (item == NULL) 188 item = familyItem->Submenu()->ItemAt(0); 189 190 if (item != NULL) { 191 item->SetMarked(true); 192 fCurrentFont.SetFamilyAndStyle(family, item->Label()); 193 _UpdateFontPreview(); 194 } 195 } 196 break; 197 } 198 199 case kMsgSetStyle: 200 { 201 const char* family; 202 const char* style; 203 if (msg->FindString("family", &family) != B_OK 204 || msg->FindString("style", &style) != B_OK) 205 break; 206 207 BMenuItem *familyItem = fFontsMenu->FindItem(family); 208 if (!familyItem) 209 break; 210 211 _SelectCurrentFont(false); 212 familyItem->SetMarked(true); 213 214 fCurrentFont.SetFamilyAndStyle(family, style); 215 _UpdateFontPreview(); 216 break; 217 } 218 219 default: 220 BView::MessageReceived(msg); 221 } 222 } 223 224 BView* 225 FontSelectionView::GetPreviewBox() const 226 { 227 return fPreviewBox; 228 } 229 230 231 BView* 232 FontSelectionView::GetFontSizeSpinner() const 233 { 234 return fFontSizeSpinner; 235 } 236 237 238 BLayoutItem* 239 FontSelectionView::CreateFontsLabelLayoutItem() const 240 { 241 return fFontsMenuField->CreateLabelLayoutItem(); 242 } 243 244 245 BLayoutItem* 246 FontSelectionView::CreateFontsMenuBarLayoutItem() const 247 { 248 return fFontsMenuField->CreateMenuBarLayoutItem(); 249 } 250 251 252 void 253 FontSelectionView::_SelectCurrentFont(bool select) 254 { 255 font_family family; 256 font_style style; 257 fCurrentFont.GetFamilyAndStyle(&family, &style); 258 259 BMenuItem *item = fFontsMenu->FindItem(family); 260 if (item != NULL) { 261 item->SetMarked(select); 262 263 if (item->Submenu() != NULL) { 264 item = item->Submenu()->FindItem(style); 265 if (item != NULL) 266 item->SetMarked(select); 267 } 268 } 269 } 270 271 272 void 273 FontSelectionView::_SelectCurrentSize() 274 { 275 fFontSizeSpinner->SetValue((int32)fCurrentFont.Size()); 276 } 277 278 void 279 FontSelectionView::_UpdateFontPreview() 280 { 281 const BRect textFrame = fPreviewTextView->Bounds(); 282 BString text = BString(kPreviewText); 283 fCurrentFont.TruncateString(&text, B_TRUNCATE_END, textFrame.Width()); 284 fPreviewTextView->SetFont(&fCurrentFont); 285 fPreviewTextView->SetText(text); 286 287 #ifdef INSTANT_UPDATE 288 _UpdateSystemFont(); 289 #endif 290 } 291 292 293 void 294 FontSelectionView::_UpdateSystemFont() 295 { 296 font_family family; 297 font_style style; 298 fCurrentFont.GetFamilyAndStyle(&family, &style); 299 300 if (strcmp(Name(), "menu") == 0) { 301 // The menu font is not handled as a system font 302 menu_info info; 303 get_menu_info(&info); 304 305 strlcpy(info.f_family, (const char*)family, B_FONT_FAMILY_LENGTH); 306 strlcpy(info.f_style, (const char*)style, B_FONT_STYLE_LENGTH); 307 info.font_size = fCurrentFont.Size(); 308 309 set_menu_info(&info); 310 } else 311 _set_system_font_(Name(), family, style, fCurrentFont.Size()); 312 } 313 314 315 void 316 FontSelectionView::SetDefaults() 317 { 318 font_family family; 319 font_style style; 320 float size; 321 const char* fontName; 322 323 if (strcmp(Name(), "menu") == 0) 324 fontName = "plain"; 325 else 326 fontName = Name(); 327 328 if (_get_system_default_font_(fontName, family, style, &size) != B_OK) { 329 Revert(); 330 return; 331 } 332 333 BFont defaultFont; 334 defaultFont.SetFamilyAndStyle(family, style); 335 defaultFont.SetSize(size); 336 337 if (defaultFont == fCurrentFont) 338 return; 339 340 _SelectCurrentFont(false); 341 342 fCurrentFont = defaultFont; 343 _UpdateFontPreview(); 344 345 _SelectCurrentFont(true); 346 _SelectCurrentSize(); 347 } 348 349 350 void 351 FontSelectionView::Revert() 352 { 353 if (!IsRevertable()) 354 return; 355 356 _SelectCurrentFont(false); 357 358 fCurrentFont = fSavedFont; 359 _UpdateFontPreview(); 360 361 _SelectCurrentFont(true); 362 _SelectCurrentSize(); 363 } 364 365 366 bool 367 FontSelectionView::IsDefaultable() 368 { 369 font_family defaultFamily; 370 font_style defaultStyle; 371 float defaultSize; 372 const char* fontName; 373 374 if (strcmp(Name(), "menu") == 0) 375 fontName = "plain"; 376 else 377 fontName = Name(); 378 379 if (_get_system_default_font_(fontName, defaultFamily, defaultStyle, 380 &defaultSize) != B_OK) { 381 return false; 382 } 383 384 font_family currentFamily; 385 font_style currentStyle; 386 float currentSize; 387 388 fCurrentFont.GetFamilyAndStyle(¤tFamily, ¤tStyle); 389 currentSize = fCurrentFont.Size(); 390 391 return strcmp(currentFamily, defaultFamily) != 0 392 || strcmp(currentStyle, defaultStyle) != 0 393 || currentSize != defaultSize; 394 } 395 396 397 bool 398 FontSelectionView::IsRevertable() 399 { 400 return fCurrentFont != fSavedFont; 401 } 402 403 404 void 405 FontSelectionView::UpdateFontsMenu() 406 { 407 int32 numFamilies = count_font_families(); 408 409 fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true); 410 BFont font; 411 fFontsMenu->GetFont(&font); 412 413 font_family currentFamily; 414 font_style currentStyle; 415 fCurrentFont.GetFamilyAndStyle(¤tFamily, ¤tStyle); 416 417 for (int32 i = 0; i < numFamilies; i++) { 418 font_family family; 419 uint32 flags; 420 if (get_font_family(i, &family, &flags) != B_OK) 421 continue; 422 423 // if we're setting the fixed font, we only want to show fixed and 424 // full-and-half-fixed fonts 425 if (strcmp(Name(), "fixed") == 0 426 && (flags 427 & (B_IS_FIXED | B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED)) == 0) { 428 continue; 429 } 430 431 float width = font.StringWidth(family); 432 if (width > fMaxFontNameWidth) 433 fMaxFontNameWidth = width; 434 435 BMenu* stylesMenu = new BMenu(family); 436 stylesMenu->SetRadioMode(true); 437 stylesMenu->SetFont(&font); 438 439 BMessage* message = new BMessage(kMsgSetFamily); 440 message->AddString("family", family); 441 message->AddString("name", Name()); 442 443 BMenuItem* familyItem = new BMenuItem(stylesMenu, message); 444 fFontsMenu->AddItem(familyItem); 445 446 int32 numStyles = count_font_styles(family); 447 448 for (int32 j = 0; j < numStyles; j++) { 449 font_style style; 450 if (get_font_style(family, j, &style, &flags) != B_OK) 451 continue; 452 453 message = new BMessage(kMsgSetStyle); 454 message->AddString("family", (char*)family); 455 message->AddString("style", (char*)style); 456 message->AddString("name", Name()); 457 458 BMenuItem* item = new BMenuItem(style, message); 459 460 if (!strcmp(style, currentStyle) 461 && !strcmp(family, currentFamily)) { 462 item->SetMarked(true); 463 familyItem->SetMarked(true); 464 } 465 stylesMenu->AddItem(item); 466 } 467 468 stylesMenu->SetTargetForItems(fMessageTarget); 469 } 470 471 fFontsMenu->SetTargetForItems(fMessageTarget); 472 } 473