/**
Each plugin is represented by an NSMutableDictionary to which you can add your own entries as needed. The keys PKPaneRegistry adds to this dictionary are:
Returns PKPaneRegistry shared instance (singleton).
*/ + (id) sharedRegistry { if (sharedPluginRegistry == nil) { sharedPluginRegistry = [[PKPaneRegistry alloc] init]; } return sharedPluginRegistry; } /* First test to run */ #ifdef HAVE_UKTEST - (void) test_Init { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); UKNotNil(paths); UKTrue([paths count] >= 1); } #endif - (id) init { self = [super init]; if (self == nil) return nil; plugins = [[NSMutableArray alloc] init]; fm = [NSFileManager defaultManager]; instantiate = YES; return self; } - (void) dealloc { DESTROY(plugins); [super dealloc]; } #ifdef HAVE_UKTEST - (void) testLoadPluginOfType { NSBundle *bundle = [NSBundle mainBundle]; NSDictionary *info = [bundle infoDictionary]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); UKNotNil(bundle); UKNotNil(info); NSString *supportDir = [[paths objectAtIndex: 0] stringByAppendingPathComponent: APPLICATION_SUPPORT]; BOOL isDir; UKTrue([fm fileExistsAtPath: supportDir isDirectory:&isDir]); UKTrue(isDir); } #endif // FIXME: Implement UTI check support for type parameter. /**Locates and loads plugin bundles with extension ext.
Normally this is the only method you need to call to load a plugin.
*/ - (void) loadPluginsOfType: (NSString *)ext { NSBundle *bundle = [NSBundle mainBundle]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); NSEnumerator *e = [paths objectEnumerator]; NSString *path = nil; NSString *appName = [[bundle infoDictionary] objectForKey: @"NSExecutable"]; if (appName == nil) appName = [[bundle infoDictionary] objectForKey: @"CFBundleExecutable"]; NSString *pluginsDir = [[APPLICATION_SUPPORT stringByAppendingPathComponent: appName] stringByAppendingPathComponent: @"PlugIns"]; while ((path = [e nextObject]) != nil) { [self loadPluginsFromPath: [path stringByAppendingPathComponent: pluginsDir] ofType: ext]; } [self loadPluginsFromPath: [bundle builtInPlugInsPath] ofType: ext]; } // FIXME: Implement UTI check support for type parameter. /**Finds plugins within folder path which are identified by an extension matching ext. Finally loads these plugins by calling -loadPluginForPath:.
*/ - (void) loadPluginsFromPath: (NSString *)folder ofType: (NSString *)ext { NSDirectoryEnumerator *e = [fm enumeratorAtPath: folder]; NSString *fileName = nil; while ((fileName = [e nextObject]) != nil ) { [e skipDescendents]; /* Ignore subfolders and don't search in packages. */ /* Skip invisible files: */ if ([fileName characterAtIndex: 0] == '.') continue; /* Only process ones that have the right suffix: */ if ([[fileName pathExtension] isEqualToString: ext] == NO) continue; NS_DURING /* Get path, bundle and display name: */ NSString *path = [folder stringByAppendingPathComponent: fileName]; [self loadPluginForPath: path]; NS_HANDLER NSLog(@"Error while listing PrefPane: %@", localException); NS_ENDHANDLER } } #ifdef HAVE_UKTEST - (void) testLoadPluginForPath { NSArray *paths; NSString *path; #ifdef GNUstep paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, YES); path = [[paths objectAtIndex: 0] stringByAppendingPathComponent: @"PreferencePanes/PrefPaneExample.prefPane"]; #else paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSSystemDomainMask, YES); path = [[paths objectAtIndex: 0] stringByAppendingPathComponent: @"PreferencePanes/Appearance.prefPane"]; #endif NSBundle *bundle = [NSBundle bundleWithPath: path]; NSDictionary *info = [bundle infoDictionary]; int lastCount = [plugins count]; BOOL isDir; UKTrue([fm fileExistsAtPath: path isDirectory: &isDir]); UKNotNil(bundle); UKNotNil(info); //UKTrue([[info allKeys] containsObject: NAME_ENTRY]); [self loadPluginForPath: path]; UKTrue(instantiate); UKIntsEqual([plugins count], lastCount + 1); #if 0 UKTrue([[pluginPaths allKeys] containsObject: path]); info = [pluginPaths objectForKey: path]; UKNotNil(info); UKTrue([[info allKeys] containsObject: @"bundle"]); UKTrue([[info allKeys] containsObject: @"image"]); UKTrue([[info allKeys] containsObject: @"name"]); UKTrue([[info allKeys] containsObject: @"path"]); UKTrue([[info allKeys] containsObject: @"class"]); #endif } #endif /**Loads the plugin bundle located at path, checks it conforms to Plugin schema stored in the related bundle property list.
Every property list values associated to Plugin schema are put in a dictionary which represents a plugin object; eventual validity errors may be reported each time a value is read in NSBundle description values returned by -infoDictionary.
*/ - (NSMutableDictionary *) loadPluginForPath: (NSString *)path { /* Find existed one */ NSMutableDictionary *info = [[self loadedPlugins] objectWithValue: path forKey: @"path"];; /* Not found */ NSBundle *bundle = [NSBundle bundleWithPath: path]; NSString *identifier; NSImage *image; NSString *name; /* We retrieve plugin's name */ name = [[bundle infoDictionary] objectForKey: @"CFBundleName"]; if (name == nil) name = [[bundle infoDictionary] objectForKey: @"ApplicationName"]; if (name == nil) name = [[bundle infoDictionary] objectForKey: @"NSExecutable"]; if (name == nil) name = @"Unknown"; /* We retrieve plugin's identifier */ identifier = [bundle bundleIdentifier]; if (identifier == nil) { NSLog(@"Plugin %@ is missing an identifier, it may be impossible to use it.", name); identifier = path; /* When no identifier is available, falling back on path otherwise. */ } /* Get icon, falling back on file icon when needed, or in worst case using our app icon: */ NSString *iconFileName = [[bundle infoDictionary] objectForKey: @"NSPrefPaneIconFile"]; NSString *iconPath = nil; if(iconFileName == nil) iconFileName = [[bundle infoDictionary] objectForKey: @"NSIcon"]; if(iconFileName == nil) iconFileName = [[bundle infoDictionary] objectForKey: @"ApplicationIcon"]; if(iconFileName == nil) iconFileName = [[bundle infoDictionary] objectForKey: @"CFBundleIcon"]; if (iconFileName != nil) iconPath = [bundle pathForImageResource: iconFileName]; if (iconPath == nil) { image = [NSImage imageNamed: @"NSApplicationIcon"]; } else { image = [[[NSImage alloc] initWithContentsOfFile: iconPath] autorelease]; } /* When image loading has failed, we skip image. */ /* Add a new entry for this pane to our list: */ info = [NSMutableDictionary dictionaryWithObjectsAndKeys: bundle, @"bundle", identifier, @"identifier", name, @"name", path, @"path", [NSValue valueWithPointer: [bundle principalClass]], @"class", nil]; if (image) { [info setObject: image forKey: @"image"]; } if (instantiate) { id obj = [[[bundle principalClass] alloc] init]; [info setObject: AUTORELEASE(obj) forKey: @"instance"]; } [plugins addObject: info]; return info; } #ifdef HAVE_UKTEST - (void) testLoadedPlugins { //UKNotNil([self loadedPlugins]); } #endif /**Returns the currently registered plugins (loaded by the way).
Nil
is returned when no plugins have been registered.
Returns instantiate
value.
Read -setInstantiate: documentation to learn more.
*/ - (BOOL) instantiate { return instantiate; } /**Sets instantiate value to YES if you want to have plugins main class automatically instantiated when they are loaded.
*/ - (void) setInstantiate: (BOOL)n { instantiate = n; } /**Loads the plugin bundle located at path, checks it conforms to Plugin schema stored in the related bundle property list.
Every property list values associated to Plugin schema are put in a dictionary to be used as plugin object, eventual validity errors are reported each time a value is read in NSBundle description values returned by -infoDictionary.
*/ - (id) paneAtPath: (NSString *) path { NSMutableDictionary *info = [[self loadedPlugins] objectWithValue: path forKey : @"path"]; /* We check whether the plugin is already loaded. When it isn't, we try to load it. */ // NOTE: We may check plugin conforms to preference pane schema. In case of // invalidity, it would be reloaded. For now, we only check the plugin // availability. if (info == nil) info = [self loadPluginForPath: path]; id pane = [info objectForKey: @"instance"]; if (pane == nil) { NSString *type = [[info objectForKey: @"path"] pathExtension]; Class mainClass = [[info objectForKey: @"class"] pointerValue]; pane = [[[mainClass alloc] initWithBundle: [info objectForKey: @"bundle"]] autorelease]; [info setObject: pane forKey: @"instance"]; } /* Make sure the main view is loaded */ if ([pane mainView] == nil) [pane loadMainView]; return pane; } - (id) paneWithIdentifier: (NSString *) identifier { NSMutableDictionary *plugin = [[self loadedPlugins] objectWithValue: identifier forKey: @"identifier"]; id instance = [plugin objectForKey: @"instance"]; if (instance) { return instance; } return [self paneAtPath: [plugin objectForKey: @"path"]]; } - (void) addPlugin: (NSDictionary *) dict { // FIXME: Should we check each value to be valid ? if ([dict objectForKey: @"identifier"] == nil) return; NSString *identifier = [dict objectForKey: @"identifier"]; if (identifier) { [plugins addObject: dict]; } } @end