// // PBSourceViewCell.m // GitX // // Created by Nathan Kinsinger on 1/7/10. // Copyright 2010 Nathan Kinsinger. All rights reserved. // #import "PBSourceViewCell.h" #import "PBGitSidebarController.h" #import "PBSourceViewBadge.h" @interface PBSourceViewCell() - (NSRect)infoButtonRectForBounds:(NSRect)bounds; - (void)mouseEntered:(NSEvent *)event; - (void)mouseExited:(NSEvent *)event; @end @implementation PBSourceViewCell @synthesize iInfoButtonAction; @synthesize showsActionButton; @synthesize badge; # pragma mark context menu delegate methods - init { if ((self = [super init])) { } return self; } - (NSMenu *) menuForEvent:(NSEvent *)event inRect:(NSRect)rect ofView:(NSOutlineView *)view { NSPoint point = [self.controlView convertPoint:[event locationInWindow] fromView:nil]; NSInteger row = [view rowAtPoint:point]; PBGitSidebarController *controller = (PBGitSidebarController*)[view delegate]; return [controller menuForRow:row]; } #pragma mark drawing - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)outlineView { if(badge){ NSImage *checkedOutImage = [PBSourceViewBadge badge:badge forCell:self]; NSSize imageSize = [checkedOutImage size]; NSRect imageFrame; NSDivideRect(cellFrame, &imageFrame, &cellFrame, imageSize.width + 3, NSMaxXEdge); imageFrame.size = imageSize; if ([outlineView isFlipped]) imageFrame.origin.y += floor((cellFrame.size.height + imageFrame.size.height) / 2); else imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2); [checkedOutImage compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver]; } [super drawWithFrame:cellFrame inView:outlineView]; } #pragma mark - #pragma mark Button support - (NSRect)infoButtonRectForBounds:(NSRect)bounds { CGFloat infoButtonWidth = 17.0f; CGFloat infoButtonHeight = 11.0f; return NSMakeRect(NSMaxX(bounds) - infoButtonWidth, NSMinY(bounds) + (NSHeight(bounds) - infoButtonHeight)/2.0f, infoButtonWidth, infoButtonHeight); } - (NSImage *)infoButtonImage { // Construct an image name based on our current state NSString *imageName = [NSString stringWithFormat:@"sourceListAction%@.png", //[self isHighlighted] ? @"selected" : @"normal", iMouseDownInInfoButton ? @"Over" : iMouseHoveredInInfoButton ? @"Over" : @""]; return [NSImage imageNamed:imageName]; } - (void)drawInteriorWithFrame:(NSRect)bounds inView:(NSView *)controlView { [super drawInteriorWithFrame:bounds inView:controlView]; if (showsActionButton) { NSRect infoButtonRect = [self infoButtonRectForBounds:bounds]; NSImage *anImage = [self infoButtonImage]; [anImage setFlipped:[controlView isFlipped]]; [anImage drawInRect:infoButtonRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; } } // Mouse tracking -- the only part we want to track is the "info" button - (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)flag { NSPoint locationOfTouch = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; BOOL mouseInButton = NSMouseInRect(locationOfTouch, [self infoButtonRectForBounds:cellFrame], [controlView isFlipped]); if (mouseInButton) { [self mouseEntered:theEvent]; // show menu NSMenu *menu = [self menuForEvent:theEvent inRect:cellFrame ofView:controlView]; if (menu){ [NSMenu popUpContextMenu:menu withEvent:theEvent forView:controlView]; return YES; } } if (iMouseDownInInfoButton) { // Send the action, and redisplay iMouseDownInInfoButton = NO; [controlView setNeedsDisplayInRect:cellFrame]; } return [super trackMouse:theEvent inRect:cellFrame ofView:controlView untilMouseUp:flag]; } // Mouse movement tracking -- we have a custom NSOutlineView subclass that automatically lets us add mouseEntered:/mouseExited: support to any cell! - (void)addTrackingAreasForView:(NSView *)controlView inRect:(NSRect)cellFrame withUserInfo:(NSDictionary *)userInfo mouseLocation:(NSPoint)mouseLocation { NSRect infoButtonRect = [self infoButtonRectForBounds:cellFrame]; NSTrackingAreaOptions options = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways; BOOL mouseIsInside = NSMouseInRect(mouseLocation, infoButtonRect, [controlView isFlipped]); if (mouseIsInside) { options |= NSTrackingAssumeInside; [controlView setNeedsDisplayInRect:cellFrame]; } // We make the view the owner, and it delegates the calls back to the cell after it is properly setup for the corresponding row/column in the outlineview NSTrackingArea *area = [[NSTrackingArea alloc] initWithRect:infoButtonRect options:options owner:controlView userInfo:userInfo]; [controlView addTrackingArea:area]; [area release]; } - (void)mouseEntered:(NSEvent *)event { iMouseHoveredInInfoButton = YES; [(NSControl *)[self controlView] updateCell:self]; } - (void)mouseExited:(NSEvent *)event { iMouseHoveredInInfoButton = NO; [(NSControl *)[self controlView] updateCell:self]; } @end