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