NSFont
The NSFont class allows control of the fonts used for displaying
text anywhere on the screen. The primary methods for getting a
particular font are +fontWithName:matrix: and +fontWithName:size: which
take the name and size of a particular font and return the NSFont object
associated with that font. In addition there are several convenience
mathods which make it easier to get certain types of fonts.
In particular, there are several methods to get the standard fonts
used by the Application to display text for a partiuclar purpose. See
the class methods listed below for more information. These default
fonts can be set using the user defaults system. The default
font names available are:
- NSBoldFont Helvetica-Bold (System bold font)
- NSControlContentFont System font
- NSFont Helvetica (System Font)
- NSLabelFont System font
- NSMenuFont System font
- NSMessageFont System font
- NSPaletteFont System bold font
- NSTitleBarFont System bold font
- NSToolTipsFont System font
- NSUserFixedPitchFont Courier
- NSUserFont System font
The default sizes are:
- NSBoldFontSize (none)
- NSControlContentFontSize (none)
- NSFontSize 12 (System Font Size)
- NSLabelFontSize (none)
- NSMenuFontSize (none)
- NSMessageFontSize (none)
- NSPaletteFontSize (none)
- NSSmallFontSize 9
- NSTitleBarFontSize (none)
- NSToolTipsFontSize (none)
- NSUserFixedPitchFontSize (none)
- NSUserFontSize (none)
Font sizes list with (none) default to NSFontSize.
*/
@implementation NSFont
/* Class variables*/
/* See comments in +initialize. */
static NSFont *placeHolder = nil;
/* Fonts that are preferred by the application */
static NSArray *_preferredFonts;
/* Class for fonts */
static Class NSFontClass = 0;
/* Cache all created fonts for reuse. */
static NSMapTable* globalFontMap = 0;
static NSUserDefaults *defaults = nil;
/*
The valid font roles. Note that these values are used when encoding and
decoding, so entries may never be removed. Entries may be added after the
last entry, and entries don't have to actually be handled.
Note that these values are multiplied by two before they are used since the
lowest bit is used to indicate an explicit size. If the lowest bit is set,
the size is explicitly specified and encoded.
*/
enum FontRoles
{
RoleExplicit=0,
RoleBoldSystemFont,
RoleSystemFont,
RoleUserFixedPitchFont,
RoleUserFont,
RoleTitleBarFont,
RoleMenuFont,
RoleMessageFont,
RolePaletteFont,
RoleToolTipsFont,
RoleControlContentFont,
RoleLabelFont,
RoleMax
};
typedef struct
{
/* Defaults key for this font. */
NSString *key;
/* If there's no defaults key, fall back to the font for this role. */
int fallback;
/* If there's no other role to fall back to, use this font. */
NSString *defaultFont;
/* Cached font for the default size of this role. */
NSFont *cachedFont;
} font_role_info_t;
/*
This table, through getNSFont, controls the behavior of getting the standard
fonts, and must match the table in the documentation above. Each entry should
have a fallback or a defaultFont. There must be a default font for the system
font. Bad Things will happen if entries are invalid.
*/
static font_role_info_t font_roles[RoleMax]={
{nil , 0 , nil, nil},
{@"NSBoldFont" , 0 , nil /* set by init_font_roles */, nil},
{@"NSFont" , 0 , nil /* set by init_font_roles */, nil},
{@"NSUserFixedPitchFont", 0 , nil /* set by init_font_roles */, nil},
{@"NSUserFont" , RoleSystemFont , nil, nil},
{@"NSTitleBarFont" , RoleBoldSystemFont, nil, nil},
{@"NSMenuFont" , RoleSystemFont , nil, nil},
{@"NSMessageFont" , RoleSystemFont , nil, nil},
{@"NSPaletteFont" , RoleBoldSystemFont, nil, nil},
{@"NSToolTipsFont" , RoleSystemFont , nil, nil},
{@"NSControlContentFont", RoleSystemFont , nil, nil},
{@"NSLabelFont" , RoleSystemFont , nil, nil}
};
static BOOL did_init_font_roles;
/*
Called by getNSFont, since font_roles is only accessed from that function
(or fontNameForRole, which is only called by getNSFont). This assures that the
function is called before the table is used, and that it's called _after_ the
backend has been loaded (or, if it isn't, the _fontWithName:... calls will
fail anyway).
*/
static void init_font_roles(void)
{
GSFontEnumerator *e = [GSFontEnumerator sharedEnumerator];
font_roles[RoleSystemFont].defaultFont = [e defaultSystemFontName];
font_roles[RoleBoldSystemFont].defaultFont = [e defaultBoldSystemFontName];
font_roles[RoleUserFixedPitchFont].defaultFont = [e defaultFixedPitchFontName];
}
static NSString *fontNameForRole(int role, int *actual_entry)
{
int i;
NSString *fontName;
i = role;
while (1)
{
fontName = [defaults stringForKey: font_roles[i].key];
if (fontName)
{
break;
}
else if (font_roles[i].fallback)
{
i = font_roles[i].fallback;
}
else if (font_roles[i].defaultFont)
{
fontName = font_roles[i].defaultFont;
break;
}
else
{
NSCAssert(NO, @"Invalid font role table entry.");
}
}
if (actual_entry)
*actual_entry = i;
return fontName;
}
static NSFont *getNSFont(float fontSize, int role)
{
NSString *fontName;
NSFont *font;
BOOL defaultSize;
int i;
int font_role;
NSCAssert(role > RoleExplicit && role < RoleMax, @"Invalid font role.");
if (!did_init_font_roles)
{
init_font_roles();
did_init_font_roles = YES;
}
font_role = role * 2;
defaultSize = (fontSize <= 0.0);
if (defaultSize)
{
if (font_roles[role].cachedFont)
return AUTORELEASE(RETAIN(font_roles[role].cachedFont));
fontSize = [defaults floatForKey:
[NSString stringWithFormat: @"%@Size", font_roles[role].key]];
if (!fontSize)
fontSize = [NSFont systemFontSize];
}
else
{
font_role |= 1;
}
fontName = fontNameForRole(role, &i);
font = [NSFontClass _fontWithName: fontName
size: fontSize
role: font_role];
/* That font couldn't be found. */
if (font == nil)
{
/* Warn using the role that specified the invalid font. */
NSLog(@"The font specified for %@, %@, can't be found.",
font_roles[i].key, fontName);
/* Try the system font. */
fontName = fontNameForRole(RoleSystemFont, NULL);
font = [NSFontClass _fontWithName: fontName
size: fontSize
role: font_role];
if (font == nil)
{
/* Try the default system font and size. */
fontName = font_roles[RoleSystemFont].defaultFont;
font = [NSFontClass _fontWithName: fontName
size: 12.0
role: font_role];
/* It seems we can't get any font here! Try some well known
* fonts as a last resort. */
if (font == nil)
{
font = [NSFontClass _fontWithName: @"Helvetica"
size: 12.0
role: font_role];
}
if (font == nil)
{
font = [NSFontClass _fontWithName: @"Courier"
size: 12.0
role: font_role];
}
if (font == nil)
{
font = [NSFontClass _fontWithName: @"Fixed"
size: 12.0
role: font_role];
}
}
}
if (defaultSize)
ASSIGN(font_roles[role].cachedFont, font);
return font;
}
static void setNSFont(NSString *key, NSFont *font)
{
int i;
[defaults setObject: [font fontName] forKey: key];
for (i = 1; i < RoleMax; i++)
{
DESTROY(font_roles[i].cachedFont);
}
/* Don't care about errors */
[defaults synchronize];
}
//
// Class methods
//
+ (void) initialize
{
if (self == [NSFont class])
{
NSFontClass = self;
/*
* The placeHolder is a dummy NSFont instance which is never used
* as a font ... the initialiser knows that whenever it gets the
* placeHolder it should either return a cached font or return a
* newly allocated font to replace it. This mechanism stops the
* +fontWithName:... methods from having to allocate fonts instances
* which would immediately have to be released for replacement by
* a cache object.
*/
placeHolder = [self alloc];
globalFontMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSNonRetainedObjectMapValueCallBacks, 64);
if (defaults == nil)
{
defaults = RETAIN([NSUserDefaults standardUserDefaults]);
}
[self setVersion: currentVersion];
}
}
/* Getting the preferred user fonts. */
/**
* Return the default bold font for use in menus and heading in standard
* gui components. If fontSize is <= 0, the default
* size is used.