OSDN Git Service

スクリーンショット関連の初期値を設定
[kcd/KCD.git] / KCD / HMScreenshotListViewController.m
1 //
2 //  ViewController.m
3 //  testScreenshotForKCD
4 //
5 //  Created by Hori,Masaki on 2016/03/27.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 #import "HMScreenshotListViewController.h"
10
11 #import "HMScreenshotModel.h"
12 #import "HMScreenshotInformation.h"
13
14 #import "HMAppDelegate.h"
15 #import "HMUserDefaults.h"
16
17 #import <Quartz/Quartz.h>
18
19 @interface NSFileManager (KCDExtension)
20 - (NSString *)_web_pathWithUniqueFilenameForPath:(NSString *)path;
21 @end
22
23 @interface HMCacheVersionInfo : NSObject <NSCopying>
24 @property (strong) NSString *fullpath;
25 @property (strong) NSNumber *version;
26 @end
27
28 @implementation HMCacheVersionInfo
29
30 - (instancetype)copyWithZone:(NSZone *)zone
31 {
32         HMCacheVersionInfo *result = [[self class] new];
33         result.fullpath = self.fullpath;
34         result.version = self.version;
35         return result;
36 }
37 - (NSUInteger)hash
38 {
39         return [self.fullpath hash];
40 }
41 - (BOOL)isEqual:(id)object
42 {
43         if([super isEqual:object]) return YES;
44         if(![object isMemberOfClass:[self class]]) return NO;
45         return [self.fullpath isEqualToString:[object fullpath]];
46 }
47 @end
48
49
50 @interface HMScreenshotListViewController () <NSSplitViewDelegate>
51
52 @property (weak, nonatomic) IBOutlet NSArrayController *screenshotsController;
53 @property (strong) NSMutableArray *deletedPaths;
54
55 @property (weak, nonatomic) IBOutlet IKImageBrowserView *browser;
56 @property (strong, nonatomic) IBOutlet NSMenu *contextMenu;
57 @property (weak, nonatomic) IBOutlet NSButton *shareButton;
58
59
60 @property (weak, nonatomic) IBOutlet NSView *standardView;
61 @property (weak, nonatomic) IBOutlet NSView *editorView;
62
63 - (void)reloadData;
64
65 @end
66
67 @implementation HMScreenshotListViewController
68
69 - (nullable instancetype)initWithCoder:(NSCoder *)coder
70 {
71         self = [super initWithCoder:coder];
72         if(self) {
73                 _screenshots = [HMScreenshotModel new];
74                 _screenshots.screenshots = [self loadCache];
75                 _deletedPaths = [NSMutableArray new];
76         }
77         return self;
78 }
79
80 - (void)viewDidLoad {
81         [super viewDidLoad];
82         
83         [self.browser setCanControlQuickLookPanel:YES];
84
85         NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO];
86         self.screenshots.sortDescriptors = @[sortDescriptor];
87         self.screenshots.selectedIndexes = [NSIndexSet indexSetWithIndex:0];
88         
89         [self performSelector:@selector(reloadData:)
90                            withObject:nil
91                            afterDelay:0.0];
92 }
93
94 - (NSString *)screenshotSaveDirectoryPath
95 {
96         HMAppDelegate *appDelegate =[[NSApplication sharedApplication] delegate];
97         NSString *parentDirctory = appDelegate.screenShotSaveDirectory;
98         NSBundle *mainBundle = [NSBundle mainBundle];
99         NSDictionary *localizedInfoDictionary = [mainBundle localizedInfoDictionary];
100         NSString *saveDirectoryName = localizedInfoDictionary[@"CFBundleName"];
101         NSString *path = [parentDirctory stringByAppendingPathComponent:saveDirectoryName];
102         
103         NSFileManager *fm = [NSFileManager defaultManager];
104         BOOL isDir = NO;
105         NSError *error = nil;
106         if(![fm fileExistsAtPath:path isDirectory:&isDir]) {
107                 BOOL ok = [fm createDirectoryAtPath:path
108                                 withIntermediateDirectories:NO
109                                                                  attributes:nil
110                                                                           error:&error];
111                 if(!ok) {
112                         NSLog(@"Can not create screenshot save directory.");
113                         return parentDirctory;
114                 }
115         } else if(!isDir) {
116                 NSLog(@"%@ is regular file, not direcory.", path);
117                 return parentDirctory;
118         }
119         
120         return path;
121 }
122
123 - (void)registerScreenshot:(NSBitmapImageRep *)image fromOnScreen:(NSRect)screenRect
124 {
125         dispatch_queue_t queue = dispatch_queue_create("Screenshot queue", DISPATCH_QUEUE_SERIAL);
126         dispatch_async(queue, ^{
127                 NSData *imageData = [image representationUsingType:NSJPEGFileType properties:@{}];
128                 
129                 NSBundle *mainBundle = [NSBundle mainBundle];
130                 NSDictionary *infoList = [mainBundle localizedInfoDictionary];
131                 NSString *filename = [infoList objectForKey:@"CFBundleName"];
132                 if([filename length] == 0) {
133                         filename = @"KCD";
134                 }
135                 filename = [filename stringByAppendingPathExtension:@"jpg"];
136                 NSString *path = [[self screenshotSaveDirectoryPath] stringByAppendingPathComponent:filename];
137                 path = [[NSFileManager defaultManager] _web_pathWithUniqueFilenameForPath:path];
138                 [imageData writeToFile:path atomically:NO];
139                 
140                 dispatch_async(dispatch_get_main_queue(), ^{
141                         HMScreenshotInformation *info = [HMScreenshotInformation new];
142                         info.path = path;
143                         info.version = [self cacheVersionForPath:path];
144                         
145                         [self.screenshotsController insertObject:info atArrangedObjectIndex:0];
146                         self.screenshotsController.selectedObjects = @[info];
147                         
148                         if(HMStandardDefaults.showsListWindowAtScreenshot) {
149                                 [self.view.window makeKeyAndOrderFront:nil];
150                         }
151                         [self saveCache];
152                 });
153         });
154 }
155
156 - (void)reloadData
157 {
158         NSMutableArray *screenshotNames = [NSMutableArray new];
159         NSMutableArray *currentArray = [self.screenshots.screenshots mutableCopy];
160         NSFileManager *fm = [NSFileManager defaultManager];
161         
162         NSError *error = nil;
163         NSArray *files = [fm contentsOfDirectoryAtPath:self.screenshotSaveDirectoryPath error:&error];
164         if(error) {
165                 NSLog(@"%s: error -> %@", __PRETTY_FUNCTION__, error);
166         }
167         
168         NSArray *imageTypes = [NSImage imageTypes];
169         NSWorkspace *ws = [NSWorkspace sharedWorkspace];
170         for(NSString *file in files) {
171                 NSString *fullpath = [self.screenshotSaveDirectoryPath stringByAppendingPathComponent:file];
172                 NSString *fileType = [ws typeOfFile:fullpath error:NULL];
173                 if([imageTypes containsObject:fileType]) {
174                         [screenshotNames addObject:fullpath];
175                 }
176         }
177         
178         // 無くなっているものを調べる
179         NSMutableArray *deleteObjects = [NSMutableArray new];
180         for(HMScreenshotInformation *info in self.screenshots.screenshots) {
181                 if(![screenshotNames containsObject:info.path]) {
182                         [self incrementCacheVersionForPath:info.path];
183                         [deleteObjects addObject:info];
184                 }
185         }
186         [currentArray removeObjectsInArray:deleteObjects];
187         
188         // 新しいものを調べる
189         for(NSString *path in screenshotNames) {
190                 NSUInteger index = [self.screenshots.screenshots indexOfObjectPassingTest:^(HMScreenshotInformation *obj, NSUInteger idx, BOOL *stop) {
191                         if([path isEqualToString:obj.path]) {
192                                 *stop = YES;
193                                 return YES;
194                         }
195                         return NO;
196                 }];
197                 if(index == NSNotFound) {
198                         HMScreenshotInformation *info = [HMScreenshotInformation new];
199                         info.path = path;
200                         [currentArray addObject:info];
201                 }
202         }
203         self.screenshots.screenshots = [currentArray copy];
204         self.screenshots.selectedIndexes = [NSIndexSet indexSetWithIndex:0];
205         
206         [self saveCache];
207 }
208
209 - (void)saveCache
210 {
211         NSString *path = [self screenshotSaveDirectoryPath];
212         path = [path stringByAppendingPathComponent:@"Cache.db"];
213         NSURL *url = [NSURL fileURLWithPath:path];
214         
215         NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.screenshots.screenshots];
216         NSError *error = nil;
217         BOOL ok = [data writeToURL:url
218                                            options:NSDataWritingAtomic
219                                                  error:&error];
220         if(!ok) {
221                 if(error) {
222                         [[NSApplication sharedApplication] presentError:error];
223                 }
224                 NSLog(@"Can not write error. %@", error);
225         }
226 }
227
228 - (NSArray *)loadCache
229 {
230         NSString *path = [self screenshotSaveDirectoryPath];
231         path = [path stringByAppendingPathComponent:@"Cache.db"];
232         NSURL *url = [NSURL fileURLWithPath:path];
233         
234         NSData *data = [NSData dataWithContentsOfURL:url];
235         if(!data) return [NSArray new];
236         
237         id loaded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
238         if(![loaded isKindOfClass:[NSArray class]]) {
239                 return [NSArray new];
240         }
241         
242         return loaded;
243 }
244
245 - (IBAction)reloadData:(id)sender
246 {
247         [self reloadData];
248 }
249
250 - (IBAction)delete:(id)sender
251 {
252         NSArray<HMScreenshotInformation *> *informations = [self.screenshotsController.selectedObjects copy];
253         NSMutableArray<NSString *> *paths = [NSMutableArray array];
254         for(HMScreenshotInformation *info in informations) {
255                 [paths addObject:info.path];
256         }
257         NSMutableArray<NSString *> *opsixPathes = [NSMutableArray array];
258         for(NSString *path in paths) {
259                 [opsixPathes addObject:[NSString stringWithFormat:@"(\"%@\" as POSIX file)", path]];
260         }
261         NSString *pathListString = [opsixPathes componentsJoinedByString:@" , "];
262         pathListString = [@"{ " stringByAppendingString:pathListString];
263         pathListString = [pathListString stringByAppendingString:@" }"];
264         
265         NSString *scriptTmplate =
266         @"tell application \"Finder\"\n"
267         @"      delete  %@\n"
268         @"end tell";
269         NSString *script = [NSString stringWithFormat:scriptTmplate, pathListString];
270         NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:script];
271         if(!appleScript) NSBeep();
272         [appleScript executeAndReturnError:nil];
273         
274         NSIndexSet *selectionIndexes = self.screenshotsController.selectionIndexes;
275         [self.screenshotsController removeObjectsAtArrangedObjectIndexes:selectionIndexes];
276         for(NSString *path in paths) {
277                 [self incrementCacheVersionForPath:path];
278         }
279         [self saveCache];
280         
281         NSInteger selectionIndex = selectionIndexes.firstIndex;
282         NSUInteger count = [self.screenshotsController.arrangedObjects count];
283         if(count == 0) return;
284         if(count <= selectionIndex) {
285                 selectionIndex = count - 1;
286         }
287         self.screenshotsController.selectionIndex = selectionIndex;
288 }
289 - (IBAction)revealInFinder:(id)sender
290 {
291         NSArray<HMScreenshotInformation *> *informations = [self.screenshotsController.selectedObjects copy];
292         NSMutableArray<NSString *> *paths = [NSMutableArray array];
293         for(HMScreenshotInformation *info in informations) {
294                 [paths addObject:info.path];
295         }
296         NSMutableArray<NSURL *> *pathURLs = [NSMutableArray array];
297         for(NSString *path in paths) {
298                 [pathURLs addObject:[NSURL fileURLWithPath:path]];
299         }
300         
301         NSWorkspace *ws = [NSWorkspace sharedWorkspace];
302         [ws activateFileViewerSelectingURLs:pathURLs];
303 }
304
305 - (IBAction)registerImage:(id)sender
306 {
307         if(![sender respondsToSelector:@selector(image)]) return;
308         
309         NSImage *image = [sender image];
310         
311         dispatch_queue_t queue = dispatch_queue_create("Screenshot queue", DISPATCH_QUEUE_SERIAL);
312         dispatch_async(queue, ^{
313                 NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:image.TIFFRepresentation];
314                 NSData *imageData = [imageRep representationUsingType:NSJPEGFileType properties:@{}];
315                 
316                 NSBundle *mainBundle = [NSBundle mainBundle];
317                 NSDictionary *infoList = [mainBundle localizedInfoDictionary];
318                 NSString *filename = [infoList objectForKey:@"CFBundleName"];
319                 if([filename length] == 0) {
320                         filename = @"KCD";
321                 }
322                 filename = [filename stringByAppendingPathExtension:@"jpg"];
323                 NSString *path = [[self screenshotSaveDirectoryPath] stringByAppendingPathComponent:filename];
324                 path = [[NSFileManager defaultManager] _web_pathWithUniqueFilenameForPath:path];
325                 [imageData writeToFile:path atomically:NO];
326                 
327                 dispatch_async(dispatch_get_main_queue(), ^{
328                         HMScreenshotInformation *info = [HMScreenshotInformation new];
329                         info.path = path;
330                         info.version = [self cacheVersionForPath:path];
331                         
332                         [self.screenshotsController insertObject:info atArrangedObjectIndex:0];
333                         self.screenshotsController.selectedObjects = @[info];
334                         
335                         if(HMStandardDefaults.showsListWindowAtScreenshot) {
336                                 [self.view.window makeKeyAndOrderFront:nil];
337                         }
338                         [self saveCache];
339                 });
340         });
341 }
342
343 - (void)incrementCacheVersionForPath:(NSString *)fullpath
344 {
345         NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fullpath = %@", fullpath];
346         NSArray *filteredArray = [self.deletedPaths filteredArrayUsingPredicate:predicate];
347         if(filteredArray.count == 0) {
348                 HMCacheVersionInfo *info = [HMCacheVersionInfo new];
349                 info.fullpath = fullpath;
350                 info.version = @(1);
351                 [self.deletedPaths addObject:info];
352         } else {
353                 HMCacheVersionInfo *info = filteredArray[0];
354                 info.version = @(info.version.unsignedIntegerValue + 1);
355         }
356 }
357 - (NSUInteger)cacheVersionForPath:(NSString *)fullpath
358 {
359         NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fullpath = %@", fullpath];
360         NSArray *filteredArray = [self.deletedPaths filteredArrayUsingPredicate:predicate];
361         if(filteredArray.count == 0) {
362                 return 0;
363         }
364         
365         HMCacheVersionInfo *cacheInfo = filteredArray[0];
366         return cacheInfo.version.unsignedIntegerValue;
367 }
368
369
370 - (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(nullable id)sender
371 {
372         NSViewController *v = segue.destinationController;
373         v.representedObject = self.screenshots;
374 }
375
376 #pragma mark - NSSplitViewDelegate
377
378 const CGFloat leftMinWidth = 299;
379 const CGFloat rightMinWidth = 400;
380
381 - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
382 {
383         if(dividerIndex == 0) {
384                 return leftMinWidth;
385         }
386         return proposedMinimumPosition;
387 }
388 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex
389 {
390         if(dividerIndex == 0) {
391                 NSSize size = splitView.frame.size;
392                 CGFloat rightWidth = size.width - proposedPosition;
393                 if(rightWidth < rightMinWidth) {
394                         return size.width - rightMinWidth;
395                 }
396         }
397         return proposedPosition;
398 }
399 - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize
400 {
401         [splitView adjustSubviews];
402         
403         NSView *leftView = splitView.subviews[0];
404         NSView *rightView = splitView.subviews[1];
405         
406         if(NSWidth(leftView.frame) < leftMinWidth) {
407                 NSRect leftRect = leftView.frame;
408                 leftRect.size.width = leftMinWidth;
409                 leftView.frame = leftRect;
410                 
411                 NSRect rightRect = rightView.frame;
412                 rightRect.size.width = NSWidth(splitView.frame) - NSWidth(leftRect) - splitView.dividerThickness;
413                 rightRect.origin.x = NSWidth(leftRect) + splitView.dividerThickness;
414                 rightView.frame = rightRect;
415         }
416 }
417 - (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view
418 {
419         NSView *leftView = splitView.subviews[0];
420         NSView *rightView = splitView.subviews[1];
421         
422         if(leftView == view) {
423                 if(NSWidth(leftView.frame) < leftMinWidth) return NO;
424         }
425         if(rightView == view) {
426                 if(NSWidth(leftView.frame) >= leftMinWidth) return NO;
427         }
428         
429         return YES;
430 }
431
432
433 #pragma mark - IKImageBrowserDelegate
434 - (void)imageBrowser:(IKImageBrowserView *) aBrowser cellWasRightClickedAtIndex:(NSUInteger) index withEvent:(NSEvent *) event
435 {
436         [NSMenu popUpContextMenu:self.contextMenu withEvent:event forView:aBrowser];
437 }
438
439 @end