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