test/index: add more tests
[GitX.git] / PBGitIndexController.m
blob5d2a949e1608823fd04890bc3876f9eb33480d2f
1 //
2 //  PBGitIndexController.m
3 //  GitX
4 //
5 //  Created by Pieter de Bie on 18-11-08.
6 //  Copyright 2008 Pieter de Bie. All rights reserved.
7 //
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;
18 @end
20 @implementation PBGitIndexController
22 - (void)awakeFromNib
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];
43         }
44         NSString *filesAsString = [fileList componentsJoinedByString:@"\n"];
46         // Write to the file
47         NSString *gitIgnoreName = [commitController.repository gitIgnoreFilename];
49         NSStringEncoding enc = NSUTF8StringEncoding;
50         NSError *error = nil;
51         NSMutableString *ignoreFile;
53         if (![[NSFileManager defaultManager] fileExistsAtPath:gitIgnoreName]) {
54                 ignoreFile = [filesAsString mutableCopy];
55         } else {
56                 ignoreFile = [NSMutableString stringWithContentsOfFile:gitIgnoreName usedEncoding:&enc error:&error];
57                 if (error) {
58                         [[commitController.repository windowController] showErrorSheet:error];
59                         return;
60                 }
61                 // Add a newline if not yet present
62                 if ([ignoreFile characterAtIndex:([ignoreFile length] - 1)] != '\n')
63                         [ignoreFile appendString:@"\n"];
64                 [ignoreFile appendString:filesAsString];
65         }
67         [ignoreFile writeToFile:gitIgnoreName atomically:YES encoding:enc error:&error];
68         if (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) {
77                         return NO;
78                 }
79         }
80         return YES;
83 - (NSMenu *) menuForTable:(NSTableView *)table
85         NSMenu *menu = [[NSMenu alloc] init];
86         id controller = [table tag] == 0 ? unstagedFilesController : stagedFilesController;
87         NSArray *selectedFiles = [controller selectedObjects];
89         // Unstaged changes
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];
95         }
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];
101         }
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];
109         // Attempt to ignore
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];
116         }
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];
123     }
125         for (PBChangedFile *file in selectedFiles)
126                 if (!file.hasUnstagedChanges)
127                         return menu;
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];
142         
143         return menu;
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)
168                 return;
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)
192                 return;
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
201         if (!force) {
202                 int ret = [[NSAlert alertWithMessageText:@"Discard changes"
203                                                                    defaultButton:nil
204                                                                  alternateButton:@"Cancel"
205                                                                          otherButton:nil
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)
208                         return;
209         }
210         
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];
229         else
230                 [commitController.index unstageFiles:files];
233 - (void) rowClicked:(NSCell *)sender
235         NSTableView *tableView = (NSTableView *)[sender controlView];
236         if([tableView numberOfSelectedRows] != 1)
237                 return;
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];
262     return YES;
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
279                           row:(NSInteger)row
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];
291         else
292                 [commitController.index stageFiles:files];
294         return YES;
297 @end