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