From 4cefe8ee920fcf43e3f765cc8dc09ddb913f6a82 Mon Sep 17 00:00:00 2001 From: Pieter de Bie Date: Sat, 1 Nov 2008 21:08:23 +0100 Subject: [PATCH] HistoryView: Allow dragging of refs to move them --- GitX.xcodeproj/project.pbxproj | 6 ++ PBCommitList.h | 3 + PBCommitList.m | 42 +++++++++++- PBGitCommit.h | 7 +- PBGitCommit.m | 8 +++ PBGitHistoryController.h | 1 + PBGitHistoryController.m | 3 +- PBGitHistoryView.xib | 82 +++++++++++++++++++++-- PBGitRevisionCell.h | 4 ++ PBGitRevisionCell.m | 118 ++++++++++++++++++++++++--------- PBRefController.h | 19 ++++++ PBRefController.m | 100 ++++++++++++++++++++++++++++ 12 files changed, 352 insertions(+), 41 deletions(-) create mode 100644 PBRefController.h create mode 100644 PBRefController.m diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 15de761..e770d3a 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ F56CC7290E65E0AD004307B4 /* PBGitGraphLine.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7280E65E0AD004307B4 /* PBGitGraphLine.m */; }; F56CC7320E65E0E5004307B4 /* PBGraphCellInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */; }; F57240BB0E9678EA00D8EE66 /* deleted_file.png in Resources */ = {isa = PBXBuildFile; fileRef = F57240BA0E9678EA00D8EE66 /* deleted_file.png */; }; + F574A2850EAE2EAC003F2CB1 /* PBRefController.m in Sources */ = {isa = PBXBuildFile; fileRef = F574A2840EAE2EAC003F2CB1 /* PBRefController.m */; }; F574A2910EAE2FF4003F2CB1 /* PBGitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */; }; F57CC3910E05DDF2000472E2 /* PBEasyPipe.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */; }; F57CC4410E05E496000472E2 /* PBGitWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F57CC4400E05E496000472E2 /* PBGitWindowController.m */; }; @@ -161,6 +162,8 @@ F56CC7300E65E0E5004307B4 /* PBGraphCellInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGraphCellInfo.h; sourceTree = ""; }; F56CC7310E65E0E5004307B4 /* PBGraphCellInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGraphCellInfo.m; sourceTree = ""; }; F57240BA0E9678EA00D8EE66 /* deleted_file.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = deleted_file.png; path = Images/deleted_file.png; sourceTree = ""; }; + F574A2830EAE2EAC003F2CB1 /* PBRefController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBRefController.h; sourceTree = ""; }; + F574A2840EAE2EAC003F2CB1 /* PBRefController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBRefController.m; sourceTree = ""; }; F57CC38F0E05DDF2000472E2 /* PBEasyPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBEasyPipe.h; sourceTree = ""; }; F57CC3900E05DDF2000472E2 /* PBEasyPipe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBEasyPipe.m; sourceTree = ""; }; F57CC43F0E05E496000472E2 /* PBGitWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitWindowController.h; sourceTree = ""; }; @@ -498,6 +501,8 @@ F565262A0E03D89B00F03B52 /* PBWebHistoryController.m */, F52BCE050E84211300AA3741 /* PBGitHistoryController.h */, F52BCE060E84211300AA3741 /* PBGitHistoryController.m */, + F574A2830EAE2EAC003F2CB1 /* PBRefController.h */, + F574A2840EAE2EAC003F2CB1 /* PBRefController.m */, ); name = History; sourceTree = ""; @@ -649,6 +654,7 @@ F5FE6C030EB13BC900F30D12 /* PBServicesController.m in Sources */, F50A41230EBB875D00208746 /* PBNiceSplitView.m in Sources */, F5FC41F40EBCBD4300191D80 /* PBGitXProtocol.m in Sources */, + F574A2850EAE2EAC003F2CB1 /* PBRefController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/PBCommitList.h b/PBCommitList.h index d142f61..9dd67fd 100644 --- a/PBCommitList.h +++ b/PBCommitList.h @@ -14,6 +14,9 @@ IBOutlet WebView* webView; IBOutlet PBWebHistoryController* webController; IBOutlet PBGitHistoryController *controller; + + NSPoint mouseDownPoint; } +@property (readonly) NSPoint mouseDownPoint; @end diff --git a/PBCommitList.m b/PBCommitList.m index 9b7c2ef..88aa0b9 100644 --- a/PBCommitList.m +++ b/PBCommitList.m @@ -7,10 +7,17 @@ // #import "PBCommitList.h" - +#import "PBGitRevisionCell.h" @implementation PBCommitList +@synthesize mouseDownPoint; +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL) local +{ + NSLog(@"a"); + return NSDragOperationCopy; +} + - (void) keyDown: (id) event { NSString* character = [event charactersIgnoringModifiers]; @@ -33,4 +40,37 @@ [controller copyCommitInfo]; }; +- (void)mouseDown:(NSEvent *)theEvent +{ + mouseDownPoint = [[self window] mouseLocationOutsideOfEventStream]; + [super mouseDown:theEvent]; +} + +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows + tableColumns:(NSArray *)tableColumns + event:(NSEvent *)dragEvent + offset:(NSPointPointer)dragImageOffset +{ + NSPoint location = [self convertPointFromBase:mouseDownPoint]; + int row = [self rowAtPoint:location]; + int column = [self columnAtPoint:location]; + PBGitRevisionCell *cell = (PBGitRevisionCell *)[self preparedCellAtColumn:column row:row]; + + int index = [cell indexAtX:location.x]; + if (index == -1) + return [super dragImageForRowsWithIndexes:dragRows tableColumns:tableColumns event:dragEvent offset:dragImageOffset]; + + NSRect rect = [cell rectAtIndex:index]; + + NSImage *newImage = [[NSImage alloc] initWithSize:NSMakeSize(rect.size.width + 3, rect.size.height + 3)]; + rect.origin = NSMakePoint(0.5, 0.5); + + [newImage lockFocus]; + [cell drawLabelAtIndex:index inRect:rect]; + [newImage unlockFocus]; + + *dragImageOffset = NSMakePoint(rect.size.width / 2 + 10, 0); + return newImage; + +} @end diff --git a/PBGitCommit.h b/PBGitCommit.h index 7e24779..75e7419 100644 --- a/PBGitCommit.h +++ b/PBGitCommit.h @@ -17,7 +17,7 @@ NSString* details; NSString *_patch; NSArray* parents; - NSArray* refs; + NSMutableArray* refs; NSDate* date; char sign; id lineInfo; @@ -26,10 +26,13 @@ - initWithRepository:(PBGitRepository*) repo andSha:(NSString*) sha; +- (void)addRef:(id)ref; + @property (copy) NSString* sha; @property (copy) NSString* subject; @property (copy) NSString* author; -@property (retain) NSArray* parents, *refs; +@property (retain) NSArray* parents; +@property (retain) NSMutableArray* refs; @property (copy) NSDate* date; @property (readonly) NSString* dateString; @property (readonly) NSString* patch; diff --git a/PBGitCommit.m b/PBGitCommit.m index 1b99db4..385bed5 100644 --- a/PBGitCommit.m +++ b/PBGitCommit.m @@ -62,6 +62,14 @@ return [PBGitTree rootForCommit: self]; } +- (void)addRef:(id)ref +{ + if (!self.refs) + self.refs = [NSMutableArray arrayWithObject:ref]; + else + [self.refs addObject:ref]; +} + + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { return NO; diff --git a/PBGitHistoryController.h b/PBGitHistoryController.h index 86201bd..d7cf31b 100644 --- a/PBGitHistoryController.h +++ b/PBGitHistoryController.h @@ -18,6 +18,7 @@ IBOutlet NSTreeController* treeController; IBOutlet NSOutlineView* fileBrowser; IBOutlet NSTableView* commitList; + IBOutlet id webView; int selectedTab; diff --git a/PBGitHistoryController.m b/PBGitHistoryController.m index 0d238a4..11254d5 100644 --- a/PBGitHistoryController.m +++ b/PBGitHistoryController.m @@ -10,6 +10,7 @@ #import "CWQuickLook.h" #import "PBGitGrapher.h" #import "PBGitRevisionCell.h" +#import "PBCommitList.h" #define QLPreviewPanel NSClassFromString(@"QLPreviewPanel") @@ -38,7 +39,7 @@ // Set a sort descriptor for the subject column in the history list, as // It can't be sorted by default (because it's bound to a PBGitCommit) [[commitList tableColumnWithIdentifier:@"subject"] setSortDescriptorPrototype:[[NSSortDescriptor alloc] initWithKey:@"subject" ascending:YES]]; - + [super awakeFromNib]; // We bind this ourselves because otherwise we would lose our selection [branchesController bind:@"selectionIndexes" toObject:repository withKeyPath:@"currentBranch" options:nil]; diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index 2333984..2120619 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -8,7 +8,7 @@ 352.00 YES - + YES @@ -1344,6 +1344,9 @@ NSView + + PBRefController + @@ -1866,6 +1869,38 @@ 230 + + + commitController + + + + 232 + + + + commitList + + + + 233 + + + + historyController + + + + 234 + + + + dataSource + + + + 235 + @@ -2366,6 +2401,11 @@ + + 231 + + + @@ -2411,6 +2451,7 @@ 224.IBPluginDependency 225.IBPluginDependency 23.IBPluginDependency + 231.IBPluginDependency 24.IBPluginDependency 25.IBPluginDependency 26.IBPluginDependency @@ -2502,6 +2543,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin PBCommitList com.apple.InterfaceBuilder.CocoaPlugin @@ -2567,7 +2609,7 @@ - 230 + 235 @@ -2696,6 +2738,29 @@ PBQLOutlineView.h + + PBRefController + NSObject + + YES + + YES + commitController + commitList + historyController + + + YES + NSArrayController + PBCommitList + PBGitHistoryController + + + + IBProjectSource + PBRefController.h + + PBUnsortableTableHeader NSTableHeaderView @@ -2724,8 +2789,17 @@ PBWebController NSObject - view - WebView + YES + + YES + repository + view + + + YES + id + WebView + IBProjectSource diff --git a/PBGitRevisionCell.h b/PBGitRevisionCell.h index 450f9b8..a87944f 100644 --- a/PBGitRevisionCell.h +++ b/PBGitRevisionCell.h @@ -18,5 +18,9 @@ IBOutlet PBGitHistoryController *controller; } +- (int) indexAtX:(float)x; +- (NSRect) rectAtIndex:(int)index; +- (void) drawLabelAtIndex:(int)index inRect:(NSRect)rect; + @property(retain) PBGitCommit* objectValue; @end diff --git a/PBGitRevisionCell.m b/PBGitRevisionCell.m index 68a5680..698627e 100644 --- a/PBGitRevisionCell.m +++ b/PBGitRevisionCell.m @@ -143,45 +143,65 @@ return [NSColor yellowColor]; } -- (void) drawRefsInRect: (NSRect*) rect +-(NSArray *)rectsForRefsinRect:(NSRect) rect; { - static const float ref_padding = 10.0f; - static const float ref_spacing = 2.0f; + NSMutableArray *array = [NSMutableArray array]; + + static const int ref_padding = 10; + static const int ref_spacing = 2; + + NSRect lastRect = rect; + lastRect.origin.x = round(lastRect.origin.x) - 0.5; + lastRect.origin.y = round(lastRect.origin.y) - 0.5; + + for (PBGitRef *ref in self.objectValue.refs) { + NSMutableDictionary* attributes = [self attributesForRefLabelSelected:NO isCurrentBranch:NO]; + NSSize textSize = [[ref shortName] sizeWithAttributes:attributes]; + + NSRect newRect = lastRect; + newRect.size.width = textSize.width + ref_padding; + newRect.size.height = textSize.height; + newRect.origin.y = rect.origin.y + (rect.size.height - newRect.size.height) / 2; + + [array addObject:[NSValue valueWithRect:newRect]]; + lastRect = newRect; + lastRect.origin.x += (int)lastRect.size.width + ref_spacing; + } + + return array; +} - NSArray* refs = [self.objectValue refs]; - NSRect refRect = (NSRect){rect->origin, rect->size}; +- (void) drawLabelAtIndex:(int)index inRect:(NSRect)rect +{ + NSArray *refs = self.objectValue.refs; + PBGitRef *ref = [refs objectAtIndex:index]; + BOOL isCurBranch = [ref.ref isEqualToString:[[[controller repository] headRef] simpleRef]]; + + NSMutableDictionary* attributes = [self attributesForRefLabelSelected:[self isHighlighted] + isCurrentBranch:isCurBranch]; + NSBezierPath *border = [NSBezierPath bezierPathWithRoundedRect:rect cornerRadius: 2.0]; + [[self colorForRef:ref] set]; + [border fill]; + + [[ref shortName] drawInRect:rect withAttributes:attributes]; + [border stroke]; +} +- (void) drawRefsInRect: (NSRect *)refRect +{ [[NSColor blackColor] setStroke]; - int index; - for (index = 0; index < [refs count]; ++index) { - PBGitRef *ref = [refs objectAtIndex:index]; - BOOL isCurBranch = [ref.ref isEqualToString:[[[controller repository] headRef] simpleRef]]; - - NSMutableDictionary* attributes = [self attributesForRefLabelSelected:[self isHighlighted] - isCurrentBranch:isCurBranch]; - NSSize refSize = [[ref shortName] sizeWithAttributes:attributes]; - - refRect.size.width = refSize.width + ref_padding; - refRect.size.height = refSize.height; - refRect.origin.y = rect->origin.y + (rect->size.height - refRect.size.height) / 2; - - // Round rects to 0.5 pixels in order to draw only a single pixel - refRect.origin.x = round(refRect.origin.x) - 0.5; - refRect.origin.y = round(refRect.origin.y) - 0.5; - - NSBezierPath *border = [NSBezierPath bezierPathWithRoundedRect:refRect cornerRadius: 2.0]; - [[self colorForRef: ref] set]; - [border fill]; - - [[ref shortName] drawInRect:refRect withAttributes:attributes]; - [border stroke]; - - refRect.origin.x += (int)refRect.size.width + ref_spacing; + NSRect lastRect; + int index = 0; + for (NSValue *rectValue in [self rectsForRefsinRect:*refRect]) + { + NSRect rect = [rectValue rectValue]; + [self drawLabelAtIndex:index inRect:rect]; + lastRect = rect; + ++index; } - - rect->size.width -= refRect.origin.x - rect->origin.x; - rect->origin.x = refRect.origin.x; + refRect->size.width -= lastRect.origin.x - refRect->origin.x + lastRect.size.width; + refRect->origin.x = lastRect.origin.x + lastRect.size.width; } - (void) drawWithFrame: (NSRect) rect inView:(NSView *)view @@ -207,6 +227,7 @@ [self drawCircleInRect: ownRect]; } + if ([self.objectValue refs]) [self drawRefsInRect:&rect]; @@ -226,4 +247,35 @@ return [[super objectValue] nonretainedObjectValue]; } +- (int) indexAtX:(float)x +{ + cellInfo = [self.objectValue lineInfo]; + float pathWidth = 0; + if (cellInfo && ![controller hasNonlinearPath]) + pathWidth = 10 + 10 * cellInfo.numColumns; + + int index = 0; + NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); + for (NSValue *rectValue in [self rectsForRefsinRect:refRect]) + { + NSRect rect = [rectValue rectValue]; + if (x >= rect.origin.x && x <= (rect.origin.x + rect.size.width)) + return index; + ++index; + } + + return -1; +} + +- (NSRect) rectAtIndex:(int)index +{ + cellInfo = [self.objectValue lineInfo]; + float pathWidth = 0; + if (cellInfo && ![controller hasNonlinearPath]) + pathWidth = 10 + 10 * cellInfo.numColumns; + NSRect refRect = NSMakeRect(pathWidth, 0, 1000, 10000); + + return [[[self rectsForRefsinRect:refRect] objectAtIndex:index] rectValue]; +} + @end diff --git a/PBRefController.h b/PBRefController.h new file mode 100644 index 0000000..37d3b4d --- /dev/null +++ b/PBRefController.h @@ -0,0 +1,19 @@ +// +// PBLabelController.h +// GitX +// +// Created by Pieter de Bie on 21-10-08. +// Copyright 2008 Pieter de Bie. All rights reserved. +// + +#import +#import "PBGitHistoryController.h" +#import "PBCommitList.h" + +@interface PBRefController : NSObject { + IBOutlet __weak PBGitHistoryController *historyController; + IBOutlet NSArrayController *commitController; + IBOutlet PBCommitList *commitList; +} + +@end diff --git a/PBRefController.m b/PBRefController.m new file mode 100644 index 0000000..e43b743 --- /dev/null +++ b/PBRefController.m @@ -0,0 +1,100 @@ +// +// 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" + +@implementation PBRefController + +- (void)awakeFromNib +{ + [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]]; +} + +- (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]; + if (column != 0) + return NO; + + PBGitRevisionCell *cell = (PBGitRevisionCell *)[tv preparedCellAtColumn:column row:row]; + + int index = [cell indexAtX:location.x]; + + if (index == -1) + 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:(int)row + proposedDropOperation:(NSTableViewDropOperation)operation +{ + if (operation == NSTableViewDropAbove) + return NSDragOperationNone; + + NSPasteboard *pboard = [info draggingPasteboard]; + if ([pboard dataForType:@"PBGitRef"]) + return NSDragOperationMove; + + return NSDragOperationNone; +} + +- (BOOL)tableView:(NSTableView *)aTableView + acceptDrop:(id )info + row:(int)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]; + int oldRefIndex = [[numbers objectAtIndex:1] intValue]; + PBGitCommit *oldCommit = [[commitController arrangedObjects] objectAtIndex: oldRow]; + PBGitRef *ref = [[oldCommit refs] objectAtIndex:oldRefIndex]; + + PBGitCommit *dropCommit = [[commitController arrangedObjects] objectAtIndex:row]; + + int a = [[NSAlert alertWithMessageText:@"Change branch" + defaultButton:@"Change" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"Do you want to change branch\n\n\t'%@'\n\n to point to commit\n\n\t'%@'", [ref shortName], [dropCommit subject]] runModal]; + if (a != NSAlertDefaultReturn) + return NO; + + int retValue = 1; + [historyController.repository outputForArguments:[NSArray arrayWithObjects:@"update-ref", @"-mUpdate from GitX", [ref ref], [dropCommit sha], NULL] retValue:&retValue]; + if (retValue) + return NO; + + [dropCommit addRef:ref]; + + [oldCommit.refs removeObject:ref]; + if ([oldCommit.refs count] == 0) + oldCommit.refs = NULL; + + [commitController rearrangeObjects]; + [aTableView needsToDrawRect:[aTableView rectOfRow:oldRow]]; + return YES; +} +@end