├── bundle_extras ├── DO_NOT_EDIT_AUTOGENERATED_FILES ├── ackmateCollapsed.pdf └── ackmateExpanded.pdf ├── English.lproj └── InfoPlist.strings ├── AckMate_Prefix.pch ├── .gitignore ├── source ├── views │ ├── JPAckControlView.h │ ├── JPAckSearchButton.h │ ├── JPAckSearchButton.m │ ├── JPAckResultTableView.h │ ├── JPAckControlView.m │ ├── JPAckResultCell.h │ ├── JPAckResultCell.m │ └── JPAckResultTableView.m ├── controllers │ ├── JPAckProcess+Parsing.h │ ├── AckMatePlugin.h │ ├── JPAckTypesProcess.h │ ├── JPAckProcess.h │ ├── JPAckResultSource.h │ ├── JPAckWindowController.h │ ├── JPAckProcess+Parsing.m.rl │ ├── JPAckTypesProcess.m │ ├── AckMatePlugin.m │ ├── JPAckProcess.m │ ├── JPAckWindowController.m │ └── JPAckResultSource.m ├── external │ ├── SDFoundation.h │ ├── SDFoundation.m │ ├── NSImage-NoodleExtensions.m │ ├── NSTableView+NoodleExtensions.h │ ├── NSImage-NoodleExtensions.h │ └── NSTableView+NoodleExtensions.m └── models │ ├── JPAckResult.h │ ├── JPAckResultRep.h │ ├── JPAckResultRep.m │ └── JPAckResult.m ├── Info.plist ├── LICENSE.txt ├── README.mdown └── AckMate.xcodeproj └── project.pbxproj /bundle_extras/DO_NOT_EDIT_AUTOGENERATED_FILES: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batterseapower/AckMate/master/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /bundle_extras/ackmateCollapsed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batterseapower/AckMate/master/bundle_extras/ackmateCollapsed.pdf -------------------------------------------------------------------------------- /bundle_extras/ackmateExpanded.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/batterseapower/AckMate/master/bundle_extras/ackmateExpanded.pdf -------------------------------------------------------------------------------- /AckMate_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'AckMate' target in the 'AckMate' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | *~.nib 4 | *.tm_build_errors 5 | *.pbxuser 6 | *.perspective 7 | *.perspectivev3 8 | scratch 9 | appcast 10 | *.TM_Completions.txt.gz 11 | *.mode1v3 12 | -------------------------------------------------------------------------------- /source/views/JPAckControlView.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @interface JPAckControlView : NSView { 7 | 8 | } 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /source/views/JPAckSearchButton.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @interface JPAckSearchButton : NSButton { 7 | 8 | } 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /source/controllers/JPAckProcess+Parsing.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | #import "JPAckProcess.h" 6 | 7 | @interface JPAckProcess (Parsing) 8 | - (void)consumeInputLines:(NSData*)data; 9 | @end 10 | -------------------------------------------------------------------------------- /source/external/SDFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // SDFoundation.h 3 | // SDToolkit 4 | // 5 | // Created by Steven Degutis on 6/14/09. 6 | // Copyright 2009 Steven Degutis Software. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // either slice or rem can be NULL. 12 | void SDDivideRect(NSRect inRect, NSRect* slice, NSRect* rem, CGFloat amount, NSRectEdge edge); 13 | -------------------------------------------------------------------------------- /source/controllers/AckMatePlugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @protocol TMPlugInController 7 | - (float)version; 8 | @end 9 | 10 | @interface AckMatePlugin : NSObject 11 | { 12 | NSMutableDictionary* ackWindows; 13 | NSMutableDictionary* ackPreferences; 14 | } 15 | - (id)initWithPlugInController:(id )aController; 16 | @end -------------------------------------------------------------------------------- /source/external/SDFoundation.m: -------------------------------------------------------------------------------- 1 | // 2 | // SDFoundation.m 3 | // SDToolkit 4 | // 5 | // Created by Steven Degutis on 6/14/09. 6 | // Copyright 2009 Steven Degutis Software. All rights reserved. 7 | // 8 | 9 | #import "SDFoundation.h" 10 | 11 | 12 | void SDDivideRect(NSRect inRect, NSRect* slice, NSRect* rem, CGFloat amount, NSRectEdge edge) { 13 | NSRect temp; 14 | NSRect* slice2 = (slice ? slice : &temp); 15 | NSRect* rem2 = (rem ? rem : &temp); 16 | NSDivideRect(inRect, slice2, rem2, amount, edge); 17 | } 18 | -------------------------------------------------------------------------------- /source/views/JPAckSearchButton.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckSearchButton.h" 5 | 6 | @implementation JPAckSearchButton 7 | 8 | - (BOOL)performKeyEquivalent:(NSEvent*)event 9 | { 10 | if ([[event charactersIgnoringModifiers] isEqualToString:[self keyEquivalent]] && ([event modifierFlags] & NSCommandKeyMask)) 11 | { 12 | [self performClick:nil]; 13 | return YES; 14 | } 15 | 16 | return [super performKeyEquivalent:event]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /source/controllers/JPAckTypesProcess.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | extern NSString * const JPAckTypesProcessComplete; 7 | extern NSString * const kJPAckTypesResult; 8 | 9 | @interface JPAckTypesProcess : NSObject { 10 | NSMutableData* typesData; 11 | NSMutableData* errorData; 12 | NSTask* ackTask; 13 | NSInteger ackState; 14 | } 15 | 16 | - (void)invokeWithPath:(NSString*)path options:(NSArray*)options; 17 | - (void)terminate; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /source/views/JPAckResultTableView.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @interface JPAckResultTableView : NSTableView { 7 | BOOL _isDrawingStickyRow; 8 | } 9 | 10 | - (NSInteger)spanningColumnForRow:(NSInteger)rowIndex; 11 | - (CGFloat)viewportOffsetForRow:(NSInteger)rowIndex; 12 | - (void)scrollRowToVisible:(NSInteger)rowIndex withViewportOffset:(CGFloat)offset; 13 | @end 14 | 15 | @interface NSObject (JPAckResultTableViewDelegate) 16 | - (BOOL)tableView:(NSTableView *)tableView activateSelectedRow:(NSInteger)row atPoint:(NSPoint)point; 17 | - (NSInteger)tableView:(NSTableView *)tableView spanningColumnForRow:(NSInteger)row; 18 | @end 19 | -------------------------------------------------------------------------------- /source/views/JPAckControlView.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckControlView.h" 5 | 6 | #define START_COLOR_GRAY [NSColor colorWithCalibratedWhite:0.75 alpha:0.8] 7 | #define END_COLOR_GRAY [NSColor colorWithCalibratedWhite:0.90 alpha:0.5] 8 | #define BORDER_WIDTH 1.0 9 | 10 | @implementation JPAckControlView 11 | 12 | - (void)drawRect:(NSRect)rect 13 | { 14 | NSGradient *gradient = [[[NSGradient alloc] initWithStartingColor:START_COLOR_GRAY 15 | endingColor:END_COLOR_GRAY] autorelease]; 16 | [gradient drawInRect:[self bounds] angle:90.0]; 17 | 18 | NSRect lineRect = [self bounds]; 19 | lineRect.size.height = BORDER_WIDTH; 20 | [[NSColor colorWithCalibratedWhite:0.40 alpha:1.0] set]; 21 | NSRectFill(lineRect); 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.jetpackpony.AckMate 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.1.3 23 | NSPrincipalClass 24 | AckMatePlugin 25 | 26 | 27 | -------------------------------------------------------------------------------- /source/controllers/JPAckProcess.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @class JPAckResultSource; 7 | extern NSString * const JPAckProcessComplete; 8 | 9 | @interface JPAckProcess : NSObject { 10 | NSMutableData* trailing; 11 | NSMutableData* errorData; 12 | JPAckResultSource* ackResult; 13 | NSTask* ackTask; 14 | NSInteger ackState; 15 | } 16 | 17 | - (id)initWithResultHolder:(JPAckResultSource*)resultHolder; 18 | - (void)invokeWithTerm:(NSString*)term path:(NSString*)path searchFolder:(NSString*)searchFolder literal:(BOOL)literal nocase:(BOOL)nocase words:(BOOL)words context:(BOOL)context symlinks:(BOOL)symlinks folderPattern:(NSString*)folderPattern filePattern:(NSString*)filePattern options:(NSArray*)options; 19 | 20 | - (void)parseData:(NSData*)data; 21 | - (void)saveTrailing:(char*)bytes length:(NSUInteger)length; 22 | 23 | - (void)terminateImmediately:(BOOL)immediately; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /source/models/JPAckResult.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | enum { 7 | JPResultTypeError = 0, 8 | JPResultTypeFilename, 9 | JPResultTypeMatchingLine, 10 | JPResultTypeContext, 11 | JPResultTypeContextBreak 12 | }; 13 | typedef NSInteger JPAckResultType; 14 | 15 | @interface JPAckResult : NSObject { 16 | JPAckResultType resultType; 17 | NSString* lineNumber; 18 | NSString* lineContent; 19 | NSArray* matchRanges; 20 | } 21 | @property(nonatomic, readonly) JPAckResultType resultType; 22 | @property(nonatomic, readonly) NSString* lineNumber; 23 | @property(nonatomic, readonly) NSString* lineContent; 24 | @property(nonatomic, readonly) NSArray* matchRanges; 25 | 26 | + (id)resultErrorWithString:(NSString*)errorString; 27 | + (id)resultFileWithName:(NSString*)fileName; 28 | + (id)resultContextBreak; 29 | + (id)resultContextLineWithNumber:(NSString*)ln content:(NSString*)lc; 30 | + (id)resultMatchingLineWithNumber:(NSString*)ln content:(NSString*)lc ranges:(NSArray*)mr; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /source/models/JPAckResultRep.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | #import "JPAckResult.h" 6 | 7 | @interface JPAckResultRep: NSObject { 8 | JPAckResultRep* parentObject; 9 | JPAckResult* resultObject; 10 | NSMutableArray* childObjects; 11 | 12 | CGFloat constrainedWidth; 13 | CGFloat calculatedHeight; 14 | BOOL alternate; 15 | BOOL collapsed; 16 | } 17 | @property(nonatomic, readonly) JPAckResultRep* parentObject; 18 | @property(nonatomic, readonly) JPAckResult* resultObject; 19 | 20 | @property(nonatomic, assign) CGFloat constrainedWidth; 21 | @property(nonatomic, assign) CGFloat calculatedHeight; 22 | @property(nonatomic, readonly) BOOL alternate; 23 | @property(nonatomic, assign) BOOL collapsed; 24 | 25 | + (id)withResultObject:(JPAckResult*)ro parent:(JPAckResultRep*)po alternate:(BOOL)alt; 26 | + (id)withResultObject:(JPAckResult*)ro alternate:(BOOL)alt; 27 | 28 | - (NSArray*)children; 29 | - (void)addChild:(JPAckResultRep*)childObject; 30 | 31 | - (JPAckResultType)resultType; 32 | @end 33 | -------------------------------------------------------------------------------- /source/views/JPAckResultCell.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | #import "JPAckResultRep.h" 6 | 7 | // disclosure triangle size 8 | #define DISCLOSURE_TRIANGLE_DIMENSION 16 9 | 10 | // left/right padding for the cell 11 | #define RESULT_ROW_PADDING 15.0 12 | 13 | // NSTextFieldCell seems to apply this X inset silently. 14 | // Unsure why it's not reflected by titleRectForBounds... 15 | #define RESULT_TEXT_XINSET 2.0 16 | 17 | // This inset makes sure any result highlights are not 18 | // mashed up against the top edge 19 | #define RESULT_TEXT_YINSET 2.0 20 | 21 | // some interior space so that text isn't mashed up 22 | // against left/right edges 23 | #define RESULT_CONTENT_INTERIOR_PADDING 5.0 24 | 25 | @interface JPAckResultCell : NSTextFieldCell { 26 | BOOL expectsFullCellDrawingRect; 27 | JPAckResultType resultType; 28 | BOOL alternate; 29 | BOOL contentColumn; 30 | BOOL collapsed; 31 | CGFloat lineNumberWidth; 32 | } 33 | 34 | -(void)configureType:(JPAckResultType)resultType_ alternate:(BOOL)alternate_ collapsed:(BOOL)collapsed_ contentColumn:(BOOL)contentColumn_; 35 | @end 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | 3 | Portions Copyright (c) 2007-2009 Noodlesoft, LLC. All Rights Reserved. 4 | Portions Copyright (c) 2009 Steven Degutis. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.mdown: -------------------------------------------------------------------------------- 1 | ## Modifications 2 | 3 | This is is a **modified version** of [AckMate](https://github.com/protocool/AckMate) Once/if [protocool](https://github.com/protocool) pulls these changes, I recommend switching back to his git repo. That said, some of my changes violate his rule about not changing ack; if they never make the cut, you'll still be able to find a copy here. 4 | 5 | Changes include: 6 | - Added standard TextMate folder references via ack -G 7 | - Updated the XCode build configuration for 10.6 (and modified ragel path to avoid build confusion) 8 | - Added better UTF-8 error output 9 | 10 | ## Description 11 | 12 | **AckMate** is a [TextMate](http://macromates.com/) plugin for running [Ack](http://betterthangrep.com) in a Cocoa window. 13 | 14 | *This plugin is only compatible with Mac OS X 10.6 and above.* 15 | 16 | ## Binary Installation 17 | 18 | - Download the latest release from the [downloads section](http://github.com/jswartwood/AckMate/downloads) 19 | - Save any unsaved documents you have open in TextMate 20 | - Unpack the zip file and double-click the AckMate.tmplugin icon 21 | 22 | ## Usage 23 | 24 | Check the [wiki](https://github.com/protocool/AckMate/wiki) for usage instructions. 25 | 26 | ## Building From Source 27 | 28 | AckMate's build process requires [Ragel](http://www.complang.org/ragel/) to be in */usr/local/bin* if you have it installed elsewhere please update XCode configuration or symlink to your install location. 29 | 30 | Installing Ragel is easiest with [Homebrew](http://mxcl.github.com/homebrew/) 31 | 32 | brew install ragel 33 | 34 | ## Contributing 35 | 36 | I'm all ears... send pull requests if you have them. 37 | 38 | If you find AckMate useful, donate to [protcool](http://pledgie.com/campaigns/9779) (the original author) 39 | -------------------------------------------------------------------------------- /source/controllers/JPAckResultSource.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @class JPAckResultRep; 7 | @class JPAckWindowController; 8 | @class JPAckResultTableView; 9 | 10 | @interface JPAckResultSource : NSObject { 11 | NSUInteger matchedFiles; 12 | NSUInteger matchedLines; 13 | NSMutableArray* resultRows; 14 | 15 | NSDictionary* headingAttributes; 16 | NSDictionary* bodyAttributes; 17 | NSDictionary* bodyNowrapAttributes; 18 | NSDictionary* bodyHighlightAttributes; 19 | 20 | IBOutlet JPAckResultTableView* resultView; 21 | IBOutlet JPAckWindowController* windowController; 22 | 23 | JPAckResultRep* currentResultFileRep; 24 | 25 | NSString* resultStats; 26 | BOOL searchingSelection; 27 | NSString* searchRoot; 28 | BOOL alternateRow; 29 | 30 | NSString* longestLineNumber; 31 | 32 | CGFloat headerHeight; 33 | CGFloat contextBreakHeight; 34 | } 35 | 36 | @property(nonatomic, copy) NSString* searchRoot; 37 | @property(nonatomic, readonly) NSUInteger matchedFiles; 38 | @property(nonatomic, readonly) NSUInteger matchedLines; 39 | @property(nonatomic, copy, readonly) NSString* resultStats; 40 | 41 | - (void)clearContents; 42 | - (void)updateStats; 43 | - (void)searchingFor:(NSString*)term inRoot:(NSString*)searchRoot inFolder:(NSString*)searchFolder; 44 | - (void)parsedError:(NSString*)errorString; 45 | - (void)parsedFilename:(NSString*)filename; 46 | - (void)parsedContextLine:(NSString*)lineNumber content:(NSString*)content; 47 | - (void)parsedMatchLine:(NSString*)lineNumber ranges:(NSArray*)ranges content:(NSString*)content; 48 | - (void)parsedContextBreak; 49 | 50 | - (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)row; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /source/models/JPAckResultRep.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckResultRep.h" 5 | 6 | @interface JPAckResultRep () 7 | - (id)initWithResultObject:(JPAckResult*)resultObject_ parent:(JPAckResultRep*)parentObject_ alternate:(BOOL)alternate_; 8 | @end; 9 | 10 | @implementation JPAckResultRep 11 | @synthesize parentObject; 12 | @synthesize resultObject; 13 | 14 | @synthesize constrainedWidth; 15 | @synthesize calculatedHeight; 16 | @synthesize alternate; 17 | @synthesize collapsed; 18 | 19 | + (id)withResultObject:(JPAckResult*)ro parent:(JPAckResultRep*)po alternate:(BOOL)alt 20 | { 21 | return [[[JPAckResultRep alloc] initWithResultObject:ro parent:po alternate:alt] autorelease]; 22 | } 23 | 24 | + (id)withResultObject:(JPAckResult*)ro alternate:(BOOL)alt 25 | { 26 | return [[[JPAckResultRep alloc] initWithResultObject:ro parent:nil alternate:alt] autorelease]; 27 | } 28 | 29 | - (id)initWithResultObject:(JPAckResult*)resultObject_ parent:(JPAckResultRep*)parentObject_ alternate:(BOOL)alternate_ 30 | { 31 | if (self = [super init]) 32 | { 33 | parentObject = [parentObject_ retain]; 34 | resultObject = [resultObject_ retain]; 35 | alternate = alternate_; 36 | 37 | if (parentObject) 38 | [parentObject addChild:self]; 39 | } 40 | return self; 41 | } 42 | 43 | - (NSString*)description 44 | { 45 | return resultObject.lineContent; 46 | } 47 | 48 | - (JPAckResultType)resultType 49 | { 50 | return resultObject.resultType; 51 | } 52 | 53 | - (NSArray*)children 54 | { 55 | return childObjects; 56 | } 57 | 58 | - (void)addChild:(JPAckResultRep*)childObject 59 | { 60 | if (!childObjects) 61 | childObjects = [[NSMutableArray alloc] initWithCapacity:1]; 62 | 63 | [childObjects addObject:childObject]; 64 | } 65 | 66 | - (void)dealloc 67 | { 68 | [parentObject release], parentObject = nil; 69 | [resultObject release], resultObject = nil; 70 | [childObjects release], childObjects = nil; 71 | [super dealloc]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /source/models/JPAckResult.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckResult.h" 5 | 6 | @interface JPAckResult () 7 | - (id)initWithType:(JPAckResultType)resultType_ lineNumber:(NSString*)lineNumber_ content:(NSString*)lineContent_ ranges:(NSArray*)matchRanges_; 8 | @end 9 | 10 | @implementation JPAckResult 11 | @synthesize resultType; 12 | @synthesize lineNumber; 13 | @synthesize lineContent; 14 | @synthesize matchRanges; 15 | 16 | + (id)resultErrorWithString:(NSString*)errorString 17 | { 18 | return [[[JPAckResult alloc] initWithType:JPResultTypeError lineNumber:nil content:errorString ranges:nil ] autorelease]; 19 | } 20 | 21 | + (id)resultFileWithName:(NSString*)fileName_ 22 | { 23 | NSString* fileName = ([fileName_ hasPrefix:@"/"]) ? [fileName_ substringFromIndex:1] : fileName_; // blech 24 | return [[[JPAckResult alloc] initWithType:JPResultTypeFilename lineNumber:nil content:fileName ranges:nil ] autorelease]; 25 | } 26 | 27 | + (id)resultContextBreak 28 | { 29 | return [[[JPAckResult alloc] initWithType:JPResultTypeContextBreak lineNumber:nil content:nil ranges:nil ] autorelease]; 30 | } 31 | 32 | + (id)resultContextLineWithNumber:(NSString*)ln content:(NSString*)lc 33 | { 34 | return [[[JPAckResult alloc] initWithType:JPResultTypeContext lineNumber:ln content:lc ranges:nil] autorelease]; 35 | } 36 | 37 | + (id)resultMatchingLineWithNumber:(NSString*)ln content:(NSString*)lc ranges:(NSArray*)mr 38 | { 39 | return [[[JPAckResult alloc] initWithType:JPResultTypeMatchingLine lineNumber:ln content:lc ranges:mr] autorelease]; 40 | } 41 | 42 | - (id)initWithType:(JPAckResultType)resultType_ lineNumber:(NSString*)lineNumber_ content:(NSString*)lineContent_ ranges:(NSArray*)matchRanges_ 43 | { 44 | if (self = [super init]) 45 | { 46 | resultType = resultType_; 47 | lineNumber = [lineNumber_ copy]; 48 | lineContent = [lineContent_ copy]; 49 | matchRanges = [matchRanges_ copy]; 50 | } 51 | return self; 52 | } 53 | 54 | - (void)dealloc 55 | { 56 | [lineNumber release], lineNumber = nil; 57 | [lineContent release], lineContent = nil; 58 | [matchRanges release], matchRanges = nil; 59 | [super dealloc]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /source/external/NSImage-NoodleExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage-NoodleExtensions.m 3 | // NoodleKit 4 | // 5 | // Created by Paul Kim on 3/24/07. 6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | #import "NSImage-NoodleExtensions.h" 30 | 31 | 32 | @implementation NSImage (NoodleExtensions) 33 | 34 | - (void)drawAdjustedAtPoint:(NSPoint)aPoint fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta 35 | { 36 | NSSize size = [self size]; 37 | 38 | [self drawAdjustedInRect:NSMakeRect(aPoint.x, aPoint.y, size.width, size.height) fromRect:srcRect operation:op fraction:delta]; 39 | } 40 | 41 | - (void)drawAdjustedInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta 42 | { 43 | NSGraphicsContext *context; 44 | BOOL contextIsFlipped; 45 | 46 | context = [NSGraphicsContext currentContext]; 47 | contextIsFlipped = [context isFlipped]; 48 | 49 | if (contextIsFlipped) 50 | { 51 | NSAffineTransform *transform; 52 | 53 | [context saveGraphicsState]; 54 | 55 | // Flip the coordinate system back. 56 | transform = [NSAffineTransform transform]; 57 | [transform translateXBy:0 yBy:NSMaxY(dstRect)]; 58 | [transform scaleXBy:1 yBy:-1]; 59 | [transform concat]; 60 | 61 | // The transform above places the y-origin right where the image should be drawn. 62 | dstRect.origin.y = 0.0; 63 | } 64 | 65 | [self drawInRect:dstRect fromRect:srcRect operation:op fraction:delta]; 66 | 67 | if (contextIsFlipped) 68 | { 69 | [context restoreGraphicsState]; 70 | } 71 | } 72 | 73 | - (NSImage *)unflippedImage 74 | { 75 | if ([self isFlipped]) 76 | { 77 | NSImage *newImage; 78 | 79 | newImage = [[[NSImage alloc] initWithSize:[self size]] autorelease]; 80 | [newImage lockFocus]; 81 | [self drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; 82 | [newImage unlockFocus]; 83 | 84 | return newImage; 85 | } 86 | return self; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /source/controllers/JPAckWindowController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import 5 | 6 | @class JPAckResultSource; 7 | @class JPAckProcess; 8 | @class JPAckTypesProcess; 9 | 10 | extern NSString * const kJPAckLiteral; 11 | extern NSString * const kJPAckShowAdvanced; 12 | extern NSString * const kJPAckNoCase; 13 | extern NSString * const kJPAckMatchWords; 14 | extern NSString * const kJPAckShowContext; 15 | extern NSString * const kJPAckFollowSymlinks; 16 | extern NSString * const kJPAckFolderReferences; 17 | extern NSString * const kJPAckSearchHistory; 18 | extern NSString * const kJPAckSearchOptions; 19 | extern NSString * const kJPAckWindowPosition; 20 | 21 | @interface JPAckWindowController : NSWindowController { 22 | IBOutlet JPAckResultSource* ackResult; 23 | IBOutlet NSTokenField* optionsField; 24 | IBOutlet NSComboBox* searchTermField; 25 | IBOutlet NSButton* showContextButton; 26 | IBOutlet NSButton* followSymlinksButton; 27 | IBOutlet NSButton* useFolderReferencesButton; 28 | IBOutlet NSButton* advancedDisclosure; 29 | IBOutlet NSButton* advancedButton; 30 | IBOutlet NSBox* optionsBox; 31 | IBOutlet NSView* controlView; 32 | IBOutlet NSScrollView* resultsView; 33 | 34 | NSInteger pasteboardChangeCount; 35 | NSString* projectDirectory; 36 | NSString* selectedSearchFolder; 37 | BOOL selectionSearch; 38 | NSString* fileName; 39 | 40 | NSMutableDictionary* preferences; 41 | NSArray* history; 42 | NSArray* ackTypes; 43 | 44 | NSString* term; 45 | BOOL showAdvanced; 46 | BOOL nocase; 47 | BOOL literal; 48 | BOOL words; 49 | BOOL context; 50 | BOOL symlinks; 51 | BOOL folders; 52 | id projectController; 53 | JPAckProcess* currentProcess; 54 | JPAckTypesProcess* currentTypesProcess; 55 | } 56 | @property(nonatomic, readonly, copy) NSString* projectDirectory; 57 | @property(nonatomic, readonly, copy) NSString* fileName; 58 | @property(nonatomic, readonly, copy) NSArray* history; 59 | @property(nonatomic, copy) NSString* term; 60 | @property(nonatomic, assign) BOOL showAdvanced; 61 | @property(nonatomic, assign) BOOL nocase; 62 | @property(nonatomic, assign) BOOL literal; 63 | @property(nonatomic, assign) BOOL words; 64 | @property(nonatomic, assign) BOOL context; 65 | @property(nonatomic, assign) BOOL symlinks; 66 | @property(nonatomic, assign) BOOL folders; 67 | 68 | - (id)initWithProjectDirectory:(NSString*)directory controller:(id)controller preferences:(NSMutableDictionary*)prefs; 69 | - (void)showAndActivate; 70 | - (IBAction)cleanseOptionsField:(id)sender; 71 | - (void)openProjectFile:(NSString*)file atLine:(NSString*)line selectionRange:(NSRange)selectionRange; 72 | - (IBAction)performSearch:(id)sender; 73 | - (IBAction)cancel:(id)sender; 74 | - (BOOL)running; 75 | - (NSString*)windowTitle; 76 | - (void)cleanupImmediately:(BOOL)immediately; 77 | @end 78 | 79 | @interface NSObject (AckMateCompilerSilencing) 80 | - (void)openFiles:(id)farray; 81 | - (void)goToLineNumber:(id)line; 82 | - (void)goToColumnNumber:(id)col; 83 | - (void)selectToLine:(id)line andColumn:(id)col; 84 | - (id)environmentVariables; 85 | - (id)document; 86 | @end -------------------------------------------------------------------------------- /source/controllers/JPAckProcess+Parsing.m.rl: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckProcess+Parsing.h" 5 | #import "JPAckResultSource.h" 6 | 7 | #define NSUTF8STRING(pFirstChar, pLastChar) [[[NSString alloc] initWithBytes:pFirstChar length:(pLastChar - pFirstChar) encoding:NSUTF8StringEncoding] autorelease] 8 | 9 | @interface JPAckProcess (ParsingPrivate) 10 | - (void)extractResult:(char*)line length:(NSUInteger)length; 11 | @end 12 | 13 | @implementation JPAckProcess (Parsing) 14 | 15 | %%{ 16 | machine lineconsumer; 17 | LF = '\n'; 18 | 19 | action mark { mark = fpc; } 20 | action process_line { [self extractResult:mark length:(fpc - mark)]; } 21 | action partial_line { [self saveTrailing:mark length:(fpc - mark)]; } 22 | 23 | input_line = ( any -- LF )** >mark LF; 24 | InputLines = (input_line %process_line)+ @!partial_line; 25 | 26 | main := InputLines; 27 | }%% 28 | 29 | %% write data; 30 | 31 | - (void)consumeInputLines:(NSData*)data 32 | { 33 | NSUInteger length = [data length]; 34 | char* bytes = (char*)[data bytes]; 35 | 36 | char *p = bytes; 37 | char *pe = bytes + length; 38 | char *eof = pe; 39 | char *mark = NULL; 40 | int cs; 41 | 42 | %% write init; 43 | %% write exec; 44 | } 45 | 46 | %%{ 47 | # this machine gets lines in the following form: 48 | # :some/FileName.whateverESC[0m 49 | # 23: some context text 50 | # 24: more context text 51 | # 25;1 4,5 4: A matching Line we are interested in has ; and optional ranges 52 | # 26: more context text 53 | # -- 54 | # 35: 55 | # 36;2 1: another Line 56 | # :another/FileNameMaybeWithBogusEscapes.whatever 57 | # 1;: Just one Line and there's no ranges 58 | 59 | machine resultextractor; 60 | 61 | LF = "\n"; 62 | 63 | action mark { mark = fpc; } 64 | 65 | action save_line_number { 66 | currentLineNumber = NSUTF8STRING(mark, p); 67 | } 68 | 69 | action save_context_break { 70 | [ackResult parsedContextBreak]; 71 | } 72 | 73 | action save_filename_content { 74 | [ackResult parsedFilename:NSUTF8STRING(mark, p)]; 75 | } 76 | 77 | action save_context_content { 78 | [ackResult parsedContextLine:currentLineNumber content:NSUTF8STRING(mark, p)]; 79 | } 80 | 81 | action save_match_content { 82 | [ackResult parsedMatchLine:currentLineNumber ranges:currentRanges content:NSUTF8STRING(mark, p)]; 83 | } 84 | 85 | action save_range_content { 86 | if (!currentRanges) 87 | currentRanges = [NSMutableArray array]; 88 | 89 | [currentRanges addObject:NSUTF8STRING(mark, p)]; 90 | } 91 | 92 | filename_content = (any -- LF)** >mark %save_filename_content; 93 | line_number = digit+ >mark %save_line_number; 94 | context_content = (any -- LF)** >mark %save_context_content; 95 | match_content = (any -- LF)** >mark %save_match_content; 96 | range_content = (digit+ >mark " " digit+) %save_range_content; 97 | 98 | FilenameResult = ":" filename_content LF; 99 | ContextResult = line_number ":" context_content LF; 100 | MatchResult = line_number ";" (range_content (',')?)** ":" match_content LF; 101 | ContextBreak = ("--" . LF) %save_context_break; 102 | 103 | main := |* 104 | FilenameResult; 105 | ContextResult; 106 | ContextBreak; 107 | MatchResult; 108 | *|; 109 | }%% 110 | 111 | %% write data; 112 | 113 | - (void)extractResult:(char*)bytes length:(NSUInteger)length 114 | { 115 | NSString* currentLineNumber = nil; 116 | NSMutableArray* currentRanges = nil; 117 | 118 | char *p = bytes; 119 | char *pe = bytes + length; 120 | char* ts; 121 | char* te; 122 | int act; 123 | char *eof = pe; 124 | char *mark = NULL; 125 | int cs; 126 | 127 | %% write init; 128 | %% write exec; 129 | } 130 | 131 | @end 132 | 133 | -------------------------------------------------------------------------------- /source/external/NSTableView+NoodleExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSTableView+NoodleExtensions.h 3 | // NoodleKit 4 | // 5 | // Created by Paul Kim on 10/22/09. 6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NSUInteger NoodleStickyRowTransition; 12 | 13 | enum 14 | { 15 | NoodleStickyRowTransitionNone, 16 | NoodleStickyRowTransitionFadeIn 17 | }; 18 | 19 | 20 | @interface NSTableView (NoodleExtensions) 21 | 22 | #pragma mark Sticky Row Header methods 23 | // Note: see NoodleTableView's -drawRect on how to hook in this functionality in a subclass 24 | 25 | /* 26 | Currently set to any groups rows (as dictated by the delegate). The 27 | delegate can implement -tableView:isStickyRow: to override this. 28 | */ 29 | - (BOOL)isRowSticky:(NSInteger)rowIndex; 30 | 31 | /* 32 | Does the actual drawing of the sticky row. Override if you want a custom look. 33 | You shouldn't invoke this directly. See -drawStickyRowHeader. 34 | */ 35 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect; 36 | 37 | /* 38 | Draws the sticky row at the top of the table. You have to override -drawRect 39 | and call this method, that being all you need to get the sticky row stuff 40 | to work in your subclass. Look at NoodleStickyRowTableView. 41 | Note that you shouldn't need to override this. To modify the look of the row, 42 | override -drawStickyRow: instead. 43 | */ 44 | - (void)drawStickyRowHeader; 45 | 46 | /* 47 | Returns the rect of the sticky view header. Will return NSZeroRect if there is no current 48 | sticky row. 49 | */ 50 | - (NSRect)stickyRowHeaderRect; 51 | 52 | /* 53 | Does an animated scroll to the current sticky row. Clicking on the sticky 54 | row header will trigger this. 55 | */ 56 | - (IBAction)peformClickOnStickyRow:(id)sender; 57 | 58 | // subclass overrides for custom handling 59 | - (void)clickedStickyRow:(NSInteger)row; 60 | 61 | /* 62 | Returns what kind of transition you want when the row becomes sticky. Fade-in 63 | is the default. 64 | */ 65 | - (NoodleStickyRowTransition)stickyRowHeaderTransition; 66 | 67 | #pragma mark Row Spanning methods 68 | 69 | /* 70 | Returns the range of the span at the given column and row indexes. The span is determined by 71 | a range of contiguous rows having the same object value. 72 | */ 73 | - (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex; 74 | 75 | @end 76 | 77 | @class NoodleRowSpanningCell; 78 | 79 | @interface NSTableColumn (NoodleExtensions) 80 | 81 | #pragma mark Row Spanning methods 82 | /* 83 | Returns whether this column will try to consolidate rows into spans. 84 | */ 85 | - (BOOL)isRowSpanningEnabled; 86 | 87 | /* 88 | Returns the cell used to draw the spanning regions. Default implementation returns nil. 89 | */ 90 | - (NoodleRowSpanningCell *)spanningCell; 91 | 92 | @end 93 | 94 | 95 | @interface NSOutlineView (NoodleExtensions) 96 | 97 | #pragma mark Sticky Row Header methods 98 | /* 99 | Currently set to any groups rows (or as dictated by the delegate). The 100 | delegate can implement -outlineView:isStickyRow: to override this. 101 | */ 102 | - (BOOL)isRowSticky:(NSInteger)rowIndex; 103 | 104 | @end 105 | 106 | 107 | @interface NSObject (NoodleStickyRowDelegate) 108 | 109 | // So that the sticky header also shows context menus 110 | - (NSMenu*)tableView:(NSTableView *)tableView contextMenuForRow:(NSInteger)rowIndex; 111 | 112 | /* 113 | Allows the delegate to specify if a row is sticky. By default, group rows 114 | are sticky. The delegate can override that by implementing this method. 115 | */ 116 | - (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)rowIndex; 117 | 118 | /* 119 | Allows the delegate to specify whether a certain cell should be drawn in the sticky row header 120 | */ 121 | - (BOOL)tableView:(NSTableView *)tableView shouldDisplayCellInStickyRowHeaderForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex; 122 | 123 | /* 124 | Same as above but for outline views. 125 | */ 126 | - (BOOL)outlineView:(NSOutlineView *)outlineView isStickyItem:(id)item; 127 | 128 | @end 129 | 130 | @interface StickyRowButton : NSButton { 131 | } 132 | @end -------------------------------------------------------------------------------- /source/controllers/JPAckTypesProcess.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckTypesProcess.h" 5 | 6 | @interface JPAckTypesProcess () 7 | @property(retain) NSMutableData* typesData; 8 | @property(retain) NSMutableData* errorData; 9 | @property(retain) NSTask* ackTask; 10 | - (void)handleStateEvent:(NSInteger)eventType; 11 | @end 12 | 13 | @implementation JPAckTypesProcess 14 | 15 | NSString * const JPAckTypesProcessComplete = @"JPAckTypesProcessComplete"; 16 | NSString * const kJPAckTypesResult = @"kJPAckTypesResult"; 17 | 18 | @synthesize typesData; 19 | @synthesize errorData; 20 | @synthesize ackTask; 21 | 22 | enum { 23 | ackInitial = 0, 24 | ackStdOutClosed = 1<<0, 25 | ackStdErrClosed = 1<<1, 26 | ackTerminated = 1<<2, 27 | ackComplete = (ackStdOutClosed | ackStdErrClosed | ackTerminated) 28 | } ackStates; 29 | 30 | 31 | - (id)init 32 | { 33 | if (self = [super init]) 34 | { 35 | errorData = nil; 36 | typesData = nil; 37 | ackTask = nil; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)invokeWithPath:(NSString*)path options:(NSArray*)options 43 | { 44 | ackState = ackInitial; 45 | 46 | self.ackTask = [[[NSTask alloc] init] autorelease]; 47 | 48 | NSString* ackmateAck = [[[NSBundle bundleForClass:self.class] resourcePath] stringByAppendingPathComponent:@"ackmate_ack"]; 49 | 50 | [self.ackTask setCurrentDirectoryPath:path]; 51 | 52 | [self.ackTask setLaunchPath:@"/usr/bin/env"]; 53 | NSMutableArray* args = [NSMutableArray arrayWithObjects:@"perl", ackmateAck, nil]; 54 | 55 | for (NSString* typeOption in options) 56 | { 57 | if ([typeOption hasPrefix:@"--type-set="] || [typeOption hasPrefix:@"--type-add="]) 58 | [args addObject:typeOption]; 59 | } 60 | 61 | [args addObject:@"--ackmate-types"]; 62 | 63 | [self.ackTask setArguments:args]; 64 | 65 | NSPipe* stdoutPipe = [NSPipe pipe]; 66 | NSPipe* stderrPipe = [NSPipe pipe]; 67 | 68 | [self.ackTask setStandardInput:[NSFileHandle fileHandleWithNullDevice]]; 69 | [self.ackTask setStandardError:stderrPipe]; 70 | [self.ackTask setStandardOutput:stdoutPipe]; 71 | 72 | [self.ackTask launch]; 73 | 74 | NSFileHandle* stdoutFileHandle = [stdoutPipe fileHandleForReading]; 75 | NSFileHandle* stderrFileHandle = [stderrPipe fileHandleForReading]; 76 | 77 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultData:) name:NSFileHandleReadCompletionNotification object:stdoutFileHandle]; 78 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(errorData:) name:NSFileHandleReadCompletionNotification object:stderrFileHandle]; 79 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskEnded:) name:NSTaskDidTerminateNotification object:ackTask]; 80 | 81 | [stdoutFileHandle readInBackgroundAndNotify]; 82 | [stderrFileHandle readInBackgroundAndNotify]; 83 | } 84 | 85 | - (void)handleStateEvent:(NSInteger)eventType 86 | { 87 | ackState |= eventType; 88 | if (ackState == ackComplete) 89 | { 90 | self.ackTask = nil; 91 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 92 | 93 | if (errorData) 94 | { 95 | NSLog(@"AckMate: error reading ack types: %@", [[[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding] autorelease]); 96 | [[NSNotificationCenter defaultCenter] postNotificationName:JPAckTypesProcessComplete object:self userInfo:nil]; 97 | } 98 | else 99 | { 100 | NSString* returnedTypes = [[[NSString alloc] initWithData:typesData encoding:NSUTF8StringEncoding] autorelease]; 101 | NSArray* typesArray = [returnedTypes componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; 102 | NSDictionary* info = [NSDictionary dictionaryWithObject:typesArray forKey:kJPAckTypesResult]; 103 | [[NSNotificationCenter defaultCenter] postNotificationName:JPAckTypesProcessComplete object:self userInfo:info]; 104 | } 105 | } 106 | } 107 | 108 | - (void)resultData:(NSNotification*)notification 109 | { 110 | NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; 111 | if ([data length] > 0) 112 | { 113 | if (!typesData) 114 | self.typesData = [NSMutableData data]; 115 | 116 | [typesData appendData:data]; 117 | [[notification object] readInBackgroundAndNotify]; 118 | } 119 | else 120 | [self handleStateEvent:ackStdOutClosed]; 121 | } 122 | 123 | - (void)errorData:(NSNotification*)notification 124 | { 125 | NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; 126 | if ([data length] > 0) 127 | { 128 | if (!errorData) 129 | self.errorData = [NSMutableData data]; 130 | 131 | [errorData appendData:data]; 132 | [[notification object] readInBackgroundAndNotify]; 133 | } 134 | else 135 | [self handleStateEvent:ackStdErrClosed]; 136 | } 137 | 138 | - (void)taskEnded:(NSNotification*)notification 139 | { 140 | if ([notification object] == self.ackTask) 141 | [self handleStateEvent:ackTerminated]; 142 | } 143 | 144 | - (void)terminate 145 | { 146 | [self.ackTask terminate]; 147 | } 148 | 149 | - (void)dealloc 150 | { 151 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 152 | [errorData release], errorData = nil; 153 | [typesData release], typesData = nil; 154 | [ackTask release], ackTask = nil; 155 | [super dealloc]; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /source/external/NSImage-NoodleExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage-NoodleExtensions.h 3 | // NoodleKit 4 | // 5 | // Created by Paul Kim on 3/24/07. 6 | // Copyright 2007-2009 Noodlesoft, LLC. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person 9 | // obtaining a copy of this software and associated documentation 10 | // files (the "Software"), to deal in the Software without 11 | // restriction, including without limitation the rights to use, 12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the 14 | // Software is furnished to do so, subject to the following 15 | // conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | // OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | #import 30 | 31 | 32 | /* 33 | This category provides methods for dealing with flipped images. These should draw images correctly regardless of 34 | whether the current context or the current image are flipped. Unless you know what you are doing, these should be used 35 | in lieu of the normal NSImage drawing/compositing methods. 36 | 37 | For more details, check out the related blog post at http://www.noodlesoft.com/blog/2009/02/02/understanding-flipped-coordinate-systems/ 38 | */ 39 | 40 | @interface NSImage (NoodleExtensions) 41 | 42 | /*! 43 | @method drawAdjustedAtPoint:fromRect:operation:fraction: 44 | @abstract Draws all or part of the image at the specified point in the current coordinate system. Unlike other methods in NSImage, this will orient the image properly in flipped coordinate systems. 45 | @param point The location in the current coordinate system at which to draw the image. 46 | @param srcRect The source rectangle specifying the portion of the image you want to draw. The coordinates of this rectangle are specified in the image's own coordinate system. If you pass in NSZeroRect, the entire image is drawn. 47 | @param op The compositing operation to use when drawing the image. See the NSCompositingOperation constants. 48 | @param delta The opacity of the image, specified as a value from 0.0 to 1.0. Specifying a value of 0.0 draws the image as fully transparent while a value of 1.0 draws the image as fully opaque. Values greater than 1.0 are interpreted as 1.0. 49 | @discussion The image content is drawn at its current resolution and is not scaled unless the CTM of the current coordinate system itself contains a scaling factor. The image is otherwise positioned and oriented using the current coordinate system, except that it takes the flipped status into account, drawing right-side-up in a such a case. 50 | 51 | Unlike the compositeToPoint:fromRect:operation: and compositeToPoint:fromRect:operation:fraction: methods, this method checks the rectangle you pass to the srcRect parameter and makes sure it does not lie outside the image bounds. 52 | */ 53 | - (void)drawAdjustedAtPoint:(NSPoint)aPoint fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta; 54 | 55 | /*! 56 | @method drawAdjustedInRect:fromRect:operation:fraction: 57 | @abstract Draws all or part of the image in the specified rectangle in the current coordinate system. Unlike other methods in NSImage, this will orient the image properly in flipped coordinate systems. 58 | @param dstRect The rectangle in which to draw the image, specified in the current coordinate system. 59 | @param srcRect The source rectangle specifying the portion of the image you want to draw. The coordinates of this rectangle must be specified using the image's own coordinate system. If you pass in NSZeroRect, the entire image is drawn. 60 | @param op The compositing operation to use when drawing the image. See the NSCompositingOperation constants. 61 | @param delta The opacity of the image, specified as a value from 0.0 to 1.0. Specifying a value of 0.0 draws the image as fully transparent while a value of 1.0 draws the image as fully opaque. Values greater than 1.0 are interpreted as 1.0. 62 | @discussion If the srcRect and dstRect rectangles have different sizes, the source portion of the image is scaled to fit the specified destination rectangle. The image is otherwise positioned and oriented using the current coordinate system, except that it takes the flipped status into account, drawing right-side-up in a such a case. 63 | 64 | Unlike the compositeToPoint:fromRect:operation: and compositeToPoint:fromRect:operation:fraction: methods, this method checks the rectangle you pass to the srcRect parameter and makes sure it does not lie outside the image bounds. 65 | */ 66 | - (void)drawAdjustedInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)delta; 67 | 68 | /*! 69 | @method unflippedImage 70 | @abstract Returns a version of the receiver but unflipped. 71 | @discussion This does not actually flip the image but returns an image with the same orientation but with an unflipped coordinate system internally (isFlipped returns NO). If the image is already unflipped, this method returns self. 72 | */ 73 | - (NSImage *)unflippedImage; 74 | 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /source/views/JPAckResultCell.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckResultCell.h" 5 | #import "JPAckResultTableView.h" 6 | #import "JPAckResultRep.h" 7 | #import "SDFoundation.h" 8 | #import "NSImage-NoodleExtensions.h" 9 | 10 | @interface JPAckResultCell () 11 | - (void)drawFillWithFrame:(NSRect)cellFrame inView:(NSView*)controlView; 12 | - (NSRect)drawingRectForBounds:(NSRect)theRect honestly:(BOOL)honestly; 13 | @end 14 | 15 | @implementation JPAckResultCell 16 | 17 | static NSImage* sectionExpanded = nil; 18 | static NSImage* sectionCollapsed = nil; 19 | 20 | + (void)initialize 21 | { 22 | // Load up our bundle images 23 | NSBundle* pluginBundle = [NSBundle bundleForClass:self]; 24 | 25 | NSString* expandedImagePath = [pluginBundle pathForResource:@"ackmateExpanded" ofType:@"pdf"]; 26 | sectionExpanded = [[NSImage alloc] initWithContentsOfFile:expandedImagePath]; 27 | NSString* collapsedImagePath = [pluginBundle pathForResource:@"ackmateCollapsed" ofType:@"pdf"]; 28 | sectionCollapsed = [[NSImage alloc] initWithContentsOfFile:collapsedImagePath]; 29 | } 30 | 31 | - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView 32 | { 33 | [self drawFillWithFrame:cellFrame inView:controlView]; 34 | [super drawInteriorWithFrame:cellFrame inView:controlView]; 35 | } 36 | 37 | - (void)drawFillWithFrame:(NSRect)cellFrame inView:(NSView*)controlView 38 | { 39 | NSColor* fill = nil; 40 | NSRect lcrect = [self drawingRectForBounds:cellFrame honestly:YES]; 41 | 42 | if (contentColumn && resultType == JPResultTypeFilename) 43 | { 44 | NSRect paddingRect; 45 | SDDivideRect(lcrect, &paddingRect, &lcrect, RESULT_ROW_PADDING, NSMinYEdge); 46 | [[(JPAckResultTableView*)controlView backgroundColor] set]; 47 | NSRectFill(paddingRect); 48 | 49 | fill = [NSColor colorWithCalibratedWhite:0.90 alpha:1.0]; 50 | } 51 | else if (!contentColumn && resultType != JPResultTypeContextBreak) 52 | fill = [NSColor colorWithCalibratedWhite:0.90 alpha:1.0]; 53 | else if ([self isHighlighted]) 54 | fill = [NSColor colorWithCalibratedRed:(190.0/255.0) green:(220.0/255.0) blue:1.0 alpha:1.0]; 55 | else if (resultType == JPResultTypeError) 56 | fill = [NSColor colorWithCalibratedRed:0.93 green:0.4 blue:0.4 alpha:1.0]; 57 | else if (alternate) 58 | fill = [NSColor colorWithCalibratedRed:0.93 green:0.93 blue:1.0 alpha:1.0]; 59 | 60 | if (fill) 61 | { 62 | [fill set]; 63 | NSRectFill(lcrect); 64 | } 65 | 66 | // After the fill has been drawn, blat the disclosure triangle on there 67 | if (contentColumn && resultType == JPResultTypeFilename) 68 | { 69 | NSPoint imageOrigin = lcrect.origin; 70 | imageOrigin.x += RESULT_CONTENT_INTERIOR_PADDING; 71 | imageOrigin.y += ceil((lcrect.size.height - DISCLOSURE_TRIANGLE_DIMENSION) / 2); 72 | NSImage* drawImage = (collapsed) ? sectionCollapsed : sectionExpanded; 73 | [drawImage drawAdjustedAtPoint:imageOrigin fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 74 | } 75 | } 76 | 77 | - (NSColor*)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView 78 | { 79 | return nil; 80 | } 81 | 82 | - (NSBackgroundStyle)interiorBackgroundStyle 83 | { 84 | return NSBackgroundStyleLight; 85 | } 86 | 87 | - (void)selectWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject start:(int)selStart length:(int)selLength 88 | { 89 | aRect = [self drawingRectForBounds:aRect]; 90 | expectsFullCellDrawingRect = YES; 91 | [super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength]; 92 | expectsFullCellDrawingRect = NO; 93 | } 94 | 95 | - (void)editWithFrame:(NSRect)aRect inView:(NSView*)controlView editor:(NSText*)textObj delegate:(id)anObject event:(NSEvent*)theEvent 96 | { 97 | aRect = [self drawingRectForBounds:aRect]; 98 | expectsFullCellDrawingRect = YES; 99 | [super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent]; 100 | expectsFullCellDrawingRect = NO; 101 | } 102 | 103 | - (NSText*)setUpFieldEditorAttributes:(NSText*)textObj 104 | { 105 | [textObj setSelectable:NO]; 106 | [textObj setEditable:NO]; 107 | [textObj setFocusRingType:NSFocusRingTypeNone]; 108 | [(NSTextView*)textObj setDrawsBackground:NO]; 109 | return textObj; 110 | } 111 | 112 | - (NSRect)drawingRectForBounds:(NSRect)theRect 113 | { 114 | return [self drawingRectForBounds:theRect honestly:expectsFullCellDrawingRect]; 115 | } 116 | 117 | - (NSRect)drawingRectForBounds:(NSRect)theRect honestly:(BOOL)honestly 118 | { 119 | NSRect drect = [super drawingRectForBounds:theRect]; 120 | 121 | if (honestly) return drect; 122 | 123 | if(resultType == JPResultTypeFilename) 124 | { 125 | SDDivideRect(drect, nil, &drect, DISCLOSURE_TRIANGLE_DIMENSION, NSMinXEdge); 126 | SDDivideRect(drect, nil, &drect, RESULT_ROW_PADDING, NSMinYEdge); 127 | } 128 | 129 | return NSInsetRect(drect, RESULT_CONTENT_INTERIOR_PADDING, RESULT_TEXT_YINSET); 130 | } 131 | 132 | 133 | -(void)configureType:(JPAckResultType)resultType_ alternate:(BOOL)alternate_ collapsed:(BOOL)collapsed_ contentColumn:(BOOL)contentColumn_ 134 | { 135 | resultType = resultType_; 136 | alternate = alternate_; 137 | contentColumn = contentColumn_; 138 | collapsed = collapsed_; 139 | 140 | if (resultType == JPResultTypeMatchingLine || resultType == JPResultTypeFilename || resultType == JPResultTypeError) 141 | [self setTextColor:[NSColor controlTextColor]]; 142 | else 143 | [self setTextColor:[NSColor disabledControlTextColor]]; 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /source/controllers/AckMatePlugin.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "AckMatePlugin.h" 5 | #import "JPAckWindowController.h" 6 | 7 | @interface AckMatePlugin () 8 | - (void)installMenuItems; 9 | - (NSMutableDictionary*)preferencesForProjectRoot:(NSString*)projectRoot; 10 | - (void)loadPluginPreferences; 11 | - (void)savePluginPreferences; 12 | - (id)firstProjectController; 13 | - (NSString*)directoryForProject:(id)projectController; 14 | @end 15 | 16 | @implementation AckMatePlugin 17 | 18 | - (id)initWithPlugInController:(id )aController 19 | { 20 | if (self = [self init]) 21 | { 22 | NSApp = [NSApplication sharedApplication]; 23 | ackWindows = [[NSMutableDictionary alloc] initWithCapacity:0]; 24 | [self installMenuItems]; 25 | [self loadPluginPreferences]; 26 | } 27 | return self; 28 | } 29 | 30 | - (BOOL)validateMenuItem:(NSMenuItem*)item 31 | { 32 | if ([item action] == @selector(findWithAck:)) 33 | return [self firstProjectController] ? YES : NO; 34 | 35 | return YES; 36 | } 37 | 38 | - (id)firstProjectController 39 | { 40 | for (NSWindow *w in [[NSApplication sharedApplication] orderedWindows]) 41 | { 42 | id wc = [w windowController]; 43 | 44 | if ([[wc className] isEqualToString:@"OakDocumentController"]) 45 | return nil; // more frontmost non-project editing window... 46 | 47 | if ([[wc className] isEqualToString:@"OakProjectController"]) 48 | return wc; 49 | } 50 | return nil; 51 | } 52 | 53 | - (void)findWithAck:(id)sender 54 | { 55 | NSString* directory = nil; 56 | id tmProjectController = [self firstProjectController]; 57 | 58 | if (tmProjectController) 59 | directory = [self directoryForProject:tmProjectController]; 60 | 61 | if (directory) 62 | { 63 | JPAckWindowController* ackWindow = [ackWindows objectForKey:directory]; 64 | if (!ackWindow) 65 | { 66 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(projectWindowWillClose:) name:NSWindowWillCloseNotification object:[tmProjectController window]]; 67 | NSMutableDictionary* pprefs = [self preferencesForProjectRoot:directory]; 68 | ackWindow = [[[JPAckWindowController alloc] initWithProjectDirectory:directory controller:tmProjectController preferences:pprefs] autorelease]; 69 | [ackWindows setObject:ackWindow forKey:directory]; 70 | } 71 | 72 | [ackWindow showAndActivate]; 73 | } 74 | } 75 | 76 | - (NSString*)directoryForProject:(id)projectController 77 | { 78 | NSDictionary* d = [projectController environmentVariables]; 79 | return [d objectForKey:@"TM_PROJECT_DIRECTORY"]; 80 | } 81 | 82 | - (void)projectWindowWillClose:(NSNotification*)notification 83 | { 84 | [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:[notification object]]; 85 | id windowController = [[notification object] windowController]; 86 | if ([[windowController className] isEqualToString:@"OakProjectController"]) 87 | { 88 | NSString* closingProjectDir = [self directoryForProject:windowController]; 89 | JPAckWindowController* existingAckController = [ackWindows objectForKey:closingProjectDir]; 90 | if (existingAckController) 91 | [existingAckController cleanupImmediately:YES]; 92 | 93 | [ackWindows removeObjectForKey:closingProjectDir]; 94 | } 95 | [self savePluginPreferences]; 96 | } 97 | 98 | - (void)installMenuItems 99 | { 100 | NSString *editTitle = NSLocalizedString(@"Edit", @""); 101 | id editMenu = [[[NSApp mainMenu] itemWithTitle:editTitle] submenu]; 102 | if (editMenu) 103 | { 104 | NSString *findTitle = NSLocalizedString(@"Find", @""); 105 | id findMenu = [[editMenu itemWithTitle:findTitle] submenu]; 106 | 107 | if (findMenu) 108 | { 109 | int index = 0; 110 | int separators = 0; 111 | NSArray *items = [findMenu itemArray]; 112 | for (separators = 0; index != [items count] && separators != 1; index++) 113 | separators += [[items objectAtIndex:index] isSeparatorItem] ? 1 : 0; 114 | 115 | NSString *title = NSLocalizedString(@"Search Project With AckMate...", @""); 116 | 117 | // don't submit patches for the key equivalent, just set your own in 118 | // system preferences under keyboard / keyboard shortcuts 119 | NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(findWithAck:) keyEquivalent:@"a"]; 120 | [menuItem setKeyEquivalentModifierMask:(NSShiftKeyMask | NSCommandKeyMask)]; 121 | [menuItem setTarget:self]; 122 | [findMenu insertItem:menuItem atIndex:index ? index-1 : 0]; 123 | [menuItem release]; 124 | } 125 | } 126 | } 127 | 128 | - (NSMutableDictionary*)preferencesForProjectRoot:(NSString*)projectRoot 129 | { 130 | NSMutableDictionary* rval = [ackPreferences objectForKey:projectRoot]; 131 | if (!rval) 132 | { 133 | rval = [NSMutableDictionary dictionary]; 134 | [ackPreferences setObject:rval forKey:projectRoot]; 135 | [rval setObject:[NSNumber numberWithBool:YES] forKey:kJPAckShowContext]; 136 | [rval setObject:[NSNumber numberWithBool:YES] forKey:kJPAckFollowSymlinks]; 137 | [rval setObject:[NSNumber numberWithBool:YES] forKey:kJPAckFolderReferences]; 138 | } 139 | 140 | // Add a default value of YES for kJPAckShowAdvanced 141 | if (![rval objectForKey:kJPAckShowAdvanced]) 142 | [rval setObject:[NSNumber numberWithBool:YES] forKey:kJPAckShowAdvanced]; 143 | 144 | return rval; 145 | } 146 | 147 | - (void)loadPluginPreferences 148 | { 149 | NSString* pluginDomain = [[NSBundle bundleForClass:[self class]] bundleIdentifier]; 150 | ackPreferences = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:pluginDomain] mutableCopy]; 151 | 152 | if (!ackPreferences) 153 | ackPreferences = [[NSMutableDictionary dictionary] retain]; 154 | 155 | for (NSString* projectRoot in [ackPreferences allKeys]) 156 | { 157 | NSDictionary* projectPrefs = [[[ackPreferences objectForKey:projectRoot] mutableCopy] autorelease]; 158 | [ackPreferences setObject:projectPrefs forKey:projectRoot]; 159 | } 160 | } 161 | 162 | - (void)savePluginPreferences 163 | { 164 | NSString* pluginDomain = [[NSBundle bundleForClass:[self class]] bundleIdentifier]; 165 | if (ackPreferences) 166 | [[NSUserDefaults standardUserDefaults] setPersistentDomain:ackPreferences forName:pluginDomain]; 167 | } 168 | 169 | - (void)dealloc 170 | { 171 | [ackPreferences release], ackPreferences = nil; 172 | [ackWindows release], ackWindows = nil; 173 | [super dealloc]; 174 | } 175 | @end 176 | -------------------------------------------------------------------------------- /source/controllers/JPAckProcess.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckProcess.h" 5 | #import "JPAckProcess+Parsing.h" 6 | #import "JPAckResultSource.h" 7 | 8 | @interface JPAckProcess () 9 | @property(retain) NSMutableData* trailing; 10 | @property(retain) NSMutableData* errorData; 11 | @property(retain) JPAckResultSource* ackResult; 12 | @property(retain) NSTask* ackTask; 13 | - (NSData*)trailingAndCurrent:(NSData*)data; 14 | - (void)handleStateEvent:(NSInteger)eventType; 15 | @end 16 | 17 | @implementation JPAckProcess 18 | 19 | NSString * const JPAckProcessComplete = @"JPAckProcessComplete"; 20 | 21 | @synthesize errorData; 22 | @synthesize trailing; 23 | @synthesize ackTask; 24 | @synthesize ackResult; 25 | 26 | enum { 27 | ackInitial = 0, 28 | ackStdOutClosed = 1<<0, 29 | ackStdErrClosed = 1<<1, 30 | ackTerminated = 1<<2, 31 | ackComplete = (ackStdOutClosed | ackStdErrClosed | ackTerminated) 32 | } ackStates; 33 | 34 | 35 | - (id)initWithResultHolder:(JPAckResultSource*)resultHolder 36 | { 37 | if (self = [super init]) 38 | { 39 | ackTask = nil; 40 | errorData = nil; 41 | trailing = nil; 42 | ackResult = [resultHolder retain]; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)invokeWithTerm:(NSString*)term path:(NSString*)path searchFolder:(NSString*)searchFolder literal:(BOOL)literal nocase:(BOOL)nocase words:(BOOL)words context:(BOOL)context symlinks:(BOOL)symlinks folderPattern:(NSString*)folderPattern filePattern:(NSString*)filePattern options:(NSArray*)options 48 | { 49 | ackState = ackInitial; 50 | [self.ackResult clearContents]; 51 | [self.ackResult searchingFor:term inRoot:path inFolder:searchFolder]; 52 | 53 | self.ackTask = [[[NSTask alloc] init] autorelease]; 54 | 55 | NSString* ackmateAck = [[[NSBundle bundleForClass:self.class] resourcePath] stringByAppendingPathComponent:@"ackmate_ack"]; 56 | 57 | [self.ackTask setCurrentDirectoryPath:path]; 58 | 59 | [self.ackTask setLaunchPath:@"/usr/bin/env"]; 60 | NSMutableArray* args = [NSMutableArray arrayWithObjects:@"perl", @"-CADS", ackmateAck, @"--ackmate", nil]; 61 | 62 | if (literal) [args addObject:@"--literal"]; 63 | if (words) [args addObject:@"--word-regexp"]; 64 | if (context) [args addObject:@"--context"]; 65 | 66 | if (symlinks) 67 | [args addObject:@"--follow"]; 68 | else 69 | [args addObject:@"--nofollow"]; 70 | 71 | if (nocase) 72 | [args addObject:@"--ignore-case"]; 73 | else 74 | [args addObject:@"--nosmart-case"]; 75 | 76 | if (folderPattern) 77 | { 78 | [args addObject:@"--ackmate-dir-filter"]; 79 | [args addObject:folderPattern]; 80 | } 81 | 82 | if (filePattern) 83 | { 84 | [args addObject:@"--invert-file-match"]; 85 | [args addObject:@"-G"]; 86 | [args addObject:filePattern]; 87 | } 88 | 89 | for (NSString* typeOption in options) 90 | [args addObject:[NSString stringWithFormat:@"%@%@", ([typeOption hasPrefix:@"-"]) ? @"" : @"--", typeOption]]; 91 | 92 | [args addObject:@"--match"]; 93 | [args addObject:term]; 94 | [args addObject:(searchFolder) ? searchFolder : path]; 95 | 96 | [self.ackTask setArguments:args]; 97 | 98 | NSPipe* stdoutPipe = [NSPipe pipe]; 99 | NSPipe* stderrPipe = [NSPipe pipe]; 100 | 101 | [self.ackTask setStandardInput:[NSFileHandle fileHandleWithNullDevice]]; 102 | [self.ackTask setStandardError:stderrPipe]; 103 | [self.ackTask setStandardOutput:stdoutPipe]; 104 | 105 | [self.ackTask launch]; 106 | 107 | NSFileHandle* stdoutFileHandle = [stdoutPipe fileHandleForReading]; 108 | NSFileHandle* stderrFileHandle = [stderrPipe fileHandleForReading]; 109 | 110 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultData:) name:NSFileHandleReadCompletionNotification object:stdoutFileHandle]; 111 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(errorData:) name:NSFileHandleReadCompletionNotification object:stderrFileHandle]; 112 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskEnded:) name:NSTaskDidTerminateNotification object:ackTask]; 113 | 114 | [stdoutFileHandle readInBackgroundAndNotify]; 115 | [stderrFileHandle readInBackgroundAndNotify]; 116 | } 117 | 118 | - (void)handleStateEvent:(NSInteger)eventType 119 | { 120 | ackState |= eventType; 121 | if (ackState == ackComplete) 122 | { 123 | self.ackTask = nil; 124 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 125 | 126 | if (errorData) 127 | [ackResult parsedError:[[[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding] autorelease]]; 128 | 129 | [ackResult updateStats]; 130 | [[NSNotificationCenter defaultCenter] postNotificationName:JPAckProcessComplete object:self userInfo:nil]; 131 | } 132 | } 133 | 134 | - (void)resultData:(NSNotification*)notification 135 | { 136 | NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; 137 | if ([data length] > 0) 138 | { 139 | [self parseData:data]; 140 | [[notification object] readInBackgroundAndNotify]; 141 | } 142 | else 143 | [self handleStateEvent:ackStdOutClosed]; 144 | } 145 | 146 | - (void)errorData:(NSNotification*)notification 147 | { 148 | NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; 149 | if ([data length] > 0) 150 | { 151 | if (!errorData) 152 | self.errorData = [NSMutableData data]; 153 | 154 | [errorData appendData:data]; 155 | [[notification object] readInBackgroundAndNotify]; 156 | } 157 | else 158 | [self handleStateEvent:ackStdErrClosed]; 159 | } 160 | 161 | - (void)taskEnded:(NSNotification*)notification 162 | { 163 | if ([notification object] == self.ackTask) 164 | [self handleStateEvent:ackTerminated]; 165 | } 166 | 167 | - (void)parseData:(NSData*)data 168 | { 169 | [self consumeInputLines:[self trailingAndCurrent:data]]; 170 | } 171 | 172 | - (void)saveTrailing:(char*)bytes length:(NSUInteger)length 173 | { 174 | self.trailing = [NSMutableData dataWithBytes:bytes length:length]; 175 | } 176 | 177 | - (NSData*)trailingAndCurrent:(NSData*)data 178 | { 179 | if (!self.trailing) 180 | return data; 181 | 182 | NSMutableData* tandc = [[self.trailing retain] autorelease]; 183 | self.trailing = nil; 184 | [tandc appendData:data]; 185 | return tandc; 186 | } 187 | 188 | - (void)terminateImmediately:(BOOL)immediately 189 | { 190 | [self.ackTask terminate]; 191 | 192 | // If immediately then we must clean up any references 193 | // that might be left dangling and don't worry about lost 194 | // callbacks - they don't matter any more because we're about 195 | // to be completely deallocated 196 | if (immediately) 197 | { 198 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 199 | [ackResult release], ackResult = nil; 200 | } 201 | } 202 | 203 | - (void)dealloc 204 | { 205 | [errorData release], errorData = nil; 206 | [trailing release], trailing = nil; 207 | [ackTask release], ackTask = nil; 208 | [ackResult release], ackResult = nil; 209 | [super dealloc]; 210 | } 211 | 212 | @end 213 | -------------------------------------------------------------------------------- /source/views/JPAckResultTableView.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckResultTableView.h" 5 | #import "JPAckResultCell.h" 6 | #import "NSTableView+NoodleExtensions.h" 7 | #import "SDFoundation.h" 8 | 9 | @interface JPAckResultTableView () 10 | - (void)activationAction:(id)sender; 11 | - (BOOL)activateRow:(NSInteger)row atPoint:(NSPoint)point; 12 | - (NSInteger)rowTrulyAtPoint:(NSPoint)point; 13 | @end 14 | 15 | @implementation JPAckResultTableView 16 | 17 | - (void)awakeFromNib 18 | { 19 | [self setTarget:self]; 20 | [self setAction:@selector(activationAction:)]; 21 | } 22 | 23 | - (void)activationAction:(id)sender 24 | { 25 | if ([[NSApp currentEvent] modifierFlags] & NSControlKeyMask) 26 | return; // never for context menu 27 | 28 | NSPoint mouseLocation = [self convertPoint:[[self window] convertScreenToBase:[NSEvent mouseLocation]] fromView:nil]; 29 | NSInteger row = [self rowTrulyAtPoint:mouseLocation]; 30 | 31 | if (row == NSNotFound) 32 | return; 33 | 34 | if ([[self delegate] tableView:self isStickyRow:row]) 35 | [self clickedStickyRow:row]; 36 | else if ([self isRowSelected:row]) 37 | [self activateRow:row atPoint:mouseLocation]; 38 | } 39 | 40 | - (BOOL)activateRow:(NSInteger)row atPoint:(NSPoint)point; 41 | { 42 | if ([self isRowSelected:row]) 43 | { 44 | return [[self delegate] tableView:self activateSelectedRow:row atPoint:point]; 45 | } 46 | return NO; 47 | } 48 | 49 | - (void)clickedStickyRow:(NSInteger)row 50 | { 51 | [[self delegate] tableView:self activateSelectedRow:row atPoint:NSMakePoint(0,0)]; //dummy point hint - sticky row was clicked, not the real row 52 | } 53 | 54 | - (NSInteger)rowTrulyAtPoint:(NSPoint)point 55 | { 56 | NSInteger mouseRow = [self rowAtPoint:point]; 57 | if (mouseRow == NSNotFound) 58 | return NSNotFound; 59 | 60 | NSRect rowRect = NSIntersectionRect([self rectOfRow:mouseRow], [self visibleRect]); 61 | 62 | if (NSMouseInRect(point, rowRect, [self isFlipped])) 63 | return mouseRow; 64 | 65 | return NSNotFound; 66 | } 67 | 68 | - (NSMenu*)menuForEvent:(NSEvent*)event 69 | { 70 | NSPoint mousePoint = [self convertPoint:[event locationInWindow] fromView:nil]; 71 | NSInteger row = [self rowTrulyAtPoint:mousePoint]; 72 | 73 | return (row != NSNotFound) ? [[self delegate] tableView:self contextMenuForRow:row] : nil; 74 | } 75 | 76 | - (void)sizeLastColumnToFit 77 | { 78 | // Brute-force the column resizing - blech. 79 | 80 | NSArray* cols = [self tableColumns]; 81 | CGFloat fixedWidth = [[cols objectAtIndex:0] width]; 82 | [[cols objectAtIndex:1] setWidth:NSWidth([self visibleRect]) - fixedWidth]; 83 | } 84 | 85 | - (NSInteger)spanningColumnForRow:(NSInteger)rowIndex 86 | { 87 | if ([[self delegate] respondsToSelector:@selector(tableView:spanningColumnForRow:)]) 88 | return [[self delegate] tableView:self spanningColumnForRow:rowIndex]; 89 | 90 | return NSNotFound; 91 | } 92 | 93 | // // Don't like this - removing it for now. Perhaps spacebar is a better activation trigger 94 | // - (void)keyDown:(NSEvent *)event 95 | // { 96 | // unichar u = [[event charactersIgnoringModifiers] characterAtIndex: 0]; 97 | // if ((u == NSEnterCharacter || u == NSCarriageReturnCharacter) && [self activateRow:[self selectedRow]]) 98 | // return; 99 | // else 100 | // [super keyDown:event]; 101 | // } 102 | 103 | - (NSRect)frameOfCellAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex 104 | { 105 | NSInteger spanningColumn = [self spanningColumnForRow:rowIndex]; 106 | if (spanningColumn != NSNotFound) 107 | return (columnIndex != spanningColumn) ? NSZeroRect : NSInsetRect([self rectOfRow:rowIndex], RESULT_ROW_PADDING, 0); 108 | 109 | NSRect foc = [super frameOfCellAtColumn:columnIndex row:rowIndex]; 110 | 111 | if (columnIndex == 0) 112 | SDDivideRect(foc, nil, &foc, RESULT_ROW_PADDING, NSMinXEdge); 113 | else 114 | SDDivideRect(foc, nil, &foc, RESULT_ROW_PADDING, NSMaxXEdge); 115 | 116 | return foc; 117 | } 118 | 119 | - (void)setFrameSize:(NSSize)newSize 120 | { 121 | [super setFrameSize:newSize]; 122 | [self sizeLastColumnToFit]; 123 | 124 | NSRange allRows = NSMakeRange(0, [self numberOfRows]); 125 | NSRange rangeToRefresh; 126 | 127 | if ([self inLiveResize]) 128 | rangeToRefresh = NSIntersectionRange([self rowsInRect:[self visibleRect]], allRows); 129 | else 130 | rangeToRefresh = allRows; 131 | 132 | NSIndexSet* refreshIndexes = [NSIndexSet indexSetWithIndexesInRange:rangeToRefresh]; 133 | [self noteHeightOfRowsWithIndexesChanged:refreshIndexes]; 134 | } 135 | 136 | - (CGFloat)viewportOffsetForRow:(NSInteger)rowIndex 137 | { 138 | return [self rectOfRow:rowIndex].origin.y - [self visibleRect].origin.y; 139 | } 140 | 141 | - (void)scrollRowToVisible:(NSInteger)rowIndex withViewportOffset:(CGFloat)offset 142 | { 143 | NSPoint sp = [self rectOfRow:rowIndex].origin; 144 | sp.y -= ((offset > 0.0) ? offset : 0); 145 | [self scrollPoint:sp]; 146 | } 147 | 148 | - (void)viewDidEndLiveResize 149 | { 150 | NSIndexSet* refreshIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]; 151 | [self noteHeightOfRowsWithIndexesChanged:refreshIndexes]; 152 | } 153 | 154 | - (void)highlightSelectionInClipRect:(NSRect)clipRect 155 | { 156 | // No selection highlighting thanks. 157 | } 158 | 159 | - (void)drawRect:(NSRect)rect 160 | { 161 | [super drawRect:rect]; 162 | [self drawStickyRowHeader]; 163 | } 164 | 165 | // Since we are going to ensure that the regular and sticky versions of a row 166 | // look the same, no transition is needed here. 167 | - (NoodleStickyRowTransition)stickyRowHeaderTransition 168 | { 169 | return NoodleStickyRowTransitionNone; 170 | } 171 | 172 | - (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)clipRect 173 | { 174 | if ([self isRowSticky:rowIndex]) 175 | { 176 | NSRect rowRect = [self rectOfRow:rowIndex]; 177 | 178 | if (!_isDrawingStickyRow) 179 | { 180 | // Note that NSTableView will still draw the special background that it does 181 | // for group row so we re-draw the background over it. 182 | [self drawBackgroundInClipRect:rowRect]; 183 | 184 | if (NSIntersectsRect(rowRect, [self stickyRowHeaderRect])) 185 | { 186 | // You can barely notice it but if the sticky view is showing, the actual 187 | // row it represents is still seen underneath. We check for this and don't 188 | // draw the row in such a case. 189 | return; 190 | } 191 | } 192 | 193 | NSInteger scol = [self spanningColumnForRow:rowIndex]; 194 | if (scol == NSNotFound) 195 | scol = 0; 196 | 197 | NSCell* cell = [self preparedCellAtColumn:scol row:rowIndex]; 198 | NSRect cellRect = [self frameOfCellAtColumn:scol row:rowIndex]; 199 | [cell drawWithFrame:cellRect inView:self]; 200 | } 201 | else 202 | { 203 | [super drawRow:rowIndex clipRect:clipRect]; 204 | } 205 | } 206 | 207 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect 208 | { 209 | _isDrawingStickyRow = YES; 210 | [self drawRow:row clipRect:clipRect]; 211 | _isDrawingStickyRow = NO; 212 | } 213 | 214 | @end 215 | -------------------------------------------------------------------------------- /source/external/NSTableView+NoodleExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTableView-NoodleExtensions.m 3 | // NoodleKit 4 | // 5 | // Created by Paul Kim on 10/22/09. 6 | // Copyright 2009 Noodlesoft, LLC. All rights reserved. 7 | // 8 | 9 | #import "NSTableView+NoodleExtensions.h" 10 | #import "NSImage-NoodleExtensions.h" 11 | 12 | #define NOODLE_STICKY_ROW_VIEW_TAG 233931134 13 | 14 | void NoodleClearRect(NSRect rect) 15 | { 16 | [[NSColor clearColor] set]; 17 | NSRectFill(rect); 18 | } 19 | 20 | @interface NSTableView () 21 | 22 | #pragma mark Sticky Row Header methods 23 | 24 | // Returns index of the sticky row previous to the first visible row. 25 | - (NSInteger)_previousStickyRow; 26 | 27 | // Returns index of the sticky row after the first visible row. 28 | - (NSInteger)_nextStickyRow; 29 | 30 | - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row; 31 | 32 | // Returns the view used for the sticky row header 33 | - (id)_stickyRowHeaderView; 34 | 35 | @end 36 | 37 | 38 | @implementation NSTableView (NoodleExtensions) 39 | 40 | #pragma mark Sticky Row Header methods 41 | 42 | - (BOOL)isRowSticky:(NSInteger)rowIndex 43 | { 44 | id delegate; 45 | 46 | delegate = [self delegate]; 47 | 48 | if ([delegate respondsToSelector:@selector(tableView:isStickyRow:)]) 49 | { 50 | return [delegate tableView:self isStickyRow:rowIndex]; 51 | } 52 | else if ([delegate respondsToSelector:@selector(tableView:isGroupRow:)]) 53 | { 54 | return [delegate tableView:self isGroupRow:rowIndex]; 55 | } 56 | return NO; 57 | } 58 | 59 | - (void)drawStickyRowHeader 60 | { 61 | id stickyView; 62 | NSInteger row; 63 | 64 | stickyView = [self _stickyRowHeaderView]; 65 | row = [self _previousStickyRow]; 66 | if (row != -1) 67 | { 68 | [stickyView setFrame:[self stickyRowHeaderRect]]; 69 | [self _updateStickyRowHeaderImageWithRow:row]; 70 | } 71 | else 72 | { 73 | [stickyView setFrame:NSZeroRect]; 74 | } 75 | } 76 | 77 | - (IBAction)peformClickOnStickyRow:(id)sender 78 | { 79 | NSInteger row; 80 | 81 | row = [self _previousStickyRow]; 82 | if (row != -1) 83 | { 84 | [self clickedStickyRow:row]; 85 | } 86 | } 87 | 88 | - (void)clickedStickyRow:(NSInteger)row 89 | { 90 | [self scrollRowToVisible:row]; 91 | } 92 | 93 | - (NSMenu*)contextMenuForStickyRow:(id)sender 94 | { 95 | NSInteger row; 96 | 97 | row = [self _previousStickyRow]; 98 | if (row != -1) 99 | { 100 | return [[self delegate] tableView:self contextMenuForRow:row]; 101 | } 102 | return nil; 103 | } 104 | 105 | - (id)_stickyRowHeaderView 106 | { 107 | NSButton *view; 108 | 109 | view = [self viewWithTag:NOODLE_STICKY_ROW_VIEW_TAG]; 110 | 111 | if (view == nil) 112 | { 113 | view = [[StickyRowButton alloc] initWithFrame:NSZeroRect]; 114 | [view setEnabled:YES]; 115 | [view setBordered:NO]; 116 | [view setImagePosition:NSImageOnly]; 117 | [view setTitle:nil]; 118 | [[view cell] setHighlightsBy:NSNoCellMask]; 119 | [[view cell] setShowsStateBy:NSNoCellMask]; 120 | [[view cell] setImageScaling:NSImageScaleNone]; 121 | [[view cell] setImageDimsWhenDisabled:NO]; 122 | 123 | [view setTag:NOODLE_STICKY_ROW_VIEW_TAG]; 124 | 125 | [view setTarget:self]; 126 | [view setAction:@selector(peformClickOnStickyRow:)]; 127 | 128 | [self addSubview:view]; 129 | [view release]; 130 | } 131 | return view; 132 | } 133 | 134 | - (void)drawStickyRow:(NSInteger)row clipRect:(NSRect)clipRect 135 | { 136 | NSRect rowRect, cellRect; 137 | NSCell *cell; 138 | NSInteger colIndex, count; 139 | id delegate; 140 | 141 | delegate = [self delegate]; 142 | 143 | if (![delegate respondsToSelector:@selector(tableView:shouldDisplayCellInStickyRowHeaderForTableColumn:row:)]) 144 | { 145 | delegate = nil; 146 | } 147 | 148 | rowRect = [self rectOfRow:row]; 149 | 150 | [[[self backgroundColor] highlightWithLevel:0.5] set]; 151 | NSRectFill(rowRect); 152 | 153 | // PENDING: -drawRow:clipRect: is too smart for its own good. If the row is not visible, 154 | // this method won't draw anything. Useless for row caching. 155 | // [self drawRow:row clipRect:rowRect]; 156 | 157 | count = [self numberOfColumns]; 158 | for (colIndex = 0; colIndex < count; colIndex++) 159 | { 160 | if ((delegate == nil) || 161 | [delegate tableView:self shouldDisplayCellInStickyRowHeaderForTableColumn:[[self tableColumns] objectAtIndex:colIndex] row:row]) 162 | { 163 | cell = [self preparedCellAtColumn:colIndex row:row]; 164 | cellRect = [self frameOfCellAtColumn:colIndex row:row]; 165 | [cell drawWithFrame:cellRect inView:self]; 166 | } 167 | } 168 | 169 | [[self gridColor] set]; 170 | [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMinX(rowRect), NSMaxY(rowRect)) toPoint:NSMakePoint(NSMaxX(rowRect), NSMaxY(rowRect))]; 171 | } 172 | 173 | - (NoodleStickyRowTransition)stickyRowHeaderTransition 174 | { 175 | return NoodleStickyRowTransitionFadeIn; 176 | } 177 | 178 | - (void)_updateStickyRowHeaderImageWithRow:(NSInteger)row 179 | { 180 | NSImage *image; 181 | NSRect rowRect, visibleRect, imageRect; 182 | CGFloat offset, alpha; 183 | NSAffineTransform *transform; 184 | id stickyView; 185 | NoodleStickyRowTransition transition; 186 | BOOL isSelected; 187 | 188 | rowRect = [self rectOfRow:row]; 189 | imageRect = NSMakeRect(0.0, 0.0, NSWidth(rowRect), NSHeight(rowRect)); 190 | stickyView = [self _stickyRowHeaderView]; 191 | 192 | isSelected = [self isRowSelected:row]; 193 | if (isSelected) 194 | { 195 | [self deselectRow:row]; 196 | } 197 | 198 | // Optimization: instead of creating a new image each time (and since we can't 199 | // add ivars in a category), just use the image in the sticky view. We're going 200 | // to put it there in the end anyways, why not reuse it? 201 | image = [stickyView image]; 202 | 203 | if ((image == nil) || !NSEqualSizes(rowRect.size, [image size])) 204 | { 205 | image = [[NSImage alloc] initWithSize:rowRect.size]; 206 | [image setFlipped:[self isFlipped]]; 207 | [stickyView setImage:image]; 208 | [image release]; 209 | } 210 | 211 | visibleRect = [self visibleRect]; 212 | 213 | // Calculate a distance between the row header and the actual sticky row and normalize it 214 | // over the row height (plus some extra). We use this to do the fade in effect as you 215 | // scroll away from the sticky row. 216 | offset = (NSMinY(visibleRect) - NSMinY(rowRect)) / (NSHeight(rowRect) * 1.25); 217 | 218 | // When the button is disabled, it passes through to the view underneath. So, until the 219 | // original header view is mostly out of view, allow mouse events to pass through. After 220 | // that, the header is clickable. 221 | // 222 | // MODS Trevor Squires 223 | // Okay, changed it from if (offset < 0.5) because I only want pass-through 224 | // if the positions are equal. It's probably an issue for me b/c of my top margin. 225 | if (NSMinY(visibleRect) == NSMinY(rowRect)) 226 | { 227 | [stickyView setEnabled:NO]; 228 | } 229 | else 230 | { 231 | [stickyView setEnabled:YES]; 232 | } 233 | 234 | // Row is drawn in tableview coord space. 235 | transform = [NSAffineTransform transform]; 236 | [transform translateXBy:-NSMinX(rowRect) yBy:-NSMinY(rowRect)]; 237 | 238 | transition = [self stickyRowHeaderTransition]; 239 | if (transition == NoodleStickyRowTransitionFadeIn) 240 | { 241 | // Since we want to adjust the transparency based on position, we draw the row into an 242 | // image which we then draw with alpha into the final image. 243 | NSImage *rowImage; 244 | 245 | // Optimization: Since this is a category and we can't add any ivars, we instead use 246 | // the unused alt image of the sticky view (which is a button) as a cache so we don't 247 | // have to keep creating images. Yes, a little hackish. 248 | rowImage = [stickyView alternateImage]; 249 | if ((rowImage == nil) || !NSEqualSizes(rowRect.size, [rowImage size])) 250 | { 251 | rowImage = [[NSImage alloc] initWithSize:rowRect.size]; 252 | [rowImage setFlipped:[self isFlipped]]; 253 | 254 | [stickyView setAlternateImage:rowImage]; 255 | [rowImage release]; 256 | } 257 | 258 | // Draw the original image 259 | [rowImage lockFocus]; 260 | NoodleClearRect(imageRect); 261 | 262 | [transform concat]; 263 | [self drawStickyRow:row clipRect:rowRect]; 264 | 265 | [rowImage unlockFocus]; 266 | 267 | alpha = MIN(offset, 0.9); 268 | 269 | // Draw it with transparency in the final image 270 | [image lockFocus]; 271 | 272 | NoodleClearRect(imageRect); 273 | [rowImage drawAdjustedAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:alpha]; 274 | 275 | [image unlockFocus]; 276 | } 277 | else if (transition == NoodleStickyRowTransitionNone) 278 | { 279 | [image lockFocus]; 280 | NoodleClearRect(imageRect); 281 | 282 | [transform concat]; 283 | [self drawStickyRow:row clipRect:rowRect]; 284 | 285 | [image unlockFocus]; 286 | } 287 | else 288 | { 289 | [image lockFocus]; 290 | NoodleClearRect(imageRect); 291 | 292 | [@"You returned a bad NoodleStickyRowTransition value. Tsk. Tsk." drawInRect:imageRect withAttributes:nil]; 293 | 294 | [image unlockFocus]; 295 | } 296 | 297 | if (isSelected) 298 | { 299 | [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:YES]; 300 | } 301 | 302 | } 303 | 304 | - (NSInteger)_previousStickyRow 305 | { 306 | NSRect visibleRect; 307 | NSInteger row; 308 | 309 | visibleRect = [self visibleRect]; 310 | row = [self rowAtPoint:visibleRect.origin]; 311 | 312 | while (row >= 0) 313 | { 314 | if ([self isRowSticky:row]) 315 | { 316 | return row; 317 | } 318 | row--; 319 | } 320 | return -1; 321 | } 322 | 323 | - (NSInteger)_nextStickyRow 324 | { 325 | NSRect visibleRect; 326 | NSInteger row; 327 | NSInteger numberOfRows; 328 | 329 | visibleRect = [self visibleRect]; 330 | row = [self rowAtPoint:visibleRect.origin]; 331 | if (row != -1) 332 | { 333 | numberOfRows = [self numberOfRows]; 334 | while (++row < numberOfRows) 335 | { 336 | if ([self isRowSticky:row]) 337 | { 338 | return row; 339 | } 340 | } 341 | } 342 | return -1; 343 | } 344 | 345 | - (NSRect)stickyRowHeaderRect 346 | { 347 | NSInteger row; 348 | 349 | row = [self _previousStickyRow]; 350 | 351 | if (row != -1) 352 | { 353 | NSInteger nextGroupRow; 354 | NSRect visibleRect, rowRect; 355 | 356 | rowRect = [self rectOfRow:row]; 357 | visibleRect = [self visibleRect]; 358 | 359 | // Move it to the top of the visible area 360 | rowRect.origin.y = NSMinY(visibleRect); 361 | 362 | nextGroupRow = [self _nextStickyRow]; 363 | if (nextGroupRow != -1) 364 | { 365 | NSRect nextRect; 366 | 367 | // "Push" the row up if it's butting up against the next sticky row 368 | nextRect = [self rectOfRow:nextGroupRow]; 369 | if (NSMinY(nextRect) < NSMaxY(rowRect)) 370 | { 371 | rowRect.origin.y = NSMinY(nextRect) - NSHeight(rowRect); 372 | } 373 | } 374 | return rowRect; 375 | } 376 | return NSZeroRect; 377 | } 378 | 379 | #pragma mark Row Spanning methods 380 | 381 | - (NSRange)rangeOfRowSpanAtColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex 382 | { 383 | id dataSource, objectValue, originalObjectValue; 384 | NSInteger i, start, end, count; 385 | NSTableColumn *column; 386 | 387 | dataSource = [self dataSource]; 388 | 389 | column = [[self tableColumns] objectAtIndex:columnIndex]; 390 | 391 | if ([column isRowSpanningEnabled]) 392 | { 393 | originalObjectValue = [dataSource tableView:self objectValueForTableColumn:column row:rowIndex]; 394 | 395 | // Figure out the span of this cell. We determine this by going up and down finding contiguous rows with 396 | // the same object value. 397 | i = rowIndex; 398 | while (i-- > 0) 399 | { 400 | objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i]; 401 | 402 | if (![objectValue isEqual:originalObjectValue]) 403 | { 404 | break; 405 | } 406 | } 407 | start = i + 1; 408 | 409 | count = [self numberOfRows]; 410 | i = rowIndex + 1; 411 | while (i < count) 412 | { 413 | objectValue = [dataSource tableView:self objectValueForTableColumn:column row:i]; 414 | 415 | if (![objectValue isEqual:originalObjectValue]) 416 | { 417 | break; 418 | } 419 | i++; 420 | } 421 | end = i - 1; 422 | 423 | return NSMakeRange(start, end - start + 1); 424 | } 425 | return NSMakeRange(rowIndex, 1); 426 | } 427 | 428 | @end 429 | 430 | @implementation NSTableColumn (NoodleExtensions) 431 | 432 | #pragma mark Row Spanning methods 433 | 434 | - (BOOL)isRowSpanningEnabled 435 | { 436 | return NO; 437 | } 438 | 439 | - (NoodleRowSpanningCell *)spanningCell 440 | { 441 | return nil; 442 | } 443 | 444 | @end 445 | 446 | @implementation NSOutlineView (NoodleExtensions) 447 | 448 | #pragma mark Sticky Row Header methods 449 | 450 | - (BOOL)isRowSticky:(NSInteger)rowIndex 451 | { 452 | id delegate; 453 | 454 | delegate = [self delegate]; 455 | 456 | if ([delegate respondsToSelector:@selector(outlineView:isStickyItem:)]) 457 | { 458 | return [delegate outlineView:self isStickyItem:[self itemAtRow:rowIndex]]; 459 | } 460 | else if ([delegate respondsToSelector:@selector(outlineView:isGroupItem:)]) 461 | { 462 | return [delegate outlineView:self isGroupItem:[self itemAtRow:rowIndex]]; 463 | } 464 | return NO; 465 | } 466 | 467 | @end 468 | 469 | @implementation StickyRowButton 470 | - (NSMenu*)menuForEvent:(NSEvent*)event 471 | { 472 | return [[self target] contextMenuForStickyRow:self]; 473 | } 474 | @end -------------------------------------------------------------------------------- /source/controllers/JPAckWindowController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckWindowController.h" 5 | #import "JPAckResultSource.h" 6 | #import "JPAckProcess.h" 7 | #import "JPAckTypesProcess.h" 8 | 9 | #define ADVANCED_HEIGHT 20.0f 10 | 11 | @interface JPAckWindowController () 12 | - (void)loadAckTypes; 13 | - (void)notePreferences; 14 | - (void)updateHistoryWithTerm:(NSString*)term; 15 | - (NSArray*)cleanseOptionList:(NSArray*)optionList; 16 | - (NSString*)projectSelectedSearchFolder; 17 | - (void)updateSearchSelectionForEvent:(NSEvent*)event; 18 | @property(nonatomic, retain) JPAckProcess* currentProcess; 19 | @property(nonatomic, retain) JPAckTypesProcess* currentTypesProcess; 20 | @property(nonatomic, retain) NSArray* ackTypes; 21 | @property(nonatomic, readwrite, copy) NSArray* history; 22 | @property(nonatomic, copy) NSString* selectedSearchFolder; 23 | @end 24 | 25 | @implementation JPAckWindowController 26 | 27 | const BOOL selectionSearchDefault = YES; 28 | 29 | NSString * const kJPAckLiteral = @"kJPAckLiteral"; 30 | NSString * const kJPAckShowAdvanced = @"kJPAckShowAdvanced"; 31 | NSString * const kJPAckNoCase = @"kJPAckNoCase"; 32 | NSString * const kJPAckMatchWords = @"kJPAckMatchWords"; 33 | NSString * const kJPAckShowContext = @"kJPAckShowContext"; 34 | NSString * const kJPAckFollowSymlinks = @"kJPAckFollowSymlinks"; 35 | NSString * const kJPAckFolderReferences = @"kJPAckFolderReferences"; 36 | NSString * const kJPAckSearchHistory = @"kJPAckSearchHistory"; 37 | NSString * const kJPAckSearchOptions = @"kJPAckSearchOptions"; 38 | NSString * const kJPAckWindowPosition = @"kJPAckWindowPosition"; 39 | 40 | @synthesize fileName; 41 | @synthesize projectDirectory; 42 | @synthesize selectedSearchFolder; 43 | @synthesize ackTypes; 44 | @synthesize history; 45 | @synthesize term; 46 | @synthesize showAdvanced; 47 | @synthesize nocase; 48 | @synthesize literal; 49 | @synthesize words; 50 | @synthesize context; 51 | @synthesize symlinks; 52 | @synthesize folders; 53 | @synthesize currentProcess; 54 | @synthesize currentTypesProcess; 55 | 56 | + (NSSet*)keyPathsForValuesAffectingRunning 57 | { 58 | return [NSSet setWithObject:@"currentProcess"]; 59 | } 60 | 61 | + (NSSet*)keyPathsForValuesAffectingWindowTitle 62 | { 63 | return [NSSet setWithObject:@"fileName"]; 64 | } 65 | 66 | + (NSSet*)keyPathsForValuesAffectingSearchTitle 67 | { 68 | return [NSSet setWithObject:@"selectedSearchFolder"]; 69 | } 70 | 71 | + (NSSet*)keyPathsForValuesAffectingCanSearch 72 | { 73 | return [NSSet setWithObjects:@"selectedSearchFolder", @"running", @"term", nil]; 74 | } 75 | 76 | - (id)initWithProjectDirectory:(NSString*)directory controller:(id)controller preferences:(NSMutableDictionary*)prefs 77 | { 78 | if (self = [self initWithWindowNibName:@"JPAckWindow"]) 79 | { 80 | // initial state of showAdvanced is always YES based on the nib layout 81 | showAdvanced = YES; 82 | 83 | history = nil; 84 | ackTypes = nil; 85 | term = nil; 86 | currentProcess = nil; 87 | currentTypesProcess = nil; 88 | 89 | projectController = controller; 90 | preferences = prefs; 91 | pasteboardChangeCount = NSNotFound; 92 | 93 | NSString* projectfile = [projectController filename] ? [projectController filename] : directory; 94 | fileName = [[[projectfile lastPathComponent] stringByDeletingPathExtension] copy]; 95 | projectDirectory = [directory copy]; 96 | } 97 | return self; 98 | } 99 | 100 | - (void)windowDidLoad 101 | { 102 | [[self window] setContentBorderThickness:20.0 forEdge:NSMinYEdge]; 103 | [optionsField setTokenizingCharacterSet:[NSCharacterSet whitespaceCharacterSet]]; 104 | 105 | self.literal = [[preferences objectForKey:kJPAckLiteral] boolValue]; 106 | self.showAdvanced = [[preferences objectForKey:kJPAckShowAdvanced] boolValue]; 107 | self.nocase = [[preferences objectForKey:kJPAckNoCase] boolValue]; 108 | self.words = [[preferences objectForKey:kJPAckMatchWords] boolValue]; 109 | self.context = [[preferences objectForKey:kJPAckShowContext] boolValue]; 110 | self.symlinks = [[preferences objectForKey:kJPAckFollowSymlinks] boolValue]; 111 | self.folders = [[preferences objectForKey:kJPAckFolderReferences] boolValue]; 112 | 113 | NSArray* savedHistory = [preferences objectForKey:kJPAckSearchHistory]; 114 | self.history = (savedHistory) ? savedHistory : [NSArray array]; 115 | 116 | NSArray* savedOptions = [preferences objectForKey:kJPAckSearchOptions]; 117 | if (savedOptions) 118 | [optionsField setObjectValue:savedOptions]; 119 | 120 | NSString* savedFrame = [preferences objectForKey:kJPAckWindowPosition]; 121 | if (savedFrame) 122 | [[self window] setFrameFromString:savedFrame]; 123 | } 124 | 125 | - (void)cleanupImmediately:(BOOL)immediately 126 | { 127 | [self notePreferences]; 128 | 129 | if (self.currentProcess) 130 | [self.currentProcess terminateImmediately:immediately]; 131 | 132 | if (self.currentTypesProcess) 133 | [self.currentTypesProcess terminate]; 134 | 135 | if (immediately) 136 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 137 | } 138 | 139 | - (BOOL)windowShouldClose:(id)sender 140 | { 141 | [self cleanupImmediately:NO]; 142 | return YES; 143 | } 144 | 145 | - (void)notePreferences 146 | { 147 | [preferences setObject:[NSNumber numberWithBool:self.literal] forKey:kJPAckLiteral]; 148 | [preferences setObject:[NSNumber numberWithBool:self.showAdvanced] forKey:kJPAckShowAdvanced]; 149 | [preferences setObject:[NSNumber numberWithBool:self.nocase] forKey:kJPAckNoCase]; 150 | [preferences setObject:[NSNumber numberWithBool:self.words] forKey:kJPAckMatchWords]; 151 | [preferences setObject:[NSNumber numberWithBool:self.context] forKey:kJPAckShowContext]; 152 | [preferences setObject:[NSNumber numberWithBool:self.symlinks] forKey:kJPAckFollowSymlinks]; 153 | [preferences setObject:[NSNumber numberWithBool:self.folders] forKey:kJPAckFolderReferences]; 154 | 155 | [preferences setObject:self.history forKey:kJPAckSearchHistory]; 156 | [preferences setObject:[optionsField objectValue] forKey:kJPAckSearchOptions]; 157 | [preferences setObject:[[self window] stringWithSavedFrame] forKey:kJPAckWindowPosition]; 158 | } 159 | 160 | - (void)loadPasteboardTerm 161 | { 162 | NSPasteboard *fbp = [NSPasteboard pasteboardWithName:NSFindPboard]; 163 | if ([fbp changeCount] != pasteboardChangeCount && [fbp availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]) 164 | { 165 | pasteboardChangeCount = [fbp changeCount]; 166 | self.term = [fbp stringForType:NSStringPboardType]; 167 | } 168 | } 169 | 170 | - (void)savePasteboardTerm:(NSString*)term_ 171 | { 172 | if (!term_ || [term_ length] == 0) 173 | return; 174 | 175 | NSPasteboard *fbp = [NSPasteboard pasteboardWithName:NSFindPboard]; 176 | 177 | // don't update the pasteboard if it's the same search term... it's *rude* 178 | if ([fbp availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] && [[fbp stringForType:NSStringPboardType] isEqualToString:term_]) 179 | return; 180 | 181 | [fbp declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; 182 | [fbp setString:term_ forType:NSStringPboardType]; 183 | pasteboardChangeCount = [fbp changeCount]; 184 | } 185 | 186 | - (void)showAndActivate 187 | { 188 | [self loadPasteboardTerm]; 189 | [self showWindow:nil]; 190 | [self loadAckTypes]; 191 | [[self window] makeFirstResponder:searchTermField]; 192 | } 193 | 194 | - (void)setShowAdvanced:(BOOL)showAdvanced_ 195 | { 196 | [self willChangeValueForKey:@"showAdvanced"]; 197 | BOOL changed = (showAdvanced != showAdvanced_); 198 | showAdvanced = showAdvanced_; 199 | 200 | if (changed) 201 | { 202 | [showContextButton setHidden:!showAdvanced]; 203 | [followSymlinksButton setHidden:!showAdvanced]; 204 | [useFolderReferencesButton setHidden:!showAdvanced]; 205 | 206 | NSRect optionsFrame = [optionsBox frame]; 207 | NSRect controlFrame = [controlView frame]; 208 | NSRect resultsFrame = [resultsView frame]; 209 | 210 | if (showAdvanced) 211 | { 212 | optionsFrame.origin.y -= ADVANCED_HEIGHT; 213 | optionsFrame.size.height += ADVANCED_HEIGHT; 214 | controlFrame.origin.y -= ADVANCED_HEIGHT; 215 | controlFrame.size.height += ADVANCED_HEIGHT; 216 | resultsFrame.size.height -= ADVANCED_HEIGHT; 217 | } 218 | else 219 | { 220 | optionsFrame.origin.y += ADVANCED_HEIGHT; 221 | optionsFrame.size.height -= ADVANCED_HEIGHT; 222 | controlFrame.origin.y += ADVANCED_HEIGHT; 223 | controlFrame.size.height -= ADVANCED_HEIGHT; 224 | resultsFrame.size.height += ADVANCED_HEIGHT; 225 | } 226 | 227 | [optionsBox setFrame:optionsFrame]; 228 | [controlView setFrame:controlFrame]; 229 | [resultsView setFrame:resultsFrame]; 230 | } 231 | 232 | [self didChangeValueForKey:@"showAdvanced"]; 233 | } 234 | 235 | - (IBAction)performSearch:(id)sender 236 | { 237 | if (!term || ![term length]) 238 | return; 239 | 240 | [self savePasteboardTerm:term]; 241 | [self updateHistoryWithTerm:term]; 242 | 243 | [self notePreferences]; 244 | 245 | [ackResult clearContents]; 246 | 247 | NSString* folderPattern = nil; 248 | NSString* filePattern = nil; 249 | if (folders) 250 | folderPattern = [[[NSUserDefaults standardUserDefaults] stringForKey:@"OakFolderReferenceFolderPattern"] substringFromIndex:1]; 251 | filePattern = [[[NSUserDefaults standardUserDefaults] stringForKey:@"OakFolderReferenceFilePattern"] substringFromIndex:1]; 252 | 253 | self.currentProcess = [[[JPAckProcess alloc] initWithResultHolder:ackResult] autorelease]; 254 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(currentProcessCompleted:) name:JPAckProcessComplete object:self.currentProcess]; 255 | NSString* path = self.projectDirectory; 256 | [self.currentProcess invokeWithTerm:term 257 | path:path 258 | searchFolder:selectedSearchFolder 259 | literal:literal 260 | nocase:nocase 261 | words:words 262 | context:context 263 | symlinks:symlinks 264 | folderPattern:folderPattern 265 | filePattern:filePattern 266 | options:[optionsField objectValue]]; 267 | } 268 | 269 | - (void)flagsChanged:(NSEvent*)event 270 | { 271 | [self updateSearchSelectionForEvent:event]; 272 | } 273 | 274 | - (NSString*)projectSelectedSearchFolder 275 | { 276 | NSString* tmSelectedFile = [[projectController environmentVariables] objectForKey:@"TM_SELECTED_FILE"]; 277 | 278 | if (!tmSelectedFile) return nil; 279 | 280 | BOOL isdir = NO; 281 | 282 | if ([[NSFileManager defaultManager] fileExistsAtPath:tmSelectedFile isDirectory:&isdir] && isdir) 283 | return tmSelectedFile; 284 | 285 | return nil; 286 | } 287 | 288 | - (void)windowDidResignMain:(NSNotification*)notification 289 | { 290 | selectionSearch = selectionSearchDefault; 291 | self.selectedSearchFolder = nil; 292 | } 293 | 294 | - (void)windowDidBecomeMain:(NSNotification*)notification 295 | { 296 | [self updateSearchSelectionForEvent:[NSApp currentEvent]]; 297 | } 298 | 299 | - (void)updateSearchSelectionForEvent:(NSEvent*)event 300 | { 301 | selectionSearch = ([event modifierFlags] & NSCommandKeyMask) ? !selectionSearchDefault : selectionSearchDefault; 302 | if (selectionSearch) 303 | self.selectedSearchFolder = [self projectSelectedSearchFolder]; 304 | else 305 | self.selectedSearchFolder = nil; 306 | } 307 | 308 | - (NSString*)searchTitle 309 | { 310 | return (selectionSearch) ? @"In Selection" : @"Search"; 311 | } 312 | 313 | - (BOOL)canSearch 314 | { 315 | if ([self running] || ![self term] || (selectionSearch && !self.selectedSearchFolder)) 316 | return NO; 317 | 318 | return YES; 319 | } 320 | 321 | - (void)loadAckTypes 322 | { 323 | if (self.currentTypesProcess) 324 | return; 325 | 326 | self.currentTypesProcess = [[[JPAckTypesProcess alloc] init] autorelease]; 327 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(currentTypesProcessCompleted:) name:JPAckTypesProcessComplete object:self.currentTypesProcess]; 328 | NSString* path = self.projectDirectory; 329 | [self.currentTypesProcess invokeWithPath:path options:[optionsField objectValue]]; 330 | } 331 | 332 | - (void)openProjectFile:(NSString*)file atLine:(NSString*)line selectionRange:(NSRange)selectionRange 333 | { 334 | NSString* absolute = [projectDirectory stringByAppendingPathComponent:file]; 335 | [[[NSApplication sharedApplication] delegate] openFiles:[NSArray arrayWithObject:absolute]]; 336 | 337 | for (NSWindow *w in [[NSApplication sharedApplication] orderedWindows]) 338 | { 339 | id wc = [w windowController]; 340 | NSString* openFileName = nil; 341 | 342 | if ([[wc className] isEqualToString:@"OakProjectController"] || [[wc className] isEqualToString:@"OakDocumentController"]) 343 | openFileName = [[[wc textView] document] filename]; 344 | 345 | if ([openFileName isEqualToString:absolute]) 346 | { 347 | [[wc textView] goToLineNumber:line]; 348 | [[wc textView] goToColumnNumber:[NSNumber numberWithInt:selectionRange.location + 1]]; 349 | 350 | if (selectionRange.length > 0) 351 | [[wc textView] selectToLine:line andColumn:[NSNumber numberWithInt:selectionRange.location + selectionRange.length + 1]]; 352 | 353 | break; 354 | } 355 | } 356 | } 357 | 358 | - (IBAction)cancel:(id)sender 359 | { 360 | if ([self running]) 361 | [self.currentProcess terminateImmediately:NO]; 362 | else 363 | [[self window] performClose:nil]; 364 | } 365 | 366 | - (void)updateHistoryWithTerm:(NSString*)term_ 367 | { 368 | NSMutableArray* newHistory = [[history mutableCopy] autorelease]; 369 | [newHistory removeObject:term_]; 370 | [newHistory insertObject:term_ atIndex:0]; 371 | 372 | NSInteger ccount = [newHistory count]; 373 | if (ccount > 10) 374 | { 375 | NSRange toRemove = NSMakeRange(10, (ccount - 10)); 376 | [newHistory removeObjectsInRange:toRemove]; 377 | } 378 | 379 | self.history = newHistory; 380 | } 381 | 382 | - (NSString*)windowTitle 383 | { 384 | return [NSString stringWithFormat:@"AckMate: %@", fileName]; 385 | } 386 | 387 | - (NSArray *)tokenField:(NSTokenField*)tokenField shouldAddObjects:(NSArray*)tokens atIndex:(NSUInteger)index 388 | { 389 | return [self cleanseOptionList:tokens]; 390 | } 391 | 392 | - (IBAction)cleanseOptionsField:(id)sender 393 | { 394 | if (sender == optionsField) 395 | [sender setObjectValue:[self cleanseOptionList:[sender objectValue]]]; 396 | } 397 | 398 | - (NSArray*)cleanseOptionList:(NSArray*)optionList 399 | { 400 | NSMutableArray* okayOptions = [NSMutableArray array]; 401 | for (NSString* option in optionList) 402 | { 403 | if ([option isEqualToString:@"--"] || [option isEqualToString:@"-"] || [option isEqualToString:@"--match"] || [option hasPrefix:@"--ackmate"]) 404 | continue; 405 | 406 | if ([option hasPrefix:@"-"] || [ackTypes containsObject:option]) 407 | [okayOptions addObject:option]; 408 | } 409 | return okayOptions; 410 | } 411 | 412 | - (NSArray *)tokenField:(NSTokenField*)tokenField completionsForSubstring:(NSString*)substring indexOfToken:(NSInteger)tokenIndex indexOfSelectedItem:(NSInteger*)selectedIndex 413 | { 414 | NSMutableArray* suggestions = [NSMutableArray array]; 415 | if ([substring hasPrefix:@"-"]) 416 | return suggestions; 417 | 418 | for (NSString* acktype in [self ackTypes]) 419 | { 420 | if ([acktype hasPrefix:substring]) 421 | [suggestions addObject:acktype]; 422 | } 423 | 424 | *selectedIndex = 0; 425 | return suggestions; 426 | } 427 | 428 | - (void)currentProcessCompleted:(NSNotification*)notification 429 | { 430 | if ([notification object] == self.currentProcess) 431 | { 432 | [[NSNotificationCenter defaultCenter] removeObserver:self name:JPAckProcessComplete object:[notification object]]; 433 | self.currentProcess = nil; 434 | [[self window] makeFirstResponder:searchTermField]; 435 | } 436 | } 437 | 438 | - (void)currentTypesProcessCompleted:(NSNotification*)notification 439 | { 440 | if ([notification object] == self.currentTypesProcess) 441 | { 442 | [[NSNotificationCenter defaultCenter] removeObserver:self name:JPAckTypesProcessComplete object:[notification object]]; 443 | self.currentTypesProcess = nil; 444 | NSMutableArray* tarr = [[[[notification userInfo] objectForKey:kJPAckTypesResult] mutableCopy] autorelease]; 445 | if (tarr) 446 | { 447 | [tarr removeObject:@""]; // empty string because of final newline 448 | for (NSString* acktype in [[notification userInfo] objectForKey:kJPAckTypesResult]) 449 | { 450 | if (![acktype isEqualToString:@""]) 451 | [tarr addObject:[NSString stringWithFormat:@"no%@", acktype]]; 452 | } 453 | 454 | self.ackTypes = tarr; 455 | } 456 | } 457 | } 458 | 459 | - (BOOL)running 460 | { 461 | return (self.currentProcess) ? YES : NO; 462 | } 463 | 464 | - (void)dealloc 465 | { 466 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 467 | 468 | [fileName release], fileName = nil; 469 | [projectDirectory release], projectDirectory = nil; 470 | [selectedSearchFolder release], selectedSearchFolder = nil; 471 | [term release], term = nil; 472 | [ackTypes release], ackTypes = nil; 473 | [history release], history = nil; 474 | [currentProcess release], currentProcess = nil; 475 | [currentTypesProcess release], currentTypesProcess = nil; 476 | [super dealloc]; 477 | } 478 | @end 479 | -------------------------------------------------------------------------------- /source/controllers/JPAckResultSource.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010 Trevor Squires. All Rights Reserved. 2 | // See License.txt for full license. 3 | 4 | #import "JPAckWindowController.h" 5 | #import "JPAckResultSource.h" 6 | #import "JPAckResultRep.h" 7 | #import "JPAckResultTableView.h" 8 | #import "JPAckResultCell.h" 9 | 10 | @interface JPAckResultSource () 11 | @property(nonatomic, copy) NSString* longestLineNumber; 12 | @property(nonatomic, retain) NSMutableArray* resultRows; 13 | @property(nonatomic, copy, readwrite) NSString* resultStats; 14 | - (void)toggleFileRep:(JPAckResultRep*)rep atIndex:(NSInteger)index; 15 | - (void)configureFontAttributes; 16 | - (CGFloat)lineContentWidth; 17 | - (void)adjustForLongestLineNumber:(NSString*)linenumber; 18 | @end 19 | 20 | @implementation JPAckResultSource 21 | 22 | NSString* const amLineNumberColumn = @"amLineNumberColumn"; 23 | NSString* const amContentColumn = @"amContentColumn"; 24 | 25 | @synthesize longestLineNumber; 26 | @synthesize searchRoot; 27 | @synthesize resultStats; 28 | @synthesize resultRows; 29 | @synthesize matchedFiles; 30 | @synthesize matchedLines; 31 | 32 | - (void)awakeFromNib 33 | { 34 | currentResultFileRep = nil; 35 | resultStats = nil; 36 | searchRoot = nil; 37 | longestLineNumber = nil; 38 | 39 | self.resultRows = [NSMutableArray array]; 40 | 41 | [resultView setIntercellSpacing:NSMakeSize(0,0)]; 42 | 43 | NSArray* rvColumns = [resultView tableColumns]; 44 | NSAssert([rvColumns count] == 2, @"Expected 2 columns in output table"); 45 | [[rvColumns objectAtIndex:0] setIdentifier:amLineNumberColumn]; 46 | [[rvColumns objectAtIndex:1] setIdentifier:amContentColumn]; 47 | 48 | [self configureFontAttributes]; 49 | } 50 | 51 | - (void)clearContents 52 | { 53 | alternateRow = NO; 54 | matchedFiles = 0; 55 | matchedLines = 0; 56 | currentResultFileRep = nil; 57 | self.resultStats = nil; 58 | [self.resultRows removeAllObjects]; 59 | [resultView reloadData]; 60 | 61 | // reset column 0 to be 3 chars wide in the current font 62 | self.longestLineNumber = nil; 63 | [self adjustForLongestLineNumber:@"..."]; 64 | } 65 | 66 | - (void)updateStats 67 | { 68 | // 5 lines matched in 2 files 69 | NSString* insel = (searchingSelection) ? @"In selection: " : @""; 70 | 71 | self.resultStats = [NSString stringWithFormat:@"%@%d line%@ matched in %d file%@", insel, matchedLines, (matchedLines == 1) ? @"" : @"s", matchedFiles, (matchedFiles == 1) ? @"" : @"s"]; 72 | } 73 | 74 | - (void)searchingFor:(NSString*)term inRoot:(NSString*)searchRoot_ inFolder:(NSString*)searchFolder 75 | { 76 | self.searchRoot = searchRoot_; 77 | searchingSelection = (searchFolder) ? YES : NO; 78 | [self updateStats]; 79 | } 80 | 81 | - (void)parsedError:(NSString*)errorString 82 | { 83 | alternateRow = NO; 84 | JPAckResult* jpar = [JPAckResult resultErrorWithString:errorString]; 85 | [self.resultRows addObject:[JPAckResultRep withResultObject:jpar alternate:alternateRow]]; 86 | [resultView noteNumberOfRowsChanged]; 87 | } 88 | 89 | - (void)parsedFilename:(NSString*)filename 90 | { 91 | alternateRow = NO; 92 | matchedFiles++; 93 | [self updateStats]; 94 | 95 | JPAckResult* fileResult = [JPAckResult resultFileWithName:[filename substringFromIndex:[self.searchRoot length]]]; 96 | currentResultFileRep = [JPAckResultRep withResultObject:fileResult alternate:NO]; 97 | [self.resultRows addObject:currentResultFileRep]; 98 | [resultView noteNumberOfRowsChanged]; 99 | } 100 | 101 | - (void)parsedContextBreak 102 | { 103 | alternateRow = NO; 104 | JPAckResult* jpar = [JPAckResult resultContextBreak]; 105 | JPAckResultRep* jparrep = [JPAckResultRep withResultObject:jpar parent:currentResultFileRep alternate:NO]; 106 | if (![currentResultFileRep collapsed]) 107 | { 108 | [self.resultRows addObject:jparrep]; 109 | [resultView noteNumberOfRowsChanged]; 110 | } 111 | } 112 | 113 | - (void)parsedContextLine:(NSString*)lineNumber content:(NSString*)content 114 | { 115 | if (currentResultFileRep) 116 | { 117 | JPAckResult* jpar = [JPAckResult resultContextLineWithNumber:lineNumber content:content]; 118 | JPAckResultRep* jparrep = [JPAckResultRep withResultObject:jpar parent:currentResultFileRep alternate:alternateRow]; 119 | if (![currentResultFileRep collapsed]) 120 | { 121 | [self.resultRows addObject:jparrep]; 122 | [resultView noteNumberOfRowsChanged]; 123 | } 124 | [self adjustForLongestLineNumber:lineNumber]; 125 | alternateRow = !alternateRow; 126 | } 127 | } 128 | 129 | - (void)parsedMatchLine:(NSString*)lineNumber ranges:(NSArray*)ranges content:(NSString*)content 130 | { 131 | if (currentResultFileRep) 132 | { 133 | NSMutableArray* matchRanges = (ranges) ? [NSMutableArray arrayWithCapacity:[ranges count]] : nil; 134 | matchedLines++; 135 | 136 | for (NSString* rangeString in ranges) 137 | [matchRanges addObject:[NSValue valueWithRange:NSRangeFromString(rangeString)]]; 138 | 139 | JPAckResult* jpar = [JPAckResult resultMatchingLineWithNumber:lineNumber content:content ranges:matchRanges]; 140 | JPAckResultRep* jparrep = [JPAckResultRep withResultObject:jpar parent:currentResultFileRep alternate:alternateRow]; 141 | if (![currentResultFileRep collapsed]) 142 | { 143 | [self.resultRows addObject:jparrep]; 144 | [resultView noteNumberOfRowsChanged]; 145 | } 146 | [self adjustForLongestLineNumber:lineNumber]; 147 | alternateRow = !alternateRow; 148 | } 149 | } 150 | 151 | - (void)adjustForLongestLineNumber:(NSString*)lineNumber 152 | { 153 | if ([lineNumber length] > [self.longestLineNumber length]) 154 | { 155 | self.longestLineNumber = lineNumber; 156 | CGFloat lineNumberWidth = ceil(RESULT_ROW_PADDING + (RESULT_CONTENT_INTERIOR_PADDING * 2.0) + (RESULT_TEXT_XINSET * 2.0) + [lineNumber sizeWithAttributes:bodyNowrapAttributes].width); 157 | 158 | [[resultView tableColumnWithIdentifier:amLineNumberColumn] setWidth:lineNumberWidth]; 159 | [resultView sizeLastColumnToFit]; 160 | 161 | NSIndexSet* refreshIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [resultView numberOfRows])]; 162 | [resultView noteHeightOfRowsWithIndexesChanged:refreshIndexes]; 163 | } 164 | } 165 | 166 | - (CGFloat)lineContentWidth 167 | { 168 | return NSWidth([resultView rectOfColumn:1]) - RESULT_ROW_PADDING - (RESULT_TEXT_XINSET * 2.0) - (RESULT_CONTENT_INTERIOR_PADDING * 2); 169 | } 170 | 171 | - (NSInteger)tableView:(NSTableView *)tableView spanningColumnForRow:(NSInteger)row 172 | { 173 | JPAckResultType rt = [[self.resultRows objectAtIndex:row] resultType]; 174 | if (rt == JPResultTypeFilename || rt == JPResultTypeError) 175 | return 1; 176 | 177 | return NSNotFound; 178 | } 179 | 180 | - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row 181 | { 182 | JPAckResultRep* resultRep = [self.resultRows objectAtIndex:row]; 183 | 184 | switch([resultRep resultType]) 185 | { 186 | case JPResultTypeFilename: 187 | return headerHeight; 188 | case JPResultTypeContextBreak: 189 | return contextBreakHeight; 190 | } 191 | 192 | CGFloat maxWidth = [self lineContentWidth]; 193 | 194 | if (resultRep.constrainedWidth != maxWidth) 195 | { 196 | NSSize constraints = NSMakeSize(maxWidth, MAXFLOAT); 197 | resultRep.constrainedWidth = maxWidth; 198 | resultRep.calculatedHeight = (RESULT_TEXT_YINSET * 2) + NSHeight([[resultRep.resultObject lineContent] boundingRectWithSize:constraints options:(NSStringDrawingUsesLineFragmentOrigin) attributes:bodyAttributes]); 199 | } 200 | return resultRep.calculatedHeight; 201 | } 202 | 203 | - (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 204 | { 205 | return NO; 206 | } 207 | 208 | - (BOOL)tableView:(NSTableView *)tableView activateSelectedRow:(NSInteger)row atPoint:(NSPoint)pointHint 209 | { 210 | JPAckResultRep* rep = [self.resultRows objectAtIndex:row]; 211 | JPAckResult* resultObject = [rep resultObject]; 212 | 213 | if (resultObject.resultType == JPResultTypeFilename) 214 | { 215 | [self toggleFileRep:rep atIndex:row]; 216 | return YES; 217 | } 218 | else if (resultObject.resultType == JPResultTypeContext || resultObject.resultType == JPResultTypeMatchingLine) 219 | { 220 | NSString* filenameToOpen = [[[rep parentObject] resultObject] lineContent]; 221 | NSRange selectionRange = NSMakeRange(0,0); 222 | 223 | // I feel like such a bandit... oh well. 224 | if (NSPointInRect(pointHint, [tableView frameOfCellAtColumn:1 row:row]) && [resultObject matchRanges]) 225 | { 226 | // Quickly load up the field editor so we can find out where the click was 227 | [tableView editColumn:1 row:row withEvent:nil select:NO]; 228 | NSTextView* tv = (NSTextView*)[tableView currentEditor]; 229 | NSPoint adjustedPoint = [tv convertPoint:pointHint fromView:tableView]; 230 | NSUInteger clickIndex = [tv characterIndexForInsertionAtPoint:adjustedPoint]; 231 | 232 | NSRange closestRange = NSMakeRange(NSNotFound, 0); 233 | 234 | // get rid of the field editor right away 235 | if (![[tableView window] makeFirstResponder:tableView]) 236 | [[tableView window] endEditingFor:nil]; 237 | 238 | NSRange lastRange = NSMakeRange(NSNotFound, 0); 239 | for (NSValue* rv in [resultObject matchRanges]) 240 | { 241 | NSRange matchRange = [rv rangeValue]; 242 | if (NSLocationInRange(clickIndex, matchRange)) 243 | { 244 | closestRange = matchRange; 245 | break; 246 | } 247 | else if (clickIndex < matchRange.location) 248 | { 249 | if (lastRange.location != NSNotFound && (clickIndex - (lastRange.location + lastRange.length)) < (matchRange.location - clickIndex)) 250 | closestRange = lastRange; 251 | else 252 | closestRange = matchRange; 253 | 254 | break; 255 | } 256 | lastRange = matchRange; 257 | } 258 | 259 | if (closestRange.location == NSNotFound) 260 | closestRange = [[[resultObject matchRanges] lastObject] rangeValue]; 261 | 262 | selectionRange = closestRange; 263 | } 264 | 265 | [windowController openProjectFile:filenameToOpen atLine:[resultObject lineNumber] selectionRange:selectionRange]; 266 | return YES; 267 | } 268 | 269 | return NO; 270 | } 271 | 272 | - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row 273 | { 274 | JPAckResultType itemtype = [[self.resultRows objectAtIndex:row] resultType]; 275 | return ((itemtype == JPResultTypeMatchingLine || itemtype == JPResultTypeContext) && !([[NSApp currentEvent] modifierFlags] & NSControlKeyMask)); 276 | } 277 | 278 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 279 | { 280 | return [self.resultRows count]; 281 | } 282 | 283 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 284 | { 285 | JPAckResultRep* rep = [self.resultRows objectAtIndex:row]; 286 | JPAckResultType resultType = [rep resultType]; 287 | id value = nil; 288 | 289 | if ([tableColumn identifier] == amLineNumberColumn) 290 | { 291 | value = (resultType == JPResultTypeContextBreak) ? @"..." : [[rep resultObject] lineNumber]; 292 | } 293 | else if ([tableColumn identifier] == amContentColumn) 294 | { 295 | NSString* lineContent = [[rep resultObject] lineContent]; 296 | if (!lineContent) 297 | lineContent = @""; 298 | 299 | if ([rep resultType] == JPResultTypeMatchingLine) 300 | { 301 | NSRange contentRange = NSMakeRange(0, [lineContent length]); 302 | 303 | NSMutableAttributedString* attributedContent = [[[NSMutableAttributedString alloc] initWithString:lineContent attributes:bodyAttributes] autorelease]; 304 | for (NSValue* rv in [[rep resultObject] matchRanges]) 305 | [attributedContent setAttributes:bodyHighlightAttributes range:NSIntersectionRange([rv rangeValue], contentRange)]; 306 | 307 | value = attributedContent; 308 | } 309 | else if ([rep resultType] == JPResultTypeFilename) 310 | value = [[[NSMutableAttributedString alloc] initWithString:lineContent attributes:headingAttributes] autorelease]; 311 | else 312 | value = lineContent; 313 | } 314 | 315 | return value; 316 | } 317 | 318 | - (BOOL)tableView:(NSTableView *)tableView isStickyRow:(NSInteger)row 319 | { 320 | return ([[self.resultRows objectAtIndex:row] resultType] == JPResultTypeFilename); 321 | } 322 | 323 | - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)row 324 | { 325 | JPAckResultRep* rep = [self.resultRows objectAtIndex:row]; 326 | [(JPAckResultCell*)aCell configureType:[rep resultType] alternate:[rep alternate] collapsed:[rep collapsed] contentColumn:([aTableColumn identifier] != amLineNumberColumn)]; 327 | } 328 | 329 | - (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 330 | { 331 | return NO; 332 | } 333 | 334 | - (BOOL)tableView:(NSTableView*)tableView shouldEditTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row 335 | { 336 | // if ([tableColumn identifier] == amContentColumn && [tableView isRowSelected:row]) 337 | // return YES; 338 | 339 | return NO; 340 | } 341 | 342 | - (NSMenu*)tableView:(NSTableView *)tableView contextMenuForRow:(NSInteger)row; 343 | { 344 | JPAckResultRep* clickRep = [self.resultRows objectAtIndex:row]; 345 | JPAckResultRep* fileRep = [clickRep parentObject] ? [clickRep parentObject] : clickRep; 346 | 347 | if ([fileRep resultType] != JPResultTypeFilename) 348 | return nil; 349 | 350 | NSMenu* mfe = [[[NSMenu alloc] initWithTitle:@""] autorelease]; 351 | NSString* fileName = [[[fileRep resultObject] lineContent] lastPathComponent]; 352 | NSString* title = [NSString stringWithFormat:@"%@ %@", ([fileRep collapsed]) ? @"Expand" : @"Collapse", fileName]; 353 | NSMenuItem *toggleThis = [[[NSMenuItem alloc] initWithTitle:title action:@selector(toggleCollapsingItem:) keyEquivalent:@""] autorelease]; 354 | [toggleThis setTarget:self]; 355 | [toggleThis setRepresentedObject:fileRep]; 356 | [mfe addItem:toggleThis]; 357 | [mfe addItem:[NSMenuItem separatorItem]]; 358 | 359 | NSMenuItem *expandAll = [[[NSMenuItem alloc] initWithTitle:@"Expand All" action:@selector(expandAll:) keyEquivalent:@""] autorelease]; 360 | [expandAll setTarget:self]; 361 | [expandAll setRepresentedObject:fileRep]; 362 | [mfe addItem:expandAll]; 363 | NSMenuItem *collapseAll = [[[NSMenuItem alloc] initWithTitle:@"Collapse All" action:@selector(collapseAll:) keyEquivalent:@""] autorelease]; 364 | [collapseAll setTarget:self]; 365 | [collapseAll setRepresentedObject:fileRep]; 366 | [mfe addItem:collapseAll]; 367 | 368 | return mfe; 369 | } 370 | 371 | - (void)toggleCollapsingItem:(id)sender 372 | { 373 | JPAckResultRep* rep = [sender representedObject]; 374 | NSUInteger repindex = [self.resultRows indexOfObject:rep]; 375 | if (repindex == NSNotFound) 376 | return; 377 | [self toggleFileRep:rep atIndex:repindex]; 378 | } 379 | 380 | - (void)toggleFileRep:(JPAckResultRep*)rep atIndex:(NSInteger)index 381 | { 382 | NSInteger selRow = [resultView selectedRow]; 383 | NSIndexSet* effectiveSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index + 1, [[rep children] count])]; 384 | 385 | if ([rep collapsed]) 386 | [self.resultRows insertObjects:[rep children] atIndexes:effectiveSet]; 387 | else 388 | [self.resultRows removeObjectsAtIndexes:effectiveSet]; 389 | 390 | [resultView noteNumberOfRowsChanged]; 391 | 392 | if (![rep collapsed]) // as in, not marked as collapsed yet - so we *removed* rows 393 | { 394 | if (selRow != -1 && selRow > [effectiveSet lastIndex]) 395 | [resultView selectRowIndexes:[NSIndexSet indexSetWithIndex:(selRow - [[rep children] count])] byExtendingSelection:NO]; 396 | else if (selRow != -1 && [effectiveSet containsIndex:selRow]) 397 | [resultView deselectAll:self]; 398 | } 399 | else if (selRow != -1 && selRow > index) // we expanded, do we need to shuffle selection along? 400 | [resultView selectRowIndexes:[NSIndexSet indexSetWithIndex:(selRow + [[rep children] count])] byExtendingSelection:NO]; 401 | 402 | [rep setCollapsed:![rep collapsed]]; // *now* it's okay to flip the state 403 | [resultView scrollRowToVisible:index]; 404 | } 405 | 406 | - (void)collapseAll:(id)sender 407 | { 408 | NSUInteger contextRow = [self.resultRows indexOfObject:[sender representedObject]]; 409 | CGFloat contextOffset = 0.0; 410 | if (contextRow != NSNotFound) 411 | contextOffset = [resultView viewportOffsetForRow:contextRow]; 412 | 413 | NSMutableArray* collapsedResults = [NSMutableArray array]; 414 | for (JPAckResultRep* rep in self.resultRows) 415 | { 416 | if (![rep parentObject]) { 417 | [collapsedResults addObject:rep]; 418 | [rep setCollapsed:YES]; 419 | } 420 | } 421 | [resultView deselectAll:self]; 422 | self.resultRows = collapsedResults; 423 | [resultView noteNumberOfRowsChanged]; 424 | 425 | contextRow = [self.resultRows indexOfObject:[sender representedObject]]; 426 | if (contextRow != NSNotFound) 427 | [resultView scrollRowToVisible:contextRow withViewportOffset:contextOffset]; 428 | } 429 | 430 | - (void)expandAll:(id)sender 431 | { 432 | NSUInteger contextRow = [self.resultRows indexOfObject:[sender representedObject]]; 433 | CGFloat contextOffset = 0.0; 434 | if (contextRow != NSNotFound) 435 | contextOffset = [resultView viewportOffsetForRow:contextRow]; 436 | 437 | JPAckResultRep* selectedRep = nil; 438 | NSInteger selRow = [resultView selectedRow]; 439 | if (selRow != -1) 440 | selectedRep = [self.resultRows objectAtIndex:selRow]; 441 | 442 | NSMutableArray* expandedResults = [NSMutableArray array]; 443 | for (JPAckResultRep* rep in self.resultRows) 444 | { 445 | [expandedResults addObject:rep]; 446 | if (![rep parentObject] && [rep collapsed]) { 447 | [rep setCollapsed:NO]; 448 | [expandedResults addObjectsFromArray:[rep children]]; 449 | } 450 | } 451 | self.resultRows = expandedResults; 452 | [resultView noteNumberOfRowsChanged]; 453 | 454 | if (selectedRep) // restore previous selection 455 | { 456 | NSUInteger newIndex = [self.resultRows indexOfObject:selectedRep]; 457 | if (newIndex != NSNotFound) 458 | [resultView selectRowIndexes:[NSIndexSet indexSetWithIndex:newIndex] byExtendingSelection:NO]; 459 | } 460 | 461 | contextRow = [self.resultRows indexOfObject:[sender representedObject]]; 462 | if (contextRow != NSNotFound) 463 | [resultView scrollRowToVisible:contextRow withViewportOffset:contextOffset]; 464 | } 465 | 466 | - (void)configureFontAttributes 467 | { 468 | NSFontManager* fm = [NSFontManager sharedFontManager]; 469 | 470 | NSFont* headingFont = [NSFont fontWithName:@"Trebuchet MS Bold" size:13.0]; 471 | if (!headingFont) 472 | headingFont = [NSFont boldSystemFontOfSize:13.0]; 473 | 474 | NSFont* bodyFont = [NSFont fontWithName:@"Menlo-Regular" size:11.0]; 475 | if (!bodyFont) 476 | bodyFont = [NSFont fontWithName:@"Monaco" size:11.0]; 477 | 478 | if (!bodyFont) 479 | bodyFont = [NSFont userFixedPitchFontOfSize:11.0]; 480 | 481 | NSFont* boldBodyFont = [fm convertWeight:YES ofFont:bodyFont]; 482 | 483 | // Heading (filename) attributes 484 | NSMutableParagraphStyle* nowrapStyle = [[[NSMutableParagraphStyle alloc] init] autorelease]; 485 | [nowrapStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 486 | 487 | headingAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: 488 | headingFont, NSFontAttributeName, 489 | nowrapStyle, NSParagraphStyleAttributeName, 490 | nil]; 491 | 492 | // Body (output context/matches) sans-wrapping 493 | bodyNowrapAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: 494 | bodyFont, NSFontAttributeName, 495 | [[nowrapStyle copy] autorelease], NSParagraphStyleAttributeName, 496 | nil]; 497 | 498 | // Body (output context/matches) attributes 499 | NSMutableParagraphStyle* wrapStyle = [[[NSMutableParagraphStyle alloc] init] autorelease]; 500 | [wrapStyle setLineBreakMode:NSLineBreakByWordWrapping]; 501 | 502 | // Force tabstops to be 2 characters wide 503 | CGFloat tabWidth = [@".." sizeWithAttributes:bodyNowrapAttributes].width; 504 | [wrapStyle setTabStops:[NSArray array]]; 505 | [wrapStyle setDefaultTabInterval:tabWidth]; 506 | 507 | bodyAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: 508 | bodyFont, NSFontAttributeName, 509 | wrapStyle, NSParagraphStyleAttributeName, 510 | nil]; 511 | 512 | // Body highlight (matched character ranges) attributes 513 | bodyHighlightAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 514 | boldBodyFont, NSFontAttributeName, 515 | [NSColor colorWithCalibratedRed:(255.0/255.0) green:(225.0/255.0) blue:(68.0/255.0) alpha:1.0], NSBackgroundColorAttributeName, 516 | nil]; 517 | 518 | // Make sure the table is using our chosen font 519 | for (NSTableColumn* tc in [resultView tableColumns]) 520 | [[tc dataCell] setFont:bodyFont]; 521 | 522 | // Precalculate a few row heights: 523 | headerHeight = RESULT_ROW_PADDING + (RESULT_TEXT_YINSET * 2) + [@"Jiminy!" sizeWithAttributes:headingAttributes].height; 524 | contextBreakHeight = (RESULT_TEXT_YINSET * 2) + [@"Jiminy!" sizeWithAttributes:bodyAttributes].height; 525 | } 526 | 527 | - (void)dealloc 528 | { 529 | [searchRoot release], searchRoot = nil; 530 | [resultStats release], resultStats = nil; 531 | [resultRows release], resultRows = nil; 532 | [longestLineNumber release], longestLineNumber = nil; 533 | 534 | [headingAttributes release], headingAttributes = nil; 535 | [bodyAttributes release], bodyAttributes = nil; 536 | [bodyNowrapAttributes release], bodyNowrapAttributes = nil; 537 | [bodyHighlightAttributes release], bodyHighlightAttributes = nil; 538 | 539 | [super dealloc]; 540 | } 541 | @end 542 | -------------------------------------------------------------------------------- /AckMate.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 691034E211605F4900126261 /* JPAckSearchButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 691034E111605F4900126261 /* JPAckSearchButton.m */; }; 11 | 691B26C7112A6065007E13BC /* AckMatePlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 691B26C6112A6065007E13BC /* AckMatePlugin.m */; }; 12 | 6935050411592F450084DE4C /* JPAckResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 6935050311592F450084DE4C /* JPAckResult.m */; }; 13 | 69360CB1112CE3ED00DE19E1 /* JPAckTypesProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = 69360CB0112CE3ED00DE19E1 /* JPAckTypesProcess.m */; }; 14 | 69830191115929B400DBA617 /* JPAckResultRep.m in Sources */ = {isa = PBXBuildFile; fileRef = 69830190115929B400DBA617 /* JPAckResultRep.m */; }; 15 | 69B8C4BE112A371A00EB45AF /* ackmate_ack.autogenerated in Resources */ = {isa = PBXBuildFile; fileRef = 69B8C4BD112A371A00EB45AF /* ackmate_ack.autogenerated */; }; 16 | 69B8C4E6112A383600EB45AF /* JPAckWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 69B8C4E4112A383600EB45AF /* JPAckWindow.xib */; }; 17 | 69C43AF6112A35EE007AC1C1 /* NSImage-NoodleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AF1112A35EE007AC1C1 /* NSImage-NoodleExtensions.m */; }; 18 | 69C43AF7112A35EE007AC1C1 /* NSTableView+NoodleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AF3112A35EE007AC1C1 /* NSTableView+NoodleExtensions.m */; }; 19 | 69C43AF8112A35EE007AC1C1 /* SDFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AF5112A35EE007AC1C1 /* SDFoundation.m */; }; 20 | 69C43AFF112A35F9007AC1C1 /* JPAckControlView.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AFA112A35F9007AC1C1 /* JPAckControlView.m */; }; 21 | 69C43B00112A35F9007AC1C1 /* JPAckResultCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AFC112A35F9007AC1C1 /* JPAckResultCell.m */; }; 22 | 69C43B01112A35F9007AC1C1 /* JPAckResultTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43AFE112A35F9007AC1C1 /* JPAckResultTableView.m */; }; 23 | 69C43B0A112A361C007AC1C1 /* JPAckProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43B03112A361C007AC1C1 /* JPAckProcess.m */; }; 24 | 69C43B0B112A361C007AC1C1 /* JPAckProcess+Parsing.m.rl in Sources */ = {isa = PBXBuildFile; fileRef = 69C43B05112A361C007AC1C1 /* JPAckProcess+Parsing.m.rl */; }; 25 | 69C43B0C112A361C007AC1C1 /* JPAckResultSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43B07112A361C007AC1C1 /* JPAckResultSource.m */; }; 26 | 69C43B0D112A361C007AC1C1 /* JPAckWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C43B09112A361C007AC1C1 /* JPAckWindowController.m */; }; 27 | 69D7008D118115CA008A6125 /* ackmateExpanded.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 69D7008B118115CA008A6125 /* ackmateExpanded.pdf */; }; 28 | 69D7008E118115CA008A6125 /* ackmateCollapsed.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 69D7008C118115CA008A6125 /* ackmateCollapsed.pdf */; }; 29 | 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; 30 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXBuildRule section */ 34 | 69C49DC4112A36B100B1349C /* PBXBuildRule */ = { 35 | isa = PBXBuildRule; 36 | compilerSpec = com.apple.compilers.proxy.script; 37 | filePatterns = "*.autogenerated"; 38 | fileType = pattern.proxy; 39 | isEditable = 1; 40 | outputFiles = ( 41 | "${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}", 42 | ); 43 | script = "cp ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}.autogenerated ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}\nchmod 555 ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}"; 44 | }; 45 | 69C49DC5112A36B100B1349C /* PBXBuildRule */ = { 46 | isa = PBXBuildRule; 47 | compilerSpec = com.apple.compilers.proxy.script; 48 | filePatterns = "*.rl"; 49 | fileType = pattern.proxy; 50 | isEditable = 1; 51 | outputFiles = ( 52 | "$(DERIVED_FILES_DIR)/$(INPUT_FILE_BASE)", 53 | ); 54 | script = "/usr/local/bin/ragel ${INPUT_FILE_DIR}/${INPUT_FILE_BASE}.rl -o ${DERIVED_FILES_DIR}/${INPUT_FILE_BASE}"; 55 | }; 56 | /* End PBXBuildRule section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 60 | 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 61 | 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 62 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 63 | 32DBCF630370AF2F00C91783 /* AckMate_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AckMate_Prefix.pch; sourceTree = ""; }; 64 | 691034E011605F4900126261 /* JPAckSearchButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckSearchButton.h; sourceTree = ""; }; 65 | 691034E111605F4900126261 /* JPAckSearchButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckSearchButton.m; sourceTree = ""; }; 66 | 691B26C5112A6065007E13BC /* AckMatePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AckMatePlugin.h; sourceTree = ""; }; 67 | 691B26C6112A6065007E13BC /* AckMatePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AckMatePlugin.m; sourceTree = ""; }; 68 | 6935050211592F450084DE4C /* JPAckResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckResult.h; sourceTree = ""; }; 69 | 6935050311592F450084DE4C /* JPAckResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckResult.m; sourceTree = ""; }; 70 | 69360CAF112CE3ED00DE19E1 /* JPAckTypesProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckTypesProcess.h; sourceTree = ""; }; 71 | 69360CB0112CE3ED00DE19E1 /* JPAckTypesProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckTypesProcess.m; sourceTree = ""; }; 72 | 6983018F115929B400DBA617 /* JPAckResultRep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckResultRep.h; sourceTree = ""; }; 73 | 69830190115929B400DBA617 /* JPAckResultRep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckResultRep.m; sourceTree = ""; }; 74 | 69B8C4BD112A371A00EB45AF /* ackmate_ack.autogenerated */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ackmate_ack.autogenerated; sourceTree = ""; }; 75 | 69B8C4E5112A383600EB45AF /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/JPAckWindow.xib; sourceTree = SOURCE_ROOT; }; 76 | 69C43AF0112A35EE007AC1C1 /* NSImage-NoodleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage-NoodleExtensions.h"; sourceTree = ""; }; 77 | 69C43AF1112A35EE007AC1C1 /* NSImage-NoodleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage-NoodleExtensions.m"; sourceTree = ""; }; 78 | 69C43AF2112A35EE007AC1C1 /* NSTableView+NoodleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTableView+NoodleExtensions.h"; sourceTree = ""; }; 79 | 69C43AF3112A35EE007AC1C1 /* NSTableView+NoodleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTableView+NoodleExtensions.m"; sourceTree = ""; }; 80 | 69C43AF4112A35EE007AC1C1 /* SDFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDFoundation.h; sourceTree = ""; }; 81 | 69C43AF5112A35EE007AC1C1 /* SDFoundation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDFoundation.m; sourceTree = ""; }; 82 | 69C43AF9112A35F9007AC1C1 /* JPAckControlView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckControlView.h; sourceTree = ""; }; 83 | 69C43AFA112A35F9007AC1C1 /* JPAckControlView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckControlView.m; sourceTree = ""; }; 84 | 69C43AFB112A35F9007AC1C1 /* JPAckResultCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckResultCell.h; sourceTree = ""; }; 85 | 69C43AFC112A35F9007AC1C1 /* JPAckResultCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckResultCell.m; sourceTree = ""; }; 86 | 69C43AFD112A35F9007AC1C1 /* JPAckResultTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckResultTableView.h; sourceTree = ""; }; 87 | 69C43AFE112A35F9007AC1C1 /* JPAckResultTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckResultTableView.m; sourceTree = ""; }; 88 | 69C43B02112A361C007AC1C1 /* JPAckProcess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckProcess.h; sourceTree = ""; }; 89 | 69C43B03112A361C007AC1C1 /* JPAckProcess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckProcess.m; sourceTree = ""; }; 90 | 69C43B04112A361C007AC1C1 /* JPAckProcess+Parsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JPAckProcess+Parsing.h"; sourceTree = ""; }; 91 | 69C43B05112A361C007AC1C1 /* JPAckProcess+Parsing.m.rl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "JPAckProcess+Parsing.m.rl"; sourceTree = ""; }; 92 | 69C43B06112A361C007AC1C1 /* JPAckResultSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckResultSource.h; sourceTree = ""; }; 93 | 69C43B07112A361C007AC1C1 /* JPAckResultSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckResultSource.m; sourceTree = ""; }; 94 | 69C43B08112A361C007AC1C1 /* JPAckWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JPAckWindowController.h; sourceTree = ""; }; 95 | 69C43B09112A361C007AC1C1 /* JPAckWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JPAckWindowController.m; sourceTree = ""; }; 96 | 69D7008B118115CA008A6125 /* ackmateExpanded.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ackmateExpanded.pdf; sourceTree = ""; }; 97 | 69D7008C118115CA008A6125 /* ackmateCollapsed.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ackmateCollapsed.pdf; sourceTree = ""; }; 98 | 78BF820513B8C77D00944D81 /* AckMate.tmplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AckMate.tmplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 99 | 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 101 | /* End PBXFileReference section */ 102 | 103 | /* Begin PBXFrameworksBuildPhase section */ 104 | 8D5B49B3048680CD000E48DA /* Frameworks */ = { 105 | isa = PBXFrameworksBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | /* End PBXFrameworksBuildPhase section */ 113 | 114 | /* Begin PBXGroup section */ 115 | 089C166AFE841209C02AAC07 /* AckMate */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 08FB77AFFE84173DC02AAC07 /* Classes */, 119 | 32C88E010371C26100C91783 /* Other Sources */, 120 | 089C167CFE841241C02AAC07 /* Resources */, 121 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, 122 | 19C28FB8FE9D52D311CA2CBB /* Products */, 123 | ); 124 | name = AckMate; 125 | sourceTree = ""; 126 | }; 127 | 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, 131 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, 132 | ); 133 | name = "Frameworks and Libraries"; 134 | sourceTree = ""; 135 | }; 136 | 089C167CFE841241C02AAC07 /* Resources */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 69B8C4E4112A383600EB45AF /* JPAckWindow.xib */, 140 | 69B8C4BC112A370200EB45AF /* bundle_extras */, 141 | 8D5B49B7048680CD000E48DA /* Info.plist */, 142 | 089C167DFE841241C02AAC07 /* InfoPlist.strings */, 143 | ); 144 | name = Resources; 145 | sourceTree = ""; 146 | }; 147 | 08FB77AFFE84173DC02AAC07 /* Classes */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 69C43AE9112A3592007AC1C1 /* controllers */, 151 | 69C43AE8112A3587007AC1C1 /* models */, 152 | 69C43AEA112A359A007AC1C1 /* views */, 153 | 69C43AEC112A35CA007AC1C1 /* external */, 154 | ); 155 | name = Classes; 156 | sourceTree = ""; 157 | }; 158 | 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, 162 | ); 163 | name = "Linked Frameworks"; 164 | sourceTree = ""; 165 | }; 166 | 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 089C167FFE841241C02AAC07 /* AppKit.framework */, 170 | D2F7E65807B2D6F200F64583 /* CoreData.framework */, 171 | 089C1672FE841209C02AAC07 /* Foundation.framework */, 172 | ); 173 | name = "Other Frameworks"; 174 | sourceTree = ""; 175 | }; 176 | 19C28FB8FE9D52D311CA2CBB /* Products */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 78BF820513B8C77D00944D81 /* AckMate.tmplugin */, 180 | ); 181 | name = Products; 182 | sourceTree = ""; 183 | }; 184 | 32C88E010371C26100C91783 /* Other Sources */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 32DBCF630370AF2F00C91783 /* AckMate_Prefix.pch */, 188 | ); 189 | name = "Other Sources"; 190 | sourceTree = ""; 191 | }; 192 | 69B8C4BC112A370200EB45AF /* bundle_extras */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 69D7008B118115CA008A6125 /* ackmateExpanded.pdf */, 196 | 69D7008C118115CA008A6125 /* ackmateCollapsed.pdf */, 197 | 69B8C4BD112A371A00EB45AF /* ackmate_ack.autogenerated */, 198 | ); 199 | path = bundle_extras; 200 | sourceTree = ""; 201 | }; 202 | 69C43AE8112A3587007AC1C1 /* models */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 6983018F115929B400DBA617 /* JPAckResultRep.h */, 206 | 69830190115929B400DBA617 /* JPAckResultRep.m */, 207 | 6935050211592F450084DE4C /* JPAckResult.h */, 208 | 6935050311592F450084DE4C /* JPAckResult.m */, 209 | ); 210 | name = models; 211 | path = source/models; 212 | sourceTree = ""; 213 | }; 214 | 69C43AE9112A3592007AC1C1 /* controllers */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 691B26C5112A6065007E13BC /* AckMatePlugin.h */, 218 | 691B26C6112A6065007E13BC /* AckMatePlugin.m */, 219 | 69C43B02112A361C007AC1C1 /* JPAckProcess.h */, 220 | 69C43B03112A361C007AC1C1 /* JPAckProcess.m */, 221 | 69C43B04112A361C007AC1C1 /* JPAckProcess+Parsing.h */, 222 | 69C43B05112A361C007AC1C1 /* JPAckProcess+Parsing.m.rl */, 223 | 69C43B06112A361C007AC1C1 /* JPAckResultSource.h */, 224 | 69C43B07112A361C007AC1C1 /* JPAckResultSource.m */, 225 | 69C43B08112A361C007AC1C1 /* JPAckWindowController.h */, 226 | 69C43B09112A361C007AC1C1 /* JPAckWindowController.m */, 227 | 69360CAF112CE3ED00DE19E1 /* JPAckTypesProcess.h */, 228 | 69360CB0112CE3ED00DE19E1 /* JPAckTypesProcess.m */, 229 | ); 230 | name = controllers; 231 | path = source/controllers; 232 | sourceTree = ""; 233 | }; 234 | 69C43AEA112A359A007AC1C1 /* views */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | 69C43AF9112A35F9007AC1C1 /* JPAckControlView.h */, 238 | 69C43AFA112A35F9007AC1C1 /* JPAckControlView.m */, 239 | 69C43AFB112A35F9007AC1C1 /* JPAckResultCell.h */, 240 | 69C43AFC112A35F9007AC1C1 /* JPAckResultCell.m */, 241 | 69C43AFD112A35F9007AC1C1 /* JPAckResultTableView.h */, 242 | 69C43AFE112A35F9007AC1C1 /* JPAckResultTableView.m */, 243 | 691034E011605F4900126261 /* JPAckSearchButton.h */, 244 | 691034E111605F4900126261 /* JPAckSearchButton.m */, 245 | ); 246 | name = views; 247 | path = source/views; 248 | sourceTree = ""; 249 | }; 250 | 69C43AEC112A35CA007AC1C1 /* external */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | 69C43AF0112A35EE007AC1C1 /* NSImage-NoodleExtensions.h */, 254 | 69C43AF1112A35EE007AC1C1 /* NSImage-NoodleExtensions.m */, 255 | 69C43AF2112A35EE007AC1C1 /* NSTableView+NoodleExtensions.h */, 256 | 69C43AF3112A35EE007AC1C1 /* NSTableView+NoodleExtensions.m */, 257 | 69C43AF4112A35EE007AC1C1 /* SDFoundation.h */, 258 | 69C43AF5112A35EE007AC1C1 /* SDFoundation.m */, 259 | ); 260 | name = external; 261 | path = source/external; 262 | sourceTree = ""; 263 | }; 264 | /* End PBXGroup section */ 265 | 266 | /* Begin PBXNativeTarget section */ 267 | 8D5B49AC048680CD000E48DA /* AckMate */ = { 268 | isa = PBXNativeTarget; 269 | buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "AckMate" */; 270 | buildPhases = ( 271 | 8D5B49AF048680CD000E48DA /* Resources */, 272 | 8D5B49B1048680CD000E48DA /* Sources */, 273 | 8D5B49B3048680CD000E48DA /* Frameworks */, 274 | 177E4DB50913322B0064163D /* ShellScript */, 275 | ); 276 | buildRules = ( 277 | 69C49DC4112A36B100B1349C /* PBXBuildRule */, 278 | 69C49DC5112A36B100B1349C /* PBXBuildRule */, 279 | ); 280 | dependencies = ( 281 | ); 282 | name = AckMate; 283 | productInstallPath = "$(HOME)/Library/Bundles"; 284 | productName = AckMate; 285 | productReference = 78BF820513B8C77D00944D81 /* AckMate.tmplugin */; 286 | productType = "com.apple.product-type.bundle"; 287 | }; 288 | /* End PBXNativeTarget section */ 289 | 290 | /* Begin PBXProject section */ 291 | 089C1669FE841209C02AAC07 /* Project object */ = { 292 | isa = PBXProject; 293 | attributes = { 294 | ORGANIZATIONNAME = "Jetpack Pony"; 295 | }; 296 | buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "AckMate" */; 297 | compatibilityVersion = "Xcode 3.2"; 298 | hasScannedForEncodings = 1; 299 | knownRegions = ( 300 | en, 301 | ); 302 | mainGroup = 089C166AFE841209C02AAC07 /* AckMate */; 303 | projectDirPath = ""; 304 | projectRoot = ""; 305 | targets = ( 306 | 8D5B49AC048680CD000E48DA /* AckMate */, 307 | ); 308 | }; 309 | /* End PBXProject section */ 310 | 311 | /* Begin PBXResourcesBuildPhase section */ 312 | 8D5B49AF048680CD000E48DA /* Resources */ = { 313 | isa = PBXResourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */, 317 | 69B8C4BE112A371A00EB45AF /* ackmate_ack.autogenerated in Resources */, 318 | 69B8C4E6112A383600EB45AF /* JPAckWindow.xib in Resources */, 319 | 69D7008D118115CA008A6125 /* ackmateExpanded.pdf in Resources */, 320 | 69D7008E118115CA008A6125 /* ackmateCollapsed.pdf in Resources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXResourcesBuildPhase section */ 325 | 326 | /* Begin PBXShellScriptBuildPhase section */ 327 | 177E4DB50913322B0064163D /* ShellScript */ = { 328 | isa = PBXShellScriptBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | ); 332 | inputPaths = ( 333 | ); 334 | outputPaths = ( 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | shellPath = /bin/sh; 338 | shellScript = "mkdir -p \"$HOME/Library/Application Support/TextMate/PlugIns\"\ncp -pR \"${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}\" \"$HOME/Library/Application Support/TextMate/PlugIns\""; 339 | }; 340 | /* End PBXShellScriptBuildPhase section */ 341 | 342 | /* Begin PBXSourcesBuildPhase section */ 343 | 8D5B49B1048680CD000E48DA /* Sources */ = { 344 | isa = PBXSourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | 69C43B0B112A361C007AC1C1 /* JPAckProcess+Parsing.m.rl in Sources */, 348 | 69C43AF6112A35EE007AC1C1 /* NSImage-NoodleExtensions.m in Sources */, 349 | 69C43AF7112A35EE007AC1C1 /* NSTableView+NoodleExtensions.m in Sources */, 350 | 69C43AF8112A35EE007AC1C1 /* SDFoundation.m in Sources */, 351 | 69C43AFF112A35F9007AC1C1 /* JPAckControlView.m in Sources */, 352 | 69C43B00112A35F9007AC1C1 /* JPAckResultCell.m in Sources */, 353 | 69C43B01112A35F9007AC1C1 /* JPAckResultTableView.m in Sources */, 354 | 69C43B0A112A361C007AC1C1 /* JPAckProcess.m in Sources */, 355 | 69C43B0C112A361C007AC1C1 /* JPAckResultSource.m in Sources */, 356 | 69C43B0D112A361C007AC1C1 /* JPAckWindowController.m in Sources */, 357 | 691B26C7112A6065007E13BC /* AckMatePlugin.m in Sources */, 358 | 69360CB1112CE3ED00DE19E1 /* JPAckTypesProcess.m in Sources */, 359 | 69830191115929B400DBA617 /* JPAckResultRep.m in Sources */, 360 | 6935050411592F450084DE4C /* JPAckResult.m in Sources */, 361 | 691034E211605F4900126261 /* JPAckSearchButton.m in Sources */, 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | }; 365 | /* End PBXSourcesBuildPhase section */ 366 | 367 | /* Begin PBXVariantGroup section */ 368 | 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = { 369 | isa = PBXVariantGroup; 370 | children = ( 371 | 089C167EFE841241C02AAC07 /* English */, 372 | ); 373 | name = InfoPlist.strings; 374 | sourceTree = ""; 375 | }; 376 | 69B8C4E4112A383600EB45AF /* JPAckWindow.xib */ = { 377 | isa = PBXVariantGroup; 378 | children = ( 379 | 69B8C4E5112A383600EB45AF /* English */, 380 | ); 381 | name = JPAckWindow.xib; 382 | sourceTree = ""; 383 | }; 384 | /* End PBXVariantGroup section */ 385 | 386 | /* Begin XCBuildConfiguration section */ 387 | 1DEB913B08733D840010E9CD /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 391 | COPY_PHASE_STRIP = NO; 392 | GCC_DYNAMIC_NO_PIC = NO; 393 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 394 | GCC_MODEL_TUNING = G5; 395 | GCC_OPTIMIZATION_LEVEL = 0; 396 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 397 | GCC_PREFIX_HEADER = AckMate_Prefix.pch; 398 | INFOPLIST_FILE = Info.plist; 399 | INSTALL_PATH = "$(HOME)/Library/Bundles"; 400 | PRODUCT_NAME = AckMate; 401 | SDKROOT = macosx10.6; 402 | WRAPPER_EXTENSION = tmplugin; 403 | ZERO_LINK = YES; 404 | }; 405 | name = Debug; 406 | }; 407 | 1DEB913C08733D840010E9CD /* Release */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 411 | GCC_GENERATE_DEBUGGING_SYMBOLS = NO; 412 | GCC_MODEL_TUNING = G5; 413 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 414 | GCC_PREFIX_HEADER = AckMate_Prefix.pch; 415 | INFOPLIST_FILE = Info.plist; 416 | INSTALL_PATH = "$(HOME)/Library/Bundles"; 417 | PRODUCT_NAME = AckMate; 418 | SDKROOT = macosx10.6; 419 | WRAPPER_EXTENSION = tmplugin; 420 | }; 421 | name = Release; 422 | }; 423 | 1DEB913F08733D840010E9CD /* Debug */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | PREBINDING = NO; 430 | SDKROOT = macosx10.6; 431 | }; 432 | name = Debug; 433 | }; 434 | 1DEB914008733D840010E9CD /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; 438 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 439 | GCC_WARN_UNUSED_VARIABLE = YES; 440 | PREBINDING = NO; 441 | SDKROOT = macosx10.6; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "AckMate" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 1DEB913B08733D840010E9CD /* Debug */, 452 | 1DEB913C08733D840010E9CD /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "AckMate" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | 1DEB913F08733D840010E9CD /* Debug */, 461 | 1DEB914008733D840010E9CD /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | /* End XCConfigurationList section */ 467 | }; 468 | rootObject = 089C1669FE841209C02AAC07 /* Project object */; 469 | } 470 | --------------------------------------------------------------------------------