/**
Sets the class used to create the NSFontManager to aClass. By default it is a NSFontManager class. You can change this behavour by implementing your own class ( a subclass of NSFontManager )
This class is init into +sharedFontManager
See Also: +sharedFontManager
*/ + (void) setFontManagerFactory: (Class)aClass { fontManagerClass = aClass; } /**Sets the class used to create a NSFontPanel. If you want to use a custom class it should be NSFontPanel subclass
See Also: -fontPanel:
*/ + (void) setFontPanelFactory: (Class)aClass { fontPanelClass = aClass; } /**Creates ( if needed ) and returns the NSFontManager shared instance. This method init the font manager class defined in +setFontManagerFactory: ( it is usally a NSFontManager class )
*/ + (NSFontManager*) sharedFontManager { if (!sharedFontManager) { sharedFontManager = [[fontManagerClass alloc] init]; } return sharedFontManager; } /* * Instance methods */ - (id) init { if (sharedFontManager && self != sharedFontManager) { RELEASE(self); return sharedFontManager; } self = [super init]; _action = @selector(changeFont:); _storedTag = NSNoFontChangeAction; _fontEnumerator = RETAIN([GSFontEnumerator sharedEnumerator]); _collections = [[NSMutableDictionary alloc] initWithCapacity: 3]; return self; } - (void) dealloc { TEST_RELEASE(_selectedFont); TEST_RELEASE(_selectedAttributes); TEST_RELEASE(_fontMenu); TEST_RELEASE(_fontEnumerator); RELEASE(_collections); [super dealloc]; } /**Returns an array of available fonts.
*/ - (NSArray*) availableFonts { return [_fontEnumerator availableFonts]; } - (NSArray*) availableFontFamilies { return [_fontEnumerator availableFontFamilies]; } - (NSArray*) availableFontNamesWithTraits: (NSFontTraitMask)fontTraitMask { unsigned int i, j; NSArray *fontFamilies = [self availableFontFamilies]; NSMutableArray *fontNames = [NSMutableArray array]; NSFontTraitMask traits; if (fontTraitMask == (NSUnitalicFontMask | NSUnboldFontMask)) { fontTraitMask = 0; } for (i = 0; i < [fontFamilies count]; i++) { NSArray *fontDefs = [self availableMembersOfFontFamily: [fontFamilies objectAtIndex: i]]; for (j = 0; j < [fontDefs count]; j++) { NSArray *fontDef = [fontDefs objectAtIndex: j]; traits = [[fontDef objectAtIndex: 3] unsignedIntValue]; // Check if the font has exactly the given mask if (traits == fontTraitMask) [fontNames addObject: [fontDef objectAtIndex: 0]]; } } return fontNames; } - (NSArray*) availableMembersOfFontFamily: (NSString*)family { return [_fontEnumerator availableMembersOfFontFamily: family]; } - (NSArray *) availableFontNamesMatchingFontDescriptor: (NSFontDescriptor *)descriptor { return [_fontEnumerator availableFontNamesMatchingFontDescriptor: descriptor]; } // GNUstep extension - (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes { return [_fontEnumerator matchingFontDescriptorsFor: attributes]; } - (NSString*) localizedNameForFamily: (NSString*)family face: (NSString*)face { // TODO return [NSString stringWithFormat: @"%@-%@", family, face]; } /** */ - (void) setSelectedFont: (NSFont*)fontObject isMultiple: (BOOL)flag { if (_selectedFont == fontObject) { if (flag != _multiple) { _multiple = flag; // The panel should also know if multiple changed if (fontPanel != nil) { [fontPanel setPanelFont: fontObject isMultiple: flag]; } } return; } _multiple = flag; ASSIGN(_selectedFont, fontObject); DESTROY(_selectedAttributes); if (fontPanel != nil) { [fontPanel setPanelFont: fontObject isMultiple: flag]; } if (_fontMenu != nil) { idReturns the selected font
See Also: -setSelectedFont:isMultiple:
*/ - (NSFont*) selectedFont { return _selectedFont; } /**Returns whether the current selection contains multiple fonts
*/ - (BOOL) isMultiple { return _multiple; } /* * Action methods */ - (void) addFontTrait: (id)sender { _storedTag = NSAddTraitFontAction; _trait = [sender tag]; [self sendAction]; // We update our own selected font if (_selectedFont != nil) { NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) { [self setSelectedFont: newFont isMultiple: _multiple]; } } } - (void) removeFontTrait: (id)sender { _storedTag = NSRemoveTraitFontAction; _trait = [sender tag]; [self sendAction]; // We update our own selected font if (_selectedFont != nil) { NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) { [self setSelectedFont: newFont isMultiple: _multiple]; } } } - (void) modifyFont: (id)sender { _storedTag = [sender tag]; [self sendAction]; // We update our own selected font if (_selectedFont != nil) { NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) { [self setSelectedFont: newFont isMultiple: _multiple]; } } } - (void) modifyFontViaPanel: (id)sender { _storedTag = NSViaPanelFontAction; [self sendAction]; // We update our own selected font if (_selectedFont != nil) { NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) { [self setSelectedFont: newFont isMultiple: _multiple]; } } } /**Converts the NSFont fontObject according to user changes in the Font panel or the font menu
See Also: -addFontTrait: -removeFontTrait: -modifyFont: -modifyFontViaPanel: -convertFont:toHaveTrait: -convertFont:toNotHaveTrait: -convertFont:toSize: -convertFont:toFamily: -convertWeight:ofFont:
*/ - (NSFont*) convertFont: (NSFont*)fontObject { NSFont *newFont = fontObject; int i; float size; float sizes[] = {4.0, 6.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 16.0, 18.0, 24.0, 36.0, 48.0, 64.0}; if (fontObject == nil) return nil; switch (_storedTag) { case NSNoFontChangeAction: break; case NSViaPanelFontAction: if (fontPanel != nil) { newFont = [fontPanel panelConvertFont: fontObject]; } break; case NSAddTraitFontAction: newFont = [self convertFont: fontObject toHaveTrait: _trait]; break; case NSRemoveTraitFontAction: newFont = [self convertFont: fontObject toNotHaveTrait: _trait]; break; case NSSizeUpFontAction: size = [fontObject pointSize]; for (i = 0; i < sizeof(sizes)/sizeof(float); i++) { if (sizes[i] > size) { size = sizes[i]; break; } } newFont = [self convertFont: fontObject toSize: size]; break; case NSSizeDownFontAction: size = [fontObject pointSize]; for (i = sizeof(sizes)/sizeof(float) -1; i >= 0; i--) { if (sizes[i] < size) { size = sizes[i]; break; } } newFont = [self convertFont: fontObject toSize: size]; break; case NSHeavierFontAction: newFont = [self convertWeight: YES ofFont: fontObject]; break; case NSLighterFontAction: newFont = [self convertWeight: NO ofFont: fontObject]; break; } return newFont; } - (NSFont*) convertFont: (NSFont*)fontObject toFamily: (NSString*)family { if ([family isEqualToString: [fontObject familyName]]) { // If already of that family then just return it return fontObject; } else { // Else convert it NSFont *newFont; NSFontTraitMask trait = [self traitsOfFont: fontObject]; int weight = [self weightOfFont: fontObject]; float size = [fontObject pointSize]; newFont = [self fontWithFamily: family traits: trait weight: weight size: size]; if (newFont == nil) return fontObject; else return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject toFace: (NSString*)typeface { NSFont *newFont; // This conversion just retains the point size if ([[fontObject fontName] isEqualToString: typeface]) { return fontObject; } newFont = [NSFont fontWithName: typeface size: [fontObject pointSize]]; if (newFont == nil) return fontObject; else return newFont; } - (NSFont*) convertFont: (NSFont*)fontObject toHaveTrait: (NSFontTraitMask)trait { NSFontTraitMask t = [self traitsOfFont: fontObject]; if (t & trait) { // If already have that trait then just return it return fontObject; } else { // Else convert it NSFont *newFont; int weight = [self weightOfFont: fontObject]; float size = [fontObject pointSize]; NSString *family = [fontObject familyName]; if (trait & NSBoldFontMask) { // We cannot reuse the weight in a bold weight = 9; t = t & ~NSUnboldFontMask; } else if (trait & NSUnboldFontMask) { // We cannot reuse the weight in an unbold weight = 5; t = t & ~NSBoldFontMask; } if (trait == NSItalicFontMask) { t = t & ~NSUnitalicFontMask; } else if (trait & NSUnitalicFontMask) { t = t & ~NSItalicFontMask; } t = t | trait; newFont = [self fontWithFamily: family traits: t weight: weight size: size]; if (newFont == nil) return fontObject; else return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject toNotHaveTrait: (NSFontTraitMask)trait { NSFontTraitMask t = [self traitsOfFont: fontObject]; if (!(t & trait)) { // If already do not have that trait then just return it return fontObject; } else { // Else convert it NSFont *newFont; int weight = [self weightOfFont: fontObject]; float size = [fontObject pointSize]; NSString *family = [fontObject familyName]; if (trait & NSBoldFontMask) { // We cannot reuse the weight in an unbold weight = 5; t = (t | NSUnboldFontMask); } else if (trait & NSUnboldFontMask) { // We cannot reuse the weight in a bold weight = 9; t = (t | NSBoldFontMask); } if (trait & NSItalicFontMask) { t = (t | NSUnitalicFontMask); } else if (trait & NSUnitalicFontMask) { t = (t | NSItalicFontMask); } t &= ~trait; newFont = [self fontWithFamily: family traits: t weight: weight size: size]; if (newFont == nil) return fontObject; else return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject toSize: (float)size { if ([fontObject pointSize] == size) { // If already that size then just return it return fontObject; } else { // Else convert it NSFont *newFont; newFont = [NSFont fontWithName: [fontObject fontName] size: size]; if (newFont == nil) return fontObject; else return newFont; } } - (NSFont*) convertWeight: (BOOL)upFlag ofFont: (NSFont*)fontObject { NSFont *newFont = nil; NSString *fontName = nil; NSFontTraitMask trait = [self traitsOfFont: fontObject]; float size = [fontObject pointSize]; NSString *family = [fontObject familyName]; int w = [self weightOfFont: fontObject]; // We check what weights we have for this family. We must // also check to see if that font has the correct traits! NSArray *fontDefs = [self availableMembersOfFontFamily: family]; if (upFlag) { unsigned int i; // The documentation is a bit unclear about the range of weights // sometimes it says 0 to 9 and sometimes 0 to 15 int next_w = 15; for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; int w1 = [[fontDef objectAtIndex: 2] intValue]; if (w1 > w && w1 < next_w && [[fontDef objectAtIndex: 3] unsignedIntValue] == trait) { next_w = w1; fontName = [fontDef objectAtIndex: 0]; } } if (fontName == nil) { // Not found, try again with changed trait trait |= NSBoldFontMask; for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; int w1 = [[fontDef objectAtIndex: 2] intValue]; if (w1 > w && w1 < next_w && [[fontDef objectAtIndex: 3] unsignedIntValue] == trait) { next_w = w1; fontName = [fontDef objectAtIndex: 0]; } } } } else { unsigned int i; int next_w = 0; for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; int w1 = [[fontDef objectAtIndex: 2] intValue]; if (w1 < w && w1 > next_w && [[fontDef objectAtIndex: 3] unsignedIntValue] == trait) { next_w = w1; fontName = [fontDef objectAtIndex: 0]; } } if (fontName == nil) { // Not found, try again with changed trait trait &= ~NSBoldFontMask; for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; int w1 = [[fontDef objectAtIndex: 2] intValue]; if (w1 < w && w1 > next_w && [[fontDef objectAtIndex: 3] unsignedIntValue] == trait) { next_w = w1; fontName = [fontDef objectAtIndex: 0]; } } } } if (fontName != nil) { newFont = [NSFont fontWithName: fontName size: size]; } if (newFont == nil) return fontObject; else return newFont; } /* * Getting a font */ - (NSFont*) fontWithFamily: (NSString*)family traits: (NSFontTraitMask)traits weight: (int)weight size: (float)size { NSArray *fontDefs = [self availableMembersOfFontFamily: family]; unsigned int i; //NSLog(@"Searching font %@: %i: %i size %.0f", family, weight, traits, size); // First do an exact match search for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; //NSLog(@"Testing font %@: %i: %i", [fontDef objectAtIndex: 0], // [[fontDef objectAtIndex: 2] intValue], // [[fontDef objectAtIndex: 3] unsignedIntValue]); if (([[fontDef objectAtIndex: 2] intValue] == weight) && ([[fontDef objectAtIndex: 3] unsignedIntValue] == traits)) { //NSLog(@"Found font"); return [NSFont fontWithName: [fontDef objectAtIndex: 0] size: size]; } } // Try to find something close by ignoring some trait flags traits &= ~(NSNonStandardCharacterSetFontMask | NSFixedPitchFontMask | NSUnitalicFontMask | NSUnboldFontMask); for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; NSFontTraitMask t = [[fontDef objectAtIndex: 3] unsignedIntValue]; t &= ~(NSNonStandardCharacterSetFontMask | NSFixedPitchFontMask | NSUnitalicFontMask | NSUnboldFontMask); if (([[fontDef objectAtIndex: 2] intValue] == weight) && (t == traits)) { //NSLog(@"Found font"); return [NSFont fontWithName: [fontDef objectAtIndex: 0] size: size]; } } if (traits & NSBoldFontMask) { //NSLog(@"Trying ignore weights for bold font"); for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; NSFontTraitMask t = [[fontDef objectAtIndex: 3] unsignedIntValue]; t &= ~(NSNonStandardCharacterSetFontMask | NSFixedPitchFontMask | NSUnitalicFontMask | NSUnboldFontMask); if (t == traits) { //NSLog(@"Found font"); return [NSFont fontWithName: [fontDef objectAtIndex: 0] size: size]; } } } if (weight == 5 || weight == 6) { //NSLog(@"Trying alternate non-bold weights for non-bold font"); for (i = 0; i < [fontDefs count]; i++) { NSArray *fontDef = [fontDefs objectAtIndex: i]; NSFontTraitMask t = [[fontDef objectAtIndex: 3] unsignedIntValue]; t &= ~(NSNonStandardCharacterSetFontMask | NSFixedPitchFontMask | NSUnitalicFontMask | NSUnboldFontMask); if ((([[fontDef objectAtIndex: 2] intValue] == 5) || ([[fontDef objectAtIndex: 2] intValue] == 6)) && (t == traits)) { //NSLog(@"Found font"); return [NSFont fontWithName: [fontDef objectAtIndex: 0] size: size]; } } } //NSLog(@"Didnt find font"); return nil; } // // Examining a font // - (NSFontTraitMask) traitsOfFont: (NSFont*)aFont { return [[aFont fontInfo] traits]; } /**Returns the weight of the NSFont fontObject
*/ - (int) weightOfFont: (NSFont*)fontObject { return [[fontObject fontInfo] weight]; } - (BOOL) fontNamed: (NSString*)typeface hasTraits: (NSFontTraitMask)fontTraitMask { // TODO: This method is implemented very slow, but I dont // see any use for it, so why change it? unsigned int i, j; NSArray *fontFamilies = [self availableFontFamilies]; NSFontTraitMask traits; for (i = 0; i < [fontFamilies count]; i++) { NSArray *fontDefs = [self availableMembersOfFontFamily: [fontFamilies objectAtIndex: i]]; for (j = 0; j < [fontDefs count]; j++) { NSArray *fontDef = [fontDefs objectAtIndex: j]; if ([[fontDef objectAtIndex: 0] isEqualToString: typeface]) { traits = [[fontDef objectAtIndex: 3] unsignedIntValue]; // FIXME: This is not exactly the right condition if ((traits & fontTraitMask) == fontTraitMask) { return YES; } else return NO; } } } return NO; } /**Returns whether the NSFontPanel is enabled ( if exists )
*/ - (BOOL) isEnabled { if (fontPanel != nil) { return [fontPanel isEnabled]; } else return NO; } /**Enables/disables the NSFontPanel and the font menu ( if they exist )
See Also: -isEnabled
*/ - (void) setEnabled: (BOOL)flag { int i; if (_fontMenu != nil) { for (i = 0; i < [_fontMenu numberOfItems]; i++) { [[_fontMenu itemAtIndex: i] setEnabled: flag]; } } if (fontPanel != nil) [fontPanel setEnabled: flag]; } /**Returns the font menu, creates it (if needed ) if create is YES.
See Also: -setFontMenu:
*/ - (NSMenu*) fontMenu: (BOOL)create { if (create && _fontMenu == nil) { idSets the font menu to newMenu
See Also: -fontMenu:
*/ - (void) setFontMenu: (NSMenu*)newMenu { ASSIGN(_fontMenu, newMenu); } /**Returns the NSFontPanel, creates it ( if needed ) if create is YES.
See Also: +setFontPanelFactory:
*/ - (NSFontPanel*) fontPanel: (BOOL)create { if ((fontPanel == nil) && (create)) { fontPanel = [[fontPanelClass alloc] init]; } return fontPanel; } - (void) orderFrontFontPanel: (id)sender { if (fontPanel == nil) fontPanel = [self fontPanel: YES]; [fontPanel orderFront: sender]; } /**Returns the NSFontManager's delegate
*/ - (id) delegate { return _delegate; } /**Sets the NSFontManager's delegate to anObject
* FIXME: This is extremely unclear. At the moment, the * NSFontManager's delegate is never used. This can't be right. */ - (void) setDelegate: (id)anObject { _delegate = anObject; } /**Returns the action sents by the NSFontManager.
See Also: -setAction:
*/ - (SEL) action { return _action; } /**Sents the action sents by the NSFontManager to aSelector.
See Also: -action
*/ - (void) setAction: (SEL)aSelector { _action = aSelector; } - (BOOL) sendAction { NSApplication *theApp = [NSApplication sharedApplication]; if (_action) { /* FIXME - shouldn't we try our own delegate first ?? It seems * what every programmer would expect, but it looks like Apple * doesn't do it! Or maybe they fixed it in recent releases ? */ return [theApp sendAction: _action to: nil from: self]; } else { return NO; } } - (BOOL) addCollection: (NSString *)name options: (int)options { [_collections setObject: [NSMutableArray arrayWithCapacity: 10] forKey: name]; return YES; } - (BOOL) removeCollection:(NSString *) collection { if ([_collections objectForKey: collection]) { [_collections removeObjectForKey: collection]; return YES; } else { return NO; } } - (NSArray *) collectionNames { return [_collections allKeys]; } - (void) addFontDescriptors: (NSArray *)descriptors toCollection: (NSString *)collection { NSMutableArray *a = [_collections objectForKey: collection]; if (a) { [a addObjectsFromArray: descriptors]; } } - (void) removeFontDescriptor: (NSFontDescriptor *)descriptor fromCollection: (NSString *)collection { NSMutableArray *a = [_collections objectForKey: collection]; if (a) { [a removeObject: descriptor]; } } - (NSArray *) fontDescriptorsInCollection: (NSString *)collection { return [_collections objectForKey: collection]; } - (NSDictionary *) convertAttributes: (NSDictionary *)attributes { NSMutableDictionary *newAttributes; int i; float size; float sizes[] = {4.0, 6.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 16.0, 18.0, 24.0, 36.0, 48.0, 64.0}; NSFontTraitMask t; if (attributes == nil) return nil; newAttributes = AUTORELEASE([attributes mutableCopy]); switch (_storedTag) { case NSNoFontChangeAction: break; case NSViaPanelFontAction: // FIXME break; case NSAddTraitFontAction: t = [[attributes objectForKey: NSFontSymbolicTrait] unsignedIntValue]; if (t & _trait) { return newAttributes; } else if (_trait == NSUnboldFontMask) { t &= ~NSBoldFontMask; } else if (_trait == NSUnitalicFontMask) { t &= ~NSItalicFontMask; } else { t &= _trait; // FIXME: What about weight for NSBoldFontMask? } [newAttributes setObject: [NSNumber numberWithUnsignedInt: t] forKey: NSFontSymbolicTrait]; break; case NSRemoveTraitFontAction: t = [[attributes objectForKey: NSFontSymbolicTrait] unsignedIntValue]; if (!(t & _trait)) { return newAttributes; } else if (_trait == NSUnboldFontMask) { t = (t | NSBoldFontMask) & ~NSUnboldFontMask; } else if (_trait == NSUnitalicFontMask) { t = (t | NSItalicFontMask) & ~NSUnitalicFontMask; } else { t &= ~_trait; // FIXME: What about weight for NSBoldFontMask? } [newAttributes setObject: [NSNumber numberWithUnsignedInt: t] forKey: NSFontSymbolicTrait]; break; case NSSizeUpFontAction: size = [[attributes objectForKey: NSFontSizeAttribute] floatValue]; for (i = 0; i < sizeof(sizes)/sizeof(float); i++) { if (sizes[i] > size) { size = sizes[i]; break; } } [newAttributes setObject: [NSString stringWithFormat: @"%f", size] forKey: NSFontSizeAttribute]; break; case NSSizeDownFontAction: size = [[attributes objectForKey: NSFontSizeAttribute] floatValue]; for (i = sizeof(sizes)/sizeof(float) -1; i >= 0; i--) { if (sizes[i] < size) { size = sizes[i]; break; } } [newAttributes setObject: [NSString stringWithFormat: @"%f", size] forKey: NSFontSizeAttribute]; break; case NSHeavierFontAction: // FIXME break; case NSLighterFontAction: // FIXME break; } return newAttributes; } - (void) setSelectedAttributes: (NSDictionary *)attributes isMultiple: (BOOL)flag { ASSIGN(_selectedAttributes, attributes); _multiple = flag; DESTROY(_selectedFont); } @end @implementation NSApplication(NSFontPanel) - (void) orderFrontFontPanel: (id)sender { [[NSFontManager sharedFontManager] orderFrontFontPanel: sender]; } @end