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