xref: /haiku/src/preferences/appearance/FontSelectionView.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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(&currentFamily, &currentStyle);
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(&currentFamily, &currentStyle);
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