Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / AppKit / NSFontManager.m
blob06deb594bd29d4434e21c41cb7e9485ebabde52b
1 /* Copyright (c) 2006-2007 Christopher J. W. Lloyd
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
9 #import <AppKit/NSFontManager.h>
10 #import <AppKit/NSFontPanel.h>
11 #import <AppKit/NSFont.h>
12 #import <AppKit/NSFontFamily.h>
13 #import <AppKit/NSFontTypeface.h>
14 #import <AppKit/NSNibLoading.h>
15 #import <AppKit/NSApplication.h>
16 #import <AppKit/NSMenuItem.h>
17 #import <AppKit/NSMenu.h>
18 #import <AppKit/NSRaise.h>
20 @implementation NSFontManager
22 static Class _fontManagerFactory;
23 static Class _fontPanelFactory;
25 +(NSFontManager *)sharedFontManager {
26    NSString *name=@"NSFontManager";
27    
28    if(_fontManagerFactory!=Nil)
29     name=NSStringFromClass(_fontManagerFactory);
30    
31    return NSThreadSharedInstance(name);
34 +(void)setFontManagerFactory:(Class)value {
35    _fontManagerFactory=value;
38 +(void)setFontPanelFactory:(Class)value {
39    _fontPanelFactory=value;
42 -init {
43    _panel=nil;
44    _action=@selector(changeFont:);
45    _selectedFont=[[NSFont userFontOfSize:0] retain];
46    _isMultiple=NO;
47    return self;
50 -delegate {
51    return _delegate;
54 -(SEL)action {
55    return _action;
58 -(void)setDelegate:delegate {
59    _delegate=delegate;
62 -(void)setAction:(SEL)value {
63    _action=value;
66 - (NSFontAction)currentFontAction
68     return _currentFontAction;
71 -(NSArray *)collectionNames {
72    NSUnimplementedMethod();
73    return nil;
76 -(BOOL)addCollection:(NSString *)name options:(int)options {
77    NSUnimplementedMethod();
78    return 0;
81 -(void)addFontDescriptors:(NSArray *)descriptors toCollection:(NSString *)name {
82    NSUnimplementedMethod();
85 -(BOOL)removeCollection:(NSString *)name {
86    NSUnimplementedMethod();
87    return 0;
90 -(NSArray *)fontDescriptorsInCollection:(NSString *)name {
91    NSUnimplementedMethod();
92    return nil;
95 -(NSArray *)availableFonts {
96    NSMutableArray *result=[NSMutableArray array];
97    NSArray        *families=[NSFontFamily allFontFamilyNames];
98    int             i,count=[families count];
100    for(i=0;i<count;i++){
101     NSString     *familyName=[families objectAtIndex:i];
102     NSFontFamily *family=[NSFontFamily fontFamilyWithName:familyName];
103     NSArray      *typefaces=[family typefaces];
104     int           t,tcount=[typefaces count];
106     for(t=0;t<tcount;t++){
107      NSFontTypeface *typeface=[typefaces objectAtIndex:t];
108      NSString       *name=[typeface name];
110      [result addObject:name];
111     }
112    }
114    return result;
117 -(NSArray *)availableFontFamilies {
118    NSArray *families=[NSFontFamily allFontFamilyNames];
120    if(![_delegate respondsToSelector:@selector(fontManager:willIncludeFont:)])
121     return families;
122    else {
123     NSMutableArray *result=[NSMutableArray array];
124     int             i,count=[families count];
126     for(i=0;i<count;i++){
127      NSString     *familyName=[families objectAtIndex:i];
128      NSFontFamily *family=[NSFontFamily fontFamilyWithName:familyName];
129      NSArray      *typefaces=[family typefaces];
130      int           t,tcount=[typefaces count];
132      for(t=0;t<tcount;t++){
133       NSFontTypeface *typeface=[typefaces objectAtIndex:t];
134       NSString       *name=[typeface name];
136       if([_delegate fontManager:self willIncludeFont:name]){
137        [result addObject:familyName];
138        break;
139       }
140      }
141     }
143     return result;
144    }
147 -(NSArray *)availableMembersOfFontFamily:(NSString *)familyName {
148    NSMutableArray *result=[NSMutableArray array];
149    NSFontFamily   *family=[NSFontFamily fontFamilyWithName:familyName];
150    NSArray        *typefaces=[family typefaces];
151    int             i,count=[typefaces count];
153    for(i=0;i<count;i++){
154     NSFontTypeface *typeface=[typefaces objectAtIndex:i];
155     NSString       *name=[typeface name];
156     NSString       *traitName=[typeface traitName];
158            // Callers expect an array of four objects
159     [result addObject:[NSArray arrayWithObjects:name,traitName, [NSNumber numberWithInt: 0], [NSNumber numberWithInt: 0], nil]];
160    }
162    return result;
165 -(NSArray *)availableFontNamesMatchingFontDescriptor:(NSFontDescriptor *)descriptor {
166    NSUnimplementedMethod();
167    return nil;
170 -(NSArray *)availableFontNamesWithTraits:(NSFontTraitMask)traits {
171    NSUnimplementedMethod();
172    return nil;
175 -(BOOL)fontNamed:(NSString *)name hasTraits:(NSFontTraitMask)traits {
176         NSFont* font = [NSFont fontWithName: name size: 12];
177         NSFontTraitMask fontTraits=[self traitsOfFont:font];
178         return (traits & fontTraits) == traits;
181 -(NSFont *)fontWithFamily:(NSString *)familyName traits:(NSFontTraitMask)traits weight:(int)weight size:(float)size {
182         // Note : weight is ignored 
183         
184         NSFontFamily *family=[NSFontFamily fontFamilyWithName:familyName];
185         NSArray      *typefaces=[family typefaces];
186         int           i,count=[typefaces count];
187         NSString     *fontName=nil; 
188         
189         for(i=0;i<count;i++){
190                 NSFontTypeface *typeface=[typefaces objectAtIndex:i];
191                 NSFontTraitMask checkTraits=[typeface traits];
192                 
193                 if(((traits&NSItalicFontMask)==(checkTraits&NSItalicFontMask)) &&
194                    ((traits&NSBoldFontMask)==(checkTraits&NSBoldFontMask))) {
195                         fontName = [typeface name];
196                         break;
197                 }
198         }
199         
200         if (fontName == nil) {
201                 // match something!
202                 if ([typefaces count] > 0) {
203                         NSFontTypeface *typeface=[typefaces objectAtIndex:0];
204                         fontName = [typeface name];
205                 }
206         }
207         
208         NSFont *font = nil;
209         if (fontName != nil) {
210                 font = [NSFont fontWithName:fontName size:size];
211         } else {
212                 NSLog(@"unable to match any font in faces '%@' of family '%@' with traits mask: %d", typefaces, familyName, traits);
213         }
214         
215         return font;
218 -(int)weightOfFont:(NSFont *)font {
219         return 5; // default to the normal weight
222 -(NSFontTraitMask)traitsOfFont:(NSFont *)font {
223    NSFontTypeface *typeface=[NSFontFamily fontTypefaceWithName:[font fontName]];
225    return [typeface traits];
228 -(NSString *)localizedNameForFamily:(NSString *)family face:(NSString *)face {
229         if (face == nil) {
230                 return family;
231         }
232         return [NSString stringWithFormat: @"%@ %@", family, face];
235 -(void)setFontPanel:(NSFontPanel *)panel {
236    panel=[panel retain];
237    [_panel release];
238    _panel=panel;
241 -(NSFontPanel *)fontPanel:(BOOL)create {
242    if(_panel==nil && create){
243     [NSBundle loadNibNamed:@"NSFontPanel" owner:self];
244    }
246         if(![_panel setFrameUsingName:@"NSFontPanel"]) {
247                 [_panel center];
248         }
249     [_panel setFrameAutosaveName:@"NSFontPanel"];
250         
251         
252    [_panel setPanelFont:_selectedFont isMultiple:_isMultiple];
253    return _panel;
256 -(BOOL)sendAction {
257    return [NSApp sendAction:_action to:nil from:[NSFontManager sharedFontManager]];
260 -(BOOL)isEnabled {
261    NSUnimplementedMethod();
262    return 0;
265 -(BOOL)isMultiple {
266    return _isMultiple;
269 -(NSFont *)selectedFont {
270    return _selectedFont;
273 -(void)_configureMenu:(NSMenu *)menu forFont:(NSFont *)font {
274    NSArray *items=[menu itemArray];
275    int      i,count=[items count];
277    for(i=0;i<count;i++){
278     NSMenuItem *item=[items objectAtIndex:i];
280     if([item hasSubmenu])
281      [self _configureMenu:[item submenu] forFont:font];
282     else if(sel_isEqual([item action],@selector(addFontTrait:)) && [item target]==self){
283      unsigned        tag=[item tag];
284      NSFontTraitMask traits=[self traitsOfFont:font];
286      if(tag&(NSItalicFontMask|NSUnitalicFontMask)){
287       if(traits&NSItalicFontMask){
288        [item setTag:NSUnitalicFontMask];
289        [item setTitle: NSLocalizedStringFromTableInBundle(@"Unitalic", nil, [NSBundle bundleForClass: [NSFontManager class]], @"Remove the italic font trait")];
290       }
291       else {
292        [item setTag:NSItalicFontMask];
293        [item setTitle: NSLocalizedStringFromTableInBundle(@"Italic", nil, [NSBundle bundleForClass: [NSFontManager class]], @"Add the italic font trait")];
294       }
295      }
296      if(tag&(NSBoldFontMask|NSUnboldFontMask)){
297       if(traits& NSBoldFontMask){
298        [item setTag:NSUnboldFontMask];
299        [item setTitle: NSLocalizedStringFromTableInBundle(@"Unbold", nil, [NSBundle bundleForClass: [NSFontManager class]], @"Remove the bold font trait")];
300       }
301       else {
302        [item setTag:NSBoldFontMask];
303                 [item setTitle: NSLocalizedStringFromTableInBundle(@"Bold", nil, [NSBundle bundleForClass: [NSFontManager class]], @"Add the bold font trait")];
304       }
305      }
306     }
307    }
310 -(void)setSelectedFont:(NSFont *)font isMultiple:(BOOL)flag {
311    [_selectedFont autorelease];
312    _selectedFont=[font retain];
313    _isMultiple=flag;
315    [[self fontPanel:NO] setPanelFont:font isMultiple:flag];
316    [self _configureMenu:[NSApp mainMenu] forFont:font];
319 - (void)_udpdateSelectedFont
321     if (_selectedFont) {
322         NSFont *font = [self convertFont:_selectedFont];
323         if (font && font != _selectedFont) {
324             [self setSelectedFont:font isMultiple:_isMultiple];
325         }
326     }
329 -(NSFont *)convertFont:(NSFont *)font {
330     
331     if (font == nil) {
332         return nil;
333     }
334     switch(_currentFontAction){
335             
336         case NSNoFontChangeAction:
337             break;
338             
339         case NSViaPanelFontAction:
340             font=[_panel panelConvertFont:font];
341             break;
342             
343         case NSAddTraitFontAction:
344             font=[self convertFont:font toHaveTrait:_currentTrait];
345             break;
346             
347         case NSSizeUpFontAction:
348             font=[self convertFont:font toSize:[font pointSize]+1];
349             break;
350             
351         case NSSizeDownFontAction:{
352             float ps=[font pointSize];
353             if(ps>1) {
354                 ps-=1;
355             }
356             font=[self convertFont:font toSize:ps];
357         }
358             break;
359             
360         case NSHeavierFontAction:
361             font=[self convertWeight:YES ofFont:font];
362             break;
363             
364         case NSLighterFontAction:
365             font=[self convertWeight:NO ofFont:font];
366             break;
367             
368         case NSRemoveTraitFontAction:
369             font=[self convertFont:font toNotHaveTrait:_currentTrait];
370             break;
371     }
372     
373     return font;
377 -(NSFont *)convertFont:(NSFont *)font toSize:(float)size {
378    if(size==[font pointSize])
379     return font;
381    return [NSFont fontWithName:[font fontName] size:size];
384 - (BOOL)_canConvertFont:(NSFont*)font toHaveTrait: (NSFontTraitMask)addTraits
386         NSFontFamily   *family = [NSFontFamily fontFamilyWithTypefaceName: [font fontName]];
387         NSFontTypeface *typeface = [family typefaceWithName: [font fontName]];
388         NSFontTraitMask traits = [typeface traits];
389         
390         if(addTraits & NSItalicFontMask) {
391                 traits |= NSItalicFontMask;
392         }
393         
394         if(addTraits & NSBoldFontMask) {
395                 traits |= NSBoldFontMask;
396         }
397         
398         if(addTraits & NSUnboldFontMask) {
399                 traits &= ~NSBoldFontMask;
400         }
401         
402         if(addTraits & NSUnitalicFontMask) {
403                 traits &= ~NSItalicFontMask;
404         }
405         
406         NSFontTypeface* newface = [family typefaceWithTraits:traits];
407         return newface != nil;
410 -(NSFont *)convertFont:(NSFont *)font toHaveTrait:(NSFontTraitMask)addTraits {
411     if (font == nil) {
412         return nil;
413     }
415    NSFontFamily   *family=[NSFontFamily fontFamilyWithTypefaceName:[font fontName]];
416    NSFontTypeface *typeface=[family typefaceWithName:[font fontName]];
417    NSFontTraitMask traits=[typeface traits];
418    NSFontTypeface *newface;
420    if(addTraits&NSItalicFontMask)
421     traits|=NSItalicFontMask;
422    if(addTraits&NSBoldFontMask)
423     traits|=NSBoldFontMask;
424    if(addTraits&NSUnboldFontMask)
425     traits&=~NSBoldFontMask;
426    if(addTraits&NSUnitalicFontMask)
427     traits&=~NSItalicFontMask;
429    newface=[family typefaceWithTraits:traits];
431         if(newface!=nil) {
432                 return [NSFont fontWithName:[newface name] size:[font pointSize]];
433         }
434    NSLog(@"%s failed, %@ %d",sel_getName(_cmd),[font fontName],addTraits);
435    return font;
438 -(NSFont *)convertFont:(NSFont *)font toNotHaveTrait:(NSFontTraitMask)trait {
439     if (font == nil) {
440         return nil;
441     }
443         NSFontFamily   *family=[NSFontFamily fontFamilyWithName: [font familyName]];
444         NSFontTypeface *typeface=[family typefaceWithName:[font fontName]];
445         NSFontTraitMask traits=[typeface traits];
446         traits &= ~trait; // remove the traits
447         NSFontTypeface *newface;
448                 
449         newface=[family typefaceWithTraits:traits];
450         
451         if(newface!=nil) {
452                 return [NSFont fontWithName:[newface name] size:[font pointSize]];
453         }
454         NSLog(@"%s failed, %@ %d",sel_getName(_cmd),[font fontName],trait);
455         return font;
458 -(NSFont *)convertFont:(NSFont *)font toFace:(NSString *)typeface {
459     if (font == nil) {
460         return nil;
461     }
463         NSFont *convertedFont = [NSFont fontWithName: typeface size: [font pointSize]];
464     if (convertedFont) {
465         return convertedFont;
466     }
467     
468     // Return the original font if the conversion fails - same as Apple
469     return font;
472 -(NSFont *)convertFont:(NSFont *)font toFamily:(NSString *)family {
473     if (font == nil) {
474         return nil;
475     }
477         // Get the current traits so we try and match them...
478         NSFontFamily   *fontFamily=[NSFontFamily fontFamilyWithName: [font familyName]];
479         NSFontTypeface *typeface=[fontFamily typefaceWithName:[font fontName]];
480         NSFontTraitMask traits=[typeface traits];
481         return [self fontWithFamily: family traits: traits weight: 5 size: [font pointSize]];
484 -(NSFont *)convertWeight:(BOOL)heavierNotLighter ofFont:(NSFont *)font {
485     if (font == nil) {
486         return nil;
487     }
489         NSLog(@"convertWeight: %d ofFont: %@ ignored", heavierNotLighter, font);
490         return font;
493 -(NSDictionary *)convertAttributes:(NSDictionary *)attributes {
494    NSUnimplementedMethod();
495    return nil;
498 -(void)addFontTrait:sender {
499     _currentTrait = [sender tag];
500     _currentFontAction = NSAddTraitFontAction;
502     [self sendAction];
503     
504     [self _udpdateSelectedFont];
507 -(void)modifyFont:sender {
508     _currentFontAction=[sender tag];
509    
510     [self sendAction];
511     
512     [self _udpdateSelectedFont];
515 -(void)modifyFontViaPanel:sender {
516     _currentFontAction = NSViaPanelFontAction;
517     
518     [self sendAction];
519     
520     [self _udpdateSelectedFont];
523 -(void)removeFontTrait:sender {
524     _currentTrait = [sender tag];
525     _currentFontAction = NSRemoveTraitFontAction;
526     
527     [self sendAction];
528     
529     [self _udpdateSelectedFont];
532 -(void)orderFrontFontPanel:sender {
533    [[NSFontPanel sharedFontPanel] orderFront:sender];
536 -(void)orderFrontStylesPanel:sender {
537    NSUnimplementedMethod();
540 #pragma mark -
541 #pragma mark Font Menu Support
543 - (BOOL)validateMenuItem:(NSMenuItem*)item
545         BOOL valid = YES;
546         SEL action = [item action];
547         if (action == @selector(addFontTrait:)) {
548                 NSInteger tag = [item tag];
549                 valid = [self _canConvertFont: [self selectedFont] toHaveTrait: tag];
550         }
551         return valid;
554 @end