usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / servers / app / font / FontManager.cpp
blob18176681dce8c857085e97e83d2a6ba861b62b48
1 /*
2 * Copyright 2001-2009, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * DarkWyrm <bpmagic@columbus.rr.com>
7 * Axel Dörfler, axeld@pinc-software.de
8 */
10 /*! Manages font families and styles */
13 #include "FontFamily.h"
14 #include "FontManager.h"
15 #include "ServerConfig.h"
16 #include "ServerFont.h"
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>
28 #include <new>
30 //#define TRACE_FONT_MANAGER
31 #ifdef TRACE_FONT_MANAGER
32 # define FTRACE(x) printf x
33 #else
34 # define FTRACE(x) ;
35 #endif
38 // TODO: needs some more work for multi-user support
40 FT_Library gFreeTypeLibrary;
41 FontManager *gFontManager = NULL;
43 struct FontManager::font_directory {
44 node_ref directory;
45 uid_t user;
46 gid_t group;
47 uint32 revision;
48 BObjectList<FontStyle> styles;
50 bool AlreadyScanned() const { return revision != 0; }
51 FontStyle* FindStyle(const node_ref& nodeRef) const;
54 struct FontManager::font_mapping {
55 BString family;
56 BString style;
57 entry_ref ref;
61 FontStyle*
62 FontManager::font_directory::FindStyle(const node_ref& nodeRef) const
64 for (int32 i = styles.CountItems(); i-- > 0;) {
65 FontStyle* style = styles.ItemAt(i);
67 if (nodeRef == style->NodeRef())
68 return style;
71 return NULL;
75 static status_t
76 set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
78 entry_ref ref;
79 ref.device = nodeRef.device;
80 ref.directory = nodeRef.node;
82 status_t status = ref.set_name(name);
83 if (status != B_OK)
84 return status;
86 return entry.SetTo(&ref);
90 static int
91 compare_font_families(const FontFamily* a, const FontFamily* b)
93 return strcmp(a->Name(), b->Name());
97 // #pragma mark -
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),
107 fDefaultPlainFont(NULL),
108 fDefaultBoldFont(NULL),
109 fDefaultFixedFont(NULL),
111 fScanned(false),
112 fNextID(0)
114 fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR;
116 if (fInitStatus == B_OK) {
117 _AddSystemPaths();
118 _LoadRecentFontMappings();
120 fInitStatus = _SetDefaultFonts();
122 if (fInitStatus == B_OK) {
123 // Precache the plain and bold fonts
124 _PrecacheFontFile(fDefaultPlainFont);
125 _PrecacheFontFile(fDefaultBoldFont);
131 //! Frees items allocated in the constructor and shuts down FreeType
132 FontManager::~FontManager()
134 delete fDefaultPlainFont;
135 delete fDefaultBoldFont;
136 delete fDefaultFixedFont;
138 // free families before we're done with FreeType
140 for (int32 i = fFamilies.CountItems(); i-- > 0;) {
141 delete fFamilies.ItemAt(i);
144 FT_Done_FreeType(gFreeTypeLibrary);
148 void
149 FontManager::MessageReceived(BMessage* message)
151 switch (message->what) {
152 case B_NODE_MONITOR:
154 // TODO: support removing fonts!
156 int32 opcode;
157 if (message->FindInt32("opcode", &opcode) != B_OK)
158 return;
160 switch (opcode) {
161 case B_ENTRY_CREATED:
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;
170 // TODO: make this better (possible under Haiku)
171 snooze(100000);
172 // let the font be written completely before trying to open it
174 BEntry entry;
175 if (set_entry(nodeRef, name, entry) != B_OK)
176 break;
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;
189 _AddFont(*directory, entry);
191 break;
194 case B_ENTRY_MOVED:
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;
209 font_directory* directory = _FindDirectory(nodeRef);
211 BEntry entry;
212 if (set_entry(nodeRef, name, entry) != B_OK)
213 break;
215 if (directory != NULL) {
216 // something has been added to our watched font directories
218 // test, if the source directory is one of ours as well
219 nodeRef.node = fromNode;
220 font_directory* fromDirectory = _FindDirectory(nodeRef);
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);
240 FTRACE(("directory renamed"));
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);
252 FTRACE(("font moved"));
253 } else {
254 FTRACE(("font added: %s\n", name));
255 _AddFont(*directory, entry);
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);
269 break;
272 case B_ENTRY_REMOVED:
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;
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);
289 break;
292 break;
298 void
299 FontManager::SaveRecentFontMappings()
304 void
305 FontManager::_AddDefaultMapping(const char* family, const char* style,
306 const char* path)
308 font_mapping* mapping = new (std::nothrow) font_mapping;
309 if (mapping == NULL)
310 return;
312 mapping->family = family;
313 mapping->style = style;
314 BEntry entry(path);
316 if (entry.GetRef(&mapping->ref) != B_OK
317 || !entry.Exists()
318 || !fMappings.AddItem(mapping))
319 delete mapping;
323 bool
324 FontManager::_LoadRecentFontMappings()
326 // default known mappings
327 // TODO: load them for real, and use these as a fallback
329 BPath ttfontsPath;
330 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) {
331 ttfontsPath.Append("ttfonts");
333 BPath veraFontPath = ttfontsPath;
334 veraFontPath.Append("DejaVuSans.ttf");
335 _AddDefaultMapping("DejaVu Sans", "Book", veraFontPath.Path());
337 veraFontPath.SetTo(ttfontsPath.Path());
338 veraFontPath.Append("DejaVuSans-Bold.ttf");
339 _AddDefaultMapping("DejaVu Sans", "Bold", veraFontPath.Path());
341 veraFontPath.SetTo(ttfontsPath.Path());
342 veraFontPath.Append("DejaVuSansMono.ttf");
343 _AddDefaultMapping("DejaVu Sans Mono", "Book", veraFontPath.Path());
345 return true;
348 return false;
352 status_t
353 FontManager::_AddMappedFont(const char* familyName, const char* styleName)
355 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n",
356 familyName, styleName));
358 for (int32 i = 0; i < fMappings.CountItems(); i++) {
359 font_mapping* mapping = fMappings.ItemAt(i);
361 if (mapping->family == familyName) {
362 if (styleName != NULL && mapping->style != styleName)
363 continue;
365 BEntry entry(&mapping->ref);
366 if (entry.InitCheck() != B_OK)
367 continue;
369 // find parent directory
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;
385 return _AddFont(*directory, entry);
389 return B_ENTRY_NOT_FOUND;
393 /*! \brief Removes the style from the font directory.
395 It doesn't necessary delete the font style, if it's still
396 in use, though.
398 void
399 FontManager::_RemoveStyle(font_directory& directory, FontStyle* style)
401 FTRACE(("font removed: %s\n", style->Name()));
403 directory.styles.RemoveItem(style);
404 directory.revision++;
406 fStyleHashTable.RemoveItem(*style);
408 style->Release();
412 void
413 FontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node)
415 // remove font style from directory
416 node_ref nodeRef;
417 nodeRef.device = device;
418 nodeRef.node = directoryNode;
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);
431 FontStyle*
432 FontManager::_GetDefaultStyle(const char *familyName, const char *styleName,
433 const char *fallbackFamily, const char *fallbackStyle,
434 uint16 fallbackFace)
436 // try to find a matching font
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);
448 return style;
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.
456 status_t
457 FontManager::_SetDefaultFonts()
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;
467 fDefaultPlainFont = new (std::nothrow) ServerFont(*style,
468 DEFAULT_PLAIN_FONT_SIZE);
469 if (fDefaultPlainFont == NULL)
470 return B_NO_MEMORY;
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);
476 fDefaultBoldFont = new (std::nothrow) ServerFont(*style,
477 DEFAULT_BOLD_FONT_SIZE);
478 if (fDefaultBoldFont == NULL)
479 return B_NO_MEMORY;
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);
485 fDefaultFixedFont = new (std::nothrow) ServerFont(*style,
486 DEFAULT_FIXED_FONT_SIZE);
487 if (fDefaultFixedFont == NULL)
488 return B_NO_MEMORY;
490 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING);
492 return B_OK;
496 void
497 FontManager::_PrecacheFontFile(const ServerFont* font)
499 if (font == NULL)
500 return;
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;
510 BFile file(font->Path(), B_READ_ONLY);
511 if (file.InitCheck() != B_OK) {
512 delete[] buffer;
513 return;
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;
523 delete[] buffer;
527 void
528 FontManager::_AddSystemPaths()
530 BPath path;
531 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK)
532 _AddPath(path.Path());
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_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK)
537 _AddPath(path.Path());
538 #endif
542 void
543 FontManager::_ScanFontsIfNecessary()
545 if (!fScanned)
546 _ScanFonts();
550 //! Scans all currently known font directories
551 void
552 FontManager::_ScanFonts()
554 if (fScanned)
555 return;
557 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
558 font_directory* directory = fDirectories.ItemAt(i);
560 if (directory->AlreadyScanned())
561 continue;
563 _ScanFontDirectory(*directory);
566 fScanned = true;
570 /*! \brief Adds the FontFamily/FontStyle that is represented by this path.
572 status_t
573 FontManager::_AddFont(font_directory& directory, BEntry& entry)
575 node_ref nodeRef;
576 status_t status = entry.GetNodeRef(&nodeRef);
577 if (status < B_OK)
578 return status;
580 BPath path;
581 status = entry.GetPath(&path);
582 if (status < B_OK)
583 return status;
585 FT_Face face;
586 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face);
587 if (error != 0)
588 return B_ERROR;
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;
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;
608 FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name));
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;
618 directory.styles.AddItem(style);
619 fStyleHashTable.AddItem(style);
621 if (directory.AlreadyScanned())
622 directory.revision++;
624 return B_OK;
628 FontManager::font_directory*
629 FontManager::_FindDirectory(node_ref& nodeRef)
631 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
632 font_directory* directory = fDirectories.ItemAt(i);
634 if (directory->directory == nodeRef)
635 return directory;
638 return NULL;
642 void
643 FontManager::_RemoveDirectory(font_directory* directory)
645 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
646 directory->directory.node));
648 fDirectories.RemoveItem(directory, false);
650 // TODO: remove styles from this directory!
652 watch_node(&directory->directory, B_STOP_WATCHING, this);
653 delete directory;
657 status_t
658 FontManager::_AddPath(const char* path)
660 BEntry entry;
661 status_t status = entry.SetTo(path);
662 if (status != B_OK)
663 return status;
665 return _AddPath(entry);
669 status_t
670 FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
672 node_ref nodeRef;
673 status_t status = entry.GetNodeRef(&nodeRef);
674 if (status != B_OK)
675 return status;
677 // check if we are already know this directory
679 font_directory* directory = _FindDirectory(nodeRef);
680 if (directory != NULL) {
681 if (_newDirectory)
682 *_newDirectory = directory;
683 return B_OK;
686 // it's a new one, so let's add it
688 directory = new (std::nothrow) font_directory;
689 if (directory == NULL)
690 return B_NO_MEMORY;
692 struct stat stat;
693 status = entry.GetStat(&stat);
694 if (status != B_OK) {
695 delete directory;
696 return status;
699 directory->directory = nodeRef;
700 directory->user = stat.st_uid;
701 directory->group = stat.st_gid;
702 directory->revision = 0;
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()));
716 fDirectories.AddItem(directory);
718 if (_newDirectory)
719 *_newDirectory = directory;
721 fScanned = false;
722 return B_OK;
726 /*! \brief Creates all unknown font_directories of the specified path - but
727 only if one of its parent directories is already known.
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).
734 status_t
735 FontManager::_CreateDirectories(const char* path)
737 FTRACE(("_CreateDirectories(path = %s)\n", path));
739 if (!strcmp(path, "/")) {
740 // we walked our way up to the root
741 return B_ENTRY_NOT_FOUND;
744 BEntry entry;
745 status_t status = entry.SetTo(path);
746 if (status != B_OK)
747 return status;
749 node_ref nodeRef;
750 status = entry.GetNodeRef(&nodeRef);
751 if (status != B_OK)
752 return status;
754 // check if we are already know this directory
756 font_directory* directory = _FindDirectory(nodeRef);
757 if (directory != NULL)
758 return B_OK;
760 // We don't know this one yet - keep walking the path upwards
761 // and try to find a match.
763 BPath parent(path);
764 status = parent.GetParent(&parent);
765 if (status != B_OK)
766 return status;
768 status = _CreateDirectories(parent.Path());
769 if (status != B_OK)
770 return status;
772 // We have our match, create sub directory
774 return _AddPath(path);
778 /*! \brief Scan a folder for all valid fonts
779 \param directoryPath Path of the folder to scan.
781 status_t
782 FontManager::_ScanFontDirectory(font_directory& fontDirectory)
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.
787 BDirectory directory;
788 status_t status = directory.SetTo(&fontDirectory.directory);
789 if (status != B_OK)
790 return status;
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);
800 continue;
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;
816 face->charmap = charmap;
817 #endif
819 _AddFont(fontDirectory, entry);
820 // takes over ownership of the FT_Face object
823 fontDirectory.revision = 1;
824 return B_OK;
828 /*! \brief Finds and returns the first valid charmap in a font
830 \param face Font handle obtained from FT_Load_Face()
831 \return An FT_CharMap or NULL if unsuccessful
833 FT_CharMap
834 FontManager::_GetSupportedCharmap(const FT_Face& face)
836 for (int32 i = 0; i < face->num_charmaps; i++) {
837 FT_CharMap charmap = face->charmaps[i];
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;
846 case 1:
847 // if Apple Unicode
848 if (charmap->encoding_id == 0)
849 return charmap;
850 break;
852 case 0:
853 // if Apple Roman
854 if (charmap->encoding_id == 0)
855 return charmap;
856 break;
858 default:
859 break;
863 return NULL;
867 int32
868 FontManager::CheckRevision(uid_t user)
870 BAutolock locker(this);
871 int32 revision = 0;
873 _ScanFontsIfNecessary();
875 for (int32 i = 0; i < fDirectories.CountItems(); i++) {
876 font_directory* directory = fDirectories.ItemAt(i);
878 // TODO: for now, add all directories
879 revision += directory->revision;
882 return revision;
886 /*! \brief Counts the number of font families available
887 \return The number of unique font families currently available
889 int32
890 FontManager::CountFamilies()
892 _ScanFontsIfNecessary();
894 return fFamilies.CountItems();
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
902 int32
903 FontManager::CountStyles(const char *familyName)
905 _ScanFontsIfNecessary();
907 FontFamily *family = GetFamily(familyName);
908 if (family)
909 return family->CountStyles();
911 return 0;
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
919 int32
920 FontManager::CountStyles(uint16 familyID)
922 _ScanFontsIfNecessary();
924 FontFamily *family = GetFamily(familyID);
925 if (family)
926 return family->CountStyles();
928 return 0;
932 FontFamily*
933 FontManager::FamilyAt(int32 index) const
935 return fFamilies.ItemAt(index);
939 FontFamily*
940 FontManager::_FindFamily(const char* name) const
942 if (name == NULL)
943 return NULL;
945 FontFamily family(name, 0);
946 return const_cast<FontFamily*>(fFamilies.BinarySearch(family,
947 compare_font_families));
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.
955 FontFamily*
956 FontManager::GetFamily(const char* name)
958 if (name == NULL)
959 return NULL;
961 FontFamily* family = _FindFamily(name);
962 if (family != NULL)
963 return family;
965 if (fScanned)
966 return NULL;
968 // try font mappings before failing
969 if (_AddMappedFont(name) == B_OK)
970 return _FindFamily(name);
972 _ScanFonts();
973 return _FindFamily(name);
977 FontFamily*
978 FontManager::GetFamily(uint16 familyID) const
980 FontKey key(familyID, 0);
981 FontStyle* style = (FontStyle*)fStyleHashTable.GetValue(key);
982 if (style != NULL)
983 return style->Family();
985 return NULL;
989 FontStyle*
990 FontManager::GetStyleByIndex(const char* familyName, int32 index)
992 FontFamily* family = GetFamily(familyName);
993 if (family != NULL)
994 return family->StyleAt(index);
996 return NULL;
1000 FontStyle*
1001 FontManager::GetStyleByIndex(uint16 familyID, int32 index)
1003 FontFamily* family = GetFamily(familyID);
1004 if (family != NULL)
1005 return family->StyleAt(index);
1007 return NULL;
1011 /*! \brief Retrieves the FontStyle object that comes closest to the one
1012 specified.
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.
1021 \return The FontStyle having those attributes or NULL if not available
1023 FontStyle*
1024 FontManager::GetStyle(const char* familyName, const char* styleName,
1025 uint16 familyID, uint16 styleID, uint16 face)
1027 FontFamily* family;
1029 // find family
1031 if (familyName != NULL && familyName[0])
1032 family = GetFamily(familyName);
1033 else
1034 family = GetFamily(familyID);
1036 if (family == NULL)
1037 return NULL;
1039 // find style
1041 if (styleName != NULL && styleName[0]) {
1042 FontStyle* fontStyle = family->GetStyle(styleName);
1043 if (fontStyle != NULL)
1044 return fontStyle;
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;
1053 _ScanFonts();
1054 return family->GetStyle(styleName);
1057 if (styleID != 0xffff)
1058 return family->GetStyleByID(styleID);
1060 // try to get from face
1061 return family->GetStyleMatchingFace(face);
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
1070 FontStyle*
1071 FontManager::GetStyle(uint16 familyID, uint16 styleID) const
1073 FontKey key(familyID, styleID);
1074 return (FontStyle*)fStyleHashTable.GetValue(key);
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.
1081 FontStyle*
1082 FontManager::FindStyleMatchingFace(uint16 face) const
1084 int32 count = fFamilies.CountItems();
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;
1093 return NULL;
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.
1101 void
1102 FontManager::RemoveStyle(FontStyle* style)
1104 FontFamily* family = style->Family();
1105 if (family == NULL)
1106 debugger("family is NULL!");
1108 FontStyle* check = GetStyle(family->ID(), style->ID());
1109 if (check != NULL)
1110 debugger("style removed but still available!");
1112 if (family->RemoveStyle(style)
1113 && family->CountStyles() == 0)
1114 fFamilies.RemoveItem(family);
1118 const ServerFont*
1119 FontManager::DefaultPlainFont() const
1121 return fDefaultPlainFont;
1125 const ServerFont*
1126 FontManager::DefaultBoldFont() const
1128 return fDefaultBoldFont;
1132 const ServerFont*
1133 FontManager::DefaultFixedFont() const
1135 return fDefaultFixedFont;
1139 void
1140 FontManager::AttachUser(uid_t userID)
1142 BAutolock locker(this);
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 _AddPath(path.Path());
1150 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true)
1151 == B_OK) {
1152 _AddPath(path.Path());
1154 #endif
1158 void
1159 FontManager::DetachUser(uid_t userID)
1161 BAutolock locker(this);
1163 // TODO!