5 // Created by Pieter de Bie on 19-09-08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
9 #import "PBGitHistoryController.h"
10 #import "CWQuickLook.h"
11 #import "PBGitGrapher.h"
12 #import "PBGitRevisionCell.h"
13 #import "PBCommitList.h"
14 #define QLPreviewPanel NSClassFromString(@"QLPreviewPanel")
17 @implementation PBGitHistoryController
18 @synthesize selectedTab, webCommit, rawCommit, gitTree, commitController;
22 self.selectedTab = [[NSUserDefaults standardUserDefaults] integerForKey:@"Repository Window Selected Tab Index"];;
23 [commitController addObserver:self forKeyPath:@"selection" options:(NSKeyValueObservingOptionNew,NSKeyValueObservingOptionOld) context:@"commitChange"];
24 [treeController addObserver:self forKeyPath:@"selection" options:0 context:@"treeChange"];
25 [repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"branchChange"];
26 NSSize cellSpacing = [commitList intercellSpacing];
27 cellSpacing.height = 0;
28 [commitList setIntercellSpacing:cellSpacing];
29 [fileBrowser setTarget:self];
30 [fileBrowser setDoubleAction:@selector(openSelectedFile:)];
32 if (!repository.currentBranch) {
33 [repository reloadRefs];
34 [repository readCurrentBranch];
37 [repository lazyReload];
39 // Set a sort descriptor for the subject column in the history list, as
40 // It can't be sorted by default (because it's bound to a PBGitCommit)
41 [[commitList tableColumnWithIdentifier:@"subject"] setSortDescriptorPrototype:[[NSSortDescriptor alloc] initWithKey:@"subject" ascending:YES]];
42 // Add a menu that allows a user to select which columns to view
43 [[commitList headerView] setMenu:[self tableColumnMenu]];
44 [historySplitView setTopMin:33.0 andBottomMin:100.0];
45 [historySplitView uncollapse];
51 NSArray* selection = [commitController selectedObjects];
53 // Remove any references in the QLPanel
54 //[[QLPreviewPanel sharedPreviewPanel] setURLs:[NSArray array] currentIndex:0 preservingDisplayState:YES];
55 // We have to do this manually, as NSTreeController leaks memory?
56 //[treeController setSelectionIndexPaths:[NSArray array]];
58 if ([selection count] > 0)
59 realCommit = [selection objectAtIndex:0];
67 switch (self.selectedTab) {
68 case 0: self.webCommit = realCommit; break;
69 case 1: self.gitTree = realCommit.tree; break;
74 - (void) setSelectedTab: (int) number
77 [[NSUserDefaults standardUserDefaults] setInteger:selectedTab forKey:@"Repository Window Selected Tab Index"];
81 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
83 if ([(NSString *)context isEqualToString: @"commitChange"]) {
87 else if ([(NSString *)context isEqualToString: @"treeChange"]) {
88 [self updateQuicklookForce: NO];
90 else if([(NSString *)context isEqualToString:@"branchChange"]) {
92 commitController.sortDescriptors = [NSArray array];
95 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
99 - (IBAction) openSelectedFile: sender
101 NSArray* selectedFiles = [treeController selectedObjects];
102 if ([selectedFiles count] == 0)
104 PBGitTree* tree = [selectedFiles objectAtIndex:0];
105 NSString* name = [tree tmpFileNameForContents];
106 [[NSWorkspace sharedWorkspace] openTempFile:name];
109 - (IBAction) setDetailedView: sender {
110 self.selectedTab = 0;
112 - (IBAction) setRawView: sender {
113 self.selectedTab = 1;
115 - (IBAction) setTreeView: sender {
116 self.selectedTab = 2;
119 - (void)keyDown:(NSEvent*)event
121 if ([[event charactersIgnoringModifiers] isEqualToString: @"f"] && [event modifierFlags] & NSAlternateKeyMask && [event modifierFlags] & NSCommandKeyMask)
122 [superController.window makeFirstResponder: searchField];
124 [super keyDown: event];
127 - (void) copyCommitInfo
129 PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
132 NSString *info = [NSString stringWithFormat:@"%@ (%@)", [[commit realSha] substringToIndex:10], [commit subject]];
134 NSPasteboard *a =[NSPasteboard generalPasteboard];
135 [a declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
136 [a setString:info forType: NSStringPboardType];
140 - (IBAction) toggleQuickView: sender
142 id panel = [QLPreviewPanel sharedPreviewPanel];
143 if ([panel isOpen]) {
146 [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:1];
147 [self updateQuicklookForce: YES];
151 - (void) updateQuicklookForce: (BOOL) force
153 if (!force && ![[QLPreviewPanel sharedPreviewPanel] isOpen])
156 NSArray* selectedFiles = [treeController selectedObjects];
158 if ([selectedFiles count] == 0)
161 NSMutableArray* fileNames = [NSMutableArray array];
162 for (PBGitTree* tree in selectedFiles) {
163 NSString* s = [tree tmpFileNameForContents];
165 [fileNames addObject:[NSURL fileURLWithPath: s]];
168 [[QLPreviewPanel sharedPreviewPanel] setURLs:fileNames currentIndex:0 preservingDisplayState:YES];
172 - (IBAction) refresh: sender
174 [repository reloadRefs];
175 [repository.revisionList reload];
183 - (NSResponder *)firstResponder;
188 - (void) selectCommit: (NSString*) commit
190 NSPredicate* selection = [NSPredicate predicateWithFormat:@"realSha == %@", commit];
191 NSArray* selectedCommits = [repository.revisionList.commits filteredArrayUsingPredicate:selection];
192 [commitController setSelectedObjects: selectedCommits];
193 int index = [[commitController selectionIndexes] firstIndex];
194 [commitList scrollRowToVisible: index];
197 - (BOOL) hasNonlinearPath
199 return [commitController filterPredicate] || [[commitController sortDescriptors] count] > 0;
205 [commitController removeObserver:self forKeyPath:@"selection"];
206 [treeController removeObserver:self forKeyPath:@"selection"];
207 [repository removeObserver:self forKeyPath:@"currentBranch"];
212 #pragma mark Table Column Methods
213 - (NSMenu *)tableColumnMenu
215 NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Table columns menu"];
216 for (NSTableColumn *column in [commitList tableColumns]) {
217 NSMenuItem *item = [[NSMenuItem alloc] init];
218 [item setTitle:[[column headerCell] stringValue]];
221 withKeyPath:@"hidden"
222 options:[NSDictionary dictionaryWithObject:@"NSNegateBoolean" forKey:NSValueTransformerNameBindingOption]];
228 #pragma mark Tree Context Menu Methods
230 - (void)showCommitsFromTree:(id)sender
232 // TODO: Enable this from webview as well!
234 NSMutableArray *filePaths = [NSMutableArray arrayWithObjects:@"HEAD", @"--", NULL];
235 [filePaths addObjectsFromArray:[sender representedObject]];
237 PBGitRevSpecifier *revSpec = [[PBGitRevSpecifier alloc] initWithParameters:filePaths];
239 repository.currentBranch = [repository addBranch:revSpec];
242 - (void)showInFinderAction:(id)sender
244 NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
246 NSWorkspace *ws = [NSWorkspace sharedWorkspace];
248 for (NSString *filePath in [sender representedObject]) {
249 path = [workingDirectory stringByAppendingPathComponent:filePath];
250 [ws selectFile: path inFileViewerRootedAtPath:path];
255 - (void)openFilesAction:(id)sender
257 NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
259 NSWorkspace *ws = [NSWorkspace sharedWorkspace];
261 for (NSString *filePath in [sender representedObject]) {
262 path = [workingDirectory stringByAppendingPathComponent:filePath];
268 - (NSMenu *)contextMenuForTreeView
270 NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"];
272 NSMenu *menu = [[NSMenu alloc] init];
273 for (NSMenuItem *item in [self menuItemsForPaths:filePaths])
278 - (NSArray *)menuItemsForPaths:(NSArray *)paths
280 BOOL multiple = [paths count] != 1;
281 NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Show history of files" : @"Show history of file"
282 action:@selector(showCommitsFromTree:)
284 NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder"
285 action:@selector(showInFinderAction:)
287 NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Open Files" : @"Open File"
288 action:@selector(openFilesAction:)
291 NSArray *menuItems = [NSArray arrayWithObjects:historyItem, finderItem, openFilesItem, nil];
292 for (NSMenuItem *item in menuItems) {
293 [item setTarget:self];
294 [item setRepresentedObject:paths];
300 - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview {
304 - (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex {
305 int index = [[splitView subviews] indexOfObject:subview];
306 // this method (and canCollapse) are called by the splitView to decide how to collapse on double-click
307 // we compare our two subviews, so that always the smaller one is collapsed.
308 if([[[splitView subviews] objectAtIndex:index] frame].size.height < [[[splitView subviews] objectAtIndex:((index+1)%2)] frame].size.height) {
314 - (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset {
315 return proposedMin + historySplitView.topViewMin;
318 - (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset {
320 return proposedMax - historySplitView.bottomViewMin;
321 return [sender frame].size.height;