diff --git a/Headers/Additions/GNUstepGUI/GSFontAssetDownloader.h b/Headers/Additions/GNUstepGUI/GSFontAssetDownloader.h new file mode 100644 index 000000000..d60b370b6 --- /dev/null +++ b/Headers/Additions/GNUstepGUI/GSFontAssetDownloader.h @@ -0,0 +1,287 @@ +/* Definition of class GSFontAssetDownloader + Copyright (C) 2024 Free Software Foundation, Inc. + + By: Gregory John Casamento + Date: September 5, 2024 + + This file is part of the GNUstep Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110 USA. +*/ + +#ifndef _GSFontAssetDownloader_h_GNUSTEP_GUI_INCLUDE +#define _GSFontAssetDownloader_h_GNUSTEP_GUI_INCLUDE +#import + +#import +#import + +@class NSFontDescriptor; +@class NSURL; +@class NSString; +@class NSPanel; +@class NSProgressIndicator; +@class NSTextField; +@class NSButton; + +#if OS_API_VERSION(MAC_OS_X_VERSION_10_13, GS_API_LATEST) + +/** + * GSFontAssetDownloader provides a pluggable mechanism for downloading + * and installing font assets from various sources. This class can be + * subclassed to implement custom font downloading strategies, such as + * downloading from different font services, using authentication, or + * implementing custom validation and installation procedures. + * + * The default implementation supports downloading fonts from HTTP/HTTPS + * URLs and local file URLs, with basic validation and cross-platform + * installation to standard font directories. + * + * Subclasses can override individual methods to customize specific + * aspects of the download and installation process while reusing + * other parts of the default implementation. + * + * CLASS REPLACEMENT SYSTEM: + * + * GSFontAssetDownloader supports a class replacement system that allows + * applications to register a custom downloader class to be used globally. + * This enables complete customization of font downloading behavior without + * needing to modify every NSFontAssetRequest instance. + * + * Example usage: + * + * // Define a custom downloader class + * @interface MyCustomFontDownloader : GSFontAssetDownloader + * @end + * + * @implementation MyCustomFontDownloader + * - (NSURL *) fontURLForDescriptor: (NSFontDescriptor *)descriptor { + * // Custom URL resolution logic + * return [NSURL URLWithString: @"https://my-font-service.com/..."]; + * } + * @end + * + * // Register the custom class globally + * [GSFontAssetDownloader setDefaultDownloaderClass: [MyCustomFontDownloader class]]; + * + * // Or through NSFontAssetRequest + * [NSFontAssetRequest setDefaultDownloaderClass: [MyCustomFontDownloader class]]; + * + * // All new font asset requests will now use the custom downloader + * NSFontAssetRequest *request = [[NSFontAssetRequest alloc] + * initWithFontDescriptors: descriptors options: 0]; + * + * PROGRESS PANEL USAGE: + * + * To show a progress panel during font download, use the NSFontAssetRequestOptionUsesStandardUI option: + * + * NSFontDescriptor *descriptor = [NSFontDescriptor fontDescriptorWithName: @"Inconsolata" size: 12]; + * GSFontAssetDownloader *downloader = [GSFontAssetDownloader downloaderWithOptions: NSFontAssetRequestOptionUsesStandardUI]; + * NSError *error = nil; + * BOOL success = [downloader downloadAndInstallFontWithDescriptor: descriptor error: &error]; + * + * The progress panel will automatically appear with: + * - A progress bar showing download/installation progress + * - Status messages describing the current operation + * - A cancel button (posts GSFontAssetDownloadCancelled notification when pressed) + */ +GS_EXPORT_CLASS +@interface GSFontAssetDownloader : NSObject +{ + NSUInteger _options; + NSPanel *_progressPanel; + NSProgressIndicator *_progressIndicator; + NSTextField *_statusLabel; + NSButton *_cancelButton; +} + +/** + * Registers a custom downloader class to be used instead of the default + * GSFontAssetDownloader class. The registered class must be a subclass + * of GSFontAssetDownloader. Pass nil to restore the default behavior. + */ ++ (void) setDefaultDownloaderClass: (Class)downloaderClass; + +/** + * Returns the currently registered downloader class, or GSFontAssetDownloader + * if no custom class has been registered. + */ ++ (Class) defaultDownloaderClass; + +/** + * Creates a new font asset downloader instance using the currently + * registered downloader class. This is the preferred method for creating + * downloader instances as it respects any custom downloader class that + * has been registered. + */ ++ (instancetype) downloaderWithOptions: (NSUInteger)options; + +/** + * Creates a new font asset downloader with the specified options. + * The options parameter contains flags that control the download + * and installation behavior, such as whether to use standard UI + * or install to user vs system directories. + */ +- (instancetype) initWithOptions: (NSUInteger)options; + +/** + * Downloads and installs a font from the specified descriptor. + * This is the main entry point for font downloading. The method + * orchestrates the complete process: URL resolution, download, + * validation, and installation. Returns YES if the font was + * successfully downloaded and installed, NO otherwise. + */ +- (BOOL) downloadAndInstallFontWithDescriptor: (NSFontDescriptor *)descriptor + error: (NSError **)error; + +/** + * Downloads and installs a font from the specified descriptor with a preferred format. + * This variant allows specifying the preferred font format (e.g., "woff2", "woff", "ttf") + * when downloading from CSS URLs. For direct font URLs, the format parameter is ignored. + * Returns YES if the font was successfully downloaded and installed, NO otherwise. + */ +- (BOOL) downloadAndInstallFontWithDescriptor: (NSFontDescriptor *)descriptor + preferredFormat: (NSString *)format + error: (NSError **)error; + +/** + * Resolves a font URL from a font descriptor. + * This method can be overridden to implement custom URL resolution + * strategies, such as querying different font services or using + * authentication tokens. The default implementation looks for a + * custom URL attribute or constructs URLs from font names. + */ +- (NSURL *) fontURLForDescriptor: (NSFontDescriptor *)descriptor; + +/** + * Downloads a font file from the specified URL. + * This method can be overridden to implement custom download + * strategies, such as using authentication, custom headers, or + * progress callbacks. Returns the path to the downloaded temporary + * file, or nil on failure. + */ +- (NSString *) downloadFontFromURL: (NSURL *)fontURL + error: (NSError **)error; + +/** + * Downloads a font file from the specified URL with a given font name. + * This variant allows specifying the font name for better filename generation. + * The downloaded file will be saved with a name based on the font name and + * appropriate extension. Returns the path to the downloaded temporary file, + * or nil on failure. + */ +- (NSString *) downloadFontFromURL: (NSURL *)fontURL + fontName: (NSString *)fontName + error: (NSError **)error; + +/** + * Extracts font URLs from CSS content based on the specified format. + * This method parses CSS @font-face declarations and extracts URLs + * that match the given format (e.g., "woff2", "woff", "ttf"). + * Returns an array of NSURL objects, or nil on error. + */ +- (NSArray *) extractFontURLsFromCSS: (NSString *)cssContent + withFormat: (NSString *)format + error: (NSError **)error; + +/** + * Downloads font data from a CSS URL that contains @font-face declarations. + * This method first downloads the CSS content, parses it to extract font URLs + * based on the specified format, and then downloads the first matching font. + * Returns the path to the downloaded temporary file, or nil on failure. + */ +- (NSString *) downloadFontDataFromCSSURL: (NSURL *)cssURL + withFormat: (NSString *)format + error: (NSError **)error; + +/** + * Downloads font data from a CSS URL with a given font name. + * This variant allows specifying the font name for better filename generation. + * The downloaded file will be saved with a name based on the font name and + * appropriate extension. Returns the path to the downloaded temporary file, + * or nil on failure. + */ +- (NSString *) downloadFontDataFromCSSURL: (NSURL *)cssURL + withFormat: (NSString *)format + fontName: (NSString *)fontName + error: (NSError **)error; + +/** + * Validates a downloaded font file. + * This method can be overridden to implement custom validation + * logic, such as checking font metadata, licensing information, + * or performing security scans. The default implementation + * checks file existence, size, and format signatures. + */ +- (BOOL) validateFontFile: (NSString *)fontPath + error: (NSError **)error; + +/** + * Installs a font file to the appropriate system location. + * This method can be overridden to implement custom installation + * strategies, such as using system APIs, registering with font + * management services, or applying custom permissions. Returns + * YES if installation was successful, NO otherwise. + */ +- (BOOL) installFontAtPath: (NSString *)fontPath + error: (NSError **)error; + +/** + * Returns the system fonts directory for the current platform. + * This method can be overridden to customize the system font + * installation location or to support additional platforms. + */ +- (NSString *) systemFontsDirectory; + +/** + * Returns the user fonts directory for the current platform. + * This method can be overridden to customize the user font + * installation location or to support additional platforms. + */ +- (NSString *) userFontsDirectory; + +/** + * Returns the options that were specified when creating this downloader. + */ +- (NSUInteger) options; + +/** + * Shows a progress panel for font downloading when NSFontAssetRequestOptionUsesStandardUI is set. + * This method creates and displays a modal panel with a progress indicator and status text. + */ +- (void) showProgressPanelWithMessage: (NSString *)message; + +/** + * Updates the progress panel with current status and progress value. + * The progress parameter should be a value between 0.0 and 1.0. + */ +- (void) updateProgressPanel: (double)progress withMessage: (NSString *)message; + +/** + * Hides and releases the progress panel. + */ +- (void) hideProgressPanel; + +/** + * Action method called when the cancel button in the progress panel is pressed. + */ +- (void) cancelDownload: (id)sender; + +@end + +#endif /* GS_API_MACOSX */ + +#endif /* _GSFontAssetDownloader_h_GNUSTEP_GUI_INCLUDE */ diff --git a/Headers/Additions/GNUstepGUI/GSFontInfo.h b/Headers/Additions/GNUstepGUI/GSFontInfo.h index 2b0283a3f..a3dabf777 100644 --- a/Headers/Additions/GNUstepGUI/GSFontInfo.h +++ b/Headers/Additions/GNUstepGUI/GSFontInfo.h @@ -7,7 +7,7 @@ Author: Adam Fedor Date: Mar 2000 - + This file is part of the GNUstep. This library is free software; you can redistribute it and/or @@ -22,8 +22,8 @@ You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. - If not, see or write to the - Free Software Foundation, 51 Franklin Street, Fifth Floor, + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -48,11 +48,18 @@ APPKIT_EXPORT_CLASS + (void) setDefaultClass: (Class)defaultClass; + (GSFontEnumerator*) sharedEnumerator; + +// Font enumeration and caching - (void) enumerateFontsAndFamilies; +- (void) refreshFontCache; + +// Querying available fonts - (NSArray*) availableFonts; - (NSArray*) availableFontFamilies; - (NSArray*) availableMembersOfFontFamily: (NSString*)family; - (NSArray*) availableFontDescriptors; + +// Font matching and searching - (NSArray *) availableFontNamesMatchingFontDescriptor: (NSFontDescriptor *)descriptor; - (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes; - (NSArray *) matchingDescriptorsForFamily: (NSString *)family @@ -60,6 +67,7 @@ APPKIT_EXPORT_CLASS inclusion: (NSArray *)queryDescriptors exculsion: (NSArray *)exclusionDescriptors; +// Default system font names (called once, backends may override) /* Note that these are only called once. NSFont will remember the returned values. Backends may override these. */ - (NSString *) defaultSystemFontName; @@ -97,7 +105,7 @@ APPKIT_EXPORT_CLASS NSFontDescriptor *fontDescriptor; } -+ (GSFontInfo*) fontInfoForFontName: (NSString*)fontName ++ (GSFontInfo*) fontInfoForFontName: (NSString*)fontName matrix: (const CGFloat*)fmatrix screenFont: (BOOL)screenFont; + (void) setDefaultClass: (Class)defaultClass; @@ -132,22 +140,22 @@ APPKIT_EXPORT_CLASS - (NSSize) minimumAdvancement; - (NSStringEncoding) mostCompatibleStringEncoding; - (NSUInteger) numberOfGlyphs; -- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph - forCharacter: (unichar)aChar +- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph + forCharacter: (unichar)aChar struckOverRect: (NSRect)aRect; - (NSPoint) positionOfGlyph: (NSGlyph)curGlyph precededByGlyph: (NSGlyph)prevGlyph isNominal: (BOOL*)nominal; -- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph - struckOverGlyph: (NSGlyph)baseGlyph +- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph + struckOverGlyph: (NSGlyph)baseGlyph metricsExist: (BOOL *)flag; -- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph - struckOverRect: (NSRect)aRect +- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph + struckOverRect: (NSRect)aRect metricsExist: (BOOL *)flag; -- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph - withRelation: (NSGlyphRelation)relation +- (NSPoint) positionOfGlyph: (NSGlyph)aGlyph + withRelation: (NSGlyphRelation)relation toBaseGlyph: (NSGlyph)baseGlyph - totalAdvancement: (NSSize *)offset + totalAdvancement: (NSSize *)offset metricsExist: (BOOL *)flag; - (NSFontTraitMask) traits; - (CGFloat) underlinePosition; diff --git a/Headers/AppKit/NSFontAssetRequest.h b/Headers/AppKit/NSFontAssetRequest.h index 0dae5781f..44a430fbd 100644 --- a/Headers/AppKit/NSFontAssetRequest.h +++ b/Headers/AppKit/NSFontAssetRequest.h @@ -28,6 +28,7 @@ #import #import +#import /** * Block type for font asset download completion handling. @@ -39,8 +40,8 @@ */ DEFINE_BLOCK_TYPE(GSFontAssetCompletionHandler, BOOL, NSError*); -@class NSProgress; -// @protocol NSProgressReporting; +@class NSFontDescriptor; +@class GSFontAssetDownloader; #if OS_API_VERSION(MAC_OS_X_VERSION_10_13, GS_API_LATEST) @@ -83,7 +84,15 @@ typedef NSUInteger NSFontAssetRequestOptions; * operation while fonts are being retrieved. */ APPKIT_EXPORT_CLASS -@interface NSFontAssetRequest : NSObject // +@interface NSFontAssetRequest : NSObject +{ + NSArray *_fontDescriptors; + NSFontAssetRequestOptions _options; + NSMutableArray *_downloadedFontDescriptors; + NSProgress *_progress; + BOOL _downloadInProgress; + GSFontAssetDownloader *_downloader; +} /** * Initializes a font asset request with the specified font descriptors and options. @@ -138,6 +147,40 @@ APPKIT_EXPORT_CLASS @end +@interface NSFontAssetRequest (GNUstep) + +/** + * Sets the default downloader class to be used for all new font asset requests. + * The specified class must be a subclass of GSFontAssetDownloader. + * Pass nil to restore the default GSFontAssetDownloader behavior. + */ ++ (void) setDefaultDownloaderClass: (Class)downloaderClass; + +/** + * Returns the currently registered default downloader class. + */ ++ (Class) defaultDownloaderClass; + +/** + * Sets a custom font asset downloader. + * This allows clients to provide custom downloading strategies + * by subclassing GSFontAssetDownloader and overriding specific + * methods for URL resolution, downloading, validation, or installation. + * The downloader parameter specifies the custom downloader to use, + * replacing the default downloader instance. + */ +- (void) setFontAssetDownloader: (GSFontAssetDownloader *)downloader; + +/** + * Returns the current font asset downloader. + * This can be used to inspect or modify the downloader's configuration, + * or to access the downloader for direct use in custom scenarios. + * Returns the GSFontAssetDownloader instance currently being used. + */ +- (GSFontAssetDownloader *) fontAssetDownloader; + +@end + #if defined(__cplusplus) } #endif diff --git a/Headers/AppKit/NSFontManager.h b/Headers/AppKit/NSFontManager.h index edb79b4fd..21d97a87e 100644 --- a/Headers/AppKit/NSFontManager.h +++ b/Headers/AppKit/NSFontManager.h @@ -1,34 +1,34 @@ -/* - NSFontManager.h +/* + NSFontManager.h - Manages system and user fonts + Manages system and user fonts - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996 Free Software Foundation, Inc. - Author: Scott Christley - Date: 1996 - Modified: Fred Kiefer - Date: January 2000 - Almost complete rewrite. + Author: Scott Christley + Date: 1996 + Modified: Fred Kiefer + Date: January 2000 + Almost complete rewrite. - This file is part of the GNUstep GUI Library. + This file is part of the GNUstep GUI Library. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; see the file COPYING.LIB. - If not, see or write to the - Free Software Foundation, 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ #ifndef _GNUstep_H_NSFontManager #define _GNUstep_H_NSFontManager @@ -48,9 +48,9 @@ #if OS_API_VERSION(MAC_OS_X_VERSION_10_3, GS_API_LATEST) enum _NSFontManagerAddCollectionOptions -{ - NSFontCollectionApplicationOnlyMask = 1 << 0 -}; + { + NSFontCollectionApplicationOnlyMask = 1 << 0 + }; #endif typedef unsigned int NSFontTraitMask; @@ -163,8 +163,8 @@ APPKIT_EXPORT_CLASS // - (NSFontTraitMask)traitsOfFont:(NSFont *)aFont; - (int)weightOfFont:(NSFont *)fontObject; -- (BOOL)fontNamed:(NSString *)typeface - hasTraits:(NSFontTraitMask)fontTraitMask; +- (BOOL)fontNamed:(NSString *)typeface + hasTraits:(NSFontTraitMask)fontTraitMask; // // Enabling @@ -201,19 +201,28 @@ APPKIT_EXPORT_CLASS - (BOOL)removeCollection:(NSString *)collection; - (NSArray *)collectionNames; -- (void)addFontDescriptors:(NSArray *)descriptors - toCollection:(NSString *)collection; -- (void)removeFontDescriptor:(NSFontDescriptor *)descriptor - fromCollection:(NSString *)collection; +- (void)addFontDescriptors:(NSArray *)descriptors + toCollection:(NSString *)collection; +- (void)removeFontDescriptor:(NSFontDescriptor *)descriptor + fromCollection:(NSString *)collection; - (NSArray *)fontDescriptorsInCollection:(NSString *)collection; - (NSArray *)availableFontNamesMatchingFontDescriptor:(NSFontDescriptor *)descriptor; - (NSDictionary *)convertAttributes:(NSDictionary *)attributes; -- (void)setSelectedAttributes:(NSDictionary *)attributes - isMultiple:(BOOL)flag; +- (void)setSelectedAttributes:(NSDictionary *)attributes + isMultiple:(BOOL)flag; #endif @end +#if OS_API_VERSION(GS_API_NONE, GS_API_LATEST) +@interface NSFontManager (Private) +// +// GNUstep extensions +// +- (void) refreshAvailableFonts; +@end +#endif + @interface NSObject (NSFontManagerDelegate) // // Methods Implemented by the Delegate @@ -221,6 +230,7 @@ APPKIT_EXPORT_CLASS - (BOOL)fontManager:(id)sender willIncludeFont:(NSString *)fontName; @end -#endif // _GNUstep_H_NSFontManager - +// Notifications +APPKIT_EXPORT NSString * const GSFontManagerAvailableFontsDidChangeNotification; +#endif // _GNUstep_H_NSFontManager diff --git a/Headers/AppKit/NSProgressIndicator.h b/Headers/AppKit/NSProgressIndicator.h index 3bfbe1ae3..9374af55b 100644 --- a/Headers/AppKit/NSProgressIndicator.h +++ b/Headers/AppKit/NSProgressIndicator.h @@ -61,7 +61,9 @@ typedef enum _NSProgressIndicatorThickness typedef enum _NSProgressIndicatorStyle { NSProgressIndicatorBarStyle = 0, - NSProgressIndicatorSpinningStyle = 1 + NSProgressIndicatorStyleBar = NSProgressIndicatorBarStyle, + NSProgressIndicatorSpinningStyle = 1, + NSProgressIndicatorStyleSpinning = NSProgressIndicatorSpinningStyle } NSProgressIndicatorStyle; APPKIT_EXPORT_CLASS diff --git a/Source/GNUmakefile b/Source/GNUmakefile index 51fb140bb..1aefa2cf1 100644 --- a/Source/GNUmakefile +++ b/Source/GNUmakefile @@ -18,8 +18,8 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library; see the file COPYING.LIB. -# If not, see or write to the -# Free Software Foundation, 51 Franklin Street, Fifth Floor, +# If not, see or write to the +# Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. PACKAGE_NAME = gnustep-gui @@ -374,7 +374,8 @@ GSCSStrength.m \ GSCSEditInfo.m \ GSCSEditVariableManager.m \ GSCSTableau.m \ -GSColorSliderCell.m +GSColorSliderCell.m \ +GSFontAssetDownloader.m # Turn off NSMenuItem warning that NSMenuItem conforms to , # but does not implement 's methods itself (it inherits @@ -389,7 +390,7 @@ libgnustep-gui_HEADER_FILES_DIR = ../Headers/Additions/GNUstepGUI libgnustep-gui_HEADER_FILES_INSTALL_DIR = /GNUstepGUI COCOA_HEADERS = \ -Cocoa.h +Cocoa.h APPKIT_HEADERS = \ AppKit.h \ @@ -646,7 +647,7 @@ NSUserInterfaceItemSearching.h \ NSUserInterfaceItemIdentification.h \ NSUserInterfaceValidation.h \ DPSOperators.h \ -PSOperators.h +PSOperators.h GUI_HEADERS = \ GSVersion.h \ @@ -693,7 +694,8 @@ GSWindowDecorationView.h \ GSXibElement.h \ GSXibLoading.h \ GSXibKeyedUnarchiver.h \ -GSHelpAttachment.h +GSHelpAttachment.h \ +GSFontAssetDownloader.h libgnustep-gui_HEADER_FILES = ${GUI_HEADERS} diff --git a/Source/GSFontAssetDownloader.m b/Source/GSFontAssetDownloader.m new file mode 100644 index 000000000..6317e497c --- /dev/null +++ b/Source/GSFontAssetDownloader.m @@ -0,0 +1,956 @@ +/* Implementation of class GSFontAssetDownloader + Copyright (C) 2024 Free Software Foundation, Inc. + + By: Gregory John Casamento + Date: September 5, 2025 + + This file is part of the GNUstep Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110 USA. +*/ + +#import +#import "GNUstepGUI/GSFontAssetDownloader.h" + +#import "AppKit/NSEvent.h" +#import "AppKit/NSFontDescriptor.h" +#import "AppKit/NSFontAssetRequest.h" +#import "AppKit/NSFontManager.h" +#import "AppKit/NSPanel.h" +#import "AppKit/NSProgressIndicator.h" +#import "AppKit/NSTextField.h" +#import "AppKit/NSButton.h" +#import "AppKit/NSApplication.h" + +static Class _defaultDownloaderClass = nil; + +/* + * EXAMPLE USAGE OF CLASS REPLACEMENT SYSTEM: + * + * // Custom downloader that logs all operations + * @interface LoggingFontDownloader : GSFontAssetDownloader + * @end + * + * @implementation LoggingFontDownloader + * - (NSURL *) fontURLForDescriptor: (NSFontDescriptor *)descriptor { + * NSLog(@"Resolving URL for font: %@", [descriptor objectForKey: NSFontNameAttribute]); + * return [super fontURLForDescriptor: descriptor]; + * } + * + * - (NSString *) downloadFontFromURL: (NSURL *)fontURL error: (NSError **)error { + * NSLog(@"Downloading font from: %@", fontURL); + * return [super downloadFontFromURL: fontURL error: error]; + * } + * @end + * + * // To use the custom downloader globally: + * [GSFontAssetDownloader setDefaultDownloaderClass: [LoggingFontDownloader class]]; + * + * // To restore default behavior: + * [GSFontAssetDownloader setDefaultDownloaderClass: nil]; + */ + +@implementation GSFontAssetDownloader + +- (instancetype) initWithOptions: (NSUInteger)options +{ + self = [super init]; + if (self != nil) + { + _options = options; + } + return self; +} + +- (instancetype) init +{ + return [self initWithOptions: 0]; +} + +- (void) dealloc +{ + [self hideProgressPanel]; + [super dealloc]; +} + ++ (void) setDefaultDownloaderClass: (Class)downloaderClass +{ + if (downloaderClass != nil && ![downloaderClass isSubclassOfClass: [GSFontAssetDownloader class]]) + { + [NSException raise: NSInvalidArgumentException + format: @"Downloader class must be a subclass of GSFontAssetDownloader"]; + return; + } + _defaultDownloaderClass = downloaderClass; +} + ++ (Class) defaultDownloaderClass +{ + return _defaultDownloaderClass ? _defaultDownloaderClass : [GSFontAssetDownloader class]; +} + ++ (instancetype) downloaderWithOptions: (NSUInteger)options +{ + Class downloaderClass = [self defaultDownloaderClass]; + return [[downloaderClass alloc] initWithOptions: options]; +} + +- (BOOL) downloadAndInstallFontWithDescriptor: (NSFontDescriptor *)descriptor + error: (NSError **)error +{ + // Delegate to the format-specific method with default format + return [self downloadAndInstallFontWithDescriptor: descriptor + preferredFormat: nil + error: error]; +} + +- (BOOL) downloadAndInstallFontWithDescriptor: (NSFontDescriptor *)descriptor + preferredFormat: (NSString *)format + error: (NSError **)error +{ + if (descriptor == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font descriptor is nil" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -1001 + userInfo: userInfo]; + } + return NO; + } + + NSError *localError = nil; + NSString *downloadedPath = nil; + BOOL success = NO; + + // Show progress panel if standard UI is requested + NSString *fontName = [descriptor objectForKey: NSFontNameAttribute]; + if (fontName == nil) + { + fontName = [descriptor objectForKey: NSFontFamilyAttribute]; + } + if (fontName == nil) + { + fontName = @"Font"; + } + + NSString *progressMessage = [NSString stringWithFormat: @"Downloading %@...", fontName]; + [self showProgressPanelWithMessage: progressMessage]; + [self updateProgressPanel: 0.1 withMessage: progressMessage]; + + NS_DURING + { + // Get font URL from descriptor + [self updateProgressPanel: 0.2 withMessage: @"Resolving font URL..."]; + NSURL *fontURL = [self fontURLForDescriptor: descriptor]; + if (fontURL == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"No font URL available for descriptor" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -1002 + userInfo: userInfo]; + } + return NO; + } + + // Download the font file + [self updateProgressPanel: 0.3 withMessage: @"Starting download..."]; + // Check if this is a CSS URL (like Google Fonts API) and handle appropriately + NSString *urlString = [fontURL absoluteString]; + if ([urlString containsString: @"fonts.googleapis.com/css"] || + [urlString containsString: @"fonts.google.com/css"] || + [urlString containsString: @"fonts.gstatic.com/css"] || + [urlString hasSuffix: @".css"] || + [[fontURL pathExtension] isEqualToString: @"css"]) + { + // This is a CSS URL containing @font-face declarations + // Use the specified format, or default to truetype/ttf if none specified + NSString *preferredFormat = format ? format : @"truetype"; + [self updateProgressPanel: 0.4 withMessage: @"Downloading CSS and extracting font URLs..."]; + downloadedPath = [self downloadFontDataFromCSSURL: fontURL withFormat: preferredFormat fontName: fontName error: &localError]; + } + else + { + // This is a direct font file URL + [self updateProgressPanel: 0.4 withMessage: @"Downloading font file..."]; + downloadedPath = [self downloadFontFromURL: fontURL fontName: fontName error: &localError]; + } + + if (downloadedPath == nil) + { + [self hideProgressPanel]; + if (error != NULL) + { + *error = localError; + } + return NO; + } + + // Validate the downloaded font file + [self updateProgressPanel: 0.7 withMessage: @"Validating font file..."]; + if (![self validateFontFile: downloadedPath error: &localError]) + { + [self hideProgressPanel]; + if (error != NULL) + { + *error = localError; + } + return NO; + } + + // Install the font + [self updateProgressPanel: 0.8 withMessage: @"Installing font..."]; + success = [self installFontAtPath: downloadedPath error: &localError]; + if (success) + { + [self updateProgressPanel: 1.0 withMessage: @"Font installed successfully!"]; + + // Refresh font cache so newly installed font appears in font panel + [[NSFontManager sharedFontManager] refreshAvailableFonts]; + + // Brief delay to show completion + [NSThread sleepForTimeInterval: 0.5]; + } + else if (error != NULL) + { + *error = localError; + } + } + NS_HANDLER + { + [self hideProgressPanel]; + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Font download and installation failed", NSLocalizedDescriptionKey, + [localException reason], NSLocalizedFailureReasonErrorKey, + nil]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -1003 + userInfo: userInfo]; + } + success = NO; + } + NS_ENDHANDLER; + + // Clean up temporary download file + if (downloadedPath != nil) + { + [[NSFileManager defaultManager] removeItemAtPath: downloadedPath error: nil]; + } + + // Hide progress panel + [self hideProgressPanel]; + + return success; +} + +- (NSURL *) fontURLForDescriptor: (NSFontDescriptor *)descriptor +{ + // Check if descriptor has a URL attribute (custom extension) + NSURL *fontURL = [descriptor objectForKey: @"NSFontURLAttribute"]; + if (fontURL != nil) + { + return fontURL; + } + + // Try to construct URL from font name for common font sources + NSString *fontName = [descriptor objectForKey: NSFontNameAttribute]; + NSString *familyName = [descriptor objectForKey: NSFontFamilyAttribute]; + + if (fontName == nil && familyName == nil) + { + return nil; + } + + NSString *searchName = fontName ? fontName : familyName; + NSString *encodedName = [searchName stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; + + // Example: Try Google Fonts API (this is simplified - real implementation would use proper API) + NSString *urlString = [NSString stringWithFormat: @"https://fonts.googleapis.com/css2?family=%@", encodedName]; + + return [NSURL URLWithString: urlString]; +} + +- (NSArray *) extractFontURLsFromCSS: (NSString *)cssContent + withFormat: (NSString *)format + error: (NSError **)error +{ + if (cssContent == nil || [cssContent length] == 0) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"CSS content is nil or empty" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2100 + userInfo: userInfo]; + } + return nil; + } + + NSMutableArray *fontURLs = [NSMutableArray array]; + + // Regular expression to match src: url(...) format('...') patterns + NSString *pattern = @"src:\\s*url\\(([^)]+)\\)\\s*format\\(['\"]([^'\"]+)['\"]\\)"; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: pattern + options: NSRegularExpressionCaseInsensitive + error: error]; + if (regex == nil) + { + return nil; + } + + NSArray *matches = [regex matchesInString: cssContent + options: 0 + range: NSMakeRange(0, [cssContent length])]; + NSEnumerator *men = [matches objectEnumerator]; + NSTextCheckingResult *match = nil; + + while ((match = [men nextObject]) != nil) + { + if ([match numberOfRanges] >= 3) + { + NSRange urlRange = [match rangeAtIndex: 1]; + NSRange formatRange = [match rangeAtIndex: 2]; + + NSString *urlString = [cssContent substringWithRange: urlRange]; + NSString *formatString = [cssContent substringWithRange: formatRange]; + + // Clean up URL string (remove quotes if present) + urlString = [urlString stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: @"\"' "]]; + + // Check if format matches what we're looking for + if (format == nil || [formatString isEqualToString: format]) + { + NSURL *url = [NSURL URLWithString: urlString]; + if (url != nil) + { + [fontURLs addObject: url]; + } + } + } + } + + return [fontURLs copy]; +} + +- (NSString *) downloadFontDataFromCSSURL: (NSURL *)cssURL + withFormat: (NSString *)format + error: (NSError **)error +{ + // Use a generic font name since we don't have the font descriptor here + NSString *genericFontName = @"WebFont"; + + // Try to extract font name from CSS URL if it's a Google Fonts URL + NSString *urlString = [cssURL absoluteString]; + if ([urlString containsString: @"fonts.googleapis.com/css"] || [urlString containsString: @"fonts.google.com/css"]) + { + // Try to extract font family from URL parameters + NSString *query = [cssURL query]; + if (query != nil) + { + NSArray *queryParts = [query componentsSeparatedByString: @"&"]; + for (NSString *part in queryParts) + { + if ([part hasPrefix: @"family="]) + { + NSString *familyPart = [part substringFromIndex: 7]; // Skip "family=" + // Take first font name if multiple are specified + NSArray *families = [familyPart componentsSeparatedByString: @"|"]; + if ([families count] > 0) + { + genericFontName = [families objectAtIndex: 0]; + // Clean up URL encoding + genericFontName = [genericFontName stringByRemovingPercentEncoding]; + // Replace + with spaces (URL encoding) + genericFontName = [genericFontName stringByReplacingOccurrencesOfString: @"+" withString: @" "]; + break; + } + } + } + } + } + + // Delegate to the method that takes font name + return [self downloadFontDataFromCSSURL: cssURL withFormat: format fontName: genericFontName error: error]; +} + +- (NSString *) downloadFontDataFromCSSURL: (NSURL *)cssURL + withFormat: (NSString *)format + fontName: (NSString *)fontName + error: (NSError **)error +{ + if (cssURL == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"CSS URL is nil" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2101 + userInfo: userInfo]; + } + return nil; + } + + // First, download the CSS content + NSString *cssContent = nil; + NSError *cssError = nil; + + [self updateProgressPanel: 0.45 withMessage: @"Downloading CSS file..."]; + + NS_DURING + { + NSData *cssData = [NSData dataWithContentsOfURL: cssURL]; + if (cssData != nil) + { + cssContent = [[NSString alloc] initWithData: cssData encoding: NSUTF8StringEncoding]; + } + } + NS_HANDLER + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Failed to download CSS from URL", NSLocalizedDescriptionKey, + [localException reason], NSLocalizedFailureReasonErrorKey, + nil]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2102 + userInfo: userInfo]; + } + return nil; + } + NS_ENDHANDLER; + + if (cssContent == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Failed to parse CSS content" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2103 + userInfo: userInfo]; + } + return nil; + } + + // Extract font URLs from CSS + [self updateProgressPanel: 0.5 withMessage: @"Parsing CSS and extracting font URLs..."]; + NSArray *fontURLs = [self extractFontURLsFromCSS: cssContent + withFormat: format + error: &cssError]; + if (fontURLs == nil || [fontURLs count] == 0) + { + if (error != NULL) + { + *error = cssError ?: [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2104 + userInfo: [NSDictionary dictionaryWithObject: @"No font URLs found in CSS" + forKey: NSLocalizedDescriptionKey]]; + } + return nil; + } + + // Download the first matching font URL using the font name + [self updateProgressPanel: 0.6 withMessage: @"Downloading font file from extracted URL..."]; + NSURL *fontURL = [fontURLs firstObject]; + return [self downloadFontFromURL: fontURL fontName: fontName error: error]; +} + +- (NSString *) downloadFontFromURL: (NSURL *)fontURL + error: (NSError **)error +{ + // Extract font name from URL or use generic name + NSString *fontName = @"UnknownFont"; + NSString *lastComponent = [fontURL lastPathComponent]; + if (lastComponent != nil && [lastComponent length] > 0) + { + // Remove file extension to get just the name + fontName = [lastComponent stringByDeletingPathExtension]; + if ([fontName length] == 0) + { + fontName = @"UnknownFont"; + } + } + + // Delegate to the method that takes font name + return [self downloadFontFromURL: fontURL fontName: fontName error: error]; +} + +- (NSString *) downloadFontFromURL: (NSURL *)fontURL + fontName: (NSString *)fontName + error: (NSError **)error +{ + if (fontURL == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font URL is nil" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2001 + userInfo: userInfo]; + } + return nil; + } + + // Create temporary file for download using font name + NSString *tempDir = NSTemporaryDirectory(); + NSString *filename = nil; + + // Clean font name for use in filename (remove spaces and special characters) + NSString *cleanFontName = fontName; + if (cleanFontName != nil && [cleanFontName length] > 0) + { + // Replace spaces and special characters with underscores + NSMutableString *mutableName = [cleanFontName mutableCopy]; + [mutableName replaceOccurrencesOfString: @" " withString: @"_" options: 0 range: NSMakeRange(0, [mutableName length])]; + [mutableName replaceOccurrencesOfString: @"-" withString: @"_" options: 0 range: NSMakeRange(0, [mutableName length])]; + [mutableName replaceOccurrencesOfString: @"+" withString: @"_" options: 0 range: NSMakeRange(0, [mutableName length])]; + cleanFontName = [NSString stringWithString: mutableName]; + [mutableName release]; + } + else + { + cleanFontName = @"UnknownFont"; + } + + // Determine appropriate file extension based on URL + NSString *extension = @"ttf"; // default + NSString *urlPath = [fontURL path]; + if ([urlPath containsString: @".woff2"]) + { + extension = @"woff2"; + } + else if ([urlPath containsString: @".woff"]) + { + extension = @"woff"; + } + else if ([urlPath containsString: @".otf"]) + { + extension = @"otf"; + } + else if ([urlPath containsString: @".ttf"]) + { + extension = @"ttf"; + } + + // Create filename with font name and appropriate extension + filename = [NSString stringWithFormat: @"%@.%@", cleanFontName, extension]; + NSString *tempPath = [tempDir stringByAppendingPathComponent: filename]; + + // Download the font file + NSData *fontData = nil; + + NS_DURING + { + // For HTTPS URLs, try to download + if ([[fontURL scheme] isEqualToString: @"https"] || [[fontURL scheme] isEqualToString: @"http"]) + { + fontData = [NSData dataWithContentsOfURL: fontURL]; + } + // For file URLs, copy the file + else if ([[fontURL scheme] isEqualToString: @"file"]) + { + fontData = [NSData dataWithContentsOfURL: fontURL]; + } + } + NS_HANDLER + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Failed to download font from URL", NSLocalizedDescriptionKey, + [localException reason], NSLocalizedFailureReasonErrorKey, + nil]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2002 + userInfo: userInfo]; + } + return nil; + } + NS_ENDHANDLER; + + if (fontData == nil || [fontData length] == 0) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"No data received from font URL" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2003 + userInfo: userInfo]; + } + return nil; + } + + // Write font data to temporary file + if (![fontData writeToFile: tempPath atomically: YES]) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Failed to write font data to temporary file" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -2004 + userInfo: userInfo]; + } + return nil; + } + + return tempPath; +} + +- (BOOL) validateFontFile: (NSString *)fontPath + error: (NSError **)error +{ + if (fontPath == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font path is nil" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -3001 + userInfo: userInfo]; + } + return NO; + } + + // Check if file exists + if (![[NSFileManager defaultManager] fileExistsAtPath: fontPath]) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font file does not exist" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -3002 + userInfo: userInfo]; + } + return NO; + } + + // Basic validation - check file size and magic bytes + NSData *fontData = [NSData dataWithContentsOfFile: fontPath]; + if (fontData == nil || [fontData length] < 12) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font file is too small or unreadable" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -3003 + userInfo: userInfo]; + } + return NO; + } + + // Check for common font file signatures (TTF, OTF, WOFF, WOFF2) + const unsigned char *bytes = [fontData bytes]; + + // TTF signature: 0x00, 0x01, 0x00, 0x00 or 'true' + // OTF signature: 'OTTO' + // WOFF signature: 'wOFF' + // WOFF2 signature: 'wOF2' + if ((bytes[0] == 0x00 && bytes[1] == 0x01 && bytes[2] == 0x00 && bytes[3] == 0x00) || + (bytes[0] == 't' && bytes[1] == 'r' && bytes[2] == 'u' && bytes[3] == 'e') || + (bytes[0] == 'O' && bytes[1] == 'T' && bytes[2] == 'T' && bytes[3] == 'O') || + (bytes[0] == 'w' && bytes[1] == 'O' && bytes[2] == 'F' && bytes[3] == 'F') || + (bytes[0] == 'w' && bytes[1] == 'O' && bytes[2] == 'F' && bytes[3] == '2')) + { + return YES; + } + + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font file does not have a valid font signature" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -3004 + userInfo: userInfo]; + } + return NO; +} + +- (BOOL) installFontAtPath: (NSString *)fontPath + error: (NSError **)error +{ + if (fontPath == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font path is nil" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -4001 + userInfo: userInfo]; + } + return NO; + } + + NSString *filename = [fontPath lastPathComponent]; + NSString *destinationDir; + + // Determine installation directory based on options + if (_options & NSFontAssetRequestOptionUsesStandardUI) + { + // Install to user fonts directory for user-level installation + destinationDir = [self userFontsDirectory]; + } + else + { + // Try system fonts directory, fall back to user directory + destinationDir = [self systemFontsDirectory]; + if (destinationDir == nil) + { + destinationDir = [self userFontsDirectory]; + } + } + + if (destinationDir == nil) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Cannot determine font installation directory" + forKey: NSLocalizedDescriptionKey]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -4002 + userInfo: userInfo]; + } + return NO; + } + + // Create destination directory if needed + NSError *dirError = nil; + if (![[NSFileManager defaultManager] createDirectoryAtPath: destinationDir + withIntermediateDirectories: YES + attributes: nil + error: &dirError]) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Failed to create font installation directory", NSLocalizedDescriptionKey, + [dirError localizedDescription], NSLocalizedFailureReasonErrorKey, + nil]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -4003 + userInfo: userInfo]; + } + return NO; + } + + // Copy font to destination + NSString *destinationPath = [destinationDir stringByAppendingPathComponent: filename]; + NSError *copyError = nil; + + // Remove existing font file if present + if ([[NSFileManager defaultManager] fileExistsAtPath: destinationPath]) + { + [[NSFileManager defaultManager] removeItemAtPath: destinationPath error: nil]; + } + + if (![[NSFileManager defaultManager] copyItemAtPath: fontPath + toPath: destinationPath + error: ©Error]) + { + if (error != NULL) + { + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Failed to copy font to installation directory", NSLocalizedDescriptionKey, + [copyError localizedDescription], NSLocalizedFailureReasonErrorKey, + nil]; + *error = [NSError errorWithDomain: @"GSFontAssetDownloaderErrorDomain" + code: -4004 + userInfo: userInfo]; + } + return NO; + } + + // Notify system of new font (platform-specific) +#ifdef __APPLE__ + // On macOS, fonts are automatically detected when placed in font directories + NSLog(@"Font installed to: %@", destinationPath); +#else + NS_DURING + { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath: @"/usr/bin/fc-cache"]; + [task setArguments: [NSArray arrayWithObject: @"-f"]]; + [task launch]; + [task waitUntilExit]; + if ([task terminationStatus] == 0) + { + NSLog(@"Font installed and cache updated: %@", destinationPath); + } + else + { + NSLog(@"Font installed, but fc-cache failed with status: %d", [task terminationStatus]); + } + } + NS_HANDLER + { + NSLog(@"Font installed, but failed to run fc-cache: %@", localException); + } + NS_ENDHANDLER; +#endif + + return YES; +} + +- (NSString *) systemFontsDirectory +{ +#ifdef __APPLE__ + return @"/Library/Fonts"; +#else + return @"/usr/local/share/fonts"; +#endif +} + +- (NSString *) userFontsDirectory +{ + NSString *homeDir = NSHomeDirectory(); + +#ifdef __APPLE__ + return [homeDir stringByAppendingPathComponent: @"Library/Fonts"]; +#else + // Generic Unix/other systems + return [homeDir stringByAppendingPathComponent: @".fonts"]; +#endif +} + +- (NSUInteger) options +{ + return _options; +} + +- (void) showProgressPanelWithMessage: (NSString *)message +{ + if (!(_options & NSFontAssetRequestOptionUsesStandardUI)) + { + return; // Don't show UI if not requested + } + + if (_progressPanel != nil) + { + [self hideProgressPanel]; // Hide existing panel if any + } + + // Create the progress panel + NSRect panelFrame = NSMakeRect(0, 0, 400, 120); + _progressPanel = [[NSPanel alloc] initWithContentRect: panelFrame + styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskClosable + backing: NSBackingStoreBuffered + defer: NO]; + [_progressPanel setTitle: @"Font Download"]; + [_progressPanel setLevel: NSModalPanelWindowLevel]; + [_progressPanel setReleasedWhenClosed: NO]; + + // Create and configure the status label + NSRect labelFrame = NSMakeRect(20, 70, 360, 20); + _statusLabel = [[NSTextField alloc] initWithFrame: labelFrame]; + [_statusLabel setStringValue: message ? message : @"Downloading font..."]; + [_statusLabel setBezeled: NO]; + [_statusLabel setDrawsBackground: NO]; + [_statusLabel setEditable: NO]; + [_statusLabel setSelectable: NO]; + [[_progressPanel contentView] addSubview: _statusLabel]; + RELEASE(_statusLabel); + + // Create and configure the progress indicator + NSRect progressFrame = NSMakeRect(20, 40, 360, 20); + _progressIndicator = [[NSProgressIndicator alloc] initWithFrame: progressFrame]; + [_progressIndicator setStyle: NSProgressIndicatorStyleBar]; + [_progressIndicator setIndeterminate: NO]; + [_progressIndicator setMinValue: 0.0]; + [_progressIndicator setMaxValue: 1.0]; + [_progressIndicator setDoubleValue: 0.0]; + [[_progressPanel contentView] addSubview: _progressIndicator]; + RELEASE(_progressIndicator); + + // Create and configure the cancel button + NSRect buttonFrame = NSMakeRect(310, 10, 80, 25); + _cancelButton = [[NSButton alloc] initWithFrame: buttonFrame]; + [_cancelButton setTitle: @"Cancel"]; + [_cancelButton setTarget: self]; + [_cancelButton setAction: @selector(cancelDownload:)]; + [[_progressPanel contentView] addSubview: _cancelButton]; + RELEASE(_cancelButton); + + // Center and show the panel + [_progressPanel center]; + [_progressPanel makeKeyAndOrderFront: nil]; +} + +- (void) updateProgressPanel: (double)progress withMessage: (NSString *)message +{ + if (_progressPanel == nil || !(_options & NSFontAssetRequestOptionUsesStandardUI)) + { + return; + } + + if (_progressIndicator != nil) + { + [_progressIndicator setDoubleValue: progress]; + } + + if (_statusLabel != nil && message != nil) + { + [_statusLabel setStringValue: message]; + } + + // Process events to update the UI + NSEvent *event; + while ((event = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate distantPast] + inMode: NSDefaultRunLoopMode + dequeue: YES])) + { + [NSApp sendEvent: event]; + } +} + +- (void) hideProgressPanel +{ + if (_progressPanel != nil) + { + [_progressPanel orderOut: nil]; + DESTROY(_progressPanel); + } +} + +- (void) cancelDownload: (id)sender +{ + // For now, just hide the panel + // In a full implementation, this would cancel the actual download operation + [self hideProgressPanel]; + + // Post a notification that download was cancelled + [[NSNotificationCenter defaultCenter] + postNotificationName: @"GSFontAssetDownloadCancelled" + object: self]; +} + +@end diff --git a/Source/GSFontInfo.m b/Source/GSFontInfo.m index cb667faad..22f162cf4 100644 --- a/Source/GSFontInfo.m +++ b/Source/GSFontInfo.m @@ -76,9 +76,9 @@ - (void) dealloc + (GSFontEnumerator*) sharedEnumerator { NSAssert(fontEnumeratorClass, - @"Called with fontEnumeratorClass unset." - @" The shared NSApplication instance must be created before methods that" - @" need the backend may be called."); + @"Called with fontEnumeratorClass unset." + @" The shared NSApplication instance must be created before methods that" + @" need the backend may be called."); if (!sharedEnumerator) sharedEnumerator = [[fontEnumeratorClass alloc] init]; return sharedEnumerator; @@ -90,6 +90,22 @@ - (void) enumerateFontsAndFamilies [self subclassResponsibility: _cmd]; } +- (void) refreshFontCache +{ + // Release old cached data + RELEASE(allFontNames); + RELEASE(allFontFamilies); + RELEASE(allFontDescriptors); + + // Reset to nil so they get rebuilt + allFontNames = nil; + allFontFamilies = nil; + allFontDescriptors = nil; + + // Re-enumerate fonts and families to rebuild cache + [self enumerateFontsAndFamilies]; +} + - (NSArray*) availableFonts { return allFontNames; @@ -117,38 +133,38 @@ - (NSArray*) availableFontDescriptors fontDescriptors = [[NSMutableArray alloc] init]; keyEnumerator = [allFontFamilies keyEnumerator]; while ((family = [keyEnumerator nextObject]) != nil) - { - NSArray *fontDefs = [allFontFamilies objectForKey: family]; - NSEnumerator *fdEnumerator; - NSArray *fontDef; - - fdEnumerator = [fontDefs objectEnumerator]; - while ((fontDef = [fdEnumerator nextObject]) != nil) - { - NSFontDescriptor *fd; - NSDictionary *attributes; - NSNumber *weight = [fontDef objectAtIndex: 2]; - NSNumber *traits = [fontDef objectAtIndex: 3]; - NSDictionary *fontTraits; - float fweight = ([weight intValue] - 6) / 6.0; - - fontTraits = [NSDictionary dictionaryWithObjectsAndKeys: - traits, NSFontSymbolicTrait, - [NSNumber numberWithFloat: fweight], - NSFontWeightTrait, - nil]; - attributes = [NSDictionary dictionaryWithObjectsAndKeys: - family, NSFontFamilyAttribute, - [fontDef objectAtIndex: 0], NSFontNameAttribute, - [fontDef objectAtIndex: 1], NSFontFaceAttribute, - fontTraits, NSFontTraitsAttribute, - nil]; - fd = [[NSFontDescriptor alloc] initWithFontAttributes: attributes]; - - [fontDescriptors addObject: fd]; - RELEASE(fd); - } - } + { + NSArray *fontDefs = [allFontFamilies objectForKey: family]; + NSEnumerator *fdEnumerator; + NSArray *fontDef; + + fdEnumerator = [fontDefs objectEnumerator]; + while ((fontDef = [fdEnumerator nextObject]) != nil) + { + NSFontDescriptor *fd; + NSDictionary *attributes; + NSNumber *weight = [fontDef objectAtIndex: 2]; + NSNumber *traits = [fontDef objectAtIndex: 3]; + NSDictionary *fontTraits; + float fweight = ([weight intValue] - 6) / 6.0; + + fontTraits = [NSDictionary dictionaryWithObjectsAndKeys: + traits, NSFontSymbolicTrait, + [NSNumber numberWithFloat: fweight], + NSFontWeightTrait, + nil]; + attributes = [NSDictionary dictionaryWithObjectsAndKeys: + family, NSFontFamilyAttribute, + [fontDef objectAtIndex: 0], NSFontNameAttribute, + [fontDef objectAtIndex: 1], NSFontFaceAttribute, + fontTraits, NSFontTraitsAttribute, + nil]; + fd = [[NSFontDescriptor alloc] initWithFontAttributes: attributes]; + + [fontDescriptors addObject: fd]; + RELEASE(fd); + } + } allFontDescriptors = fontDescriptors; } @@ -178,9 +194,9 @@ - (NSArray *) availableFontNamesMatchingFontDescriptor: (NSFontDescriptor *)desc { fontName = [fd objectForKey: NSFontNameAttribute]; if (fontName != nil) - { - [found addObject: fontName]; - } + { + [found addObject: fontName]; + } } return found; @@ -200,47 +216,47 @@ - (BOOL) _fontDescriptor: (NSFontDescriptor *)fd matches: (NSDictionary *)attrib id valueA = [attributes objectForKey: key]; if (valueA != nil) - { - id valueB = [fd objectForKey: key]; - - if (valueB == nil) - { - match = NO; - break; - } - - // Special handling for NSFontTraitsAttribute - if ([key isEqual: NSFontTraitsAttribute]) - { - NSNumber *traitsA = [valueA objectForKey: NSFontSymbolicTrait]; - NSNumber *traitsB = [valueB objectForKey: NSFontSymbolicTrait]; - - // FIXME: For now we only compare symbolic traits - if ((traitsA != nil) && - ((traitsB == nil) || - ([traitsA unsignedIntValue] != [traitsB unsignedIntValue]))) - { - match = NO; - break; - } - } - else if ([key isEqual: NSFontCharacterSetAttribute]) - { - if (![valueB isSupersetOfSet: valueA]) - { - match = NO; - break; - } - } - else - { - if (![valueA isEqual: valueB]) - { - match = NO; - break; - } - } - } + { + id valueB = [fd objectForKey: key]; + + if (valueB == nil) + { + match = NO; + break; + } + + // Special handling for NSFontTraitsAttribute + if ([key isEqual: NSFontTraitsAttribute]) + { + NSNumber *traitsA = [valueA objectForKey: NSFontSymbolicTrait]; + NSNumber *traitsB = [valueB objectForKey: NSFontSymbolicTrait]; + + // FIXME: For now we only compare symbolic traits + if ((traitsA != nil) && + ((traitsB == nil) || + ([traitsA unsignedIntValue] != [traitsB unsignedIntValue]))) + { + match = NO; + break; + } + } + else if ([key isEqual: NSFontCharacterSetAttribute]) + { + if (![valueB isSupersetOfSet: valueA]) + { + match = NO; + break; + } + } + else + { + if (![valueA isEqual: valueB]) + { + match = NO; + break; + } + } + } } return match; @@ -258,9 +274,9 @@ - (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes while ((fd = [fdEnumerator nextObject]) != nil) { if ([self _fontDescriptor: fd matches: attributes]) - { - [found addObject: fd]; - } + { + [found addObject: fd]; + } } return found; @@ -276,9 +292,9 @@ - (BOOL) _fontDescriptor: (NSFontDescriptor *)fd matchesAny: (NSArray *)a while ((o = [en nextObject]) != nil) { if ([self _fontDescriptor: fd matches: [o fontAttributes]]) - { - return YES; - } + { + return YES; + } } return NO; @@ -290,9 +306,9 @@ - (BOOL) _fontDescriptor: (NSFontDescriptor *)fd matchesAny: (NSArray *)a * Currently we ignore the options as these may only be implemented in the backend. */ - (NSArray *) matchingDescriptorsForFamily: (NSString *)family - options: (NSDictionary *)options - inclusion: (NSArray *)queryDescriptors - exculsion: (NSArray *)exclusionDescriptors + options: (NSDictionary *)options + inclusion: (NSArray *)queryDescriptors + exculsion: (NSArray *)exclusionDescriptors { NSMutableArray *r = [NSMutableArray arrayWithCapacity: 50]; NSEnumerator *en = [[self availableFontDescriptors] objectEnumerator]; @@ -302,22 +318,22 @@ - (NSArray *) matchingDescriptorsForFamily: (NSString *)family { // Check if the font descriptor matches the family value if one is given if ((family != nil) && - ![[fd objectForKey: NSFontFamilyAttribute] isEqualToString: family]) - { - continue; - } + ![[fd objectForKey: NSFontFamilyAttribute] isEqualToString: family]) + { + continue; + } // Check if the font descriptor matches any of the query descriptors if (![self _fontDescriptor: fd matchesAny: queryDescriptors]) - { - continue; - } + { + continue; + } // Check if the font descriptor matches none of the exclusion descriptors if ([self _fontDescriptor: fd matchesAny: exclusionDescriptors]) - { - continue; - } + { + continue; + } // Add it to the result [r addObject: fd]; @@ -357,7 +373,7 @@ + (void) setDefaultClass: (Class)defaultClass } + (GSFontInfo*) fontInfoForFontName: (NSString*)nfontName - matrix: (const CGFloat *)fmatrix + matrix: (const CGFloat *)fmatrix screenFont: (BOOL)screenFont; { NSAssert(fontInfoClass, @@ -506,53 +522,53 @@ - (NSDictionary*) afmDictionary [fontDictionary setObject: fontName forKey: NSAFMFontName]; if (familyName != nil) - { + { [fontDictionary setObject: familyName forKey: NSAFMFamilyName]; } if (ascender != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: ascender] forKey: NSAFMAscender]; } if (descender != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: descender] forKey: NSAFMDescender]; } if (xHeight != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: xHeight] forKey: NSAFMXHeight]; } if (capHeight != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: capHeight] forKey: NSAFMCapHeight]; } if (italicAngle != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: italicAngle] forKey: NSAFMItalicAngle]; } if (underlinePosition != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: underlinePosition] forKey: NSAFMUnderlinePosition]; } if (underlineThickness != 0.0) - { + { [fontDictionary setObject: [NSNumber numberWithFloat: underlineThickness] forKey: NSAFMUnderlineThickness]; } weightString = [GSFontInfo stringForWeight: weight]; if (weightString != nil) - { + { [fontDictionary setObject: weightString forKey: NSAFMWeight]; } if (encodingScheme != nil) - { + { [fontDictionary setObject: encodingScheme forKey: NSAFMEncodingScheme]; } @@ -837,32 +853,32 @@ - (NSFontDescriptor*) fontDescriptor { // Create a new one if ((matrix[0] == matrix[3]) && (matrix[1] == 0.0) && - (matrix[2] == 0.0) && (matrix[4] == 0.0) && (matrix[5] == 0.0)) - { - ASSIGN(fontDescriptor, [NSFontDescriptor fontDescriptorWithName: fontName - size: matrix[0]]); - } + (matrix[2] == 0.0) && (matrix[4] == 0.0) && (matrix[5] == 0.0)) + { + ASSIGN(fontDescriptor, [NSFontDescriptor fontDescriptorWithName: fontName + size: matrix[0]]); + } else - { - NSAffineTransform *transform = [NSAffineTransform new]; - NSAffineTransformStruct ats; - NSDictionary *attributes; - - ats.m11 = matrix[0]; - ats.m12 = matrix[1]; - ats.m21 = matrix[2]; - ats.m22 = matrix[3]; - ats.tX = matrix[4]; - ats.tY = matrix[5]; - [transform setTransformStruct: ats]; - - attributes = [NSDictionary dictionaryWithObjectsAndKeys: - fontName, NSFontNameAttribute, - transform, NSFontMatrixAttribute, - nil]; - RELEASE(transform); - fontDescriptor = [[NSFontDescriptor alloc] initWithFontAttributes: attributes]; - } + { + NSAffineTransform *transform = [NSAffineTransform new]; + NSAffineTransformStruct ats; + NSDictionary *attributes; + + ats.m11 = matrix[0]; + ats.m12 = matrix[1]; + ats.m21 = matrix[2]; + ats.m22 = matrix[3]; + ats.tX = matrix[4]; + ats.tY = matrix[5]; + [transform setTransformStruct: ats]; + + attributes = [NSDictionary dictionaryWithObjectsAndKeys: + fontName, NSFontNameAttribute, + transform, NSFontMatrixAttribute, + nil]; + RELEASE(transform); + fontDescriptor = [[NSFontDescriptor alloc] initWithFontAttributes: attributes]; + } } return fontDescriptor; } diff --git a/Source/NSFontAssetRequest.m b/Source/NSFontAssetRequest.m index c5d0bf587..a2932e8af 100644 --- a/Source/NSFontAssetRequest.m +++ b/Source/NSFontAssetRequest.m @@ -1,21 +1,21 @@ /* Implementation of class NSFontAssetRequest Copyright (C) 2019 Free Software Foundation, Inc. - + By: Gregory John Casamento Date: Tue Apr 7 08:06:56 EDT 2020 This file is part of the GNUstep Library. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, @@ -24,33 +24,223 @@ #import #import "AppKit/NSFontAssetRequest.h" +#import "AppKit/NSFontDescriptor.h" +#import "GNUstepGUI/GSFontAssetDownloader.h" + +@interface NSFontAssetRequest (Private) +- (void) _performFontDownloadWithCompletionHandler: (GSFontAssetCompletionHandler)completionHandler; +- (void) _updateProgressWithDescription: (NSString *)description; +- (void) _completeDownloadWithError: (NSError *)error + completionHandler: (GSFontAssetCompletionHandler)completionHandler; +@end + +@implementation NSFontAssetRequest (GNUstep) + ++ (void) setDefaultDownloaderClass: (Class)downloaderClass +{ + [GSFontAssetDownloader setDefaultDownloaderClass: downloaderClass]; +} + ++ (Class) defaultDownloaderClass +{ + return [GSFontAssetDownloader defaultDownloaderClass]; +} + +- (void) setFontAssetDownloader: (GSFontAssetDownloader *)downloader +{ + ASSIGN(_downloader, downloader); +} + +- (GSFontAssetDownloader *) fontAssetDownloader +{ + return _downloader; +} + +@end + @implementation NSFontAssetRequest - (instancetype) initWithFontDescriptors: (NSArray *)fontDescriptors options: (NSFontAssetRequestOptions)options { - return [super init]; + self = [super init]; + if (self != nil) + { + _fontDescriptors = [fontDescriptors copy]; + _options = options; + _downloadedFontDescriptors = [[NSMutableArray alloc] init]; + _progress = RETAIN([NSProgress progressWithTotalUnitCount: [fontDescriptors count]]); + [_progress setCompletedUnitCount: 0]; + _downloadInProgress = NO; + _downloader = [[GSFontAssetDownloader alloc] initWithOptions: options]; + [_progress setLocalizedDescription: @"Downloading fonts..."]; + [_progress setLocalizedAdditionalDescription: @"Preparing to download font assets"]; + } + return self; +} + +- (void) dealloc +{ + RELEASE(_fontDescriptors); + RELEASE(_downloadedFontDescriptors); + RELEASE(_progress); + RELEASE(_downloader); + [super dealloc]; } - (NSArray *) downloadedFontDescriptors { - return nil; + return [[_downloadedFontDescriptors copy] autorelease]; } - (NSProgress *) progress { - return nil; // [NSProgress progressWithTotalUnitCount: 0.0]; + return _progress; } - (void) downloadFontAssetsWithCompletionHandler: (GSFontAssetCompletionHandler)completionHandler { - NSError *error = nil; - NSAssert(completionHandler != NULL, NSInvalidArgumentException); - CALL_NON_NULL_BLOCK(completionHandler, error); + + if (_downloadInProgress) + { + // Already downloading, call completion handler with error + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font asset download already in progress" + forKey: NSLocalizedDescriptionKey]; + NSError *error = [NSError errorWithDomain: @"NSFontAssetRequestErrorDomain" + code: -1001 + userInfo: userInfo]; + CALL_NON_NULL_BLOCK(completionHandler, error); + return; + } + + _downloadInProgress = YES; + + // Use timer-based simulation instead of GCD blocks for compatibility + [self performSelector: @selector(_performFontDownloadWithCompletionHandler:) + withObject: completionHandler + afterDelay: 0.0]; } -@end +- (void) _performFontDownloadWithCompletionHandler: (GSFontAssetCompletionHandler)completionHandler +{ + NSError *downloadError = nil; + BOOL success = YES; + NSUInteger i, count; + + NS_DURING + { + count = [_fontDescriptors count]; + + // Process each font descriptor + for (i = 0; i < count; i++) + { + NSFontDescriptor *descriptor = [_fontDescriptors objectAtIndex: i]; + NSError *fontError = nil; + + // Update progress description + NSString *fontName = [descriptor objectForKey: NSFontNameAttribute]; + if (fontName == nil) + { + fontName = @"Unknown"; + } + NSString *progressDescription = [NSString stringWithFormat: @"Processing font: %@", fontName]; + [self _updateProgressWithDescription: progressDescription]; + + // Check if progress was cancelled + if ([_progress isCancelled]) + { + success = NO; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject: @"Font asset download was cancelled" + forKey: NSLocalizedDescriptionKey]; + downloadError = [NSError errorWithDomain: @"NSFontAssetRequestErrorDomain" + code: -1002 + userInfo: userInfo]; + break; + } + + // Use the downloader to download and install the font + if ([_downloader downloadAndInstallFontWithDescriptor: descriptor error: &fontError]) + { + // Successfully downloaded and installed + [_downloadedFontDescriptors addObject: descriptor]; + NSLog(@"Successfully installed font: %@", fontName); + } + else + { + NSLog(@"Failed to download/install font %@: %@", fontName, [fontError localizedDescription]); + } + + // Update progress + [_progress setCompletedUnitCount: i + 1]; + } + if (success) + { + [self _updateProgressWithDescription: @"Font download and installation completed"]; + } + } + NS_HANDLER + { + success = NO; + NSString *reason = [localException reason]; + if (reason == nil) + { + reason = @"Unknown error"; + } + NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + @"Font asset download failed", NSLocalizedDescriptionKey, + reason, NSLocalizedFailureReasonErrorKey, + nil]; + downloadError = [NSError errorWithDomain: @"NSFontAssetRequestErrorDomain" + code: -1003 + userInfo: userInfo]; + } + NS_ENDHANDLER + + [self _completeDownloadWithError: downloadError + completionHandler: completionHandler]; +} + +- (void) _updateProgressWithDescription: (NSString *)description +{ + [_progress setLocalizedAdditionalDescription: description]; +} + +- (void) _completeDownloadWithError: (NSError *)downloadError + completionHandler: (GSFontAssetCompletionHandler)completionHandler +{ + _downloadInProgress = NO; + + if (downloadError == nil) + { + // Success case - no error + CALL_NON_NULL_BLOCK(completionHandler, nil); + } + else + { + // Error case + CALL_NON_NULL_BLOCK(completionHandler, downloadError); + } +} + +// Additional helper methods that could be useful + +- (NSArray *) fontDescriptors +{ + return AUTORELEASE([_fontDescriptors copy]); +} + +- (NSFontAssetRequestOptions) options +{ + return _options; +} + +- (BOOL) isDownloadInProgress +{ + return _downloadInProgress; +} + +@end diff --git a/Source/NSFontManager.m b/Source/NSFontManager.m index e8e6f91ad..62d41a0dd 100644 --- a/Source/NSFontManager.m +++ b/Source/NSFontManager.m @@ -7,7 +7,7 @@ Author: Fred Kiefer Date: January 2000 Almost complete rewrite. - + This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or @@ -22,10 +22,10 @@ You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. - If not, see or write to the - Free Software Foundation, 51 Franklin Street, Fifth Floor, + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ +*/ #include "config.h" #import @@ -43,6 +43,8 @@ #import "AppKit/NSMenuItem.h" #import "GNUstepGUI/GSFontInfo.h" +/* Notification names */ +NSString * const GSFontManagerAvailableFontsDidChangeNotification = @"GSFontManagerAvailableFontsDidChangeNotification"; /* * Class variables @@ -76,7 +78,7 @@ + (void) initialize implementing your own class ( a subclass of NSFontManager )

