Merge branch 'pu/pb/index_quick_fix' into stable
[GitX.git] / PBRefController.m
blob31c55905c54f7d7cf30d7647e6d4439c50e0e8fd
1 //
2 //  PBLabelController.m
3 //  GitX
4 //
5 //  Created by Pieter de Bie on 21-10-08.
6 //  Copyright 2008 Pieter de Bie. All rights reserved.
7 //
9 #import "PBRefController.h"
10 #import "PBGitRevisionCell.h"
11 #import "PBRefMenuItem.h"
13 @implementation PBRefController
15 - (void)awakeFromNib
17         [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]];
18         [historyController addObserver:self forKeyPath:@"repository.branches" options:0 context:@"branchChange"];
19         [historyController addObserver:self forKeyPath:@"repository.currentBranch" options:0 context:@"currentBranchChange"];
20         [self updateBranchMenu];
21         [self selectCurrentBranch];
24 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
26     if ([(NSString *)context isEqualToString: @"branchChange"]) {
27                 [self updateBranchMenu];
28         }
29         else if ([(NSString *)context isEqualToString:@"currentBranchChange"]) {
30                 [self selectCurrentBranch];
31         }
32         else {
33                 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
34         }
38 - (void) removeRef:(PBRefMenuItem *) sender
40         NSString *ref_desc = [NSString stringWithFormat:@"%@ %@", [[sender ref] type], [[sender ref] shortName]];
41         NSString *question = [NSString stringWithFormat:@"Are you sure you want to remove the %@?", ref_desc];
42         int choice = NSRunAlertPanel([NSString stringWithFormat:@"Delete %@?", ref_desc], question, @"Delete", @"Cancel", nil);
43         // TODO: Use a non-modal alert here, so we don't block all the GitX windows
45         if(choice) {
46                 int ret = 1;
47                 [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-d", [[sender ref] ref], nil] retValue: &ret];
48                 if (ret) {
49                         NSLog(@"Removing ref failed!");
50                         return;
51                 }
52                 [historyController.repository removeBranch:[[PBGitRevSpecifier alloc] initWithRef:[sender ref]]];
53                 [[sender commit] removeRef:[sender ref]];
54                 [commitController rearrangeObjects];
55         }
58 - (void) checkoutRef:(PBRefMenuItem *)sender
60         int ret = 1;
61         [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"checkout", [[sender ref] shortName], nil] retValue: &ret];
62         if (ret) {
63                 [[historyController.repository windowController] showMessageSheet:@"Checking out branch failed" infoText:@"There was an error checking out the branch. Perhaps your working directory is not clean?"];
64                 return;
65         }
66         [historyController.repository reloadRefs];
67         [commitController rearrangeObjects];
70 - (void) tagInfo:(PBRefMenuItem *)sender
72     NSString *message = [NSString stringWithFormat:@"Info for tag: %@", [[sender ref] shortName]];
74     int ret = 1;
75     NSString *info = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"tag", @"-n50", @"-l", [[sender ref] shortName], nil] retValue: &ret];
77     if (!ret) {
78             [[historyController.repository windowController] showMessageSheet:message infoText:info];
79     }
80     return;
83 - (NSArray *) menuItemsForRef:(PBGitRef *)ref commit:(PBGitCommit *)commit
85         return [PBRefMenuItem defaultMenuItemsForRef:ref commit:commit target:self];
88 # pragma mark Tableview delegate methods
90 - (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
92         NSPoint location = [tv convertPointFromBase:[(PBCommitList *)tv mouseDownPoint]];
93         int row = [tv rowAtPoint:location];
94         int column = [tv columnAtPoint:location];
95         if (column != 0)
96                 return NO;
97         
98         PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row];
99         
100         int index = [cell indexAtX:location.x];
101         
102         if (index == -1)
103                 return NO;
104         
105         NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:row], [NSNumber numberWithInt:index], NULL]];
106         [pboard declareTypes:[NSArray arrayWithObject:@"PBGitRef"] owner:self];
107         [pboard setData:data forType:@"PBGitRef"];
108         
109         return YES;
112 - (NSDragOperation)tableView:(NSTableView*)tv
113                                 validateDrop:(id <NSDraggingInfo>)info
114                                  proposedRow:(NSInteger)row
115            proposedDropOperation:(NSTableViewDropOperation)operation
117         if (operation == NSTableViewDropAbove)
118                 return NSDragOperationNone;
119         
120         NSPasteboard *pboard = [info draggingPasteboard];
121         if ([pboard dataForType:@"PBGitRef"])
122                 return NSDragOperationMove;
123         
124         return NSDragOperationNone;
127 - (BOOL)tableView:(NSTableView *)aTableView
128            acceptDrop:(id <NSDraggingInfo>)info
129                           row:(NSInteger)row
130         dropOperation:(NSTableViewDropOperation)operation
132         if (operation != NSTableViewDropOn)
133                 return NO;
134         
135         NSPasteboard *pboard = [info draggingPasteboard];
136         NSData *data = [pboard dataForType:@"PBGitRef"];
137         if (!data)
138                 return NO;
139         
140         NSArray *numbers = [NSKeyedUnarchiver unarchiveObjectWithData:data];
141         int oldRow = [[numbers objectAtIndex:0] intValue];
142         int oldRefIndex = [[numbers objectAtIndex:1] intValue];
143         PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex: oldRow];
144         PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex];
145         
146         PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row];
147         
148         int a = [[NSAlert alertWithMessageText:@"Change branch"
149                                                          defaultButton:@"Change"
150                                                    alternateButton:@"Cancel"
151                                                            otherButton:nil
152                                  informativeTextWithFormat:@"Do you want to change branch\n\n\t'%@'\n\n to point to commit\n\n\t'%@'", [ref shortName], [dropCommit subject]] runModal];
153         if (a != NSAlertDefaultReturn)
154                 return NO;
155         
156         int retValue = 1;
157         [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit realSha], NULL] retValue:&retValue];
158         if (retValue)
159                 return NO;
160         
161         [dropCommit addRef:ref];
162         [oldCommit removeRef:ref];
163         
164         [commitController rearrangeObjects];
165         [aTableView needsToDrawRect:[aTableView rectOfRow:oldRow]];
166         return YES;
169 # pragma mark Add ref methods
170 -(void)addRef:(id)sender
172         [errorMessage setStringValue:@""];
173         [NSApp beginSheet:newBranchSheet
174            modalForWindow:[[historyController view] window]
175                 modalDelegate:NULL
176            didEndSelector:NULL
177                   contextInfo:NULL];
180 -(void)saveSheet:(id) sender
182         NSString *branchName = [@"refs/heads/" stringByAppendingString:[newBranchName stringValue]];
183         
184         if ([[commitController selectedObjects] count] == 0)
185                 return;
187         PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
189         int retValue = 1;
190         [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"check-ref-format", branchName, nil] retValue:&retValue];
191         if (retValue != 0) {
192                 [errorMessage setStringValue:@"Invalid name"];
193                 return;
194         }
196         retValue = 1;
197         [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mCreate branch from GitX", branchName, [commit realSha], @"0000000000000000000000000000000000000000", NULL] retValue:&retValue];
198         if (retValue)
199         {
200                 [errorMessage setStringValue:@"Branch exists"];
201                 return;
202         }
203         [historyController.repository addBranch:[[PBGitRevSpecifier alloc] initWithRef:[PBGitRef refFromString:branchName]]];
204         [self closeSheet:sender];
205         [commit addRef:[PBGitRef refFromString:branchName]];
206         [commitController rearrangeObjects];
209 -(void)closeSheet:(id) sender
210 {       
211         [NSApp endSheet:newBranchSheet];
212         [newBranchName setStringValue:@""];
213         [newBranchSheet orderOut:self];
216 # pragma mark Branches menu
218 - (void) updateBranchMenu
220         if (!branchPopUp)
221                 return;
223         NSMutableArray *localBranches = [NSMutableArray array];
224         NSMutableArray *remoteBranches = [NSMutableArray array];
225         NSMutableArray *tags = [NSMutableArray array];
226         NSMutableArray *other = [NSMutableArray array];
228         NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Branch menu"];
229         for (PBGitRevSpecifier *rev in historyController.repository.branches)
230         {
231                 if (![rev isSimpleRef])
232                 {
233                         [other addObject:rev];
234                         continue;
235                 }
237                 NSString *ref = [rev simpleRef];
239                 if ([ref hasPrefix:@"refs/heads"])
240                         [localBranches addObject:rev];
241                 else if ([ref hasPrefix:@"refs/tags"])
242                         [tags addObject:rev];
243                 else if ([ref hasPrefix:@"refs/remote"])
244                         [remoteBranches addObject:rev];
245         }
247         for (PBGitRevSpecifier *rev in localBranches)
248         {
249                 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
250                 [item setRepresentedObject:rev];
251                 [item setTarget:self];
252                 [menu addItem:item];
253         }
255         [menu addItem:[NSMenuItem separatorItem]];
257         // Remotes
258         NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Remotes"];
259         NSMenu *currentMenu = NULL;
260         for (PBGitRevSpecifier *rev in remoteBranches)
261         {
262                 NSString *ref = [rev simpleRef];
263                 NSArray *components = [ref componentsSeparatedByString:@"/"];
264                 
265                 NSString *remoteName = [components objectAtIndex:2];
266                 NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
268                 if (![[currentMenu title] isEqualToString:remoteName])
269                 {
270                         currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
271                         NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
272                         [item setSubmenu:currentMenu];
273                         [remoteMenu addItem:item];
274                 }
276                 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(changeBranch:) keyEquivalent:@""];
277                 [item setTarget:self];
278                 [item setRepresentedObject:rev];
279                 [currentMenu addItem:item];
280         }
282         NSMenuItem *remoteItem = [[NSMenuItem alloc] initWithTitle:@"Remotes" action:NULL keyEquivalent:@""];
283         [remoteItem setSubmenu:remoteMenu];
284         [menu addItem:remoteItem];
286         // Tags
287         NSMenu *tagMenu = [[NSMenu alloc] initWithTitle:@"Tags"];
288         for (PBGitRevSpecifier *rev in tags)
289         {
290                 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
291                 [item setTarget:self];
292                 [item setRepresentedObject:rev];
293                 [tagMenu addItem:item];
294         }               
295         
296         NSMenuItem *tagItem = [[NSMenuItem alloc] initWithTitle:@"Tags" action:NULL keyEquivalent:@""];
297         [tagItem setSubmenu:tagMenu];
298         [menu addItem:tagItem];
301         // Others
302         [menu addItem:[NSMenuItem separatorItem]];
304         for (PBGitRevSpecifier *rev in other)
305         {
306                 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
307                 [item setRepresentedObject:rev];
308                 [item setTarget:self];
309                 [menu addItem:item];
310         }
311         
312         [[branchPopUp cell] setMenu: menu];
315 - (void) changeBranch:(NSMenuItem *)sender
317         PBGitRevSpecifier *rev = [sender representedObject];
318         historyController.repository.currentBranch = rev;
321 - (void) selectCurrentBranch
323         PBGitRevSpecifier *rev = historyController.repository.currentBranch;
324         if (rev)
325                 [branchPopUp setTitle:[rev description]];
328 @end