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
40FT_Library gFreeTypeLibrary;
41FontManager *gFontManager = NULL;
42
43struct 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
54struct FontManager::font_mapping {
55	BString		family;
56	BString		style;
57	entry_ref	ref;
58};
59
60
61FontStyle*
62FontManager::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
75static status_t
76set_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
90static int
91compare_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
101FontManager::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
132FontManager::~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
148void
149FontManager::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
298void
299FontManager::SaveRecentFontMappings()
300{
301}
302
303
304void
305FontManager::_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
323bool
324FontManager::_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
352status_t
353FontManager::_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*/
398void
399FontManager::_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
412void
413FontManager::_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
431FontStyle*
432FontManager::_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*/
456status_t
457FontManager::_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
496void
497FontManager::_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
527void
528FontManager::_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
542void
543FontManager::_ScanFontsIfNecessary()
544{
545	if (!fScanned)
546		_ScanFonts();
547}
548
549
550//! Scans all currently known font directories
551void
552FontManager::_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*/
572status_t
573FontManager::_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
628FontManager::font_directory*
629FontManager::_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
642void
643FontManager::_RemoveDirectory(font_directory* directory)
644{
645	FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
646		directory->directory.node));
647
648	fDirectories.RemoveItem(directory, false);
649
650	// TODO: remove styles from this directory!
651
652	watch_node(&directory->directory, B_STOP_WATCHING, this);
653	delete directory;
654}
655
656
657status_t
658FontManager::_AddPath(const char* path)
659{
660	BEntry entry;
661	status_t status = entry.SetTo(path);
662	if (status != B_OK)
663		return status;
664
665	return _AddPath(entry);
666}
667
668
669status_t
670FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
671{
672	node_ref nodeRef;
673	status_t status = entry.GetNodeRef(&nodeRef);
674	if (status != B_OK)
675		return status;
676
677	// check if we are already know this directory
678
679	font_directory* directory = _FindDirectory(nodeRef);
680	if (directory != NULL) {
681		if (_newDirectory)
682			*_newDirectory = directory;
683		return B_OK;
684	}
685
686	// it's a new one, so let's add it
687
688	directory = new (std::nothrow) font_directory;
689	if (directory == NULL)
690		return B_NO_MEMORY;
691
692	struct stat stat;
693	status = entry.GetStat(&stat);
694	if (status != B_OK) {
695		delete directory;
696		return status;
697	}
698
699	directory->directory = nodeRef;
700	directory->user = stat.st_uid;
701	directory->group = stat.st_gid;
702	directory->revision = 0;
703
704	status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
705	if (status != B_OK) {
706		// we cannot watch this directory - while this is unfortunate,
707		// it's not a critical error
708		printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n",
709			nodeRef.device, nodeRef.node);
710			// TODO: should go into syslog()
711	} else {
712		BPath path(&entry);
713		FTRACE(("FontManager: now watching: %s\n", path.Path()));
714	}
715
716	fDirectories.AddItem(directory);
717
718	if (_newDirectory)
719		*_newDirectory = directory;
720
721	fScanned = false;
722	return B_OK;
723}
724
725
726/*!	\brief Creates all unknown font_directories of the specified path - but
727		only if one of its parent directories is already known.
728
729	This method is used to create the font_directories for font_mappings.
730	It recursively walks upwards in the directory hierarchy until it finds
731	a known font_directory (or hits the root directory, in which case it
732	bails out).
733*/
734status_t
735FontManager::_CreateDirectories(const char* path)
736{
737	FTRACE(("_CreateDirectories(path = %s)\n", path));
738
739	if (!strcmp(path, "/")) {
740		// we walked our way up to the root
741		return B_ENTRY_NOT_FOUND;
742	}
743
744	BEntry entry;
745	status_t status = entry.SetTo(path);
746	if (status != B_OK)
747		return status;
748
749	node_ref nodeRef;
750	status = entry.GetNodeRef(&nodeRef);
751	if (status != B_OK)
752		return status;
753
754	// check if we are already know this directory
755
756	font_directory* directory = _FindDirectory(nodeRef);
757	if (directory != NULL)
758		return B_OK;
759
760	// We don't know this one yet - keep walking the path upwards
761	// and try to find a match.
762
763	BPath parent(path);
764	status = parent.GetParent(&parent);
765	if (status != B_OK)
766		return status;
767
768	status = _CreateDirectories(parent.Path());
769	if (status != B_OK)
770		return status;
771
772	// We have our match, create sub directory
773
774	return _AddPath(path);
775}
776
777
778/*!	\brief Scan a folder for all valid fonts
779	\param directoryPath Path of the folder to scan.
780*/
781status_t
782FontManager::_ScanFontDirectory(font_directory& fontDirectory)
783{
784	// This bad boy does all the real work. It loads each entry in the
785	// directory. If a valid font file, it adds both the family and the style.
786
787	BDirectory directory;
788	status_t status = directory.SetTo(&fontDirectory.directory);
789	if (status != B_OK)
790		return status;
791
792	BEntry entry;
793	while (directory.GetNextEntry(&entry) == B_OK) {
794		if (entry.IsDirectory()) {
795			// scan this directory recursively
796			font_directory* newDirectory;
797			if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL)
798				_ScanFontDirectory(*newDirectory);
799
800			continue;
801		}
802
803// TODO: Commenting this out makes my "Unicode glyph lookup"
804// work with our default fonts. The real fix is to select the
805// Unicode char map (if supported), and/or adjust the
806// utf8 -> glyph-index mapping everywhere to handle other
807// char maps. We could also ignore fonts that don't support
808// the Unicode lookup as a temporary "solution".
809#if 0
810		FT_CharMap charmap = _GetSupportedCharmap(face);
811		if (!charmap) {
812		    FT_Done_Face(face);
813		    continue;
814    	}
815
816		face->charmap = charmap;
817#endif
818
819		_AddFont(fontDirectory, entry);
820			// takes over ownership of the FT_Face object
821	}
822
823	fontDirectory.revision = 1;
824	return B_OK;
825}
826
827
828/*!	\brief Finds and returns the first valid charmap in a font
829
830	\param face Font handle obtained from FT_Load_Face()
831	\return An FT_CharMap or NULL if unsuccessful
832*/
833FT_CharMap
834FontManager::_GetSupportedCharmap(const FT_Face& face)
835{
836	for (int32 i = 0; i < face->num_charmaps; i++) {
837		FT_CharMap charmap = face->charmaps[i];
838
839		switch (charmap->platform_id) {
840			case 3:
841				// if Windows Symbol or Windows Unicode
842				if (charmap->encoding_id == 0 || charmap->encoding_id == 1)
843					return charmap;
844				break;
845
846			case 1:
847				// if Apple Unicode
848				if (charmap->encoding_id == 0)
849					return charmap;
850				break;
851
852			case 0:
853				// if Apple Roman
854				if (charmap->encoding_id == 0)
855					return charmap;
856				break;
857
858			default:
859				break;
860		}
861	}
862
863	return NULL;
864}
865
866
867int32
868FontManager::CheckRevision(uid_t user)
869{
870	BAutolock locker(this);
871	int32 revision = 0;
872
873	_ScanFontsIfNecessary();
874
875	for (int32 i = 0; i < fDirectories.CountItems(); i++) {
876		font_directory* directory = fDirectories.ItemAt(i);
877
878		// TODO: for now, add all directories
879		revision += directory->revision;
880	}
881
882	return revision;
883}
884
885
886/*!	\brief Counts the number of font families available
887	\return The number of unique font families currently available
888*/
889int32
890FontManager::CountFamilies()
891{
892	_ScanFontsIfNecessary();
893
894	return fFamilies.CountItems();
895}
896
897
898/*!	\brief Counts the number of styles available in a font family
899	\param family Name of the font family to scan
900	\return The number of font styles currently available for the font family
901*/
902int32
903FontManager::CountStyles(const char *familyName)
904{
905	_ScanFontsIfNecessary();
906
907	FontFamily *family = GetFamily(familyName);
908	if (family)
909		return family->CountStyles();
910
911	return 0;
912}
913
914
915/*!	\brief Counts the number of styles available in a font family
916	\param family Name of the font family to scan
917	\return The number of font styles currently available for the font family
918*/
919int32
920FontManager::CountStyles(uint16 familyID)
921{
922	_ScanFontsIfNecessary();
923
924	FontFamily *family = GetFamily(familyID);
925	if (family)
926		return family->CountStyles();
927
928	return 0;
929}
930
931
932FontFamily*
933FontManager::FamilyAt(int32 index) const
934{
935	return fFamilies.ItemAt(index);
936}
937
938
939FontFamily*
940FontManager::_FindFamily(const char* name) const
941{
942	if (name == NULL)
943		return NULL;
944
945	FontFamily family(name, 0);
946	return const_cast<FontFamily*>(fFamilies.BinarySearch(family,
947		compare_font_families));
948}
949
950
951/*!	\brief Locates a FontFamily object by name
952	\param name The family to find
953	\return Pointer to the specified family or NULL if not found.
954*/
955FontFamily*
956FontManager::GetFamily(const char* name)
957{
958	if (name == NULL)
959		return NULL;
960
961	FontFamily* family = _FindFamily(name);
962	if (family != NULL)
963		return family;
964
965	if (fScanned)
966		return NULL;
967
968	// try font mappings before failing
969	if (_AddMappedFont(name) == B_OK)
970		return _FindFamily(name);
971
972	_ScanFonts();
973	return _FindFamily(name);
974}
975
976
977FontFamily*
978FontManager::GetFamily(uint16 familyID) const
979{
980	FontKey key(familyID, 0);
981	FontStyle* style = (FontStyle*)fStyleHashTable.GetValue(key);
982	if (style != NULL)
983		return style->Family();
984
985	return NULL;
986}
987
988
989FontStyle*
990FontManager::GetStyleByIndex(const char* familyName, int32 index)
991{
992	FontFamily* family = GetFamily(familyName);
993	if (family != NULL)
994		return family->StyleAt(index);
995
996	return NULL;
997}
998
999
1000FontStyle*
1001FontManager::GetStyleByIndex(uint16 familyID, int32 index)
1002{
1003	FontFamily* family = GetFamily(familyID);
1004	if (family != NULL)
1005		return family->StyleAt(index);
1006
1007	return NULL;
1008}
1009
1010
1011/*!	\brief Retrieves the FontStyle object that comes closest to the one
1012		specified.
1013
1014	\param family The font's family or NULL in which case \a familyID is used
1015	\param style The font's style or NULL in which case \a styleID is used
1016	\param familyID will only be used if \a family is NULL (or empty)
1017	\param styleID will only be used if \a style is NULL (or empty)
1018	\param face is used to specify the style if both \a style is NULL or empty
1019		and styleID is 0xffff.
1020
1021	\return The FontStyle having those attributes or NULL if not available
1022*/
1023FontStyle*
1024FontManager::GetStyle(const char* familyName, const char* styleName,
1025	uint16 familyID, uint16 styleID, uint16 face)
1026{
1027	FontFamily* family;
1028
1029	// find family
1030
1031	if (familyName != NULL && familyName[0])
1032		family = GetFamily(familyName);
1033	else
1034		family = GetFamily(familyID);
1035
1036	if (family == NULL)
1037		return NULL;
1038
1039	// find style
1040
1041	if (styleName != NULL && styleName[0]) {
1042		FontStyle* fontStyle = family->GetStyle(styleName);
1043		if (fontStyle != NULL)
1044			return fontStyle;
1045
1046		// before we fail, we try the mappings for a match
1047		if (_AddMappedFont(family->Name(), styleName) == B_OK) {
1048			fontStyle = family->GetStyle(styleName);
1049			if (fontStyle != NULL)
1050				return fontStyle;
1051		}
1052
1053		_ScanFonts();
1054		return family->GetStyle(styleName);
1055	}
1056
1057	if (styleID != 0xffff)
1058		return family->GetStyleByID(styleID);
1059
1060	// try to get from face
1061	return family->GetStyleMatchingFace(face);
1062}
1063
1064
1065/*!	\brief Retrieves the FontStyle object
1066	\param family ID for the font's family
1067	\param style ID of the font's style
1068	\return The FontStyle having those attributes or NULL if not available
1069*/
1070FontStyle*
1071FontManager::GetStyle(uint16 familyID, uint16 styleID) const
1072{
1073	FontKey key(familyID, styleID);
1074	return (FontStyle*)fStyleHashTable.GetValue(key);
1075}
1076
1077
1078/*!	\brief If you don't find your preferred font style, but are anxious
1079		to have one fitting your needs, you may want to use this method.
1080*/
1081FontStyle*
1082FontManager::FindStyleMatchingFace(uint16 face) const
1083{
1084	int32 count = fFamilies.CountItems();
1085
1086	for (int32 i = 0; i < count; i++) {
1087		FontFamily* family = fFamilies.ItemAt(i);
1088		FontStyle* style = family->GetStyleMatchingFace(face);
1089		if (style != NULL)
1090			return style;
1091	}
1092
1093	return NULL;
1094}
1095
1096
1097/*!	\brief This call is used by the FontStyle class - and the FontStyle class
1098		only - to remove itself from the font manager.
1099	At this point, the style is already no longer available to the user.
1100*/
1101void
1102FontManager::RemoveStyle(FontStyle* style)
1103{
1104	FontFamily* family = style->Family();
1105	if (family == NULL)
1106		debugger("family is NULL!");
1107
1108	FontStyle* check = GetStyle(family->ID(), style->ID());
1109	if (check != NULL)
1110		debugger("style removed but still available!");
1111
1112	if (family->RemoveStyle(style)
1113		&& family->CountStyles() == 0)
1114		fFamilies.RemoveItem(family);
1115}
1116
1117
1118const ServerFont*
1119FontManager::DefaultPlainFont() const
1120{
1121	return fDefaultPlainFont;
1122}
1123
1124
1125const ServerFont*
1126FontManager::DefaultBoldFont() const
1127{
1128	return fDefaultBoldFont;
1129}
1130
1131
1132const ServerFont*
1133FontManager::DefaultFixedFont() const
1134{
1135	return fDefaultFixedFont;
1136}
1137
1138
1139void
1140FontManager::AttachUser(uid_t userID)
1141{
1142	BAutolock locker(this);
1143
1144#if !TEST_MODE
1145	// TODO: actually, find_directory() cannot know which user ID we want here
1146	// TODO: avoids user fonts in safe mode
1147	BPath path;
1148	if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) != B_OK)
1149		return;
1150
1151	_AddPath(path.Path());
1152#endif
1153}
1154
1155
1156void
1157FontManager::DetachUser(uid_t userID)
1158{
1159	BAutolock locker(this);
1160
1161	// TODO!
1162}
1163
1164