diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index df9ae8e..dd75fb2 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -3,16 +3,15 @@ 1050 10F569 - 804 + 788 1038.29 461.00 com.apple.InterfaceBuilder.CocoaPlugin - 804 + 788 YES - YES @@ -1104,22 +1103,6 @@ 199 - - - performFindPanelAction: - - - - 200 - - - - performFindPanelAction: - - - - 201 - performFindPanelAction: @@ -1368,6 +1351,22 @@ 966 + + + selectNext: + + + + 967 + + + + selectPrevious: + + + + 968 + @@ -2332,7 +2331,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - {{864, 473}, {238, 103}} + {{762, 747}, {238, 103}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2498,7 +2497,7 @@ - 966 + 968 @@ -2665,6 +2664,13 @@ + + NSApplication + + IBProjectSource + NSApplication+GitXScripting.h + + NSOutlineView @@ -2688,12 +2694,14 @@ YES controller + searchController webController webView YES PBGitHistoryController + PBHistorySearchController PBWebHistoryController WebView @@ -2703,6 +2711,7 @@ YES controller + searchController webController webView @@ -2712,6 +2721,10 @@ controller PBGitHistoryController + + searchController + PBHistorySearchController + webController PBWebHistoryController @@ -3148,6 +3161,7 @@ rebaseButton refController scopeBarView + searchController searchField selectedBranchFilterItem treeController @@ -3168,6 +3182,7 @@ NSButton PBRefController PBGitGradientBarView + PBHistorySearchController NSSearchField NSButton NSTreeController @@ -3191,6 +3206,7 @@ rebaseButton refController scopeBarView + searchController searchField selectedBranchFilterItem treeController @@ -3244,6 +3260,10 @@ scopeBarView PBGitGradientBarView + + searchController + PBHistorySearchController + searchField NSSearchField @@ -3617,6 +3637,78 @@ + + PBHistorySearchController + NSObject + + stepperPressed: + id + + + stepperPressed: + + stepperPressed: + id + + + + YES + + YES + commitController + historyController + numberOfMatchesField + searchField + stepper + + + YES + NSArrayController + PBGitHistoryController + NSTextField + NSSearchField + NSSegmentedControl + + + + YES + + YES + commitController + historyController + numberOfMatchesField + searchField + stepper + + + YES + + commitController + NSArrayController + + + historyController + PBGitHistoryController + + + numberOfMatchesField + NSTextField + + + searchField + NSSearchField + + + stepper + NSSegmentedControl + + + + + IBProjectSource + PBHistorySearchController.h + + PBNiceSplitView NSSplitView @@ -4542,6 +4634,34 @@ Foundation.framework/Headers/NSURLDownload.h + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CAAnimation.h + + + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CALayer.h + + + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CIImageProvider.h + + + + NSObject + + IBFrameworkSource + ScriptingBridge.framework/Headers/SBApplication.h + + NSObject @@ -4678,6 +4798,14 @@ AppKit.framework/Headers/NSSearchField.h + + NSSegmentedControl + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSSegmentedControl.h + + NSSplitView NSView diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 5cb9bd1..f95d9c4 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -68,18 +68,22 @@ D858108411274D28007F254B /* RemoteBranch.png in Resources */ = {isa = PBXBuildFile; fileRef = D858108111274D28007F254B /* RemoteBranch.png */; }; D858108511274D28007F254B /* Tag.png in Resources */ = {isa = PBXBuildFile; fileRef = D858108211274D28007F254B /* Tag.png */; }; D85B939310E3D8B4007F3C28 /* PBCreateBranchSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = D85B939210E3D8B4007F3C28 /* PBCreateBranchSheet.xib */; }; + D87127011229A21C00012334 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D87127001229A21C00012334 /* QuartzCore.framework */; }; + D8712A00122B14EC00012334 /* GitXTextFieldCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D87129FF122B14EC00012334 /* GitXTextFieldCell.m */; }; D889EB3110E6BCBB00F08413 /* PBCreateTagSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = D889EB3010E6BCBB00F08413 /* PBCreateTagSheet.xib */; }; D89E9B141218BA260097A90B /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */; }; D8A4BB6F11337D5C00E92D51 /* PBGitGradientBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */; }; D8A4BD071134AD2900E92D51 /* CherryPickTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */; }; D8A4BD081134AD2900E92D51 /* MergeTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD051134AD2900E92D51 /* MergeTemplate.png */; }; D8A4BD091134AD2900E92D51 /* RebaseTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = D8A4BD061134AD2900E92D51 /* RebaseTemplate.png */; }; + D8B4DC571220D1E4004166D6 /* PBHistorySearchController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B4DC561220D1E4004166D6 /* PBHistorySearchController.m */; }; D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E105461157C18200FC28A4 /* PBQLTextView.m */; }; D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; }; D8E3B34D10DCA958001096A3 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */; }; D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */; }; D8F01C4B12182F19007F729F /* GitX.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D8F01C4A12182F19007F729F /* GitX.sdef */; }; D8F01D531218A164007F729F /* NSApplication+GitXScripting.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */; }; + D8F4AB7912298CE200D6D53C /* rewindImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = D8F4AB7812298CE200D6D53C /* rewindImage.pdf */; }; D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FBCF18115FA20C0098676A /* PBGitSHA.m */; }; D8FDD9F711432A12005647F6 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8FDD9F511432A12005647F6 /* PBCloneRepositoryPanel.xib */; }; D8FDDA6A114335E8005647F6 /* PBGitSVBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FDDA5D114335E8005647F6 /* PBGitSVBranchItem.m */; }; @@ -307,6 +311,9 @@ D858108111274D28007F254B /* RemoteBranch.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RemoteBranch.png; path = Images/RemoteBranch.png; sourceTree = ""; }; D858108211274D28007F254B /* Tag.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Tag.png; path = Images/Tag.png; sourceTree = ""; }; D85B93F610E51279007F3C28 /* PBGitRefish.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitRefish.h; sourceTree = ""; }; + D87127001229A21C00012334 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + D87129FE122B14EC00012334 /* GitXTextFieldCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXTextFieldCell.h; sourceTree = ""; }; + D87129FF122B14EC00012334 /* GitXTextFieldCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXTextFieldCell.m; sourceTree = ""; }; D89E9AB21218A9DA0097A90B /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; }; D89E9B4F1218C2750097A90B /* GitXScriptingConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXScriptingConstants.h; sourceTree = ""; }; D8A4BB6D11337D5C00E92D51 /* PBGitGradientBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitGradientBarView.h; sourceTree = ""; }; @@ -314,6 +321,8 @@ D8A4BD041134AD2900E92D51 /* CherryPickTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = CherryPickTemplate.png; path = Images/CherryPickTemplate.png; sourceTree = ""; }; D8A4BD051134AD2900E92D51 /* MergeTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = MergeTemplate.png; path = Images/MergeTemplate.png; sourceTree = ""; }; D8A4BD061134AD2900E92D51 /* RebaseTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RebaseTemplate.png; path = Images/RebaseTemplate.png; sourceTree = ""; }; + D8B4DC551220D1E4004166D6 /* PBHistorySearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBHistorySearchController.h; sourceTree = ""; }; + D8B4DC561220D1E4004166D6 /* PBHistorySearchController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBHistorySearchController.m; sourceTree = ""; }; D8C1B77210E875CF009B7F8B /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBRemoteProgressSheet.xib; sourceTree = ""; }; D8E105451157C18200FC28A4 /* PBQLTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBQLTextView.h; sourceTree = ""; }; D8E105461157C18200FC28A4 /* PBQLTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBQLTextView.m; sourceTree = ""; }; @@ -327,6 +336,7 @@ D8F01D511218A164007F729F /* NSApplication+GitXScripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSApplication+GitXScripting.h"; sourceTree = ""; }; D8F01D521218A164007F729F /* NSApplication+GitXScripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSApplication+GitXScripting.m"; sourceTree = ""; }; D8F01D841218A406007F729F /* GitX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitX.h; sourceTree = ""; }; + D8F4AB7812298CE200D6D53C /* rewindImage.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rewindImage.pdf; path = Images/rewindImage.pdf; sourceTree = ""; }; D8FBCF17115FA20C0098676A /* PBGitSHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSHA.h; sourceTree = ""; }; D8FBCF18115FA20C0098676A /* PBGitSHA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSHA.m; sourceTree = ""; }; D8FDD9F611432A12005647F6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCloneRepositoryPanel.xib; sourceTree = ""; }; @@ -477,6 +487,7 @@ F5E4DBFB0EAB58D90013FAFC /* SystemConfiguration.framework in Frameworks */, F5C580E50EDA250900995434 /* libgit2.a in Frameworks */, D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */, + D87127011229A21C00012334 /* QuartzCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -534,6 +545,7 @@ 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 29B97324FDCFA39411CA2CEA /* AppKit.framework */, D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */, + D87127001229A21C00012334 /* QuartzCore.framework */, ); name = "Other Frameworks"; sourceTree = ""; @@ -580,6 +592,7 @@ D858108011274D28007F254B /* Branch.png */, D858108111274D28007F254B /* RemoteBranch.png */, D858108211274D28007F254B /* Tag.png */, + D8F4AB7812298CE200D6D53C /* rewindImage.pdf */, D85810541127476E007F254B /* StageView.png */, D8FDDBF31143F318005647F6 /* AddRemote.png */, D828A5EF1128AE7200F09D11 /* FetchTemplate.png */, @@ -825,6 +838,8 @@ D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */, D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */, D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */, + D87129FE122B14EC00012334 /* GitXTextFieldCell.h */, + D87129FF122B14EC00012334 /* GitXTextFieldCell.m */, ); name = Aux; sourceTree = ""; @@ -952,6 +967,8 @@ F52BCE060E84211300AA3741 /* PBGitHistoryController.m */, F574A2830EAE2EAC003F2CB1 /* PBRefController.h */, F574A2840EAE2EAC003F2CB1 /* PBRefController.m */, + D8B4DC551220D1E4004166D6 /* PBHistorySearchController.h */, + D8B4DC561220D1E4004166D6 /* PBHistorySearchController.m */, ); name = History; sourceTree = ""; @@ -1154,6 +1171,7 @@ D828AEEC112F411100F09D11 /* CloneRepositoryTemplate.png in Resources */, D8022FE811E124A0003C21F6 /* PBGitXMessageSheet.xib in Resources */, D8F01C4B12182F19007F729F /* GitX.sdef in Resources */, + D8F4AB7912298CE200D6D53C /* rewindImage.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1301,6 +1319,8 @@ D8022FED11E124C8003C21F6 /* PBGitXMessageSheet.m in Sources */, D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */, D8F01D531218A164007F729F /* NSApplication+GitXScripting.m in Sources */, + D8B4DC571220D1E4004166D6 /* PBHistorySearchController.m in Sources */, + D8712A00122B14EC00012334 /* GitXTextFieldCell.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GitXTextFieldCell.h b/GitXTextFieldCell.h new file mode 100644 index 0000000..4bd3ec7 --- /dev/null +++ b/GitXTextFieldCell.h @@ -0,0 +1,16 @@ +// +// GitXTextFieldCell.h +// GitX +// +// Created by Nathan Kinsinger on 8/27/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@interface GitXTextFieldCell : NSTextFieldCell { + +} + +@end diff --git a/GitXTextFieldCell.m b/GitXTextFieldCell.m new file mode 100644 index 0000000..bc95397 --- /dev/null +++ b/GitXTextFieldCell.m @@ -0,0 +1,20 @@ +// +// GitXTextFieldCell.m +// GitX +// +// Created by Nathan Kinsinger on 8/27/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "GitXTextFieldCell.h" + + +@implementation GitXTextFieldCell + +- (NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + // disables the cell's selection highlight + return nil; +} + +@end diff --git a/Images/rewindImage.pdf b/Images/rewindImage.pdf new file mode 100644 index 0000000..40066ca Binary files /dev/null and b/Images/rewindImage.pdf differ diff --git a/PBCommitList.h b/PBCommitList.h index d83dd80..3829a41 100644 --- a/PBCommitList.h +++ b/PBCommitList.h @@ -16,6 +16,8 @@ IBOutlet WebView* webView; IBOutlet PBWebHistoryController *webController; IBOutlet PBGitHistoryController *controller; + IBOutlet PBHistorySearchController *searchController; + BOOL useAdjustScroll; NSPoint mouseDownPoint; } diff --git a/PBCommitList.m b/PBCommitList.m index d5ea35d..dbf33b4 100644 --- a/PBCommitList.m +++ b/PBCommitList.m @@ -9,6 +9,7 @@ #import "PBCommitList.h" #import "PBGitRevisionCell.h" #import "PBWebHistoryController.h" +#import "PBHistorySearchController.h" @implementation PBCommitList @@ -122,4 +123,66 @@ return newImage; } + + +#pragma mark Row highlighting + +- (NSColor *)searchResultHighlightColorForRow:(NSInteger)rowIndex +{ + // if the row is selected use default colors + if ([self isRowSelected:rowIndex]) { + if ([[self window] isKeyWindow]) { + if ([[self window] firstResponder] == self) { + return [NSColor alternateSelectedControlColor]; + } + return [NSColor selectedControlColor]; + } + return [NSColor secondarySelectedControlColor]; + } + + // light blue color highlighting search results + return [NSColor colorWithCalibratedRed:0.751f green:0.831f blue:0.943f alpha:0.800f]; +} + +- (NSColor *)searchResultHighlightStrokeColorForRow:(NSInteger)rowIndex +{ + if ([self isRowSelected:rowIndex]) + return [NSColor colorWithCalibratedWhite:0.0f alpha:0.30f]; + + return [NSColor colorWithCalibratedWhite:0.0f alpha:0.05f]; +} + +- (void)drawRow:(NSInteger)rowIndex clipRect:(NSRect)tableViewClipRect +{ + NSRect rowRect = [self rectOfRow:rowIndex]; + BOOL isRowVisible = NSIntersectsRect(rowRect, tableViewClipRect); + + // draw special highlighting if the row is part of search results + if (isRowVisible && [searchController isRowInSearchResults:rowIndex]) { + NSRect highlightRect = NSInsetRect(rowRect, 1.0f, 1.0f); + float radius = highlightRect.size.height / 2.0f; + + NSBezierPath *highlightPath = [NSBezierPath bezierPathWithRoundedRect:highlightRect xRadius:radius yRadius:radius]; + + [[self searchResultHighlightColorForRow:rowIndex] set]; + [highlightPath fill]; + + [[self searchResultHighlightStrokeColorForRow:rowIndex] set]; + [highlightPath stroke]; + } + + // draws the content inside the row + [super drawRow:rowIndex clipRect:tableViewClipRect]; +} + +- (void)highlightSelectionInClipRect:(NSRect)tableViewClipRect +{ + // disable highlighting if the selected row is part of search results + // instead do the highlighting in drawRow:clipRect: above + if ([searchController isRowInSearchResults:[self selectedRow]]) + return; + + [super highlightSelectionInClipRect:tableViewClipRect]; +} + @end diff --git a/PBGitHistoryController.h b/PBGitHistoryController.h index f848c4c..8905277 100644 --- a/PBGitHistoryController.h +++ b/PBGitHistoryController.h @@ -20,6 +20,8 @@ @class PBCommitList; @class PBGitSHA; +@class PBHistorySearchController; + @interface PBGitHistoryController : PBViewController { IBOutlet PBRefController *refController; IBOutlet NSSearchField *searchField; @@ -31,6 +33,7 @@ IBOutlet PBCollapsibleSplitView *historySplitView; IBOutlet PBWebHistoryController *webHistoryController; QLPreviewPanel* previewPanel; + IBOutlet PBHistorySearchController *searchController; IBOutlet PBGitGradientBarView *upperToolbarView; IBOutlet NSButton *mergeButton; @@ -56,6 +59,8 @@ @property (retain) PBGitTree* gitTree; @property (readonly) NSArrayController *commitController; @property (readonly) PBRefController *refController; +@property (readonly) PBHistorySearchController *searchController; +@property (readonly) PBCommitList *commitList; - (IBAction) setDetailedView:(id)sender; - (IBAction) setTreeView:(id)sender; @@ -82,6 +87,10 @@ - (IBAction) cherryPick:(id)sender; - (IBAction) rebase:(id)sender; +// Find/Search methods +- (IBAction)selectNext:(id)sender; +- (IBAction)selectPrevious:(id)sender; + - (void) copyCommitInfo; - (void) copyCommitSHA; diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m index 3f08f65..1de0764 100644 --- a/PBGitHistoryController.m +++ b/PBGitHistoryController.m @@ -20,7 +20,9 @@ #import "PBDiffWindowController.h" #import "PBGitDefaults.h" #import "PBGitRevList.h" +#import "PBHistorySearchController.h" #define QLPreviewPanel NSClassFromString(@"QLPreviewPanel") +#import "PBQLTextView.h" #define kHistorySelectedDetailIndexKey @"PBHistorySelectedDetailIndex" @@ -38,6 +40,8 @@ @implementation PBGitHistoryController @synthesize selectedCommitDetailsIndex, webCommit, gitTree, commitController, refController; +@synthesize searchController; +@synthesize commitList; - (void)awakeFromNib { @@ -298,6 +302,30 @@ [super keyDown: event]; } +// NSSearchField (actually textfields in general) prevent the normal Find operations from working. Setup custom actions for the +// next and previous menuitems (in MainMenu.nib) so they will work when the search field is active. When searching for text in +// a file make sure to call the Find panel's action method instead. +- (IBAction)selectNext:(id)sender +{ + NSResponder *firstResponder = [[[self view] window] firstResponder]; + if ([firstResponder isKindOfClass:[PBQLTextView class]]) { + [(PBQLTextView *)firstResponder performFindPanelAction:sender]; + return; + } + + [searchController selectNextResult]; +} +- (IBAction)selectPrevious:(id)sender +{ + NSResponder *firstResponder = [[[self view] window] firstResponder]; + if ([firstResponder isKindOfClass:[PBQLTextView class]]) { + [(PBQLTextView *)firstResponder performFindPanelAction:sender]; + return; + } + + [searchController selectPreviousResult]; +} + - (void) copyCommitInfo { PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0]; @@ -436,8 +464,7 @@ NSArray *selectedCommits = [self selectedObjectsForSHA:commitSHA]; [commitController setSelectedObjects:selectedCommits]; - if (repository.currentBranchFilter != kGitXSelectedBranchFilter) - [self scrollSelectionToTopOfViewFrom:oldIndex]; + [self scrollSelectionToTopOfViewFrom:oldIndex]; forceSelectionUpdate = NO; } diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index 37d329e..4e83049 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -21,7 +21,7 @@ YES - + YES @@ -702,31 +702,6 @@ 266 YES - - - 268 - {{49, 2}, {57, 17}} - - 1 - YES - - 67239424 - 134348800 - Remote - - LucidaGrande-Bold - 11 - 16 - - - -1232846593 - 173 - - - 400 - 75 - - 265 @@ -737,7 +712,6 @@ 343014976 268567552 - Subject YES 1 @@ -788,6 +762,88 @@ 255 + + + 265 + {{611, 3}, {43, 18}} + + YES + + 67239424 + 131072 + + LucidaGrande + 9 + 16 + + + + YES + + 18 + + Previous search result + 0 + + + 18 + + Next search result + 1 + 0 + + + 1 + 2 + 3 + + + + + 265 + {{527, 5}, {80, 14}} + + YES + + 68288064 + 71435264 + # of matches + + + + 6 + System + controlColor + + + + + + + + 268 + {{49, 2}, {57, 17}} + + 1 + YES + + 67239424 + 134348800 + Remote + + LucidaGrande-Bold + 11 + 16 + + + -1232846593 + 173 + + + 400 + 75 + + 268 @@ -912,12 +968,7 @@ {852, 232} Details - - 6 - System - controlColor - - + @@ -1328,6 +1379,9 @@ GitXRelativeDateFormatter + + PBHistorySearchController + @@ -1709,95 +1763,6 @@ 291 - - - predicate: filterPredicate - - - - - - predicate: filterPredicate - predicate - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - Subject - subject contains[c] $value - - - 2 - - - 301 - - - - predicate2: filterPredicate - - - - - - predicate2: filterPredicate - predicate2 - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - Author - author contains[c] $value - - - - 2 - - - 304 - - - - predicate3: filterPredicate - - - - - - predicate3: filterPredicate - predicate3 - filterPredicate - - YES - - YES - NSDisplayName - NSPredicateFormat - - - YES - SHA - realSha contains[c] $value - - - - 2 - - - 308 - searchField @@ -2050,6 +2015,86 @@ 422 + + + commitController + + + + 424 + + + + historyController + + + + 425 + + + + searchField + + + + 426 + + + + stepper + + + + 429 + + + + numberOfMatchesField + + + + 432 + + + + delegate + + + + 433 + + + + updateSearch: + + + + 434 + + + + stepperPressed: + + + + 435 + + + + searchController + + + + 436 + + + + searchController + + + + 437 + @@ -2387,10 +2432,12 @@ YES - + + + Commits Scope Bar @@ -2614,6 +2661,39 @@ + + 423 + + + + + 427 + + + YES + + + + + + 428 + + + + + 430 + + + YES + + + + + + 431 + + + @@ -2650,6 +2730,7 @@ 28.IBPluginDependency 28.IBShouldRemoveOnLegacySave 287.IBPluginDependency + 288.CustomClassName 288.IBPluginDependency 29.IBPluginDependency 29.IBShouldRemoveOnLegacySave @@ -2680,6 +2761,7 @@ 337.IBAttributePlaceholdersKey 337.IBPluginDependency 338.IBPluginDependency + 34.CustomClassName 34.IBPluginDependency 340.IBAttributePlaceholdersKey 340.IBPluginDependency @@ -2695,6 +2777,7 @@ 356.IBPluginDependency 359.IBAttributePlaceholdersKey 359.IBPluginDependency + 36.CustomClassName 36.IBPluginDependency 36.ImportedFromIB2 360.IBPluginDependency @@ -2707,11 +2790,18 @@ 4.IBAttributePlaceholdersKey 4.IBPluginDependency 409.IBPluginDependency + 410.CustomClassName 410.IBPluginDependency 414.IBDateFormatterBehaviorMetadataKey 414.IBPluginDependency 418.IBPluginDependency + 419.CustomClassName 419.IBPluginDependency + 427.IBPluginDependency + 428.IBPluginDependency + 428.IBSegmentedControlInspectorSelectedSegmentMetadataKey + 430.IBPluginDependency + 431.IBPluginDependency 46.IBEditorWindowLastContentRect 46.IBPluginDependency 48.IBPluginDependency @@ -2762,6 +2852,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + GitXTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2813,6 +2904,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + GitXTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin ToolTip @@ -2856,6 +2948,7 @@ com.apple.InterfaceBuilder.CocoaPlugin + GitXTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -2881,10 +2974,17 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + GitXTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + GitXTextFieldCell + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin {{9, 486}, {852, 432}} com.apple.InterfaceBuilder.CocoaPlugin @@ -2922,7 +3022,7 @@ - 422 + 437 @@ -2935,6 +3035,21 @@ GitXRelativeDateFormatter.h + + GitXTextFieldCell + NSTextFieldCell + + IBProjectSource + GitXTextFieldCell.h + + + + NSApplication + + IBProjectSource + NSApplication+GitXScripting.h + + NSOutlineView @@ -2958,12 +3073,14 @@ YES controller + searchController webController webView YES PBGitHistoryController + PBHistorySearchController PBWebHistoryController WebView @@ -2973,6 +3090,7 @@ YES controller + searchController webController webView @@ -2982,6 +3100,10 @@ controller PBGitHistoryController + + searchController + PBHistorySearchController + webController PBWebHistoryController @@ -3028,6 +3150,8 @@ openSelectedFile: rebase: refresh: + selectNext: + selectPrevious: setBranchFilter: setDetailedView: setTreeView: @@ -3053,6 +3177,8 @@ id id id + id + id @@ -3067,6 +3193,8 @@ openSelectedFile: rebase: refresh: + selectNext: + selectPrevious: setBranchFilter: setDetailedView: setTreeView: @@ -3109,6 +3237,14 @@ refresh: id + + selectNext: + id + + + selectPrevious: + id + setBranchFilter: id @@ -3154,6 +3290,7 @@ rebaseButton refController scopeBarView + searchController searchField selectedBranchFilterItem treeController @@ -3174,6 +3311,7 @@ NSButton PBRefController PBGitGradientBarView + PBHistorySearchController NSSearchField NSButton NSTreeController @@ -3197,6 +3335,7 @@ rebaseButton refController scopeBarView + searchController searchField selectedBranchFilterItem treeController @@ -3250,6 +3389,10 @@ scopeBarView PBGitGradientBarView + + searchController + PBHistorySearchController + searchField NSSearchField @@ -3329,6 +3472,99 @@ PBGitRevisionCell.h + + PBHistorySearchController + NSObject + + YES + + YES + stepperPressed: + updateSearch: + + + YES + id + id + + + + YES + + YES + stepperPressed: + updateSearch: + + + YES + + stepperPressed: + id + + + updateSearch: + id + + + + + YES + + YES + commitController + historyController + numberOfMatchesField + searchField + stepper + + + YES + NSArrayController + PBGitHistoryController + NSTextField + NSSearchField + NSSegmentedControl + + + + YES + + YES + commitController + historyController + numberOfMatchesField + searchField + stepper + + + YES + + commitController + NSArrayController + + + historyController + PBGitHistoryController + + + numberOfMatchesField + NSTextField + + + searchField + NSSearchField + + + stepper + NSSegmentedControl + + + + + IBProjectSource + PBHistorySearchController.h + + PBNiceSplitView NSSplitView @@ -4102,6 +4338,34 @@ Foundation.framework/Headers/NSURLDownload.h + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CAAnimation.h + + + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CALayer.h + + + + NSObject + + IBFrameworkSource + QuartzCore.framework/Headers/CIImageProvider.h + + + + NSObject + + IBFrameworkSource + ScriptingBridge.framework/Headers/SBApplication.h + + NSObject diff --git a/PBGitRevisionCell.m b/PBGitRevisionCell.m index 0e77c22..65889a6 100644 --- a/PBGitRevisionCell.m +++ b/PBGitRevisionCell.m @@ -9,6 +9,7 @@ #import "PBGitRevisionCell.h" #import "PBGitRef.h" #import "RoundedRectangle.h" +#import "GitXTextFieldCell.h" @implementation PBGitRevisionCell @@ -16,7 +17,7 @@ - (id) initWithCoder: (id) coder { self = [super initWithCoder:coder]; - textCell = [[NSTextFieldCell alloc] initWithCoder:coder]; + textCell = [[GitXTextFieldCell alloc] initWithCoder:coder]; return self; } diff --git a/PBHistorySearchController.h b/PBHistorySearchController.h new file mode 100644 index 0000000..75bd250 --- /dev/null +++ b/PBHistorySearchController.h @@ -0,0 +1,46 @@ +// +// PBHistorySearchController.h +// GitX +// +// Created by Nathan Kinsinger on 8/21/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import + + +@class PBGitHistoryController; + + +@interface PBHistorySearchController : NSObject { + PBGitHistoryController *historyController; + NSArrayController *commitController; + + NSIndexSet *results; + + NSSearchField *searchField; + NSSegmentedControl *stepper; + NSTextField *numberOfMatchesField; + + NSPanel *rewindPanel; +} + +@property (assign) IBOutlet PBGitHistoryController *historyController; +@property (assign) IBOutlet NSArrayController *commitController; + +@property (assign) IBOutlet NSSearchField *searchField; +@property (assign) IBOutlet NSSegmentedControl *stepper; +@property (assign) IBOutlet NSTextField *numberOfMatchesField; + + +- (BOOL)isRowInSearchResults:(NSInteger)rowIndex; +- (BOOL)hasSearchResults; + +- (void)selectNextResult; +- (void)selectPreviousResult; +- (IBAction)stepperPressed:(id)sender; + +- (void)clearSearch; +- (IBAction)updateSearch:(id)sender; + +@end diff --git a/PBHistorySearchController.m b/PBHistorySearchController.m new file mode 100644 index 0000000..99ea7a8 --- /dev/null +++ b/PBHistorySearchController.m @@ -0,0 +1,313 @@ +// +// PBHistorySearchController.m +// GitX +// +// Created by Nathan Kinsinger on 8/21/10. +// Copyright 2010 Nathan Kinsinger. All rights reserved. +// + +#import "PBHistorySearchController.h" +#import "PBGitHistoryController.h" +#import "PBGitRepository.h" +#import + + +@interface PBHistorySearchController () + +- (void)selectNextResultInDirection:(NSInteger)direction; + +- (void)updateUI; + +- (void)startBasicSearch; + +- (void)showSearchRewindPanelReverse:(BOOL)isReversed; + +@end + + +#define kGitXSearchDirectionNext 1 +#define kGitXSearchDirectionPrevious -1 + +#define kGitXBasicSearchLabel @"Subject, Author, SHA" + +#define kGitXSearchArrangedObjectsContext @"GitXSearchArrangedObjectsContext" + + +@implementation PBHistorySearchController + +@synthesize historyController; +@synthesize commitController; + +@synthesize searchField; +@synthesize stepper; +@synthesize numberOfMatchesField; + + + +#pragma mark - +#pragma mark Public methods + +- (BOOL)isRowInSearchResults:(NSInteger)rowIndex +{ + return [results containsIndex:rowIndex]; +} + +- (BOOL)hasSearchResults +{ + return ([results count] > 0); +} + +- (void)selectNextResult +{ + [self selectNextResultInDirection:kGitXSearchDirectionNext]; +} + +- (void)selectPreviousResult +{ + [self selectNextResultInDirection:kGitXSearchDirectionPrevious]; +} + +- (IBAction)stepperPressed:(id)sender +{ + NSInteger selectedSegment = [sender selectedSegment]; + + if (selectedSegment == 0) + [self selectPreviousResult]; + else + [self selectNextResult]; +} + +- (void)clearSearch +{ + [searchField setStringValue:@""]; + if (results) { + results = nil; + [historyController.commitList reloadData]; + } + [self updateUI]; +} + +- (IBAction)updateSearch:(id)sender +{ + [self startBasicSearch]; +} + +- (void)awakeFromNib +{ + [[searchField cell] setPlaceholderString:@"Subject, Author, SHA"]; + + [self updateUI]; + + [commitController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:kGitXSearchArrangedObjectsContext]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([(NSString *)context isEqualToString:kGitXSearchArrangedObjectsContext]) { + // the objects in the commitlist changed so the result indexes are no longer valid + [self clearSearch]; + return; + } + + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; +} + + + +#pragma mark - +#pragma mark Private methods + +- (void)selectIndex:(NSUInteger)index +{ + if ([[commitController arrangedObjects] count] > index) { + PBGitCommit *commit = [[commitController arrangedObjects] objectAtIndex:index]; + [historyController selectCommit:[commit sha]]; + } +} + +- (void)selectNextResultInDirection:(NSInteger)direction +{ + if (![results count]) + return; + + NSUInteger selectedRow = [historyController.commitList selectedRow]; + if (selectedRow == NSNotFound) { + [self selectIndex:[results firstIndex]]; + return; + } + + NSUInteger currentResult = NSNotFound; + if (direction == kGitXSearchDirectionNext) + currentResult = [results indexGreaterThanIndex:selectedRow]; + else + currentResult = [results indexLessThanIndex:selectedRow]; + + if (currentResult == NSNotFound) { + if (direction == kGitXSearchDirectionNext) + currentResult = [results firstIndex]; + else + currentResult = [results lastIndex]; + + [self showSearchRewindPanelReverse:(direction != kGitXSearchDirectionNext)]; + } + + [self selectIndex:currentResult]; +} + +- (NSString *)numberOfMatchesString +{ + NSUInteger numberOfMatches = [results count]; + + if (numberOfMatches == 0) + return @"Not found"; + + if (numberOfMatches == 1) + return @"1 match"; + + return [NSString stringWithFormat:@"%d matches", numberOfMatches]; +} + +- (void)updateUI +{ + if ([[searchField stringValue] isEqualToString:@""]) { + [numberOfMatchesField setHidden:YES]; + [stepper setHidden:YES]; + } + else { + [numberOfMatchesField setStringValue:[self numberOfMatchesString]]; + [numberOfMatchesField setHidden:NO]; + [stepper setHidden:NO]; + [historyController.commitList reloadData]; + } +} + +// changes the selection to the next match after the current selected row unless the current row is already a match +- (void)updateSelectedResult +{ + NSString *searchString = [searchField stringValue]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + if (![self isRowInSearchResults:[historyController.commitList selectedRow]]) + [self selectNextResult]; + + [self updateUI]; +} + + + +#pragma mark Basic Search + +- (void)startBasicSearch +{ + NSString *searchString = [searchField stringValue]; + if ([searchString isEqualToString:@""]) { + [self clearSearch]; + return; + } + + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:@"subject CONTAINS[cd] %@ OR author CONTAINS[cd] %@ OR realSha BEGINSWITH[c] %@", searchString, searchString, searchString]; + + NSUInteger index = 0; + for (PBGitCommit *commit in [commitController arrangedObjects]) { + if ([searchPredicate evaluateWithObject:commit]) + [indexes addIndex:index]; + index++; + } + + results = indexes; + + [self updateSelectedResult]; +} + + + +#pragma mark - +#pragma mark Rewind Panel + +#define kRewindPanelSize 125.0f + +- (void)closeRewindPanel +{ + [[[historyController view] window] removeChildWindow:rewindPanel]; + [rewindPanel close]; + rewindPanel = nil; +} + +- (NSPanel *)rewindPanelReverse:(BOOL)isReversed +{ + NSRect windowFrame = [[[historyController view] window] frame]; + NSRect historyFrame = [[historyController view] convertRectToBase:[[historyController view] frame]]; + NSRect panelRect = NSMakeRect(0.0f, 0.0f, kRewindPanelSize, kRewindPanelSize); + panelRect.origin.x = windowFrame.origin.x + historyFrame.origin.x + ((historyFrame.size.width - kRewindPanelSize) / 2.0f); + panelRect.origin.y = windowFrame.origin.y + historyFrame.origin.y + ((historyFrame.size.height - kRewindPanelSize) / 2.0f); + + NSPanel *panel = [[NSPanel alloc] initWithContentRect:panelRect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + [panel setIgnoresMouseEvents:YES]; + [panel setOneShot:YES]; + [panel setOpaque:NO]; + [panel setBackgroundColor:[NSColor clearColor]]; + [panel setHasShadow:NO]; + [panel useOptimizedDrawing:YES]; + [panel setAlphaValue:0.0f]; + + NSBox *box = [[NSBox alloc] initWithFrame:[[panel contentView] frame]]; + [box setBoxType:NSBoxCustom]; + [box setBorderType:NSLineBorder]; + [box setFillColor:[NSColor colorWithCalibratedWhite:0.0f alpha:0.5f]]; + [box setBorderColor:[NSColor colorWithCalibratedWhite:0.5f alpha:0.5f]]; + [box setCornerRadius:12.0f]; + [[panel contentView] addSubview:box]; + + NSImage *rewindImage = [[NSImage imageNamed:@"rewindImage"] copy]; + [rewindImage setFlipped:isReversed]; + NSSize imageSize = [rewindImage size]; + NSRect imageViewFrame = NSMakeRect(21.0f, 5.0f, imageSize.width, imageSize.height); + NSImageView *rewindImageView = [[NSImageView alloc] initWithFrame:imageViewFrame]; + [rewindImageView setImage:rewindImage]; + [[box contentView] addSubview:rewindImageView]; + + return panel; +} + +- (CAKeyframeAnimation *)rewindPanelFadeOutAnimation +{ + CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; + animation.duration = 1.0f; + animation.values = [NSArray arrayWithObjects: + [NSNumber numberWithFloat:1.0f], + [NSNumber numberWithFloat:1.0f], + [NSNumber numberWithFloat:0.0f], + [NSNumber numberWithFloat:0.0f], nil]; + animation.keyTimes = [NSArray arrayWithObjects: + [NSNumber numberWithFloat:0.1f], + [NSNumber numberWithFloat:0.3f], + [NSNumber numberWithFloat:0.7f], + [NSNumber numberWithFloat:animation.duration], nil]; + + return animation; +} + +- (void)showSearchRewindPanelReverse:(BOOL)isReversed +{ + if (rewindPanel) + [self closeRewindPanel]; + + rewindPanel = [self rewindPanelReverse:isReversed]; + + [[[historyController view] window] addChildWindow:rewindPanel ordered:NSWindowAbove]; + + CAKeyframeAnimation *alphaAnimation = [self rewindPanelFadeOutAnimation]; + [rewindPanel setAnimations:[NSDictionary dictionaryWithObject:alphaAnimation forKey:@"alphaValue"]]; + [[rewindPanel animator] setAlphaValue:0.0f]; + + [self performSelector:@selector(closeRewindPanel) withObject:nil afterDelay:0.7f]; +} + +@end