diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPActivityTextFieldCell.h | 49 | ||||
-rw-r--r-- | Source/SPActivityTextFieldCell.m | 357 | ||||
-rw-r--r-- | Source/SPAppController.h | 8 | ||||
-rw-r--r-- | Source/SPAppController.m | 31 | ||||
-rw-r--r-- | Source/SPConstants.h | 1 | ||||
-rw-r--r-- | Source/SPConstants.m | 1 | ||||
-rw-r--r-- | Source/SPCopyTable.m | 9 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.h | 8 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 20 | ||||
-rw-r--r-- | Source/SPStringAdditions.h | 2 | ||||
-rw-r--r-- | Source/SPStringAdditions.m | 21 | ||||
-rw-r--r-- | Source/SPTableInfo.h | 1 | ||||
-rw-r--r-- | Source/SPTableInfo.m | 89 | ||||
-rw-r--r-- | Source/SPTableTextFieldCell.m | 5 | ||||
-rw-r--r-- | Source/SPTextViewAdditions.m | 9 |
15 files changed, 552 insertions, 59 deletions
diff --git a/Source/SPActivityTextFieldCell.h b/Source/SPActivityTextFieldCell.h new file mode 100644 index 00000000..4f027710 --- /dev/null +++ b/Source/SPActivityTextFieldCell.h @@ -0,0 +1,49 @@ +// +// $Id: SPActivityTextFieldCell.h 2781 2010-10-19 23:37:15Z stuart02 $ +// +// SPActivityTextFieldCell.h +// sequel-pro +// +// Created by Hans-Jörg Bibiko on Dec 01, 2010 +// +// 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 "ImageAndTextCell.h" + +@interface SPActivityTextFieldCell : ImageAndTextCell + +{ + NSString *activityName; + NSString *activityInfo; + NSDictionary *contextInfo; + + NSButtonCell *cancelButton; + + NSInteger drawState; + + NSColor *mainStringColor; + NSColor *subStringColor; +} + +@property(readwrite,retain) NSString *activityName; +@property(readwrite,retain) NSString *activityInfo; +@property(readwrite,retain) NSDictionary *contextInfo; + +- (void)invertFontColors; +- (void)restoreFontColors; + +@end 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 diff --git a/Source/SPAppController.h b/Source/SPAppController.h index d96af12f..2fb4aef2 100644 --- a/Source/SPAppController.h +++ b/Source/SPAppController.h @@ -50,7 +50,7 @@ NSMutableDictionary *bundleKeyEquivalents; NSMutableDictionary *installedBundleUUIDs; - NSMutableArray *runningBASHprocesses; + NSMutableArray *runningActivitiesArray; } @@ -97,9 +97,9 @@ - (NSArray *)bundleCategoriesForScope:(NSString*)scope; - (NSArray *)bundleItemsForScope:(NSString*)scope; - (NSDictionary *)bundleKeyEquivalentsForScope:(NSString*)scope; -- (void)registerBASHCommand:(NSDictionary*)commandDict; -- (void)unRegisterBASHCommand:(NSInteger)pid; -- (NSArray*)runningBASHProcesses; +- (void)registerActivity:(NSDictionary*)commandDict; +- (void)removeRegisteredActivity:(NSInteger)pid; +- (NSArray*)runningActivities; - (void)handleEventWithURL:(NSURL*)url; diff --git a/Source/SPAppController.m b/Source/SPAppController.m index ad456b89..67d0427f 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -57,7 +57,7 @@ bundleUsedScopes = [[NSMutableArray alloc] initWithCapacity:1]; bundleKeyEquivalents = [[NSMutableDictionary alloc] initWithCapacity:1]; installedBundleUUIDs = [[NSMutableDictionary alloc] initWithCapacity:1]; - runningBASHprocesses = [[NSMutableArray alloc] init]; + runningActivitiesArray = [[NSMutableArray alloc] init]; [NSApp setDelegate:self]; } @@ -781,7 +781,14 @@ return; } - NSString *output = [cmd runBashCommandWithEnvironment:env atCurrentDirectoryPath:nil callerDocument:self withName:([cmdData objectForKey:SPBundleFileNameKey])?[cmdData objectForKey:SPBundleFileNameKey]:@"" error:&err]; + NSString *output = [cmd runBashCommandWithEnvironment:env + atCurrentDirectoryPath:nil + callerInstance:self + contextInfo:[NSDictionary dictionaryWithObjectsAndKeys: + ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name", + NSLocalizedString(@"General", @"general menu item label"), @"scope", + nil] + error:&err]; [[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil]; @@ -832,24 +839,24 @@ } -- (void)registerBASHCommand:(NSDictionary*)commandDict +- (void)registerActivity:(NSDictionary*)commandDict { - [runningBASHprocesses addObject:commandDict]; + [runningActivitiesArray addObject:commandDict]; } -- (void)unRegisterBASHCommand:(NSInteger)pid +- (void)removeRegisteredActivity:(NSInteger)pid { - for(id cmd in runningBASHprocesses) { + for(id cmd in runningActivitiesArray) { if([[cmd objectForKey:@"pid"] integerValue] == pid) { - [runningBASHprocesses removeObject:cmd]; + [runningActivitiesArray removeObject:cmd]; break; } } } -- (NSArray*)runningBASHProcesses +- (NSArray*)runningActivities { - return (NSArray*)runningBASHprocesses; + return (NSArray*)runningActivitiesArray; } #pragma mark - @@ -1538,7 +1545,7 @@ for (NSWindow *aWindow in [NSApp orderedWindows]) { if([[aWindow windowController] isMemberOfClass:[SPWindowController class]]) { for(SPDatabaseDocument *doc in [[aWindow windowController] documents]) { - for(NSDictionary* cmd in [doc runningBASHProcesses]) { + for(NSDictionary* cmd in [doc runningActivities]) { NSInteger pid = [[cmd objectForKey:@"pid"] intValue]; NSTask *killTask = [[NSTask alloc] init]; [killTask setLaunchPath:@"/bin/sh"]; @@ -1550,7 +1557,7 @@ } } } - for(NSDictionary* cmd in [self runningBASHProcesses]) { + for(NSDictionary* cmd in [self runningActivities]) { NSInteger pid = [[cmd objectForKey:@"pid"] intValue]; NSTask *killTask = [[NSTask alloc] init]; [killTask setLaunchPath:@"/bin/sh"]; @@ -1577,7 +1584,7 @@ if(bundleCategories) [bundleCategories release]; if(bundleKeyEquivalents) [bundleKeyEquivalents release]; if(installedBundleUUIDs) [installedBundleUUIDs release]; - if (runningBASHprocesses) [runningBASHprocesses release]; + if (runningActivitiesArray) [runningActivitiesArray release]; [prefsController release], prefsController = nil; diff --git a/Source/SPConstants.h b/Source/SPConstants.h index fce149f0..5327fd45 100644 --- a/Source/SPConstants.h +++ b/Source/SPConstants.h @@ -358,6 +358,7 @@ extern NSString *SPNoBOMforSQLdumpFile; extern NSString *SPContentFilters; extern NSString *SPDocumentTaskEndNotification; extern NSString *SPDocumentTaskStartNotification; +extern NSString *SPActivitiesUpdateNotification; extern NSString *SPFieldEditorSheetFont; extern NSString *SPLastSQLFileEncoding; extern NSString *SPPrintBackground; diff --git a/Source/SPConstants.m b/Source/SPConstants.m index 2ab44912..495a7580 100644 --- a/Source/SPConstants.m +++ b/Source/SPConstants.m @@ -170,6 +170,7 @@ NSString *SPNoBOMforSQLdumpFile = @"NoBOMforSQLdumpFile"; NSString *SPContentFilters = @"ContentFilters"; NSString *SPDocumentTaskEndNotification = @"DocumentTaskEnded"; NSString *SPDocumentTaskStartNotification = @"DocumentTaskStarted"; +NSString *SPActivitiesUpdateNotification = @"ActivitiesUpdateNotification"; NSString *SPFieldEditorSheetFont = @"FieldEditorSheetFont"; NSString *SPLastSQLFileEncoding = @"lastSqlFileEncoding"; NSString *SPPrintBackground = @"PrintBackground"; diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index 94e0eb45..86fb9d85 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -923,7 +923,14 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003; return; } - NSString *output = [cmd runBashCommandWithEnvironment:env atCurrentDirectoryPath:nil callerDocument:[[NSApp delegate] frontDocument] withName:([cmdData objectForKey:SPBundleFileNameKey])?[cmdData objectForKey:SPBundleFileNameKey]:@"" error:&err]; + NSString *output = [cmd runBashCommandWithEnvironment:env + atCurrentDirectoryPath:nil + callerInstance:[[NSApp delegate] frontDocument] + contextInfo:[NSDictionary dictionaryWithObjectsAndKeys: + ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name", + NSLocalizedString(@"Data Table", @"data table menu item label"), @"scope", + nil] + error:&err]; [[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil]; diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h index 16330b39..57131de8 100644 --- a/Source/SPDatabaseDocument.h +++ b/Source/SPDatabaseDocument.h @@ -189,7 +189,7 @@ NSMutableDictionary *spfPreferences; NSMutableDictionary *spfDocData; - NSMutableArray *runningBASHprocesses; + NSMutableArray *runningActivitiesArray; NSString *keyChainID; @@ -357,9 +357,9 @@ // Scripting - (void)handleSchemeCommand:(NSDictionary*)commandDict; -- (void)registerBASHCommand:(NSDictionary*)commandDict; -- (void)unRegisterBASHCommand:(NSInteger)pid; -- (NSArray*)runningBASHProcesses; +- (void)registerActivity:(NSDictionary*)commandDict; +- (void)removeRegisteredActivity:(NSInteger)pid; +- (NSArray*)runningActivities; - (NSDictionary*)shellVariables; // State saving and setting diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index b71ef8f8..309679fe 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -116,7 +116,7 @@ spfSession = nil; spfPreferences = [[NSMutableDictionary alloc] init]; spfDocData = [[NSMutableDictionary alloc] init]; - runningBASHprocesses = [[NSMutableArray alloc] init]; + runningActivitiesArray = [[NSMutableArray alloc] init]; titleAccessoryView = nil; taskProgressWindow = nil; @@ -3675,7 +3675,7 @@ } // Terminate all running BASH commands - for(NSDictionary* cmd in [self runningBASHProcesses]) { + for(NSDictionary* cmd in [self runningActivities]) { NSInteger pid = [[cmd objectForKey:@"pid"] intValue]; NSTask *killTask = [[NSTask alloc] init]; [killTask setLaunchPath:@"/bin/sh"]; @@ -4757,24 +4757,24 @@ NSLog(@"received: %@", commandDict); } -- (void)registerBASHCommand:(NSDictionary*)commandDict +- (void)registerActivity:(NSDictionary*)commandDict { - [runningBASHprocesses addObject:commandDict]; + [runningActivitiesArray addObject:commandDict]; } -- (void)unRegisterBASHCommand:(NSInteger)pid +- (void)removeRegisteredActivity:(NSInteger)pid { - for(id cmd in runningBASHprocesses) { + for(id cmd in runningActivitiesArray) { if([[cmd objectForKey:@"pid"] integerValue] == pid) { - [runningBASHprocesses removeObject:cmd]; + [runningActivitiesArray removeObject:cmd]; break; } } } -- (NSArray*)runningBASHProcesses +- (NSArray*)runningActivities { - return (NSArray*)runningBASHprocesses; + return (NSArray*)runningActivitiesArray; } - (NSDictionary*)shellVariables @@ -5077,7 +5077,7 @@ if (taskProgressWindow) [taskProgressWindow release]; if (serverSupport) [serverSupport release]; if (processID) [processID release]; - if (runningBASHprocesses) [runningBASHprocesses release]; + if (runningActivitiesArray) [runningActivitiesArray release]; [super dealloc]; } diff --git a/Source/SPStringAdditions.h b/Source/SPStringAdditions.h index b0e085aa..042e69f0 100644 --- a/Source/SPStringAdditions.h +++ b/Source/SPStringAdditions.h @@ -77,7 +77,7 @@ static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedS - (CGFloat)levenshteinDistanceWithWord:(NSString *)stringB; -- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerDocument:(id)caller withName:(NSString*)name error:(NSError**)theError; +- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerInstance:(id)caller contextInfo:(NSDictionary*)contextInfo error:(NSError**)theError; - (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path error:(NSError**)theError; @end diff --git a/Source/SPStringAdditions.m b/Source/SPStringAdditions.m index 658baadb..27b8fcd0 100644 --- a/Source/SPStringAdditions.m +++ b/Source/SPStringAdditions.m @@ -451,7 +451,7 @@ * @param theError If not nil and the bash command failed it contains the returned error message as NSLocalizedDescriptionKey * */ -- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerDocument:(id)caller withName:(NSString*)name error:(NSError**)theError +- (NSString *)runBashCommandWithEnvironment:(NSDictionary*)shellEnvironment atCurrentDirectoryPath:(NSString*)path callerInstance:(id)caller contextInfo:(NSDictionary*)contextInfo error:(NSError**)theError { BOOL userTerminated = NO; @@ -549,14 +549,16 @@ NSFileHandle *stderr_file = [stderr_pipe fileHandleForReading]; [bashTask launch]; NSInteger pid = -1; - if(caller != nil && [caller respondsToSelector:@selector(registerBASHCommand:)]) { + if(caller != nil && [caller respondsToSelector:@selector(registerActivity:)]) { // register command pid = [bashTask processIdentifier]; NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:pid], @"pid", - name, @"name", + (contextInfo)?:[NSDictionary dictionary], @"contextInfo", + @"bashcommand", @"type", [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]], @"starttime", nil]; - [caller registerBASHCommand:dict]; + [caller registerActivity:dict]; + [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil]; } // Listen to ⌘. to terminate @@ -584,15 +586,12 @@ [bashTask waitUntilExit]; // unregister BASH command if it was registered - if(pid >= 0) [caller unRegisterBASHCommand:pid]; - - if(userTerminated) { - if(bashTask) [bashTask release]; - NSBeep(); - NSLog(@"“%@” was terminated by user.", ([self length] > 50) ? [self substringToIndex:50] : self); - return @""; + if(pid >= 0) { + [caller removeRegisteredActivity:pid]; + [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil]; } + // Remove script file if used if(redirectForScript) [[NSFileManager defaultManager] removeItemAtPath:scriptFilePath error:nil]; diff --git a/Source/SPTableInfo.h b/Source/SPTableInfo.h index bae3da4e..8663ac6a 100644 --- a/Source/SPTableInfo.h +++ b/Source/SPTableInfo.h @@ -38,6 +38,7 @@ NSMutableArray *info; NSMutableArray *activities; + BOOL _activitiesWillBeUpdated; } - (void)tableChanged:(NSNotification *)notification; diff --git a/Source/SPTableInfo.m b/Source/SPTableInfo.m index ba24040a..3dc64b01 100644 --- a/Source/SPTableInfo.m +++ b/Source/SPTableInfo.m @@ -27,6 +27,8 @@ #import "SPDatabaseDocument.h" #import "SPTablesList.h" #import "SPTableData.h" +#import "SPActivityTextFieldCell.h" +#import "SPTableTextFieldCell.h" @interface SPTableInfo (PrivateAPI) @@ -41,6 +43,7 @@ if ((self = [super init])) { info = [[NSMutableArray alloc] init]; activities = [[NSMutableArray alloc] init]; + _activitiesWillBeUpdated = NO; } return self; @@ -48,19 +51,26 @@ - (void)awakeFromNib { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tableChanged:) name:SPTableChangedNotification object:tableDocumentInstance]; - [tableInfoScrollView setAlphaValue:1.0f]; - [activitiesScrollView setAlphaValue:0.0f]; + // Register activities update notifications for add/remove BASH commands etc. + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateActivities) + name:SPActivitiesUpdateNotification + object:nil]; - // [self performSelector:@selector(updateActivities) withObject:nil afterDelay:1.0f]; + [tableInfoScrollView setHidden:NO]; + [activitiesScrollView setHidden:YES]; + // Add activities header [activities addObject:[NSDictionary dictionaryWithObjectsAndKeys:NSLocalizedString(@"ACTIVITIES", @"header for activities pane"), @"name", nil]]; [activitiesTable reloadData]; + // Add Information header [info addObject:NSLocalizedString(@"TABLE INFORMATION", @"header for table info pane")]; [infoTable reloadData]; } @@ -80,11 +90,14 @@ NSMutableArray *acts = [NSMutableArray array]; [acts removeAllObjects]; [acts addObject:[NSDictionary dictionaryWithObjectsAndKeys:NSLocalizedString(@"ACTIVITIES", @"header for activities pane"), @"name", nil]]; - [acts addObjectsFromArray:[tableDocumentInstance runningBASHProcesses]]; - [acts addObjectsFromArray:[[NSApp delegate] runningBASHProcesses]]; + [acts addObjectsFromArray:[tableDocumentInstance runningActivities]]; + [acts addObjectsFromArray:[[NSApp delegate] runningActivities]]; + _activitiesWillBeUpdated = YES; [activities setArray:acts]; + _activitiesWillBeUpdated = NO; [activitiesTable reloadData]; - // [self performSelector:@selector(updateActivities) withObject:nil afterDelay:1.0f]; + [infoTable deselectAll:nil]; + [activitiesTable deselectAll:nil]; } /** @@ -285,10 +298,33 @@ if(aTableView == infoTable) { return [info objectAtIndex:rowIndex]; } else { - if(rowIndex > -1 && rowIndex < [activities count]) - return [NSArrayObjectAtIndex(activities,rowIndex) objectForKey:@"name"]; - else + if(rowIndex == 0) + { + SPTableTextFieldCell *c = [[[SPTableTextFieldCell alloc] initTextCell:NSLocalizedString(@"ACTIVITIES", @"header for activities pane")] autorelease]; + [aTableColumn setDataCell:c]; + return NSLocalizedString(@"ACTIVITIES", @"header for activities pane"); + } + else if(!_activitiesWillBeUpdated && rowIndex > 0 && rowIndex < [activities count]) { + NSDictionary *dict = NSArrayObjectAtIndex(activities,rowIndex); + SPActivityTextFieldCell *c = [[[SPActivityTextFieldCell alloc] init] autorelease]; + [c setActivityName:[[dict objectForKey:@"contextInfo"] objectForKey:@"name"]]; + if([dict objectForKey:@"type"] && [[dict objectForKey:@"type"] isEqualToString:@"bashcommand"]) { + [c setContextInfo:[NSDictionary dictionaryWithObjectsAndKeys:[dict objectForKey:@"type"], @"type", [dict objectForKey:@"pid"], @"pid", nil]]; + [c setActivityInfo:[NSString stringWithFormat:@"[%@] %@: %@", [[dict objectForKey:@"contextInfo"] objectForKey:@"scope"], NSLocalizedString(@"started", @"started"), [dict objectForKey:@"starttime"]]]; + } + else { + [c setActivityInfo:@"..."]; + } + [aTableColumn setDataCell:c]; + return [dict objectForKey:@"name"]; + } else { + SPActivityTextFieldCell *c = [[[SPActivityTextFieldCell alloc] init] autorelease]; + [c setActivityName:@"..."]; + [c setActivityInfo:@""]; + [aTableColumn setDataCell:c]; return @"..."; + } + return @""; } } @@ -300,28 +336,51 @@ - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex { if(rowIndex == 0) return YES; + if(aTableView == infoTable) { + return NO; + } else { + return YES; + } return NO; } - (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { if(rowIndex > 0) return NO; - if([tableInfoScrollView alphaValue] == 1.0f) { - [tableInfoScrollView setAlphaValue:0.0f]; - [activitiesScrollView setAlphaValue:1.0f]; + + if(![tableInfoScrollView isHidden]) { + [tableInfoScrollView setHidden:YES]; + [activitiesScrollView setHidden:NO]; + [[NSApp mainWindow] makeFirstResponder:activitiesTable]; } else { - [tableInfoScrollView setAlphaValue:1.0f]; - [activitiesScrollView setAlphaValue:0.0f]; + [activitiesScrollView setHidden:YES]; + [tableInfoScrollView setHidden:NO]; + [[NSApp mainWindow] makeFirstResponder:infoTable]; } + [infoTable deselectAll:nil]; [activitiesTable deselectAll:nil]; [self updateActivities]; return NO; } +- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex mouseLocation:(NSPoint)mouseLocation +{ + if(aTableView == activitiesTable) { + if(rowIndex == 0) return @""; + if(mouseLocation.x > rect->origin.x + rect->size.width - 30) + return NSLocalizedString(@"Cancel", @"cancel"); + NSDictionary *dict = NSArrayObjectAtIndex(activities,rowIndex); + if([[dict objectForKey:@"contextInfo"] objectForKey:@"name"]) + return [[dict objectForKey:@"contextInfo"] objectForKey:@"name"]; + return @""; + } + return nil; +} + - (BOOL)tableView:(NSTableView *)aTableView isGroupRow:(NSInteger)row { - // This makes the top row (TABLE INFORMATION) have the diff styling + // This makes the top row (TABLE INFORMATION/ACTIVITIES) have the diff styling return (row == 0); } diff --git a/Source/SPTableTextFieldCell.m b/Source/SPTableTextFieldCell.m index bcfa0138..33508517 100644 --- a/Source/SPTableTextFieldCell.m +++ b/Source/SPTableTextFieldCell.m @@ -56,6 +56,11 @@ } +- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView *)view +{ + return NSMakeRect(cellFrame.origin.x, cellFrame.origin.y, [self cellSize].width, [self cellSize].height); +} + - (NSSize)cellSize { NSSize cellSize = [super cellSize]; diff --git a/Source/SPTextViewAdditions.m b/Source/SPTextViewAdditions.m index e613dac1..70548bf3 100644 --- a/Source/SPTextViewAdditions.m +++ b/Source/SPTextViewAdditions.m @@ -603,7 +603,14 @@ return; } - NSString *output = [cmd runBashCommandWithEnvironment:env atCurrentDirectoryPath:nil callerDocument:[[NSApp delegate] frontDocument] withName:([cmdData objectForKey:SPBundleFileNameKey])?[cmdData objectForKey:SPBundleFileNameKey]:@"" error:&err]; + NSString *output = [cmd runBashCommandWithEnvironment:env + atCurrentDirectoryPath:nil + callerInstance:[[NSApp delegate] frontDocument] + contextInfo:[NSDictionary dictionaryWithObjectsAndKeys: + ([cmdData objectForKey:SPBundleFileNameKey])?:@"-", @"name", + NSLocalizedString(@"Input Field", @"input field menu item label"), @"scope", + nil] + error:&err]; [[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil]; |