forked from ImageOptim/ImageOptim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMyTableView.m
More file actions
268 lines (221 loc) · 9.49 KB
/
MyTableView.m
File metadata and controls
268 lines (221 loc) · 9.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#import "MyTableView.h"
#import "FilesController.h"
#import "RevealButtonCell.h"
#import "File.h"
#import "log.h"
@implementation MyTableView
-(void)removeObjects:(NSArray *)objects {
FilesController *f = (FilesController*)[self delegate];
[[self undoManager] registerUndoWithTarget:self selector:@selector(addObjects:) object:objects];
[f removeObjects:objects];
}
-(void)addObjects:(NSArray *)objects {
FilesController *f = (FilesController*)[self delegate];
[[self undoManager] registerUndoWithTarget:self selector:@selector(removeObjects:) object:objects];
[f addObjects:objects];
}
- (IBAction)delete:(id)sender {
FilesController *f = (FilesController*)[self delegate];
[self removeObjects:[f selectedObjects]];
}
- (IBAction)copy:(id)sender {
FilesController *f = (FilesController*)[self delegate];
NSArray *selected = [f selectedObjects];
NSMutableArray *filePaths = [NSMutableArray arrayWithCapacity:[selected count]];
NSMutableArray *fileNames = [NSMutableArray arrayWithCapacity:[selected count]];
for(File *file in selected) {
NSString *path = file.filePath.path;
[filePaths addObject:path];
[fileNames addObject:[path.lastPathComponent stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
};
if ([filePaths count]) {
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard declareTypes:@[NSFilenamesPboardType, NSStringPboardType] owner:self];
[pboard setPropertyList:filePaths forType:NSFilenamesPboardType];
[pboard setString:[fileNames componentsJoinedByString:@"\n"] forType:NSStringPboardType];
}
}
-(NSArray*)filesForDataURI {
FilesController *f = (FilesController*)[self delegate];
NSArray *selectedFiles = [f selectedObjects];
NSMutableArray *files = [NSMutableArray arrayWithCapacity:[selectedFiles count]];
NSUInteger totalSize = 0;
for(File *file in selectedFiles) {
if (![file isDone] || !file.byteSizeOptimized) continue;
if (file.byteSizeOptimized > 100000) continue;
totalSize += file.byteSizeOptimized;
if (totalSize > 1000000) break;
[files addObject:file];
}
return files;
}
- (IBAction)copyAsDataURI:(id)sender {
NSMutableArray *urls = [NSMutableArray new];
for(File *file in [self filesForDataURI]) {
NSData *data = [NSData dataWithContentsOfURL:file.filePath];
NSString *type = [file mimeType];
if (!type) continue;
NSString *url = [[NSString stringWithFormat:@"data:%@;base64,", type]
stringByAppendingString:[data base64Encoding]];
[urls addObject:url];
}
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard declareTypes:@[NSStringPboardType] owner:nil];
[pboard setString:[urls componentsJoinedByString:@"\n"] forType:NSStringPboardType];
}
- (IBAction)cut:(id)sender
{
[self copy:sender];
[self delete:sender];
[[self undoManager] setActionName:NSLocalizedString(@"Cut",@"undo command name")];
}
- (IBAction)paste:(id)sender
{
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:[paths count]];
for(NSString *path in paths) {
[urls addObject:[NSURL fileURLWithPath:path]];
}
FilesController *f = (FilesController*)[self delegate];
[f addURLsBelowSelection:urls];
}
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
SEL action = [menuItem action];
if (action == @selector(delete:) || action == @selector(copy:) || action == @selector(cut:)) {
return [self numberOfSelectedRows] > 0;
} else if (action == @selector(copyAsDataURI:)) {
NSData *data = [NSData data];
return [data respondsToSelector:@selector(base64Encoding)] && [self numberOfSelectedRows] > 0 && [[self filesForDataURI] count] > 0;
} else if (action == @selector(paste:)) {
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
return [paths count]>0;
} else if (action == @selector(selectAll:)) {
return [self numberOfRows]>0;
}
return [menuItem isEnabled];
}
- (void)keyDown:(NSEvent *)theEvent {
if (![theEvent isARepeat] && [self numberOfSelectedRows]) {
switch ([theEvent keyCode]) {
case 49: /*space*/
[self quickLook];
return;
case 51: /*backspace*/
case 117: /*delete*/
[self delete:self];
return;
}
}
[super keyDown:theEvent];
}
// Tracking rect support
- (void)updateTrackingAreas {
for (NSTrackingArea *area in [self trackingAreas]) {
// We have to uniquely identify our own tracking areas
if (([area owner] == self) && ([area userInfo][@"Row"] != nil)) {
[self removeTrackingArea:area];
}
}
// Find the visible cells that have a non-empty tracking rect and add rects for each of them
NSRange visibleRows = [self rowsInRect:[self visibleRect]];
NSIndexSet *visibleColIndexes = [self columnIndexesInRect:[self visibleRect]];
CGRect rect = [self.window convertRectFromScreen:(CGRect){
.origin = [NSEvent mouseLocation],
}];
NSPoint mouseLocation = [self convertPoint:rect.origin fromView:nil];
for (NSInteger row = visibleRows.location; row < visibleRows.location + visibleRows.length; row++) {
// If it is a "full width" cell, we don't have to go through the rows
for (NSInteger col = [visibleColIndexes firstIndex]; col != NSNotFound; col = [visibleColIndexes indexGreaterThanIndex:col]) {
NSCell *cell = [self preparedCellAtColumn:col row:row];
if ([cell isKindOfClass:[RevealButtonCell class]]) {
RevealButtonCell *imagecell = (id)cell;
NSDictionary *userInfo = @{@"Col": @(col),
@"Row": @(row)};
[imagecell addTrackingAreasForView:self inRect:[self frameOfCellAtColumn:col row:row]
withUserInfo:userInfo mouseLocation:mouseLocation];
}
}
}
}
-(void)setMouseEntered:(BOOL)entered fromEvent:(NSEvent *)event {
// Delegate this to the appropriate cell. In order to allow the cell to maintain state, we copy it and use the copy until the mouse is moved outside of the cell.
NSDictionary *userInfo = [event userData];
NSNumber *row = [userInfo valueForKey:@"Row"];
NSNumber *col = [userInfo valueForKey:@"Col"];
if (row && col) {
NSInteger rowVal = [row integerValue], colVal = [col integerValue];
RevealButtonCell *cell = (RevealButtonCell *)[self preparedCellAtColumn:colVal row:rowVal];
assert([cell isKindOfClass:[RevealButtonCell class]]);
if (iMouseCell != cell) {
iMouseCol = colVal;
iMouseRow = rowVal;
// Store a COPY of the cell for use when tracking in an area
iMouseCell = cell = [cell copy];
}
[cell setMouseEntered:entered];
[self updateCell:cell];
}
}
- (void)mouseEntered:(NSEvent *)event {
[self setMouseEntered:YES fromEvent:event];
}
- (void)mouseExited:(NSEvent *)event {
[self setMouseEntered:NO fromEvent:event];
iMouseCell = nil;
iMouseCol = -1;
iMouseRow = -1;
}
/* Since NSTableView/NSOutineView uses the same cell to "stamp" out each row, we need to send the mouseEntered/mouseExited events each time it is drawn. The easy hook for this is the preparedCell method.
*/
- (NSCell *)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row {
// We check if the selectedCell is nil or not -- the selectedCell is a cell that is currently being edited or tracked. We don't want to return our override if we are in that state.
if (iMouseCell && [self selectedCell] == nil && (row == iMouseRow) && (column == iMouseCol)) {
return iMouseCell;
} else {
return [super preparedCellAtColumn:column row:row];
}
}
/* In order for the cell to properly update itself with an "updateCell:" call, we must handle the "mouseCell" as a special case
*/
- (void)updateCell:(NSCell *)aCell {
if (aCell == iMouseCell) {
[self setNeedsDisplayInRect:[self frameOfCellAtColumn:iMouseCol row:iMouseRow]];
} else {
[super updateCell:aCell];
}
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
if (isLocal) return NSDragOperationMove;
return NSDragOperationCopy | NSDragOperationGeneric;
}
-(void) quickLook {
if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) {
[[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
} else {
[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
}
}
-(void)awakeFromNib {
[self setDoubleAction:@selector(openInFinder:)];
}
-(NSArray *)clickedRowSelection {
FilesController *fc = (FilesController *)[self delegate];
assert([fc isKindOfClass:[FilesController class]]);
NSInteger row = [self clickedRow];
if (row < 0) {
return nil;
} else if ([self isRowSelected:row] && [self numberOfSelectedRows] > 1) {
return [fc selectedObjects];
} else {
return @[[fc arrangedObjects][row]];
}
}
-(void)openInFinder:(id)sender {
NSArray *files = [self clickedRowSelection];
if (!files) return; // double-click on header
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:[files valueForKey:@"filePath"]];
}
@end