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 "FontManager.h"
16
17#include <FontPrivate.h>
18
19
20const uint32 kInvalidFamilyFlags = ~(uint32)0;
21
22
23static int
24font_score(const FontStyle* style)
25{
26	int score = 0;
27	if (style->Face() & B_REGULAR_FACE)
28		score += 10;
29	else {
30		if (style->Face() & B_BOLD_FACE)
31			score += 5;
32		if (style->Face() & B_ITALIC_FACE)
33			score--;
34	}
35
36	return score;
37}
38
39
40static int
41compare_font_styles(const FontStyle* a, const FontStyle* b)
42{
43	// Regular fonts come first, then bold, then italics
44	return font_score(b) - font_score(a);
45}
46
47
48//	#pragma mark -
49
50
51/*!
52	\brief Constructor
53	\param namestr Name of the family
54*/
55FontFamily::FontFamily(const char *name, uint16 id)
56	:
57	fName(name),
58	fID(id),
59	fNextID(0),
60	fFlags(kInvalidFamilyFlags)
61{
62	fName.Truncate(B_FONT_FAMILY_LENGTH);
63		// make sure this family can be found using the Be API
64}
65
66
67/*!
68	\brief Destructor
69
70	Deletes all attached styles. Note that a FontFamily must only be deleted
71	by the font manager.
72*/
73FontFamily::~FontFamily()
74{
75	for (int32 i = fStyles.CountItems(); i-- > 0;) {
76		FontStyle* style = fStyles.RemoveItemAt(i);
77
78		// we remove us before deleting the style, so that the font manager
79		// is not contacted to remove the style from us
80		style->_SetFontFamily(NULL, -1);
81		delete style;
82	}
83}
84
85
86/*!
87	\brief Returns the name of the family
88	\return The family's name
89*/
90const char*
91FontFamily::Name() const
92{
93	return fName.String();
94}
95
96
97/*!
98	\brief Adds the style to the family
99	\param style pointer to FontStyle object to be added
100*/
101bool
102FontFamily::AddStyle(FontStyle *style)
103{
104	if (!style)
105		return false;
106
107	// Don't add if it already is in the family.
108	int32 count = fStyles.CountItems();
109	for (int32 i = 0; i < count; i++) {
110		FontStyle *item = fStyles.ItemAt(i);
111		if (!strcmp(item->Name(), style->Name()))
112			return false;
113	}
114
115	if (!fStyles.BinaryInsert(style, compare_font_styles))
116		return false;
117
118	style->_SetFontFamily(this, fNextID++);
119
120	// force a refresh if a request for font flags is needed
121	fFlags = kInvalidFamilyFlags;
122
123	return true;
124}
125
126
127/*!
128	\brief Removes a style from the family.
129
130	The font style will not be deleted.
131*/
132bool
133FontFamily::RemoveStyle(FontStyle* style)
134{
135	if (!gFontManager->IsLocked()) {
136		debugger("FontFamily::RemoveStyle() called without having the font manager locked!");
137		return false;
138	}
139
140	if (!fStyles.RemoveItem(style))
141		return false;
142
143	style->_SetFontFamily(NULL, -1);
144
145	// force a refresh if a request for font flags is needed
146	fFlags = kInvalidFamilyFlags;
147	return true;
148}
149
150
151/*!
152	\brief Returns the number of styles in the family
153	\return The number of styles in the family
154*/
155int32
156FontFamily::CountStyles() const
157{
158	return fStyles.CountItems();
159}
160
161
162FontStyle*
163FontFamily::_FindStyle(const char* name) const
164{
165	int32 count = fStyles.CountItems();
166	if (!name || count < 1)
167		return NULL;
168
169	for (int32 i = 0; i < count; i++) {
170		FontStyle *style = fStyles.ItemAt(i);
171		if (!strcmp(style->Name(), name))
172			return style;
173	}
174
175	return NULL;
176}
177
178
179/*!
180	\brief Determines whether the style belongs to the family
181	\param style Name of the style being checked
182	\return True if it belongs, false if not
183*/
184bool
185FontFamily::HasStyle(const char *styleName) const
186{
187	return _FindStyle(styleName) != NULL;
188}
189
190
191/*!
192	\brief Returns the name of a style in the family
193	\param index list index of the style to be found
194	\return name of the style or NULL if the index is not valid
195*/
196FontStyle*
197FontFamily::StyleAt(int32 index) const
198{
199	return fStyles.ItemAt(index);
200}
201
202
203/*!
204	\brief Get the FontStyle object for the name given
205	\param style Name of the style to be obtained
206	\return The FontStyle object or NULL if none was found.
207
208	The object returned belongs to the family and must not be deleted.
209*/
210FontStyle*
211FontFamily::GetStyle(const char *name) const
212{
213	if (name == NULL || !name[0])
214		return NULL;
215
216	FontStyle* style = _FindStyle(name);
217	if (style != NULL)
218		return style;
219
220	// try alternative names
221
222	if (!strcmp(name, "Roman") || !strcmp(name, "Regular")
223		|| !strcmp(name, "Book")) {
224		style = _FindStyle("Roman");
225		if (style == NULL) {
226			style = _FindStyle("Regular");
227			if (style == NULL)
228				style = _FindStyle("Book");
229		}
230		return style;
231	}
232
233	BString alternative = name;
234	if (alternative.FindFirst("Italic") >= 0) {
235		alternative.ReplaceFirst("Italic", "Oblique");
236		return _FindStyle(alternative.String());
237	}
238	if (alternative.FindFirst("Oblique") >= 0) {
239		alternative.ReplaceFirst("Oblique", "Italic");
240		return _FindStyle(alternative.String());
241	}
242
243	return NULL;
244}
245
246
247FontStyle*
248FontFamily::GetStyleByID(uint16 id) const
249{
250	int32 count = fStyles.CountItems();
251	for (int32 i = 0; i < count; i++) {
252		FontStyle* style = fStyles.ItemAt(i);
253		if (style->ID() == id)
254			return style;
255	}
256
257	return NULL;
258}
259
260
261FontStyle*
262FontFamily::GetStyleMatchingFace(uint16 face) const
263{
264	// TODO: support other faces (strike through, underlined, outlines...)
265	face &= B_BOLD_FACE | B_ITALIC_FACE | B_REGULAR_FACE | B_CONDENSED_FACE
266		| B_LIGHT_FACE | B_HEAVY_FACE;
267
268	int32 count = fStyles.CountItems();
269	for (int32 i = 0; i < count; i++) {
270		FontStyle* style = fStyles.ItemAt(i);
271
272		if (style->Face() == face)
273			return style;
274	}
275
276	return NULL;
277}
278
279
280uint32
281FontFamily::Flags()
282{
283	if (fFlags == kInvalidFamilyFlags) {
284		fFlags = 0;
285
286		int32 count = fStyles.CountItems();
287		for (int32 i = 0; i < count; i++) {
288			FontStyle* style = fStyles.ItemAt(i);
289
290			if (style->IsFixedWidth())
291				fFlags |= B_IS_FIXED;
292			if (style->IsFullAndHalfFixed())
293				fFlags |= B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED;
294			if (style->TunedCount() > 0)
295				fFlags |= B_HAS_TUNED_FONT;
296		}
297	}
298
299	return fFlags;
300}
301