Merge branch 'pu/pb/index_quick_fix' into stable
[GitX.git] / PBGitHistoryController.m
blobf8ad04a26ac6655a758c648b2d5e8e5037c353c4
1 //
2 //  PBGitHistoryView.m
3 //  GitX
4 //
5 //  Created by Pieter de Bie on 19-09-08.
6 //  Copyright 2008 __MyCompanyName__. All rights reserved.
7 //
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;
20 - (void)awakeFromNib
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];
35         }
36         else
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];
46         [super awakeFromNib];
49 - (void) updateKeys
51         NSArray* selection = [commitController selectedObjects];
52         
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]];
57         
58         if ([selection count] > 0)
59                 realCommit = [selection objectAtIndex:0];
60         else
61                 realCommit = nil;
62         
63         self.webCommit = nil;
64         self.rawCommit = nil;
65         self.gitTree = nil;
66         
67         switch (self.selectedTab) {
68                 case 0: self.webCommit = realCommit;                    break;
69                 case 1: self.rawCommit = realCommit;                    break;
70                 case 2: self.gitTree   = realCommit.tree;       break;
71         }
72 }       
75 - (void) setSelectedTab: (int) number
77         selectedTab = number;
78         [[NSUserDefaults standardUserDefaults] setInteger:selectedTab forKey:@"Repository Window Selected Tab Index"];
79         [self updateKeys];
82 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
84     if ([(NSString *)context isEqualToString: @"commitChange"]) {
85                 [self updateKeys];
86                 return;
87         }
88         else if ([(NSString *)context isEqualToString: @"treeChange"]) {
89                 [self updateQuicklookForce: NO];
90         }
91         else if([(NSString *)context isEqualToString:@"branchChange"]) {
92                 // Reset the sorting
93                 commitController.sortDescriptors = [NSArray array];
94         }
95         else {
96                 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
97         }
100 - (IBAction) openSelectedFile: sender
102         NSArray* selectedFiles = [treeController selectedObjects];
103         if ([selectedFiles count] == 0)
104                 return;
105         PBGitTree* tree = [selectedFiles objectAtIndex:0];
106         NSString* name = [tree tmpFileNameForContents];
107         [[NSWorkspace sharedWorkspace] openTempFile:name];
110 - (IBAction) setDetailedView: sender {
111         self.selectedTab = 0;
113 - (IBAction) setRawView: sender {
114         self.selectedTab = 1;
116 - (IBAction) setTreeView: sender {
117         self.selectedTab = 2;
120 - (void)keyDown:(NSEvent*)event
122         if ([[event charactersIgnoringModifiers] isEqualToString: @"f"] && [event modifierFlags] & NSAlternateKeyMask && [event modifierFlags] & NSCommandKeyMask)
123                 [superController.window makeFirstResponder: searchField];
124         else
125                 [super keyDown: event];
128 - (void) copyCommitInfo
130         PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
131         if (!commit)
132                 return;
133         NSString *info = [NSString stringWithFormat:@"%@ (%@)", [[commit realSha] substringToIndex:10], [commit subject]];
135         NSPasteboard *a =[NSPasteboard generalPasteboard];
136         [a declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
137         [a setString:info forType: NSStringPboardType];
138         
141 - (IBAction) toggleQuickView: sender
143         id panel = [QLPreviewPanel sharedPreviewPanel];
144         if ([panel isOpen]) {
145                 [panel closePanel];
146         } else {
147                 [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFrontWithEffect:1];
148                 [self updateQuicklookForce: YES];
149         }
152 - (void) updateQuicklookForce: (BOOL) force
154         if (!force && ![[QLPreviewPanel sharedPreviewPanel] isOpen])
155                 return;
156         
157         NSArray* selectedFiles = [treeController selectedObjects];
158         
159         if ([selectedFiles count] == 0)
160                 return;
161         
162         NSMutableArray* fileNames = [NSMutableArray array];
163         for (PBGitTree* tree in selectedFiles) {
164                 NSString* s = [tree tmpFileNameForContents];
165                 if (s)
166                         [fileNames addObject:[NSURL fileURLWithPath: s]];
167         }
168         
169         [[QLPreviewPanel sharedPreviewPanel] setURLs:fileNames currentIndex:0 preservingDisplayState:YES];
170         
173 - (IBAction) refresh: sender
175         [repository reloadRefs];
176         [repository.revisionList reload];
179 - (void) updateView
181         [self refresh:nil];
184 - (NSResponder *)firstResponder;
186         return commitList;
189 - (void) selectCommit: (NSString*) commit
191         NSPredicate* selection = [NSPredicate predicateWithFormat:@"realSha == %@", commit];
192         NSArray* selectedCommits = [repository.revisionList.commits filteredArrayUsingPredicate:selection];
193         [commitController setSelectedObjects: selectedCommits];
194         int index = [[commitController selectionIndexes] firstIndex];
195         [commitList scrollRowToVisible: index];
198 - (BOOL) hasNonlinearPath
200         return [commitController filterPredicate] || [[commitController sortDescriptors] count] > 0;
203 - (void) removeView
205         [webView close];
206         [commitController removeObserver:self forKeyPath:@"selection"];
207         [treeController removeObserver:self forKeyPath:@"selection"];
208         [repository removeObserver:self forKeyPath:@"currentBranch"];
210         [super removeView];
213 #pragma mark Table Column Methods
214 - (NSMenu *)tableColumnMenu
216         NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Table columns menu"];
217         for (NSTableColumn *column in [commitList tableColumns]) {
218                 NSMenuItem *item = [[NSMenuItem alloc] init];
219                 [item setTitle:[[column headerCell] stringValue]];
220                 [item bind:@"value"
221                   toObject:column
222            withKeyPath:@"hidden"
223                    options:[NSDictionary dictionaryWithObject:@"NSNegateBoolean" forKey:NSValueTransformerNameBindingOption]];
224                 [menu addItem:item];
225         }
226         return menu;
229 #pragma mark Tree Context Menu Methods
231 - (void)showCommitsFromTree:(id)sender
233         // TODO: Enable this from webview as well!
235         NSMutableArray *filePaths = [NSMutableArray arrayWithObjects:@"HEAD", @"--", NULL];
236         [filePaths addObjectsFromArray:[sender representedObject]];
238         PBGitRevSpecifier *revSpec = [[PBGitRevSpecifier alloc] initWithParameters:filePaths];
240         repository.currentBranch = [repository addBranch:revSpec];
243 - (void)showInFinderAction:(id)sender
245         NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
246         NSString *path;
247         NSWorkspace *ws = [NSWorkspace sharedWorkspace];
249         for (NSString *filePath in [sender representedObject]) {
250                 path = [workingDirectory stringByAppendingPathComponent:filePath];
251                 [ws selectFile: path inFileViewerRootedAtPath:path];
252         }
256 - (void)openFilesAction:(id)sender
258         NSString *workingDirectory = [[repository workingDirectory] stringByAppendingString:@"/"];
259         NSString *path;
260         NSWorkspace *ws = [NSWorkspace sharedWorkspace];
262         for (NSString *filePath in [sender representedObject]) {
263                 path = [workingDirectory stringByAppendingPathComponent:filePath];
264                 [ws openFile:path];
265         }
269 - (NSMenu *)contextMenuForTreeView
271         NSArray *filePaths = [[treeController selectedObjects] valueForKey:@"fullPath"];
273         NSMenu *menu = [[NSMenu alloc] init];
274         for (NSMenuItem *item in [self menuItemsForPaths:filePaths])
275                 [menu addItem:item];
276         return menu;
279 - (NSArray *)menuItemsForPaths:(NSArray *)paths
281         BOOL multiple = [paths count] != 1;
282         NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Show history of files" : @"Show history of file"
283                                                                                                                  action:@selector(showCommitsFromTree:)
284                                                                                                  keyEquivalent:@""];
285         NSMenuItem *finderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder"
286                                                                                                                 action:@selector(showInFinderAction:)
287                                                                                                  keyEquivalent:@""];
288         NSMenuItem *openFilesItem = [[NSMenuItem alloc] initWithTitle:multiple? @"Open Files" : @"Open File"
289                                                                                                                    action:@selector(openFilesAction:)
290                                                                                                         keyEquivalent:@""];
292         NSArray *menuItems = [NSArray arrayWithObjects:historyItem, finderItem, openFilesItem, nil];
293         for (NSMenuItem *item in menuItems) {
294                 [item setTarget:self];
295                 [item setRepresentedObject:paths];
296         }
298         return menuItems;
301 - (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview {
302         return TRUE;
305 - (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex {
306         int index = [[splitView subviews] indexOfObject:subview];
307         // this method (and canCollapse) are called by the splitView to decide how to collapse on double-click
308         // we compare our two subviews, so that always the smaller one is collapsed.
309         if([[[splitView subviews] objectAtIndex:index] frame].size.height < [[[splitView subviews] objectAtIndex:((index+1)%2)] frame].size.height) {
310                 return TRUE;
311         }
312         return FALSE;
315 - (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset {
316         return proposedMin + historySplitView.topViewMin;
319 - (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset {
320         if(offset == 1)
321                 return proposedMax - historySplitView.bottomViewMin;
322         return [sender frame].size.height;
325 @end