xref: /haiku/src/servers/app/font/FontManager.cpp (revision 19ae20e67e91fc09cc9fc5c0e60e21e24e7a53eb)
1 /*
2  * Copyright 2001-2009, 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 /*!	Manages font families and styles */
11 
12 
13 #include "FontFamily.h"
14 #include "FontManager.h"
15 #include "ServerConfig.h"
16 #include "ServerFont.h"
17 
18 #include <Autolock.h>
19 #include <Directory.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <FindDirectory.h>
23 #include <Message.h>
24 #include <NodeMonitor.h>
25 #include <Path.h>
26 #include <String.h>
27 
28 #include <new>
29 
30 //#define TRACE_FONT_MANAGER
31 #ifdef TRACE_FONT_MANAGER
32 #	define FTRACE(x) printf x
33 #else
34 #	define FTRACE(x) ;
35 #endif
36 
37 
38 // TODO: needs some more work for multi-user support
39 
40 FT_Library gFreeTypeLibrary;
41 FontManager *gFontManager = NULL;
42 
43 struct FontManager::font_directory {
44 	node_ref	directory;
45 	uid_t		user;
46 	gid_t		group;
47 	uint32		revision;
48 	BObjectList<FontStyle> styles;
49 
50 	bool AlreadyScanned() const { return revision != 0; }
51 	FontStyle* FindStyle(const node_ref& nodeRef) const;
52 };
53 
54 struct FontManager::font_mapping {
55 	BString		family;
56 	BString		style;
57 	entry_ref	ref;
58 };
59 
60 
61 FontStyle*
62 FontManager::font_directory::FindStyle(const node_ref& nodeRef) const
63 {
64 	for (int32 i = styles.CountItems(); i-- > 0;) {
65 		FontStyle* style = styles.ItemAt(i);
66 
67 		if (nodeRef == style->NodeRef())
68 			return style;
69 	}
70 
71 	return NULL;
72 }
73 
74 
75 static status_t
76 set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
77 {
78 	entry_ref ref;
79 	ref.device = nodeRef.device;
80 	ref.directory = nodeRef.node;
81 
82 	status_t status = ref.set_name(name);
83 	if (status != B_OK)
84 		return status;
85 
86 	return entry.SetTo(&ref);
87 }
88 
89 
90 static int
91 compare_font_families(const FontFamily* a, const FontFamily* b)
92 {
93 	return strcmp(a->Name(), b->Name());
94 }
95 
96 
97 //	#pragma mark -
98 
99 
100 //! Does basic set up so that directories can be scanned
101 FontManager::FontManager()
102 	: BLooper("Font Manager"),
103 	fDirectories(10, true),
104 	fMappings(10, true),
105 	fFamilies(20),
106 
107 	fDefaultPlainFont(NULL),
108 	fDefaultBoldFont(NULL),
109 	fDefaultFixedFont(NULL),
110 
111 	fScanned(false),
112 	fNextID(0)
113 {
114 	fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR;
115 
116 	if (fInitStatus == B_OK) {
117 		_AddSystemPaths();
118 		_LoadRecentFontMappings();
119 
120 		fInitStatus = _SetDefaultFonts();
121 
122 		if (fInitStatus == B_OK) {
123 			// Precache the plain and bold fonts
124 			_PrecacheFontFile(fDefaultPlainFont);
125 			_PrecacheFontFile(fDefaultBoldFont);
126 		}
127 	}
128 }
129 
130 
131 //! Frees items allocated in the constructor and shuts down FreeType
132 FontManager::~FontManager()
133 {
134 	delete fDefaultPlainFont;
135 	delete fDefaultBoldFont;
136 	delete fDefaultFixedFont;
137 
138 	// free families before we're done with FreeType
139 
140 	for (int32 i = fFamilies.CountItems(); i-- > 0;) {
141 		delete fFamilies.ItemAt(i);
142 	}
143 
144 	FT_Done_FreeType(gFreeTypeLibrary);
145 }
146 
147 
148 void
149 FontManager::MessageReceived(BMessage* message)
150 {
151 	switch (message->what) {
152 		case B_NODE_MONITOR:
153 		{
154 			// TODO: support removing fonts!
155 
156 			int32 opcode;
157 			if (message->FindInt32("opcode", &opcode) != B_OK)
158 				return;
159 
160 			switch (opcode) {
161 				case B_ENTRY_CREATED:
162 				{
163 					const char* name;
164 					node_ref nodeRef;
165 					if (message->FindInt32("device", &nodeRef.device) != B_OK
166 						|| message->FindInt64("directory", &nodeRef.node) != B_OK
167 						|| message->FindString("name", &name) != B_OK)
168 						break;
169 
170 					// TODO: make this better (possible under Haiku)
171 					snooze(100000);
172 						// let the font be written completely before trying to open it
173 
174 					BEntry entry;
175 					if (set_entry(nodeRef, name, entry) != B_OK)
176 						break;
177 
178 					if (entry.IsDirectory()) {
179 						// a new directory to watch for us
180 						_AddPath(entry);
181 					} else {
182 						// a new font
183 						font_directory* directory = _FindDirectory(nodeRef);
184 						if (directory == NULL) {
185 							// unknown directory? how come?
186 							break;
187 						}
188 
189 						_AddFont(*directory, entry);
190 					}
191 					break;
192 				}
193 
194 				case B_ENTRY_MOVED:
195 				{
196 					// has the entry been moved into a monitored directory or has
197 					// it been removed from one?
198 					const char* name;
199 					node_ref nodeRef;
200 					uint64 fromNode;
201 					uint64 node;
202 					if (message->FindInt32("device", &nodeRef.device) != B_OK
203 						|| message->FindInt64("to directory", &nodeRef.node) != B_OK
204 						|| message->FindInt64("from directory", (int64 *)&fromNode) != B_OK
205 						|| message->FindInt64("node", (int64 *)&node) != B_OK
206 						|| message->FindString("name", &name) != B_OK)
207 						break;
208 
209 					font_directory* directory = _FindDirectory(nodeRef);
210 
211 					BEntry entry;
212 					if (set_entry(nodeRef, name, entry) != B_OK)
213 						break;
214 
215 					if (directory != NULL) {
216 						// something has been added to our watched font directories
217 
218 						// test, if the source directory is one of ours as well
219 						nodeRef.node = fromNode;
220 						font_directory* fromDirectory = _FindDirectory(nodeRef);
221 
222 						if (entry.IsDirectory()) {
223 							if (fromDirectory == NULL) {
224 								// there is a new directory to watch for us
225 								_AddPath(entry);
226 								FTRACE("new directory moved in");
227 							} else {
228 								// A directory from our watched directories has
229 								// been renamed or moved within the watched
230 								// directories - we only need to update the
231 								// path names of the styles in that directory
232 								nodeRef.node = node;
233 								directory = _FindDirectory(nodeRef);
234 								if (directory != NULL) {
235 									for (int32 i = 0; i < directory->styles.CountItems(); i++) {
236 										FontStyle* style = directory->styles.ItemAt(i);
237 										style->UpdatePath(directory->directory);
238 									}
239 								}
240 								FTRACE("directory renamed");
241 							}
242 						} else {
243 							if (fromDirectory != NULL) {
244 								// find style in source and move it to the target
245 								nodeRef.node = node;
246 								FontStyle* style = fromDirectory->FindStyle(nodeRef);
247 								if (style != NULL) {
248 									fromDirectory->styles.RemoveItem(style, false);
249 									directory->styles.AddItem(style);
250 									style->UpdatePath(directory->directory);
251 								}
252 								FTRACE(("font moved"));
253 							} else {
254 								FTRACE(("font added: %s\n", name));
255 								_AddFont(*directory, entry);
256 							}
257 						}
258 					} else {
259 						// and entry has been removed from our font directories
260 						if (entry.IsDirectory()) {
261 							if (entry.GetNodeRef(&nodeRef) == B_OK
262 								&& (directory = _FindDirectory(nodeRef)) != NULL)
263 								_RemoveDirectory(directory);
264 						} else {
265 							// remove font style from directory
266 							_RemoveStyle(nodeRef.device, fromNode, node);
267 						}
268 					}
269 					break;
270 				}
271 
272 				case B_ENTRY_REMOVED:
273 				{
274 					node_ref nodeRef;
275 					uint64 directoryNode;
276 					if (message->FindInt32("device", &nodeRef.device) != B_OK
277 						|| message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
278 						|| message->FindInt64("node", &nodeRef.node) != B_OK)
279 						break;
280 
281 					font_directory* directory = _FindDirectory(nodeRef);
282 					if (directory != NULL) {
283 						// the directory has been removed, so we remove it as well
284 						_RemoveDirectory(directory);
285 					} else {
286 						// remove font style from directory
287 						_RemoveStyle(nodeRef.device, directoryNode, nodeRef.node);
288 					}
289 					break;
290 				}
291 			}
292 			break;
293 		}
294 	}
295 }
296 
297 
298 void
299 FontManager::SaveRecentFontMappings()
300 {
301 }
302 
303 
304 void
305 FontManager::_AddDefaultMapping(const char* family, const char* style,
306 	const char* path)
307 {
308 	font_mapping* mapping = new (std::nothrow) font_mapping;
309 	if (mapping == NULL)
310 		return;
311 
312 	mapping->family = family;
313 	mapping->style = style;
314 	BEntry entry(path);
315 
316 	if (entry.GetRef(&mapping->ref) != B_OK
317 		|| !entry.Exists()
318 		|| !fMappings.AddItem(mapping))
319 		delete mapping;
320 }
321 
322 
323 bool
324 FontManager::_LoadRecentFontMappings()
325 {
326 	// default known mappings
327 	// TODO: load them for real, and use these as a fallback
328 
329 	BPath ttfontsPath;
330 	if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) {
331 		ttfontsPath.Append("ttfonts");
332 
333 		BPath veraFontPath = ttfontsPath;
334 		veraFontPath.Append("DejaVuSans.ttf");
335 		_AddDefaultMapping("DejaVu Sans", "Book", veraFontPath.Path());
336 
337 		veraFontPath.SetTo(ttfontsPath.Path());
338 		veraFontPath.Append("DejaVuSans-Bold.ttf");
339 		_AddDefaultMapping("DejaVu Sans", "Bold", veraFontPath.Path());
340 
341 		veraFontPath.SetTo(ttfontsPath.Path());
342 		veraFontPath.Append("DejaVuSansMono.ttf");
343 		_AddDefaultMapping("DejaVu Sans Mono", "Book", veraFontPath.Path());
344 
345 		return true;
346 	}
347 
348 	return false;
349 }
350 
351 
352 status_t
353 FontManager::_AddMappedFont(const char* familyName, const char* styleName)
354 {
355 	FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n",
356 		familyName, styleName));
357 
358 	for (int32 i = 0; i < fMappings.CountItems(); i++) {
359 		font_mapping* mapping = fMappings.ItemAt(i);
360 
361 		if (mapping->family == familyName) {
362 			if (styleName != NULL && mapping->style != styleName)
363 				continue;
364 
365 			BEntry entry(&mapping->ref);
366 			if (entry.InitCheck() != B_OK)
367 				continue;
368 
369 			// find parent directory
370 
371 			node_ref nodeRef;
372 			nodeRef.device = mapping->ref.device;
373 			nodeRef.node = mapping->ref.directory;
374 			font_directory* directory = _FindDirectory(nodeRef);
375 			if (directory == NULL) {
376 				// unknown directory, maybe this is a user font - try
377 				// to create the missing directory
378 				BPath path(&entry);
379 				if (path.GetParent(&path) != B_OK
380 					|| _CreateDirectories(path.Path()) != B_OK
381 					|| (directory = _FindDirectory(nodeRef)) == NULL)
382 					continue;
383 			}
384 
385 			return _AddFont(*directory, entry);
386 		}
387 	}
388 
389 	return B_ENTRY_NOT_FOUND;
390 }
391 
392 
393 /*!	\brief Removes the style from the font directory.
394 
395 	It doesn't necessary delete the font style, if it's still
396 	in use, though.
397 */
398 void
399 FontManager::_RemoveStyle(font_directory& directory, FontStyle* style)
400 {
401 	FTRACE(("font removed: %s\n", style->Name()));
402 
403 	directory.styles.RemoveItem(style);
404 	directory.revision++;
405 
406 	fStyleHashTable.RemoveItem(*style);
407 
408 	style->Release();
409 }
410 
411 
412 void
413 FontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node)
414 {
415 	// remove font style from directory
416 	node_ref nodeRef;
417 	nodeRef.device = device;
418 	nodeRef.node = directoryNode;
419 
420 	font_directory* directory = _FindDirectory(nodeRef);
421 	if (directory != NULL) {
422 		// find style in directory and remove it
423 		nodeRef.node = node;
424 		FontStyle* style = directory->FindStyle(nodeRef);
425 		if (style != NULL)
426 			_RemoveStyle(*directory, style);
427 	}
428 }
429 
430 
431 FontStyle*
432 FontManager::_GetDefaultStyle(const char *familyName, const char *styleName,
433 	const char *fallbackFamily, const char *fallbackStyle,
434 	uint16 fallbackFace)
435 {
436 	// try to find a matching font
437 
438 	FontStyle* style = GetStyle(familyName, styleName);
439 	if (style == NULL) {
440 		style = GetStyle(fallbackFamily, fallbackStyle);
441 		if (style == NULL) {
442 			style = FindStyleMatchingFace(fallbackFace);
443 			if (style == NULL && FamilyAt(0) != NULL)
444 				style = FamilyAt(0)->StyleAt(0);
445 		}
446 	}
447 
448 	return style;
449 }
450 
451 
452 /*!	\brief Sets the fonts that will be used when you create an empty
453 		ServerFont without specifying a style, as well as the default
454 		Desktop fonts if there are no settings available.
455 */
456 status_t
457 FontManager::_SetDefaultFonts()
458 {
459 	// plain font
460 	FontStyle* style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY,
461 		DEFAULT_PLAIN_FONT_STYLE, FALLBACK_PLAIN_FONT_FAMILY,
462 		DEFAULT_PLAIN_FONT_STYLE,
463 		B_REGULAR_FACE);
464 	if (style == NULL)
465 		return B_ERROR;
466 
467 	fDefaultPlainFont = new (std::nothrow) ServerFont(*style,
468 		DEFAULT_PLAIN_FONT_SIZE);
469 	if (fDefaultPlainFont == NULL)
470 		return B_NO_MEMORY;
471 
472 	// bold font
473 	style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE,
474 		FALLBACK_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, B_BOLD_FACE);
475 
476 	fDefaultBoldFont = new (std::nothrow) ServerFont(*style,
477 		DEFAULT_BOLD_FONT_SIZE);
478 	if (fDefaultBoldFont == NULL)
479 		return B_NO_MEMORY;
480 
481 	// fixed font
482 	style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE,
483 		FALLBACK_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, B_REGULAR_FACE);
484 
485 	fDefaultFixedFont = new (std::nothrow) ServerFont(*style,
486 		DEFAULT_FIXED_FONT_SIZE);
487 	if (fDefaultFixedFont == NULL)
488 		return B_NO_MEMORY;
489 
490 	fDefaultFixedFont->SetSpacing(B_FIXED_SPACING);
491 
492 	return B_OK;
493 }
494 
495 
496 void
497 FontManager::_PrecacheFontFile(const ServerFont* font)
498 {
499 	if (font == NULL)
500 		return;
501 
502 	size_t bufferSize = 32768;
503 	uint8* buffer = new (std::nothrow) uint8[bufferSize];
504 	if (buffer == NULL) {
505 		// We don't care. Pre-caching doesn't make sense anyways when there
506 		// is not enough RAM...
507 		return;
508 	}
509 
510 	BFile file(font->Path(), B_READ_ONLY);
511 	if (file.InitCheck() != B_OK) {
512 		delete[] buffer;
513 		return;
514 	}
515 
516 	while (true) {
517 		// We just want the file in the kernel file cache...
518 		ssize_t read = file.Read(buffer, bufferSize);
519 		if (read < (ssize_t)bufferSize)
520 			break;
521 	}
522 
523 	delete[] buffer;
524 }
525 
526 
527 void
528 FontManager::_AddSystemPaths()
529 {
530 	BPath path;
531 	if (find_directory(B_BEOS_FONTS_DIRECTORY, &path, true) == B_OK)
532 		_AddPath(path.Path());
533 
534 	// We don't scan these in test mode to help shave off some startup time
535 #if !TEST_MODE
536 	if (find_directory(B_COMMON_FONTS_DIRECTORY, &path, true) == B_OK)
537 		_AddPath(path.Path());
538 #endif
539 }
540 
541 
542 void
543 FontManager::_ScanFontsIfNecessary()
544 {
545 	if (!fScanned)
546 		_ScanFonts();
547 }
548 
549 
550 //! Scans all currently known font directories
551 void
552 FontManager::_ScanFonts()
553 {
554 	if (fScanned)
555 		return;
556 
557 	for (int32 i = fDirectories.CountItems(); i-- > 0;) {
558 		font_directory* directory = fDirectories.ItemAt(i);
559 
560 		if (directory->AlreadyScanned())
561 			continue;
562 
563 		_ScanFontDirectory(*directory);
564 	}
565 
566 	fScanned = true;
567 }
568 
569 
570 /*!	\brief Adds the FontFamily/FontStyle that is represented by this path.
571 */
572 status_t
573 FontManager::_AddFont(font_directory& directory, BEntry& entry)
574 {
575 	node_ref nodeRef;
576 	status_t status = entry.GetNodeRef(&nodeRef);
577 	if (status < B_OK)
578 		return status;
579 
580 	BPath path;
581 	status = entry.GetPath(&path);
582 	if (status < B_OK)
583 		return status;
584 
585 	FT_Face face;
586 	FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face);
587 	if (error != 0)
588 		return B_ERROR;
589 
590 	FontFamily *family = _FindFamily(face->family_name);
591 	if (family != NULL && family->HasStyle(face->style_name)) {
592 		// prevent adding the same style twice
593 		// (this indicates a problem with the installed fonts maybe?)
594 		FT_Done_Face(face);
595 		return B_OK;
596 	}
597 
598 	if (family == NULL) {
599 		family = new (std::nothrow) FontFamily(face->family_name, fNextID++);
600 		if (family == NULL
601 			|| !fFamilies.BinaryInsert(family, compare_font_families)) {
602 			delete family;
603 			FT_Done_Face(face);
604 			return B_NO_MEMORY;
605 		}
606 	}
607 
608 	FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name));
609 
610 	// the FontStyle takes over ownership of the FT_Face object
611 	FontStyle *style = new FontStyle(nodeRef, path.Path(), face);
612 	if (!family->AddStyle(style)) {
613 		delete style;
614 		delete family;
615 		return B_NO_MEMORY;
616 	}
617 
618 	directory.styles.AddItem(style);
619 	fStyleHashTable.AddItem(style);
620 
621 	if (directory.AlreadyScanned())
622 		directory.revision++;
623 
624 	return B_OK;
625 }
626 
627 
628 FontManager::font_directory*
629 FontManager::_FindDirectory(node_ref& nodeRef)
630 {
631 	for (int32 i = fDirectories.CountItems(); i-- > 0;) {
632 		font_directory* directory = fDirectories.ItemAt(i);
633 
634 		if (directory->directory == nodeRef)
635 			return directory;
636 	}
637 
638 	return NULL;
639 }
640 
641 
642 void
643 FontManager::_RemoveDirectory(font_directory* directory)
644 {
645 	FTRACE(("FontManager: Remove directory (%Ld)!\n", directory->directory.node));
646 
647 	fDirectories.RemoveItem(directory, false);
648 
649 	// TODO: remove styles from this directory!
650 
651 	watch_node(&directory->directory, B_STOP_WATCHING, this);
652 	delete directory;
653 }
654 
655 
656 status_t
657 FontManager::_AddPath(const char* path)
658 {
659 	BEntry entry;
660 	status_t status = entry.SetTo(path);
661 	if (status != B_OK)
662 		return status;
663 
664 	return _AddPath(entry);
665 }
666 
667 
668 status_t
669 FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
670 {
671 	node_ref nodeRef;
672 	status_t status = entry.GetNodeRef(&nodeRef);
673 	if (status != B_OK)
674 		return status;
675 
676 	// check if we are already know this directory
677 
678 	font_directory* directory = _FindDirectory(nodeRef);
679 	if (directory != NULL) {
680 		if (_newDirectory)
681 			*_newDirectory = directory;
682 		return B_OK;
683 	}
684 
685 	// it's a new one, so let's add it
686 
687 	directory = new (std::nothrow) font_directory;
688 	if (directory == NULL)
689 		return B_NO_MEMORY;
690 
691 	struct stat stat;
692 	status = entry.GetStat(&stat);
693 	if (status != B_OK) {
694 		delete directory;
695 		return status;
696 	}
697 
698 	directory->directory = nodeRef;
699 	directory->user = stat.st_uid;
700 	directory->group = stat.st_gid;
701 	directory->revision = 0;
702 
703 	status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
704 	if (status != B_OK) {
705 		// we cannot watch this directory - while this is unfortunate,
706 		// it's not a critical error
707 		printf("could not watch directory %ld:%Ld\n", nodeRef.device,
708 			nodeRef.node);
709 			// TODO: should go into syslog()
710 	} else {
711 		BPath path(&entry);
712 		FTRACE(("FontManager: now watching: %s\n", path.Path()));
713 	}
714 
715 	fDirectories.AddItem(directory);
716 
717 	if (_newDirectory)
718 		*_newDirectory = directory;
719 
720 	fScanned = false;
721 	return B_OK;
722 }
723 
724 
725 /*!	\brief Creates all unknown font_directories of the specified path - but
726 		only if one of its parent directories is already known.
727 
728 	This method is used to create the font_directories for font_mappings.
729 	It recursively walks upwards in the directory hierarchy until it finds
730 	a known font_directory (or hits the root directory, in which case it
731 	bails out).
732 */
733 status_t
734 FontManager::_CreateDirectories(const char* path)
735 {
736 	FTRACE(("_CreateDirectories(path = %s)\n", path));
737 
738 	if (!strcmp(path, "/")) {
739 		// we walked our way up to the root
740 		return B_ENTRY_NOT_FOUND;
741 	}
742 
743 	BEntry entry;
744 	status_t status = entry.SetTo(path);
745 	if (status != B_OK)
746 		return status;
747 
748 	node_ref nodeRef;
749 	status = entry.GetNodeRef(&nodeRef);
750 	if (status != B_OK)
751 		return status;
752 
753 	// check if we are already know this directory
754 
755 	font_directory* directory = _FindDirectory(nodeRef);
756 	if (directory != NULL)
757 		return B_OK;
758 
759 	// We don't know this one yet - keep walking the path upwards
760 	// and try to find a match.
761 
762 	BPath parent(path);
763 	status = parent.GetParent(&parent);
764 	if (status != B_OK)
765 		return status;
766 
767 	status = _CreateDirectories(parent.Path());
768 	if (status != B_OK)
769 		return status;
770 
771 	// We have our match, create sub directory
772 
773 	return _AddPath(path);
774 }
775 
776 
777 /*!	\brief Scan a folder for all valid fonts
778 	\param directoryPath Path of the folder to scan.
779 */
780 status_t
781 FontManager::_ScanFontDirectory(font_directory& fontDirectory)
782 {
783 	// This bad boy does all the real work. It loads each entry in the
784 	// directory. If a valid font file, it adds both the family and the style.
785 
786 	BDirectory directory;
787 	status_t status = directory.SetTo(&fontDirectory.directory);
788 	if (status != B_OK)
789 		return status;
790 
791 	BEntry entry;
792 	while (directory.GetNextEntry(&entry) == B_OK) {
793 		if (entry.IsDirectory()) {
794 			// scan this directory recursively
795 			font_directory* newDirectory;
796 			if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL)
797 				_ScanFontDirectory(*newDirectory);
798 
799 			continue;
800 		}
801 
802 // TODO: Commenting this out makes my "Unicode glyph lookup"
803 // work with our default fonts. The real fix is to select the
804 // Unicode char map (if supported), and/or adjust the
805 // utf8 -> glyph-index mapping everywhere to handle other
806 // char maps. We could also ignore fonts that don't support
807 // the Unicode lookup as a temporary "solution".
808 #if 0
809 		FT_CharMap charmap = _GetSupportedCharmap(face);
810 		if (!charmap) {
811 		    FT_Done_Face(face);
812 		    continue;
813     	}
814 
815 		face->charmap = charmap;
816 #endif
817 
818 		_AddFont(fontDirectory, entry);
819 			// takes over ownership of the FT_Face object
820 	}
821 
822 	fontDirectory.revision = 1;
823 	return B_OK;
824 }
825 
826 
827 /*!	\brief Finds and returns the first valid charmap in a font
828 
829 	\param face Font handle obtained from FT_Load_Face()
830 	\return An FT_CharMap or NULL if unsuccessful
831 */
832 FT_CharMap
833 FontManager::_GetSupportedCharmap(const FT_Face& face)
834 {
835 	for (int32 i = 0; i < face->num_charmaps; i++) {
836 		FT_CharMap charmap = face->charmaps[i];
837 
838 		switch (charmap->platform_id) {
839 			case 3:
840 				// if Windows Symbol or Windows Unicode
841 				if (charmap->encoding_id == 0 || charmap->encoding_id == 1)
842 					return charmap;
843 				break;
844 
845 			case 1:
846 				// if Apple Unicode
847 				if (charmap->encoding_id == 0)
848 					return charmap;
849 				break;
850 
851 			case 0:
852 				// if Apple Roman
853 				if (charmap->encoding_id == 0)
854 					return charmap;
855 				break;
856 
857 			default:
858 				break;
859 		}
860 	}
861 
862 	return NULL;
863 }
864 
865 
866 int32
867 FontManager::CheckRevision(uid_t user)
868 {
869 	BAutolock locker(this);
870 	int32 revision = 0;
871 
872 	_ScanFontsIfNecessary();
873 
874 	for (int32 i = 0; i < fDirectories.CountItems(); i++) {
875 		font_directory* directory = fDirectories.ItemAt(i);
876 
877 		// TODO: for now, add all directories
878 		revision += directory->revision;
879 	}
880 
881 	return revision;
882 }
883 
884 
885 /*!	\brief Counts the number of font families available
886 	\return The number of unique font families currently available
887 */
888 int32
889 FontManager::CountFamilies()
890 {
891 	_ScanFontsIfNecessary();
892 
893 	return fFamilies.CountItems();
894 }
895 
896 
897 /*!	\brief Counts the number of styles available in a font family
898 	\param family Name of the font family to scan
899 	\return The number of font styles currently available for the font family
900 */
901 int32
902 FontManager::CountStyles(const char *familyName)
903 {
904 	_ScanFontsIfNecessary();
905 
906 	FontFamily *family = GetFamily(familyName);
907 	if (family)
908 		return family->CountStyles();
909 
910 	return 0;
911 }
912 
913 
914 /*!	\brief Counts the number of styles available in a font family
915 	\param family Name of the font family to scan
916 	\return The number of font styles currently available for the font family
917 */
918 int32
919 FontManager::CountStyles(uint16 familyID)
920 {
921 	_ScanFontsIfNecessary();
922 
923 	FontFamily *family = GetFamily(familyID);
924 	if (family)
925 		return family->CountStyles();
926 
927 	return 0;
928 }
929 
930 
931 FontFamily*
932 FontManager::FamilyAt(int32 index) const
933 {
934 	return fFamilies.ItemAt(index);
935 }
936 
937 
938 FontFamily*
939 FontManager::_FindFamily(const char* name) const
940 {
941 	if (name == NULL)
942 		return NULL;
943 
944 	FontFamily family(name, 0);
945 	return const_cast<FontFamily*>(fFamilies.BinarySearch(family,
946 		compare_font_families));
947 }
948 
949 
950 /*!	\brief Locates a FontFamily object by name
951 	\param name The family to find
952 	\return Pointer to the specified family or NULL if not found.
953 */
954 FontFamily*
955 FontManager::GetFamily(const char* name)
956 {
957 	if (name == NULL)
958 		return NULL;
959 
960 	FontFamily* family = _FindFamily(name);
961 	if (family != NULL)
962 		return family;
963 
964 	if (fScanned)
965 		return NULL;
966 
967 	// try font mappings before failing
968 	if (_AddMappedFont(name) == B_OK)
969 		return _FindFamily(name);
970 
971 	_ScanFonts();
972 	return _FindFamily(name);
973 }
974 
975 
976 FontFamily*
977 FontManager::GetFamily(uint16 familyID) const
978 {
979 	FontKey key(familyID, 0);
980 	FontStyle* style = (FontStyle*)fStyleHashTable.GetValue(key);
981 	if (style != NULL)
982 		return style->Family();
983 
984 	return NULL;
985 }
986 
987 
988 FontStyle*
989 FontManager::GetStyleByIndex(const char* familyName, int32 index)
990 {
991 	FontFamily* family = GetFamily(familyName);
992 	if (family != NULL)
993 		return family->StyleAt(index);
994 
995 	return NULL;
996 }
997 
998 
999 FontStyle*
1000 FontManager::GetStyleByIndex(uint16 familyID, int32 index)
1001 {
1002 	FontFamily* family = GetFamily(familyID);
1003 	if (family != NULL)
1004 		return family->StyleAt(index);
1005 
1006 	return NULL;
1007 }
1008 
1009 
1010 /*!	\brief Retrieves the FontStyle object that comes closest to the one
1011 		specified.
1012 
1013 	\param family The font's family or NULL in which case \a familyID is used
1014 	\param style The font's style or NULL in which case \a styleID is used
1015 	\param familyID will only be used if \a family is NULL (or empty)
1016 	\param styleID will only be used if \a style is NULL (or empty)
1017 	\param face is used to specify the style if both \a style is NULL or empty
1018 		and styleID is 0xffff.
1019 
1020 	\return The FontStyle having those attributes or NULL if not available
1021 */
1022 FontStyle*
1023 FontManager::GetStyle(const char* familyName, const char* styleName,
1024 	uint16 familyID, uint16 styleID, uint16 face)
1025 {
1026 	FontFamily* family;
1027 
1028 	// find family
1029 
1030 	if (familyName != NULL && familyName[0])
1031 		family = GetFamily(familyName);
1032 	else
1033 		family = GetFamily(familyID);
1034 
1035 	if (family == NULL)
1036 		return NULL;
1037 
1038 	// find style
1039 
1040 	if (styleName != NULL && styleName[0]) {
1041 		FontStyle* fontStyle = family->GetStyle(styleName);
1042 		if (fontStyle != NULL)
1043 			return fontStyle;
1044 
1045 		// before we fail, we try the mappings for a match
1046 		if (_AddMappedFont(family->Name(), styleName) == B_OK) {
1047 			fontStyle = family->GetStyle(styleName);
1048 			if (fontStyle != NULL)
1049 				return fontStyle;
1050 		}
1051 
1052 		_ScanFonts();
1053 		return family->GetStyle(styleName);
1054 	}
1055 
1056 	if (styleID != 0xffff)
1057 		return family->GetStyleByID(styleID);
1058 
1059 	// try to get from face
1060 	return family->GetStyleMatchingFace(face);
1061 }
1062 
1063 
1064 /*!	\brief Retrieves the FontStyle object
1065 	\param family ID for the font's family
1066 	\param style ID of the font's style
1067 	\return The FontStyle having those attributes or NULL if not available
1068 */
1069 FontStyle*
1070 FontManager::GetStyle(uint16 familyID, uint16 styleID) const
1071 {
1072 	FontKey key(familyID, styleID);
1073 	return (FontStyle*)fStyleHashTable.GetValue(key);
1074 }
1075 
1076 
1077 /*!	\brief If you don't find your preferred font style, but are anxious
1078 		to have one fitting your needs, you may want to use this method.
1079 */
1080 FontStyle*
1081 FontManager::FindStyleMatchingFace(uint16 face) const
1082 {
1083 	int32 count = fFamilies.CountItems();
1084 
1085 	for (int32 i = 0; i < count; i++) {
1086 		FontFamily* family = fFamilies.ItemAt(i);
1087 		FontStyle* style = family->GetStyleMatchingFace(face);
1088 		if (style != NULL)
1089 			return style;
1090 	}
1091 
1092 	return NULL;
1093 }
1094 
1095 
1096 /*!	\brief This call is used by the FontStyle class - and the FontStyle class
1097 		only - to remove itself from the font manager.
1098 	At this point, the style is already no longer available to the user.
1099 */
1100 void
1101 FontManager::RemoveStyle(FontStyle* style)
1102 {
1103 	FontFamily* family = style->Family();
1104 	if (family == NULL)
1105 		debugger("family is NULL!");
1106 
1107 	FontStyle* check = GetStyle(family->ID(), style->ID());
1108 	if (check != NULL)
1109 		debugger("style removed but still available!");
1110 
1111 	if (family->RemoveStyle(style)
1112 		&& family->CountStyles() == 0)
1113 		fFamilies.RemoveItem(family);
1114 }
1115 
1116 
1117 const ServerFont*
1118 FontManager::DefaultPlainFont() const
1119 {
1120 	return fDefaultPlainFont;
1121 }
1122 
1123 
1124 const ServerFont*
1125 FontManager::DefaultBoldFont() const
1126 {
1127 	return fDefaultBoldFont;
1128 }
1129 
1130 
1131 const ServerFont*
1132 FontManager::DefaultFixedFont() const
1133 {
1134 	return fDefaultFixedFont;
1135 }
1136 
1137 
1138 void
1139 FontManager::AttachUser(uid_t userID)
1140 {
1141 	BAutolock locker(this);
1142 
1143 #if !TEST_MODE
1144 	// TODO: actually, find_directory() cannot know which user ID we want here
1145 	// TODO: avoids user fonts in safe mode
1146 	BPath path;
1147 	if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) != B_OK)
1148 		return;
1149 
1150 	_AddPath(path.Path());
1151 #endif
1152 }
1153 
1154 
1155 void
1156 FontManager::DetachUser(uid_t userID)
1157 {
1158 	BAutolock locker(this);
1159 
1160 	// TODO!
1161 }
1162 
1163