diff options
Diffstat (limited to 'Source/SPActivityTextFieldCell.m')
-rw-r--r-- | Source/SPActivityTextFieldCell.m | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/Source/SPActivityTextFieldCell.m b/Source/SPActivityTextFieldCell.m new file mode 100644 index 00000000..856fde52 --- /dev/null +++ b/Source/SPActivityTextFieldCell.m @@ -0,0 +1,357 @@ +// +// $Id: SPActivityTextFieldCell.m 2691 2010-09-25 12:21:03Z stuart02 $ +// +// SPActivityTextFieldCell.m +// sequel-pro +// +// Created by Hans-Jörg Bibiko on Dec 01, 2010 +// Copyright (c) 2008 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPActivityTextFieldCell.h" + +#define FAVORITE_NAME_FONT_SIZE 12.0 + +@interface SPActivityTextFieldCell (PrivateAPI) + +- (NSAttributedString *)constructSubStringAttributedString; +- (NSAttributedString *)attributedStringForFavoriteName; +- (NSDictionary *)mainStringAttributedStringAttributes; +- (NSDictionary *)subStringAttributedStringAttributes; + +@end + +@implementation SPActivityTextFieldCell + +/** + * Provide a method to derive the link rect from a cell rect. + */ +static inline NSRect SPTextLinkRectFromCellRect(NSRect inRect) +{ + return NSMakeRect(inRect.origin.x + inRect.size.width - 30, inRect.origin.y - 1, 15, inRect.size.height); +} + + +@synthesize activityName; +@synthesize activityInfo; +@synthesize contextInfo; + +/** + * Init. + */ +- (id)init +{ + if ((self = [super init])) { + mainStringColor = [NSColor blackColor]; + subStringColor = [NSColor grayColor]; + activityName = nil; + activityInfo = nil; + cancelButton = nil; + contextInfo = nil; + drawState = SPLinkDrawStateNormal; + + cancelButton = [[NSButtonCell alloc] init]; + [cancelButton setButtonType:NSMomentaryChangeButton]; + [cancelButton setImagePosition:NSImageRight]; + [cancelButton setTitle:@""]; + [cancelButton setBordered:NO]; + [cancelButton setShowsBorderOnlyWhileMouseInside:YES]; + [cancelButton setImage:[NSImage imageNamed:@"link-arrow"]]; + [cancelButton setAlternateImage:[NSImage imageNamed:@"link-arrow-clicked"]]; + } + + return self; +} + +/** + * Encodes using a given receiver. + */ +- (void) encodeWithCoder:(NSCoder *)coder +{ + [super encodeWithCoder:coder]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + SPActivityTextFieldCell *cell = (SPActivityTextFieldCell *)[super copyWithZone:zone]; + + cell->activityName = nil; + if (activityName) cell->activityName = [activityName copyWithZone:zone]; + + cell->activityInfo = nil; + if (activityInfo) cell->activityInfo = [activityInfo copyWithZone:zone]; + + cell->contextInfo = nil; + if (contextInfo) cell->contextInfo = [contextInfo copyWithZone:zone]; + + cell->cancelButton = nil; + if (cancelButton) cell->cancelButton = [cancelButton copyWithZone:zone]; + + return cell; +} + + +/** + * Draws the actual cell. + */ +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + + [cancelButton setEnabled:(contextInfo != nil)]; + + (([self isHighlighted]) && (![[self highlightColorWithFrame:cellFrame inView:controlView] isEqualTo:[NSColor secondarySelectedControlColor]])) ? [self invertFontColors] : [self restoreFontColors]; + + // Construct and get the sub text attributed string + NSAttributedString *mainString = [self attributedStringForFavoriteName]; + NSAttributedString *subString = [self constructSubStringAttributedString]; + + NSRect subFrame = NSMakeRect(0.0, 0.0, [subString size].width, [subString size].height); + + // Total height of both strings with a 2 pixel separation space + CGFloat totalHeight = [mainString size].height + [subString size].height + 1.0; + + cellFrame.origin.y += (cellFrame.size.height - totalHeight) / 2.0; + cellFrame.origin.x += 10.0; // Indent main string from image + + // Position the sub text's frame rect + subFrame.origin.y = [mainString size].height + cellFrame.origin.y + 1.0; + subFrame.origin.x = cellFrame.origin.x; + + cellFrame.size.height = totalHeight; + + NSInteger i; + CGFloat maxWidth = cellFrame.size.width - 30; + CGFloat mainStringWidth = [mainString size].width; + CGFloat subStringWidth = [subString size].width; + + // Set a right-padding + maxWidth -= 10; + + if (maxWidth < mainStringWidth) { + for (i = 0; i <= [mainString length]; i++) { + if ([[mainString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth && i >= 3) { + mainString = [[[NSMutableAttributedString alloc] initWithString:[[[mainString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self mainStringAttributedStringAttributes]] autorelease]; + } + } + } + + if (maxWidth < subStringWidth) { + for (i = 0; i <= [subString length]; i++) { + if ([[subString attributedSubstringFromRange:NSMakeRange(0, i)] size].width >= maxWidth && i >= 3) { + subString = [[[NSMutableAttributedString alloc] initWithString:[[[subString attributedSubstringFromRange:NSMakeRange(0, i - 3)] string] stringByAppendingString:@"..."] attributes:[self subStringAttributedStringAttributes]] autorelease]; + } + } + } + + [mainString drawInRect:NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, cellFrame.size.width-30, cellFrame.size.height)]; + [subString drawInRect:subFrame]; + + NSRect linkRect = SPTextLinkRectFromCellRect(cellFrame); + + // Get the new link state + NSInteger newDrawState = ([self isHighlighted])? + ((([(NSTableView *)[self controlView] editedColumn] != -1 + || [[[self controlView] window] firstResponder] == [self controlView]) + && [[[self controlView] window] isKeyWindow])?SPLinkDrawStateHighlight:SPLinkDrawStateBackgroundHighlight): + SPLinkDrawStateNormal; + + // Update the link arrow style if the state has changed + if (drawState != newDrawState) { + drawState = newDrawState; + switch (drawState) { + case SPLinkDrawStateNormal: + [cancelButton setImage:[NSImage imageNamed:@"link-arrow"]]; + [cancelButton setAlternateImage:[NSImage imageNamed:@"link-arrow-clicked"]]; + break; + case SPLinkDrawStateHighlight: + [cancelButton setImage:[NSImage imageNamed:@"link-arrow-highlighted"]]; + [cancelButton setAlternateImage:[NSImage imageNamed:@"link-arrow-highlighted-clicked"]]; + break; + case SPLinkDrawStateBackgroundHighlight: + [cancelButton setImage:[NSImage imageNamed:@"link-arrow-clicked"]]; + [cancelButton setAlternateImage:[NSImage imageNamed:@"link-arrow"]]; + break; + } + } + + [cancelButton drawWithFrame:linkRect inView:controlView]; +} + +- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView *)view +{ + return NSZeroRect; +} + +/** + * Allow hit tracking for cancel functionality + */ +- (NSUInteger)hitTestForEvent:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView +{ + return NSCellHitContentArea | NSCellHitTrackableArea; +} + +/** + * Allow mouse tracking within the button cell, to support expected click + * behaviour in the button cell. + */ +- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp +{ + + NSPoint p = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; + NSRect linkRect = SPTextLinkRectFromCellRect(cellFrame); + linkRect.origin.x += 15; + + // Fast path for if not in button rect - just pass to super + if (!NSMouseInRect(p, linkRect, [controlView isFlipped])) + return [super trackMouse:theEvent inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp]; + + // Ignore events other than mouse down. + if ([theEvent type] != NSLeftMouseDown) return YES; + + // Continue tracking the mouse while it's down, updating the state as it enters and leaves the cell, + // until it is released; if still within the cell, follow the link. + BOOL mouseInButton = YES; + while (1) { + if (mouseInButton) { + + // Highlight the button + [cancelButton highlight:YES withFrame:linkRect inView:controlView]; + + // Continue to track until mouse completes a click or exits the cell while still down + BOOL mouseClicked = [cancelButton trackMouse:theEvent inRect:linkRect ofView:controlView untilMouseUp:NO]; + if (mouseClicked) { + + // Remove highlight, and follow the link + [cancelButton highlight:NO withFrame:linkRect inView:controlView]; + + // Cancel activity + if([contextInfo objectForKey:@"type"] && [[contextInfo objectForKey:@"type"] isEqualToString:@"bashcommand"]) { + NSInteger pid = [[contextInfo objectForKey:@"pid"] intValue]; + if(pid > 0) { + NSTask *killTask = [[NSTask alloc] init]; + [killTask setLaunchPath:@"/bin/sh"]; + [killTask setArguments:[NSArray arrayWithObjects:@"-c", [NSString stringWithFormat:@"kill -9 -%ld", pid], nil]]; + [killTask launch]; + [killTask waitUntilExit]; + [killTask release]; + } + } + return YES; + } + + // Mouse has exited the cell. Remove highlight. + mouseInButton = NO; + [cancelButton highlight:NO withFrame:linkRect inView:controlView]; + } + + // Keep tracking the mouse outside the button, until the mouse button is released or it reenters the button + theEvent = [[controlView window] nextEventMatchingMask: NSLeftMouseUpMask | NSLeftMouseDraggedMask]; + p = [controlView convertPoint:[theEvent locationInWindow] fromView:nil]; + mouseInButton = NSMouseInRect(p, linkRect, [controlView isFlipped]); + + // If the event is a mouse release, break the loop. + if ([theEvent type] == NSLeftMouseUp) break; + } + + return YES; +} + +- (NSSize)cellSize +{ + NSSize cellSize = [super cellSize]; + NSAttributedString *mainString = [self attributedStringForFavoriteName]; + NSAttributedString *subString = [self constructSubStringAttributedString]; + + // 15 := indention 10 from image to string plus 5 px padding + CGFloat theWidth = MAX([mainString size].width, [subString size].width) + (([self image] != nil) ? [[self image] size].width : 0) + 15; + + CGFloat totalHeight = [mainString size].height + [subString size].height + 1.0; + + cellSize.width = theWidth; + cellSize.height = totalHeight + 13.0; + return cellSize; +} + +/** + * Inverts the displayed font colors when the cell is selected. + */ +- (void)invertFontColors +{ + mainStringColor = [NSColor whiteColor]; + subStringColor = [NSColor whiteColor]; +} + +/** + * Restores the displayed font colors once the cell is no longer selected. + */ +- (void)restoreFontColors +{ + mainStringColor = [NSColor blackColor]; + subStringColor = [NSColor grayColor]; +} + +/** + * Dealloc. + */ +- (void)dealloc +{ + if(activityName) [activityName release], activityName = nil; + if(activityInfo) [activityInfo release], activityInfo = nil; + if(contextInfo) [contextInfo release], contextInfo = nil; + if(cancelButton) [cancelButton release]; + + [super dealloc]; +} + +@end + +@implementation SPActivityTextFieldCell (PrivateAPI) + +/** + * Constructs the attributed string to be used as the cell's substring. + */ +- (NSAttributedString *)constructSubStringAttributedString +{ + return [[[NSAttributedString alloc] initWithString:activityInfo attributes:[self subStringAttributedStringAttributes]] autorelease]; +} + +/** + * Constructs the attributed string for the cell's favorite name. + */ +- (NSAttributedString *)attributedStringForFavoriteName +{ + return [[[NSAttributedString alloc] initWithString:activityName attributes:[self mainStringAttributedStringAttributes]] autorelease]; +} + +/** + * Returns the attributes of the cell's main string. + */ +- (NSDictionary *)mainStringAttributedStringAttributes +{ + return [NSDictionary dictionaryWithObjectsAndKeys:mainStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:FAVORITE_NAME_FONT_SIZE], NSFontAttributeName, nil]; +} + +/** + * Returns the attributes of the cell's sub string. + */ +- (NSDictionary *)subStringAttributedStringAttributes +{ + return [NSDictionary dictionaryWithObjectsAndKeys:subStringColor, NSForegroundColorAttributeName, [NSFont systemFontOfSize:[NSFont smallSystemFontSize]], NSFontAttributeName, nil]; +} + +@end |