2 // PBGitIndexController.m
5 // Created by Pieter de Bie on 18-11-08.
6 // Copyright 2008 Pieter de Bie. All rights reserved.
9 #import "PBGitIndexController.h"
10 #import "PBChangedFile.h"
11 #import "PBGitRepository.h"
12 #import "PBGitIndex.h"
14 #define FileChangesTableViewType @"GitFileChangedType"
16 @interface PBGitIndexController ()
17 - (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force;
20 @implementation PBGitIndexController
24 [unstagedTable setDoubleAction:@selector(tableClicked:)];
25 [stagedTable setDoubleAction:@selector(tableClicked:)];
27 [unstagedTable setTarget:self];
28 [stagedTable setTarget:self];
30 [unstagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]];
31 [stagedTable registerForDraggedTypes: [NSArray arrayWithObject:FileChangesTableViewType]];
34 // FIXME: Find a proper place for this method -- this is not it.
35 - (void)ignoreFiles:(NSArray *)files
37 // Build output string
38 NSMutableArray *fileList = [NSMutableArray array];
39 for (PBChangedFile *file in files) {
40 NSString *name = file.path;
41 if ([name length] > 0)
42 [fileList addObject:name];
44 NSString *filesAsString = [fileList componentsJoinedByString:@"\n"];
47 NSString *gitIgnoreName = [commitController.repository gitIgnoreFilename];
49 NSStringEncoding enc = NSUTF8StringEncoding;
51 NSMutableString *ignoreFile;
53 if (![[NSFileManager defaultManager] fileExistsAtPath:gitIgnoreName]) {
54 ignoreFile = [filesAsString mutableCopy];
56 ignoreFile = [NSMutableString stringWithContentsOfFile:gitIgnoreName usedEncoding:&enc error:&error];
58 [[commitController.repository windowController] showErrorSheet:error];
61 // Add a newline if not yet present
62 if ([ignoreFile characterAtIndex:([ignoreFile length] - 1)] != '\n')
63 [ignoreFile appendString:@"\n"];
64 [ignoreFile appendString:filesAsString];
67 [ignoreFile writeToFile:gitIgnoreName atomically:YES encoding:enc error:&error];
69 [[commitController.repository windowController] showErrorSheet:error];
72 # pragma mark Context Menu methods
73 - (BOOL) allSelectedCanBeIgnored:(NSArray *)selectedFiles
75 for (PBChangedFile *selectedItem in selectedFiles) {
76 if (selectedItem.status != NEW) {
83 - (NSMenu *) menuForTable:(NSTableView *)table
85 NSMenu *menu = [[NSMenu alloc] init];
86 id controller = [table tag] == 0 ? unstagedFilesController : stagedFilesController;
87 NSArray *selectedFiles = [controller selectedObjects];
90 if ([table tag] == 0) {
91 NSMenuItem *stageItem = [[NSMenuItem alloc] initWithTitle:@"Stage Changes" action:@selector(stageFilesAction:) keyEquivalent:@""];
92 [stageItem setTarget:self];
93 [stageItem setRepresentedObject:selectedFiles];
94 [menu addItem:stageItem];
96 else if ([table tag] == 1) {
97 NSMenuItem *unstageItem = [[NSMenuItem alloc] initWithTitle:@"Unstage Changes" action:@selector(unstageFilesAction:) keyEquivalent:@""];
98 [unstageItem setTarget:self];
99 [unstageItem setRepresentedObject:selectedFiles];
100 [menu addItem:unstageItem];
103 NSString *title = [selectedFiles count] == 1 ? @"Open file" : @"Open files";
104 NSMenuItem *openItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(openFilesAction:) keyEquivalent:@""];
105 [openItem setTarget:self];
106 [openItem setRepresentedObject:selectedFiles];
107 [menu addItem:openItem];
110 if ([self allSelectedCanBeIgnored:selectedFiles]) {
111 NSString *ignoreText = [selectedFiles count] == 1 ? @"Ignore File": @"Ignore Files";
112 NSMenuItem *ignoreItem = [[NSMenuItem alloc] initWithTitle:ignoreText action:@selector(ignoreFilesAction:) keyEquivalent:@""];
113 [ignoreItem setTarget:self];
114 [ignoreItem setRepresentedObject:selectedFiles];
115 [menu addItem:ignoreItem];
118 if ([selectedFiles count] == 1) {
119 NSMenuItem *showInFinderItem = [[NSMenuItem alloc] initWithTitle:@"Show in Finder" action:@selector(showInFinderAction:) keyEquivalent:@""];
120 [showInFinderItem setTarget:self];
121 [showInFinderItem setRepresentedObject:selectedFiles];
122 [menu addItem:showInFinderItem];
125 for (PBChangedFile *file in selectedFiles)
126 if (!file.hasUnstagedChanges)
129 NSMenuItem *discardItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes…" action:@selector(discardFilesAction:) keyEquivalent:@""];
130 [discardItem setTarget:self];
131 [discardItem setAlternate:NO];
132 [discardItem setRepresentedObject:selectedFiles];
134 [menu addItem:discardItem];
136 NSMenuItem *discardForceItem = [[NSMenuItem alloc] initWithTitle:@"Discard changes" action:@selector(forceDiscardFilesAction:) keyEquivalent:@""];
137 [discardForceItem setTarget:self];
138 [discardForceItem setAlternate:YES];
139 [discardForceItem setRepresentedObject:selectedFiles];
140 [discardForceItem setKeyEquivalentModifierMask:NSAlternateKeyMask];
141 [menu addItem:discardForceItem];
146 - (void) stageFilesAction:(id) sender
148 [commitController.index stageFiles:[sender representedObject]];
151 - (void) unstageFilesAction:(id) sender
153 [commitController.index unstageFiles:[sender representedObject]];
156 - (void) openFilesAction:(id) sender
158 NSArray *files = [sender representedObject];
159 NSString *workingDirectory = [commitController.repository workingDirectory];
160 for (PBChangedFile *file in files)
161 [[NSWorkspace sharedWorkspace] openFile:[workingDirectory stringByAppendingPathComponent:[file path]]];
164 - (void) ignoreFilesAction:(id) sender
166 NSArray *selectedFiles = [sender representedObject];
167 if ([selectedFiles count] == 0)
170 [self ignoreFiles:selectedFiles];
171 [commitController.index refresh];
174 - (void)discardFilesAction:(id) sender
176 NSArray *selectedFiles = [sender representedObject];
177 if ([selectedFiles count] > 0)
178 [self discardChangesForFiles:selectedFiles force:FALSE];
181 - (void)forceDiscardFilesAction:(id) sender
183 NSArray *selectedFiles = [sender representedObject];
184 if ([selectedFiles count] > 0)
185 [self discardChangesForFiles:selectedFiles force:TRUE];
188 - (void) showInFinderAction:(id) sender
190 NSArray *selectedFiles = [sender representedObject];
191 if ([selectedFiles count] == 0)
193 NSString *workingDirectory = [[commitController.repository workingDirectory] stringByAppendingString:@"/"];
194 NSString *path = [workingDirectory stringByAppendingPathComponent:[[selectedFiles objectAtIndex:0] path]];
195 NSWorkspace *ws = [NSWorkspace sharedWorkspace];
196 [ws selectFile: path inFileViewerRootedAtPath:nil];
199 - (void)discardChangesForFiles:(NSArray *)files force:(BOOL)force
202 int ret = [[NSAlert alertWithMessageText:@"Discard changes"
204 alternateButton:@"Cancel"
206 informativeTextWithFormat:@"Are you sure you wish to discard the changes to this file?\n\nYou cannot undo this operation."] runModal];
207 if (ret != NSAlertDefaultReturn)
211 [commitController.index discardChangesForFiles:files];
214 # pragma mark TableView icon delegate
215 - (void)tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)rowIndex
217 id controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController;
218 [[tableColumn dataCell] setImage:[[[controller arrangedObjects] objectAtIndex:rowIndex] icon]];
221 - (void) tableClicked:(NSTableView *) tableView
223 NSArrayController *controller = [tableView tag] == 0 ? unstagedFilesController : stagedFilesController;
225 NSIndexSet *selectionIndexes = [tableView selectedRowIndexes];
226 NSArray *files = [[controller arrangedObjects] objectsAtIndexes:selectionIndexes];
227 if ([tableView tag] == 0)
228 [commitController.index stageFiles:files];
230 [commitController.index unstageFiles:files];
233 - (void) rowClicked:(NSCell *)sender
235 NSTableView *tableView = (NSTableView *)[sender controlView];
236 if([tableView numberOfSelectedRows] != 1)
238 [self tableClicked: tableView];
241 - (BOOL)tableView:(NSTableView *)tv
242 writeRowsWithIndexes:(NSIndexSet *)rowIndexes
243 toPasteboard:(NSPasteboard*)pboard
245 // Copy the row numbers to the pasteboard.
246 [pboard declareTypes:[NSArray arrayWithObjects:FileChangesTableViewType, NSFilenamesPboardType, nil] owner:self];
248 // Internal, for dragging from one tableview to the other
249 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
250 [pboard setData:data forType:FileChangesTableViewType];
252 // External, to drag them to for example XCode or Textmate
253 NSArrayController *controller = [tv tag] == 0 ? unstagedFilesController : stagedFilesController;
254 NSArray *files = [[controller arrangedObjects] objectsAtIndexes:rowIndexes];
255 NSString *workingDirectory = [commitController.repository workingDirectory];
257 NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:[rowIndexes count]];
258 for (PBChangedFile *file in files)
259 [filenames addObject:[workingDirectory stringByAppendingPathComponent:[file path]]];
261 [pboard setPropertyList:filenames forType:NSFilenamesPboardType];
265 - (NSDragOperation)tableView:(NSTableView*)tableView
266 validateDrop:(id <NSDraggingInfo>)info
267 proposedRow:(NSInteger)row
268 proposedDropOperation:(NSTableViewDropOperation)operation
270 if ([info draggingSource] == tableView)
271 return NSDragOperationNone;
273 [tableView setDropRow:-1 dropOperation:NSTableViewDropOn];
274 return NSDragOperationCopy;
277 - (BOOL)tableView:(NSTableView *)aTableView
278 acceptDrop:(id <NSDraggingInfo>)info
280 dropOperation:(NSTableViewDropOperation)operation
282 NSPasteboard* pboard = [info draggingPasteboard];
283 NSData* rowData = [pboard dataForType:FileChangesTableViewType];
284 NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
286 NSArrayController *controller = [aTableView tag] == 0 ? stagedFilesController : unstagedFilesController;
287 NSArray *files = [[controller arrangedObjects] objectsAtIndexes:rowIndexes];
289 if ([aTableView tag] == 0)
290 [commitController.index unstageFiles:files];
292 [commitController.index stageFiles:files];