// // TaskTableView.m // RubyFrictionless // // Created by Pierce T. Wetter III on 4/17/07. // Copyright 2007 __MyCompanyName__. All rights reserved. // #import "TaskTableView.h" //#import "NSTableView-OAExtensions.h" typedef struct { float red1, green1, blue1, alpha1; float red2, green2, blue2, alpha2; } _twoColorsType; void _linearColorBlendFunction(void *info, const float *in, float *out) { _twoColorsType *twoColors = info; out[0] = (1.0 - *in) * twoColors->red1 + *in * twoColors->red2; out[1] = (1.0 - *in) * twoColors->green1 + *in * twoColors->green2; out[2] = (1.0 - *in) * twoColors->blue1 + *in * twoColors->blue2; out[3] = (1.0 - *in) * twoColors->alpha1 + *in * twoColors->alpha2; } void _linearColorReleaseInfoFunction(void *info) { free(info); } //#import //static OATypeAheadSelectionHelper *TypeAheadHelper = nil; static const CGFunctionCallbacks linearFunctionCallbacks = {0, &_linearColorBlendFunction, &_linearColorReleaseInfoFunction}; @implementation TaskTableView - (void) showActionable: (id) sender { [_delegate showActionable: sender]; } - (void) showToDo: (id) sender { [_delegate showToDo: sender]; } - (void) showDone: (id) sender { [_delegate showDone: sender]; } - (void) delete: (id) sender { [_delegate delete: sender]; } - (void) addChild: (id) sender { [_delegate addChildToTasks: sender]; } - (void) addSibling: (id) sender { [_delegate addChildToTasks: sender]; } - (void) textDidEndEditing: (NSNotification *) notification { NSDictionary *userInfo; userInfo = [notification userInfo]; NSNumber *textMovement; textMovement = [userInfo objectForKey: @"NSTextMovement"]; int movementCode; movementCode = [textMovement intValue]; // see if this a 'pressed-return' instance if (movementCode == NSReturnTextMovement) { // hijack the notification and pass a different textMovement // value textMovement = [NSNumber numberWithInt: NSIllegalTextMovement]; NSDictionary *newUserInfo; newUserInfo = [NSDictionary dictionaryWithObject: textMovement forKey: @"NSTextMovement"]; notification = [NSNotification notificationWithName: [notification name] object: [notification object] userInfo: newUserInfo]; } [super textDidEndEditing: notification]; } // textDidEndEditing - (void)dealloc; { [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } // NSView - (void)viewWillMoveToWindow:(NSWindow *)newWindow; { [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeKeyNotification:) name:NSWindowDidResignKeyNotification object:newWindow]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeKeyNotification:) name:NSWindowDidBecomeKeyNotification object:newWindow]; } // NSTableView - (void)highlightSelectionInClipRect:(NSRect)rect; { // Take the color apart NSColor *alternateSelectedControlColor = [NSColor alternateSelectedControlColor]; float hue, saturation, brightness, alpha; [[alternateSelectedControlColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]; // Create synthetic darker and lighter versions NSColor *lighterColor = [NSColor colorWithDeviceHue:hue saturation:MAX(0.0, saturation-.12) brightness:MIN(1.0,brightness+0.30) alpha:alpha]; NSColor *darkerColor = [NSColor colorWithDeviceHue:hue saturation:MIN(1.0, (saturation > .04) ? saturation+0.12 : 0.0) brightness:MAX(0.0, brightness-0.045) alpha:alpha]; /* If this view isn't key, use the gray version of the dark color. Note that this varies from the standard gray version that NSCell returns as its highlightColorWithFrame: when the cell is not in a key view, in that this is a lot darker. Mike and I think this is justified for this kind of view -- if you're using the dark selection color to show the selected status, it makes sense to leave it dark. */ NSResponder *firstResponder = [[self window] firstResponder]; if (![firstResponder isKindOfClass:[NSView class]] || ![(NSView *)firstResponder isDescendantOf:self] || ![[self window] isKeyWindow]) { alternateSelectedControlColor = [[alternateSelectedControlColor colorUsingColorSpaceName:NSDeviceWhiteColorSpace] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; lighterColor = [[lighterColor colorUsingColorSpaceName:NSDeviceWhiteColorSpace] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; darkerColor = [[darkerColor colorUsingColorSpaceName:NSDeviceWhiteColorSpace] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; } // Set up the helper function for drawing washes CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); _twoColorsType *twoColors = malloc(sizeof(_twoColorsType)); /* We malloc() the helper data because we may draw this wash during printing, in which case it won't necessarily be evaluated immediately. We need for all the data the shading function needs to draw to potentially outlive us.*/ [lighterColor getRed:&twoColors->red1 green:&twoColors->green1 blue:&twoColors->blue1 alpha:&twoColors->alpha1]; [darkerColor getRed:&twoColors->red2 green:&twoColors->green2 blue:&twoColors->blue2 alpha:&twoColors->alpha2]; static const float domainAndRange[8] = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}; CGFunctionRef linearBlendFunctionRef = CGFunctionCreate(twoColors, 1, domainAndRange, 4, domainAndRange, &linearFunctionCallbacks); NSIndexSet *selectedRowIndexes = [self selectedRowIndexes]; unsigned int rowIndex = [selectedRowIndexes indexGreaterThanOrEqualToIndex:0]; while (rowIndex != NSNotFound) { unsigned int endOfCurrentRunRowIndex, newRowIndex = rowIndex; do { endOfCurrentRunRowIndex = newRowIndex; newRowIndex = [selectedRowIndexes indexGreaterThanIndex:endOfCurrentRunRowIndex]; } while (newRowIndex == endOfCurrentRunRowIndex + 1); NSRect rowRect = NSUnionRect([self rectOfRow:rowIndex], [self rectOfRow:endOfCurrentRunRowIndex]); NSRect topBar, washRect; NSDivideRect(rowRect, &topBar, &washRect, 1.0, NSMinYEdge); // Draw the top line of pixels of the selected row in the alternateSelectedControlColor [alternateSelectedControlColor set]; NSRectFill(topBar); // Draw a soft wash underneath it CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGContextSaveGState(context); { CGContextClipToRect(context, (CGRect){{NSMinX(washRect), NSMinY(washRect)}, {NSWidth(washRect), NSHeight(washRect)}}); CGShadingRef cgShading = CGShadingCreateAxial(colorSpace, CGPointMake(0, NSMinY(washRect)), CGPointMake(0, NSMaxY(washRect)), linearBlendFunctionRef, NO, NO); CGContextDrawShading(context, cgShading); CGShadingRelease(cgShading); } CGContextRestoreGState(context); rowIndex = newRowIndex; } CGFunctionRelease(linearBlendFunctionRef); CGColorSpaceRelease(colorSpace); } - (void)selectRow:(int)row byExtendingSelection:(BOOL)extend; { [super selectRow:row byExtendingSelection:extend]; [self setNeedsDisplay:YES]; // we display extra because we draw multiple contiguous selected rows differently, so changing one row's selection can change how others draw. } - (void)deselectRow:(int)row; { [super deselectRow:row]; [self setNeedsDisplay:YES]; // we display extra because we draw multiple contiguous selected rows differently, so changing one row's selection can change how others draw. } - (void)keyDown:(NSEvent *)theEvent; { NSString *characters; unichar firstCharacter; unsigned int modifierFlags; characters = [theEvent characters]; modifierFlags = [theEvent modifierFlags]; firstCharacter = [characters characterAtIndex:0]; switch (firstCharacter) { case 13: if (modifierFlags & NSAlternateKeyMask) [_delegate addChild:nil]; else [_delegate addSibling:nil]; return; case NSDeleteFunctionKey: case 127: [self delete: nil]; return; case 'a': [self showActionable: nil]; return; case 't': [self showToDo: nil]; return; case 'd': [self showDone: nil]; return; case 'e': { // not reached if type-ahead selection is turned on int columnIndex, rowIndex; columnIndex = [[self tableColumns] indexOfObject:[self columnWithIdentifier:@"name"]]; rowIndex = [self selectedRow]; [self editColumn:columnIndex row:rowIndex withEvent:nil select:YES]; return; } /*case 'g': // not reached if type-ahead selection is turned on [self group:nil]; return; case 'u': // not reached if type-ahead selection is turned on [self ungroup:nil]; return;*/ /* case '<': { NSArray *selectedItems; selectedItems = [self selectedItems]; [self contractAll:nil]; [self setSelectedItems:selectedItems]; return; } case '>': { NSArray *selectedItems; selectedItems = [self selectedItems]; [self expandAll:nil]; [self setSelectedItems:selectedItems]; return; } case NSLeftArrowFunctionKey: if (modifierFlags & NSAlternateKeyMask) { [self contractSelection:nil]; return; } break; case NSRightArrowFunctionKey: if (modifierFlags & NSAlternateKeyMask) { [self expandSelection:nil]; return; } break;*/ default: break; } [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; } // NSTableView (Private) - (id)_highlightColorForCell:(NSCell *)cell; { return nil; } @end @implementation TaskTableView (Private) - (void)_windowDidChangeKeyNotification:(NSNotification *)notification; { [self setNeedsDisplay:YES]; } @end