bin/pc: Mark non-returning function as void
[haiku.git] / src / servers / app / font / FontManager.cpp
blob088b23f81c40f9a6bc4dd688661240a7a6874a28
1 /*
2 * Copyright 2001-2016, 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 */
11 /*! Manages font families and styles */
14 #include "FontManager.h"
16 #include <new>
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 "FontFamily.h"
29 #include "ServerConfig.h"
30 #include "ServerFont.h"
33 //#define TRACE_FONT_MANAGER
34 #ifdef TRACE_FONT_MANAGER
35 # define FTRACE(x) printf x
36 #else
37 # define FTRACE(x) ;
38 #endif
41 // TODO: needs some more work for multi-user support
43 FT_Library gFreeTypeLibrary;
44 FontManager *gFontManager = NULL;
46 struct FontManager::font_directory {
47 node_ref directory;
48 uid_t user;
49 gid_t group;
50 uint32 revision;
51 BObjectList<FontStyle> styles;
53 bool AlreadyScanned() const { return revision != 0; }
54 FontStyle* FindStyle(const node_ref& nodeRef) const;
57 struct FontManager::font_mapping {
58 BString family;
59 BString style;
60 entry_ref ref;
64 FontStyle*
65 FontManager::font_directory::FindStyle(const node_ref& nodeRef) const
67 for (int32 i = styles.CountItems(); i-- > 0;) {
68 FontStyle* style = styles.ItemAt(i);
70 if (nodeRef == style->NodeRef())
71 return style;
74 return NULL;
78 static status_t
79 set_entry(node_ref& nodeRef, const char* name, BEntry& entry)
81 entry_ref ref;
82 ref.device = nodeRef.device;
83 ref.directory = nodeRef.node;
85 status_t status = ref.set_name(name);
86 if (status != B_OK)
87 return status;
89 return entry.SetTo(&ref);
93 static int
94 compare_font_families(const FontFamily* a, const FontFamily* b)
96 return strcmp(a->Name(), b->Name());
100 // #pragma mark -
103 //! Does basic set up so that directories can be scanned
104 FontManager::FontManager()
105 : BLooper("Font Manager"),
106 fDirectories(10, true),
107 fMappings(10, true),
108 fFamilies(20),
110 fDefaultPlainFont(NULL),
111 fDefaultBoldFont(NULL),
112 fDefaultFixedFont(NULL),
114 fScanned(false),
115 fNextID(0)
117 fInitStatus = FT_Init_FreeType(&gFreeTypeLibrary) == 0 ? B_OK : B_ERROR;
119 if (fInitStatus == B_OK) {
120 _AddSystemPaths();
121 _LoadRecentFontMappings();
123 fInitStatus = _SetDefaultFonts();
125 if (fInitStatus == B_OK) {
126 // Precache the plain and bold fonts
127 _PrecacheFontFile(fDefaultPlainFont);
128 _PrecacheFontFile(fDefaultBoldFont);
134 //! Frees items allocated in the constructor and shuts down FreeType
135 FontManager::~FontManager()
137 delete fDefaultPlainFont;
138 delete fDefaultBoldFont;
139 delete fDefaultFixedFont;
141 // free families before we're done with FreeType
143 for (int32 i = fFamilies.CountItems(); i-- > 0;) {
144 delete fFamilies.ItemAt(i);
147 FT_Done_FreeType(gFreeTypeLibrary);
151 void
152 FontManager::MessageReceived(BMessage* message)
154 switch (message->what) {
155 case B_NODE_MONITOR:
157 // TODO: support removing fonts!
159 int32 opcode;
160 if (message->FindInt32("opcode", &opcode) != B_OK)
161 return;
163 switch (opcode) {
164 case B_ENTRY_CREATED:
166 const char* name;
167 node_ref nodeRef;
168 if (message->FindInt32("device", &nodeRef.device) != B_OK
169 || message->FindInt64("directory", &nodeRef.node) != B_OK
170 || message->FindString("name", &name) != B_OK)
171 break;
173 // TODO: make this better (possible under Haiku)
174 snooze(100000);
175 // let the font be written completely before trying to open it
177 BEntry entry;
178 if (set_entry(nodeRef, name, entry) != B_OK)
179 break;
181 if (entry.IsDirectory()) {
182 // a new directory to watch for us
183 _AddPath(entry);
184 } else {
185 // a new font
186 font_directory* directory = _FindDirectory(nodeRef);
187 if (directory == NULL) {
188 // unknown directory? how come?
189 break;
192 _AddFont(*directory, entry);
194 break;
197 case B_ENTRY_MOVED:
199 // has the entry been moved into a monitored directory or has
200 // it been removed from one?
201 const char* name;
202 node_ref nodeRef;
203 uint64 fromNode;
204 uint64 node;
205 if (message->FindInt32("device", &nodeRef.device) != B_OK
206 || message->FindInt64("to directory", &nodeRef.node) != B_OK
207 || message->FindInt64("from directory", (int64 *)&fromNode) != B_OK
208 || message->FindInt64("node", (int64 *)&node) != B_OK
209 || message->FindString("name", &name) != B_OK)
210 break;
212 font_directory* directory = _FindDirectory(nodeRef);
214 BEntry entry;
215 if (set_entry(nodeRef, name, entry) != B_OK)
216 break;
218 if (directory != NULL) {
219 // something has been added to our watched font directories
221 // test, if the source directory is one of ours as well
222 nodeRef.node = fromNode;
223 font_directory* fromDirectory = _FindDirectory(nodeRef);
225 if (entry.IsDirectory()) {
226 if (fromDirectory == NULL) {
227 // there is a new directory to watch for us
228 _AddPath(entry);
229 FTRACE(("new directory moved in"));
230 } else {
231 // A directory from our watched directories has
232 // been renamed or moved within the watched
233 // directories - we only need to update the
234 // path names of the styles in that directory
235 nodeRef.node = node;
236 directory = _FindDirectory(nodeRef);
237 if (directory != NULL) {
238 for (int32 i = 0; i < directory->styles.CountItems(); i++) {
239 FontStyle* style = directory->styles.ItemAt(i);
240 style->UpdatePath(directory->directory);
243 FTRACE(("directory renamed"));
245 } else {
246 if (fromDirectory != NULL) {
247 // find style in source and move it to the target
248 nodeRef.node = node;
249 FontStyle* style = fromDirectory->FindStyle(nodeRef);
250 if (style != NULL) {
251 fromDirectory->styles.RemoveItem(style, false);
252 directory->styles.AddItem(style);
253 style->UpdatePath(directory->directory);
255 FTRACE(("font moved"));
256 } else {
257 FTRACE(("font added: %s\n", name));
258 _AddFont(*directory, entry);
261 } else {
262 // and entry has been removed from our font directories
263 if (entry.IsDirectory()) {
264 if (entry.GetNodeRef(&nodeRef) == B_OK
265 && (directory = _FindDirectory(nodeRef)) != NULL)
266 _RemoveDirectory(directory);
267 } else {
268 // remove font style from directory
269 _RemoveStyle(nodeRef.device, fromNode, node);
272 break;
275 case B_ENTRY_REMOVED:
277 node_ref nodeRef;
278 uint64 directoryNode;
279 if (message->FindInt32("device", &nodeRef.device) != B_OK
280 || message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
281 || message->FindInt64("node", &nodeRef.node) != B_OK)
282 break;
284 font_directory* directory = _FindDirectory(nodeRef);
285 if (directory != NULL) {
286 // the directory has been removed, so we remove it as well
287 _RemoveDirectory(directory);
288 } else {
289 // remove font style from directory
290 _RemoveStyle(nodeRef.device, directoryNode, nodeRef.node);
292 break;
295 break;
301 void
302 FontManager::SaveRecentFontMappings()
307 void
308 FontManager::_AddDefaultMapping(const char* family, const char* style,
309 const char* path)
311 font_mapping* mapping = new (std::nothrow) font_mapping;
312 if (mapping == NULL)
313 return;
315 mapping->family = family;
316 mapping->style = style;
317 BEntry entry(path);
319 if (entry.GetRef(&mapping->ref) != B_OK
320 || !entry.Exists()
321 || !fMappings.AddItem(mapping))
322 delete mapping;
326 bool
327 FontManager::_LoadRecentFontMappings()
329 // default known mappings
330 // TODO: load them for real, and use these as a fallback
332 BPath ttfontsPath;
333 if (find_directory(B_BEOS_FONTS_DIRECTORY, &ttfontsPath) == B_OK) {
334 ttfontsPath.Append("ttfonts");
336 BPath veraFontPath = ttfontsPath;
337 veraFontPath.Append("NotoSans-Regular.ttf");
338 _AddDefaultMapping("Noto Sans", "Book", veraFontPath.Path());
340 veraFontPath.SetTo(ttfontsPath.Path());
341 veraFontPath.Append("NotoSans-Bold.ttf");
342 _AddDefaultMapping("Noto Sans", "Bold", veraFontPath.Path());
344 veraFontPath.SetTo(ttfontsPath.Path());
345 veraFontPath.Append("NotoMono-Regular.ttf");
346 _AddDefaultMapping("Noto Mono", "Regular", veraFontPath.Path());
348 return true;
351 return false;
355 status_t
356 FontManager::_AddMappedFont(const char* familyName, const char* styleName)
358 FTRACE(("_AddMappedFont(family = \"%s\", style = \"%s\")\n",
359 familyName, styleName));
361 for (int32 i = 0; i < fMappings.CountItems(); i++) {
362 font_mapping* mapping = fMappings.ItemAt(i);
364 if (mapping->family == familyName) {
365 if (styleName != NULL && mapping->style != styleName)
366 continue;
368 BEntry entry(&mapping->ref);
369 if (entry.InitCheck() != B_OK)
370 continue;
372 // find parent directory
374 node_ref nodeRef;
375 nodeRef.device = mapping->ref.device;
376 nodeRef.node = mapping->ref.directory;
377 font_directory* directory = _FindDirectory(nodeRef);
378 if (directory == NULL) {
379 // unknown directory, maybe this is a user font - try
380 // to create the missing directory
381 BPath path(&entry);
382 if (path.GetParent(&path) != B_OK
383 || _CreateDirectories(path.Path()) != B_OK
384 || (directory = _FindDirectory(nodeRef)) == NULL)
385 continue;
388 return _AddFont(*directory, entry);
392 return B_ENTRY_NOT_FOUND;
396 /*! \brief Removes the style from the font directory.
398 It doesn't necessary delete the font style, if it's still
399 in use, though.
401 void
402 FontManager::_RemoveStyle(font_directory& directory, FontStyle* style)
404 FTRACE(("font removed: %s\n", style->Name()));
406 directory.styles.RemoveItem(style);
407 directory.revision++;
409 fStyleHashTable.RemoveItem(*style);
411 style->Release();
415 void
416 FontManager::_RemoveStyle(dev_t device, uint64 directoryNode, uint64 node)
418 // remove font style from directory
419 node_ref nodeRef;
420 nodeRef.device = device;
421 nodeRef.node = directoryNode;
423 font_directory* directory = _FindDirectory(nodeRef);
424 if (directory != NULL) {
425 // find style in directory and remove it
426 nodeRef.node = node;
427 FontStyle* style = directory->FindStyle(nodeRef);
428 if (style != NULL)
429 _RemoveStyle(*directory, style);
434 FontStyle*
435 FontManager::_GetDefaultStyle(const char *familyName, const char *styleName,
436 const char *fallbackFamily, const char *fallbackStyle,
437 uint16 fallbackFace)
439 // try to find a matching font
441 FontStyle* style = GetStyle(familyName, styleName);
442 if (style == NULL) {
443 style = GetStyle(fallbackFamily, fallbackStyle);
444 if (style == NULL) {
445 style = FindStyleMatchingFace(fallbackFace);
446 if (style == NULL && FamilyAt(0) != NULL)
447 style = FamilyAt(0)->StyleAt(0);
451 return style;
455 /*! \brief Sets the fonts that will be used when you create an empty
456 ServerFont without specifying a style, as well as the default
457 Desktop fonts if there are no settings available.
459 status_t
460 FontManager::_SetDefaultFonts()
462 // plain font
463 FontStyle* style = _GetDefaultStyle(DEFAULT_PLAIN_FONT_FAMILY,
464 DEFAULT_PLAIN_FONT_STYLE, FALLBACK_PLAIN_FONT_FAMILY,
465 DEFAULT_PLAIN_FONT_STYLE,
466 B_REGULAR_FACE);
467 if (style == NULL)
468 return B_ERROR;
470 fDefaultPlainFont = new (std::nothrow) ServerFont(*style,
471 DEFAULT_PLAIN_FONT_SIZE);
472 if (fDefaultPlainFont == NULL)
473 return B_NO_MEMORY;
475 // bold font
476 style = _GetDefaultStyle(DEFAULT_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE,
477 FALLBACK_BOLD_FONT_FAMILY, DEFAULT_BOLD_FONT_STYLE, B_BOLD_FACE);
479 fDefaultBoldFont = new (std::nothrow) ServerFont(*style,
480 DEFAULT_BOLD_FONT_SIZE);
481 if (fDefaultBoldFont == NULL)
482 return B_NO_MEMORY;
484 // fixed font
485 style = _GetDefaultStyle(DEFAULT_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE,
486 FALLBACK_FIXED_FONT_FAMILY, DEFAULT_FIXED_FONT_STYLE, B_REGULAR_FACE);
488 fDefaultFixedFont = new (std::nothrow) ServerFont(*style,
489 DEFAULT_FIXED_FONT_SIZE);
490 if (fDefaultFixedFont == NULL)
491 return B_NO_MEMORY;
493 fDefaultFixedFont->SetSpacing(B_FIXED_SPACING);
495 return B_OK;
499 void
500 FontManager::_PrecacheFontFile(const ServerFont* font)
502 if (font == NULL)
503 return;
505 size_t bufferSize = 32768;
506 uint8* buffer = new (std::nothrow) uint8[bufferSize];
507 if (buffer == NULL) {
508 // We don't care. Pre-caching doesn't make sense anyways when there
509 // is not enough RAM...
510 return;
513 BFile file(font->Path(), B_READ_ONLY);
514 if (file.InitCheck() != B_OK) {
515 delete[] buffer;
516 return;
519 while (true) {
520 // We just want the file in the kernel file cache...
521 ssize_t read = file.Read(buffer, bufferSize);
522 if (read < (ssize_t)bufferSize)
523 break;
526 delete[] buffer;
530 void
531 FontManager::_AddSystemPaths()
533 BPath path;
534 if (find_directory(B_SYSTEM_FONTS_DIRECTORY, &path, true) == B_OK)
535 _AddPath(path.Path());
537 // We don't scan these in test mode to help shave off some startup time
538 #if !TEST_MODE
539 if (find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, &path, true) == B_OK)
540 _AddPath(path.Path());
541 #endif
545 void
546 FontManager::_ScanFontsIfNecessary()
548 if (!fScanned)
549 _ScanFonts();
553 //! Scans all currently known font directories
554 void
555 FontManager::_ScanFonts()
557 if (fScanned)
558 return;
560 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
561 font_directory* directory = fDirectories.ItemAt(i);
563 if (directory->AlreadyScanned())
564 continue;
566 _ScanFontDirectory(*directory);
569 fScanned = true;
573 /*! \brief Adds the FontFamily/FontStyle that is represented by this path.
575 status_t
576 FontManager::_AddFont(font_directory& directory, BEntry& entry)
578 node_ref nodeRef;
579 status_t status = entry.GetNodeRef(&nodeRef);
580 if (status < B_OK)
581 return status;
583 BPath path;
584 status = entry.GetPath(&path);
585 if (status < B_OK)
586 return status;
588 FT_Face face;
589 FT_Error error = FT_New_Face(gFreeTypeLibrary, path.Path(), 0, &face);
590 if (error != 0)
591 return B_ERROR;
593 FontFamily *family = _FindFamily(face->family_name);
594 if (family != NULL && family->HasStyle(face->style_name)) {
595 // prevent adding the same style twice
596 // (this indicates a problem with the installed fonts maybe?)
597 FT_Done_Face(face);
598 return B_OK;
601 if (family == NULL) {
602 family = new (std::nothrow) FontFamily(face->family_name, fNextID++);
603 if (family == NULL
604 || !fFamilies.BinaryInsert(family, compare_font_families)) {
605 delete family;
606 FT_Done_Face(face);
607 return B_NO_MEMORY;
611 FTRACE(("\tadd style: %s, %s\n", face->family_name, face->style_name));
613 // the FontStyle takes over ownership of the FT_Face object
614 FontStyle *style = new (std::nothrow) FontStyle(nodeRef, path.Path(), face);
615 if (style == NULL || !family->AddStyle(style)) {
616 delete style;
617 delete family;
618 return B_NO_MEMORY;
621 directory.styles.AddItem(style);
622 fStyleHashTable.AddItem(style);
624 if (directory.AlreadyScanned())
625 directory.revision++;
627 return B_OK;
631 FontManager::font_directory*
632 FontManager::_FindDirectory(node_ref& nodeRef)
634 for (int32 i = fDirectories.CountItems(); i-- > 0;) {
635 font_directory* directory = fDirectories.ItemAt(i);
637 if (directory->directory == nodeRef)
638 return directory;
641 return NULL;
645 void
646 FontManager::_RemoveDirectory(font_directory* directory)
648 FTRACE(("FontManager: Remove directory (%" B_PRIdINO ")!\n",
649 directory->directory.node));
651 fDirectories.RemoveItem(directory, false);
653 // TODO: remove styles from this directory!
655 watch_node(&directory->directory, B_STOP_WATCHING, this);
656 delete directory;
660 status_t
661 FontManager::_AddPath(const char* path)
663 BEntry entry;
664 status_t status = entry.SetTo(path);
665 if (status != B_OK)
666 return status;
668 return _AddPath(entry);
672 status_t
673 FontManager::_AddPath(BEntry& entry, font_directory** _newDirectory)
675 node_ref nodeRef;
676 status_t status = entry.GetNodeRef(&nodeRef);
677 if (status != B_OK)
678 return status;
680 // check if we are already know this directory
682 font_directory* directory = _FindDirectory(nodeRef);
683 if (directory != NULL) {
684 if (_newDirectory)
685 *_newDirectory = directory;
686 return B_OK;
689 // it's a new one, so let's add it
691 directory = new (std::nothrow) font_directory;
692 if (directory == NULL)
693 return B_NO_MEMORY;
695 struct stat stat;
696 status = entry.GetStat(&stat);
697 if (status != B_OK) {
698 delete directory;
699 return status;
702 directory->directory = nodeRef;
703 directory->user = stat.st_uid;
704 directory->group = stat.st_gid;
705 directory->revision = 0;
707 status = watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
708 if (status != B_OK) {
709 // we cannot watch this directory - while this is unfortunate,
710 // it's not a critical error
711 printf("could not watch directory %" B_PRIdDEV ":%" B_PRIdINO "\n",
712 nodeRef.device, nodeRef.node);
713 // TODO: should go into syslog()
714 } else {
715 BPath path(&entry);
716 FTRACE(("FontManager: now watching: %s\n", path.Path()));
719 fDirectories.AddItem(directory);
721 if (_newDirectory)
722 *_newDirectory = directory;
724 fScanned = false;
725 return B_OK;
729 /*! \brief Creates all unknown font_directories of the specified path - but
730 only if one of its parent directories is already known.
732 This method is used to create the font_directories for font_mappings.
733 It recursively walks upwards in the directory hierarchy until it finds
734 a known font_directory (or hits the root directory, in which case it
735 bails out).
737 status_t
738 FontManager::_CreateDirectories(const char* path)
740 FTRACE(("_CreateDirectories(path = %s)\n", path));
742 if (!strcmp(path, "/")) {
743 // we walked our way up to the root
744 return B_ENTRY_NOT_FOUND;
747 BEntry entry;
748 status_t status = entry.SetTo(path);
749 if (status != B_OK)
750 return status;
752 node_ref nodeRef;
753 status = entry.GetNodeRef(&nodeRef);
754 if (status != B_OK)
755 return status;
757 // check if we are already know this directory
759 font_directory* directory = _FindDirectory(nodeRef);
760 if (directory != NULL)
761 return B_OK;
763 // We don't know this one yet - keep walking the path upwards
764 // and try to find a match.
766 BPath parent(path);
767 status = parent.GetParent(&parent);
768 if (status != B_OK)
769 return status;
771 status = _CreateDirectories(parent.Path());
772 if (status != B_OK)
773 return status;
775 // We have our match, create sub directory
777 return _AddPath(path);
781 /*! \brief Scan a folder for all valid fonts
782 \param directoryPath Path of the folder to scan.
784 status_t
785 FontManager::_ScanFontDirectory(font_directory& fontDirectory)
787 // This bad boy does all the real work. It loads each entry in the
788 // directory. If a valid font file, it adds both the family and the style.
790 BDirectory directory;
791 status_t status = directory.SetTo(&fontDirectory.directory);
792 if (status != B_OK)
793 return status;
795 BEntry entry;
796 while (directory.GetNextEntry(&entry) == B_OK) {
797 if (entry.IsDirectory()) {
798 // scan this directory recursively
799 font_directory* newDirectory;
800 if (_AddPath(entry, &newDirectory) == B_OK && newDirectory != NULL)
801 _ScanFontDirectory(*newDirectory);
803 continue;
806 // TODO: Commenting this out makes my "Unicode glyph lookup"
807 // work with our default fonts. The real fix is to select the
808 // Unicode char map (if supported), and/or adjust the
809 // utf8 -> glyph-index mapping everywhere to handle other
810 // char maps. We could also ignore fonts that don't support
811 // the Unicode lookup as a temporary "solution".
812 #if 0
813 FT_CharMap charmap = _GetSupportedCharmap(face);
814 if (!charmap) {
815 FT_Done_Face(face);
816 continue;
819 face->charmap = charmap;
820 #endif
822 _AddFont(fontDirectory, entry);
823 // takes over ownership of the FT_Face object
826 fontDirectory.revision = 1;
827 return B_OK;
831 /*! \brief Finds and returns the first valid charmap in a font
833 \param face Font handle obtained from FT_Load_Face()
834 \return An FT_CharMap or NULL if unsuccessful
836 FT_CharMap
837 FontManager::_GetSupportedCharmap(const FT_Face& face)
839 for (int32 i = 0; i < face->num_charmaps; i++) {
840 FT_CharMap charmap = face->charmaps[i];
842 switch (charmap->platform_id) {
843 case 3:
844 // if Windows Symbol or Windows Unicode
845 if (charmap->encoding_id == 0 || charmap->encoding_id == 1)
846 return charmap;
847 break;
849 case 1:
850 // if Apple Unicode
851 if (charmap->encoding_id == 0)
852 return charmap;
853 break;
855 case 0:
856 // if Apple Roman
857 if (charmap->encoding_id == 0)
858 return charmap;
859 break;
861 default:
862 break;
866 return NULL;
870 int32
871 FontManager::CheckRevision(uid_t user)
873 BAutolock locker(this);
874 int32 revision = 0;
876 _ScanFontsIfNecessary();
878 for (int32 i = 0; i < fDirectories.CountItems(); i++) {
879 font_directory* directory = fDirectories.ItemAt(i);
881 // TODO: for now, add all directories
882 revision += directory->revision;
885 return revision;
889 /*! \brief Counts the number of font families available
890 \return The number of unique font families currently available
892 int32
893 FontManager::CountFamilies()
895 _ScanFontsIfNecessary();
897 return fFamilies.CountItems();
901 /*! \brief Counts the number of styles available in a font family
902 \param family Name of the font family to scan
903 \return The number of font styles currently available for the font family
905 int32
906 FontManager::CountStyles(const char *familyName)
908 _ScanFontsIfNecessary();
910 FontFamily *family = GetFamily(familyName);
911 if (family)
912 return family->CountStyles();
914 return 0;
918 /*! \brief Counts the number of styles available in a font family
919 \param family Name of the font family to scan
920 \return The number of font styles currently available for the font family
922 int32
923 FontManager::CountStyles(uint16 familyID)
925 _ScanFontsIfNecessary();
927 FontFamily *family = GetFamily(familyID);
928 if (family)
929 return family->CountStyles();
931 return 0;
935 FontFamily*
936 FontManager::FamilyAt(int32 index) const
938 return fFamilies.ItemAt(index);
942 FontFamily*
943 FontManager::_FindFamily(const char* name) const
945 if (name == NULL)
946 return NULL;
948 FontFamily family(name, 0);
949 return const_cast<FontFamily*>(fFamilies.BinarySearch(family,
950 compare_font_families));
954 /*! \brief Locates a FontFamily object by name
955 \param name The family to find
956 \return Pointer to the specified family or NULL if not found.
958 FontFamily*
959 FontManager::GetFamily(const char* name)
961 if (name == NULL)
962 return NULL;
964 FontFamily* family = _FindFamily(name);
965 if (family != NULL)
966 return family;
968 if (fScanned)
969 return NULL;
971 // try font mappings before failing
972 if (_AddMappedFont(name) == B_OK)
973 return _FindFamily(name);
975 _ScanFonts();
976 return _FindFamily(name);
980 FontFamily*
981 FontManager::GetFamily(uint16 familyID) const
983 FontKey key(familyID, 0);
984 FontStyle* style = (FontStyle*)fStyleHashTable.GetValue(key);
985 if (style != NULL)
986 return style->Family();
988 return NULL;
992 FontStyle*
993 FontManager::GetStyleByIndex(const char* familyName, int32 index)
995 FontFamily* family = GetFamily(familyName);
996 if (family != NULL)
997 return family->StyleAt(index);
999 return NULL;
1003 FontStyle*
1004 FontManager::GetStyleByIndex(uint16 familyID, int32 index)
1006 FontFamily* family = GetFamily(familyID);
1007 if (family != NULL)
1008 return family->StyleAt(index);
1010 return NULL;
1014 /*! \brief Retrieves the FontStyle object that comes closest to the one
1015 specified.
1017 \param family The font's family or NULL in which case \a familyID is used
1018 \param style The font's style or NULL in which case \a styleID is used
1019 \param familyID will only be used if \a family is NULL (or empty)
1020 \param styleID will only be used if \a style is NULL (or empty)
1021 \param face is used to specify the style if both \a style is NULL or empty
1022 and styleID is 0xffff.
1024 \return The FontStyle having those attributes or NULL if not available
1026 FontStyle*
1027 FontManager::GetStyle(const char* familyName, const char* styleName,
1028 uint16 familyID, uint16 styleID, uint16 face)
1030 FontFamily* family;
1032 // find family
1034 if (familyName != NULL && familyName[0])
1035 family = GetFamily(familyName);
1036 else
1037 family = GetFamily(familyID);
1039 if (family == NULL)
1040 return NULL;
1042 // find style
1044 if (styleName != NULL && styleName[0]) {
1045 FontStyle* fontStyle = family->GetStyle(styleName);
1046 if (fontStyle != NULL)
1047 return fontStyle;
1049 // before we fail, we try the mappings for a match
1050 if (_AddMappedFont(family->Name(), styleName) == B_OK) {
1051 fontStyle = family->GetStyle(styleName);
1052 if (fontStyle != NULL)
1053 return fontStyle;
1056 _ScanFonts();
1057 return family->GetStyle(styleName);
1060 if (styleID != 0xffff)
1061 return family->GetStyleByID(styleID);
1063 // try to get from face
1064 return family->GetStyleMatchingFace(face);
1068 /*! \brief Retrieves the FontStyle object
1069 \param family ID for the font's family
1070 \param style ID of the font's style
1071 \return The FontStyle having those attributes or NULL if not available
1073 FontStyle*
1074 FontManager::GetStyle(uint16 familyID, uint16 styleID) const
1076 FontKey key(familyID, styleID);
1077 return (FontStyle*)fStyleHashTable.GetValue(key);
1081 /*! \brief If you don't find your preferred font style, but are anxious
1082 to have one fitting your needs, you may want to use this method.
1084 FontStyle*
1085 FontManager::FindStyleMatchingFace(uint16 face) const
1087 int32 count = fFamilies.CountItems();
1089 for (int32 i = 0; i < count; i++) {
1090 FontFamily* family = fFamilies.ItemAt(i);
1091 FontStyle* style = family->GetStyleMatchingFace(face);
1092 if (style != NULL)
1093 return style;
1096 return NULL;
1100 /*! \brief This call is used by the FontStyle class - and the FontStyle class
1101 only - to remove itself from the font manager.
1102 At this point, the style is already no longer available to the user.
1104 void
1105 FontManager::RemoveStyle(FontStyle* style)
1107 FontFamily* family = style->Family();
1108 if (family == NULL)
1109 debugger("family is NULL!");
1111 FontStyle* check = GetStyle(family->ID(), style->ID());
1112 if (check != NULL)
1113 debugger("style removed but still available!");
1115 if (family->RemoveStyle(style)
1116 && family->CountStyles() == 0)
1117 fFamilies.RemoveItem(family);
1121 const ServerFont*
1122 FontManager::DefaultPlainFont() const
1124 return fDefaultPlainFont;
1128 const ServerFont*
1129 FontManager::DefaultBoldFont() const
1131 return fDefaultBoldFont;
1135 const ServerFont*
1136 FontManager::DefaultFixedFont() const
1138 return fDefaultFixedFont;
1142 void
1143 FontManager::AttachUser(uid_t userID)
1145 BAutolock locker(this);
1147 #if !TEST_MODE
1148 // TODO: actually, find_directory() cannot know which user ID we want here
1149 // TODO: avoids user fonts in safe mode
1150 BPath path;
1151 if (find_directory(B_USER_FONTS_DIRECTORY, &path, true) == B_OK)
1152 _AddPath(path.Path());
1153 if (find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, &path, true)
1154 == B_OK) {
1155 _AddPath(path.Path());
1157 #endif
1161 void
1162 FontManager::DetachUser(uid_t userID)
1164 BAutolock locker(this);
1166 // TODO!