xref: /haiku/src/preferences/appearance/FontSelectionView.cpp (revision 91c0454716f24a7454b20f7a54cfe32e288e3710)
1 /*
2  * Copyright 2001-2011, 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, 21, 24, 0};
255 
256 	// build size menu
257 	for (int32 i = 0; sizes[i]; i++) {
258 		int32 size = sizes[i];
259 		if (size < kMinSize || size > kMaxSize)
260 			continue;
261 
262 		char label[32];
263 		snprintf(label, sizeof(label), "%" B_PRId32, size);
264 
265 		BMessage* message = new BMessage(kMsgSetSize);
266 		message->AddInt32("size", size);
267 		message->AddString("name", Name());
268 
269 		BMenuItem* item = new BMenuItem(label, message);
270 		if (size == fCurrentFont.Size())
271 			item->SetMarked(true);
272 
273 		fSizesMenu->AddItem(item);
274 	}
275 }
276 
277 
278 void
279 FontSelectionView::_SelectCurrentFont(bool select)
280 {
281 	font_family family;
282 	font_style style;
283 	fCurrentFont.GetFamilyAndStyle(&family, &style);
284 
285 	BMenuItem *item = fFontsMenu->FindItem(family);
286 	if (item != NULL) {
287 		item->SetMarked(select);
288 
289 		if (item->Submenu() != NULL) {
290 			item = item->Submenu()->FindItem(style);
291 			if (item != NULL)
292 				item->SetMarked(select);
293 		}
294 	}
295 }
296 
297 
298 void
299 FontSelectionView::_SelectCurrentSize(bool select)
300 {
301 	char label[16];
302 	snprintf(label, sizeof(label), "%" B_PRId32, (int32)fCurrentFont.Size());
303 
304 	BMenuItem* item = fSizesMenu->FindItem(label);
305 	if (item != NULL)
306 		item->SetMarked(select);
307 }
308 
309 
310 void
311 FontSelectionView::_UpdateFontPreview()
312 {
313 	fPreviewText->SetFont(&fCurrentFont);
314 
315 #ifdef INSTANT_UPDATE
316 	_UpdateSystemFont();
317 #endif
318 }
319 
320 
321 void
322 FontSelectionView::_UpdateSystemFont()
323 {
324 	font_family family;
325 	font_style style;
326 	fCurrentFont.GetFamilyAndStyle(&family, &style);
327 
328 	if (strcmp(Name(), "menu") == 0) {
329 		// The menu font is not handled as a system font
330 		menu_info info;
331 		get_menu_info(&info);
332 
333 		strlcpy(info.f_family, (const char*)family, B_FONT_FAMILY_LENGTH);
334 		strlcpy(info.f_style, (const char*)style, B_FONT_STYLE_LENGTH);
335 		info.font_size = fCurrentFont.Size();
336 
337 		set_menu_info(&info);
338 	} else
339 		_set_system_font_(Name(), family, style, fCurrentFont.Size());
340 }
341 
342 
343 void
344 FontSelectionView::SetDefaults()
345 {
346 	font_family family;
347 	font_style style;
348 	float size;
349 	const char* fontName;
350 
351 	if (strcmp(Name(), "menu") == 0)
352 		fontName = "plain";
353 	else
354 		fontName = Name();
355 
356 	if (_get_system_default_font_(fontName, family, style, &size) != B_OK) {
357 		Revert();
358 		return;
359 	}
360 
361 	BFont defaultFont;
362 	defaultFont.SetFamilyAndStyle(family, style);
363 	defaultFont.SetSize(size);
364 
365 	if (defaultFont == fCurrentFont)
366 		return;
367 
368 	_SelectCurrentFont(false);
369 
370 	fCurrentFont = defaultFont;
371 	_UpdateFontPreview();
372 
373 	_SelectCurrentFont(true);
374 	_SelectCurrentSize(true);
375 }
376 
377 
378 void
379 FontSelectionView::Revert()
380 {
381 	if (!IsRevertable())
382 		return;
383 
384 	_SelectCurrentFont(false);
385 
386 	fCurrentFont = fSavedFont;
387 	_UpdateFontPreview();
388 
389 	_SelectCurrentFont(true);
390 	_SelectCurrentSize(true);
391 }
392 
393 
394 bool
395 FontSelectionView::IsDefaultable()
396 {
397 	font_family defaultFamily;
398 	font_style defaultStyle;
399 	float defaultSize;
400 	const char* fontName;
401 
402 	if (strcmp(Name(), "menu") == 0)
403 		fontName = "plain";
404 	else
405 		fontName = Name();
406 
407 	if (_get_system_default_font_(fontName, defaultFamily, defaultStyle,
408 		&defaultSize) != B_OK) {
409 		return false;
410 	}
411 
412 	font_family currentFamily;
413 	font_style currentStyle;
414 	float currentSize;
415 
416 	fCurrentFont.GetFamilyAndStyle(&currentFamily, &currentStyle);
417 	currentSize = fCurrentFont.Size();
418 
419 	return strcmp(currentFamily, defaultFamily) != 0
420 		|| strcmp(currentStyle, defaultStyle) != 0
421 		|| currentSize != defaultSize;
422 }
423 
424 
425 bool
426 FontSelectionView::IsRevertable()
427 {
428 	return fCurrentFont != fSavedFont;
429 }
430 
431 
432 void
433 FontSelectionView::UpdateFontsMenu()
434 {
435 	int32 numFamilies = count_font_families();
436 
437 	fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true);
438 	BFont font;
439 	fFontsMenu->GetFont(&font);
440 
441 	font_family currentFamily;
442 	font_style currentStyle;
443 	fCurrentFont.GetFamilyAndStyle(&currentFamily, &currentStyle);
444 
445 	for (int32 i = 0; i < numFamilies; i++) {
446 		font_family family;
447 		uint32 flags;
448 		if (get_font_family(i, &family, &flags) != B_OK)
449 			continue;
450 
451 		// if we're setting the fixed font, we only want to show fixed fonts
452 		if (!strcmp(Name(), "fixed") && (flags & B_IS_FIXED) == 0)
453 			continue;
454 
455 		float width = font.StringWidth(family);
456 		if (width > fMaxFontNameWidth)
457 			fMaxFontNameWidth = width;
458 
459 		BMenu* stylesMenu = new BMenu(family);
460 		stylesMenu->SetRadioMode(true);
461 		stylesMenu->SetFont(&font);
462 
463 		BMessage* message = new BMessage(kMsgSetFamily);
464 		message->AddString("family", family);
465 		message->AddString("name", Name());
466 
467 		BMenuItem* familyItem = new BMenuItem(stylesMenu, message);
468 		fFontsMenu->AddItem(familyItem);
469 
470 		int32 numStyles = count_font_styles(family);
471 
472 		for (int32 j = 0; j < numStyles; j++) {
473 			font_style style;
474 			if (get_font_style(family, j, &style, &flags) != B_OK)
475 				continue;
476 
477 			message = new BMessage(kMsgSetStyle);
478 			message->AddString("family", (char*)family);
479 			message->AddString("style", (char*)style);
480 			message->AddString("name", Name());
481 
482 			BMenuItem *item = new BMenuItem(style, message);
483 
484 			if (!strcmp(style, currentStyle)
485 				&& !strcmp(family, currentFamily)) {
486 				item->SetMarked(true);
487 				familyItem->SetMarked(true);
488 			}
489 			stylesMenu->AddItem(item);
490 		}
491 
492 		stylesMenu->SetTargetForItems(fMessageTarget);
493 	}
494 
495 	fFontsMenu->SetTargetForItems(fMessageTarget);
496 }
497