1/*
2 * Copyright 2001-2008, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Axel D��rfler, axeld@pinc-software.de
8 */
9
10/**	Classes to represent font styles and families */
11
12
13#include "FontFamily.h"
14
15#include <FontPrivate.h>
16
17
18const uint32 kInvalidFamilyFlags = ~(uint32)0;
19
20
21static int
22font_score(const FontStyle* style)
23{
24	int score = 0;
25	if (style->Face() & B_REGULAR_FACE)
26		score += 10;
27	else {
28		if (style->Face() & B_BOLD_FACE)
29			score += 5;
30		if (style->Face() & B_ITALIC_FACE)
31			score--;
32	}
33
34	return score;
35}
36
37
38static int
39compare_font_styles(const FontStyle* a, const FontStyle* b)
40{
41	// Regular fonts come first, then bold, then italics
42	return font_score(b) - font_score(a);
43}
44
45
46//	#pragma mark -
47
48
49/*!
50	\brief Constructor
51	\param namestr Name of the family
52*/
53FontFamily::FontFamily(const char *name, uint16 id)
54	:
55	fName(name),
56	fID(id),
57	fNextID(0),
58	fFlags(kInvalidFamilyFlags)
59{
60	fName.Truncate(B_FONT_FAMILY_LENGTH);
61		// make sure this family can be found using the Be API
62}
63
64
65/*!
66	\brief Returns the name of the family
67	\return The family's name
68*/
69const char*
70FontFamily::Name() const
71{
72	return fName.String();
73}
74
75
76/*!
77	\brief Adds the style to the family
78	\param style pointer to FontStyle object to be added
79*/
80bool
81FontFamily::AddStyle(FontStyle* style)
82{
83	if (!style)
84		return false;
85
86	// Don't add if it already is in the family.
87	int32 count = fStyles.CountItems();
88	for (int32 i = 0; i < count; i++) {
89		FontStyle *item = fStyles.ItemAt(i);
90		if (!strcmp(item->Name(), style->Name()))
91			return false;
92	}
93
94	if (!fStyles.BinaryInsert(style, compare_font_styles))
95		return false;
96
97	style->_SetFontFamily(this, fNextID++);
98
99	// force a refresh if a request for font flags is needed
100	fFlags = kInvalidFamilyFlags;
101
102	return true;
103}
104
105
106/*!
107	\brief Removes a style from the family.
108
109	The font style will not be deleted.
110*/
111bool
112FontFamily::RemoveStyle(FontStyle* style)
113{
114	if (style == NULL)
115		return false;
116
117	if (!fStyles.RemoveItem(style))
118		return false;
119
120	// force a refresh if a request for font flags is needed
121	fFlags = kInvalidFamilyFlags;
122	return true;
123}
124
125
126/*!
127	\brief Returns the number of styles in the family
128	\return The number of styles in the family
129*/
130int32
131FontFamily::CountStyles() const
132{
133	return fStyles.CountItems();
134}
135
136
137FontStyle*
138FontFamily::_FindStyle(const char* name) const
139{
140	int32 count = fStyles.CountItems();
141	if (!name || count < 1)
142		return NULL;
143
144	for (int32 i = 0; i < count; i++) {
145		FontStyle *style = fStyles.ItemAt(i);
146		if (!strcmp(style->Name(), name))
147			return style;
148	}
149
150	return NULL;
151}
152
153
154/*!
155	\brief Determines whether the style belongs to the family
156	\param style Name of the style being checked
157	\return True if it belongs, false if not
158*/
159bool
160FontFamily::HasStyle(const char *styleName) const
161{
162	return _FindStyle(styleName) != NULL;
163}
164
165
166/*!
167	\brief Returns the name of a style in the family
168	\param index list index of the style to be found
169	\return name of the style or NULL if the index is not valid
170*/
171FontStyle*
172FontFamily::StyleAt(int32 index) const
173{
174	return fStyles.ItemAt(index);
175}
176
177
178/*!
179	\brief Get the FontStyle object for the name given
180	\param style Name of the style to be obtained
181	\return The FontStyle object or NULL if none was found.
182
183	The object returned belongs to the family and must not be deleted.
184*/
185FontStyle*
186FontFamily::GetStyle(const char *name) const
187{
188	if (name == NULL || !name[0])
189		return NULL;
190
191	FontStyle* style = _FindStyle(name);
192	if (style != NULL)
193		return style;
194
195	// try alternative names
196
197	if (!strcmp(name, "Roman") || !strcmp(name, "Regular")
198		|| !strcmp(name, "Book")) {
199		style = _FindStyle("Roman");
200		if (style == NULL) {
201			style = _FindStyle("Regular");
202			if (style == NULL)
203				style = _FindStyle("Book");
204		}
205		return style;
206	}
207
208	BString alternative = name;
209	if (alternative.FindFirst("Italic") >= 0) {
210		alternative.ReplaceFirst("Italic", "Oblique");
211		return _FindStyle(alternative.String());
212	}
213	if (alternative.FindFirst("Oblique") >= 0) {
214		alternative.ReplaceFirst("Oblique", "Italic");
215		return _FindStyle(alternative.String());
216	}
217
218	return NULL;
219}
220
221
222FontStyle*
223FontFamily::GetStyleMatchingFace(uint16 face) const
224{
225	// Other face flags do not impact the font selection (they are applied
226	// during drawing)
227	face &= B_BOLD_FACE | B_ITALIC_FACE | B_REGULAR_FACE | B_CONDENSED_FACE
228		| B_LIGHT_FACE | B_HEAVY_FACE;
229	if (face == 0)
230		face = B_REGULAR_FACE;
231
232	int32 count = fStyles.CountItems();
233	for (int32 i = 0; i < count; i++) {
234		FontStyle* style = fStyles.ItemAt(i);
235
236		if (style->Face() == face)
237			return style;
238	}
239
240	return NULL;
241}
242
243
244uint32
245FontFamily::Flags()
246{
247	if (fFlags == kInvalidFamilyFlags) {
248		fFlags = 0;
249
250		int32 count = fStyles.CountItems();
251		for (int32 i = 0; i < count; i++) {
252			FontStyle* style = fStyles.ItemAt(i);
253
254			if (style->IsFixedWidth())
255				fFlags |= B_IS_FIXED;
256			if (style->IsFullAndHalfFixed())
257				fFlags |= B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED;
258			if (style->TunedCount() > 0)
259				fFlags |= B_HAS_TUNED_FONT;
260		}
261	}
262
263	return fFlags;
264}
265