FontCase Developers
- Create Your Own
- Examples
- Additional Code
Writing plug-ins for FontCase
FontCase can be extended with user-written plug-ins very easily. Plugins need to be written in Cocoa and are intended to act upon the current selection (although they could do significantly more if you wish to). And what's more, you can get a free license key to FontCase if you write a plug-in. This document will explain how FontCase is structured and what tools you can use to interact with FontCase' data. Every plug-in is intended to perform one action which can be invoked through the Fonts or Tools menu:
Cocoa and other Frameworks
As noted above, plug-ins need to be written in Cocoa so you should at least be familiar with it. Also, FontCase uses Apple's robust Core Data-framework to manage the data and any plug-in will have to deal with this, so familiarity with this would also be a helpful. Note that a FontCase plug-in gives you a lot of power so if you don't know what you're doing, you might end up corrupting the users' database. That said, FontCase provides a few helper methods which should make interacting with the data peanuts.
Tutorial: Creating a basic Plug-in
As noted above, plug-ins need to be written in Cocoa so you should at least be familiar with it. Also, FontCase uses Apple's robust Core Data-framework to manage the data and any plug-in will have to deal with this, so familiarity with this would also be a helpful. Note that a FontCase plug-in gives you a lot of power so if you don't know what you're doing, you might end up corrupting the users' database. That said, FontCase provides a few helper methods which should make interacting with the data peanuts.
You need to have Xcode Tools installed and you should be familiar with them, or this will probably go too fast for you.
- Start out with a new project and chooose Bundle -> Cocoa Bundle.
- Import the FontCasePluginKit-framework (click to download) but do not copy the items to the destination folder
- Then create a new class, import the FontCasePluginKit and make it a subclass of FCActionPlugin:
FCActionPlugin:
#import
#import
@interface SomePlugin : FCActionPlugin
{
}
@end
- Then go to the implementation file and overwrite (some of) these methods:
- (NSString *)displayName;
- (NSString *)keyEquivalent;
- (NSString *)keyEquivalentModifierMask;
- (int)displayMenuPosition;
- (IBAction)performActionOnFonts:(NSArray *)variations
context:(NSManagedObjectContext *)context;
- Then go to the implementation file and overwrite (some of) these methods:
- You need to overwrite the -displayName method, this is the name that will show up in the fonts-menu, so return something that makes sense.
- . If you want to provide a shortcut for your action, overwrite keyEquivalent and keyEquivalentModifierMask but if not, you don't have to overwrite these methods.
- The performActionOnFonts:context will be called whenever a user clicks your menu-item. The variations-argument are the variations the user had selected when they clicked your action and the context-argument is the central managedObjectContext you can use to add new entities such as tags, designers, collections and foundries. FontCase declares a very handy category on NSManagedObjectContext so that might be handy for you. Take a look at the Adding new entities-page for the header.
- If you're making big changes, you might want to update the interface. You can call the -sendUpdate method of FCActionPlugin.
- If you want your plugin to appear in the 'Tools' menu instead of the 'Fonts' menu, overwrite - (int)displayMenuPosition and return FCDisplayInToolsMenu instead.
- If your plugin contains more than one class you'll have to provide a principal-class in the info.plist
- For FontCase to recognise your plugin as a FontCase-plugin,select the target in the sourcelist, press cmd+I, search for Extension and change the 'Wrapper Extension' from 'bundle' to 'fcplugin'.
Some Examples
You can take a look at these samples if you're interested in developing your own plug-ins. These plug-ins can be downloaded, both the source and the working version.
New Collection from Selection
This is a very basic action; it takes the current selection and puts that in a new collection. The code is also very simple. Basically you need two lines of code for the real work. Note the use of the createEntity:withUniqueValueForKey:defaultValue:, this is one of the methods declared in FontCase and it works very nice; it starts out with the name 'New Collection', but if there already is Collection with that name, it will take 'New Collection 1', 'New Collection 2', until it finds a name not yet taken.
@implementation FCCollectionFromSelection
- (NSString *)displayName
{
return @"New Collection from Selection";
}
- (IBAction)performActionOnFonts:(NSArray *)variations
context:(NSManagedObjectContext *)context
{
NSManagedObject *collection = [context createEntity:@"Collection"
withUniqueValueForKey:@"name"
defaultValue:@"New Collection"];
[[collection mutableSetValueForKey:@"variations"]
addObjectsFromArray:variations];
[self sendUpdate];
}
- (NSString *)keyEquivalent
{
return @"N";
}
- (NSInteger)keyEquivalentModifierMask
{
return NSAlternateKeyMask | NSCommandKeyMask | NSShiftKeyMask;
}
@end
Download the source file here:collectionfromselection.zip
Clear Cache for Selection
This action clears the cache and tells the grid to reload again. The Variation-entity implements the IKImageBrowserItem protocol and these methods are declared in FCVariation to clear the cache programatically:
@implementation FCClearCache
- (NSString *)displayName
{
return @"Clear Cache for Selection";
}
- (IBAction)performActionOnFonts:(NSArray *)variations
context:(NSManagedObjectContext *)context
{
for (NSManagedObject *var in variations) {
[var clearCache];
[var updateVersion];
}
[self sendUpdate];
}
@end
Download the source file here: clearcache.zip
FontCase' Data Structure
The central class or entity in FontCase is a Variation. The schema below should make the structure clear for those who are familiar with CoreData:
As you can see, the central component of the entire system is the 'Variation'-entity. You should also note that apart from the name, there's basically no difference between a tag, a designer or a collection, which makes it possible for FontCase to be incredibly flexible.
Basically everything you need is in this schema, and you can use key-value-coding to change any of these properties. However also included in the plugin-framework is the header file of class that is used internally to represent a variations which supports a few more actions like activating and loading fonts.
Convenience Methods
While you can use NSEntityDescription to insert new entities, we advice you to use the methods we declared as a category on NSManagedObjectContext because they provide extra functionality and give you a lot of nice things for free. This header is also included in the framework and is also printed below:
@interface NSManagedObjectContext (CDManagedObjectContextExtensions)
- (BOOL)entityWithNameExists:(NSString*)entityName
whereKey:(NSString*)key
like:(NSString*)value;
- (id)entityWithName:(NSString*)entityName
whereKey:(NSString*)key
like:(NSString*)value;
- (id)retrieveOrCreateEntityWithName:(NSString*)entityName
whereKey:(NSString*)key
like:(NSString*)value;
- (id)createEntity:(NSString*)entityName
withUniqueValueForKey:(NSString*)key
defaultValue:(NSString*)def;
- (id)createEntity:(NSString*)entityName
whereKey:(NSString*)key
like:(NSString*)value;
- (int)numberOfEntitiesWithName:(NSString*)entityName;
- (int)numberOfEntitiesWithName:(NSString*)entityName
where:(NSString*)key
like:(NSString*)value;
- (NSArray*)entitiesWithName:(NSString*)entityName;
- (NSArray*)entitiesWithName:(NSString*)entityName
where:(NSString*)key
like:(NSString*)value;
- (BOOL)deleteEntitiesWithName:(NSString*)entityName
withEmptyRelationshipForKey:(NSString*)key;
@end
Download the source file here: collectionfromselection.zip
Variation header file
Below you'll find the complete interface-file for the Variations entity. It may seem a lot but look at it from the upside; you won't need anything, but if you need something special, it's there.
@interface FCVariation : NSManagedObject
{
}
@property (retain) NSString *copyright, *familyName, *generator, *kind, *postscriptName;
@property (retain) NSString *location, *name, *notes, *price;
@property (retain) NSString *specimen, *supportFile, *version;
@property (retain) NSNumber *fixedWidth, *rating, numberOfActivations;
@property (retain) NSDate *lastActivationDate, *importDate;
@property (retain) NSManagedObject * foundry;
@property (retain) NSSet *genres, *tags, *designers, *collections;
- (NSString *)displayName;
- (NSString *)fullLocation;
- (NSString *)fullSupportFileLocation;
- (NSArray *)copyToFolder:(NSString *)folderPath;
- (BOOL)isSystemFont;
@end
@interface FCVariation (AccessorsStringAccess)
@property (retain) NSString *foundryName;
//the names of these entities, separated by komma's (note the setter works as well)
@property (retain) NSString *designerNames;
@property (retain) NSString *tagNames;
@property (retain) NSString *genreNames;
- (int)numberOfTags;
- (int)numberOfGenres;
- (int)numberOfCollections;
- (NSString *)isSystemFontTitle;
- (NSString *)hasSpecimenTitle;
@end
@interface FCVariation (FCSpecimenExtensions)
- (void)openSpecimen;
- (void)deleteSpecimen;
- (void)addSpecimen:(NSString *)filename;
@end
@interface FCVariation (CoreDataGeneratedAccessors)
- (void)addCollectionsObject:(NSManagedObject *)value;
- (void)removeCollectionsObject:(NSManagedObject *)value;
- (void)addCollections:(NSSet *)value;
- (void)removeCollections:(NSSet *)value;
- (void)addDesignersObject:(NSManagedObject *)value;
- (void)removeDesignersObject:(NSManagedObject *)value;
- (void)addDesigners:(NSSet *)value;
- (void)removeDesigners:(NSSet *)value;
- (void)addGenresObject:(NSManagedObject *)value;
- (void)removeGenresObject:(NSManagedObject *)value;
- (void)addGenres:(NSSet *)value;
- (void)removeGenres:(NSSet *)value;
- (void)addTagsObject:(NSManagedObject *)value;
- (void)removeTagsObject:(NSManagedObject *)value;
- (void)addTags:(NSSet *)value;
- (void)removeTags:(NSSet *)value;
@end
@interface NSObject (QueryExtensions)
- (BOOL)isVariation;
- (BOOL)isLibrary;
- (BOOL)isSmartCollection;
@end
@interface NSManagedObject (NSManagedObjectCompare)
- (NSComparisonResult)caseInsensitiveCompare:(id)other; //compare by 'name'
@end
@interface FCVariation (VariationsActivationExtensions)
- (BOOL)isLoaded;
- (void)load;
- (BOOL)isActivated;
- (void)setIsActivated:(BOOL)flag;
- (void)activate;
- (BOOL)deactivate;
- (NSFont *)asFontWithSize:(CGFloat)size; //creating a font directly out of a variation
@end