5 // Created by Pieter de Bie on 21-10-08.
6 // Copyright 2008 Pieter de Bie. All rights reserved.
9 #import "PBRefController.h"
10 #import "PBGitRevisionCell.h"
11 #import "PBRefMenuItem.h"
13 @implementation PBRefController
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];
29 else if ([(NSString *)context isEqualToString:@"currentBranchChange"]) {
30 [self selectCurrentBranch];
33 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
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
47 [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-d", [[sender ref] ref], nil] retValue: &ret];
49 NSLog(@"Removing ref failed!");
52 [historyController.repository removeBranch:[[PBGitRevSpecifier alloc] initWithRef:[sender ref]]];
53 [[sender commit] removeRef:[sender ref]];
54 [commitController rearrangeObjects];
58 - (void) checkoutRef:(PBRefMenuItem *)sender
61 [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"checkout", [[sender ref] shortName], nil] retValue: &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?"];
66 [historyController.repository reloadRefs];
67 [commitController rearrangeObjects];
70 - (void) tagInfo:(PBRefMenuItem *)sender
72 NSString *message = [NSString stringWithFormat:@"Info for tag: %@", [[sender ref] shortName]];
75 NSString *info = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"tag", @"-n50", @"-l", [[sender ref] shortName], nil] retValue: &ret];
78 [[historyController.repository windowController] showMessageSheet:message infoText:info];
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];
98 PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row];
100 int index = [cell indexAtX:location.x];
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"];
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;
120 NSPasteboard *pboard = [info draggingPasteboard];
121 if ([pboard dataForType:@"PBGitRef"])
122 return NSDragOperationMove;
124 return NSDragOperationNone;
127 - (BOOL)tableView:(NSTableView *)aTableView
128 acceptDrop:(id <NSDraggingInfo>)info
130 dropOperation:(NSTableViewDropOperation)operation
132 if (operation != NSTableViewDropOn)
135 NSPasteboard *pboard = [info draggingPasteboard];
136 NSData *data = [pboard dataForType:@"PBGitRef"];
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];
146 PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row];
148 int a = [[NSAlert alertWithMessageText:@"Change branch"
149 defaultButton:@"Change"
150 alternateButton:@"Cancel"
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)
157 [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit realSha], NULL] retValue:&retValue];
161 [dropCommit addRef:ref];
162 [oldCommit removeRef:ref];
164 [commitController rearrangeObjects];
165 [aTableView needsToDrawRect:[aTableView rectOfRow:oldRow]];
169 # pragma mark Add ref methods
170 -(void)addRef:(id)sender
172 [errorMessage setStringValue:@""];
173 [NSApp beginSheet:newBranchSheet
174 modalForWindow:[[historyController view] window]
180 -(void)saveSheet:(id) sender
182 NSString *branchName = [@"refs/heads/" stringByAppendingString:[newBranchName stringValue]];
184 if ([[commitController selectedObjects] count] == 0)
187 PBGitCommit *commit = [[commitController selectedObjects] objectAtIndex:0];
190 [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"check-ref-format", branchName, nil] retValue:&retValue];
192 [errorMessage setStringValue:@"Invalid name"];
197 [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mCreate branch from GitX", branchName, [commit realSha], @"0000000000000000000000000000000000000000", NULL] retValue:&retValue];
200 [errorMessage setStringValue:@"Branch exists"];
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
211 [NSApp endSheet:newBranchSheet];
212 [newBranchName setStringValue:@""];
213 [newBranchSheet orderOut:self];
216 # pragma mark Branches menu
218 - (void) updateBranchMenu
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)
231 if (![rev isSimpleRef])
233 [other addObject:rev];
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];
247 for (PBGitRevSpecifier *rev in localBranches)
249 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
250 [item setRepresentedObject:rev];
251 [item setTarget:self];
255 [menu addItem:[NSMenuItem separatorItem]];
258 NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Remotes"];
259 NSMenu *currentMenu = NULL;
260 for (PBGitRevSpecifier *rev in remoteBranches)
262 NSString *ref = [rev simpleRef];
263 NSArray *components = [ref componentsSeparatedByString:@"/"];
265 NSString *remoteName = [components objectAtIndex:2];
266 NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"];
268 if (![[currentMenu title] isEqualToString:remoteName])
270 currentMenu = [[NSMenu alloc] initWithTitle:remoteName];
271 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""];
272 [item setSubmenu:currentMenu];
273 [remoteMenu addItem:item];
276 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(changeBranch:) keyEquivalent:@""];
277 [item setTarget:self];
278 [item setRepresentedObject:rev];
279 [currentMenu addItem:item];
282 NSMenuItem *remoteItem = [[NSMenuItem alloc] initWithTitle:@"Remotes" action:NULL keyEquivalent:@""];
283 [remoteItem setSubmenu:remoteMenu];
284 [menu addItem:remoteItem];
287 NSMenu *tagMenu = [[NSMenu alloc] initWithTitle:@"Tags"];
288 for (PBGitRevSpecifier *rev in tags)
290 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
291 [item setTarget:self];
292 [item setRepresentedObject:rev];
293 [tagMenu addItem:item];
296 NSMenuItem *tagItem = [[NSMenuItem alloc] initWithTitle:@"Tags" action:NULL keyEquivalent:@""];
297 [tagItem setSubmenu:tagMenu];
298 [menu addItem:tagItem];
302 [menu addItem:[NSMenuItem separatorItem]];
304 for (PBGitRevSpecifier *rev in other)
306 NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""];
307 [item setRepresentedObject:rev];
308 [item setTarget:self];
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;
325 [branchPopUp setTitle:[rev description]];