// // PBLabelController.m // GitX // // Created by Pieter de Bie on 21-10-08. // Copyright 2008 Pieter de Bie. All rights reserved. // #import "PBRefController.h" #import "PBGitRevisionCell.h" #import "PBRefMenuItem.h" #import "PBCreateBranchSheet.h" #import "PBCreateTagSheet.h" #import "PBGitDefaults.h" #import "PBDiffWindowController.h" #import "PBGitResetController.h" #import "PBArgumentPickerController.h" #define kDialogAcceptDroppedRef @"Accept Dropped Ref" #define kDialogConfirmPush @"Confirm Push" #define kDialogDeleteRef @"Delete Ref" @implementation PBRefController - (void)awakeFromNib { [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]]; } #pragma mark Fetch - (void) fetchRemote:(PBRefMenuItem *)sender { id refish = [sender refish]; if ([refish refishType] == kGitXCommitType) return; [historyController.repository beginFetchFromRemoteForRef:refish]; } #pragma mark Pull - (void) pullRemote:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository beginPullFromRemote:nil forRef:refish]; } #pragma mark Push - (void)showConfirmPushRefSheet:(PBGitRef *)ref remote:(PBGitRef *)remoteRef { if ((!ref && !remoteRef) || (ref && ![ref isBranch] && ![ref isRemoteBranch]) || (remoteRef && !([remoteRef refishType] == kGitXRemoteType))) return; if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogConfirmPush]) { [historyController.repository beginPushRef:ref toRemote:remoteRef]; return; } NSString *description = nil; if (ref && remoteRef) description = [NSString stringWithFormat:@"Push %@ '%@' to remote %@", [ref refishType], [ref shortName], [remoteRef remoteName]]; else if (ref) description = [NSString stringWithFormat:@"Push %@ '%@' to default remote", [ref refishType], [ref shortName]]; else description = [NSString stringWithFormat:@"Push updates to remote %@", [remoteRef remoteName]]; NSString * sdesc = [NSString stringWithFormat:@"p%@", [description substringFromIndex:1]]; NSAlert *alert = [NSAlert alertWithMessageText:description defaultButton:@"Push" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:@"Are you sure you want to %@?", sdesc]; [alert setShowsSuppressionButton:YES]; NSMutableDictionary *info = [NSMutableDictionary dictionary]; if (ref) [info setObject:ref forKey:kGitXBranchType]; if (remoteRef) [info setObject:remoteRef forKey:kGitXRemoteType]; [alert beginSheetModalForWindow:[historyController.repository.windowController window] modalDelegate:self didEndSelector:@selector(confirmPushRefSheetDidEnd:returnCode:contextInfo:) contextInfo:info]; } - (void)confirmPushRefSheetDidEnd:(NSAlert *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { [[sheet window] orderOut:nil]; if ([[sheet suppressionButton] state] == NSOnState) [PBGitDefaults suppressDialogWarningForDialog:kDialogConfirmPush]; if (returnCode == NSAlertDefaultReturn) { PBGitRef *ref = [(NSDictionary *)contextInfo objectForKey:kGitXBranchType]; PBGitRef *remoteRef = [(NSDictionary *)contextInfo objectForKey:kGitXRemoteType]; [historyController.repository beginPushRef:ref toRemote:remoteRef]; } } - (void) pushUpdatesToRemote:(PBRefMenuItem *)sender { PBGitRef *remoteRef = [(PBGitRef *)[sender refish] remoteRef]; [self showConfirmPushRefSheet:nil remote:remoteRef]; } - (void) pushDefaultRemoteForRef:(PBRefMenuItem *)sender { PBGitRef *ref = (PBGitRef *)[sender refish]; [self showConfirmPushRefSheet:ref remote:nil]; } - (void) pushToRemote:(PBRefMenuItem *)sender { PBGitRef *ref = (PBGitRef *)[sender refish]; NSString *remoteName = [sender representedObject]; PBGitRef *remoteRef = [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:remoteName]]; [self showConfirmPushRefSheet:ref remote:remoteRef]; } #pragma mark Merge - (void) merge:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository mergeWithRefish:refish]; } #pragma mark Checkout - (void) checkout:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository checkoutRefish:refish]; } #pragma mark Reset - (void) reset:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository.resetController resetToRefish: refish type: PBResetTypeMixed]; } #pragma mark Cherry Pick - (void) cherryPick:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository cherryPickRefish:refish]; } #pragma mark Rebase - (void) rebaseHeadBranch:(PBRefMenuItem *)sender { id refish = [sender refish]; [historyController.repository rebaseBranch:nil onRefish:refish]; } #pragma mark Create Branch - (void) createBranch:(PBRefMenuItem *)sender { id refish = [sender refish]; [PBCreateBranchSheet beginCreateBranchSheetAtRefish:refish inRepository:historyController.repository]; } #pragma mark Copy info - (void) copySHA:(PBRefMenuItem *)sender { PBGitCommit *commit = nil; if ([[sender refish] refishType] == kGitXCommitType) commit = (PBGitCommit *)[sender refish]; else commit = [historyController.repository commitForRef:[sender refish]]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; [pasteboard setString:[commit realSha] forType:NSStringPboardType]; } - (void) copyPatch:(PBRefMenuItem *)sender { PBGitCommit *commit = nil; if ([[sender refish] refishType] == kGitXCommitType) commit = (PBGitCommit *)[sender refish]; else commit = [historyController.repository commitForRef:[sender refish]]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; [pasteboard setString:[commit patch] forType:NSStringPboardType]; } #pragma mark Diff - (void) diffWithHEAD:(PBRefMenuItem *)sender { PBGitCommit *commit = nil; if ([[sender refish] refishType] == kGitXCommitType) commit = (PBGitCommit *)[sender refish]; else commit = [historyController.repository commitForRef:[sender refish]]; [PBDiffWindowController showDiffWindowWithFiles:nil fromCommit:commit diffCommit:nil]; } #pragma mark Tags - (void) createTag:(PBRefMenuItem *)sender { id refish = [sender refish]; [PBCreateTagSheet beginCreateTagSheetAtRefish:refish inRepository:historyController.repository]; } - (void) showTagInfoSheet:(PBRefMenuItem *)sender { if ([[sender refish] refishType] != kGitXTagType) return; NSString *tagName = [(PBGitRef *)[sender refish] tagName]; int retValue = 1; NSArray *args = [NSArray arrayWithObjects:@"tag", @"-n50", @"-l", tagName, nil]; NSString *info = [historyController.repository outputInWorkdirForArguments:args retValue:&retValue]; if (!retValue) { NSString *message = [NSString stringWithFormat:@"Info for tag: %@", tagName]; [historyController.repository.windowController showMessageSheet:message infoText:info]; } } #pragma mark Remove a branch, remote or tag - (void)showDeleteRefSheet:(PBRefMenuItem *)sender { if ([[sender refish] refishType] == kGitXCommitType) return; PBGitRef *ref = (PBGitRef *)[sender refish]; if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogDeleteRef]) { [historyController.repository deleteRef:ref]; return; } NSString *ref_desc = [NSString stringWithFormat:@"%@ '%@'", [ref refishType], [ref shortName]]; NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"Delete %@?", ref_desc] defaultButton:@"Delete" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:@"Are you sure you want to remove the %@?", ref_desc]; [alert setShowsSuppressionButton:YES]; [alert beginSheetModalForWindow:[historyController.repository.windowController window] modalDelegate:self didEndSelector:@selector(deleteRefSheetDidEnd:returnCode:contextInfo:) contextInfo:ref]; } - (void)deleteRefSheetDidEnd:(NSAlert *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { [[sheet window] orderOut:nil]; if ([[sheet suppressionButton] state] == NSOnState) [PBGitDefaults suppressDialogWarningForDialog:kDialogDeleteRef]; if (returnCode == NSAlertDefaultReturn) { PBGitRef *ref = (PBGitRef *)contextInfo; [historyController.repository deleteRef:ref]; } } #pragma mark Contextual menus - (NSArray *) menuItemsForRef:(PBGitRef *)ref { return [PBRefMenuItem defaultMenuItemsForRef:ref inRepository:historyController.repository target:self]; } - (NSArray *) menuItemsForCommit:(PBGitCommit *)commit { return [PBRefMenuItem defaultMenuItemsForCommit:commit target:self]; } - (NSArray *)menuItemsForRow:(NSInteger)rowIndex { NSArray *commits = [commitController arrangedObjects]; if ([commits count] <= rowIndex) return nil; return [self menuItemsForCommit:[commits objectAtIndex:rowIndex]]; } # pragma mark Tableview delegate methods - (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard { NSPoint location = [tv convertPointFromBase:[(PBCommitList *)tv mouseDownPoint]]; int row = [tv rowAtPoint:location]; int column = [tv columnAtPoint:location]; int subjectColumn = [tv columnWithIdentifier:@"SubjectColumn"]; if (column != subjectColumn) return NO; PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row]; NSRect cellFrame = [tv frameOfCellAtColumn:column row:row]; int index = [cell indexAtX:(location.x - cellFrame.origin.x)]; if (index == -1) return NO; PBGitRef *ref = [[[cell objectValue] refs] objectAtIndex:index]; if ([ref isTag] || [ref isRemoteBranch]) return NO; if ([[[historyController.repository headRef] ref] isEqualToRef:ref]) return NO; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:row], [NSNumber numberWithInt:index], NULL]]; [pboard declareTypes:[NSArray arrayWithObject:@"PBGitRef"] owner:self]; [pboard setData:data forType:@"PBGitRef"]; return YES; } - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation { if (operation == NSTableViewDropAbove) return NSDragOperationNone; NSPasteboard *pboard = [info draggingPasteboard]; if ([pboard dataForType:@"PBGitRef"]) return NSDragOperationMove; return NSDragOperationNone; } - (void) dropRef:(NSDictionary *)dropInfo { PBGitRef *ref = [dropInfo objectForKey:@"dragRef"]; PBGitCommit *oldCommit = [dropInfo objectForKey:@"oldCommit"]; PBGitCommit *dropCommit = [dropInfo objectForKey:@"dropCommit"]; if (!ref || ! oldCommit || !dropCommit) return; int retValue = 1; [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit realSha], NULL] retValue:&retValue]; if (retValue) return; [dropCommit addRef:ref]; [oldCommit removeRef:ref]; [historyController.commitList reloadData]; } - (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id )info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation { if (operation != NSTableViewDropOn) return NO; NSPasteboard *pboard = [info draggingPasteboard]; NSData *data = [pboard dataForType:@"PBGitRef"]; if (!data) return NO; NSArray *numbers = [NSKeyedUnarchiver unarchiveObjectWithData:data]; int oldRow = [[numbers objectAtIndex:0] intValue]; if (oldRow == row) return NO; int oldRefIndex = [[numbers objectAtIndex:1] intValue]; PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex:oldRow]; PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex]; PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row]; NSDictionary *dropInfo = [NSDictionary dictionaryWithObjectsAndKeys: ref, @"dragRef", oldCommit, @"oldCommit", dropCommit, @"dropCommit", nil]; if ([PBGitDefaults isDialogWarningSuppressedForDialog:kDialogAcceptDroppedRef]) { [self dropRef:dropInfo]; return YES; } NSString *subject = [dropCommit subject]; if ([subject length] > 99) subject = [[subject substringToIndex:99] stringByAppendingString:@"…"]; NSString *infoText = [NSString stringWithFormat:@"Move the %@ to point to the commit: %@", [ref refishType], subject]; NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"Move %@: %@", [ref refishType], [ref shortName]] defaultButton:@"Move" alternateButton:@"Cancel" otherButton:nil informativeTextWithFormat:infoText]; [alert setShowsSuppressionButton:YES]; [alert beginSheetModalForWindow:[historyController.repository.windowController window] modalDelegate:self didEndSelector:@selector(acceptDropInfoAlertDidEnd:returnCode:contextInfo:) contextInfo:dropInfo]; return YES; } - (void)acceptDropInfoAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { [[alert window] orderOut:nil]; if (returnCode == NSAlertDefaultReturn) [self dropRef:contextInfo]; if ([[alert suppressionButton] state] == NSOnState) [PBGitDefaults suppressDialogWarningForDialog:kDialogAcceptDroppedRef]; } @end