├── .gitignore ├── DuplicateScanner.xcodeproj └── project.pbxproj ├── FindDuplicateFiles ├── AppDelegate.h ├── AppDelegate.m ├── DuplicateScanner-Info.plist ├── DuplicateScanner-Prefix.pch ├── FindDuplicateFiles.entitlements ├── INAppStoreWindow.h ├── INAppStoreWindow.m ├── NSArray+Remove.h ├── NSArray+Remove.m ├── NSString+Size.h ├── NSString+Size.m ├── PXListView │ ├── Cells │ │ ├── PXListViewCell+Private.h │ │ ├── PXListViewCell.h │ │ ├── PXListViewCell.m │ │ └── PXListViewDropHighlight.h │ ├── Misc │ │ ├── PXListDocumentView.h │ │ └── PXListDocumentView.m │ ├── PXListView+Private.h │ ├── PXListView+UserInteraction.h │ ├── PXListView+UserInteraction.m │ ├── PXListView.h │ ├── PXListView.m │ └── PXListViewDelegate.h ├── THBackgroundView.h ├── THBackgroundView.m ├── THBarView.h ├── THBarView.m ├── THDuplicateCell.h ├── THDuplicateCell.m ├── THDuplicationHelper.h ├── THDuplicationHelper.m ├── THFileUtility.h ├── THFileUtility.m ├── THHeaderView │ ├── McFlipView.h │ ├── McFlipView.m │ ├── McHeaderView.h │ ├── McHeaderView.m │ ├── McHeaderViewController.h │ ├── McHeaderViewController.m │ ├── McHeaderViewController.xib │ ├── McImageHeaderView.h │ └── McImageHeaderView.m ├── THLock.h ├── THLock.m ├── THPathSelectedViewController │ ├── ImageBrowserBackgroundLayer.h │ ├── ImageBrowserBackgroundLayer.m │ ├── ImageBrowserCell.h │ ├── ImageBrowserCell.m │ ├── ImageBrowserView.h │ ├── ImageBrowserView.m │ ├── THPathSelectedViewController.h │ ├── THPathSelectedViewController.m │ ├── THPathSelectedViewController.xib │ ├── Utilities.h │ ├── Utilities.m │ ├── glossy.png │ ├── metal_background.png │ ├── metal_background@2x.png │ ├── pin.png │ └── pin@2x.png ├── THPredicateEditor │ ├── THConditionPredicateEditorRowTemplate.h │ ├── THConditionPredicateEditorRowTemplate.m │ ├── THPredicateEditorViewController.h │ ├── THPredicateEditorViewController.m │ ├── THSizePredicateEditorRowTemplate.h │ ├── THSizePredicateEditorRowTemplate.m │ ├── THStringPredicateEditorRowTemplate.h │ ├── THStringPredicateEditorRowTemplate.m │ ├── en.lproj │ │ └── THPredicateEditorViewController.xib │ ├── zh-Hans.lproj │ │ └── THPredicateEditorViewController.xib │ └── zh-Hant.lproj │ │ └── THPredicateEditorViewController.xib ├── THShadowField.h ├── THShadowField.m ├── THStaticTextField.h ├── THStaticTextField.m ├── THWebService │ ├── THDownloadWebService.h │ ├── THDownloadWebService.m │ ├── THPostWebService.h │ ├── THPostWebService.m │ ├── THWebDefine.h │ ├── THWebService.h │ ├── THWebService.m │ ├── THWebUtility.h │ ├── THWebUtility.m │ └── readme.txt ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── MainMenu.xib ├── main.m ├── zh-Hans.lproj │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── MainMenu.xib └── zh-Hant.lproj │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── MainMenu.xib ├── README.md └── Resources ├── headerBg.png ├── headerBg@2x.png ├── headerLogo.png ├── headerLogo@2x.png ├── icon.icns ├── start.png └── stop.png /.gitignore: -------------------------------------------------------------------------------- 1 | # osx noise 2 | .DS_Store 3 | profile 4 | 5 | # xcode noise 6 | build/* 7 | *.mode1 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspective 11 | *.perspectivev3 12 | *.pbxuser 13 | *.xcworkspace 14 | xcuserdata 15 | 16 | # svn & cvs 17 | .svn 18 | CVS 19 | -------------------------------------------------------------------------------- /FindDuplicateFiles/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-2. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | enum 12 | { 13 | THDuplicationSortDefault = -1, 14 | THDuplicationSortSize, 15 | THDuplicationSortCount, 16 | THDuplicationSortType 17 | }; 18 | typedef NSInteger THDuplicationSort; 19 | 20 | @class McHeaderViewController; 21 | @class McHeaderView; 22 | @class THPredicateEditorViewController; 23 | @class THPathSelectedViewController; 24 | @class THDuplicationHelper; 25 | @class PXListView; 26 | @interface AppDelegate : NSObject 27 | { 28 | THDuplicationHelper *helper; 29 | NSMutableArray *showLists; 30 | NSMutableArray *results; 31 | NSMutableDictionary *resultDictionary; 32 | NSMutableDictionary *tempResultDictionary; 33 | NSTimeInterval notificationInterval; 34 | 35 | NSString *filterString; 36 | THDuplicationSort sortKind; 37 | 38 | IBOutlet NSButton *startButton; 39 | IBOutlet NSButton *stopButton; 40 | 41 | IBOutlet NSView *barView; 42 | IBOutlet NSView *headerDocumentView; 43 | IBOutlet McHeaderView *showHeaderView; 44 | IBOutlet PXListView *resultView; 45 | IBOutlet NSView *filterDocumentView; 46 | IBOutlet NSView *pathDocumentView; 47 | IBOutlet NSProgressIndicator *loadingView; 48 | IBOutlet NSTextField *showResultView; 49 | 50 | McHeaderViewController *headerVC; 51 | THPathSelectedViewController *pathVC; 52 | THPredicateEditorViewController *predicateVC; 53 | } 54 | 55 | @property (assign) IBOutlet NSWindow *window; 56 | - (IBAction)start:(id)sender; 57 | - (IBAction)stop:(id)sender; 58 | - (IBAction)searchClick:(id)sender; 59 | - (IBAction)sortClick:(id)sender; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /FindDuplicateFiles/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-2. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "THFileUtility.h" 11 | #import "THWebUtility.h" 12 | #import "THDuplicationHelper.h" 13 | #import "PXListView.h" 14 | #import "THDuplicateCell.h" 15 | #import "McHeaderViewController.h" 16 | #import "THPathSelectedViewController.h" 17 | #import "THPredicateEditorViewController.h" 18 | #import "THBackgroundView.h" 19 | #import "INAppStoreWindow.h" 20 | 21 | @interface AppDelegate () 22 | 23 | @end 24 | 25 | const NSString *THDuplicationFileExtenstion = @"THDuplicationFileExtenstion"; 26 | 27 | @implementation AppDelegate 28 | 29 | - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag 30 | { 31 | [self.window makeKeyAndOrderFront:nil]; 32 | return YES; 33 | } 34 | 35 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 36 | { 37 | [(INAppStoreWindow *)self.window setTitleBarHeight:NSHeight(barView.frame)]; 38 | [(INAppStoreWindow *)self.window setCenterTrafficLightButtons:NO]; 39 | [barView setFrameOrigin:NSMakePoint(0, 0)]; 40 | [[(INAppStoreWindow *)self.window titleBarView] addSubview:barView]; 41 | 42 | resultView.cellSpacing = 5; 43 | resultView.allowsSelection = NO; 44 | 45 | [startButton setEnabled:YES]; 46 | [stopButton setEnabled:NO]; 47 | 48 | headerVC = [McHeaderViewController sharedController]; 49 | [headerVC.view setFrame:headerDocumentView.bounds]; 50 | [headerVC.view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; 51 | [headerDocumentView addSubview:headerVC.view]; 52 | 53 | predicateVC = [[THPredicateEditorViewController alloc] init]; 54 | [predicateVC.view setFrame:filterDocumentView.bounds]; 55 | [predicateVC.view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; 56 | [filterDocumentView addSubview:predicateVC.view]; 57 | 58 | pathVC = [[THPathSelectedViewController alloc] init]; 59 | [pathVC.view setFrame:pathDocumentView.bounds]; 60 | [pathVC.view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; 61 | [pathDocumentView addSubview:pathVC.view]; 62 | 63 | sortKind = THDuplicationSortDefault; 64 | helper = [[THDuplicationHelper alloc] init]; 65 | helper.notificationQueue = dispatch_queue_create(NULL, NULL); 66 | 67 | [[NSNotificationCenter defaultCenter] addObserver:self 68 | selector:@selector(receiveResults:) 69 | name:THDuplicationNotification 70 | object:helper]; 71 | } 72 | 73 | - (IBAction)start:(id)sender 74 | { 75 | if (helper.searching) 76 | { 77 | return; 78 | } 79 | 80 | //初使化helper 81 | helper.searchPaths = [pathVC filePaths]; 82 | [predicateVC reload]; 83 | helper.extensionsPredicate = [predicateVC extensionPredicate]; 84 | helper.minFileSize = [predicateVC minSize]; 85 | helper.maxFileSize = [predicateVC maxSize]; 86 | helper.filterPackage = ![predicateVC scanPackage]; 87 | 88 | //初使化界面显示 89 | results = [[NSMutableArray alloc] init]; 90 | resultDictionary = [[NSMutableDictionary alloc] init]; 91 | tempResultDictionary = [[NSMutableDictionary alloc] init]; 92 | notificationInterval = 0.0; 93 | 94 | showHeaderView.duration = kMcHeaderDurationForever; 95 | [headerVC addHeaderView:showHeaderView immediately:YES]; 96 | 97 | [startButton setEnabled:NO]; 98 | [stopButton setEnabled:YES]; 99 | [loadingView setHidden:NO]; 100 | [loadingView startAnimation:nil]; 101 | 102 | [self reloadList]; 103 | [helper startSearch]; 104 | } 105 | 106 | - (IBAction)stop:(id)sender 107 | { 108 | [helper stopSearch]; 109 | } 110 | 111 | - (IBAction)searchClick:(id)sender 112 | { 113 | NSString *string = [(NSSearchField *)sender stringValue]; 114 | if ([string length] > 0) 115 | { 116 | filterString = string; 117 | }else 118 | { 119 | filterString = nil; 120 | } 121 | 122 | [self reloadList]; 123 | } 124 | 125 | - (IBAction)sortClick:(id)sender 126 | { 127 | NSInteger idx = [(NSSegmentedControl *)sender selectedSegment]; 128 | if (sortKind == idx) 129 | { 130 | [(NSSegmentedControl *)sender setSelected:NO forSegment:idx]; 131 | sortKind = THDuplicationSortDefault; 132 | }else 133 | { 134 | sortKind = idx; 135 | } 136 | 137 | [self reloadList]; 138 | } 139 | 140 | - (NSString *)idealExtension:(NSArray *)listArray 141 | { 142 | NSString *resultExtension = nil; 143 | NSInteger appearCount = 0; 144 | for (NSString *aFilePath in listArray) 145 | { 146 | NSString *aExtension = [[aFilePath pathExtension] lowercaseString]; 147 | if ([aExtension length] > 0) 148 | { 149 | if (!resultExtension) 150 | { 151 | resultExtension = aExtension; 152 | appearCount = 1; 153 | } 154 | else 155 | { 156 | NSArray *extensionArray = [NSArray arrayWithObjects:aExtension,[aExtension uppercaseString], nil]; 157 | NSInteger currentCount = [[listArray pathsMatchingExtensions:extensionArray] count]; 158 | if (currentCount > appearCount) 159 | { 160 | resultExtension = aExtension; 161 | appearCount = currentCount; 162 | } 163 | } 164 | } 165 | } 166 | return resultExtension; 167 | } 168 | 169 | - (void)receiveResults:(NSNotification *)notify 170 | { 171 | @autoreleasepool { 172 | BOOL finished = [[[notify userInfo] objectForKey:THDuplicationFinished] boolValue]; 173 | NSNumber *fileSize = [[notify userInfo] objectForKey:THDuplicationFileSize]; 174 | NSString *fileHash = [[notify userInfo] objectForKey:THDuplicationFileHash]; 175 | NSArray *fileList = [[notify userInfo] objectForKey:THDuplicationFileList]; 176 | NSString *fileExtension = [self idealExtension:fileList]; 177 | 178 | if (fileSize && fileList && fileHash) 179 | { 180 | NSMutableDictionary *listInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: 181 | fileList,THDuplicationFileList, 182 | fileSize,THDuplicationFileSize,nil]; 183 | if (fileExtension) [listInfo setObject:fileExtension forKey:THDuplicationFileExtenstion]; 184 | @synchronized(tempResultDictionary){ 185 | [tempResultDictionary setObject:listInfo forKey:fileHash]; 186 | } 187 | } 188 | 189 | NSTimeInterval currentInterval = [NSDate timeIntervalSinceReferenceDate]; 190 | NSTimeInterval interval = currentInterval - notificationInterval; 191 | 192 | if ((finished || interval > 5.0) && [tempResultDictionary count] > 0) 193 | { 194 | NSDictionary *updateInfo = [NSDictionary dictionaryWithDictionary:tempResultDictionary]; 195 | @synchronized(tempResultDictionary){ 196 | [tempResultDictionary removeAllObjects]; 197 | } 198 | notificationInterval = currentInterval; 199 | dispatch_async(dispatch_get_main_queue(), ^{ 200 | [self refreshResultList:updateInfo]; 201 | notificationInterval = [NSDate timeIntervalSinceReferenceDate]; 202 | }); 203 | } 204 | 205 | if (finished) 206 | { 207 | dispatch_async(dispatch_get_main_queue(), ^{ 208 | [startButton setEnabled:YES]; 209 | [stopButton setEnabled:NO]; 210 | [loadingView stopAnimation:nil]; 211 | [loadingView setHidden:YES]; 212 | }); 213 | } 214 | } 215 | } 216 | 217 | - (void)refreshResultList:(NSDictionary *)updateInfo 218 | { 219 | for (id key in updateInfo) 220 | { 221 | NSDictionary *listInfo = [updateInfo objectForKey:key]; 222 | NSDictionary *exist = [resultDictionary objectForKey:key]; 223 | if (exist) 224 | { 225 | NSInteger idx = [results indexOfObject:exist]; 226 | [results replaceObjectAtIndex:idx withObject:listInfo]; 227 | }else 228 | { 229 | [results addObject:listInfo]; 230 | } 231 | } 232 | [resultDictionary addEntriesFromDictionary:updateInfo]; 233 | [self reloadList]; 234 | } 235 | 236 | - (void)reloadList 237 | { 238 | if ([filterString length] > 0) 239 | { 240 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self contains[cd] %@",filterString]; 241 | showLists = [[NSMutableArray alloc] init]; 242 | for (NSDictionary *listInfo in results) 243 | { 244 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 245 | for (NSString *filePath in fileList) 246 | { 247 | if ([predicate evaluateWithObject:filePath]) 248 | { 249 | [showLists addObject:listInfo]; 250 | break; 251 | } 252 | } 253 | } 254 | }else 255 | { 256 | showLists = [results mutableCopy]; 257 | } 258 | 259 | if (sortKind != THDuplicationSortDefault) 260 | { 261 | [showLists sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 262 | @autoreleasepool { 263 | NSDictionary *listInfo1 = (NSDictionary *)obj1; 264 | NSDictionary *listInfo2 = (NSDictionary *)obj2; 265 | 266 | NSArray *fileList1 = [listInfo1 objectForKey:THDuplicationFileList]; 267 | NSArray *fileList2 = [listInfo2 objectForKey:THDuplicationFileList]; 268 | 269 | NSComparisonResult comparisionResult = NSOrderedSame; 270 | if (sortKind == THDuplicationSortCount) 271 | { 272 | if (fileList1.count > fileList2.count) comparisionResult = NSOrderedAscending; 273 | if (fileList2.count > fileList1.count) comparisionResult = NSOrderedDescending; 274 | } 275 | 276 | if (sortKind == THDuplicationSortSize) 277 | { 278 | NSNumber *fileSize1 = [listInfo1 objectForKey:THDuplicationFileSize]; 279 | NSNumber *fileSize2 = [listInfo2 objectForKey:THDuplicationFileSize]; 280 | comparisionResult = [fileSize2 compare:fileSize1]; 281 | } 282 | 283 | if (sortKind == THDuplicationSortType) 284 | { 285 | NSString *extension1 = [listInfo1 objectForKey:THDuplicationFileExtenstion]; 286 | NSString *extension2 = [listInfo2 objectForKey:THDuplicationFileExtenstion]; 287 | if (!extension1 && extension2) comparisionResult = NSOrderedDescending; 288 | else if (extension1 && !extension2) comparisionResult = NSOrderedAscending; 289 | else comparisionResult = [extension1 compare:extension2]; 290 | } 291 | 292 | return comparisionResult; 293 | } 294 | }]; 295 | } 296 | 297 | uint64 totalCount = 0; 298 | uint64 totalSize = 0; 299 | for (NSDictionary *listInfo in showLists) 300 | { 301 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 302 | totalCount += [fileList count]-1; 303 | NSNumber *fileSize = [listInfo objectForKey:THDuplicationFileSize]; 304 | totalSize += ([fileList count]-1)*[fileSize longLongValue]; 305 | } 306 | NSString *resultString = [NSString stringWithFormat:@"%@/%lld files",[NSString stringWithSize:totalSize],totalCount]; 307 | [showResultView setStringValue:resultString]; 308 | [resultView reloadData]; 309 | } 310 | 311 | #pragma mark - 312 | #pragma mark PXListViewDelegate 313 | 314 | - (NSUInteger)numberOfRowsInListView:(PXListView*)aListView 315 | { 316 | return [showLists count]; 317 | } 318 | 319 | - (CGFloat)listView:(PXListView*)aListView heightOfRow:(NSUInteger)row 320 | { 321 | NSDictionary *listInfo = [showLists objectAtIndex:row]; 322 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 323 | return kTHDuplicateCellMinSize.height + kTHPathViewHeight*([fileList count]-1); 324 | } 325 | 326 | - (PXListViewCell*)listView:(PXListView*)aListView cellForRow:(NSUInteger)row 327 | { 328 | static NSString *kCellIdentifier = @"THDuplicateCellIdentifier"; 329 | THDuplicateCell *cell = (THDuplicateCell*)[aListView dequeueCellWithReusableIdentifier:kCellIdentifier]; 330 | 331 | if(!cell) { 332 | cell = [[THDuplicateCell alloc] initWithReusableIdentifier:kCellIdentifier]; 333 | cell.delegate = self; 334 | } 335 | 336 | NSDictionary *listInfo = [showLists objectAtIndex:row]; 337 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 338 | NSNumber *fileSize = [listInfo objectForKey:THDuplicationFileSize]; 339 | cell.fileLists = fileList; 340 | cell.fileSize = [fileSize unsignedLongLongValue]; 341 | 342 | return cell; 343 | } 344 | 345 | #pragma mark - 346 | #pragma mark THDuplicateCellDelegate 347 | 348 | - (void)cellOpenClick:(THDuplicateCell *)cell index:(NSInteger)index 349 | { 350 | NSUInteger row = cell.row; 351 | NSDictionary *listInfo = [showLists objectAtIndex:row]; 352 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 353 | NSString *filePath = [fileList objectAtIndex:index]; 354 | [[NSWorkspace sharedWorkspace] selectFile:filePath 355 | inFileViewerRootedAtPath:[filePath stringByDeletingLastPathComponent]]; 356 | } 357 | 358 | - (void)cellRemoveClick:(THDuplicateCell *)cell index:(NSInteger)index 359 | { 360 | NSUInteger row = cell.row; 361 | NSDictionary *listInfo = [showLists objectAtIndex:row]; 362 | NSArray *fileList = [listInfo objectForKey:THDuplicationFileList]; 363 | NSArray *keys = [resultDictionary allKeysForObject:listInfo]; 364 | NSString *filePath = [fileList objectAtIndex:index]; 365 | 366 | if ([fileList count] > 2) 367 | { 368 | NSInteger idx = [results indexOfObject:listInfo]; 369 | fileList = [fileList arrayByRemoveObject:filePath]; 370 | listInfo = [NSDictionary dictionaryWithObjectsAndKeys: 371 | fileList,THDuplicationFileList, 372 | [listInfo objectForKey:THDuplicationFileSize],THDuplicationFileSize,nil]; 373 | 374 | [results replaceObjectAtIndex:idx withObject:listInfo]; 375 | for (NSArray *key in keys) 376 | { 377 | [resultDictionary setObject:listInfo forKey:key]; 378 | } 379 | }else 380 | { 381 | NSInteger idx = [results indexOfObject:listInfo]; 382 | [results removeObjectAtIndex:idx]; 383 | for (NSArray *key in keys) 384 | { 385 | [resultDictionary removeObjectForKey:key]; 386 | } 387 | } 388 | 389 | [self reloadList]; 390 | 391 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 392 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) 393 | { 394 | [[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL]; 395 | } 396 | }); 397 | } 398 | 399 | #pragma mark - 400 | #pragma mark NSSplitViewDelegate 401 | 402 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedCoordinate ofSubviewAt:(NSInteger)index 403 | { 404 | CGFloat constrainedCoordinate = proposedCoordinate; 405 | if (index == 0) 406 | { 407 | constrainedCoordinate = proposedCoordinate + 320; 408 | } 409 | return constrainedCoordinate; 410 | } 411 | 412 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedCoordinate ofSubviewAt:(NSInteger)index 413 | { 414 | CGFloat constrainedCoordinate = proposedCoordinate; 415 | if (index == ([[splitView subviews] count] - 2)) 416 | { 417 | constrainedCoordinate = proposedCoordinate - 320; 418 | } 419 | 420 | return constrainedCoordinate; 421 | } 422 | 423 | - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview 424 | { 425 | return NO; 426 | } 427 | 428 | @end 429 | -------------------------------------------------------------------------------- /FindDuplicateFiles/DuplicateScanner-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | icon.icns 11 | CFBundleIdentifier 12 | com.moft.DuplicateScanner 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.utilities 27 | LSHasLocalizedDisplayName 28 | 29 | LSMinimumSystemVersion 30 | ${MACOSX_DEPLOYMENT_TARGET} 31 | NSHumanReadableCopyright 32 | Copyright © 2012年 tanhao.me. All rights reserved. 33 | NSMainNibFile 34 | MainMenu 35 | NSPrincipalClass 36 | NSApplication 37 | 38 | 39 | -------------------------------------------------------------------------------- /FindDuplicateFiles/DuplicateScanner-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'FindDuplicateFiles' target in the 'FindDuplicateFiles' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import "NSString+Size.h" 8 | #import "NSArray+Remove.h" 9 | #define kSelfBundle [NSBundle bundleForClass:self.class] 10 | #define THLocaleString(key) NSLocalizedStringFromTableInBundle(key, nil, [NSBundle bundleForClass:self.class], nil) 11 | #endif 12 | -------------------------------------------------------------------------------- /FindDuplicateFiles/FindDuplicateFiles.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-write 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /FindDuplicateFiles/INAppStoreWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAppStoreWindow.h 3 | // 4 | // Copyright 2011 Indragie Karunaratne. All rights reserved. 5 | // 6 | // Licensed under the BSD License 7 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 9 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 10 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 11 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 12 | // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 13 | // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 | // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | // 17 | 18 | #import 19 | 20 | #if __has_feature(objc_arc) 21 | #define INAppStoreWindowCopy nonatomic, strong 22 | #define INAppStoreWindowRetain nonatomic, strong 23 | #else 24 | #define INAppStoreWindowCopy nonatomic, copy 25 | #define INAppStoreWindowRetain nonatomic, retain 26 | #endif 27 | 28 | /** @class INTitlebarView 29 | Draws a default style Mac OS X title bar. 30 | */ 31 | @interface INTitlebarView : NSView 32 | @end 33 | 34 | /** @class INAppStoreWindow 35 | Creates a window similar to the Mac App Store window, with centered traffic lights and an 36 | enlarged title bar. This does not handle creating the toolbar. 37 | */ 38 | @interface INAppStoreWindow : NSWindow 39 | 40 | /** The height of the title bar. By default, this is set to the standard title bar height. */ 41 | @property (nonatomic) CGFloat titleBarHeight; 42 | 43 | /** The title bar view itself. Add subviews to this view that you want to show in the title bar 44 | (e.g. buttons, a toolbar, etc.). This view can also be set if you want to use a different 45 | styled title bar aside from the default one (textured, etc.). */ 46 | @property (INAppStoreWindowRetain) NSView *titleBarView; 47 | 48 | /** Set whether the fullscreen or traffic light buttons are horizontally centered */ 49 | @property (nonatomic) BOOL centerFullScreenButton; 50 | @property (nonatomic) BOOL centerTrafficLightButtons; 51 | 52 | /** If you want to hide the title bar in fullscreen mode, set this boolean to YES */ 53 | @property (nonatomic) BOOL hideTitleBarInFullScreen; 54 | 55 | /** Use this API to hide the baseline INAppStoreWindow draws between itself and the main window contents. */ 56 | @property (nonatomic) BOOL showsBaselineSeparator; 57 | 58 | /** Adjust the left and right padding of the trafficlight and fullscreen buttons */ 59 | @property (nonatomic) CGFloat trafficLightButtonsLeftMargin; 60 | @property (nonatomic) CGFloat fullScreenButtonRightMargin; 61 | 62 | 63 | /** So much logic and work has gone into this window subclass to achieve a custom title bar, 64 | it would be a shame to have to re-invent that just to change the look. So this block can be used 65 | to override the default Mac App Store style titlebar drawing with your own drawing code! 66 | */ 67 | typedef void (^INAppStoreWindowTitleBarDrawingBlock)(BOOL drawsAsMainWindow, 68 | CGRect drawingRect, CGPathRef clippingPath); 69 | @property (INAppStoreWindowCopy) INAppStoreWindowTitleBarDrawingBlock titleBarDrawingBlock; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /FindDuplicateFiles/NSArray+Remove.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+NSArray_remove.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (Remove) 12 | - (NSArray*)arrayByRemoveObject:(id)obj; 13 | - (NSArray*)arrayByRemoveObjectsFromArray:(NSArray *)array; 14 | @end 15 | -------------------------------------------------------------------------------- /FindDuplicateFiles/NSArray+Remove.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+NSArray_remove.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Remove.h" 10 | 11 | @implementation NSArray (Remove) 12 | 13 | - (NSArray*)arrayByRemoveObject:(id)obj 14 | { 15 | if (!obj || ![self containsObject:obj]) 16 | { 17 | return [self copy]; 18 | } 19 | 20 | NSMutableArray *tempArray = [self mutableCopy]; 21 | [tempArray removeObject:obj]; 22 | NSArray *array = [[NSArray alloc] initWithArray:tempArray]; 23 | return array; 24 | } 25 | 26 | - (NSArray*)arrayByRemoveObjectsFromArray:(NSArray *)array 27 | { 28 | if ([array count] == 0) 29 | { 30 | return [self copy]; 31 | } 32 | 33 | NSArray *resultArray = self; 34 | for (id obj in array) 35 | { 36 | resultArray = [resultArray arrayByRemoveObject:obj]; 37 | } 38 | return resultArray; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /FindDuplicateFiles/NSString+Size.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Size.h 3 | // randomTest 4 | // 5 | // Created by TanHao on 12-9-24. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString(Size) 12 | 13 | + (NSString *)stringWithSize:(uint64_t)size; 14 | + (NSString *)stringWithDiskSize:(uint64_t)size; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /FindDuplicateFiles/NSString+Size.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Size.m 3 | // randomTest 4 | // 5 | // Created by TanHao on 12-9-24. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "NSString+Size.h" 10 | 11 | @implementation NSString(Size) 12 | 13 | static NSString* sizeUnit[]={@"KB",@"MB",@"GB",@"TB"}; 14 | 15 | + (NSString *)stringWithSize:(uint64_t)size 16 | { 17 | double value = size; 18 | int unitIdx = 0; 19 | 20 | value /= 1024; 21 | while (value > 1000.0f && unitIdx+1 1000.0f && unitIdx+1 10 | #import "PXListViewDropHighlight.h" 11 | 12 | 13 | @class PXListView; 14 | 15 | @interface PXListViewCell : NSView 16 | { 17 | NSString *_reusableIdentifier; 18 | 19 | PXListView *__unsafe_unretained _listView; 20 | NSUInteger _row; 21 | PXListViewDropHighlight _dropHighlight; 22 | } 23 | 24 | @property (nonatomic, unsafe_unretained) PXListView *listView; 25 | 26 | @property (readonly, copy) NSString *reusableIdentifier; 27 | @property (readonly) NSUInteger row; 28 | 29 | @property (readonly,getter=isSelected) BOOL selected; 30 | @property (assign) PXListViewDropHighlight dropHighlight; 31 | 32 | + (id)cellLoadedFromNibNamed:(NSString*)nibName reusableIdentifier:(NSString*)identifier; 33 | + (id)cellLoadedFromNibNamed:(NSString*)nibName bundle:(NSBundle*)bundle reusableIdentifier:(NSString*)identifier; 34 | 35 | - (id)initWithReusableIdentifier:(NSString*)identifier; 36 | - (void)prepareForReuse; 37 | 38 | -(void)layoutSubviews; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/Cells/PXListViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // PXListViewCell.m 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 29/05/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | #import "PXListViewCell.h" 10 | #import "PXListViewCell+Private.h" 11 | 12 | #import 13 | 14 | #import "PXListView.h" 15 | #import "PXListView+Private.h" 16 | #import "PXListView+UserInteraction.h" 17 | 18 | #pragma mark - 19 | 20 | @implementation PXListViewCell 21 | 22 | @synthesize reusableIdentifier = _reusableIdentifier; 23 | @synthesize listView = _listView; 24 | @synthesize row = _row; 25 | @synthesize dropHighlight = _dropHighlight; 26 | 27 | + (id)cellLoadedFromNibNamed:(NSString*)nibName reusableIdentifier:(NSString*)identifier 28 | { 29 | return [self cellLoadedFromNibNamed:nibName bundle:nil reusableIdentifier:identifier]; 30 | } 31 | 32 | + (id)cellLoadedFromNibNamed:(NSString*)nibName bundle:(NSBundle*)bundle reusableIdentifier:(NSString*)identifier 33 | { 34 | NSNib *cellNib = [[NSNib alloc] initWithNibNamed:nibName bundle:bundle]; 35 | NSArray *objects = nil; 36 | 37 | id cell = nil; 38 | 39 | [cellNib instantiateNibWithOwner:nil topLevelObjects:&objects]; 40 | for(id object in objects) { 41 | if([object isKindOfClass:[self class]]) { 42 | cell = object; 43 | [cell setReusableIdentifier:identifier]; 44 | break; 45 | } 46 | } 47 | 48 | 49 | return cell; 50 | } 51 | 52 | #pragma mark - 53 | #pragma mark Init/Dealloc 54 | 55 | - (id)initWithReusableIdentifier:(NSString*)identifier 56 | { 57 | if((self = [super initWithFrame: NSZeroRect])) 58 | { 59 | _reusableIdentifier = [identifier copy]; 60 | } 61 | 62 | return self; 63 | } 64 | 65 | 66 | - (id)initWithCoder:(NSCoder *)aDecoder 67 | { 68 | if((self = [super initWithCoder: aDecoder])) 69 | { 70 | _reusableIdentifier = NSStringFromClass([self class]); 71 | } 72 | 73 | return self; 74 | } 75 | 76 | 77 | 78 | #pragma mark - 79 | #pragma mark Handling Selection 80 | 81 | - (void)mouseDown:(NSEvent*)theEvent 82 | { 83 | [[self listView] handleMouseDown:theEvent inCell:self]; 84 | } 85 | 86 | - (BOOL)isSelected 87 | { 88 | return [[[self listView] selectedRows] containsIndex:[self row]]; 89 | } 90 | 91 | #pragma mark - 92 | #pragma mark Drag & Drop 93 | 94 | - (PXListViewDropHighlight)dropHighlight 95 | { 96 | return _dropHighlight; 97 | } 98 | 99 | - (void)setDropHighlight:(PXListViewDropHighlight)inState 100 | { 101 | [[self listView] setShowsDropHighlight: inState != PXListViewDropNowhere]; 102 | 103 | _dropHighlight = inState; 104 | [self setNeedsDisplay:YES]; 105 | } 106 | 107 | 108 | - (void)drawRect:(NSRect)dirtyRect 109 | { 110 | if(_dropHighlight == PXListViewDropAbove) 111 | { 112 | [[NSColor alternateSelectedControlColor] set]; 113 | NSRect theBox = [self bounds]; 114 | theBox.origin.y += theBox.size.height -1.0f; 115 | theBox.size.height = 2.0f; 116 | [NSBezierPath setDefaultLineWidth: 2.0f]; 117 | [NSBezierPath strokeRect: theBox]; 118 | } 119 | else if(_dropHighlight == PXListViewDropBelow) 120 | { 121 | [[NSColor alternateSelectedControlColor] set]; 122 | NSRect theBox = [self bounds]; 123 | theBox.origin.y += 1.0f; 124 | theBox.size.height = 2.0f; 125 | [NSBezierPath setDefaultLineWidth: 2.0f]; 126 | [NSBezierPath strokeRect: theBox]; 127 | } 128 | else if(_dropHighlight == PXListViewDropOn) 129 | { 130 | [[NSColor alternateSelectedControlColor] set]; 131 | NSRect theBox = [self bounds]; 132 | [NSBezierPath setDefaultLineWidth: 2.0f]; 133 | [NSBezierPath strokeRect: NSInsetRect(theBox,1.0f,1.0f)]; 134 | } 135 | } 136 | 137 | 138 | #pragma mark - 139 | #pragma mark Reusing Cells 140 | 141 | - (void)prepareForReuse 142 | { 143 | _dropHighlight = PXListViewDropNowhere; 144 | } 145 | 146 | 147 | #pragma mark layout 148 | 149 | -(void)layoutSubviews; 150 | { 151 | 152 | } 153 | 154 | #pragma mark - 155 | #pragma mark Accessibility 156 | 157 | -(NSArray*) accessibilityAttributeNames 158 | { 159 | NSMutableArray* attribs = [[super accessibilityAttributeNames] mutableCopy]; 160 | 161 | [attribs addObject: NSAccessibilityRoleAttribute]; 162 | [attribs addObject: NSAccessibilityEnabledAttribute]; 163 | 164 | return attribs; 165 | } 166 | 167 | -(BOOL) accessibilityIsAttributeSettable: (NSString *)attribute 168 | { 169 | if( [attribute isEqualToString: NSAccessibilityRoleAttribute] 170 | or [attribute isEqualToString: NSAccessibilityEnabledAttribute] ) 171 | { 172 | return NO; 173 | } 174 | else 175 | return [super accessibilityIsAttributeSettable: attribute]; 176 | } 177 | 178 | -(id) accessibilityAttributeValue: (NSString *)attribute 179 | { 180 | if( [attribute isEqualToString: NSAccessibilityRoleAttribute] ) 181 | { 182 | return NSAccessibilityRowRole; 183 | } 184 | else if( [attribute isEqualToString: NSAccessibilityEnabledAttribute] ) 185 | { 186 | return [NSNumber numberWithBool: YES]; 187 | } 188 | else 189 | return [super accessibilityAttributeValue: attribute]; 190 | } 191 | 192 | 193 | -(NSArray *) accessibilityActionNames 194 | { 195 | return [NSArray arrayWithObjects: NSAccessibilityPressAction, nil]; 196 | } 197 | 198 | 199 | -(NSString *) accessibilityActionDescription: (NSString *)action 200 | { 201 | return NSAccessibilityActionDescription(action); 202 | } 203 | 204 | 205 | -(void) accessibilityPerformAction: (NSString *)action 206 | { 207 | if( [action isEqualToString: NSAccessibilityPressAction] ) 208 | { 209 | [[self listView] handleMouseDown: nil inCell: self]; 210 | } 211 | } 212 | 213 | 214 | -(BOOL) accessibilityIsIgnored 215 | { 216 | return NO; 217 | } 218 | 219 | @end 220 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/Cells/PXListViewDropHighlight.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PXListViewDropHighlight.h 3 | * PXListView 4 | * 5 | * Created by Uli Kusterer on 11.07.10. 6 | * Copyright 2010 Uli Kusterer. All rights reserved. 7 | * 8 | */ 9 | 10 | enum PXListViewDropHighlight 11 | { 12 | PXListViewDropNowhere = 0, 13 | PXListViewDropOn, 14 | PXListViewDropAbove, 15 | PXListViewDropBelow 16 | }; 17 | typedef NSUInteger PXListViewDropHighlight; 18 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/Misc/PXListDocumentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXListDocumentView.h 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 29/05/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PXListViewDropHighlight.h" 11 | 12 | 13 | @class PXListView; 14 | 15 | @interface PXListDocumentView : NSView 16 | { 17 | PXListView *__unsafe_unretained _listView; 18 | PXListViewDropHighlight _dropHighlight; 19 | } 20 | 21 | @property (unsafe_unretained) PXListView *listView; 22 | @property (assign) PXListViewDropHighlight dropHighlight; 23 | 24 | -(void) setDropHighlight: (PXListViewDropHighlight)inState; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/Misc/PXListDocumentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PXListDocumentView.m 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 29/05/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | #import "PXListDocumentView.h" 10 | 11 | #import "PXListView.h" 12 | #import "PXListView+Private.h" 13 | #import "PXListView+UserInteraction.h" 14 | 15 | @implementation PXListDocumentView 16 | 17 | @synthesize listView = _listView; 18 | @synthesize dropHighlight = _dropHighlight; 19 | 20 | - (BOOL)isFlipped 21 | { 22 | return YES; 23 | } 24 | 25 | - (void)mouseDown:(NSEvent *)theEvent 26 | { 27 | [[self listView] handleMouseDownOutsideCells: theEvent]; 28 | } 29 | 30 | 31 | -(void) drawRect: (NSRect)dirtyRect 32 | { 33 | #pragma unused(dirtyRect) 34 | //NSLog( @"drawRect %lu", _dropHighlight ); 35 | 36 | // We always show the outline: 37 | if( _dropHighlight != PXListViewDropNowhere ) 38 | { 39 | CGFloat lineWidth = 2.0f; 40 | CGFloat lineWidthHalf = lineWidth / 2.0f; 41 | 42 | [[NSColor selectedControlColor] set]; 43 | [NSBezierPath setDefaultLineWidth: lineWidth]; 44 | [NSBezierPath strokeRect: NSInsetRect([self visibleRect], lineWidthHalf, lineWidthHalf)]; 45 | //NSLog( @"drawing drop outline" ); 46 | } 47 | 48 | if( _dropHighlight == PXListViewDropAbove || _dropHighlight == PXListViewDropBelow ) // DropAbove means as first cell, DropBelow after last cell. 49 | { 50 | CGFloat lineWidth = 2.0f; 51 | NSRect theBox = ([_listView numberOfRows] == 0) ? NSMakeRect(0,0,[self bounds].size.width,0) : [_listView rectOfRow: [_listView numberOfRows] -1]; 52 | 53 | theBox.origin.y += theBox.size.height -2.0f; 54 | theBox.size.height = 2.0f; 55 | 56 | [[NSColor alternateSelectedControlColor] set]; 57 | [NSBezierPath setDefaultLineWidth: lineWidth]; 58 | [NSBezierPath strokeRect: theBox]; 59 | //NSLog( @"drawing drop ABOVE indicator" ); 60 | } 61 | } 62 | 63 | - (PXListViewDropHighlight)dropHighlight 64 | { 65 | return _dropHighlight; 66 | } 67 | 68 | -(void) setDropHighlight: (PXListViewDropHighlight)inState 69 | { 70 | _dropHighlight = inState; 71 | //NSLog( @"setDropHighlight %lu", _dropHighlight ); 72 | [self setNeedsDisplayInRect: [self visibleRect]]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/PXListView+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXListView+Private.h 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 01/06/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | // This is a renamed copy of UKIsDragStart from : 10 | // Possible return values from UKIsDragStart: 11 | enum 12 | { 13 | PXIsDragStartMouseReleased = 0, 14 | PXIsDragStartTimedOut, 15 | PXIsDragStartMouseMovedHorizontally, 16 | PXIsDragStartMouseMovedVertically 17 | }; 18 | typedef NSInteger PXIsDragStartResult; 19 | 20 | 21 | @interface PXListView () 22 | 23 | - (NSRect)contentViewRect; 24 | 25 | - (void)cacheCellLayout; 26 | - (void)layoutCells; 27 | - (void)layoutCell:(PXListViewCell*)cell atRow:(NSUInteger)row; 28 | 29 | - (void)addCellsFromVisibleRange; 30 | - (PXListViewCell*)visibleCellForRow:(NSUInteger)row; 31 | - (NSArray*)visibleCellsForRowIndexes:(NSIndexSet*)rows; 32 | 33 | - (NSUInteger)numberOfRows; 34 | - (void)deselectRowIndexes:(NSIndexSet*)rows; 35 | - (void)postSelectionDidChangeNotification; 36 | 37 | - (void)updateCells; 38 | - (void)enqueueCell:(PXListViewCell*)cell; 39 | 40 | - (void)contentViewBoundsDidChange:(NSNotification*)notification; 41 | -(void)layoutCellsForResizeEvent; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/PXListView+UserInteraction.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXListView+UserInteraction.h 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 27/03/2011. 6 | // Copyright 2011 Alex Rozanski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "PXListView.h" 12 | 13 | @interface PXListView (UserInteraction) 14 | 15 | - (void)handleMouseDown:(NSEvent*)theEvent inCell:(PXListViewCell*)theCell; 16 | - (void)handleMouseDownOutsideCells:(NSEvent*)theEvent; 17 | 18 | - (BOOL)attemptDragWithMouseDown:(NSEvent*)theEvent inCell:(PXListViewCell*)theCell; 19 | - (NSImage*)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows 20 | event:(NSEvent*)dragEvent clickedCell:(PXListViewCell*)clickedCell 21 | offset:(NSPointPointer)dragImageOffset; 22 | - (void)setShowsDropHighlight:(BOOL)inState; 23 | - (void)setDropRow:(NSUInteger)row dropHighlight:(PXListViewDropHighlight)dropHighlight; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/PXListView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXListView.h 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 29/05/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "PXListViewDelegate.h" 12 | #import "PXListViewCell.h" 13 | 14 | 15 | #if DEBUG 16 | #define PXLog(...) NSLog(__VA_ARGS__) 17 | #endif 18 | 19 | 20 | @interface PXListView : NSScrollView 21 | { 22 | id _delegate; 23 | 24 | NSMutableArray *_reusableCells; 25 | NSMutableArray *_visibleCells; 26 | NSRange _currentRange; 27 | 28 | NSUInteger _numberOfRows; 29 | NSMutableIndexSet *_selectedRows; 30 | 31 | NSRange _visibleRange; 32 | CGFloat _totalHeight; 33 | CGFloat *_cellYOffsets; 34 | 35 | CGFloat _cellSpacing; 36 | 37 | BOOL _allowsSelection; 38 | BOOL _allowsEmptySelection; 39 | BOOL _allowsMultipleSelection; 40 | NSInteger _lastSelectedRow; 41 | 42 | BOOL _verticalMotionCanBeginDrag; 43 | 44 | BOOL _usesLiveResize; 45 | CGFloat _widthPriorToResize; 46 | 47 | NSUInteger _dropRow; 48 | PXListViewDropHighlight _dropHighlight; 49 | } 50 | 51 | @property (nonatomic, unsafe_unretained) IBOutlet id delegate; 52 | 53 | @property (nonatomic, strong) NSIndexSet *selectedRows; 54 | @property (nonatomic, assign) NSUInteger selectedRow; 55 | 56 | @property (nonatomic, assign) BOOL allowsSelection; 57 | @property (nonatomic, assign) BOOL allowsEmptySelection; 58 | @property (nonatomic, assign) BOOL allowsMultipleSelection; 59 | @property (nonatomic, assign) BOOL verticalMotionCanBeginDrag; 60 | 61 | @property (nonatomic, assign) CGFloat cellSpacing; 62 | @property (nonatomic, assign) BOOL usesLiveResize; 63 | 64 | - (void)reloadData; 65 | -(void)reloadRowAtIndex:(NSInteger)inIndex; 66 | 67 | - (PXListViewCell*)dequeueCellWithReusableIdentifier:(NSString*)identifier; 68 | 69 | - (NSArray*)visibleCells; 70 | - (PXListViewCell *)cellForRowAtIndex:(NSUInteger)inIndex; 71 | 72 | - (NSRange)visibleRange; 73 | - (NSRect)rectOfRow:(NSUInteger)row; 74 | - (void)deselectRows; 75 | - (void)selectRowIndexes:(NSIndexSet*)rows byExtendingSelection:(BOOL)doExtend; 76 | 77 | - (void)scrollToRow:(NSUInteger)row; 78 | - (void)scrollRowToVisible:(NSUInteger)row; 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /FindDuplicateFiles/PXListView/PXListViewDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PXListViewDelegate.h 3 | // PXListView 4 | // 5 | // Created by Alex Rozanski on 29/05/2010. 6 | // Copyright 2010 Alex Rozanski. http://perspx.com. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PXListViewDropHighlight.h" 11 | 12 | extern NSString * const PXListViewSelectionDidChange; 13 | 14 | @class PXListView, PXListViewCell; 15 | 16 | @protocol PXListViewDelegate 17 | 18 | @required 19 | - (NSUInteger)numberOfRowsInListView:(PXListView*)aListView; 20 | - (CGFloat)listView:(PXListView*)aListView heightOfRow:(NSUInteger)row; 21 | - (PXListViewCell*)listView:(PXListView*)aListView cellForRow:(NSUInteger)row; 22 | 23 | @optional 24 | - (void)listViewSelectionDidChange:(NSNotification*)aNotification; 25 | - (void)listView:(PXListView*)aListView rowDoubleClicked:(NSUInteger)rowIndex; 26 | 27 | - (BOOL)listView:(PXListView*)aListView writeRowsWithIndexes:(NSIndexSet*)rowIndexes toPasteboard:(NSPasteboard *)pboard; 28 | - (NSDragOperation)listView:(PXListView*)aListView 29 | validateDrop:(id )info 30 | proposedRow:(NSUInteger)row 31 | proposedDropHighlight:(PXListViewDropHighlight)dropHighlight; 32 | - (BOOL)listView:(PXListView*)aListView 33 | acceptDrop:(id )info 34 | row:(NSUInteger)row 35 | dropHighlight:(PXListViewDropHighlight)dropHighlight; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THBackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // THBackgroundView.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THBackgroundView : NSView 12 | 13 | @property (nonatomic,strong) NSImage *image; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THBackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // THBackgroundView.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THBackgroundView.h" 10 | 11 | @implementation THBackgroundView 12 | @synthesize image; 13 | 14 | - (NSImage *)image 15 | { 16 | return image; 17 | } 18 | 19 | - (void)setImage:(NSImage *)value 20 | { 21 | image = value; 22 | [self setNeedsDisplay:YES]; 23 | } 24 | 25 | - (void)drawRect:(NSRect)dirtyRect 26 | { 27 | if (!image) 28 | { 29 | return; 30 | } 31 | NSRect visibleRect = [self bounds]; 32 | float width = image.size.width; 33 | float height = image.size.height; 34 | 35 | float top, right; 36 | 37 | for (top = 0; top 10 | 11 | @interface THBarView : NSView 12 | @property (nonatomic,strong) IBOutlet NSString *title; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THBarView.m: -------------------------------------------------------------------------------- 1 | // 2 | // THBarView.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-18. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THBarView.h" 10 | 11 | @implementation THBarView 12 | @synthesize title; 13 | 14 | - (id)initWithFrame:(NSRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | // Initialization code here. 19 | } 20 | 21 | return self; 22 | } 23 | 24 | - (void)drawRect:(NSRect)dirtyRect 25 | { 26 | // Drawing code here. 27 | // 渐变 28 | NSArray * colors = [NSArray arrayWithObjects: 29 | [NSColor colorWithDeviceRed:0.8627 green:0.8627 blue:0.8627 alpha:1.0], 30 | [NSColor colorWithDeviceRed:0.7059 green:0.7059 blue:0.7059 alpha:1.0], nil]; 31 | NSGradient * gradient = [[NSGradient alloc] initWithColors:colors]; 32 | [gradient drawInRect:NSInsetRect(self.bounds, 0, 0) angle:270]; 33 | 34 | //边框 35 | /* 36 | [[NSColor colorWithDeviceRed:114/255.0 green:120.0/255.0 blue:132.0/255.0 alpha:1.0] setStroke]; 37 | NSBezierPath * border = [NSBezierPath bezierPathWithRect:NSInsetRect(self.bounds, 1, 1)]; 38 | [border setLineWidth:1.0]; 39 | [border stroke]; 40 | */ 41 | 42 | //画文字 43 | NSColor * textColor = [NSColor colorWithDeviceRed:90/255.0 green:90/255.0 blue:90/255.0 alpha:1.0]; 44 | NSDictionary *textAtt = [NSDictionary dictionaryWithObjectsAndKeys: 45 | [NSFont boldSystemFontOfSize:12], NSFontAttributeName, 46 | textColor, NSForegroundColorAttributeName, nil]; 47 | 48 | NSSize textsize = [title sizeWithAttributes:textAtt]; 49 | 50 | NSRect textRect = NSMakeRect((NSWidth(self.bounds)-textsize.width)*0.5, 51 | (NSHeight(self.bounds)-textsize.height)*0.5, 52 | textsize.width, 53 | textsize.height); 54 | 55 | [title drawInRect:textRect withAttributes:textAtt]; 56 | 57 | NSBezierPath * lastLine = [NSBezierPath bezierPath]; 58 | [lastLine moveToPoint:NSZeroPoint]; 59 | [lastLine lineToPoint:NSMakePoint(NSMaxX(self.bounds), 0)]; 60 | [[NSColor darkGrayColor] setStroke]; 61 | [lastLine setLineWidth:2.0]; 62 | [lastLine stroke]; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THDuplicateCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDuplicateCell.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-10. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "PXListViewCell.h" 10 | #import "THStaticTextField.h" 11 | 12 | #define kTHDuplicateCellMinSize (NSMakeSize(400, 40)) 13 | #define kTHPathViewHeight (20) 14 | 15 | @protocol THDuplicateCellDelegate; 16 | @interface THDuplicateCell : PXListViewCell 17 | { 18 | NSImageView *iconView; 19 | THStaticTextField *sizeField; 20 | NSMutableArray *viewItemList; 21 | } 22 | @property (nonatomic,unsafe_unretained) id delegate; 23 | @property (nonatomic,strong) NSArray *fileLists; 24 | @property (nonatomic,assign) uint64 fileSize; 25 | 26 | @end 27 | 28 | @protocol THDuplicateCellDelegate 29 | 30 | - (void)cellOpenClick:(THDuplicateCell *)cell index:(NSInteger)index; 31 | - (void)cellRemoveClick:(THDuplicateCell *)cell index:(NSInteger)index; 32 | 33 | @end -------------------------------------------------------------------------------- /FindDuplicateFiles/THDuplicateCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // THDuplicateCell.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-10. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THDuplicateCell.h" 10 | #import "THStaticTextField.h" 11 | 12 | #define kTHDuplicateCellIconSpacingBegin 8.0 13 | #define kTHDuplicateCellIconSpacingEnd 8.0 14 | #define kTHDuplicateCellIconHeight 28.0 15 | 16 | @class THPathView; 17 | @protocol THPathViewDelegate 18 | 19 | @optional 20 | - (void)openClick:(THPathView *)cell; 21 | - (void)removeClick:(THPathView *)cell; 22 | 23 | @end 24 | 25 | @interface THPathView : NSView 26 | { 27 | THStaticTextField *textField; 28 | NSButton *showButton; 29 | NSButton *removeButton; 30 | } 31 | @property (nonatomic, unsafe_unretained) id delegate; 32 | @property (nonatomic, strong) NSString *filePath; 33 | 34 | + (THPathView *)pathView; 35 | 36 | @end 37 | 38 | @implementation THPathView 39 | @synthesize delegate; 40 | @synthesize filePath; 41 | 42 | - (NSString *)filePath 43 | { 44 | return filePath; 45 | } 46 | 47 | - (void)setFilePath:(NSString *)value 48 | { 49 | if (filePath == value) 50 | { 51 | return; 52 | } 53 | filePath = value; 54 | if (filePath) 55 | { 56 | [textField setStringValue:filePath]; 57 | }else 58 | { 59 | [textField setStringValue:@""]; 60 | } 61 | } 62 | 63 | - (id)initWithFrame:(NSRect)frameRect 64 | { 65 | self = [super initWithFrame:frameRect]; 66 | if (self) 67 | { 68 | textField = [[THStaticTextField alloc] initWithFrame:NSMakeRect(0, NSMidY(frameRect)-17/2, NSWidth(frameRect)-60, 17)]; 69 | [textField.cell setLineBreakMode:NSLineBreakByTruncatingMiddle]; 70 | [textField setAutoresizingMask:NSViewWidthSizable]; 71 | [self addSubview:textField]; 72 | 73 | showButton = [[NSButton alloc] initWithFrame:NSMakeRect(NSWidth(frameRect)-50, NSMidY(frameRect)-16/2, 16, 16)]; 74 | [showButton setBordered:NO]; 75 | [showButton.cell setImageScaling:NSImageScaleProportionallyUpOrDown]; 76 | [showButton setImage:[NSImage imageNamed:NSImageNameRevealFreestandingTemplate]]; 77 | [showButton setAutoresizingMask:NSViewMinXMargin]; 78 | [showButton setTarget:self]; 79 | [showButton setAction:@selector(openClick:)]; 80 | [self addSubview:showButton]; 81 | 82 | removeButton = [[NSButton alloc] initWithFrame:NSMakeRect(NSWidth(frameRect)-26, NSMidY(frameRect)-16/2, 16, 16)]; 83 | [removeButton setBordered:NO]; 84 | [removeButton.cell setImageScaling:NSImageScaleProportionallyUpOrDown]; 85 | [removeButton setImage:[NSImage imageNamed:NSImageNameStopProgressFreestandingTemplate]]; 86 | [removeButton setAutoresizingMask:NSViewMinXMargin]; 87 | [removeButton setTarget:self]; 88 | [removeButton setAction:@selector(removeClick:)]; 89 | [self addSubview:removeButton]; 90 | 91 | [self setAutoresizingMask:NSViewWidthSizable]; 92 | } 93 | return self; 94 | } 95 | 96 | - (void)openClick:(id)sender 97 | { 98 | if (delegate && [delegate respondsToSelector:@selector(openClick:)]) 99 | { 100 | [delegate openClick:self]; 101 | } 102 | } 103 | 104 | - (void)removeClick:(id)sender 105 | { 106 | if (delegate && [delegate respondsToSelector:@selector(removeClick:)]) 107 | { 108 | [delegate removeClick:self]; 109 | } 110 | } 111 | 112 | + (THPathView *)pathView 113 | { 114 | THPathView *pathView = [[THPathView alloc] initWithFrame: 115 | NSMakeRect(0, 0, 116 | kTHDuplicateCellMinSize.width-kTHDuplicateCellIconHeight-kTHDuplicateCellIconSpacingBegin-kTHDuplicateCellIconSpacingEnd, 117 | kTHPathViewHeight)]; 118 | return pathView; 119 | } 120 | 121 | @end 122 | 123 | 124 | @implementation THDuplicateCell 125 | @synthesize delegate; 126 | @synthesize fileLists; 127 | @synthesize fileSize; 128 | 129 | - (id)initWithReusableIdentifier:(NSString*)identifier 130 | { 131 | self = [super initWithReusableIdentifier:identifier]; 132 | if (self) { 133 | 134 | [self setFrameSize:kTHDuplicateCellMinSize]; 135 | 136 | viewItemList = [[NSMutableArray alloc] initWithCapacity:2]; 137 | 138 | iconView = [[NSImageView alloc] initWithFrame: 139 | NSMakeRect(kTHDuplicateCellIconSpacingBegin, (kTHDuplicateCellMinSize.height-kTHDuplicateCellIconHeight)/2, kTHDuplicateCellIconHeight, kTHDuplicateCellIconHeight)]; 140 | 141 | [iconView setImageFrameStyle:NSImageFrameNone]; 142 | [iconView setImageScaling:NSImageScaleProportionallyUpOrDown]; 143 | [iconView setAutoresizingMask:NSViewMinYMargin|NSViewMaxYMargin|NSViewMaxXMargin]; 144 | [self addSubview:iconView]; 145 | 146 | sizeField = [[THStaticTextField alloc] initWithFrame:NSMakeRect(5, NSMidY(self.bounds)-25, 50, 10)]; 147 | [sizeField setFont:[NSFont labelFontOfSize:9]]; 148 | [sizeField setTextColor:[NSColor redColor]]; 149 | [self addSubview:sizeField]; 150 | 151 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameDidChanged:) name:NSViewFrameDidChangeNotification object:self]; 152 | } 153 | 154 | return self; 155 | } 156 | 157 | - (void)dealloc 158 | { 159 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 160 | } 161 | 162 | - (void)frameDidChanged:(NSNotification *)notify 163 | { 164 | [sizeField setFrame:NSMakeRect(5, NSMidY(self.bounds)-25, 50, 10)]; 165 | } 166 | 167 | - (uint64)fileSize 168 | { 169 | return fileSize; 170 | } 171 | 172 | - (void)setFileSize:(uint64)value 173 | { 174 | if (fileSize == value) 175 | { 176 | return; 177 | } 178 | fileSize = value; 179 | [sizeField setStringValue:[NSString stringWithSize:fileSize]]; 180 | } 181 | 182 | - (NSArray *)fileLists 183 | { 184 | return fileLists; 185 | } 186 | 187 | - (void)setFileLists:(NSArray *)value 188 | { 189 | if (fileLists == value) 190 | { 191 | return; 192 | } 193 | fileLists = value; 194 | 195 | NSString *filePath = [fileLists objectAtIndex:0]; 196 | NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:filePath]; 197 | [icon setSize:iconView.frame.size]; 198 | [iconView setImage:icon]; 199 | 200 | [viewItemList makeObjectsPerformSelector:@selector(removeFromSuperview)]; 201 | for (int i=0; i<[fileLists count]; i++) 202 | { 203 | THPathView *pathView = nil; 204 | if ([viewItemList count] < i+1) 205 | { 206 | pathView = [THPathView pathView]; 207 | pathView.delegate = self; 208 | [viewItemList addObject:pathView]; 209 | }else 210 | { 211 | pathView = [viewItemList objectAtIndex:i]; 212 | } 213 | 214 | pathView.filePath = [fileLists objectAtIndex:i]; 215 | double iconSpacing = kTHDuplicateCellIconSpacingBegin+kTHDuplicateCellIconHeight+kTHDuplicateCellIconSpacingEnd; 216 | pathView.frame = NSMakeRect(iconSpacing, (kTHDuplicateCellMinSize.height-kTHPathViewHeight)/2+i*kTHPathViewHeight, NSWidth(self.frame)-iconSpacing, kTHPathViewHeight); 217 | [self addSubview:pathView]; 218 | } 219 | } 220 | 221 | - (void)drawRect:(NSRect)dirtyRect 222 | { 223 | static NSShadow *shadow = nil; 224 | static dispatch_once_t onceToken; 225 | dispatch_once(&onceToken, ^{ 226 | shadow = [[NSShadow alloc] init]; 227 | [shadow setShadowColor:[NSColor blackColor]]; 228 | [shadow setShadowBlurRadius:2]; 229 | [shadow setShadowOffset:NSMakeSize(1, -1)]; 230 | }); 231 | [shadow set]; 232 | 233 | [[NSColor whiteColor] set]; 234 | 235 | //Draw the border and background 236 | NSBezierPath *roundedRect = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect([self bounds], 5.0, 2.0) 237 | xRadius:6.0 238 | yRadius:6.0]; 239 | [roundedRect fill]; 240 | } 241 | 242 | #pragma mark - 243 | #pragma mark THPathViewDelegate 244 | 245 | - (void)openClick:(THPathView *)cell 246 | { 247 | if (delegate && [delegate respondsToSelector:@selector(cellOpenClick:index:)]) 248 | { 249 | [delegate cellOpenClick:self index:[viewItemList indexOfObject:cell]]; 250 | } 251 | } 252 | 253 | - (void)removeClick:(THPathView *)cell 254 | { 255 | if (delegate && [delegate respondsToSelector:@selector(cellRemoveClick:index:)]) 256 | { 257 | [delegate cellRemoveClick:self index:[viewItemList indexOfObject:cell]]; 258 | } 259 | } 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THDuplicationHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDuplicationHelper.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-5. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString* const THDuplicationNotification; 12 | extern NSString* const THDuplicationFileSize; 13 | extern NSString* const THDuplicationFileHash; 14 | extern NSString* const THDuplicationFileList; 15 | extern NSString* const THDuplicationFinished; 16 | 17 | @interface THDuplicationHelper : NSObject 18 | { 19 | NSMutableDictionary *fileInfo; 20 | dispatch_semaphore_t semaphore; 21 | dispatch_semaphore_t lock; 22 | CFIndex queryCount; 23 | BOOL isStop; 24 | } 25 | @property (assign) dispatch_queue_t notificationQueue; 26 | 27 | @property (strong) NSArray *searchPaths; 28 | @property (strong) NSArray *filterFilePaths; 29 | 30 | @property (strong) NSPredicate *extensionsPredicate; 31 | @property (assign) uint64 minFileSize; 32 | @property (assign) uint64 maxFileSize; 33 | @property (assign) BOOL filterPackage; 34 | 35 | @property (readonly) BOOL searching; 36 | 37 | - (void)startSearch; 38 | - (void)stopSearch; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THDuplicationHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // THDuplicationHelper.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-5. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THDuplicationHelper.h" 10 | #import "THWebDefine.h" 11 | #import "THFileUtility.h" 12 | #import "THLock.h" 13 | 14 | #define kTHDuplictionFileMinSize (1024) 15 | #define kTHDuplictionConcurrentCount 5 16 | 17 | NSString* const THDuplicationNotification = @"THDuplicationNotification"; 18 | NSString* const THDuplicationFileSize = @"THDuplicationFileSize"; 19 | NSString* const THDuplicationFileHash = @"THDuplicationFileHash"; 20 | NSString* const THDuplicationFileList = @"THDuplicationFileList"; 21 | NSString* const THDuplicationFinished = @"THDuplicationFinished"; 22 | 23 | @implementation THDuplicationHelper 24 | @synthesize notificationQueue; 25 | 26 | @synthesize searchPaths; 27 | @synthesize filterFilePaths; 28 | @synthesize extensionsPredicate; 29 | @synthesize minFileSize; 30 | @synthesize maxFileSize; 31 | @synthesize filterPackage; 32 | 33 | @synthesize searching; 34 | 35 | //判断文件的有效性 36 | - (BOOL)fileValid:(NSString *)filePath size:(UInt64 *)size 37 | { 38 | @autoreleasepool { 39 | uint64 fileSize = [THFileUtility fileSizeByPath:filePath]; 40 | if (size) 41 | { 42 | *size = fileSize; 43 | } 44 | if (fileSize < MAX(minFileSize, kTHDuplictionFileMinSize)) 45 | { 46 | return NO; 47 | } 48 | if (maxFileSize > MAX(minFileSize, kTHDuplictionFileMinSize) && fileSize > maxFileSize) 49 | { 50 | return NO; 51 | } 52 | if (extensionsPredicate) 53 | { 54 | NSString *fileExtenstion = [filePath pathExtension]; 55 | return [extensionsPredicate evaluateWithObject:fileExtenstion]; 56 | } 57 | return YES; 58 | } 59 | } 60 | 61 | //判断目录的有效性 62 | - (BOOL)directoryValid:(NSString *)filePath 63 | { 64 | @autoreleasepool { 65 | if (filterFilePaths) 66 | { 67 | for (NSString *filterPath in filterFilePaths) 68 | { 69 | if ([filePath hasPrefix:filterPath]) 70 | { 71 | return NO; 72 | } 73 | } 74 | } 75 | 76 | if (filterPackage && [[NSWorkspace sharedWorkspace] isFilePackageAtPath:filePath]) 77 | { 78 | return NO; 79 | } 80 | return YES; 81 | } 82 | } 83 | 84 | //返回目录的的子目录绝对路径 85 | - (NSArray *)directorySubPath:(NSString *)filePath 86 | { 87 | @autoreleasepool { 88 | NSMutableArray *subPaths = [NSMutableArray array]; 89 | NSArray *subItems = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:filePath error:NULL]; 90 | for (NSString *path in subItems) 91 | { 92 | if ([[path lastPathComponent] hasPrefix:@"."] 93 | ||[[path lastPathComponent] hasPrefix:@"__MACOSX"]) 94 | { 95 | continue; 96 | } 97 | NSString *fullPath = [filePath stringByAppendingPathComponent:path]; 98 | [subPaths addObject:fullPath]; 99 | } 100 | 101 | return [NSArray arrayWithArray:subPaths]; 102 | } 103 | } 104 | 105 | //处理扫描到的文件 106 | - (void)fileDispose:(NSString *)filePath 107 | { 108 | @autoreleasepool { 109 | uint64 fileSize = 0; 110 | BOOL valid = [self fileValid:filePath size:&fileSize]; 111 | if (!valid) 112 | { 113 | return; 114 | } 115 | 116 | //以文件大小为Key 117 | NSNumber *fileKey = [NSNumber numberWithUnsignedLongLong:fileSize]; 118 | 119 | id exist = nil; 120 | 121 | @synchronized(fileInfo){ 122 | exist = [fileInfo objectForKey:fileKey]; 123 | if (!exist) 124 | { 125 | [fileInfo setObject:filePath forKey:fileKey]; 126 | return; 127 | } 128 | } 129 | 130 | //计算文件的hash值 131 | NSString *hashKey = [THWebUtility lazyHashFile:filePath]; 132 | if (!hashKey) 133 | { 134 | return; 135 | } 136 | 137 | //文件大小相同,以下的代码互斥,加锁 138 | //////////////////////////////////////// 139 | [THLock lockForKey:fileKey]; 140 | /////////////////////////////////////// 141 | 142 | //二次判断(防止在hash过程中其它线程已经改变fileInfo结构) 143 | @synchronized(fileInfo){ 144 | exist = [fileInfo objectForKey:fileKey]; 145 | } 146 | 147 | //标识是否已经找到重复项 148 | BOOL duplication = NO; 149 | NSArray *fileList = nil; 150 | 151 | //如果fileInfo中相同大小的为string,刚说明还没有计算过hash值 152 | if ([exist isKindOfClass:[NSString class]]) 153 | { 154 | NSString *existFile = (NSString *)exist; 155 | NSString *existHashKey = [THWebUtility lazyHashFile:existFile]; 156 | if (!existHashKey) 157 | { 158 | //如果计算已存在文件的hash值失败,则覆盖,并退出 159 | NSMutableArray *list = [NSMutableArray arrayWithObject:filePath]; 160 | NSDictionary *hashDic = [NSMutableDictionary dictionaryWithObject:list forKey:hashKey]; 161 | 162 | @synchronized(fileInfo){ 163 | [fileInfo setObject:hashDic forKey:fileKey]; 164 | } 165 | 166 | //////////////////////////////////////// 167 | [THLock unLockForKey:fileKey]; 168 | /////////////////////////////////////// 169 | return; 170 | } 171 | 172 | NSMutableDictionary *hashDic = [NSMutableDictionary dictionaryWithCapacity:2]; 173 | if ([existHashKey isEqualToString:hashKey]) 174 | { 175 | duplication = YES; 176 | NSMutableArray *list = [NSMutableArray arrayWithObjects:filePath,exist, nil]; 177 | [hashDic setObject:list forKey:hashKey]; 178 | fileList = [list copy]; 179 | }else 180 | { 181 | NSMutableArray *list = [NSMutableArray arrayWithObject:filePath]; 182 | [hashDic setObject:list forKey:hashKey]; 183 | list = [NSMutableArray arrayWithObject:exist]; 184 | [hashDic setObject:list forKey:existHashKey]; 185 | } 186 | 187 | @synchronized(fileInfo){ 188 | [fileInfo setObject:hashDic forKey:fileKey]; 189 | } 190 | } 191 | 192 | if ([exist isKindOfClass:[NSMutableDictionary class]]) 193 | { 194 | NSMutableDictionary *hashDic = (NSMutableDictionary *)exist; 195 | 196 | //hash值相同,以下代码就互斥,加锁 197 | //////////////////////////////////////// 198 | [THLock lockForKey:hashKey]; 199 | /////////////////////////////////////// 200 | NSMutableArray *list = [hashDic objectForKey:hashKey]; 201 | if (list) 202 | { 203 | duplication = YES; 204 | @synchronized(list){ 205 | [list addObject:filePath]; 206 | } 207 | fileList = [list copy]; 208 | }else 209 | { 210 | list = [NSMutableArray arrayWithObject:filePath]; 211 | @synchronized(hashDic){ 212 | [hashDic setObject:list forKey:hashKey]; 213 | } 214 | } 215 | //////////////////////////////////////// 216 | [THLock unLockForKey:hashKey]; 217 | /////////////////////////////////////// 218 | } 219 | 220 | //////////////////////////////////////// 221 | [THLock unLockForKey:fileKey]; 222 | /////////////////////////////////////// 223 | 224 | if (duplication) 225 | { 226 | dispatch_async(notificationQueue, ^{ 227 | [self postNotificationSize:fileKey hash:hashKey fileList:fileList]; 228 | }); 229 | } 230 | } 231 | } 232 | 233 | //目录扫描 234 | - (void)searth:(NSString *)filePath isDirectory:(BOOL)isDirectory 235 | { 236 | //对一个文件的排队处理 237 | static void(^handleFile)(NSString *); 238 | handleFile = ^(NSString *currentFilePath){ 239 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 240 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 241 | @autoreleasepool { 242 | [self fileDispose:currentFilePath]; 243 | } 244 | dispatch_semaphore_signal(semaphore); 245 | }); 246 | }; 247 | 248 | @autoreleasepool { 249 | NSArray *subItems = nil; 250 | if (isDirectory) 251 | { 252 | //如果是Package,先当做一个文件找重复 253 | if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filePath]) 254 | { 255 | handleFile(filePath); 256 | } 257 | //文件夹是否满足规则 258 | if (![self directoryValid:filePath]) 259 | { 260 | return; 261 | } 262 | subItems = [self directorySubPath:filePath]; 263 | }else 264 | { 265 | subItems = [NSArray arrayWithObject:filePath]; 266 | } 267 | for (NSString *fullPath in subItems) 268 | { 269 | @autoreleasepool 270 | { 271 | if (isStop) 272 | { 273 | break; 274 | } 275 | 276 | if ([THFileUtility fileIsDirectory:fullPath]) 277 | { 278 | [self searth:fullPath isDirectory:YES]; 279 | } 280 | else if ([THFileUtility fileIsRegular:fullPath]) 281 | { 282 | handleFile(fullPath); 283 | } 284 | } 285 | } 286 | } 287 | } 288 | 289 | - (void)searchByEnum:(NSArray *)filePathList 290 | { 291 | for (NSString *searchPath in filePathList) 292 | { 293 | if ([THFileUtility fileIsDirectory:searchPath]) 294 | { 295 | [self searth:searchPath isDirectory:YES]; 296 | } 297 | else if ([THFileUtility fileIsRegular:searchPath]) 298 | { 299 | [self searth:searchPath isDirectory:NO]; 300 | } 301 | } 302 | } 303 | 304 | - (void)queryUpdateNotification:(NSNotification *)notify 305 | { 306 | @autoreleasepool { 307 | NSString *notificationName = [notify name]; 308 | if ([notificationName isEqualToString:(__bridge NSString *)kMDQueryDidFinishNotification]) 309 | { 310 | isStop = YES; 311 | } 312 | else if ([notificationName isEqualToString:(__bridge NSString *)kMDQueryProgressNotification]) 313 | { 314 | MDQueryRef query = (__bridge MDQueryRef)[notify object]; 315 | CFIndex totalCount = MDQueryGetResultCount(query); 316 | for (CFIndex i=queryCount; i '%lld')",MAX(minFileSize,1024)]; 351 | if (maxFileSize > MAX(minFileSize,1024)) 352 | { 353 | searchScope = [searchScope stringByAppendingFormat:@" && (kMDItemFSSize < '%lld')",maxFileSize]; 354 | } 355 | searchScope = [searchScope stringByAppendingFormat:@"&& (kMDItemContentType != 'public.folder')"]; 356 | searchScope = [searchScope stringByAppendingFormat:@"&& (kMDItemContentType != 'com.apple.mail.emlx')"]; 357 | searchScope = [searchScope stringByAppendingFormat:@"&& (kMDItemContentType != 'public.vcard')"]; 358 | MDQueryRef query = MDQueryCreate(kCFAllocatorDefault, (__bridge CFStringRef)searchScope, NULL, NULL); 359 | MDQuerySetSearchScope(query, (__bridge CFArrayRef)filePathList, 0); 360 | if (!query) 361 | { 362 | return NO; 363 | } 364 | 365 | [[NSNotificationCenter defaultCenter] addObserver:self 366 | selector:@selector(queryUpdateNotification:) 367 | name:(__bridge NSString *)kMDQueryDidFinishNotification 368 | object:(__bridge id)query]; 369 | [[NSNotificationCenter defaultCenter] addObserver:self 370 | selector:@selector(queryUpdateNotification:) 371 | name:(__bridge NSString *)kMDQueryProgressNotification 372 | object:(__bridge id)query]; 373 | BOOL sucess = MDQueryExecute(query, 0); 374 | 375 | while (sucess) 376 | { 377 | if (isStop) 378 | { 379 | break; 380 | } 381 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 382 | usleep(100*1000); 383 | } 384 | 385 | [[NSNotificationCenter defaultCenter] removeObserver:self 386 | name:(__bridge NSString *)kMDQueryDidFinishNotification 387 | object:(__bridge id)query]; 388 | [[NSNotificationCenter defaultCenter] removeObserver:self 389 | name:(__bridge NSString *)kMDQueryProgressNotification 390 | object:(__bridge id)query]; 391 | 392 | MDQueryStop(query); 393 | CFRelease(query); 394 | return sucess; 395 | } 396 | } 397 | 398 | - (void)searchStart 399 | { 400 | //开始检索 401 | searching = YES; 402 | queryCount = 0; 403 | isStop = NO; 404 | 405 | //去除重复、包含的扫描路径 406 | NSMutableArray *validSearchPaths = [NSMutableArray array]; 407 | for (NSString *searchPathA in searchPaths) 408 | { 409 | NSString *validPath = searchPathA; 410 | for (NSString *searchPathB in searchPaths) 411 | { 412 | if (searchPathB != validPath && 413 | [[validPath stringByResolvingSymlinksInPath] hasPrefix:[searchPathB stringByResolvingSymlinksInPath]]) 414 | { 415 | validPath = searchPathB; 416 | } 417 | } 418 | 419 | if (![validSearchPaths containsObject:validPath]) 420 | { 421 | [validSearchPaths addObject:validPath]; 422 | } 423 | } 424 | 425 | //使用spotlight查询 426 | BOOL result = [self searchByQuery:validSearchPaths]; 427 | //使用枚举方式遍历所有目录(阻塞的) 428 | if (!result) 429 | { 430 | [self searchByEnum:validSearchPaths]; 431 | } 432 | 433 | //等待所有线程结束 434 | int waitTread = kTHDuplictionConcurrentCount; 435 | while (waitTread > 0) 436 | { 437 | waitTread--; 438 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 439 | } 440 | 441 | //让semaphore恢复初使状态 442 | while (waitTread < kTHDuplictionConcurrentCount) 443 | { 444 | waitTread ++; 445 | dispatch_semaphore_signal(semaphore); 446 | } 447 | 448 | searching = NO; 449 | 450 | dispatch_async(notificationQueue, ^{ 451 | [self postNotificationSize:nil hash:nil fileList:nil]; 452 | }); 453 | } 454 | 455 | - (id)init 456 | { 457 | self = [super init]; 458 | if (self) 459 | { 460 | notificationQueue = dispatch_get_main_queue(); 461 | semaphore = dispatch_semaphore_create(kTHDuplictionConcurrentCount); 462 | lock = dispatch_semaphore_create(1); 463 | } 464 | return self; 465 | } 466 | 467 | - (void)startSearch 468 | { 469 | if (searching) 470 | { 471 | return; 472 | } 473 | 474 | fileInfo = [[NSMutableDictionary alloc] init]; 475 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 476 | @autoreleasepool { 477 | [self searchStart]; 478 | fileInfo = nil; 479 | } 480 | }); 481 | } 482 | 483 | - (void)stopSearch 484 | { 485 | isStop = YES; 486 | } 487 | 488 | - (void)postNotificationSize:(NSNumber *)sizeKey hash:(NSString *)hashKey fileList:(NSArray *)fileList 489 | { 490 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; 491 | if (sizeKey) [userInfo setObject:sizeKey forKey:THDuplicationFileSize]; 492 | if (hashKey) [userInfo setObject:hashKey forKey:THDuplicationFileHash]; 493 | if (fileList) [userInfo setObject:fileList forKey:THDuplicationFileList]; 494 | if (!sizeKey && !hashKey && !fileList) 495 | { 496 | [userInfo setObject:[NSNumber numberWithBool:!searching] forKey:THDuplicationFinished]; 497 | } 498 | 499 | [[NSNotificationCenter defaultCenter] postNotificationName:THDuplicationNotification object:self userInfo:userInfo]; 500 | } 501 | 502 | @end 503 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THFileUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // THFileUtility.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-2. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THFileUtility : NSObject 12 | 13 | + (BOOL)fileIsRegular:(NSString *)filePath; 14 | + (BOOL)fileIsDirectory:(NSString *)filePath; 15 | + (uint64)fileSizeByPath:(NSString *)filePath; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THFileUtility.m: -------------------------------------------------------------------------------- 1 | // 2 | // THFileUtility.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-2. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THFileUtility.h" 10 | #include 11 | 12 | @implementation THFileUtility 13 | 14 | + (mode_t)fileTypeByPath:(NSString *)filePath 15 | { 16 | struct stat stat1; 17 | if(stat([filePath fileSystemRepresentation], &stat1) == 0) 18 | { 19 | mode_t fileType = stat1.st_mode; 20 | return fileType; 21 | } 22 | return S_IFWHT; 23 | } 24 | 25 | + (BOOL)fileIsRegular:(NSString *)filePath 26 | { 27 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL]; 28 | NSString *fileType = [attributes objectForKey:NSFileType]; 29 | return [fileType isEqualToString:NSFileTypeRegular]; 30 | } 31 | 32 | + (BOOL)fileIsDirectory:(NSString *)filePath 33 | { 34 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL]; 35 | NSString *fileType = [attributes objectForKey:NSFileType]; 36 | return [fileType isEqualToString:NSFileTypeDirectory]; 37 | } 38 | 39 | + (uint64)fileSizeByPath:(NSString *)filePath 40 | { 41 | uint64 size = 0; 42 | MDItemRef item = MDItemCreate(kCFAllocatorDefault, (__bridge CFStringRef)filePath); 43 | if (item) 44 | { 45 | NSNumber *sizeNumber = (__bridge_transfer NSNumber*)MDItemCopyAttribute(item,kMDItemFSSize); 46 | CFRelease(item); 47 | size = [sizeNumber longLongValue]; 48 | if (size > 0) 49 | { 50 | return size; 51 | } 52 | } 53 | 54 | struct stat stat1; 55 | if(stat([filePath fileSystemRepresentation], &stat1) == 0) 56 | { 57 | size = stat1.st_size; 58 | if (size > 0) 59 | { 60 | return size; 61 | } 62 | } 63 | 64 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; 65 | size = [fileHandle seekToEndOfFile]; 66 | [fileHandle closeFile]; 67 | 68 | return size; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McFlipView.h: -------------------------------------------------------------------------------- 1 | // 2 | // McFlipView.h 3 | // McCleaner 4 | // 5 | // Created by wenyuan on 11-10-25. 6 | // Copyright 2011 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface McFlipView : NSView 13 | { 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McFlipView.m: -------------------------------------------------------------------------------- 1 | // 2 | // McFlipView.m 3 | // McCleaner 4 | // 5 | // Created by wenyuan on 11-10-25. 6 | // Copyright 2011 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import "McFlipView.h" 10 | 11 | 12 | @implementation McFlipView 13 | 14 | - (id)initWithFrame:(NSRect)frame { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | // Initialization code here. 18 | } 19 | return self; 20 | } 21 | 22 | - (BOOL)isFlipped 23 | { 24 | return YES; 25 | } 26 | 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // McHeaderView.h 3 | // McUICommon 4 | // 5 | // Created by tanhao on 12-4-24. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | enum 13 | { 14 | kMcHeaderLevelDefault, //level is hightest 15 | kMcHeaderLevelConfirm, 16 | kMcHeaderLevelWeather, 17 | kMcHeaderLevelLogo, 18 | }; 19 | typedef NSInteger McHeaderLevel; 20 | 21 | enum 22 | { 23 | kMcHeaderRepeatForever, 24 | kMcHeaderRepeatOnces 25 | }; 26 | typedef NSInteger McHeaderRepeat; 27 | 28 | #define kMcHeaderViewFrame NSMakeRect(0, 0, 240, 32) 29 | #define kMcHeaderDurationDefault 5 30 | #define kMcHeaderDurationForever 100000000 31 | 32 | @interface McHeaderView : NSView 33 | { 34 | BOOL setUp; 35 | } 36 | 37 | @property (assign) McHeaderLevel level; 38 | @property (assign) McHeaderRepeat repeat; 39 | @property (assign) double duration; 40 | 41 | - (void)dismiss; 42 | 43 | @end -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // McHeaderView.m 3 | // McUICommon 4 | // 5 | // Created by tanhao on 12-4-24. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import "McHeaderView.h" 10 | #import "McHeaderViewController.h" 11 | 12 | @implementation McHeaderView 13 | @synthesize level; 14 | @synthesize repeat; 15 | @synthesize duration; 16 | 17 | 18 | - (void)setUpDefault 19 | { 20 | if (!setUp) 21 | { 22 | setUp = YES; 23 | 24 | self.level = kMcHeaderLevelDefault; 25 | self.repeat = kMcHeaderRepeatOnces; 26 | self.duration = kMcHeaderDurationDefault; 27 | 28 | NSImageView *bgView = [[NSImageView alloc] initWithFrame:self.bounds]; 29 | [bgView setImageFrameStyle:NSImageFrameNone]; 30 | [bgView setImageScaling:NSScaleToFit]; 31 | [bgView setImageAlignment:NSImageAlignCenter]; 32 | [bgView.cell setImageScaling:NSImageScaleAxesIndependently]; 33 | NSString *imgPath = [kSelfBundle pathForImageResource:@"headerBg"]; 34 | NSImage *img = [[NSImage alloc] initWithContentsOfFile:imgPath]; 35 | [bgView setImage:img]; 36 | [self addSubview:bgView positioned:NSWindowBelow relativeTo:nil]; 37 | } 38 | } 39 | 40 | - (id)init 41 | { 42 | self = [super initWithFrame:kMcHeaderViewFrame]; 43 | if (self) 44 | { 45 | [self setUpDefault]; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)awakeFromNib 51 | { 52 | [super awakeFromNib]; 53 | [self setUpDefault]; 54 | } 55 | 56 | - (void)dismiss 57 | { 58 | [[McHeaderViewController sharedController] removeHeaderView:self immediately:YES]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McHeaderViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // McHeaderViewController.h 3 | // MagicanCastle 4 | // 5 | // Created by tanhao on 12-4-18. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "McHeaderView.h" 11 | #import "McImageHeaderView.h" 12 | 13 | @class McLocatedConfirmHeaderView; 14 | @interface McHeaderViewController : NSViewController 15 | { 16 | NSTimer *timer; 17 | NSViewAnimation *showAnimation; 18 | NSMutableArray *viewArray; 19 | McHeaderView *logoHeader; 20 | } 21 | 22 | @property (strong,readonly) McHeaderView *currentHeaderView; 23 | 24 | + (McHeaderViewController *)sharedController; 25 | 26 | - (void)addHeaderView:(McHeaderView *)headerView immediately:(BOOL)immediately; 27 | - (void)removeHeaderView:(McHeaderView *)headerView immediately:(BOOL)immediately; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McHeaderViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // McHeaderViewController.m 3 | // MagicanCastle 4 | // 5 | // Created by tanhao on 12-4-18. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import "McHeaderViewController.h" 10 | #import "McImageHeaderView.h" 11 | #import 12 | 13 | #define kMcTemperatureUnitHeaderViewShowed @"McTemperatureUnitHeaderViewShowed" 14 | #define kMcLocatedConfirmHeaderViewShowed @"McLocatedConfirmHeaderViewShowed" 15 | 16 | @interface McHeaderViewController() 17 | @end 18 | 19 | @implementation McHeaderViewController 20 | @synthesize currentHeaderView; 21 | 22 | + (McHeaderViewController *)sharedController 23 | { 24 | __strong static id instance = nil; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | instance = [[McHeaderViewController alloc] initWithNibName:@"McHeaderViewController" bundle:kSelfBundle]; 28 | }); 29 | return instance; 30 | } 31 | 32 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 33 | { 34 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 35 | if (self) 36 | { 37 | viewArray = [[NSMutableArray alloc] init]; 38 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveWindowWillBeginSheetNotification:) name:NSWindowWillBeginSheetNotification object:nil]; 39 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveWindowDidEndSheetNotification:) name:NSWindowDidEndSheetNotification object:nil]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)dealloc 45 | { 46 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 47 | } 48 | 49 | - (void)awakeFromNib 50 | { 51 | [super awakeFromNib]; 52 | 53 | NSString *imgPath = [kSelfBundle pathForImageResource:@"headerLogo"]; 54 | NSImage *headerImg = [[NSImage alloc] initWithContentsOfFile:imgPath]; 55 | logoHeader = [[McImageHeaderView alloc] initWithImage:headerImg]; 56 | logoHeader.level = kMcHeaderLevelLogo; 57 | logoHeader.repeat = kMcHeaderRepeatForever; 58 | [self addHeaderView:logoHeader immediately:YES]; 59 | } 60 | 61 | #pragma mark - 62 | #pragma mark NSAnimationDelegate 63 | 64 | - (void)animationDidEnd:(NSAnimation*)animation 65 | { 66 | NSArray *animationArr = [(NSViewAnimation *)animation viewAnimations]; 67 | for (NSDictionary *animationDic in animationArr) 68 | { 69 | NSView *aView = [animationDic objectForKey:NSViewAnimationTargetKey]; 70 | NSRect endFrame = [[animationDic objectForKey:NSViewAnimationEndFrameKey] rectValue]; 71 | if (endFrame.origin.y < 0) 72 | { 73 | [aView removeFromSuperview]; 74 | }else 75 | { 76 | [aView setFrame:endFrame]; 77 | } 78 | } 79 | } 80 | 81 | - (void)animationDidStop:(NSAnimation *)animation 82 | { 83 | NSArray *animationArr = [(NSViewAnimation *)animation viewAnimations]; 84 | for (NSDictionary *animationDic in animationArr) 85 | { 86 | NSView *aView = [animationDic objectForKey:NSViewAnimationTargetKey]; 87 | NSRect endFrame = [[animationDic objectForKey:NSViewAnimationEndFrameKey] rectValue]; 88 | if (endFrame.origin.y < 0) 89 | { 90 | [aView removeFromSuperview]; 91 | }else 92 | { 93 | [aView setFrame:endFrame]; 94 | } 95 | } 96 | } 97 | 98 | - (void)replaceView:(McHeaderView *)aView 99 | { 100 | if (aView == currentHeaderView) 101 | { 102 | return; 103 | } 104 | 105 | if (!aView) 106 | { 107 | [currentHeaderView removeFromSuperview]; 108 | currentHeaderView = nil; 109 | return; 110 | } 111 | 112 | //create timer for next replace 113 | [timer invalidate]; 114 | timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:aView.duration] interval:aView.duration target:self selector:@selector(timerMethod) userInfo:nil repeats:NO]; 115 | [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 116 | 117 | if (currentHeaderView) 118 | { 119 | //[[self.view animator] replaceSubview:currentHeaderView with:aView]; 120 | { 121 | [showAnimation stopAnimation]; 122 | showAnimation = nil; 123 | 124 | [aView setFrameOrigin:NSMakePoint(0, aView.bounds.size.height)]; 125 | [self.view addSubview:aView]; 126 | NSDictionary *aViewAnimationDic = [[NSDictionary alloc] initWithObjectsAndKeys: 127 | aView,NSViewAnimationTargetKey, 128 | [NSValue valueWithRect:aView.frame],NSViewAnimationStartFrameKey, 129 | [NSValue valueWithRect:currentHeaderView.frame],NSViewAnimationEndFrameKey, nil]; 130 | NSRect currentEndFrame = currentHeaderView.frame; 131 | currentEndFrame.origin.y = -currentEndFrame.size.height; 132 | NSDictionary *currentViewAnimationDic = [[NSDictionary alloc] initWithObjectsAndKeys: 133 | currentHeaderView,NSViewAnimationTargetKey, 134 | [NSValue valueWithRect:currentHeaderView.frame],NSViewAnimationStartFrameKey, 135 | [NSValue valueWithRect:currentEndFrame],NSViewAnimationEndFrameKey, nil]; 136 | NSArray *animationArr = [[NSArray alloc] initWithObjects:aViewAnimationDic,currentViewAnimationDic, nil]; 137 | showAnimation = [[NSViewAnimation alloc] initWithViewAnimations:animationArr]; 138 | [showAnimation setDelegate:self]; 139 | [showAnimation startAnimation]; 140 | } 141 | 142 | if (currentHeaderView.repeat == kMcHeaderRepeatOnces) 143 | { 144 | [viewArray removeObject:currentHeaderView]; 145 | } 146 | }else 147 | { 148 | [self.view addSubview:aView]; 149 | } 150 | 151 | currentHeaderView = aView; 152 | } 153 | 154 | - (void)timerMethod 155 | { 156 | if ([viewArray count] == 0) 157 | { 158 | [self replaceView:nil]; 159 | return; 160 | } 161 | 162 | McHeaderView *aView = nil; 163 | 164 | if (currentHeaderView) 165 | { 166 | NSInteger idx = [viewArray indexOfObject:currentHeaderView]; 167 | if (idx == NSNotFound || idx+1 >= [viewArray count]) 168 | { 169 | aView = [viewArray objectAtIndex:0]; 170 | }else 171 | { 172 | aView = [viewArray objectAtIndex:idx+1]; 173 | } 174 | }else 175 | { 176 | aView = [viewArray objectAtIndex:0]; 177 | } 178 | 179 | [self replaceView:aView]; 180 | } 181 | 182 | - (void)addHeaderView:(McHeaderView *)headerView immediately:(BOOL)immediately 183 | { 184 | [viewArray addObject:headerView]; 185 | 186 | [viewArray sortUsingComparator:^NSComparisonResult(McHeaderView * obj1, McHeaderView * obj2) { 187 | NSComparisonResult result = NSOrderedSame; 188 | if (obj1.level < obj2.level) result = NSOrderedAscending; 189 | if (obj1.level > obj2.level) result = NSOrderedDescending; 190 | return result; 191 | }]; 192 | 193 | if (immediately) 194 | { 195 | [self replaceView:headerView]; 196 | } 197 | } 198 | 199 | - (void)removeHeaderView:(McHeaderView *)headerView immediately:(BOOL)immediately 200 | { 201 | if ([viewArray containsObject:headerView]) 202 | { 203 | [viewArray removeObject:headerView]; 204 | if (immediately && currentHeaderView==headerView) 205 | { 206 | [self timerMethod]; 207 | } 208 | } 209 | } 210 | 211 | #pragma mark - 212 | #pragma NotificationMethod 213 | 214 | - (void)receiveWindowWillBeginSheetNotification:(NSNotification *)notify 215 | { 216 | NSWindow *notifyWindow = [notify object]; 217 | if (notifyWindow == [self.view window]) 218 | { 219 | [timer invalidate]; 220 | timer = nil; 221 | } 222 | } 223 | 224 | - (void)receiveWindowDidEndSheetNotification:(NSNotification *)notify 225 | { 226 | if (timer) return; 227 | if (!currentHeaderView) return; 228 | NSWindow *notifyWindow = [notify object]; 229 | if (notifyWindow == [self.view window]) 230 | { 231 | NSTimeInterval duration = [currentHeaderView duration]; 232 | timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:duration] interval:duration target:self selector:@selector(timerMethod) userInfo:nil repeats:NO]; 233 | [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 234 | return; 235 | } 236 | } 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McHeaderViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1080 5 | 12C60 6 | 2843 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 2843 12 | 13 | 14 | NSCustomObject 15 | NSCustomView 16 | 17 | 18 | com.apple.InterfaceBuilder.CocoaPlugin 19 | 20 | 21 | PluginDependencyRecalculationVersion 22 | 23 | 24 | 25 | 26 | McHeaderViewController 27 | 28 | 29 | FirstResponder 30 | 31 | 32 | NSApplication 33 | 34 | 35 | 36 | 268 37 | {240, 32} 38 | 39 | 40 | 41 | McFlipView 42 | 43 | 44 | 45 | 46 | 47 | 48 | view 49 | 50 | 51 | 52 | 2 53 | 54 | 55 | 56 | 57 | 58 | 0 59 | 60 | 61 | 62 | 63 | 64 | -2 65 | 66 | 67 | File's Owner 68 | 69 | 70 | -1 71 | 72 | 73 | First Responder 74 | 75 | 76 | -3 77 | 78 | 79 | Application 80 | 81 | 82 | 1 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | com.apple.InterfaceBuilder.CocoaPlugin 91 | com.apple.InterfaceBuilder.CocoaPlugin 92 | com.apple.InterfaceBuilder.CocoaPlugin 93 | com.apple.InterfaceBuilder.CocoaPlugin 94 | 95 | 96 | 97 | 98 | 99 | 19 100 | 101 | 102 | 103 | 104 | McFlipView 105 | NSView 106 | 107 | IBProjectSource 108 | ./Classes/McFlipView.h 109 | 110 | 111 | 112 | McHeaderViewController 113 | NSViewController 114 | 115 | IBProjectSource 116 | ./Classes/McHeaderViewController.h 117 | 118 | 119 | 120 | 121 | 0 122 | IBCocoaFramework 123 | YES 124 | 3 125 | 126 | 127 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McImageHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // McImageHeaderView.h 3 | // McUICommon 4 | // 5 | // Created by tanhao on 12-4-24. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import "McHeaderView.h" 10 | 11 | @interface McImageHeaderView : McHeaderView 12 | 13 | - (id)initWithImage:(NSImage *)image; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THHeaderView/McImageHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // McImageHeaderView.m 3 | // McUICommon 4 | // 5 | // Created by tanhao on 12-4-24. 6 | // Copyright (c) 2012 Magican Software Ltd. All rights reserved. 7 | // 8 | 9 | #import "McImageHeaderView.h" 10 | 11 | @implementation McImageHeaderView 12 | 13 | - (id)initWithImage:(NSImage *)image 14 | { 15 | self = [super init]; 16 | if (self) 17 | { 18 | NSImageView *imageView = [[NSImageView alloc] initWithFrame:self.bounds]; 19 | [imageView setImageFrameStyle:NSImageFrameNone]; 20 | [imageView setImageScaling:NSScaleProportionally]; 21 | [imageView setImageAlignment:NSImageAlignCenter]; 22 | [imageView setImage:image]; 23 | [self addSubview:imageView]; 24 | } 25 | return self; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THLock.h: -------------------------------------------------------------------------------- 1 | // 2 | // THLock.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THLock : NSObject 12 | 13 | + (void)lockForKey:(id)key; 14 | + (void)unLockForKey:(id)key; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THLock.m: -------------------------------------------------------------------------------- 1 | // 2 | // THLock.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THLock.h" 10 | 11 | @implementation THLock 12 | 13 | static NSMutableDictionary *lockList = nil; 14 | 15 | + (void)lockCreate 16 | { 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | lockList = [[NSMutableDictionary alloc] init]; 20 | }); 21 | } 22 | 23 | + (void)lockForKey:(id)key 24 | { 25 | if (!lockList) 26 | { 27 | [self lockCreate]; 28 | } 29 | 30 | NSLock *lock = NULL; 31 | @synchronized(lockList){ 32 | lock = [lockList objectForKey:key]; 33 | if (!lock) 34 | { 35 | lock = [[NSLock alloc] init]; 36 | [lockList setObject:lock forKey:key]; 37 | } 38 | } 39 | [lock lock]; 40 | } 41 | 42 | + (void)unLockForKey:(id)key 43 | { 44 | NSLock *lock = NULL; 45 | @synchronized(lockList){ 46 | lock = [lockList objectForKey:key]; 47 | } 48 | 49 | [lock unlock]; 50 | @synchronized(lockList){ 51 | if ([lock tryLock]){ 52 | [lockList removeObjectForKey:key]; 53 | [lock unlock]; 54 | } 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/ImageBrowserBackgroundLayer.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: ImageBrowserBackgroundLayer.h 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | #import 54 | #import 55 | 56 | 57 | @interface ImageBrowserBackgroundLayer : CALayer { 58 | __unsafe_unretained IKImageBrowserView *owner; 59 | } 60 | @property (unsafe_unretained) IKImageBrowserView *owner; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/ImageBrowserBackgroundLayer.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: ImageBrowserBackgroundLayer.m 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | 54 | #import "ImageBrowserBackgroundLayer.h" 55 | #import "Utilities.h" 56 | 57 | static CGImageRef backgroundImage() 58 | { 59 | static CGImageRef image = NULL; 60 | 61 | if(image == NULL) 62 | image = createImageWithName(@"metal_background"); 63 | 64 | return image; 65 | } 66 | 67 | 68 | @implementation ImageBrowserBackgroundLayer 69 | 70 | @synthesize owner; 71 | 72 | // ------------------------------------------------------------------------- 73 | // init 74 | // ------------------------------------------------------------------------- 75 | - (id) init 76 | { 77 | if((self = [super init])){ 78 | //needs to redraw when bounds change 79 | [self setNeedsDisplayOnBoundsChange:YES]; 80 | } 81 | 82 | return self; 83 | } 84 | 85 | // ------------------------------------------------------------------------- 86 | // actionForKey: 87 | // 88 | // always return nil, to never animate 89 | // ------------------------------------------------------------------------- 90 | - (id)actionForKey:(NSString *)event 91 | { 92 | return nil; 93 | } 94 | 95 | // ------------------------------------------------------------------------- 96 | // drawInContext: 97 | // 98 | // draw a metal background that scrolls when the image browser scroll 99 | // ------------------------------------------------------------------------- 100 | - (void)drawInContext:(CGContextRef)context 101 | { 102 | //retreive bounds and visible rect 103 | NSRect visibleRect = [owner visibleRect]; 104 | NSRect bounds = [owner bounds]; 105 | 106 | //retreive background image 107 | CGImageRef image = backgroundImage(); 108 | float width = (float) CGImageGetWidth(image); 109 | float height = (float) CGImageGetHeight(image); 110 | 111 | //compute coordinates to fill the view 112 | float left, top, right, bottom; 113 | 114 | top = bounds.size.height - NSMaxY(visibleRect); 115 | top = fmod(top, height); 116 | top = height - top; 117 | 118 | right = NSMaxX(visibleRect); 119 | bottom = -height; 120 | 121 | // tile the image and take in account the offset to 'emulate' a scrolling background 122 | for (top = visibleRect.size.height-top; top>bottom; top -= height){ 123 | for(left=0; left 54 | #import 55 | 56 | 57 | @interface ImageBrowserCell : IKImageBrowserCell { 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/ImageBrowserCell.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: ImageBrowserCell.m 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | 54 | #import "ImageBrowserCell.h" 55 | #import "Utilities.h" 56 | 57 | 58 | //--------------------------------------------------------------------------------- 59 | // glossyImage 60 | // 61 | // utilty function that creates, caches and returns the image named glossy.png 62 | //--------------------------------------------------------------------------------- 63 | static CGImageRef glossyImage() 64 | { 65 | static CGImageRef image = NULL; 66 | 67 | if(image == NULL) 68 | image = createImageWithName(@"glossy.png"); 69 | 70 | return image; 71 | } 72 | 73 | //--------------------------------------------------------------------------------- 74 | // pinImage 75 | // 76 | // utilty function that creates, caches and returns the image named pin.tiff 77 | //--------------------------------------------------------------------------------- 78 | static CGImageRef pinImage() 79 | { 80 | static CGImageRef image = NULL; 81 | 82 | if(image == NULL) 83 | image = createImageWithName(@"pin.tiff"); 84 | 85 | return image; 86 | } 87 | 88 | 89 | @implementation ImageBrowserCell 90 | 91 | //--------------------------------------------------------------------------------- 92 | // layerForType: 93 | // 94 | // provides the layers for the given types 95 | //--------------------------------------------------------------------------------- 96 | - (CALayer *) layerForType:(NSString*) type 97 | { 98 | CGColorRef color; 99 | 100 | //retrieve some usefull rects 101 | NSRect frame = [self frame]; 102 | NSRect imageFrame = [self imageFrame]; 103 | NSRect relativeImageFrame = NSMakeRect(imageFrame.origin.x - frame.origin.x, imageFrame.origin.y - frame.origin.y, imageFrame.size.width, imageFrame.size.height); 104 | 105 | /* place holder layer */ 106 | if(type == IKImageBrowserCellPlaceHolderLayer){ 107 | //create a place holder layer 108 | CALayer *layer = [CALayer layer]; 109 | layer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height); 110 | 111 | CALayer *placeHolderLayer = [CALayer layer]; 112 | placeHolderLayer.frame = *(CGRect*) &relativeImageFrame; 113 | 114 | CGFloat fillComponents[4] = {1.0, 1.0, 1.0, 0.3}; 115 | CGFloat strokeComponents[4] = {1.0, 1.0, 1.0, 0.9}; 116 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 117 | 118 | //set a background color 119 | color = CGColorCreate(colorSpace, fillComponents); 120 | [placeHolderLayer setBackgroundColor:color]; 121 | CFRelease(color); 122 | 123 | //set a stroke color 124 | color = CGColorCreate(colorSpace, strokeComponents); 125 | [placeHolderLayer setBorderColor:color]; 126 | CFRelease(color); 127 | 128 | [placeHolderLayer setBorderWidth:2.0]; 129 | [placeHolderLayer setCornerRadius:10]; 130 | CFRelease(colorSpace); 131 | 132 | [layer addSublayer:placeHolderLayer]; 133 | 134 | return layer; 135 | } 136 | 137 | /* foreground layer */ 138 | if(type == IKImageBrowserCellForegroundLayer){ 139 | //no foreground layer on place holders 140 | if([self cellState] != IKImageStateReady) 141 | return nil; 142 | 143 | //create a foreground layer that will contain several childs layer 144 | CALayer *layer = [CALayer layer]; 145 | layer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height); 146 | 147 | NSRect imageContainerFrame = [self imageContainerFrame]; 148 | NSRect relativeImageContainerFrame = NSMakeRect(imageContainerFrame.origin.x - frame.origin.x, imageContainerFrame.origin.y - frame.origin.y, imageContainerFrame.size.width, imageContainerFrame.size.height); 149 | 150 | //add a glossy overlay 151 | CALayer *glossyLayer = [CALayer layer]; 152 | glossyLayer.frame = *(CGRect*) &relativeImageContainerFrame; 153 | [glossyLayer setContents:(__bridge id)glossyImage()]; 154 | [layer addSublayer:glossyLayer]; 155 | 156 | //add a pin icon 157 | CALayer *pinLayer = [CALayer layer]; 158 | [pinLayer setContents:(__bridge id)pinImage()]; 159 | pinLayer.frame = CGRectMake((frame.size.width/2)+8, frame.size.height - 12, 12, 15); 160 | [layer addSublayer:pinLayer]; 161 | 162 | return layer; 163 | } 164 | 165 | /* selection layer */ 166 | if(type == IKImageBrowserCellSelectionLayer){ 167 | 168 | //create a selection layer 169 | CALayer *selectionLayer = [CALayer layer]; 170 | selectionLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height); 171 | 172 | //CGFloat fillComponents[4] = {1.0, 0, 0.5, 0.3}; 173 | //CGFloat strokeComponents[4] = {1.0, 0.0, 0.5, 1.0}; 174 | 175 | CGFloat fillComponents[4] = {0.2, 0.2, 0.2, 0.3}; 176 | CGFloat strokeComponents[4] = {0.8, 0.8, 0.8, 1.0}; 177 | 178 | //set a background color 179 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 180 | color = CGColorCreate(colorSpace, fillComponents); 181 | [selectionLayer setBackgroundColor:color]; 182 | CFRelease(color); 183 | 184 | //set a border color 185 | color = CGColorCreate(colorSpace, strokeComponents); 186 | [selectionLayer setBorderColor:color]; 187 | CFRelease(color); 188 | 189 | [selectionLayer setBorderWidth:2.0]; 190 | [selectionLayer setCornerRadius:5]; 191 | 192 | return selectionLayer; 193 | } 194 | 195 | /* background layer */ 196 | if(type == IKImageBrowserCellBackgroundLayer){ 197 | //no background layer on place holders 198 | if([self cellState] != IKImageStateReady) 199 | return nil; 200 | 201 | CALayer *layer = [CALayer layer]; 202 | layer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height); 203 | 204 | NSRect backgroundRect = NSMakeRect(0, 0, frame.size.width, frame.size.height); 205 | 206 | CALayer *photoBackgroundLayer = [CALayer layer]; 207 | photoBackgroundLayer.frame = *(CGRect*) &backgroundRect; 208 | 209 | CGFloat fillComponents[4] = {0.95, 0.95, 0.95, 0.75}; 210 | CGFloat strokeComponents[4] = {0.2, 0.2, 0.2, 0.5}; 211 | 212 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 213 | 214 | color = CGColorCreate(colorSpace, fillComponents); 215 | [photoBackgroundLayer setBackgroundColor:color]; 216 | CFRelease(color); 217 | 218 | color = CGColorCreate(colorSpace, strokeComponents); 219 | [photoBackgroundLayer setBorderColor:color]; 220 | CFRelease(color); 221 | 222 | [photoBackgroundLayer setBorderWidth:1.0]; 223 | [photoBackgroundLayer setShadowOpacity:0.5]; 224 | [photoBackgroundLayer setCornerRadius:3]; 225 | 226 | CFRelease(colorSpace); 227 | 228 | [layer addSublayer:photoBackgroundLayer]; 229 | 230 | return layer; 231 | } 232 | 233 | return nil; 234 | } 235 | 236 | //--------------------------------------------------------------------------------- 237 | // imageFrame 238 | // 239 | // define where the image should be drawn 240 | //--------------------------------------------------------------------------------- 241 | - (NSRect) imageFrame 242 | { 243 | //get default imageFrame and aspect ratio 244 | NSRect imageFrame = [super imageFrame]; 245 | 246 | if(imageFrame.size.height == 0 || imageFrame.size.width == 0) return NSZeroRect; 247 | 248 | float aspectRatio = imageFrame.size.width / imageFrame.size.height; 249 | 250 | // compute the rectangle included in container with a margin of at least 10 pixel at the bottom, 5 pixel at the top and keep a correct aspect ratio 251 | NSRect container = [self imageContainerFrame]; 252 | container = NSInsetRect(container, 8, 8); 253 | 254 | if(container.size.height <= 0) return NSZeroRect; 255 | 256 | float containerAspectRatio = container.size.width / container.size.height; 257 | 258 | if(containerAspectRatio > aspectRatio){ 259 | imageFrame.size.height = container.size.height; 260 | imageFrame.origin.y = container.origin.y; 261 | imageFrame.size.width = imageFrame.size.height * aspectRatio; 262 | imageFrame.origin.x = container.origin.x + (container.size.width - imageFrame.size.width)*0.5; 263 | } 264 | else{ 265 | imageFrame.size.width = container.size.width; 266 | imageFrame.origin.x = container.origin.x; 267 | imageFrame.size.height = imageFrame.size.width / aspectRatio; 268 | imageFrame.origin.y = container.origin.y + container.size.height - imageFrame.size.height; 269 | } 270 | 271 | //round it 272 | imageFrame.origin.x = floorf(imageFrame.origin.x); 273 | imageFrame.origin.y = floorf(imageFrame.origin.y); 274 | imageFrame.size.width = ceilf(imageFrame.size.width); 275 | imageFrame.size.height = ceilf(imageFrame.size.height); 276 | 277 | return imageFrame; 278 | } 279 | 280 | //--------------------------------------------------------------------------------- 281 | // imageContainerFrame 282 | // 283 | // override the default image container frame 284 | //--------------------------------------------------------------------------------- 285 | - (NSRect) imageContainerFrame 286 | { 287 | NSRect container = [super frame]; 288 | 289 | //make the image container 15 pixels up 290 | container.origin.y += 15; 291 | container.size.height -= 15; 292 | 293 | return container; 294 | } 295 | 296 | //--------------------------------------------------------------------------------- 297 | // titleFrame 298 | // 299 | // override the default frame for the title 300 | //--------------------------------------------------------------------------------- 301 | - (NSRect) titleFrame 302 | { 303 | //get the default frame for the title 304 | NSRect titleFrame = [super titleFrame]; 305 | 306 | //move the title inside the 'photo' background image 307 | NSRect container = [self frame]; 308 | titleFrame.origin.y = container.origin.y + 3; 309 | 310 | //make sure the title has a 7px margin with the left/right borders 311 | float margin = titleFrame.origin.x - (container.origin.x + 2); 312 | if(margin < 0) 313 | titleFrame = NSInsetRect(titleFrame, -margin, 0); 314 | 315 | return titleFrame; 316 | } 317 | 318 | //--------------------------------------------------------------------------------- 319 | // selectionFrame 320 | // 321 | // make the selection frame a little bit larger than the default one 322 | //--------------------------------------------------------------------------------- 323 | - (NSRect) selectionFrame 324 | { 325 | return NSInsetRect([self frame], -10, -10); 326 | } 327 | 328 | @end 329 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/ImageBrowserView.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: ImageBrowserView.h 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | #import 54 | #import 55 | 56 | 57 | @interface ImageBrowserView : IKImageBrowserView { 58 | NSRect lastVisibleRect; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/ImageBrowserView.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: ImageBrowserView.m 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | #import "ImageBrowserView.h" 54 | #import "ImageBrowserCell.h" 55 | 56 | 57 | @implementation ImageBrowserView 58 | 59 | //--------------------------------------------------------------------------------- 60 | // newCellForRepresentedItem: 61 | // 62 | // Allocate and return our own cell class for the specified item. The returned cell must not be autoreleased 63 | //--------------------------------------------------------------------------------- 64 | - (IKImageBrowserCell *) newCellForRepresentedItem:(id) cell 65 | { 66 | return [[ImageBrowserCell alloc] init]; 67 | } 68 | 69 | //--------------------------------------------------------------------------------- 70 | // drawRect: 71 | // 72 | // override draw rect and force the background layer to redraw if the view did resize or did scroll 73 | //--------------------------------------------------------------------------------- 74 | - (void) drawRect:(NSRect) rect 75 | { 76 | //retrieve the visible area 77 | NSRect visibleRect = [self visibleRect]; 78 | 79 | //compare with the visible rect at the previous frame 80 | if(!NSEqualRects(visibleRect, lastVisibleRect)){ 81 | //we did scroll or resize, redraw the background 82 | [[self backgroundLayer] setNeedsDisplay]; 83 | 84 | //update last visible rect 85 | lastVisibleRect = visibleRect; 86 | } 87 | 88 | [super drawRect:rect]; 89 | } 90 | 91 | - (void)mouseUp:(NSEvent *)theEvent 92 | { 93 | if (theEvent.clickCount > 1) 94 | { 95 | return; 96 | } 97 | [super mouseUp:theEvent]; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/THPathSelectedViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPathSelectedView.h 3 | // image-browser-appearance 4 | // 5 | // Created by TanHao on 12-11-12. 6 | // 7 | // 8 | 9 | #import 10 | #import "ImageBrowserView.h" 11 | 12 | @class THBarView; 13 | @interface THPathSelectedViewController : NSViewController 14 | { 15 | IBOutlet THBarView *barView; 16 | IBOutlet ImageBrowserView *imageBrowser; 17 | NSMutableArray *images; 18 | NSMutableArray *importedImages; 19 | } 20 | - (IBAction)addClick:(id)sender; 21 | - (IBAction)removeClick:(id)sender; 22 | 23 | - (NSArray *)filePaths; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/THPathSelectedViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPathSelectedView.m 3 | // image-browser-appearance 4 | // 5 | // Created by TanHao on 12-11-12. 6 | // 7 | // 8 | 9 | #import "THPathSelectedViewController.h" 10 | #import "ImageBrowserBackgroundLayer.h" 11 | #import "THBarView.h" 12 | 13 | @interface myImageObject : NSObject 14 | { 15 | NSString* path; 16 | } 17 | @end 18 | 19 | @implementation myImageObject 20 | 21 | - (NSString *)path 22 | { 23 | return path; 24 | } 25 | 26 | - (void)setPath:(NSString*)inPath 27 | { 28 | if (path != inPath) 29 | { 30 | path = inPath; 31 | } 32 | } 33 | 34 | #pragma mark - 35 | #pragma mark item data source protocol 36 | 37 | - (NSString*)imageRepresentationType 38 | { 39 | return IKImageBrowserNSImageRepresentationType; 40 | } 41 | 42 | - (id)imageRepresentation 43 | { 44 | return [[NSWorkspace sharedWorkspace] iconForFile:path]; 45 | } 46 | 47 | - (NSString*)imageUID 48 | { 49 | return path; 50 | } 51 | 52 | - (NSString*)imageTitle 53 | { 54 | return [[path lastPathComponent] stringByDeletingPathExtension]; 55 | } 56 | 57 | - (NSString*)imageSubtitle 58 | { 59 | return [path pathExtension]; 60 | } 61 | @end 62 | 63 | 64 | @implementation THPathSelectedViewController 65 | 66 | - (id)init 67 | { 68 | self = [super initWithNibName:@"THPathSelectedViewController" bundle:[NSBundle bundleForClass:self.class]]; 69 | return self; 70 | } 71 | 72 | - (void)awakeFromNib 73 | { 74 | [super awakeFromNib]; 75 | 76 | imageBrowser.dataSource = self; 77 | imageBrowser.delegate = self; 78 | 79 | images = [[NSMutableArray alloc] init]; 80 | importedImages = [[NSMutableArray alloc] init]; 81 | 82 | // Allow reordering, animations and set the dragging destination delegate. 83 | [imageBrowser setAllowsReordering:YES]; 84 | [imageBrowser setAnimates:YES]; 85 | [imageBrowser setDraggingDestinationDelegate:self]; 86 | 87 | // customize the appearance 88 | [imageBrowser setCellsStyleMask:IKCellsStyleTitled]; 89 | 90 | // background layer 91 | ImageBrowserBackgroundLayer *backgroundLayer = [[ImageBrowserBackgroundLayer alloc] init]; 92 | [imageBrowser setBackgroundLayer:backgroundLayer]; 93 | backgroundLayer.owner = imageBrowser; 94 | 95 | //-- change default font 96 | // create a centered paragraph style 97 | NSMutableParagraphStyle *paraphStyle = [[NSMutableParagraphStyle alloc] init]; 98 | [paraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 99 | [paraphStyle setAlignment:NSCenterTextAlignment]; 100 | 101 | NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; 102 | [attributes setObject:[NSFont systemFontOfSize:10] forKey:NSFontAttributeName]; 103 | [attributes setObject:paraphStyle forKey:NSParagraphStyleAttributeName]; 104 | [attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName]; 105 | [imageBrowser setValue:attributes forKey:IKImageBrowserCellsTitleAttributesKey]; 106 | 107 | attributes = [[NSMutableDictionary alloc] init]; 108 | [attributes setObject:[NSFont boldSystemFontOfSize:10] forKey:NSFontAttributeName]; 109 | [attributes setObject:paraphStyle forKey:NSParagraphStyleAttributeName]; 110 | [attributes setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName]; 111 | 112 | [imageBrowser setValue:attributes forKey:IKImageBrowserCellsHighlightedTitleAttributesKey]; 113 | 114 | //change intercell spacing 115 | [imageBrowser setIntercellSpacing:NSMakeSize(10, 10)]; 116 | 117 | //change selection color 118 | [imageBrowser setValue:[NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:1.0] forKey:IKImageBrowserSelectionColorKey]; 119 | 120 | //set initial zoom value 121 | [imageBrowser setZoomValue:0.25]; 122 | 123 | [barView setTitle:THLocaleString(@"Scanning Paths")]; 124 | } 125 | 126 | - (void)updateDatasource 127 | { 128 | [images addObjectsFromArray:importedImages]; 129 | [importedImages removeAllObjects]; 130 | [imageBrowser reloadData]; 131 | } 132 | 133 | - (IBAction)addClick:(id)sender 134 | { 135 | NSOpenPanel *openPanel = [[NSOpenPanel alloc] init]; 136 | [openPanel setCanChooseFiles:YES]; 137 | [openPanel setCanChooseDirectories:YES]; 138 | [openPanel setAllowsMultipleSelection:YES]; 139 | 140 | /* 141 | if ([openPanel runModal] == NSFileHandlingPanelOKButton) 142 | { 143 | NSArray *fileURLs = [openPanel URLs]; 144 | NSMutableArray *filePaths = [NSMutableArray arrayWithCapacity:[fileURLs count]]; 145 | for (NSURL *aUrl in fileURLs) 146 | { 147 | [filePaths addObject:[aUrl path]]; 148 | } 149 | [self addImagesWithPaths:filePaths]; 150 | [self updateDatasource]; 151 | } 152 | */ 153 | 154 | [openPanel beginSheetModalForWindow:[self.view window] 155 | completionHandler:^(NSInteger result) { 156 | if (result == NSOKButton) 157 | { 158 | NSArray *fileURLs = [openPanel URLs]; 159 | NSMutableArray *filePaths = [NSMutableArray arrayWithCapacity:[fileURLs count]]; 160 | for (NSURL *aUrl in fileURLs) 161 | { 162 | [filePaths addObject:[aUrl path]]; 163 | } 164 | [self addImagesWithPaths:filePaths]; 165 | [self updateDatasource]; 166 | 167 | } 168 | }]; 169 | } 170 | 171 | - (IBAction)removeClick:(id)sender 172 | { 173 | NSIndexSet *indexes = [imageBrowser selectionIndexes]; 174 | [images removeObjectsAtIndexes:indexes]; 175 | [self updateDatasource]; 176 | } 177 | 178 | #pragma mark - 179 | #pragma mark IKImageBrowserDataSource 180 | 181 | // Implement the image browser data source protocol . 182 | // The data source representation is a simple mutable array. 183 | // ------------------------------------------------------------------------- 184 | - (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)view 185 | { 186 | // The item count to display is the datadsource item count. 187 | return [images count]; 188 | } 189 | 190 | // ------------------------------------------------------------------------- 191 | // imageBrowser:view:index: 192 | // ------------------------------------------------------------------------- 193 | - (id)imageBrowser:(IKImageBrowserView *) view itemAtIndex:(NSUInteger) index 194 | { 195 | return [images objectAtIndex:index]; 196 | } 197 | 198 | // ------------------------------------------------------------------------- 199 | // The user wants to delete images, so remove these entries from the data source. 200 | // ------------------------------------------------------------------------- 201 | - (void)imageBrowser:(IKImageBrowserView*)view removeItemsAtIndexes: (NSIndexSet*)indexes 202 | { 203 | [images removeObjectsAtIndexes:indexes]; 204 | } 205 | 206 | // ------------------------------------------------------------------------- 207 | // The user wants to reorder images, update the datadsource and the browser 208 | // will reflect our changes. 209 | // ------------------------------------------------------------------------- 210 | - (BOOL)imageBrowser:(IKImageBrowserView*)view moveItemsAtIndexes: (NSIndexSet*)indexes toIndex:(unsigned int)destinationIndex 211 | { 212 | NSInteger index; 213 | NSMutableArray* temporaryArray; 214 | 215 | temporaryArray = [[NSMutableArray alloc] init]; 216 | 217 | // First remove items from the data source and keep them in a temporary array. 218 | for (index = [indexes lastIndex]; index != NSNotFound; index = [indexes indexLessThanIndex:index]) 219 | { 220 | if (index < destinationIndex) 221 | destinationIndex --; 222 | 223 | id obj = [images objectAtIndex:index]; 224 | [temporaryArray addObject:obj]; 225 | [images removeObjectAtIndex:index]; 226 | } 227 | 228 | // Then insert the removed items at the appropriate location. 229 | NSInteger n = [temporaryArray count]; 230 | for (index = 0; index < n; index++) 231 | { 232 | [images insertObject:[temporaryArray objectAtIndex:index] atIndex:destinationIndex]; 233 | } 234 | 235 | return YES; 236 | } 237 | 238 | 239 | #pragma mark - 240 | #pragma mark drag n drop 241 | 242 | - (NSDragOperation)draggingEntered:(id )sender 243 | { 244 | return NSDragOperationCopy; 245 | } 246 | 247 | - (NSDragOperation)draggingUpdated:(id )sender 248 | { 249 | return NSDragOperationCopy; 250 | } 251 | 252 | - (BOOL)performDragOperation:(id )sender 253 | { 254 | NSData* data = nil; 255 | NSPasteboard* pasteboard = [sender draggingPasteboard]; 256 | 257 | // Look for paths on the pasteboard. 258 | if ([[pasteboard types] containsObject:NSFilenamesPboardType]) 259 | data = [pasteboard dataForType:NSFilenamesPboardType]; 260 | 261 | if (data) 262 | { 263 | NSString* errorDescription; 264 | 265 | // Retrieve paths. 266 | NSArray* filenames = [NSPropertyListSerialization propertyListFromData:data 267 | mutabilityOption:kCFPropertyListImmutable 268 | format:nil 269 | errorDescription:&errorDescription]; 270 | 271 | // Add paths to the data source. 272 | [self addImagesWithPaths:filenames]; 273 | 274 | // Make the image browser reload the data source. 275 | [self updateDatasource]; 276 | } 277 | 278 | // Accept the drag operation. 279 | return YES; 280 | } 281 | 282 | - (void)addImagesWithPaths:(NSArray*)paths 283 | { 284 | @autoreleasepool 285 | { 286 | for (int i = 0; i < [paths count]; i++) 287 | { 288 | NSString* path = [paths objectAtIndex:i]; 289 | if ([[NSFileManager defaultManager] fileExistsAtPath:path]) 290 | { 291 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path == %@",path]; 292 | NSArray *filteredArr = [images filteredArrayUsingPredicate:predicate]; 293 | if ([filteredArr count] > 0) 294 | { 295 | continue; 296 | } 297 | 298 | myImageObject* p = [[myImageObject alloc] init]; 299 | [p setPath:path]; 300 | [importedImages addObject:p]; 301 | } 302 | } 303 | } 304 | } 305 | 306 | - (NSArray *)filePaths 307 | { 308 | NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[images count]]; 309 | for (myImageObject *obj in images) 310 | { 311 | [resultArray addObject:[obj path]]; 312 | } 313 | return resultArray; 314 | } 315 | 316 | @end 317 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/Utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Utilities.h 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | #import 53 | 54 | 55 | CGImageRef createImageWithName(NSString * imageName); 56 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/Utilities.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Utilities.h 4 | 5 | Abstract: IKImageBrowserView is a view that can display and browse a 6 | large amount of images and movies. This sample code demonstrates 7 | how to use the view in a Cocoa Application. 8 | 9 | Version: 1.0 10 | 11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 12 | Inc. ("Apple") in consideration of your agreement to the 13 | following terms, and your use, installation, modification or 14 | redistribution of this Apple software constitutes acceptance of these 15 | terms. If you do not agree with these terms, please do not use, 16 | install, modify or redistribute this Apple software. 17 | 18 | In consideration of your agreement to abide by the following terms, and 19 | subject to these terms, Apple grants you a personal, non-exclusive 20 | license, under Apple's copyrights in this original Apple software (the 21 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 22 | Software, with or without modifications, in source and/or binary forms; 23 | provided that if you redistribute the Apple Software in its entirety and 24 | without modifications, you must retain this notice and the following 25 | text and disclaimers in all such redistributions of the Apple Software. 26 | Neither the name, trademarks, service marks or logos of Apple Inc. 27 | may be used to endorse or promote products derived from the Apple 28 | Software without specific prior written permission from Apple. Except 29 | as expressly stated in this notice, no other rights or licenses, express 30 | or implied, are granted by Apple herein, including but not limited to 31 | any patent rights that may be infringed by your derivative works or by 32 | other works in which the Apple Software may be incorporated. 33 | 34 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 35 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 36 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 38 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 39 | 40 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 41 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 44 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 45 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 46 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 47 | POSSIBILITY OF SUCH DAMAGE. 48 | 49 | Copyright © 2009 Apple Inc. All Rights Reserved 50 | 51 | */ 52 | 53 | #import "Utilities.h" 54 | 55 | //--------------------------------------------------------------------------------- 56 | // createImageWithName: 57 | // 58 | // simple utility function that create an image from a name by looking into the main bundle 59 | //--------------------------------------------------------------------------------- 60 | 61 | CGImageRef convertToCGImageFromNasImage(NSImage *image) 62 | { 63 | NSData* cocoaData = [NSBitmapImageRep TIFFRepresentationOfImageRepsInArray: [image representations]]; 64 | CFDataRef carbonData = (__bridge CFDataRef)cocoaData; 65 | CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData(carbonData, NULL); 66 | CGImageRef myCGImage = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, NULL); 67 | CFRelease(imageSourceRef); 68 | return myCGImage; 69 | } 70 | 71 | CGImageRef createImageWithName(NSString * imageName) 72 | { 73 | CGImageRef returnValue = NULL; 74 | 75 | NSString *path = [[NSBundle mainBundle] pathForImageResource:imageName]; 76 | NSImage *img = [[NSImage alloc] initWithContentsOfFile:path]; 77 | return convertToCGImageFromNasImage(img); 78 | if(path){ 79 | CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:path], NULL); 80 | 81 | if(imageSource){ 82 | returnValue = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); 83 | } 84 | } 85 | 86 | return returnValue; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/glossy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/FindDuplicateFiles/THPathSelectedViewController/glossy.png -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/metal_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/FindDuplicateFiles/THPathSelectedViewController/metal_background.png -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/metal_background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/FindDuplicateFiles/THPathSelectedViewController/metal_background@2x.png -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/FindDuplicateFiles/THPathSelectedViewController/pin.png -------------------------------------------------------------------------------- /FindDuplicateFiles/THPathSelectedViewController/pin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/FindDuplicateFiles/THPathSelectedViewController/pin@2x.png -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THConditionPredicateEditorRowTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // THConditionPredicateEditorRowTemplate.h 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THConditionPredicateEditorRowTemplate : NSPredicateEditorRowTemplate 12 | { 13 | NSPopUpButton *conditionButton; 14 | BOOL once; 15 | } 16 | 17 | + (id)defaultTemplate; 18 | 19 | - (BOOL)boolValue; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THConditionPredicateEditorRowTemplate.m: -------------------------------------------------------------------------------- 1 | // 2 | // THConditionPredicateEditorRowTemplate.m 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THConditionPredicateEditorRowTemplate.h" 10 | 11 | @implementation THConditionPredicateEditorRowTemplate 12 | 13 | + (id)defaultTemplate 14 | { 15 | NSArray *leftExpressions = [NSArray arrayWithObjects:[NSExpression expressionForKeyPath:@"condition"], nil]; 16 | NSArray *operators = [NSArray arrayWithObjects: 17 | [NSNumber numberWithInteger:NSEqualToPredicateOperatorType],nil]; 18 | NSAttributeType rightType = NSInteger16AttributeType; 19 | NSComparisonPredicateModifier modifier = NSAllPredicateModifier; 20 | 21 | THConditionPredicateEditorRowTemplate *sizeTemplate = 22 | [[THConditionPredicateEditorRowTemplate alloc] initWithLeftExpressions:leftExpressions 23 | rightExpressionAttributeType:rightType 24 | modifier:modifier 25 | operators:operators 26 | options:0]; 27 | return sizeTemplate; 28 | 29 | } 30 | 31 | - (NSArray *)templateViews 32 | { 33 | if (!conditionButton) 34 | { 35 | conditionButton = [[NSPopUpButton alloc] init]; 36 | [conditionButton addItemsWithTitles:[NSArray arrayWithObjects:THLocaleString(@"NO"),THLocaleString(@"YES"), nil]]; 37 | } 38 | NSArray *currentViews = [super templateViews]; 39 | NSMutableArray *resultViews = [currentViews mutableCopy]; 40 | 41 | for (NSView *aView in currentViews) 42 | { 43 | if ([aView isKindOfClass:[NSTextField class]]) 44 | { 45 | if ([(NSTextField *)aView isEditable]) 46 | { 47 | [resultViews removeObject:aView]; 48 | } 49 | } 50 | } 51 | 52 | if (![currentViews containsObject:conditionButton]) 53 | { 54 | [resultViews addObject:conditionButton]; 55 | } 56 | return resultViews; 57 | } 58 | 59 | - (BOOL)boolValue 60 | { 61 | NSArray *views = [self templateViews]; 62 | NSInteger rightIdx = [[views objectAtIndex:2] indexOfSelectedItem]; 63 | if (rightIdx == 1) 64 | { 65 | return YES; 66 | } 67 | return NO; 68 | } 69 | 70 | - (NSPredicate *)predicateWithSubpredicates:(NSArray *)subpredicates 71 | { 72 | NSArray *views = [self templateViews]; 73 | 74 | NSInteger leftIdx = [[views objectAtIndex:0] indexOfSelectedItem]; 75 | NSExpression *leftExpression = [self.leftExpressions objectAtIndex:leftIdx]; 76 | 77 | NSInteger rightIdx = [[views objectAtIndex:2] indexOfSelectedItem]; 78 | NSExpression *rightExpression = [NSExpression expressionForConstantValue:[NSNumber numberWithBool:NO]];; 79 | NSPredicateOperatorType operatorType = NSEqualToPredicateOperatorType; 80 | if (rightIdx == 1) 81 | { 82 | operatorType = NSNotEqualToPredicateOperatorType; 83 | } 84 | 85 | NSPredicate *newPredicate = [NSComparisonPredicate predicateWithLeftExpression:leftExpression 86 | rightExpression:rightExpression 87 | modifier:NSDirectPredicateModifier 88 | type:operatorType 89 | options:0]; 90 | if (subpredicates) 91 | { 92 | subpredicates = [subpredicates arrayByAddingObject:newPredicate]; 93 | }else 94 | { 95 | subpredicates = [NSArray arrayWithObject:newPredicate]; 96 | } 97 | NSCompoundPredicate *compoundPredicate = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType 98 | subpredicates:subpredicates]; 99 | return compoundPredicate; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THPredicateEditorViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPredicateEditorViewController.h 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class THBarView; 12 | @interface THPredicateEditorViewController : NSViewController 13 | { 14 | IBOutlet THBarView *barView; 15 | IBOutlet NSPredicateEditor *predicateEditor; 16 | 17 | uint64 minSize; 18 | uint64 maxSize; 19 | BOOL scanPackage; 20 | NSPredicate *suffixPredicate; 21 | } 22 | 23 | - (uint64)minSize; 24 | - (uint64)maxSize; 25 | - (BOOL)scanPackage; 26 | - (NSPredicate *)extensionPredicate; 27 | 28 | - (void)reload; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THPredicateEditorViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPredicateEditorViewController.m 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THPredicateEditorViewController.h" 10 | #import "THSizePredicateEditorRowTemplate.h" 11 | #import "THConditionPredicateEditorRowTemplate.h" 12 | #import "THBarView.h" 13 | 14 | @interface THPredicateEditorViewController () 15 | 16 | @end 17 | 18 | @implementation THPredicateEditorViewController 19 | 20 | - (id)init 21 | { 22 | self = [super initWithNibName:@"THPredicateEditorViewController" bundle:[NSBundle bundleForClass:self.class]]; 23 | return self; 24 | } 25 | 26 | - (void)awakeFromNib 27 | { 28 | [super awakeFromNib]; 29 | 30 | [barView setTitle:THLocaleString(@"Scanning Conditions")]; 31 | 32 | /* 33 | THSizePredicateEditorRowTemplate *sizeTemplate = [THSizePredicateEditorRowTemplate defaultTemplate]; 34 | THConditionPredicateEditorRowTemplate *conditionTemplate = [THConditionPredicateEditorRowTemplate defaultTemplate]; 35 | NSArray *templates = [predicateEditor.rowTemplates arrayByAddingObject:sizeTemplate]; 36 | templates = [templates arrayByAddingObject:conditionTemplate]; 37 | [predicateEditor setRowTemplates:templates]; 38 | */ 39 | 40 | [predicateEditor setNestingMode:NSRuleEditorNestingModeCompound]; 41 | [predicateEditor setCanRemoveAllRows:NO]; 42 | [predicateEditor insertRowAtIndex:0 withType:NSRuleEditorRowTypeCompound asSubrowOfRow:-1 animate:NO]; 43 | [predicateEditor insertRowAtIndex:1 withType:NSRuleEditorRowTypeSimple asSubrowOfRow:0 animate:NO]; 44 | [predicateEditor insertRowAtIndex:2 withType:NSRuleEditorRowTypeSimple asSubrowOfRow:0 animate:NO]; 45 | [predicateEditor insertRowAtIndex:3 withType:NSRuleEditorRowTypeSimple asSubrowOfRow:0 animate:NO]; 46 | } 47 | 48 | - (uint64)minSize 49 | { 50 | return minSize; 51 | } 52 | 53 | - (uint64)maxSize 54 | { 55 | return maxSize; 56 | } 57 | 58 | - (BOOL)scanPackage 59 | { 60 | return scanPackage; 61 | } 62 | 63 | - (NSPredicate *)extensionPredicate 64 | { 65 | return suffixPredicate; 66 | } 67 | 68 | - (void)reload 69 | { 70 | suffixPredicate = nil; 71 | scanPackage = NO; 72 | minSize = 0; 73 | maxSize = 0; 74 | 75 | static void(^searchPredicate)(NSPredicate*); 76 | searchPredicate = ^(NSPredicate*predicate){ 77 | if ([predicate isKindOfClass:[NSCompoundPredicate class]]) 78 | { 79 | NSArray *predicateArray = [(NSCompoundPredicate*)predicate subpredicates]; 80 | for (NSPredicate *aPredicate in predicateArray) 81 | { 82 | searchPredicate(aPredicate); 83 | } 84 | } 85 | 86 | if ([predicate isKindOfClass:[NSComparisonPredicate class]]) 87 | { 88 | NSExpression *leftEx = [(NSComparisonPredicate *)predicate leftExpression]; 89 | NSExpression *rightEx = [(NSComparisonPredicate *)predicate rightExpression]; 90 | NSPredicateOperatorType oprator = [(NSComparisonPredicate *)predicate predicateOperatorType]; 91 | 92 | if ([[leftEx keyPath] isEqualToString:@"suffix"]) 93 | { 94 | NSComparisonPredicateOptions options = [(NSComparisonPredicate *)predicate options]; 95 | suffixPredicate = 96 | [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionWithFormat:@"SELF"] 97 | rightExpression:rightEx 98 | modifier:NSDirectPredicateModifier 99 | type:oprator 100 | options:options]; 101 | } 102 | 103 | if ([[leftEx keyPath] isEqualToString:@"size"]) 104 | { 105 | uint64 sizeValue = (uint64)[[rightEx constantValue] longLongValue]; 106 | 107 | if (oprator == NSLessThanPredicateOperatorType) 108 | { 109 | minSize = minSize==0?sizeValue:MAX(minSize,sizeValue); 110 | } 111 | 112 | if (oprator == NSGreaterThanPredicateOperatorType) 113 | { 114 | maxSize = maxSize==0?sizeValue:MIN(maxSize,sizeValue); 115 | } 116 | } 117 | 118 | if ([[leftEx keyPath] isEqualToString:@"package"]) 119 | { 120 | if (oprator == NSEqualToPredicateOperatorType) 121 | { 122 | scanPackage = NO; 123 | } 124 | 125 | if (oprator == NSNotEqualToPredicateOperatorType) 126 | { 127 | scanPackage = YES; 128 | } 129 | } 130 | } 131 | }; 132 | 133 | NSPredicate *predicate = [predicateEditor predicate]; 134 | searchPredicate(predicate); 135 | 136 | /* 137 | NSLog(@"suffixPredicate:%@",suffixPredicate); 138 | NSLog(@"minSize:%lld",minSize); 139 | NSLog(@"maxSize:%lld",maxSize); 140 | NSLog(@"scanPackage:%d",scanPackage); 141 | */ 142 | 143 | /* 144 | NSDictionary *textInfo = @{@"size" : @(10000),@"package":@(NO)}; 145 | NSDictionary *info2 = @{@"size" : @(100),@"package":@(2)}; 146 | NSArray *textArray = @[textInfo,info2]; 147 | 148 | NSLog(@"%@",predicate); 149 | NSLog(@"%d",[predicate evaluateWithObject:textInfo]); 150 | NSLog(@"%@",[textArray filteredArrayUsingPredicate:predicate]); 151 | 152 | NSLog(@"%@",[(NSCompoundPredicate *)predicate subpredicates]); 153 | */ 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THSizePredicateEditorRowTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // THSizePredicateEditorRowTemplate.h 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | enum 12 | { 13 | THSizeUndefineOperatorType, 14 | THSizeLessThanOperatorType, 15 | THSizeGreaterThanOperatorType 16 | }; 17 | typedef NSInteger THSizeOperatorType; 18 | 19 | @interface THSizePredicateEditorRowTemplate : NSPredicateEditorRowTemplate 20 | { 21 | NSPopUpButton *sizeButton; 22 | BOOL once; 23 | } 24 | 25 | + (id)defaultTemplate; 26 | 27 | - (THSizeOperatorType)operatorType; 28 | - (uint64)size; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THSizePredicateEditorRowTemplate.m: -------------------------------------------------------------------------------- 1 | // 2 | // THSizePredicateEditorRowTemplate.m 3 | // Test 4 | // 5 | // Created by TanHao on 12-11-13. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THSizePredicateEditorRowTemplate.h" 10 | 11 | @implementation THSizePredicateEditorRowTemplate 12 | 13 | + (id)defaultTemplate 14 | { 15 | NSArray *leftExpressions = [NSArray arrayWithObjects:[NSExpression expressionForKeyPath:@"size"], nil]; 16 | NSArray *operators = [NSArray arrayWithObjects: 17 | [NSNumber numberWithInteger:NSGreaterThanPredicateOperatorType], 18 | [NSNumber numberWithInteger:NSLessThanPredicateOperatorType],nil]; 19 | NSAttributeType rightType = NSInteger16AttributeType; 20 | NSComparisonPredicateModifier modifier = NSAllPredicateModifier; 21 | 22 | THSizePredicateEditorRowTemplate *sizeTemplate = 23 | [[THSizePredicateEditorRowTemplate alloc] initWithLeftExpressions:leftExpressions 24 | rightExpressionAttributeType:rightType 25 | modifier:modifier 26 | operators:operators 27 | options:0]; 28 | return sizeTemplate; 29 | } 30 | 31 | - (THSizeOperatorType)operatorType 32 | { 33 | NSArray *views = [self templateViews]; 34 | NSInteger opeatorIdx = [[views objectAtIndex:1] indexOfSelectedItem]; 35 | NSPredicateOperatorType opeatorType = [[[self operators] objectAtIndex:opeatorIdx] intValue]; 36 | if (opeatorType == NSLessThanPredicateOperatorType) 37 | { 38 | return THSizeLessThanOperatorType; 39 | } 40 | if (opeatorType == NSGreaterThanPredicateOperatorType) 41 | { 42 | return THSizeGreaterThanOperatorType; 43 | } 44 | return THSizeUndefineOperatorType; 45 | } 46 | 47 | - (uint64)size 48 | { 49 | NSArray *views = [self templateViews]; 50 | uint64 size = [[views objectAtIndex:2] intValue]; 51 | NSInteger rightIdx = [[views objectAtIndex:3] indexOfSelectedItem]; 52 | switch (rightIdx) 53 | { 54 | case 0:size*=1024;break; 55 | case 1:size*=1024*1024;break; 56 | case 2:size*=1024*1024*1024;break; 57 | default:break; 58 | } 59 | return size; 60 | } 61 | 62 | - (NSArray *)templateViews 63 | { 64 | if (!sizeButton) 65 | { 66 | sizeButton = [[NSPopUpButton alloc] init]; 67 | [sizeButton addItemsWithTitles:[NSArray arrayWithObjects:@"KB", @"MB", @"GB", nil]]; 68 | } 69 | NSArray *currentViews = [super templateViews]; 70 | if (!once) 71 | { 72 | once = YES; 73 | for (NSView *aView in currentViews) 74 | { 75 | if ([aView isKindOfClass:[NSTextField class]] 76 | && [(NSTextField *)aView isEditable]) 77 | { 78 | [(NSTextField *)aView setStringValue:@"100"]; 79 | } 80 | } 81 | } 82 | if (![currentViews containsObject:sizeButton]) 83 | { 84 | return [currentViews arrayByAddingObject:sizeButton]; 85 | } 86 | return currentViews; 87 | } 88 | 89 | - (NSPredicate *)predicateWithSubpredicates:(NSArray *)subpredicates 90 | { 91 | NSArray *views = [self templateViews]; 92 | 93 | NSInteger leftIdx = [[views objectAtIndex:0] indexOfSelectedItem]; 94 | NSExpression *leftExpression = [self.leftExpressions objectAtIndex:leftIdx]; 95 | 96 | NSInteger opeatorIdx = [[views objectAtIndex:1] indexOfSelectedItem]; 97 | NSPredicateOperatorType opeatorType = [[[self operators] objectAtIndex:opeatorIdx] intValue]; 98 | 99 | uint64 size = [[views objectAtIndex:2] intValue]; 100 | NSInteger rightIdx = [[views objectAtIndex:3] indexOfSelectedItem]; 101 | switch (rightIdx) 102 | { 103 | case 0:size*=1024;break; 104 | case 1:size*=1024*1024;break; 105 | case 2:size*=1024*1024*1024;break; 106 | default:break; 107 | } 108 | NSExpression *rightExpression = [NSExpression expressionForConstantValue:@(size)]; 109 | 110 | NSPredicate *newPredicate = [NSComparisonPredicate predicateWithLeftExpression:leftExpression 111 | rightExpression:rightExpression 112 | modifier:NSDirectPredicateModifier 113 | type:opeatorType 114 | options:0]; 115 | if (subpredicates) 116 | { 117 | subpredicates = [subpredicates arrayByAddingObject:newPredicate]; 118 | }else 119 | { 120 | subpredicates = [NSArray arrayWithObject:newPredicate]; 121 | } 122 | NSCompoundPredicate *compoundPredicate = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType 123 | subpredicates:subpredicates]; 124 | return compoundPredicate; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THStringPredicateEditorRowTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // THStringPredicateEditorRowTemplate.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THStringPredicateEditorRowTemplate : NSPredicateEditorRowTemplate 12 | { 13 | BOOL once; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THPredicateEditor/THStringPredicateEditorRowTemplate.m: -------------------------------------------------------------------------------- 1 | // 2 | // THStringPredicateEditorRowTemplate.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-14. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THStringPredicateEditorRowTemplate.h" 10 | 11 | @implementation THStringPredicateEditorRowTemplate 12 | 13 | - (NSArray *)templateViews 14 | { 15 | NSArray *views = [super templateViews]; 16 | if (!once) 17 | { 18 | once = YES; 19 | for (NSView *aView in views) 20 | { 21 | if ([aView isKindOfClass:[NSTextField class]] 22 | && [(NSTextField *)aView isEditable]) 23 | { 24 | [(NSTextField *)aView setStringValue:@"JPG"]; 25 | } 26 | } 27 | } 28 | return views; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THShadowField.h: -------------------------------------------------------------------------------- 1 | // 2 | // THShadowField.h 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-18. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THShadowField : NSTextField 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THShadowField.m: -------------------------------------------------------------------------------- 1 | // 2 | // THShadowField.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-18. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THShadowField.h" 10 | 11 | @implementation THShadowField 12 | 13 | - (id)initWithFrame:(NSRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | // Initialization code here. 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (void)awakeFromNib 24 | { 25 | [self setWantsLayer:YES]; 26 | NSShadow *shadow = [[NSShadow alloc] init]; 27 | [shadow setShadowBlurRadius:1.0]; 28 | [shadow setShadowColor:[NSColor whiteColor]]; 29 | [shadow setShadowOffset:NSMakeSize(1, 1)]; 30 | [self setShadow:shadow]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THStaticTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // THStaticTextFiled.h 3 | // DesktopActivity 4 | // 5 | // Created by TanHao on 12-9-29. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface THStaticTextField : NSTextField 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THStaticTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // THStaticTextFiled.m 3 | // DesktopActivity 4 | // 5 | // Created by TanHao on 12-9-29. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import "THStaticTextField.h" 10 | 11 | @implementation THStaticTextField 12 | 13 | - (id)initWithFrame:(NSRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) 17 | { 18 | [self setBackgroundColor:[NSColor clearColor]]; 19 | [self setBordered:NO]; 20 | [self setEditable:NO]; 21 | [self setSelectable:NO]; 22 | if ([self respondsToSelector:@selector(setAllowsExpansionToolTips:)]) 23 | { 24 | [self setAllowsExpansionToolTips:YES]; 25 | } 26 | } 27 | 28 | return self; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THDownloadWebService.h: -------------------------------------------------------------------------------- 1 | // 2 | // THDownloadWebService.h 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-21. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import 10 | #import "THWebService.h" 11 | 12 | typedef void(^THDownloadWebServiceBlock)(NSURLResponse *response, NSError *error, double progress, BOOL finished); 13 | 14 | @protocol THDownloadWebServiceDelegate; 15 | @interface THDownloadWebService : THWebService 16 | { 17 | BOOL overwrite; 18 | NSString *_fileName; 19 | NSString *_filePath; 20 | NSDate *_startDate; 21 | unsigned long long fileSize; 22 | double peakSpeed; 23 | double averageSpeed; 24 | 25 | @private 26 | NSTimer *_timer; 27 | NSString *_destinationPath; 28 | NSString *_temporaryPath; 29 | NSFileHandle *_fileHandle; 30 | unsigned long long offset; 31 | unsigned long long totalDownSize; 32 | unsigned long long onceDownSize; 33 | } 34 | 35 | - (void)setDelegate:(id)anObject; 36 | - (id)delegate; 37 | 38 | /* 39 | 当文件名相同时是否覆盖,overwriter为NO的时候,当文件已经存在,则下载结束,否则覆盖 40 | 默认为NO 41 | */ 42 | @property (nonatomic, assign) BOOL overwrite; 43 | /* 44 | 下载文件的名字名,默认为下载原文件名 45 | */ 46 | @property (nonatomic, strong) NSString *fileName; 47 | /* 48 | 文件保存的path(不包括文件名),默认路径为DocumentDirectory 49 | */ 50 | @property (nonatomic, strong) NSString *filePath; 51 | /* 52 | 下载的大小,只有当下载任务成功启动之后才能获取 53 | */ 54 | @property (nonatomic, readonly) unsigned long long fileSize; 55 | /* 56 | 下载的峰值速度 57 | */ 58 | @property (nonatomic, readonly) double peakSpeed; 59 | /* 60 | 下载的平均速度 61 | */ 62 | @property (nonatomic, readonly) double averageSpeed; 63 | /* 64 | Block 65 | */ 66 | @property (nonatomic, copy) THDownloadWebServiceBlock downloadBlock; 67 | 68 | - (void)startWithHandler:(THDownloadWebServiceBlock)block; 69 | 70 | @end 71 | 72 | 73 | @protocol THDownloadWebServiceDelegate 74 | 75 | - (void)downloadProgressChange:(THDownloadWebService *)aDownload progress:(double)newProgress; 76 | 77 | @end -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THDownloadWebService.m: -------------------------------------------------------------------------------- 1 | // 2 | // THDownloadWebService.m 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-21. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import "THDownloadWebService.h" 10 | #import "THWebDefine.h" 11 | 12 | @implementation THDownloadWebService 13 | @synthesize overwrite; 14 | @synthesize fileName=_fileName; 15 | @synthesize filePath=_filePath; 16 | @synthesize fileSize; 17 | @synthesize peakSpeed; 18 | @synthesize averageSpeed; 19 | @synthesize downloadBlock; 20 | 21 | - (void)setDelegate:(id)anObject 22 | { 23 | _delegate = anObject; 24 | } 25 | 26 | - (id)delegate 27 | { 28 | return _delegate; 29 | } 30 | 31 | - (void)stop 32 | { 33 | [super stop]; 34 | [_fileHandle closeFile]; 35 | _fileHandle = nil; 36 | 37 | [_timer invalidate]; 38 | _timer = nil; 39 | 40 | _startDate = nil; 41 | 42 | totalDownSize = 0; 43 | onceDownSize = 0; 44 | peakSpeed =0; 45 | averageSpeed = 0; 46 | 47 | fileSize = 0; 48 | } 49 | 50 | - (NSURLRequest *)setUpRequest:(NSError **)error 51 | { 52 | //当url为空的时间,返回失败 53 | if (!_url) 54 | { 55 | if (error) *error = THErrorURLFail; 56 | return nil; 57 | } 58 | 59 | //未指定文件名 60 | if (!_fileName) 61 | { 62 | NSURLResponse *response = [[NSURLResponse alloc] initWithURL:_url MIMEType:NULL expectedContentLength:0 textEncodingName:NULL]; 63 | _fileName = [response suggestedFilename]; 64 | /* 65 | NSString *urlStr = [_url absoluteString]; 66 | _fileName = [urlStr lastPathComponent]; 67 | if ([_fileName length] > 32) _fileName = [_fileName substringFromIndex:[_fileName length]-32]; 68 | */ 69 | } 70 | 71 | //未指定路径 72 | if (!_filePath) 73 | { 74 | NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 75 | NSString *documentsDir = [documentPaths objectAtIndex:0]; 76 | _filePath = documentsDir; 77 | } 78 | 79 | //目标地址与缓存地址 80 | _destinationPath=[_filePath stringByAppendingPathComponent:_fileName]; 81 | _temporaryPath=[_destinationPath stringByAppendingFormat:kTHDownLoadTask_TempSuffix]; 82 | 83 | //处理如果文件已经存在的情况 84 | if ([[NSFileManager defaultManager] fileExistsAtPath:_destinationPath]) 85 | { 86 | if (overwrite) 87 | { 88 | [[NSFileManager defaultManager] removeItemAtPath:_destinationPath error:nil]; 89 | }else 90 | { 91 | if (error) *error = THErrorFileExist; 92 | return nil; 93 | } 94 | } 95 | 96 | //缓存文件不存在,则创建缓存文件 97 | if (![[NSFileManager defaultManager] fileExistsAtPath:_temporaryPath]) 98 | { 99 | BOOL createSucces = [[NSFileManager defaultManager] createFileAtPath:_temporaryPath contents:nil attributes:nil]; 100 | if (!createSucces) 101 | { 102 | if (error) *error = THErrorCreateFail; 103 | return nil; 104 | } 105 | } 106 | 107 | //设置fileHandle 108 | _fileHandle = [NSFileHandle fileHandleForWritingAtPath:_temporaryPath]; 109 | offset = [_fileHandle seekToEndOfFile]; 110 | NSString *range = [NSString stringWithFormat:@"bytes=%llu-",offset]; 111 | 112 | //设置下载的一些属性 113 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:_url]; 114 | [request addValue:range forHTTPHeaderField:@"Range"]; 115 | if (error) *error = NULL; 116 | return request; 117 | } 118 | 119 | - (void)startWithHandler:(THDownloadWebServiceBlock)block; 120 | { 121 | self.downloadBlock = block; 122 | [self startAsynchronous]; 123 | } 124 | 125 | - (BOOL)startAsynchronous 126 | { 127 | BOOL sucess = [super startAsynchronous]; 128 | if (sucess) 129 | { 130 | _timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:kTHDownLoadTimerInterval target:self selector:@selector(lisenProgressAndSpeed) userInfo:nil repeats:YES]; 131 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; 132 | 133 | _startDate = [[NSDate alloc] init]; 134 | } 135 | return sucess; 136 | } 137 | 138 | - (void)lisenProgressAndSpeed 139 | { 140 | offset = [_fileHandle offsetInFile]; 141 | 142 | double totalTimeGap = fabs([_startDate timeIntervalSinceNow]); 143 | double onceTimeGap = [_timer timeInterval]; 144 | averageSpeed = totalDownSize/totalTimeGap; 145 | peakSpeed = onceDownSize/onceTimeGap; 146 | onceDownSize = 0; 147 | 148 | if (fileSize > 0) 149 | { 150 | double progress = offset*1.0/fileSize; 151 | if ([_delegate respondsToSelector:@selector(downloadProgressChange:progress:)]) 152 | { 153 | [[self delegate] downloadProgressChange:self progress:progress]; 154 | } 155 | if (self.downloadBlock) 156 | { 157 | self.downloadBlock(NULL,NULL,progress,NO); 158 | } 159 | } 160 | } 161 | 162 | #pragma mark - 163 | #pragma mark NSURLConnectionDelegate 164 | 165 | - (void)didReceiveResponse:(NSURLResponse *)response 166 | { 167 | if ([response expectedContentLength] != NSURLResponseUnknownLength) 168 | { 169 | fileSize = [response expectedContentLength] + offset; 170 | } 171 | if (self.downloadBlock) 172 | { 173 | self.downloadBlock(response,NULL,0,NO); 174 | } 175 | [super didReceiveResponse:response]; 176 | } 177 | 178 | - (void)didReceiveData:(NSData *)aData 179 | { 180 | totalDownSize += aData.length; 181 | onceDownSize += aData.length; 182 | [_fileHandle writeData:aData]; 183 | } 184 | 185 | - (void)didFailWithError:(NSError *)error 186 | { 187 | if (self.downloadBlock) 188 | { 189 | self.downloadBlock(NULL,error,0,NO); 190 | } 191 | [super didFailWithError:error]; 192 | } 193 | 194 | - (void)didFinishLoading 195 | { 196 | [[NSFileManager defaultManager] moveItemAtPath:_temporaryPath toPath:_destinationPath error:nil]; 197 | if (self.downloadBlock) 198 | { 199 | self.downloadBlock(NULL,NULL,0,YES); 200 | } 201 | [super didFinishLoading]; 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THPostWebService.h: -------------------------------------------------------------------------------- 1 | // 2 | // THPostWebService.h 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import 10 | #import "THWebService.h" 11 | 12 | @interface THPostWebService : THWebService 13 | { 14 | NSDictionary *_postDic; 15 | } 16 | @property (nonatomic, strong) NSDictionary *postDic; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THPostWebService.m: -------------------------------------------------------------------------------- 1 | // 2 | // THPostWebService.m 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import "THPostWebService.h" 10 | #import "THWebDefine.h" 11 | 12 | static NSString *kBoundaryStr=@"_insert_some_boundary_here_"; 13 | 14 | @implementation THPostWebService 15 | @synthesize postDic=_postDic; 16 | 17 | //将字典按照HTTP的协议进行编码 18 | - (NSData*)generateFormData:(NSDictionary*)dict 19 | { 20 | NSString* boundary = [NSString stringWithString:kBoundaryStr]; 21 | NSArray* keys = [dict allKeys]; 22 | NSMutableData* result = [[NSMutableData alloc] init]; 23 | 24 | NSStringEncoding encoding = NSUTF8StringEncoding; //NSASCIIStringEncoding; 25 | for (int i = 0; i < [keys count]; i++) 26 | { 27 | id value = [dict valueForKey: [keys objectAtIndex: i]]; 28 | [result appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:encoding]]; 29 | if ([value isKindOfClass:[NSString class]]) 30 | { 31 | [result appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", [keys objectAtIndex:i]] dataUsingEncoding:encoding]]; 32 | [result appendData:[[NSString stringWithFormat:@"%@",value] dataUsingEncoding:encoding]]; 33 | } 34 | if ([value isKindOfClass:[NSNumber class]]) 35 | { 36 | [result appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", [keys objectAtIndex:i]] dataUsingEncoding:encoding]]; 37 | [result appendData:[[value stringValue] dataUsingEncoding:encoding]]; 38 | } 39 | else if ([value isKindOfClass:[NSURL class]] && [value isFileURL]) 40 | { 41 | [result appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [keys objectAtIndex:i], [[value path] lastPathComponent]] dataUsingEncoding:encoding]]; 42 | [result appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:encoding]]; 43 | [result appendData:[NSData dataWithContentsOfFile:[value path]]]; 44 | } 45 | else if ([value isKindOfClass:[NSData class]]) 46 | { 47 | [result appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", [keys objectAtIndex:i]] dataUsingEncoding:encoding]]; 48 | [result appendData:value]; 49 | } 50 | [result appendData:[@"\r\n" dataUsingEncoding:encoding]]; 51 | } 52 | [result appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:encoding]]; 53 | 54 | return result; 55 | } 56 | 57 | - (NSURLRequest *)setUpRequest:(NSError **)error 58 | { 59 | if (!_url) 60 | { 61 | if (error) *error = THErrorURLFail; 62 | return nil; 63 | } 64 | 65 | NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:_url 66 | cachePolicy:NSURLRequestUseProtocolCachePolicy 67 | timeoutInterval:15.0]; 68 | if (!request) 69 | { 70 | if (error) *error = THErrorRequestFail; 71 | return nil; 72 | } 73 | //设置request的属性和Header 74 | [request setHTTPMethod:@"POST"]; 75 | NSString *header_type = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBoundaryStr]; 76 | [request addValue: header_type forHTTPHeaderField: @"Content-Type"]; 77 | 78 | //按照HTTP的相关协议格式化数据 79 | NSData *postData=[self generateFormData:_postDic]; 80 | [request addValue:[NSString stringWithFormat:@"%ld",[postData length]] forHTTPHeaderField:@"Content-Length"]; 81 | [request setHTTPBody:postData]; 82 | if (error) *error = NULL; 83 | return request; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THWebDefine.h: -------------------------------------------------------------------------------- 1 | // 2 | // THWebDefine.h 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-21. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import "THWebUtility.h" 10 | #import "THWebService.h" 11 | #import "THPostWebService.h" 12 | #import "THDownloadWebService.h" 13 | 14 | #ifndef UserSystem_THWebDefine_h 15 | #define UserSystem_THWebDefine_h 16 | 17 | //Error 18 | #define THErrorURLFail [NSError errorWithDomain:@"URL Can not be nil!" code:100 userInfo:nil] 19 | #define THErrorRequestFail [NSError errorWithDomain:@"URL Request Create Error!" code:101 userInfo:nil] 20 | #define THErrorFileExist [NSError errorWithDomain:@"File Exist!" code:102 userInfo:nil] 21 | #define THErrorCreateFail [NSError errorWithDomain:@"Create File Fail!" code:103 userInfo:nil] 22 | 23 | //下载的临时文件的后缀 24 | #define kTHDownLoadTask_TempSuffix @".TempDownload" 25 | //计算下载速度的取样时间 26 | #define kTHDownLoadTimerInterval 2.0 27 | //THDispatchQueue默认的并发数 28 | #define kTHDispatchQueueDefaultConcurrentCount 10 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THWebService.h: -------------------------------------------------------------------------------- 1 | // 2 | // THWebService.h 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^THWebServiceBlock) (NSURLResponse *response, NSError *error, NSData *data); 12 | 13 | @protocol THWebServiceDelegate; 14 | @interface THWebService : NSObject 15 | { 16 | id __unsafe_unretained _delegate; 17 | NSURL *_url; 18 | 19 | @protected 20 | NSURLConnection *_con; 21 | NSMutableData *_data; 22 | } 23 | @property (nonatomic, unsafe_unretained) id delegate; 24 | @property (nonatomic, strong) NSURL *url; 25 | @property (nonatomic, copy) THWebServiceBlock webServiceBlock; 26 | 27 | - (void)stop; 28 | - (BOOL)startAsynchronous; 29 | - (NSData *)startSynchronous; 30 | - (void)startWithHandler:(THWebServiceBlock)block; 31 | 32 | @end 33 | 34 | @interface THWebService() 35 | - (void)didReceiveResponse:(NSURLResponse *)response; 36 | - (void)didReceiveData:(NSData *)aData; 37 | - (void)didFailWithError:(NSError *)error; 38 | - (void)didFinishLoading; 39 | @end 40 | 41 | @protocol THWebServiceDelegate 42 | 43 | - (void)webServiceBegin:(THWebService *)webService; 44 | - (void)webServiceFinish:(THWebService *)webService didReceiveData:(NSData *)data; 45 | - (void)webServiceFail:(THWebService *)webService didFailWithError:(NSError *)error; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THWebService.m: -------------------------------------------------------------------------------- 1 | // 2 | // THWebService.m 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import "THWebService.h" 10 | #import "THWebDefine.h" 11 | 12 | @implementation THWebService 13 | @synthesize delegate=_delegate; 14 | @synthesize url=_url; 15 | @synthesize webServiceBlock; 16 | 17 | - (void)dealloc 18 | { 19 | [self stop]; 20 | } 21 | 22 | - (void)stop 23 | { 24 | [_con cancel]; 25 | _con = nil; 26 | _data = nil; 27 | } 28 | 29 | - (NSURLRequest *)setUpRequest:(NSError **)error 30 | { 31 | if (!_url) 32 | { 33 | if (error) *error = THErrorURLFail; 34 | return nil; 35 | } 36 | 37 | NSMutableURLRequest *request=[[NSMutableURLRequest alloc] initWithURL:_url 38 | cachePolicy:NSURLRequestUseProtocolCachePolicy 39 | timeoutInterval:15.0]; 40 | if (!request) 41 | { 42 | if (error) *error = THErrorRequestFail; 43 | return nil; 44 | } 45 | 46 | [request setHTTPMethod:@"GET"]; 47 | if (error) *error = NULL; 48 | return request; 49 | } 50 | 51 | - (BOOL)startAsynchronous 52 | { 53 | [self stop]; 54 | NSError *error = nil; 55 | NSURLRequest *request = [self setUpRequest:&error]; 56 | if (!request) 57 | { 58 | [self didFailWithError:error]; 59 | return NO; 60 | } 61 | _data = [[NSMutableData alloc] init]; 62 | _con = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 63 | [_con scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 64 | [_con start]; 65 | return YES; 66 | } 67 | 68 | - (NSData *)startSynchronous 69 | { 70 | [self stop]; 71 | NSURLRequest *request = [self setUpRequest:NULL]; 72 | if (!request) return nil; 73 | NSURLResponse *response; 74 | NSData *receiveData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL]; 75 | return receiveData; 76 | } 77 | 78 | - (void)startWithHandler:(THWebServiceBlock)block 79 | { 80 | [self stop]; 81 | self.webServiceBlock = block; 82 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 83 | __block NSURLResponse *response = nil; 84 | __block NSError *error = nil; 85 | __block NSData *receiveData = nil; 86 | NSURLRequest *request = [self setUpRequest:&error]; 87 | if (request) 88 | { 89 | receiveData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 90 | } 91 | if (self.webServiceBlock) 92 | { 93 | dispatch_async(dispatch_get_main_queue(), ^{ 94 | self.webServiceBlock(response,error,receiveData); 95 | }); 96 | } 97 | }); 98 | } 99 | 100 | - (void)didReceiveResponse:(NSURLResponse *)response 101 | { 102 | if (self.webServiceBlock) 103 | { 104 | self.webServiceBlock(response,NULL,NULL); 105 | } 106 | if ([_delegate respondsToSelector:@selector(webServiceBegin:)]) 107 | { 108 | [_delegate webServiceBegin:self]; 109 | } 110 | } 111 | 112 | - (void)didReceiveData:(NSData *)aData 113 | { 114 | if(_con!=nil) 115 | { 116 | [_data appendData:aData]; 117 | } 118 | } 119 | 120 | - (void)didFailWithError:(NSError *)error 121 | { 122 | if (self.webServiceBlock) 123 | { 124 | self.webServiceBlock(NULL,error,NULL); 125 | } 126 | if ([_delegate respondsToSelector:@selector(webServiceFail:didFailWithError:)]) 127 | { 128 | [_delegate webServiceFail:self didFailWithError:error]; 129 | } 130 | [self stop]; 131 | } 132 | 133 | - (void)didFinishLoading 134 | { 135 | if (self.webServiceBlock) 136 | { 137 | self.webServiceBlock(NULL,NULL,_data); 138 | } 139 | if ([_delegate respondsToSelector:@selector(webServiceFinish:didReceiveData:)]) 140 | { 141 | [_delegate webServiceFinish:self didReceiveData:_data]; 142 | } 143 | [self stop]; 144 | } 145 | 146 | #pragma mark - 147 | #pragma mark NSURLConnectionDelegate 148 | 149 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 150 | { 151 | [self didReceiveResponse:response]; 152 | } 153 | 154 | -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)aData 155 | { 156 | [self didReceiveData:aData]; 157 | } 158 | 159 | -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 160 | { 161 | [self didFailWithError:error]; 162 | } 163 | 164 | -(void)connectionDidFinishLoading:(NSURLConnection *)connection 165 | { 166 | [self didFinishLoading]; 167 | } 168 | 169 | @end -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THWebUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // THWebUtility.h 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | enum 12 | { 13 | THHashKindMd5, 14 | THHashKindSha1, 15 | THHashKindSha256, 16 | THHashKindSha512, 17 | }; 18 | typedef NSInteger THHashKind; 19 | 20 | @interface THWebUtility : NSObject 21 | 22 | //快速部分MD5 23 | + (NSString *)lazyHashFile:(NSString *)filePath; 24 | 25 | //Hash 算法 26 | + (NSString *)hashFile:(NSString *)filePath with:(THHashKind)hashKind; 27 | + (NSString *)hashData:(NSData *)data with:(THHashKind)hashKind; 28 | + (NSString *)hashString:(NSString *)string with:(THHashKind)hashKind; 29 | 30 | //HMAC 算法 (keyed-Hash Message Authentication Code,密钥相关的哈希运算消息认证码) 31 | + (NSString *)hMacData:(NSData *)data withSecretKey:(NSString *)secretKey withHashKind:(THHashKind)hashKind; 32 | + (NSString *)hMacString:(NSString *)string withSecretKey:(NSString *)secretKey withHashKind:(THHashKind)hashKind; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/THWebUtility.m: -------------------------------------------------------------------------------- 1 | // 2 | // THWebUtility.m 3 | // UserSystem 4 | // 5 | // Created by Hao Tan on 12-3-20. 6 | // Copyright (c) 2012年 http://www.tanhao.me All rights reserved. 7 | // 8 | 9 | #import "THWebUtility.h" 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation THWebUtility 15 | 16 | #pragma mark - 17 | #pragma mark Hash 18 | 19 | #define THLazyHashMaxCount 3UL 20 | 21 | #define THHashSizeForRead (4*1024) 22 | 23 | + (NSString *)lazyMD5File:(NSString *)filePath 24 | { 25 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; 26 | uint64 size = [fileHandle seekToEndOfFile]; 27 | if (size == 0) 28 | { 29 | [fileHandle closeFile]; 30 | return nil; 31 | } 32 | 33 | void *CTXPoint = (CC_MD5_CTX *)calloc(1, (sizeof(CC_SHA1_CTX))); 34 | CC_MD5_Init(CTXPoint); 35 | 36 | 37 | uint64 hashCount = MIN(THLazyHashMaxCount, 1+size/THHashSizeForRead); 38 | uint64 preMove = size/hashCount; 39 | 40 | for (int i=0;i 3) 102 | { 103 | continue; 104 | } 105 | keyString = subItem; 106 | NSArray *subSearchPaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fullPath error:nil]; 107 | for (NSString *filePath in subSearchPaths) 108 | { 109 | NSString *subFulPath = [subItem stringByAppendingPathComponent:filePath]; 110 | [searchPaths addObject:subFulPath]; 111 | } 112 | } 113 | else if ([attType isEqualToString:NSFileTypeRegular]) 114 | { 115 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:fullPath error:NULL]; 116 | NSNumber *sizeNumber = [attributes objectForKey:NSFileSystemSize]; 117 | keyString = [NSString stringWithFormat:@"%@-%lld",subItem,[sizeNumber longLongValue]]; 118 | } 119 | 120 | if (keyString) 121 | { 122 | [resultArray addObject:keyString]; 123 | } 124 | } 125 | [resultArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 126 | NSString *keyString1 = (NSString *)obj1; 127 | NSString *keyString2 = (NSString *)obj2; 128 | return [keyString1 compare:keyString2]; 129 | }]; 130 | return resultArray; 131 | }; 132 | 133 | NSArray *itemsKeyList = directorySubPath(filePath); 134 | NSString *itemsKeyString = [itemsKeyList componentsJoinedByString:@","]; 135 | return [self hashString:itemsKeyString with:THHashKindMd5]; 136 | } 137 | 138 | + (NSString *)lazyHashFile:(NSString *)filePath 139 | { 140 | if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filePath]) 141 | { 142 | return [self lazyHashPackage:filePath]; 143 | } 144 | return [self lazyMD5File:filePath]; 145 | } 146 | 147 | + (NSString *)hashFile:(NSString *)filePath with:(THHashKind)hashKind 148 | { 149 | NSInputStream *inputStream = [[NSInputStream alloc] initWithFileAtPath:filePath]; 150 | if (!inputStream) 151 | { 152 | return nil; 153 | } 154 | 155 | void *CTXPoint = NULL; 156 | switch (hashKind) 157 | { 158 | case THHashKindMd5: 159 | { 160 | CTXPoint = (CC_MD5_CTX *)calloc(1, (sizeof(CC_SHA1_CTX))); 161 | CC_MD5_Init(CTXPoint); 162 | break; 163 | } 164 | case THHashKindSha1: 165 | { 166 | CTXPoint = (CC_SHA1_CTX *)calloc(1, (sizeof(CC_SHA1_CTX))); 167 | CC_SHA1_Init(CTXPoint); 168 | break; 169 | } 170 | case THHashKindSha256: 171 | { 172 | CTXPoint = (CC_SHA256_CTX *)calloc(1, (sizeof(CC_SHA256_CTX))); 173 | CC_SHA256_Init(CTXPoint); 174 | break; 175 | } 176 | case THHashKindSha512: 177 | { 178 | CTXPoint = (CC_SHA512_CTX *)calloc(1, (sizeof(CC_SHA512_CTX))); 179 | CC_SHA512_Init(CTXPoint); 180 | break; 181 | } 182 | default: 183 | { 184 | return nil; 185 | break; 186 | } 187 | } 188 | 189 | [inputStream open]; 190 | while (YES) 191 | { 192 | uint8_t buffer[THHashSizeForRead]; 193 | NSInteger readBytesCount = [inputStream read:buffer maxLength:sizeof(buffer)]; 194 | if (readBytesCount < 0) 195 | { 196 | [inputStream close]; 197 | free(CTXPoint); 198 | return nil; 199 | } 200 | else if (readBytesCount == 0) 201 | { 202 | break; 203 | } 204 | 205 | switch (hashKind) 206 | { 207 | case THHashKindMd5: 208 | { 209 | CC_MD5_Update(CTXPoint,(const void *)buffer,(CC_LONG)readBytesCount); 210 | break; 211 | } 212 | case THHashKindSha1: 213 | { 214 | CC_SHA1_Update(CTXPoint,(const void *)buffer,(CC_LONG)readBytesCount); 215 | break; 216 | } 217 | case THHashKindSha256: 218 | { 219 | CC_SHA256_Update(CTXPoint,(const void *)buffer,(CC_LONG)readBytesCount); 220 | break; 221 | } 222 | case THHashKindSha512: 223 | { 224 | CC_SHA512_Update(CTXPoint,(const void *)buffer,(CC_LONG)readBytesCount); 225 | break; 226 | } 227 | } 228 | } 229 | 230 | unsigned char *digest = NULL; 231 | NSUInteger digestLength = 0; 232 | switch (hashKind) 233 | { 234 | case THHashKindMd5: 235 | { 236 | digestLength = CC_MD5_DIGEST_LENGTH; 237 | digest = (u_char *)calloc(1, digestLength); 238 | CC_MD5_Final(digest, CTXPoint); 239 | break; 240 | } 241 | case THHashKindSha1: 242 | { 243 | digestLength = CC_SHA1_DIGEST_LENGTH; 244 | digest = (u_char *)calloc(1, digestLength); 245 | CC_SHA1_Final(digest, CTXPoint); 246 | break; 247 | } 248 | case THHashKindSha256: 249 | { 250 | digestLength = CC_SHA256_DIGEST_LENGTH; 251 | digest = (u_char *)calloc(1, digestLength); 252 | CC_SHA256_Final(digest, CTXPoint); 253 | break; 254 | } 255 | case THHashKindSha512: 256 | { 257 | digestLength = CC_SHA512_DIGEST_LENGTH; 258 | digest = (u_char *)calloc(1, digestLength); 259 | CC_SHA512_Final(digest, CTXPoint); 260 | break; 261 | } 262 | } 263 | // Compute the string result 264 | NSMutableString *hashString = [NSMutableString string]; 265 | for (size_t i = 0; i < digestLength; ++i) 266 | { 267 | [hashString appendFormat:@"%02x",digest[i]]; 268 | } 269 | 270 | [inputStream close]; 271 | free(digest); 272 | free(CTXPoint); 273 | return hashString; 274 | } 275 | 276 | + (NSString *)hashData:(NSData *)data with:(THHashKind)hashKind 277 | { 278 | if (!data) 279 | { 280 | return nil; 281 | } 282 | 283 | const char *cStr = [data bytes]; 284 | 285 | unsigned char *digest = NULL; 286 | NSUInteger digestLength = 0; 287 | switch (hashKind) 288 | { 289 | case THHashKindMd5: 290 | { 291 | digestLength = CC_MD5_DIGEST_LENGTH; 292 | digest = (u_char *)calloc(1, digestLength); 293 | CC_MD5(cStr, (uint32_t)data.length, digest); 294 | break; 295 | } 296 | case THHashKindSha1: 297 | { 298 | digestLength = CC_SHA1_DIGEST_LENGTH; 299 | digest = (u_char *)calloc(1, digestLength); 300 | CC_SHA1(cStr, (uint32_t)data.length, digest); 301 | break; 302 | } 303 | case THHashKindSha256: 304 | { 305 | digestLength = CC_SHA256_DIGEST_LENGTH; 306 | digest = (u_char *)calloc(1, digestLength); 307 | CC_SHA256(cStr, (uint32_t)data.length, digest); 308 | break; 309 | } 310 | case THHashKindSha512: 311 | { 312 | digestLength = CC_SHA512_DIGEST_LENGTH; 313 | digest = (u_char *)calloc(1, digestLength); 314 | CC_SHA512(cStr, (uint32_t)data.length, digest); 315 | break; 316 | } 317 | default: 318 | { 319 | return nil; 320 | break; 321 | } 322 | } 323 | 324 | NSMutableString *hashString = [NSMutableString string]; 325 | for (size_t i = 0; i < digestLength; ++i) 326 | { 327 | [hashString appendFormat:@"%02x",digest[i]]; 328 | } 329 | free(digest); 330 | return hashString; 331 | } 332 | 333 | + (NSString *)hashString:(NSString *)string with:(THHashKind)hashKind 334 | { 335 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 336 | if (!data) 337 | { 338 | return nil; 339 | } 340 | return [self hashData:data with:hashKind]; 341 | } 342 | 343 | #pragma mark - 344 | #pragma mark HMAC 345 | 346 | + (NSString *)hMacData:(NSData *)data withSecretKey:(NSString *)secretKey withHashKind:(THHashKind)hashKind 347 | { 348 | if (!data || !secretKey) 349 | { 350 | return nil; 351 | } 352 | 353 | CCHmacAlgorithm algorithm; 354 | NSUInteger digestLength; 355 | switch (hashKind) 356 | { 357 | case THHashKindMd5: 358 | { 359 | digestLength = CC_MD5_DIGEST_LENGTH; 360 | algorithm = kCCHmacAlgMD5; 361 | break; 362 | } 363 | case THHashKindSha1: 364 | { 365 | digestLength = CC_SHA1_DIGEST_LENGTH; 366 | algorithm = kCCHmacAlgSHA1; 367 | break; 368 | } 369 | case THHashKindSha256: 370 | { 371 | digestLength = CC_SHA256_DIGEST_LENGTH; 372 | algorithm = kCCHmacAlgSHA256; 373 | break; 374 | } 375 | case THHashKindSha512: 376 | { 377 | digestLength = CC_SHA512_DIGEST_LENGTH; 378 | algorithm = kCCHmacAlgSHA512; 379 | break; 380 | } 381 | default: 382 | { 383 | return nil; 384 | break; 385 | } 386 | } 387 | 388 | const char *cKey = [secretKey cStringUsingEncoding:NSUTF8StringEncoding]; 389 | unsigned char cHMAC[digestLength]; 390 | CCHmac(algorithm, cKey, strlen(cKey), data.bytes, data.length, cHMAC); 391 | 392 | NSMutableString* hash = [NSMutableString string]; 393 | for(int i = 0; i < sizeof(cHMAC); i++) 394 | { 395 | [hash appendFormat:@"%02x", cHMAC[i]]; 396 | } 397 | return hash; 398 | } 399 | 400 | + (NSString *)hMacString:(NSString *)string withSecretKey:(NSString *)secretKey withHashKind:(THHashKind)hashKind 401 | { 402 | NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; 403 | if (!data || !secretKey) 404 | { 405 | return nil; 406 | } 407 | return [self hMacData:data withSecretKey:secretKey withHashKind:hashKind]; 408 | } 409 | 410 | @end 411 | -------------------------------------------------------------------------------- /FindDuplicateFiles/THWebService/readme.txt: -------------------------------------------------------------------------------- 1 | THWebService是一个轻量级的网络访问管理类,对NSURLConnection进行封装,提供了get、post网络请求以及提供了文件的断点下载的简单接口(同步、异步、block)。另外将网络请求中常用的HASH算法、HMAC等算法的封装,如果你开发的软件经常与网络访问,或许这些代码将会为你带来不少的便利。 2 | 3 | 注:代码采用ARC。 4 | 5 | 1.1版本 6 | 添加了THDispatchQueue,GCD实现的一个多线程并发控制的队列。 7 | 添加了THWebDataCache,非常简单的缓存组件。 8 | 9 | 1.0版本: 10 | 11 | 1.对get、post(文件上传)、文件下载的接口的封装; 12 | 2.支持同步、异步、block三种调用方式; 13 | 3.对常用HASH及HMac算法的封装(支持对Data和文件两种方式)。 -------------------------------------------------------------------------------- /FindDuplicateFiles/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /FindDuplicateFiles/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | CFBundleDisplayName = "Duplicate Scanner"; 4 | CFBundleName = "Duplicate Scanner"; -------------------------------------------------------------------------------- /FindDuplicateFiles/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | FindDuplicateFiles 4 | 5 | Created by TanHao on 12-11-20. 6 | Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | */ 8 | 9 | "YES" = "YES"; 10 | "NO" = "NO"; 11 | 12 | "Scanning Conditions" = "Scanning Conditions"; 13 | "Scanning Paths" = "Scanning Paths"; -------------------------------------------------------------------------------- /FindDuplicateFiles/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FindDuplicateFiles 4 | // 5 | // Created by TanHao on 12-11-2. 6 | // Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /FindDuplicateFiles/zh-Hans.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | CFBundleDisplayName = "重复文件扫描"; 4 | CFBundleName = "重复文件扫描"; -------------------------------------------------------------------------------- /FindDuplicateFiles/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | FindDuplicateFiles 4 | 5 | Created by TanHao on 12-11-20. 6 | Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | */ 8 | 9 | "YES" = "是"; 10 | "NO" = "否"; 11 | 12 | "Scanning Conditions" = "扫描条件"; 13 | "Scanning Paths" = "扫描路径"; -------------------------------------------------------------------------------- /FindDuplicateFiles/zh-Hant.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | CFBundleDisplayName = "重複文件掃描"; 4 | CFBundleName = "重複文件掃描"; -------------------------------------------------------------------------------- /FindDuplicateFiles/zh-Hant.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | FindDuplicateFiles 4 | 5 | Created by TanHao on 12-11-20. 6 | Copyright (c) 2012年 tanhao.me. All rights reserved. 7 | */ 8 | 9 | "YES" = "是"; 10 | "NO" = "否"; 11 | 12 | "Scanning Conditions" = "掃描條件"; 13 | "Scanning Paths" = "掃描路徑"; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DuplicateScanner 2 | ================ 3 | 4 | 扫描重复文件 5 | 6 | 要腾出Mac的磁盘空间,找到无用的重复文件就是这其中比较重要的一个工作了,扫描重复文件的软件我试用过很多,包括大名鼎鼎的专业扫描重复文件的软件——Gemini,还有一些其它综合性的电脑维护软件如MacKeeper等,但我觉得查找效率是其中很大的问题,并且大多这类软件都依赖于spotlight服务,如果将spotlight关闭,它们将无法正常的工作。 7 | 于是就有了想法做自己的重复文件扫描软件,旨在完全压榨多线程所能达到的极致,和不依赖于spotlight的快速文件遍历等多种技术,当然个人的能力有限,所制作软件无法达到像Gemini软件炫丽的UI及效果,但这并不阻碍它拥有以下的优点: 8 | 1.自定义扫描条件,包括扫描的路径,扫描目标的文件大小,文件类型,是否扫描package等等; 9 | 2.及时的显示扫描结果,而不像大多数同类软件需要等到扫描结束后才能看到所有的结果; 10 | 3.完全运行于sandbox之内,更安全放心。 11 | 12 | 如果你想要捐助作者的话,就购买该软件在Mac App Store上面的收费版本吧,虽然我知道在国内很少有人愿意购买收费软件,该软件售价$4.99,人民币30RMB, 13 | https://itunes.apple.com/cn/app/sao-miao-zhong-fu-wen-jian/id580824234?mt=12 -------------------------------------------------------------------------------- /Resources/headerBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/headerBg.png -------------------------------------------------------------------------------- /Resources/headerBg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/headerBg@2x.png -------------------------------------------------------------------------------- /Resources/headerLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/headerLogo.png -------------------------------------------------------------------------------- /Resources/headerLogo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/headerLogo@2x.png -------------------------------------------------------------------------------- /Resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/icon.icns -------------------------------------------------------------------------------- /Resources/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/start.png -------------------------------------------------------------------------------- /Resources/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanhaogg/DuplicateScanner/5cd5fd654653cabfc41896b3418bef19338e23ba/Resources/stop.png --------------------------------------------------------------------------------