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