#import "OutlineViewDelegate.h" #import "Resource.h" #import "ResourceDocument.h" #import "ResourceDataSource.h" #import "ResourceNameCell.h" #import "ApplicationDelegate.h" @implementation OutlineViewDelegate - (id)init { self = [super init]; if(!self) return nil; if(NSAppKitVersionNumber >= 700.0) // darwin 7.0 == Mac OS 10.3, needed for -setPlaceholderString: { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceNameDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceTypeDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceholder:) name:ResourceIDDidChangeNotification object:nil]; } return self; } - (void)updatePlaceholder:(NSNotification *)notification { Resource *resource = [notification object]; ResourceNameCell *cell = (ResourceNameCell *) [[outlineView tableColumnWithIdentifier:@"name"] dataCellForRow:[outlineView rowForItem:resource]]; if([[resource name] isEqualToString:@""]) { if([[resource resID] shortValue] == -16455) [cell setPlaceholderString:NSLocalizedString(@"Custom Icon", nil)]; else [cell setPlaceholderString:NSLocalizedString(@"Untitled Resource", nil)]; } } /*! @method tableView:didClickTableColumn: @pending not needed in 10.3+, use existing sort functionality */ - (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn { NSArray *newResources; NSArray *oldResources = [(ResourceDataSource *)[tableView dataSource] resources]; // sort the array NSImage *indicator = [tableView indicatorImageInTableColumn:tableColumn]; NSImage *upArrow = [NSTableView _defaultTableHeaderSortImage]; if(indicator == upArrow) newResources = [oldResources sortedArrayUsingFunction:compareResourcesAscending context:[tableColumn identifier]]; else newResources = [oldResources sortedArrayUsingFunction:compareResourcesDescending context:[tableColumn identifier]]; // swap new array for old one [(ResourceDataSource *)[tableView dataSource] setResources:[NSMutableArray arrayWithArray:newResources]]; [tableView reloadData]; } /*! @function compareResourcesAscending @change 2003-10-25 NGS: now uses KVC methods to obtain the strings to compare */ int compareResourcesAscending(Resource *r1, Resource *r2, void *context) { NSString *key = (NSString *)context; // compare two NSStrings (case-insensitive) if([key isEqualToString:@"name"] || [key isEqualToString:@"type"]) return [(NSString *)[r1 valueForKey:key] caseInsensitiveCompare:(NSString *)[r2 valueForKey:key]]; // compare two NSNumbers (or any other class) else return [(NSNumber *)[r1 valueForKey:key] compare:(NSNumber *)[r2 valueForKey:key]]; } /*! @function compareResourcesDescending @change 2003-10-25 NGS: now uses KVC methods to obtain the strings to compare */ int compareResourcesDescending(Resource *r1, Resource *r2, void *context) { NSString *key = (NSString *)context; // compare two NSStrings (case-insensitive) if([key isEqualToString:@"name"] || [key isEqualToString:@"type"]) return -1 * [(NSString *)[r1 valueForKey:key] caseInsensitiveCompare: (NSString *)[r2 valueForKey:key]]; // compare two NSNumbers (or any other class) else return -1 * [(NSNumber *)[r1 valueForKey:key] compare: (NSNumber *)[r2 valueForKey:key]]; } - (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item { if([[tableColumn identifier] isEqualToString:@"size"] || [[tableColumn identifier] isEqualToString:@"attributes"]) return NO; else return YES; } /*! @method outlineView:willDisplayCell:forTableColumn:item: @change 2003-10-25 NGS: Moved functionality of NameFormatter into this method, removed NameFormatter class. @change 2003-10-24 NGS: Swapped row colours so first row is white (as per 10.3), conditionalised drawing line background colours to system versions < 10.3, since in 10.3 it is handled by the nib file. @change 2003-10-24 NGS: Added iconForResourceType method to app delegate instead of interrogating the cache here. @pending remove setting of the cell formatter when that capability is in interface builder */ - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item { Resource *resource = (Resource *)item; NSString *identifier = [tableColumn identifier]; // set formatters for cells (remove when IB can set a formatter for an entire table column) if([identifier isEqualToString:@"size"]) [cell setFormatter:sizeFormatter]; else if([identifier isEqualToString:@"attributes"]) [cell setFormatter:attributesFormatter]; // set resource icon if([identifier isEqualToString:@"name"]) { if(![resource representedFork]) [(ResourceNameCell *)cell setImage:[(ApplicationDelegate *)[NSApp delegate] iconForResourceType:[resource type]]]; else [(ResourceNameCell *)cell setImage:[(ApplicationDelegate *)[NSApp delegate] iconForResourceType:nil]]; if([[resource name] isEqualToString:@""]) { if([cell respondsToSelector:@selector(setPlaceholderString:)]) // 10.3+ { // 10.3+ uses placeholder strings if([[resource resID] shortValue] == -16455) // don't bother checking type since there are too many icon types [cell setPlaceholderString:NSLocalizedString(@"Custom Icon", nil)]; else if([[resource type] isEqualToString:@"carb"] && [[resource resID] shortValue] == 0) [cell setPlaceholderString:NSLocalizedString(@"Carbon Identifier", nil)]; else if([[resource type] isEqualToString:@"pnot"] && [[resource resID] shortValue] == 0) [cell setPlaceholderString:NSLocalizedString(@"File Preview", nil)]; else if([[resource type] isEqualToString:@"STR "] && [[resource resID] shortValue] == -16396) [cell setPlaceholderString:NSLocalizedString(@"Creator Information", nil)]; else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 1) [cell setPlaceholderString:NSLocalizedString(@"File Version", nil)]; else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 2) [cell setPlaceholderString:NSLocalizedString(@"Package Version", nil)]; else [cell setPlaceholderString:NSLocalizedString(@"Untitled Resource", nil)]; } else { // pre-10.3, set text colour to grey and set title accordingly if([[resource resID] shortValue] == -16455) [cell setTitle:NSLocalizedString(@"Custom Icon", nil)]; else if([[resource type] isEqualToString:@"carb"] && [[resource resID] shortValue] == 0) [cell setTitle:NSLocalizedString(@"Carbon Identifier", nil)]; else if([[resource type] isEqualToString:@"pnot"] && [[resource resID] shortValue] == 0) [cell setTitle:NSLocalizedString(@"File Preview", nil)]; else if([[resource type] isEqualToString:@"STR "] && [[resource resID] shortValue] == -16396) [cell setTitle:NSLocalizedString(@"Creator Information", nil)]; else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 1) [cell setTitle:NSLocalizedString(@"File Version", nil)]; else if([[resource type] isEqualToString:@"vers"] && [[resource resID] shortValue] == 2) [cell setTitle:NSLocalizedString(@"Package Version", nil)]; else [cell setTitle:NSLocalizedString(@"Untitled Resource", nil)]; // if([[outlineView selectedItems] containsObject:resource]) // [cell setTextColor:[NSColor whiteColor]]; // else [cell setTextColor:[NSColor grayColor]]; } } } // draw alternating blue/white backgrounds (if pre-10.3) if(NSAppKitVersionNumber < 700.0) { int row = [outlineView rowForItem:item]; if(row % 2) [cell setBackgroundColor:[NSColor colorWithCalibratedRed:0.93 green:0.95 blue:1.0 alpha:1.0]]; else [cell setBackgroundColor:[NSColor whiteColor]]; [cell setDrawsBackground:YES]; } } @end @implementation RKOutlineView /*! @method draggingSourceOperationMaskForLocal: */ - (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)local { if(local) return NSDragOperationEvery; else return NSDragOperationCopy; } - (void)keyDown:(NSEvent *)event { int selectedRow = [self selectedRow]; if(selectedRow != -1 && [[event characters] isEqualToString:[NSString stringWithCString:"\r"]]) [self editColumn:0 row:selectedRow withEvent:nil select:YES]; else if(selectedRow != -1 && [[event characters] isEqualToString:[NSString stringWithCString:"\x7F"]]) [(ResourceDocument *)[[[self window] windowController] document] deleteSelectedResources]; else [super keyDown:event]; } - (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)selector { // pressed return, end editing if(selector == @selector(insertNewline:)) { [[self window] makeFirstResponder:self]; [self abortEditing]; return YES; } // pressed tab, move to next editable field else if(selector == @selector(insertTab:)) { int newColumn = ([self editedColumn] +1) % [self numberOfColumns]; NSString *newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) { newColumn = (newColumn +1) % [self numberOfColumns]; newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) newColumn = (newColumn +1) % [self numberOfColumns]; } [self editColumn:newColumn row:[self selectedRow] withEvent:nil select:YES]; return YES; } // pressed shift-tab, move to previous editable field else if(selector == @selector(insertBacktab:)) { int newColumn = ([self editedColumn] + [self numberOfColumns] -1) % [self numberOfColumns]; NSString *newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) { newColumn = (newColumn + [self numberOfColumns] -1) % [self numberOfColumns]; newColIdentifier = [[[self tableColumns] objectAtIndex:newColumn] identifier]; if([newColIdentifier isEqualToString:@"size"] || [newColIdentifier isEqualToString:@"attributes"]) newColumn = (newColumn + [self numberOfColumns] -1) % [self numberOfColumns]; } [self editColumn:newColumn row:[self selectedRow] withEvent:nil select:YES]; return YES; } return NO; } /*! @method _sendDelegateDidClickColumn: @pending not needed in 10.3+, use existing sort functionality */ //- (void)_sendDelegateDidMouseDownInHeader:(int)columnIndex - (void)_sendDelegateDidClickColumn:(int)columnIndex { // if(NSAppKitVersionNumber < 700.0) { NSTableColumn *tableColumn = [[self tableColumns] objectAtIndex:columnIndex]; NSImage *indicator = [self indicatorImageInTableColumn:tableColumn]; NSImage *upArrow = [NSTableView _defaultTableHeaderSortImage]; NSImage *downArrow = [NSTableView _defaultTableHeaderReverseSortImage]; if(indicator) { // column already selected if(indicator == upArrow) [self setIndicatorImage:downArrow inTableColumn:tableColumn]; else [self setIndicatorImage:upArrow inTableColumn:tableColumn]; } else // new column selected { // if there is an existing selection, clear it's image if([self highlightedTableColumn] != nil) [self setIndicatorImage:nil inTableColumn:[self highlightedTableColumn]]; // sort name and type columns ascending by default if([[tableColumn identifier] isEqualToString:@"name"] || [[tableColumn identifier] isEqualToString:@"type"]) [self setIndicatorImage:upArrow inTableColumn:tableColumn]; // sort all other columns descending by default else [self setIndicatorImage:downArrow inTableColumn:tableColumn]; [self setHighlightedTableColumn:tableColumn]; } [[self delegate] tableView:self didClickTableColumn:tableColumn]; } // else [super _sendDelegateDidClickColumn:columnIndex]; } @end