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 Destructor
67
68	Deletes all attached styles. Note that a FontFamily must only be deleted
69	by the font manager.
70*/
71FontFamily::~FontFamily()
72{
73	for (int32 i = fStyles.CountItems(); i-- > 0;) {
74		FontStyle* style = fStyles.RemoveItemAt(i);
75
76		// we remove us before deleting the style, so that the font manager
77		// is not contacted to remove the style from us
78		style->_SetFontFamily(NULL, -1);
79	}
80}
81
82
83/*!
84	\brief Returns the name of the family
85	\return The family's name
86*/
87const char*
88FontFamily::Name() const
89{
90	return fName.String();
91}
92
93
94/*!
95	\brief Adds the style to the family
96	\param style pointer to FontStyle object to be added
97*/
98bool
99FontFamily::AddStyle(FontStyle* style)
100{
101	if (!style)
102		return false;
103
104	// Don't add if it already is in the family.
105	int32 count = fStyles.CountItems();
106	for (int32 i = 0; i < count; i++) {
107		FontStyle *item = fStyles.ItemAt(i);
108		if (!strcmp(item->Name(), style->Name()))
109			return false;
110	}
111
112	if (!fStyles.BinaryInsert(style, compare_font_styles))
113		return false;
114
115	style->_SetFontFamily(this, fNextID++);
116
117	// force a refresh if a request for font flags is needed
118	fFlags = kInvalidFamilyFlags;
119
120	return true;
121}
122
123
124/*!
125	\brief Removes a style from the family.
126
127	The font style will not be deleted.
128*/
129bool
130FontFamily::RemoveStyle(FontStyle* style)
131{
132	if (style == NULL)
133		return false;
134
135	if (!fStyles.RemoveItem(style))
136		return false;
137
138	style->_SetFontFamily(NULL, -1);
139
140	// force a refresh if a request for font flags is needed
141	fFlags = kInvalidFamilyFlags;
142	return true;
143}
144
145
146/*!
147	\brief Returns the number of styles in the family
148	\return The number of styles in the family
149*/
150int32
151FontFamily::CountStyles() const
152{
153	return fStyles.CountItems();
154}
155
156
157FontStyle*
158FontFamily::_FindStyle(const char* name) const
159{
160	int32 count = fStyles.CountItems();
161	if (!name || count < 1)
162		return NULL;
163
164	for (int32 i = 0; i < count; i++) {
165		FontStyle *style = fStyles.ItemAt(i);
166		if (!strcmp(style->Name(), name))
167			return style;
168	}
169
170	return NULL;
171}
172
173
174/*!
175	\brief Determines whether the style belongs to the family
176	\param style Name of the style being checked
177	\return True if it belongs, false if not
178*/
179bool
180FontFamily::HasStyle(const char *styleName) const
181{
182	return _FindStyle(styleName) != NULL;
183}
184
185
186/*!
187	\brief Returns the name of a style in the family
188	\param index list index of the style to be found
189	\return name of the style or NULL if the index is not valid
190*/
191FontStyle*
192FontFamily::StyleAt(int32 index) const
193{
194	return fStyles.ItemAt(index);
195}
196
197
198/*!
199	\brief Get the FontStyle object for the name given
200	\param style Name of the style to be obtained
201	\return The FontStyle object or NULL if none was found.
202
203	The object returned belongs to the family and must not be deleted.
204*/
205FontStyle*
206FontFamily::GetStyle(const char *name) const
207{
208	if (name == NULL || !name[0])
209		return NULL;
210
211	FontStyle* style = _FindStyle(name);
212	if (style != NULL)
213		return style;
214
215	// try alternative names
216
217	if (!strcmp(name, "Roman") || !strcmp(name, "Regular")
218		|| !strcmp(name, "Book")) {
219		style = _FindStyle("Roman");
220		if (style == NULL) {
221			style = _FindStyle("Regular");
222			if (style == NULL)
223				style = _FindStyle("Book");
224		}
225		return style;
226	}
227
228	BString alternative = name;
229	if (alternative.FindFirst("Italic") >= 0) {
230		alternative.ReplaceFirst("Italic", "Oblique");
231		return _FindStyle(alternative.String());
232	}
233	if (alternative.FindFirst("Oblique") >= 0) {
234		alternative.ReplaceFirst("Oblique", "Italic");
235		return _FindStyle(alternative.String());
236	}
237
238	return NULL;
239}
240
241
242FontStyle*
243FontFamily::GetStyleByID(uint16 id) const
244{
245	int32 count = fStyles.CountItems();
246	for (int32 i = 0; i < count; i++) {
247		FontStyle* style = fStyles.ItemAt(i);
248		if (style->ID() == id)
249			return style;
250	}
251
252	return NULL;
253}
254
255
256FontStyle*
257FontFamily::GetStyleMatchingFace(uint16 face) const
258{
259	// Other face flags do not impact the font selection (they are applied
260	// during drawing)
261	face &= B_BOLD_FACE | B_ITALIC_FACE | B_REGULAR_FACE | B_CONDENSED_FACE
262		| B_LIGHT_FACE | B_HEAVY_FACE;
263	if (face == 0)
264		face = B_REGULAR_FACE;
265
266	int32 count = fStyles.CountItems();
267	for (int32 i = 0; i < count; i++) {
268		FontStyle* style = fStyles.ItemAt(i);
269
270		if (style->Face() == face)
271			return style;
272	}
273
274	return NULL;
275}
276
277
278uint32
279FontFamily::Flags()
280{
281	if (fFlags == kInvalidFamilyFlags) {
282		fFlags = 0;
283
284		int32 count = fStyles.CountItems();
285		for (int32 i = 0; i < count; i++) {
286			FontStyle* style = fStyles.ItemAt(i);
287
288			if (style->IsFixedWidth())
289				fFlags |= B_IS_FIXED;
290			if (style->IsFullAndHalfFixed())
291				fFlags |= B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED;
292			if (style->TunedCount() > 0)
293				fFlags |= B_HAS_TUNED_FONT;
294		}
295	}
296
297	return fFlags;
298}
299