This class is init into +sharedFontManager

See Also: +sharedFontManager

- */ +*/ + (void) setFontManagerFactory: (Class)aClass { fontManagerClass = aClass; @@ -85,7 +87,7 @@ + (void) setFontManagerFactory: (Class)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; @@ -146,6 +148,15 @@ - (NSArray*) availableFontFamilies return [_fontEnumerator availableFontFamilies]; } +- (void) refreshAvailableFonts +{ + [_fontEnumerator refreshFontCache]; + + // Post notification that available fonts have changed + [[NSNotificationCenter defaultCenter] postNotificationName: GSFontManagerAvailableFontsDidChangeNotification + object: self]; +} + - (NSArray*) availableFontNamesWithTraits: (NSFontTraitMask)fontTraitMask { unsigned int i, j; @@ -160,18 +171,18 @@ - (NSArray*) availableFontNamesWithTraits: (NSFontTraitMask)fontTraitMask for (i = 0; i < [fontFamilies count]; i++) { - NSArray *fontDefs = [self availableMembersOfFontFamily: - [fontFamilies objectAtIndex: 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]]; - } + { + 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; @@ -193,8 +204,8 @@ - (NSArray *) matchingFontDescriptorsFor: (NSDictionary *)attributes return [_fontEnumerator matchingFontDescriptorsFor: attributes]; } -- (NSString*) localizedNameForFamily: (NSString*)family - face: (NSString*)face +- (NSString*) localizedNameForFamily: (NSString*)family + face: (NSString*)face { // TODO return [NSString stringWithFormat: @"%@-%@", family, face]; @@ -203,19 +214,19 @@ - (NSString*) localizedNameForFamily: (NSString*)family /** */ - (void) setSelectedFont: (NSFont*)fontObject - isMultiple: (BOOL)flag + 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]; - } - } + { + _multiple = flag; + // The panel should also know if multiple changed + if (fontPanel != nil) + { + [fontPanel setPanelFont: fontObject isMultiple: flag]; + } + } return; } @@ -227,7 +238,7 @@ - (void) setSelectedFont: (NSFont*)fontObject { [fontPanel setPanelFont: fontObject isMultiple: flag]; } - + if (_fontMenu != nil) { id menuItem; @@ -238,42 +249,42 @@ - (void) setSelectedFont: (NSFont*)fontObject * We keep the tag, to mark the item */ if (trait & NSItalicFontMask) - { - menuItem = [_fontMenu itemWithTag: NSItalicFontMask]; - if (menuItem != nil) - { - [menuItem setTitle: @"Unitalic"]; - [menuItem setAction: @selector(removeFontTrait:)]; - } - } + { + menuItem = [_fontMenu itemWithTag: NSItalicFontMask]; + if (menuItem != nil) + { + [menuItem setTitle: @"Unitalic"]; + [menuItem setAction: @selector(removeFontTrait:)]; + } + } else - { - menuItem = [_fontMenu itemWithTag: NSItalicFontMask]; - if (menuItem != nil) - { - [menuItem setTitle: @"Italic"]; - [menuItem setAction: @selector(addFontTrait:)]; - } - } + { + menuItem = [_fontMenu itemWithTag: NSItalicFontMask]; + if (menuItem != nil) + { + [menuItem setTitle: @"Italic"]; + [menuItem setAction: @selector(addFontTrait:)]; + } + } if (trait & NSBoldFontMask) - { - menuItem = [_fontMenu itemWithTag: NSBoldFontMask]; - if (menuItem != nil) - { - [menuItem setTitle: @"Unbold"]; - [menuItem setAction: @selector(removeFontTrait:)]; - } - } + { + menuItem = [_fontMenu itemWithTag: NSBoldFontMask]; + if (menuItem != nil) + { + [menuItem setTitle: @"Unbold"]; + [menuItem setAction: @selector(removeFontTrait:)]; + } + } else - { - menuItem = [_fontMenu itemWithTag: NSBoldFontMask]; - if (menuItem != nil) - { - [menuItem setTitle: @"Bold"]; - [menuItem setAction: @selector(addFontTrait:)]; - } - } + { + menuItem = [_fontMenu itemWithTag: NSBoldFontMask]; + if (menuItem != nil) + { + [menuItem setTitle: @"Bold"]; + [menuItem setAction: @selector(addFontTrait:)]; + } + } // TODO Update the rest of the font menu to reflect this font } @@ -309,9 +320,9 @@ - (void) addFontTrait: (id)sender NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) - { - [self setSelectedFont: newFont isMultiple: _multiple]; - } + { + [self setSelectedFont: newFont isMultiple: _multiple]; + } } } @@ -327,9 +338,9 @@ - (void) removeFontTrait: (id)sender NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) - { - [self setSelectedFont: newFont isMultiple: _multiple]; - } + { + [self setSelectedFont: newFont isMultiple: _multiple]; + } } } @@ -344,9 +355,9 @@ - (void) modifyFont: (id)sender NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) - { - [self setSelectedFont: newFont isMultiple: _multiple]; - } + { + [self setSelectedFont: newFont isMultiple: _multiple]; + } } } @@ -361,15 +372,15 @@ - (void) modifyFontViaPanel: (id)sender NSFont *newFont = [self convertFont: _selectedFont]; if (newFont != nil) - { - [self setSelectedFont: newFont isMultiple: _multiple]; - } + { + [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: +

See Also: -addFontTrait: -removeFontTrait: -modifyFont: -modifyFontViaPanel: -convertFont:toHaveTrait: -convertFont:toNotHaveTrait: -convertFont:toSize: -convertFont:toFamily: -convertWeight:ofFont:

*/ @@ -378,60 +389,60 @@ - (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}; + 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; + 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; @@ -439,7 +450,7 @@ - (NSFont*) convertFont: (NSFont*)fontObject - (NSFont*) convertFont: (NSFont*)fontObject - toFamily: (NSString*)family + toFamily: (NSString*)family { if ([family isEqualToString: [fontObject familyName]]) { @@ -454,19 +465,19 @@ - (NSFont*) convertFont: (NSFont*)fontObject int weight = [self weightOfFont: fontObject]; float size = [fontObject pointSize]; - newFont = [self fontWithFamily: family - traits: trait - weight: weight - size: size]; + newFont = [self fontWithFamily: family + traits: trait + weight: weight + size: size]; if (newFont == nil) - return fontObject; - else - return newFont; + return fontObject; + else + return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject - toFace: (NSString*)typeface + toFace: (NSString*)typeface { NSFont *newFont; @@ -479,12 +490,12 @@ - (NSFont*) convertFont: (NSFont*)fontObject newFont = [NSFont fontWithName: typeface size: [fontObject pointSize]]; if (newFont == nil) return fontObject; - else + else return newFont; } - (NSFont*) convertFont: (NSFont*)fontObject - toHaveTrait: (NSFontTraitMask)trait + toHaveTrait: (NSFontTraitMask)trait { NSFontTraitMask t = [self traitsOfFont: fontObject]; @@ -503,42 +514,42 @@ - (NSFont*) convertFont: (NSFont*)fontObject NSString *family = [fontObject familyName]; if (trait & NSBoldFontMask) - { - // We cannot reuse the weight in a bold - weight = 9; - t = t & ~NSUnboldFontMask; - } + { + // 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; - } + { + // We cannot reuse the weight in an unbold + weight = 5; + t = t & ~NSBoldFontMask; + } if (trait == NSItalicFontMask) - { - t = t & ~NSUnitalicFontMask; - } + { + t = t & ~NSUnitalicFontMask; + } else if (trait & NSUnitalicFontMask) - { - t = t & ~NSItalicFontMask; - } + { + t = t & ~NSItalicFontMask; + } t = t | trait; - newFont = [self fontWithFamily: family - traits: t - weight: weight - size: size]; + newFont = [self fontWithFamily: family + traits: t + weight: weight + size: size]; if (newFont == nil) - return fontObject; - else - return newFont; + return fontObject; + else + return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject - toNotHaveTrait: (NSFontTraitMask)trait + toNotHaveTrait: (NSFontTraitMask)trait { NSFontTraitMask t = [self traitsOfFont: fontObject]; @@ -557,40 +568,40 @@ - (NSFont*) convertFont: (NSFont*)fontObject NSString *family = [fontObject familyName]; if (trait & NSBoldFontMask) - { - // We cannot reuse the weight in an unbold - weight = 5; - t = (t | NSUnboldFontMask); - } + { + // 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); - } + { + // We cannot reuse the weight in a bold + weight = 9; + t = (t | NSBoldFontMask); + } if (trait & NSItalicFontMask) - { - t = (t | NSUnitalicFontMask); - } + { + t = (t | NSUnitalicFontMask); + } else if (trait & NSUnitalicFontMask) - { - t = (t | NSItalicFontMask); - } - + { + t = (t | NSItalicFontMask); + } + t &= ~trait; - newFont = [self fontWithFamily: family - traits: t - weight: weight - size: size]; + newFont = [self fontWithFamily: family + traits: t + weight: weight + size: size]; if (newFont == nil) - return fontObject; - else - return newFont; + return fontObject; + else + return newFont; } } - (NSFont*) convertFont: (NSFont*)fontObject - toSize: (float)size + toSize: (float)size { if ([fontObject pointSize] == size) { @@ -602,17 +613,17 @@ - (NSFont*) convertFont: (NSFont*)fontObject // Else convert it NSFont *newFont; - newFont = [NSFont fontWithName: [fontObject fontName] - size: size]; + newFont = [NSFont fontWithName: [fontObject fontName] + size: size]; if (newFont == nil) - return fontObject; - else - return newFont; + return fontObject; + else + return newFont; } } - (NSFont*) convertWeight: (BOOL)upFlag - ofFont: (NSFont*)fontObject + ofFont: (NSFont*)fontObject { NSFont *newFont = nil; NSString *fontName = nil; @@ -632,36 +643,36 @@ - (NSFont*) convertWeight: (BOOL)upFlag 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]; - } - } + { + 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]; - } - } - } + { + // 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 { @@ -669,46 +680,46 @@ - (NSFont*) convertWeight: (BOOL)upFlag 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]; - } - } + { + 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]; - } - } - } + { + // 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]; + size: size]; } if (newFont == nil) return fontObject; - else + else return newFont; } @@ -716,9 +727,9 @@ - (NSFont*) convertWeight: (BOOL)upFlag * Getting a font */ - (NSFont*) fontWithFamily: (NSString*)family - traits: (NSFontTraitMask)traits - weight: (int)weight - size: (float)size + traits: (NSFontTraitMask)traits + weight: (int)weight + size: (float)size { NSArray *fontDefs = [self availableMembersOfFontFamily: family]; unsigned int i; @@ -730,78 +741,78 @@ - (NSFont*) fontWithFamily: (NSString*)family { NSArray *fontDef = [fontDefs objectAtIndex: i]; - //NSLog(@"Testing font %@: %i: %i", [fontDef objectAtIndex: 0], - // [[fontDef objectAtIndex: 2] intValue], - // [[fontDef objectAtIndex: 3] unsignedIntValue]); + //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]; - } + ([[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); + | 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); + | NSUnitalicFontMask | NSUnboldFontMask); if (([[fontDef objectAtIndex: 2] intValue] == weight) && - (t == traits)) - { - //NSLog(@"Found font"); - return [NSFont fontWithName: [fontDef objectAtIndex: 0] - size: size]; - } + (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]; - } - } + { + 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]; - } - } + { + 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"); + //NSLog(@"Didnt find font"); return nil; } @@ -820,42 +831,42 @@ - (int) weightOfFont: (NSFont*)fontObject return [[fontObject fontInfo] weight]; } -- (BOOL) fontNamed: (NSString*)typeface - hasTraits: (NSFontTraitMask)fontTraitMask +- (BOOL) fontNamed: (NSString*)typeface + hasTraits: (NSFontTraitMask)fontTraitMask { - // TODO: This method is implemented very slow, but I dont + // 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]]; - + 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; - } - } + { + 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 )

+/**

Returns whether the NSFontPanel is enabled ( if exists )

*/ - (BOOL) isEnabled { @@ -867,7 +878,7 @@ - (BOOL) isEnabled return NO; } -/**

Enables/disables the NSFontPanel and the font menu ( if they exist )

+/**

Enables/disables the NSFontPanel and the font menu ( if they exist )

See Also: -isEnabled

*/ - (void) setEnabled: (BOOL)flag @@ -877,16 +888,16 @@ - (void) setEnabled: (BOOL)flag if (_fontMenu != nil) { for (i = 0; i < [_fontMenu numberOfItems]; i++) - { - [[_fontMenu itemAtIndex: i] setEnabled: flag]; - } + { + [[_fontMenu itemAtIndex: i] setEnabled: flag]; + } } if (fontPanel != nil) [fontPanel setEnabled: flag]; } -/**

Returns the font menu, creates it (if needed ) if create +/**

Returns the font menu, creates it (if needed ) if create is YES.

See Also: -setFontMenu:

*/ - (NSMenu*) fontMenu: (BOOL)create @@ -894,57 +905,57 @@ - (NSMenu*) fontMenu: (BOOL)create if (create && _fontMenu == nil) { id menuItem; - - // As the font menu is stored in a instance variable we + + // As the font menu is stored in a instance variable we // dont autorelease it _fontMenu = [NSMenu new]; [_fontMenu setTitle: @"Font Menu"]; // First an entry to start the font panel menuItem = [_fontMenu addItemWithTitle: @"Font Panel" - action: @selector(orderFrontFontPanel:) - keyEquivalent: @"t"]; + action: @selector(orderFrontFontPanel:) + keyEquivalent: @"t"]; [menuItem setTarget: self]; // Entry for italic menuItem = [_fontMenu addItemWithTitle: @"Italic" - action: @selector(addFontTrait:) - keyEquivalent: @"i"]; + action: @selector(addFontTrait:) + keyEquivalent: @"i"]; [menuItem setTag: NSItalicFontMask]; [menuItem setTarget: self]; // Entry for bold menuItem = [_fontMenu addItemWithTitle: @"Bold" - action: @selector(addFontTrait:) - keyEquivalent: @"b"]; + action: @selector(addFontTrait:) + keyEquivalent: @"b"]; [menuItem setTag: NSBoldFontMask]; [menuItem setTarget: self]; // Entry to increase weight menuItem = [_fontMenu addItemWithTitle: @"Heavier" - action: @selector(modifyFont:) - keyEquivalent: @""]; + action: @selector(modifyFont:) + keyEquivalent: @""]; [menuItem setTag: NSHeavierFontAction]; [menuItem setTarget: self]; - + // Entry to decrease weight menuItem = [_fontMenu addItemWithTitle: @"Lighter" - action: @selector(modifyFont:) - keyEquivalent: @""]; + action: @selector(modifyFont:) + keyEquivalent: @""]; [menuItem setTag: NSLighterFontAction]; [menuItem setTarget: self]; - + // Entry to increase size menuItem = [_fontMenu addItemWithTitle: @"Larger" - action: @selector(modifyFont:) - keyEquivalent: @"+"]; + action: @selector(modifyFont:) + keyEquivalent: @"+"]; [menuItem setTag: NSSizeUpFontAction]; [menuItem setTarget: self]; // Entry to decrease size menuItem = [_fontMenu addItemWithTitle: @"Smaller" - action: @selector(modifyFont:) - keyEquivalent: @"-"]; + action: @selector(modifyFont:) + keyEquivalent: @"-"]; [menuItem setTag: NSSizeDownFontAction]; [menuItem setTarget: self]; } @@ -956,7 +967,7 @@ - (NSMenu*) fontMenu: (BOOL)create */ - (void) setFontMenu: (NSMenu*)newMenu { - ASSIGN(_fontMenu, newMenu); + ASSIGN(_fontMenu, newMenu); } /**

Returns the NSFontPanel, creates it ( if needed ) if create @@ -1053,8 +1064,8 @@ - (NSArray *) collectionNames return [_collections allKeys]; } -- (void) addFontDescriptors: (NSArray *)descriptors - toCollection: (NSString *)collection +- (void) addFontDescriptors: (NSArray *)descriptors + toCollection: (NSString *)collection { NSMutableArray *a = [_collections objectForKey: collection]; @@ -1064,8 +1075,8 @@ - (void) addFontDescriptors: (NSArray *)descriptors } } -- (void) removeFontDescriptor: (NSFontDescriptor *)descriptor - fromCollection: (NSString *)collection +- (void) removeFontDescriptor: (NSFontDescriptor *)descriptor + fromCollection: (NSString *)collection { NSMutableArray *a = [_collections objectForKey: collection]; @@ -1085,8 +1096,8 @@ - (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}; + 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) @@ -1095,96 +1106,96 @@ - (NSDictionary *) convertAttributes: (NSDictionary *)attributes 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; + 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 +- (void) setSelectedAttributes: (NSDictionary *)attributes + isMultiple: (BOOL)flag { ASSIGN(_selectedAttributes, attributes); _multiple = flag; diff --git a/Source/NSFontPanel.m b/Source/NSFontPanel.m index 3b29c87c5..5f531e918 100644 --- a/Source/NSFontPanel.m +++ b/Source/NSFontPanel.m @@ -8,7 +8,7 @@ Date: Febuary 2000 Author: Nicola Pero Date: January 2001 - sizings and resizings - + This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or @@ -23,10 +23,10 @@ You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. - If not, see or write to the - Free Software Foundation, 51 Franklin Street, Fifth Floor, + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ +*/ #include "config.h" #import @@ -56,7 +56,7 @@ static inline void _setFloatValue (NSTextField *field, float size) { - /* If casting size to int and then back to float we get no change, + /* If casting size to int and then back to float we get no change, it means it's an integer */ if ((float)((int)size) == size) { @@ -133,7 +133,7 @@ - (void) resizeWithOldSuperviewSize: (NSSize)oldSize if (_autoresizingMask == NSViewNotSizable) return; - + if (!NSEqualRects(NSZeroRect, _autoresizingFrameError)) { newFrame.origin.x -= _autoresizingFrameError.origin.x; @@ -216,7 +216,7 @@ + (void) initialize } } -/**

Creates ( if needed ) and returns the shared NSFontPanel.

+/**

Creates ( if needed ) and returns the shared NSFontPanel.

*/ + (NSFontPanel*) sharedFontPanel { @@ -246,11 +246,19 @@ - (id) init [self reloadDefaultFontFamilies]; [self _getOriginalSize]; + // Observe font manager notifications to refresh panel when fonts are updated + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(fontManagerDidChangeAvailableFonts:) + name: GSFontManagerAvailableFontsDidChangeNotification + object: nil]; + return self; } - (void) dealloc { + [[NSNotificationCenter defaultCenter] removeObserver: self]; + RELEASE(_panelFont); RELEASE(_familyList); TEST_RELEASE(_faceList); @@ -314,13 +322,19 @@ - (void) reloadDefaultFontFamilies DESTROY(_familyList); _familyList = familyList; - // Reload the display. + // Reload the display. [familyBrowser loadColumnZero]; // Reselect the current font. (Hopefully still there) [self setPanelFont: [fm selectedFont] isMultiple: [fm isMultiple]]; } +- (void) fontManagerDidChangeAvailableFonts: (NSNotification *)notification +{ + // Reload the font families when the font cache is updated + [self reloadDefaultFontFamilies]; +} + - (void) setPanelFont: (NSFont *)fontObject isMultiple: (BOOL)flag { @@ -330,7 +344,7 @@ - (void) setPanelFont: (NSFont *)fontObject ASSIGN(_panelFont, fontObject); _multiple = flag; - + if (fontObject == nil) { return; @@ -353,11 +367,11 @@ - (void) setPanelFont: (NSFont *)fontObject NSBrowser *faceBrowser = [[self contentView] viewWithTag: NSFPFaceBrowser]; NSString *face = @""; unsigned int i; - + // Store style information for font _traits = [fm traitsOfFont: fontObject]; _weight = [fm weightOfFont: fontObject]; - + // Select the row for the font family for (i = 0; i < [_familyList count]; i++) { @@ -376,7 +390,7 @@ - (void) setPanelFont: (NSFont *)fontObject // Select the row for the font face for (i = 0; i < [_faceList count]; i++) { - if ([[[_faceList objectAtIndex: i] objectAtIndex: 0] + if ([[[_faceList objectAtIndex: i] objectAtIndex: 0] isEqualToString: fontName]) break; } @@ -393,7 +407,7 @@ - (void) setPanelFont: (NSFont *)fontObject // Use in preview [previewArea setFont: fontObject]; if (_previewString == nil) - { + { [previewArea setStringValue: [NSString stringWithFormat: @"%@ %@ %d PT", family, face, (int)size]]; } @@ -408,13 +422,13 @@ - (NSFont *) panelConvertFont: (NSFont *)fontObject if (_multiple) { - //TODO: We go over every item in the panel and check if a + //TODO: We go over every item in the panel and check if a // value is selected. If so we send it on to the manager // newFont = [fm convertFont: fontObject toHaveTrait: NSItalicFontMask]; NSLog(@"Multiple font conversion not implemented in NSFontPanel"); newFont = [self _fontForSelection: fontObject]; } - else + else { newFont = [self _fontForSelection: fontObject]; } @@ -423,7 +437,7 @@ - (NSFont *) panelConvertFont: (NSFont *)fontObject { newFont = fontObject; } - + return newFont; } @@ -454,7 +468,7 @@ - (void) setAccessoryView: (NSView*)aView if (aView == _accessoryView) return; - + /* The following code is very tricky. Please think and test a lot before changing it. */ @@ -469,8 +483,8 @@ - (void) setAccessoryView: (NSView*)aView could be a problem. */ [self setMinSize: _originalMinSize]; - /* Resize the panel to the height without the accessory view. - This must be done with the special care of not resizing + /* Resize the panel to the height without the accessory view. + This must be done with the special care of not resizing the heights of the other views. */ addedHeight = accessoryViewFrame.size.height + (_SAVE_PANEL_Y_PAD * 2); contentSize = [[self contentView] frame].size; @@ -480,15 +494,15 @@ - (void) setAccessoryView: (NSView*)aView [self setContentSize: contentSize]; [_topView setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; } - + /* Resize the panel to its original size. This resizes freely the heights of the views. NB: minSize *must* come first */ [self setMinSize: _originalMinSize]; [self setContentSize: _originalSize]; - + /* Set the new accessory view */ _accessoryView = aView; - + /* If there is a new accessory view, plug it in */ if (_accessoryView != nil) { @@ -496,9 +510,9 @@ - (void) setAccessoryView: (NSView*)aView * and its position relative to the bottom of the superview must not * change - so its position rlative to the top must be changable. */ [_accessoryView setAutoresizingMask: NSViewMaxYMargin - | ([_accessoryView autoresizingMask] - & ~(NSViewHeightSizable | NSViewMinYMargin))]; - + | ([_accessoryView autoresizingMask] + & ~(NSViewHeightSizable | NSViewMinYMargin))]; + /* Compute size taken by the new accessory view */ accessoryViewFrame = [_accessoryView frame]; addedHeight = accessoryViewFrame.size.height + (_SAVE_PANEL_Y_PAD * 2); @@ -511,7 +525,7 @@ - (void) setAccessoryView: (NSView*)aView { contentSize.width = accessoryWidth; } - + /* Set new content size without resizing heights of topView, bottomView */ // Our views should resize horizontally if needed, but not vertically [_topView setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin]; @@ -524,7 +538,7 @@ - (void) setAccessoryView: (NSView*)aView contentMinSize.height += addedHeight; // width is more delicate tmpRect = NSMakeRect (0, 0, contentMinSize.width, contentMinSize.height); - tmpRect = [NSWindow contentRectForFrameRect: tmpRect + tmpRect = [NSWindow contentRectForFrameRect: tmpRect styleMask: [self styleMask]]; if (accessoryWidth > tmpRect.size.width) { @@ -541,7 +555,7 @@ - (void) setAccessoryView: (NSView*)aView bottomFrame = [_bottomView frame]; /* AccessoryView */ - accessoryViewFrame.origin.x + accessoryViewFrame.origin.x = (contentSize.width - accessoryViewFrame.size.width) / 2; accessoryViewFrame.origin.y = NSMaxY (bottomFrame) + _SAVE_PANEL_Y_PAD; [_accessoryView setFrameOrigin: accessoryViewFrame.origin]; @@ -586,11 +600,11 @@ - (NSText *) fieldEditor: (BOOL)createFlag && ([anObject tag] == NSFPSizeField)) { if ((sizeFieldText == nil) && createFlag) - { - sizeFieldText = [NSText new]; - [sizeFieldText setUsesFontPanel: NO]; - [sizeFieldText setFieldEditor: YES]; - } + { + sizeFieldText = [NSText new]; + [sizeFieldText setUsesFontPanel: NO]; + [sizeFieldText setFieldEditor: YES]; + } return sizeFieldText; } @@ -639,9 +653,9 @@ - (id) _initWithoutGModel NSBox *slash; unsigned int style = NSTitledWindowMask | NSClosableWindowMask - | NSResizableWindowMask | NSUtilityWindowMask; + | NSResizableWindowMask | NSUtilityWindowMask; - self = [super initWithContentRect: contentRect + self = [super initWithContentRect: contentRect styleMask: style backing: NSBackingStoreRetained defer: YES @@ -662,7 +676,7 @@ - (id) _initWithoutGModel _topView = topArea; splitView = [[NSSplitView alloc] initWithFrame: splitViewRect]; - [splitView setVertical: NO]; + [splitView setVertical: NO]; [splitView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)]; topSplit = [[NSView alloc] initWithFrame: topSplitRect]; @@ -869,7 +883,7 @@ - (id) _initWithoutGModel [self setInitialFirstResponder: setButton]; [self setBecomesKeyOnlyIfNeeded: YES]; - + return self; } @@ -896,12 +910,12 @@ - (void) _doPreview } if (_previewString == nil) - { + { NSTextField *sizeField = [[self contentView] viewWithTag: NSFPSizeField]; float size = [sizeField floatValue]; NSString *faceName; NSString *familyName; - + if (size == 0 && font != nil) { size = [font pointSize]; @@ -924,7 +938,7 @@ - (void) _doPreview } [previewArea setStringValue: [NSString stringWithFormat: @"%@ %@ %d PT", familyName, faceName, (int)size]]; - } + } } - (void) ok: (id)sender @@ -941,7 +955,7 @@ - (void) cancel: (id)sender /* * The cancel button has been pushed - * we should reset the items in the panel + * we should reset the items in the panel */ [self setPanelFont: _panelFont isMultiple: _multiple]; @@ -993,7 +1007,7 @@ the delegate has refused all fonts (so that our family and face lists else return nil; } - + // FIXME: We should check if the font is correct return [NSFont fontWithName: fontName size: size]; } @@ -1012,15 +1026,15 @@ -(void) _trySelectSize: (float)size sizeField = [[self contentView] viewWithTag: NSFPSizeField]; _setFloatValue (sizeField, size); } - - /* Make sure our column is loaded. */ + + /* Make sure our column is loaded. */ [sizeBrowser loadColumnZero]; for (i = 0; i < sizeof(sizes) / sizeof(float); i++) { if (size == sizes[i]) { - /* select the cell */ + /* select the cell */ [sizeBrowser selectRow: i inColumn: 0]; break; } @@ -1109,7 +1123,7 @@ - (void) _familySelectionChanged: (id)sender NSMutableArray *faceList; entireFaceList = [fm availableMembersOfFontFamily: - [_familyList objectAtIndex: row]]; + [_familyList objectAtIndex: row]]; faceList = [[NSMutableArray alloc] initWithCapacity: [entireFaceList count]]; @@ -1202,7 +1216,7 @@ - (void) _sizeSelectionChanged: (id)sender sizeField = [[self contentView] viewWithTag: NSFPSizeField]; _setFloatValue (sizeField, sizes[row]); - + [self _doPreview]; } @@ -1211,7 +1225,7 @@ - (NSInteger) browser: (NSBrowser*)sender numberOfRowsInColumn: (NSInteger)colu { switch ([sender tag]) { - case NSFPFamilyBrowser: + case NSFPFamilyBrowser: { return [_familyList count]; } @@ -1249,9 +1263,9 @@ - (NSString*) browser: (NSBrowser*)sender titleOfColumn: (NSInteger)column } } -- (void) browser: (NSBrowser *)sender - willDisplayCell: (id)cell - atRow: (NSInteger)row +- (void) browser: (NSBrowser *)sender + willDisplayCell: (id)cell + atRow: (NSInteger)row column: (NSInteger)column { NSString *value = nil; @@ -1274,7 +1288,7 @@ - (void) browser: (NSBrowser *)sender if ([_faceList count] > (NSUInteger)row) { value = [[_faceList objectAtIndex: row] objectAtIndex: 1]; - } + } break; } case NSFPSizeBrowser: @@ -1283,12 +1297,12 @@ - (void) browser: (NSBrowser *)sender value = [NSString stringWithFormat: @"%d", (int) sizes[row]]; } } - + [cell setStringValue: value]; [cell setLeaf: YES]; } -- (BOOL) browser: (NSBrowser *)sender +- (BOOL) browser: (NSBrowser *)sender isColumnValid: (NSInteger)column; { return NO; @@ -1298,8 +1312,8 @@ - (BOOL) browser: (NSBrowser *)sender @implementation NSFontPanel (NSSplitViewDelegate) -- (void) splitView: (NSSplitView *)splitView -constrainMinCoordinate: (CGFloat *)min +- (void) splitView: (NSSplitView *)splitView +constrainMinCoordinate: (CGFloat *)min maxCoordinate: (CGFloat *)max ofSubviewAt: (NSInteger)offset {