// Created by Mac-arena the Bored Zo on 2005-10-30. // Copyright 2005 Adium Team. All rights reserved. #import "AXCIconPackDocument.h" #import "NSMutableArrayAdditions.h" #import "NSMenu+ImmediatePopulation.h" #import "AXCIconPackEntry.h" //columns of the icon keys outline view. #define KEY_COLUMN_NAME @"key" #define RESOURCE_COLUMN_NAME @"file" @implementation AXCIconPackDocument if (( self = [ super init ])) { categoryNames = [[ self categoryNames ] copy ]; NSMutableDictionary * temp = [[ NSMutableDictionary alloc ] initWithCapacity : [ categoryNames count ]]; NSEnumerator * categoryNamesEnum = [ categoryNames objectEnumerator ]; while (( categoryName = [ categoryNamesEnum nextObject ])) [ temp setObject : [[[ self entriesForNewDocumentInCategory : categoryName ] mutableCopy ] autorelease ] forKey : categoryName ]; categoryStorage = [ temp copy ]; [ categoryStorage release ]; #pragma mark Document nature - ( void ) windowControllerDidLoadNib: ( NSWindowController * ) controller [ super windowControllerDidLoadNib : controller ]; [[[[ iconPlistView tableColumnWithIdentifier : RESOURCE_COLUMN_NAME ] dataCell ] menu ] populateFromDelegate ]; - ( BOOL ) writeToFile: ( NSString * ) path ofType: ( NSString * ) docType BOOL success = [ super writeToFile : path ofType : docType ]; NSMutableDictionary * iconsPlist = [ NSMutableDictionary dictionaryWithCapacity : [ categoryStorage count ]]; NSEnumerator * categoryNamesEnum = [ categoryStorage keyEnumerator ]; while (( categoryName = [ categoryNamesEnum nextObject ])) { NSArray * categoryArray = [ categoryStorage objectForKey : categoryName ]; NSMutableDictionary * categoryPlist = [ NSMutableDictionary dictionaryWithCapacity : [ categoryArray count ]]; NSEnumerator * categoryEnum = [ categoryArray objectEnumerator ]; while (( entry = [ categoryEnum nextObject ])) { NSString * resourcePath = [[ entry path ] lastPathComponent ]; [ categoryPlist setObject : resourcePath forKey : [ entry key ]]; [ iconsPlist setObject : categoryPlist forKey : categoryName ]; NSString * iconsPlistPath = [[[ path stringByAppendingPathComponent : @"Contents" ] stringByAppendingPathComponent : @"Resources" ] stringByAppendingPathComponent : @"Icons.plist" ]; success = [ iconsPlist writeToFile : iconsPlistPath atomically : NO ]; - ( BOOL ) readFromFile: ( NSString * ) path ofType: ( NSString * ) type BOOL success = [ super readFromFile : path ofType : type ]; [ self removeResource : @"Icons.plist" ]; NSDictionary * iconsPlist = [ NSDictionary dictionaryWithContentsOfFile : [ bundle pathForResource : @"Icons" ofType : @"plist" ]]; categoryNames = [[ iconsPlist allKeys ] retain ]; NSMutableDictionary * storage = [[ NSMutableDictionary alloc ] initWithCapacity : [ categoryNames count ]]; NSEnumerator * categoryNamesEnum = [ categoryNames objectEnumerator ]; while (( categoryName = [ categoryNamesEnum nextObject ])) { NSDictionary * category = [ iconsPlist objectForKey : categoryName ]; NSMutableArray * entries = [[ NSMutableArray alloc ] initWithCapacity : [ category count ]]; NSEnumerator * categoryKeysEnum = [ category keyEnumerator ]; while (( key = [ categoryKeysEnum nextObject ])) { NSString * iconPath = [ category objectForKey : key ]; if ([ resourcesSet containsObject : iconPath ]) { AXCIconPackEntry * entry = [[ AXCIconPackEntry alloc ] initWithKey : key path : iconPath ]; [ entries addObject : entry ]; NSLog ( @"Error while loading %@: Icons.plist contains a key (%@) in category %@ whose resource path (%@) does not exist in this bundle" , path , key , categoryName , iconPath ); [ storage setObject : entries forKey : categoryName ]; [ categoryStorage release ]; categoryStorage = [ storage retain ]; //use this, NOT -categoryNames, for bindings. - ( NSArray * ) categoryNamesArray #pragma mark Outline view data source conformance - ( id ) outlineView: ( NSOutlineView * ) outlineView child: ( int ) idx ofItem: ( id ) item if ( ! item ) //return a category name return [ categoryNames objectAtIndex : idx ]; else //return category storage return [[ categoryStorage objectForKey : item ] objectAtIndex : idx ]; - ( BOOL ) outlineView: ( NSOutlineView * ) outlineView isItemExpandable: ( id ) item return ([ categoryStorage objectForKey : item ] != nil ); - ( int ) outlineView: ( NSOutlineView * ) outlineView numberOfChildrenOfItem: ( id ) item return [ categoryNames count ]; NSDictionary * storage = [ categoryStorage objectForKey : item ]; - ( id ) outlineView: ( NSOutlineView * ) outlineView objectValueForTableColumn: ( NSTableColumn * ) col byItem: ( id ) item BOOL isKeyColumn = [ KEY_COLUMN_NAME isEqualToString : [ col identifier ]]; unsigned categoryIndex = [ categoryNames indexOfObjectIdenticalTo : item ]; if ( categoryIndex != NSNotFound ) return isKeyColumn ? item : [ NSNumber numberWithInt : -1 ]; return isKeyColumn ? ( NSObject * )[ item key ] : ( NSObject * )[ NSNumber numberWithUnsignedInt : [ resources indexOfObject : [ item path ]]]; - ( void ) outlineView: ( NSOutlineView * ) outlineView setObjectValue: ( id ) newValue forTableColumn: ( NSTableColumn * ) col byItem: ( id ) item int index = [( NSNumber * ) newValue intValue ]; [( AXCIconPackEntry * ) item setPath : [ resources objectAtIndex : index ]]; [( AXCIconPackEntry * ) item setPath : nil ]; - ( NSDragOperation ) outlineView: ( NSOutlineView * ) outlineView validateDrop: ( id < NSDraggingInfo > ) info proposedItem: ( id ) item proposedChildIndex: ( int ) index NSArray * plist = [[ info draggingPasteboard ] propertyListForType : NSFilenamesPboardType ]; if ([ item isKindOfClass : [ AXCIconPackEntry class ]] //it's an icon pack entry && ([ plist count ] == 1 ) //the user is only dragging one file && ( index == NSOutlineViewDropOnItemIndex ) //we're dropping onto the entry return NSDragOperationLink ; return NSDragOperationNone ; - ( BOOL ) outlineView: ( NSOutlineView * ) outlineView acceptDrop: ( id < NSDraggingInfo > ) info item: ( id ) item childIndex: ( int ) index [( AXCIconPackEntry * ) item setPath : [[[ info draggingPasteboard ] propertyListForType : NSFilenamesPboardType ] objectAtIndex : 0 ]]; #pragma mark NSOutlineView delegate conformance - ( void ) outlineView: ( NSOutlineView * ) outlineView willDisplayCell: ( id ) cell forTableColumn: ( NSTableColumn * ) tableColumn item: ( id ) item { if ([ cell isKindOfClass : [ NSPopUpButtonCell class ]]) { if ([ categoryNames containsObject : item ]) { [ cell setMenu : emptyMenu ]; [ cell setArrowPosition : NSPopUpNoArrow ]; //hide arrow for categories [ cell setMenu : menuWithResourceFiles ]; [ cell setArrowPosition : NSPopUpArrowAtBottom ]; //show arrow for item pairs //we have to do this because of an NSMenu bug. //http://www.corbinstreehouse.com/blog/archives/2005/07/dynamically_pop.html NSMenu * menu = [ cell menu ]; [ menu populateFromDelegate ]; //this is lame but necessary too. [ cell selectItemAtIndex : -1 ]; else //because we just changed the menu of the cell... [ cell selectItemAtIndex : [ resources indexOfObject : [ item path ]]]; /*keep the outline view from changing the width of the Key column (thereby * hiding the pop-up menu arrows and showing a scroll-bar) whenever the user expands or collapses a category. - ( void ) restoreKeyColumnMaxWidth [[ iconPlistView tableColumnWithIdentifier : KEY_COLUMN_NAME ] setMaxWidth : previousColumnMaxWidth ]; - ( void ) outlineViewItemWillExpand: ( NSNotification * ) notification NSTableColumn * col = [ iconPlistView tableColumnWithIdentifier : KEY_COLUMN_NAME ]; previousColumnMaxWidth = [ col maxWidth ]; [ col setMaxWidth : [ col width ]]; - ( void ) outlineViewItemDidExpand: ( NSNotification * ) notification [ self performSelector : @selector ( restoreKeyColumnMaxWidth ) withObject : nil afterDelay : 0.05 ]; #pragma mark NSMenu delegate conformance - ( int ) numberOfItemsInMenu: ( NSMenu * ) menu return [ resources count ]; - ( BOOL ) menu: ( NSMenu * ) menu updateItem: ( NSMenuItem * ) item atIndex: ( int ) index shouldCancel: ( BOOL ) shouldCancel NSString * path = [ resources objectAtIndex : index ]; [ item setTitle : [ displayNames objectForKey : path ]]; NSImage * image = [[ imagePreviews objectForKey : path ] copy ]; #pragma mark Implementation of Xtra-document methods - ( NSArray * ) validResourceTypes return [ NSImage imageFileTypes ]; - ( NSArray * ) tabViewItems [ NSBundle loadNibNamed : @"IconPack_IconPlistView" owner : self ]; NSTabViewItem * tvi = [[ NSTabViewItem alloc ] initWithIdentifier : @"IconPlist" ]; [ tvi setView : topLevelView ]; [ tvi setLabel : @"Icon keys" ]; //XXX LOCALIZEME tabViewItems = [[ NSArray alloc ] initWithObjects :& tvi count : 1 ]; #pragma mark Implementation of icon-pack abstract methods - ( NSArray * ) categoryNames - ( NSArray * ) entriesInCategory: ( NSString * ) categoryName return [ categoryStorage objectForKey : categoryName ]; - ( NSArray * ) entriesForNewDocumentInCategory: ( NSString * ) categoryName