├── .gitignore ├── Credits.rtf ├── DraggableIconView.h ├── DraggableIconView.m ├── IconScanner-Info.plist ├── IconScanner.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── sveinbjorn.xcuserdatad │ └── xcschemes │ ├── IconScanner.xcscheme │ └── xcschememanagement.plist ├── IconScanner └── Images.xcassets │ └── AppIcon.appiconset │ ├── Contents.json │ └── icon.png ├── IconScannerController.h ├── IconScannerController.m ├── IconScanner_Prefix.pch ├── LICENSE.txt ├── MainMenu.xib ├── NSWorkspace+Additions.h ├── NSWorkspace+Additions.m ├── README.md ├── build.sh ├── main.m ├── screenshot.jpg └── searchfs /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | DerivedData 3 | ## Various settings 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | 14 | ## Other 15 | *.xccheckout 16 | *.moved-aside 17 | *.xcuserstate 18 | *.xcworkspace 19 | 20 | dsa_priv.pem 21 | -------------------------------------------------------------------------------- /Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf100 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | \paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0 6 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 7 | 8 | \f0\fs24 \cf0 Created by {\field{\*\fldinst{HYPERLINK "mailto:sveinbjornt@gmail.com"}}{\fldrslt Sveinbjorn Thordarson}}\ 9 | \ 10 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 11 | {\field{\*\fldinst{HYPERLINK "http://sveinbjorn.org/iconscanner"}}{\fldrslt \cf0 http://sveinbjorn.org/iconscanner}}\ 12 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\qc\partightenfactor0 13 | \cf0 \ 14 | IconScanner is free, open-source software distributed under a BSD license.} -------------------------------------------------------------------------------- /DraggableIconView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DraggableIconView.h 3 | // 4 | // Created by Sveinbjorn Thordarson on 9/7/10. 5 | // Copyright 2010 Sveinbjorn Thordarson. All rights reserved. 6 | // Distributed under a 3-clause BSD License 7 | // 8 | 9 | #import 10 | 11 | @protocol DraggableIconViewDelegate 12 | - (NSString *)selectedFilePath; 13 | - (NSDragOperation)draggingEntered:(id )sender; 14 | - (void)draggingExited:(id )sender; 15 | - (NSDragOperation)draggingUpdated:(id )sender; 16 | - (BOOL)prepareForDragOperation:(id )sender; 17 | - (BOOL)performDragOperation:(id )sender; 18 | - (void)concludeDragOperation:(id )sender; 19 | @end 20 | 21 | @interface DraggableIconView : NSImageView 22 | { 23 | NSEvent *downEvent; 24 | } 25 | 26 | @property (nonatomic, unsafe_unretained) id delegate; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /DraggableIconView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DraggableIconView.m 3 | // 4 | // Created by Sveinbjorn Thordarson on 9/7/10. 5 | // Copyright 2010 Sveinbjorn Thordarson. All rights reserved. 6 | // Distributed under a 3-clause BSD License 7 | // 8 | 9 | #import "DraggableIconView.h" 10 | 11 | @implementation DraggableIconView 12 | 13 | #pragma mark - Dragging 14 | 15 | - (NSDragOperation)draggingEntered:(id )sender { 16 | if (_delegate && [_delegate respondsToSelector:@selector(draggingEntered:)]) 17 | return [_delegate draggingEntered:sender]; 18 | else 19 | return [super draggingEntered:sender]; 20 | } 21 | 22 | - (void)draggingExited:(id )sender { 23 | if (_delegate && [_delegate respondsToSelector:@selector(draggingExited:)]) 24 | [_delegate draggingExited:sender]; 25 | else 26 | [super draggingExited:sender]; 27 | } 28 | 29 | - (NSDragOperation)draggingUpdated:(id )sender { 30 | if (_delegate && [_delegate respondsToSelector:@selector(draggingUpdated:)]) 31 | return [_delegate draggingUpdated:sender]; 32 | else 33 | return [super draggingUpdated:sender]; 34 | } 35 | 36 | - (BOOL)prepareForDragOperation:(id )sender { 37 | if (_delegate && [_delegate respondsToSelector:@selector(prepareForDragOperation:)]) 38 | return [_delegate prepareForDragOperation:sender]; 39 | else 40 | return [super prepareForDragOperation:sender]; 41 | } 42 | 43 | - (BOOL)performDragOperation:(id )sender { 44 | if (_delegate && [_delegate respondsToSelector:@selector(performDragOperation:)]) 45 | return [_delegate performDragOperation:sender]; 46 | else 47 | return [super performDragOperation:sender]; 48 | } 49 | 50 | - (void)concludeDragOperation:(id )sender { 51 | if (_delegate && [_delegate respondsToSelector:@selector(concludeDragOperation:)]) 52 | [_delegate concludeDragOperation:sender]; 53 | else 54 | [super concludeDragOperation:sender]; 55 | } 56 | 57 | #pragma mark - Drag source 58 | 59 | - (void)mouseDown:(NSEvent*)event { 60 | if ([self image] == nil) { 61 | return; 62 | } 63 | 64 | //get the Pasteboard used for drag and drop operations 65 | NSPasteboard *dragPasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; 66 | 67 | //create a new image for our semi-transparent drag image 68 | NSImage *dragImage = [[NSImage alloc] initWithSize:[[self image] size]]; 69 | if (dragImage == nil) { 70 | return; 71 | } 72 | 73 | //OK, let's see if we have an icns file behind this 74 | NSString *path = [_delegate selectedFilePath]; 75 | if (![path isEqualToString:@""]) { 76 | [dragPasteboard declareTypes:@[NSFilenamesPboardType] owner:self]; 77 | [dragPasteboard setPropertyList:@[path] forType:NSFilenamesPboardType]; 78 | } 79 | 80 | //draw our original image as 50% transparent 81 | [dragImage lockFocus]; 82 | [[self image] dissolveToPoint:NSZeroPoint fraction:.5]; 83 | [dragImage unlockFocus];//finished drawing 84 | [dragImage setSize:[self bounds].size];//change to the size we are displaying 85 | 86 | //execute the drag 87 | [self dragImage:dragImage //image to be displayed under the mouse 88 | at:[self bounds].origin //point to start drawing drag image 89 | offset:NSZeroSize //no offset, drag starts at mousedown location 90 | event:event //mousedown event 91 | pasteboard:dragPasteboard //pasteboard to pass to receiver 92 | source:self //object where the image is coming from 93 | slideBack:YES]; //if the drag fails slide the icon back 94 | 95 | } 96 | 97 | - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag { 98 | return flag ? NSDragOperationNone : NSDragOperationCopy; 99 | } 100 | 101 | - (BOOL)ignoreModifierKeysWhileDragging { 102 | return YES; 103 | } 104 | 105 | - (BOOL)acceptsFirstMouse:(NSEvent *)event { 106 | //so source doesn't have to be the active window 107 | return YES; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /IconScanner-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleGetInfoString 6 | © 2010 Sveinbjorn Thordarson. 7 | NSHumanReadableCopyright 8 | © 2010 Sveinbjorn Thordarson. 9 | CFBundleDevelopmentRegion 10 | English 11 | CFBundleExecutable 12 | ${EXECUTABLE_NAME} 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | IconScanner 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.2 23 | LSMinimumSystemVersion 24 | ${MACOSX_DEPLOYMENT_TARGET} 25 | NSMainNibFile 26 | MainMenu 27 | NSPrincipalClass 28 | NSApplication 29 | 30 | 31 | -------------------------------------------------------------------------------- /IconScanner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 256AC3DA0F4B6AC300CF3369 /* IconScannerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* IconScannerController.m */; }; 11 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 12 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 13 | F4493F3D203D92490035D947 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4493F3C203D92490035D947 /* Images.xcassets */; }; 14 | F4493F3F203D92930035D947 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = F4493F3E203D92930035D947 /* Credits.rtf */; }; 15 | F4493F48203E22580035D947 /* NSWorkspace+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F4493F47203E22580035D947 /* NSWorkspace+Additions.m */; }; 16 | F470F9921235CC1900F6498D /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F470F9911235CC1900F6498D /* Quartz.framework */; }; 17 | F4A1941120DBC2ED001EECE7 /* searchfs in Resources */ = {isa = PBXBuildFile; fileRef = F4A1941020DBC2ED001EECE7 /* searchfs */; }; 18 | F4F0D13B123719D000A70C51 /* DraggableIconView.m in Sources */ = {isa = PBXBuildFile; fileRef = F4F0D13A123719D000A70C51 /* DraggableIconView.m */; }; 19 | F4F0D21212372F7400A70C51 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58150DA1D0A300B32029 /* MainMenu.xib */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 24 | 1DDD58150DA1D0A300B32029 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 25 | 256AC3D80F4B6AC300CF3369 /* IconScannerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconScannerController.h; sourceTree = ""; }; 26 | 256AC3D90F4B6AC300CF3369 /* IconScannerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IconScannerController.m; sourceTree = ""; }; 27 | 256AC3F00F4B6AF500CF3369 /* IconScanner_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconScanner_Prefix.pch; sourceTree = ""; }; 28 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 8D1107310486CEB800E47090 /* IconScanner-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IconScanner-Info.plist"; sourceTree = ""; }; 30 | 8D1107320486CEB800E47090 /* IconScanner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IconScanner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | F4493F3C203D92490035D947 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = IconScanner/Images.xcassets; sourceTree = ""; }; 32 | F4493F3E203D92930035D947 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 33 | F4493F44203D9EBC0035D947 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 34 | F4493F46203E22580035D947 /* NSWorkspace+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSWorkspace+Additions.h"; sourceTree = ""; }; 35 | F4493F47203E22580035D947 /* NSWorkspace+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSWorkspace+Additions.m"; sourceTree = ""; }; 36 | F470F9911235CC1900F6498D /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = /System/Library/Frameworks/Quartz.framework; sourceTree = ""; }; 37 | F4A122E22035AEB200A7F19E /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; 38 | F4A1941020DBC2ED001EECE7 /* searchfs */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = searchfs; sourceTree = ""; }; 39 | F4F0D139123719D000A70C51 /* DraggableIconView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DraggableIconView.h; sourceTree = ""; }; 40 | F4F0D13A123719D000A70C51 /* DraggableIconView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DraggableIconView.m; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 49 | F470F9921235CC1900F6498D /* Quartz.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 080E96DDFE201D6D7F000001 /* Source */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 256AC3F00F4B6AF500CF3369 /* IconScanner_Prefix.pch */, 60 | 29B97316FDCFA39411CA2CEA /* main.m */, 61 | 256AC3D80F4B6AC300CF3369 /* IconScannerController.h */, 62 | 256AC3D90F4B6AC300CF3369 /* IconScannerController.m */, 63 | F4493F43203D93990035D947 /* Util */, 64 | ); 65 | name = Source; 66 | sourceTree = ""; 67 | }; 68 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 8D1107320486CEB800E47090 /* IconScanner.app */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 29B97314FDCFA39411CA2CEA /* IconScanner */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | F4493F44203D9EBC0035D947 /* README.md */, 80 | F4A122E22035AEB200A7F19E /* LICENSE.txt */, 81 | 080E96DDFE201D6D7F000001 /* Source */, 82 | 29B97317FDCFA39411CA2CEA /* Resources */, 83 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 84 | 19C28FACFE9D520D11CA2CBB /* Products */, 85 | ); 86 | name = IconScanner; 87 | sourceTree = ""; 88 | }; 89 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | F4A1941020DBC2ED001EECE7 /* searchfs */, 93 | F4493F3E203D92930035D947 /* Credits.rtf */, 94 | F4493F3C203D92490035D947 /* Images.xcassets */, 95 | 8D1107310486CEB800E47090 /* IconScanner-Info.plist */, 96 | 1DDD58150DA1D0A300B32029 /* MainMenu.xib */, 97 | ); 98 | name = Resources; 99 | sourceTree = ""; 100 | }; 101 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 105 | F470F9911235CC1900F6498D /* Quartz.framework */, 106 | ); 107 | name = Frameworks; 108 | sourceTree = ""; 109 | }; 110 | F4493F43203D93990035D947 /* Util */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | F4493F46203E22580035D947 /* NSWorkspace+Additions.h */, 114 | F4493F47203E22580035D947 /* NSWorkspace+Additions.m */, 115 | F4F0D139123719D000A70C51 /* DraggableIconView.h */, 116 | F4F0D13A123719D000A70C51 /* DraggableIconView.m */, 117 | ); 118 | name = Util; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | 8D1107260486CEB800E47090 /* IconScanner */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IconScanner" */; 127 | buildPhases = ( 128 | 8D1107290486CEB800E47090 /* Resources */, 129 | 8D11072C0486CEB800E47090 /* Sources */, 130 | 8D11072E0486CEB800E47090 /* Frameworks */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | ); 136 | name = IconScanner; 137 | productInstallPath = "$(HOME)/Applications"; 138 | productName = IconScanner; 139 | productReference = 8D1107320486CEB800E47090 /* IconScanner.app */; 140 | productType = "com.apple.product-type.application"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastUpgradeCheck = 0940; 149 | }; 150 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IconScanner" */; 151 | compatibilityVersion = "Xcode 3.1"; 152 | developmentRegion = English; 153 | hasScannedForEncodings = 1; 154 | knownRegions = ( 155 | English, 156 | Japanese, 157 | French, 158 | German, 159 | ); 160 | mainGroup = 29B97314FDCFA39411CA2CEA /* IconScanner */; 161 | projectDirPath = ""; 162 | projectRoot = ""; 163 | targets = ( 164 | 8D1107260486CEB800E47090 /* IconScanner */, 165 | ); 166 | }; 167 | /* End PBXProject section */ 168 | 169 | /* Begin PBXResourcesBuildPhase section */ 170 | 8D1107290486CEB800E47090 /* Resources */ = { 171 | isa = PBXResourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | F4493F3D203D92490035D947 /* Images.xcassets in Resources */, 175 | F4F0D21212372F7400A70C51 /* MainMenu.xib in Resources */, 176 | F4A1941120DBC2ED001EECE7 /* searchfs in Resources */, 177 | F4493F3F203D92930035D947 /* Credits.rtf in Resources */, 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | }; 181 | /* End PBXResourcesBuildPhase section */ 182 | 183 | /* Begin PBXSourcesBuildPhase section */ 184 | 8D11072C0486CEB800E47090 /* Sources */ = { 185 | isa = PBXSourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 189 | F4493F48203E22580035D947 /* NSWorkspace+Additions.m in Sources */, 190 | 256AC3DA0F4B6AC300CF3369 /* IconScannerController.m in Sources */, 191 | F4F0D13B123719D000A70C51 /* DraggableIconView.m in Sources */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | /* End PBXSourcesBuildPhase section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | C01FCF4B08A954540054247B /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 202 | CLANG_ENABLE_OBJC_ARC = YES; 203 | CODE_SIGN_IDENTITY = "Mac Developer"; 204 | CODE_SIGN_STYLE = Automatic; 205 | COPY_PHASE_STRIP = NO; 206 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 207 | DEVELOPMENT_TEAM = 5WX26Y89JP; 208 | GCC_DYNAMIC_NO_PIC = NO; 209 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 210 | GCC_OPTIMIZATION_LEVEL = 0; 211 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 212 | GCC_PREFIX_HEADER = IconScanner_Prefix.pch; 213 | GENERATE_PKGINFO_FILE = NO; 214 | INFOPLIST_FILE = "IconScanner-Info.plist"; 215 | INSTALL_PATH = ""; 216 | MACOSX_DEPLOYMENT_TARGET = 10.8; 217 | PRODUCT_BUNDLE_IDENTIFIER = org.sveinbjorn.IconScanner; 218 | PRODUCT_NAME = IconScanner; 219 | PROVISIONING_PROFILE_SPECIFIER = ""; 220 | }; 221 | name = Debug; 222 | }; 223 | C01FCF4C08A954540054247B /* Release */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 227 | CLANG_ENABLE_OBJC_ARC = YES; 228 | CODE_SIGN_IDENTITY = "Mac Developer"; 229 | CODE_SIGN_STYLE = Automatic; 230 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 231 | DEVELOPMENT_TEAM = 5WX26Y89JP; 232 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 233 | GCC_PREFIX_HEADER = IconScanner_Prefix.pch; 234 | GENERATE_PKGINFO_FILE = NO; 235 | INFOPLIST_FILE = "IconScanner-Info.plist"; 236 | INSTALL_PATH = ""; 237 | MACOSX_DEPLOYMENT_TARGET = 10.8; 238 | PRODUCT_BUNDLE_IDENTIFIER = org.sveinbjorn.IconScanner; 239 | PRODUCT_NAME = IconScanner; 240 | PROVISIONING_PROFILE_SPECIFIER = ""; 241 | }; 242 | name = Release; 243 | }; 244 | C01FCF4F08A954540054247B /* Debug */ = { 245 | isa = XCBuildConfiguration; 246 | buildSettings = { 247 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 248 | CLANG_WARN_BOOL_CONVERSION = YES; 249 | CLANG_WARN_COMMA = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 258 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 260 | CLANG_WARN_STRICT_PROTOTYPES = YES; 261 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 262 | CLANG_WARN_UNREACHABLE_CODE = YES; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | ENABLE_STRICT_OBJC_MSGSEND = YES; 265 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 266 | GCC_NO_COMMON_BLOCKS = NO; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | MACOSX_DEPLOYMENT_TARGET = 10.8; 275 | ONLY_ACTIVE_ARCH = YES; 276 | SDKROOT = macosx; 277 | }; 278 | name = Debug; 279 | }; 280 | C01FCF5008A954540054247B /* Release */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 284 | CLANG_WARN_BOOL_CONVERSION = YES; 285 | CLANG_WARN_COMMA = YES; 286 | CLANG_WARN_CONSTANT_CONVERSION = YES; 287 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 288 | CLANG_WARN_EMPTY_BODY = YES; 289 | CLANG_WARN_ENUM_CONVERSION = YES; 290 | CLANG_WARN_INFINITE_RECURSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 293 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 294 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 295 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 296 | CLANG_WARN_STRICT_PROTOTYPES = YES; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_UNREACHABLE_CODE = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | ENABLE_STRICT_OBJC_MSGSEND = YES; 301 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 302 | GCC_NO_COMMON_BLOCKS = NO; 303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 305 | GCC_WARN_UNDECLARED_SELECTOR = YES; 306 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 307 | GCC_WARN_UNUSED_FUNCTION = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | MACOSX_DEPLOYMENT_TARGET = 10.8; 310 | ONLY_ACTIVE_ARCH = NO; 311 | SDKROOT = macosx; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "IconScanner" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | C01FCF4B08A954540054247B /* Debug */, 322 | C01FCF4C08A954540054247B /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "IconScanner" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | C01FCF4F08A954540054247B /* Debug */, 331 | C01FCF5008A954540054247B /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | }; 338 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 339 | } 340 | -------------------------------------------------------------------------------- /IconScanner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IconScanner.xcodeproj/xcuserdata/sveinbjorn.xcuserdatad/xcschemes/IconScanner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /IconScanner.xcodeproj/xcuserdata/sveinbjorn.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | IconScanner.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 8D1107260486CEB800E47090 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /IconScanner/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "512x512", 45 | "idiom" : "mac", 46 | "filename" : "icon.png", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "idiom" : "mac", 51 | "size" : "512x512", 52 | "scale" : "2x" 53 | } 54 | ], 55 | "info" : { 56 | "version" : 1, 57 | "author" : "xcode" 58 | } 59 | } -------------------------------------------------------------------------------- /IconScanner/Images.xcassets/AppIcon.appiconset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/IconScanner/ee4f821ff35a0dcda59b17e18e4f5636bf9ec01e/IconScanner/Images.xcassets/AppIcon.appiconset/icon.png -------------------------------------------------------------------------------- /IconScannerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // IconScannerAppDelegate.h 3 | // IconScanner 4 | // 5 | // Created by Sveinbjorn Thordarson on 9/7/10. 6 | // Copyright 2010 Sveinbjorn Thordarson. All rights reserved. 7 | // Distributed under a 3-clause BSD License 8 | // 9 | 10 | #import 11 | 12 | @interface IconScannerController : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /IconScannerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // IconScannerAppDelegate.m 3 | // IconScanner 4 | // 5 | // Created by Sveinbjorn Thordarson on 9/7/10. 6 | // Copyright 2010 Sveinbjorn Thordarson. All rights reserved. 7 | // Distributed under a 3-clause BSD License 8 | // 9 | 10 | #import "IconScannerController.h" 11 | #import "NSWorkspace+Additions.h" 12 | #import 13 | 14 | #pragma mark - Data source object 15 | 16 | @interface myImageObject : NSObject 17 | { 18 | NSString* path; 19 | int imageSize; 20 | } 21 | @property (nonatomic) int imageSize; 22 | 23 | @end 24 | 25 | @implementation myImageObject 26 | 27 | - (void)setPath:(NSString *)inPath { 28 | if (path != inPath) { 29 | path = inPath; 30 | } 31 | } 32 | 33 | #pragma mark IKImageBrowserItem protocol 34 | 35 | - (NSString*)imageRepresentationType { 36 | return IKImageBrowserPathRepresentationType; 37 | } 38 | 39 | - (id)imageRepresentation { 40 | return path; 41 | } 42 | 43 | - (NSString*)imageUID { 44 | return path; 45 | } 46 | 47 | - (int)imageSize { 48 | return imageSize; 49 | } 50 | 51 | - (void)setImageSize:(int)size { 52 | imageSize = size; 53 | } 54 | 55 | @end 56 | 57 | #pragma mark - UI Controller 58 | 59 | @interface IconScannerController () 60 | { 61 | IBOutlet NSWindow *window; 62 | IBOutlet id progressIndicator; 63 | IBOutlet IKImageBrowserView *imageBrowser; 64 | NSMutableArray *images; 65 | NSMutableArray *imagesSubset; 66 | NSMutableArray *activeSet; 67 | NSMutableArray *importedImages; 68 | NSTask *task; 69 | NSTimer *checkStatusTimer; 70 | NSTimer *filterTimer; 71 | NSString *output; 72 | NSPipe *outputPipe; 73 | NSFileHandle *readHandle; 74 | IBOutlet id scanButton; 75 | IBOutlet id selectedIconPathLabel; 76 | IBOutlet id selectedIconSizeLabel; 77 | IBOutlet id selectedIconFileSizeLabel; 78 | IBOutlet id selectedIconImageView; 79 | IBOutlet id selectedIconRepsLabel; 80 | IBOutlet id statusLabel; 81 | IBOutlet id iconSizeSlider; 82 | IBOutlet id selectedIconBox; 83 | IBOutlet id numItemsLabel; 84 | IBOutlet id searchFilterTextField; 85 | IBOutlet id searchToolPopupButton; 86 | IBOutlet id saveCopyButton; 87 | 88 | 89 | int itemsFound; 90 | IBOutlet id progressWindow; 91 | IBOutlet id progressBar; 92 | IBOutlet id progressTextField; 93 | } 94 | - (IBAction)scan:(id)sender; 95 | - (IBAction)zoomSliderDidChange:(id)sender; 96 | - (IBAction)saveCopy:(id)sender; 97 | @end 98 | 99 | @implementation IconScannerController 100 | 101 | + (void)initialize { 102 | // create the user defaults here if none exists 103 | NSMutableDictionary *defaultPrefs = [NSMutableDictionary dictionary]; 104 | 105 | defaultPrefs[@"pathInFilter"] = @YES; 106 | defaultPrefs[@"filenameInFilter"] = @NO; 107 | defaultPrefs[@"scanToolIndex"] = @0; 108 | defaultPrefs[@"iconDisplaySize"] = @0.5f; 109 | // load tool cmd dictionary 110 | NSDictionary *scanToolsDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ScanTools" ofType:@"plist"]]; 111 | defaultPrefs[@"ScanTools"] = scanToolsDict[@"ScanTools"]; 112 | 113 | // register the dictionary of defaults 114 | [[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs]; 115 | } 116 | 117 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 118 | 119 | [window setRepresentedURL:[NSURL URLWithString:@""]]; 120 | [[window standardWindowButton:NSWindowDocumentIconButton] setImage:[NSApp applicationIconImage]]; 121 | 122 | [[NSNotificationCenter defaultCenter] addObserver:self 123 | selector:@selector(foundFiles) 124 | name:@"IconScannerFilesFoundNotification" 125 | object:nil]; 126 | 127 | 128 | } 129 | 130 | - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu { 131 | // Prevent popup menu when window icon/title is cmd-clicked 132 | return NO; 133 | } 134 | 135 | - (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard { 136 | // Prevent dragging of title bar icon 137 | return NO; 138 | } 139 | 140 | - (void)awakeFromNib { 141 | // Create two arrays: The first is for the data source representation. 142 | // The second one contains temporary imported images for thread safety. 143 | images = [NSMutableArray array]; 144 | importedImages = [NSMutableArray array]; 145 | 146 | // Allow reordering, animations and set the dragging destination delegate. 147 | [imageBrowser setAnimates:YES]; 148 | [imageBrowser setDraggingDestinationDelegate:self]; 149 | [imageBrowser setAllowsMultipleSelection:NO]; 150 | [imageBrowser setAllowsReordering:NO]; 151 | [imageBrowser setZoomValue:[[[NSUserDefaults standardUserDefaults] objectForKey:@"iconDisplaySize"] floatValue]]; 152 | } 153 | 154 | - (void)updateDatasource { 155 | // Update the datasource, add recently imported items. 156 | [images addObjectsFromArray:importedImages]; 157 | 158 | // Empty the temporary array. 159 | [importedImages removeAllObjects]; 160 | 161 | // Reload the image browser, which triggers setNeedsDisplay. 162 | [imageBrowser reloadData]; 163 | } 164 | 165 | // ------------------------------------------------------------------------- 166 | // isImageFile:filePath 167 | // 168 | // This utility method indicates if the file located at 'filePath' is 169 | // an image file based on the UTI. It relies on the ImageIO framework for the 170 | // supported type identifiers. 171 | // 172 | // ------------------------------------------------------------------------- 173 | - (BOOL)isImageFile:(NSString *)filePath { 174 | BOOL isImageFile = NO; 175 | LSItemInfoRecord info; 176 | CFStringRef uti = NULL; 177 | 178 | CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE); 179 | 180 | if (LSCopyItemInfoForURL(url, kLSRequestExtension | kLSRequestTypeCreator, &info) == noErr) { 181 | // Obtain the UTI using the file information. 182 | 183 | // If there is a file extension, get the UTI. 184 | if (info.extension != NULL) { 185 | uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, info.extension, kUTTypeData); 186 | CFRelease(info.extension); 187 | } 188 | 189 | // No UTI yet 190 | if (uti == NULL) { 191 | // If there is an OSType, get the UTI. 192 | CFStringRef typeString = UTCreateStringForOSType(info.filetype); 193 | if ( typeString != NULL) { 194 | uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, typeString, kUTTypeData); 195 | CFRelease(typeString); 196 | } 197 | } 198 | 199 | // Verify that this is a file that the ImageIO framework supports. 200 | if (uti != NULL) { 201 | CFArrayRef supportedTypes = CGImageSourceCopyTypeIdentifiers(); 202 | CFIndex i, typeCount = CFArrayGetCount(supportedTypes); 203 | 204 | for (i = 0; i < typeCount; i++) { 205 | if (UTTypeConformsTo(uti, (CFStringRef)CFArrayGetValueAtIndex(supportedTypes, i))) { 206 | isImageFile = YES; 207 | break; 208 | } 209 | } 210 | 211 | CFRelease(supportedTypes); 212 | CFRelease(uti); 213 | } 214 | } 215 | 216 | CFRelease(url); 217 | 218 | return isImageFile; 219 | } 220 | 221 | - (void)addAnImageWithPath:(NSString*)path { 222 | if ([self isImageFile:path]) { 223 | // Add a path to the temporary images array. 224 | myImageObject* p = [[myImageObject alloc] init]; 225 | [p setPath:path]; 226 | [importedImages addObject:p]; 227 | } 228 | } 229 | 230 | - (IBAction)scan:(id)sender { 231 | if ([[sender title] isEqualToString:@"Cancel"]) { 232 | [task terminate]; 233 | [progressIndicator stopAnimation:self]; 234 | [sender setTitle:@"Scan"]; 235 | return; 236 | } 237 | 238 | itemsFound = 0; 239 | [self foundFiles]; 240 | [sender setTitle:@"Cancel"]; 241 | 242 | [progressIndicator setUsesThreadedAnimation:YES]; 243 | [progressIndicator startAnimation:self]; 244 | 245 | [images removeAllObjects]; 246 | output = @""; 247 | 248 | task = [[NSTask alloc] init]; 249 | 250 | outputPipe = [NSPipe pipe]; 251 | [task setStandardOutput:outputPipe]; 252 | readHandle = [outputPipe fileHandleForReading]; 253 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getTextData:) name:NSFileHandleReadCompletionNotification object:readHandle]; 254 | [readHandle readInBackgroundAndNotify]; 255 | 256 | NSString *title = [searchToolPopupButton titleOfSelectedItem]; 257 | if ([title isEqualToString:@"mdfind"]) { 258 | [task setLaunchPath:@"/usr/bin/mdfind"]; 259 | //[task setArguments:@[@"-name", @".icns"]]; 260 | [task setArguments:@[@"kMDItemContentType == 'com.apple.icns'"]]; 261 | } else if ([title isEqualToString:@"find"]) { 262 | [task setLaunchPath:@"/usr/bin/find"]; 263 | [task setArguments:@[@"/", @"-name", @"*.icns"]]; 264 | } else if ([title isEqualToString:@"searchfs"]) { 265 | NSString *searchfsPath = [[NSBundle mainBundle] pathForResource:@"searchfs" ofType:@""]; 266 | if ([[NSFileManager defaultManager] fileExistsAtPath:searchfsPath]) { 267 | [task setLaunchPath:searchfsPath]; 268 | [task setArguments:@[@".icns"]]; 269 | } else { 270 | NSBeep(); 271 | } 272 | } else { 273 | [task setLaunchPath:@"/usr/bin/locate"]; 274 | [task setArguments:@[@"*.icns"]]; 275 | } 276 | 277 | [task launch]; 278 | 279 | [progressBar setUsesThreadedAnimation:YES]; 280 | [progressBar startAnimation:self]; 281 | 282 | [NSApp beginSheet:progressWindow 283 | modalForWindow:window 284 | modalDelegate:nil 285 | didEndSelector:nil 286 | contextInfo:nil]; 287 | 288 | //set off timer that checks task status, i.e. when it's done 289 | checkStatusTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 290 | target:self 291 | selector:@selector(checkTaskStatus) 292 | userInfo:nil 293 | repeats:YES]; 294 | } 295 | 296 | // read from the file handle and append it to the text window 297 | - (void)getTextData:(NSNotification *)aNotification { 298 | //get the data 299 | NSData *data = [aNotification userInfo][NSFileHandleNotificationDataItem]; 300 | 301 | //make sure there's actual data 302 | if ([data length]) { 303 | //append the output to the text field 304 | NSString *outputStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; 305 | 306 | output = [output stringByAppendingString:outputStr]; 307 | 308 | itemsFound += [[outputStr componentsSeparatedByString:@"\n"] count]-1; 309 | [[NSNotificationCenter defaultCenter] postNotificationName:@"IconScannerFilesFoundNotification" object:self]; 310 | 311 | // we schedule the file handle to go and read more data in the background again. 312 | [[aNotification object] readInBackgroundAndNotify]; 313 | } 314 | } 315 | 316 | // check if task is running 317 | - (void)checkTaskStatus { 318 | if (![task isRunning]) { 319 | [checkStatusTimer invalidate]; 320 | [self taskFinished]; 321 | } 322 | } 323 | 324 | - (void)taskFinished { 325 | NSMutableArray *appPaths = [NSMutableArray arrayWithArray:[output componentsSeparatedByString:@"\n"]]; 326 | [appPaths removeLastObject]; 327 | [self addImagesWithPaths:appPaths]; 328 | 329 | [progressIndicator stopAnimation:self]; 330 | [self filterResults]; 331 | [self updateDatasource]; 332 | [numItemsLabel setStringValue:[NSString stringWithFormat:@"%d items", (int)[activeSet count]]]; 333 | [searchFilterTextField setEnabled:YES]; 334 | task = nil; 335 | [scanButton setTitle:@"Scan"]; 336 | 337 | // Dialog ends here. 338 | [NSApp endSheet:progressWindow]; 339 | [progressWindow orderOut:self]; 340 | } 341 | 342 | // creates a subset of the list of files based on our filtering criterion 343 | - (void)filterResults { 344 | NSEnumerator *e = [images objectEnumerator]; 345 | id object; 346 | 347 | 348 | imagesSubset = [[NSMutableArray alloc] init]; 349 | NSString *filterString = [searchFilterTextField stringValue]; 350 | 351 | while (object = [e nextObject]) { 352 | BOOL filtered = NO; 353 | 354 | if ([filterString length]) { 355 | NSString *rep = [object imageRepresentation]; 356 | 357 | if ([rep rangeOfString:filterString options:NSCaseInsensitiveSearch].location == NSNotFound) { 358 | filtered = YES; 359 | } 360 | } 361 | 362 | if (!filtered) { 363 | [imagesSubset addObject:object]; 364 | } 365 | } 366 | 367 | activeSet = imagesSubset; 368 | 369 | [numItemsLabel setStringValue:[NSString stringWithFormat:@"%d items", (int)[activeSet count]]]; 370 | } 371 | 372 | - (void)controlTextDidChange:(NSNotification *)aNotification { 373 | if (filterTimer != nil) { 374 | [filterTimer invalidate]; 375 | filterTimer = nil; 376 | } 377 | filterTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(updateListing) userInfo:nil repeats:NO]; 378 | } 379 | 380 | - (void)updateListing { 381 | [self filterResults]; 382 | [self updateDatasource]; 383 | [filterTimer invalidate]; 384 | filterTimer = nil; 385 | } 386 | 387 | - (NSString *)selectedFilePath { 388 | NSUInteger sel = [[imageBrowser selectionIndexes] firstIndex]; 389 | if (sel == -1 || sel > [activeSet count]) { 390 | return @""; 391 | } 392 | 393 | NSString *path = [activeSet[sel] imageRepresentation]; 394 | return path; 395 | } 396 | 397 | - (void)foundFiles { 398 | [progressTextField setStringValue:[NSString stringWithFormat:@"Found %d icons", itemsFound]]; 399 | } 400 | 401 | // ------------------------------------------------------------------------- 402 | // addImagesWithPaths:paths 403 | // 404 | // Performed in an independent thread, parse all paths in "paths" and 405 | // add these paths in the temporary images array. 406 | // ------------------------------------------------------------------------- 407 | - (void)addImagesWithPaths:(NSArray*)paths { 408 | @autoreleasepool { 409 | 410 | NSInteger i, n; 411 | n = [paths count]; 412 | for (i = 0; i < n; i++) { 413 | NSString* path = paths[i]; 414 | 415 | BOOL dir; 416 | [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&dir]; 417 | if (!dir) { 418 | [self addAnImageWithPath:path]; 419 | } 420 | } 421 | 422 | // Update the data source in the main thread. 423 | [self performSelectorOnMainThread:@selector(updateDatasource) withObject:nil waitUntilDone:YES]; 424 | 425 | } 426 | } 427 | 428 | - (IBAction)zoomSliderDidChange:(id)sender { 429 | // update the zoom value to scale images 430 | [imageBrowser setZoomValue:[sender floatValue]]; 431 | 432 | // redisplay 433 | [imageBrowser setNeedsDisplay:YES]; 434 | } 435 | 436 | - (IBAction)saveCopy:(id)sender { 437 | NSString *path = [self selectedFilePath]; 438 | if ([[NSFileManager defaultManager] fileExistsAtPath:path] == NO) { 439 | NSBeep(); 440 | return; 441 | } 442 | 443 | // Run save panel 444 | NSSavePanel *sPanel = [NSSavePanel savePanel]; 445 | [sPanel setPrompt:@"Save"]; 446 | [sPanel setNameFieldStringValue:[path lastPathComponent]]; 447 | [sPanel beginSheetModalForWindow:window completionHandler:^(NSInteger result) { 448 | NSString *destPath = [[sPanel URL] path]; 449 | NSError *error; 450 | BOOL res = [[NSFileManager defaultManager] copyItemAtPath:path toPath:destPath error:&error]; 451 | if (!res) { 452 | NSBeep(); 453 | NSLog(@"%@", [error localizedDescription]); 454 | } 455 | }]; 456 | } 457 | 458 | #pragma mark IKImageBrowserDataSource 459 | 460 | // Implement the image browser data source protocol . 461 | // The data source representation is a simple mutable array. 462 | 463 | - (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)view { 464 | // The item count to display is the datadsource item count. 465 | return [activeSet count]; 466 | } 467 | 468 | - (id)imageBrowser:(IKImageBrowserView *) view itemAtIndex:(NSUInteger) index { 469 | return activeSet[index]; 470 | } 471 | 472 | - (void)imageBrowser:(IKImageBrowserView*)view removeItemsAtIndexes:(NSIndexSet*)indexes { 473 | } 474 | 475 | - (BOOL)imageBrowser:(IKImageBrowserView*)view moveItemsAtIndexes:(NSIndexSet*)indexes toIndex:(NSUInteger)destinationIndex { 476 | return NO; 477 | } 478 | 479 | #pragma mark IKImageBrowserDelegate 480 | 481 | - (void)imageBrowserSelectionDidChange:(IKImageBrowserView *)aBrowser { 482 | [saveCopyButton setEnabled:[[self selectedFilePath] length]]; 483 | 484 | NSString *path = [self selectedFilePath]; 485 | [selectedIconPathLabel setStringValue:path]; 486 | [selectedIconFileSizeLabel setStringValue:[[NSWorkspace sharedWorkspace] fileOrFolderSizeAsHumanReadable:path]]; 487 | 488 | NSImage *img = [[NSImage alloc] initByReferencingFile:path]; 489 | [selectedIconImageView setImage:img]; 490 | [selectedIconBox setTitle:[path lastPathComponent]]; 491 | 492 | NSArray *reps = [img representations]; 493 | NSInteger highestRep = 0; 494 | for (NSInteger i = 0; i < [reps count]; i++) { 495 | NSInteger height = [(NSImageRep *)reps[i] pixelsHigh]; 496 | if (height > highestRep) { 497 | highestRep = height; 498 | } 499 | } 500 | 501 | NSString *iconSizeStr = [NSString stringWithFormat:@"%d x %d", (int)highestRep, (int)highestRep]; 502 | [selectedIconSizeLabel setStringValue:iconSizeStr]; 503 | [selectedIconRepsLabel setStringValue:[NSString stringWithFormat:@"%d", (int)[reps count]]]; 504 | } 505 | 506 | - (void)imageBrowser:(IKImageBrowserView *)aBrowser cellWasDoubleClickedAtIndex:(NSUInteger)index { 507 | NSString *path = [activeSet[index] imageRepresentation]; 508 | BOOL cmdKeyDown = (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask); 509 | 510 | if (cmdKeyDown) { 511 | [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:path]; 512 | } else { 513 | [[NSWorkspace sharedWorkspace] openFile:path]; 514 | } 515 | } 516 | 517 | - (void)imageBrowser:(IKImageBrowserView *)aBrowser cellWasRightClickedAtIndex:(NSUInteger)index withEvent:(NSEvent *)event { 518 | NSUInteger i = [aBrowser indexOfItemAtPoint:[aBrowser convertPoint:[event locationInWindow] fromView:nil]]; 519 | if (i == NSNotFound) { 520 | return; 521 | } 522 | 523 | NSString *path = [self selectedFilePath]; 524 | if ([[NSFileManager defaultManager] fileExistsAtPath:path] == NO) { 525 | return; 526 | } 527 | 528 | NSMenu *menu = [[NSMenu alloc] initWithTitle:@"menu"]; 529 | [menu setAutoenablesItems:NO]; 530 | 531 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"Open" action:@selector(openFile:) keyEquivalent:@""]; 532 | [item setTarget:self]; 533 | [menu addItem:item]; 534 | 535 | item = [[NSMenuItem alloc] initWithTitle:@"Open With" action:nil keyEquivalent:@""]; 536 | [item setSubmenu:[self getOpenWithSubmenuForFile:path]]; 537 | [menu addItem:item]; 538 | 539 | [menu addItem:[NSMenuItem separatorItem]]; 540 | 541 | item = [[NSMenuItem alloc] initWithTitle:@"Get Info" action:@selector(getInfo:) keyEquivalent:@""]; 542 | [item setTarget:self]; 543 | [menu addItem:item]; 544 | 545 | item = [[NSMenuItem alloc] initWithTitle:@"Show in Finder" action:@selector(showInFinder:) keyEquivalent:@""]; 546 | [item setTarget:self]; 547 | [menu addItem:item]; 548 | 549 | [NSMenu popUpContextMenu:menu withEvent:event forView:aBrowser]; 550 | } 551 | 552 | #pragma mark Contextual menu 553 | 554 | - (void)openFile:(id)sender { 555 | NSString *path = [self selectedFilePath]; 556 | [[NSWorkspace sharedWorkspace] openFile:path]; 557 | } 558 | 559 | - (void)showInFinder:(id)sender { 560 | NSString *path = [self selectedFilePath]; 561 | [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:path]; 562 | } 563 | 564 | - (void)getInfo:(id)sender { 565 | NSString *path = [self selectedFilePath]; 566 | [[NSWorkspace sharedWorkspace] getInfoInFinderForFile:path]; 567 | } 568 | 569 | - (void)openWithSelected:(id)sender { 570 | NSString *appName = [[[sender toolTip] lastPathComponent] stringByDeletingPathExtension]; 571 | [[NSWorkspace sharedWorkspace] openFile:[self selectedFilePath] withApplication:appName]; 572 | NSLog(@"Opening %@ with %@", [self selectedFilePath], appName); 573 | } 574 | 575 | - (NSMenu *)getOpenWithSubmenuForFile:(NSString *)path { 576 | // lazy load 577 | static NSMenu *menu = nil; 578 | if (menu) { 579 | return menu; 580 | } 581 | menu = [[NSWorkspace sharedWorkspace] openWithMenuForFile:path target:self action:@selector(openWithSelected:)]; 582 | return menu; 583 | } 584 | 585 | #pragma mark Drag and Drop 586 | 587 | - (NSDragOperation)draggingEntered:(id )sender { 588 | return NSDragOperationNone; 589 | } 590 | 591 | - (NSDragOperation)draggingUpdated:(id )sender { 592 | return NSDragOperationCopy; 593 | } 594 | 595 | - (BOOL)performDragOperation:(id )sender { 596 | return NO; 597 | } 598 | 599 | @end 600 | -------------------------------------------------------------------------------- /IconScanner_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'IconScanner' target in the 'IconScanner' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2010 Sveinbjorn Thordarson. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 628 | 629 | 630 | 631 | 632 | 633 | -------------------------------------------------------------------------------- /NSWorkspace+Additions.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | #import 33 | 34 | @interface NSWorkspace (Additions) 35 | 36 | - (unsigned long long)nrCalculateFolderSize:(NSString *)folderPath; 37 | - (UInt64)fileOrFolderSize:(NSString *)path; 38 | - (NSString *)fileSizeAsHumanReadableString:(UInt64)size; 39 | - (NSString *)fileOrFolderSizeAsHumanReadable:(NSString *)path; 40 | 41 | - (NSMenu *)openWithMenuForFile:(NSString *)path target:(id)target action:(SEL)selector; 42 | - (BOOL)getInfoInFinderForFile:(NSString *)path; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /NSWorkspace+Additions.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "NSWorkspace+Additions.h" 32 | #import 33 | #import 34 | #import 35 | 36 | @implementation NSWorkspace (Additions) 37 | 38 | #pragma mark - File/folder size 39 | 40 | // Copyright (c) 2015 Nikolai Ruhe. All rights reserved. 41 | // 42 | // This method calculates the accumulated size of a directory on the volume in bytes. 43 | // 44 | // As there's no simple way to get this information from the file system it has to crawl the entire hierarchy, 45 | // accumulating the overall sum on the way. The resulting value is roughly equivalent with the amount of bytes 46 | // that would become available on the volume if the directory would be deleted. 47 | // 48 | // Caveat: There are a couple of oddities that are not taken into account (like symbolic links, meta data of 49 | // directories, hard links, ...). 50 | 51 | - (BOOL)getAllocatedSize:(unsigned long long *)size ofDirectoryAtURL:(NSURL *)directoryURL error:(NSError * __autoreleasing *)error 52 | { 53 | NSParameterAssert(size != NULL); 54 | NSParameterAssert(directoryURL != nil); 55 | 56 | // We'll sum up content size here: 57 | unsigned long long accumulatedSize = 0; 58 | 59 | // prefetching some properties during traversal will speed up things a bit. 60 | NSArray *prefetchedProperties = @[NSURLIsRegularFileKey, 61 | NSURLFileAllocatedSizeKey, 62 | NSURLTotalFileAllocatedSizeKey]; 63 | 64 | // The error handler simply signals errors to outside code. 65 | __block BOOL errorDidOccur = NO; 66 | BOOL (^errorHandler)(NSURL *, NSError *) = ^(NSURL *url, NSError *localError) { 67 | if (error != NULL) { 68 | *error = localError; 69 | } 70 | errorDidOccur = YES; 71 | return NO; 72 | }; 73 | 74 | // We have to enumerate all directory contents, including subdirectories. 75 | NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:directoryURL 76 | includingPropertiesForKeys:prefetchedProperties 77 | options:(NSDirectoryEnumerationOptions)0 78 | errorHandler:errorHandler]; 79 | 80 | // Start the traversal: 81 | for (NSURL *contentItemURL in enumerator) { 82 | 83 | // Bail out on errors from the errorHandler. 84 | if (errorDidOccur) 85 | return NO; 86 | 87 | // Get the type of this item, making sure we only sum up sizes of regular files. 88 | NSNumber *isRegularFile; 89 | if (! [contentItemURL getResourceValue:&isRegularFile forKey:NSURLIsRegularFileKey error:error]) 90 | return NO; 91 | if (! [isRegularFile boolValue]) 92 | continue; // Ignore anything except regular files. 93 | 94 | // To get the file's size we first try the most comprehensive value in terms of what the file may use on disk. 95 | // This includes metadata, compression (on file system level) and block size. 96 | NSNumber *fileSize; 97 | if (! [contentItemURL getResourceValue:&fileSize forKey:NSURLTotalFileAllocatedSizeKey error:error]) 98 | return NO; 99 | 100 | // In case the value is unavailable we use the fallback value (excluding meta data and compression) 101 | // This value should always be available. 102 | if (fileSize == nil) { 103 | if (! [contentItemURL getResourceValue:&fileSize forKey:NSURLFileAllocatedSizeKey error:error]) 104 | return NO; 105 | 106 | NSAssert(fileSize != nil, @"huh? NSURLFileAllocatedSizeKey should always return a value"); 107 | } 108 | 109 | // We're good, add up the value. 110 | accumulatedSize += [fileSize unsignedLongLongValue]; 111 | } 112 | 113 | // Bail out on errors from the errorHandler. 114 | if (errorDidOccur) 115 | return NO; 116 | 117 | // We finally got it. 118 | *size = accumulatedSize; 119 | return YES; 120 | } 121 | 122 | - (unsigned long long)nrCalculateFolderSize:(NSString *)folderPath { 123 | unsigned long long size = 0; 124 | NSURL *url = [NSURL fileURLWithPath:folderPath]; 125 | [self getAllocatedSize:&size ofDirectoryAtURL:url error:nil]; 126 | return size; 127 | } 128 | 129 | - (UInt64)fileOrFolderSize:(NSString *)path { 130 | NSString *fileOrFolderPath = [path copy]; 131 | 132 | BOOL isDir; 133 | if (path == nil || ![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) { 134 | return 0; 135 | } 136 | 137 | // resolve if symlink 138 | NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:fileOrFolderPath error:nil]; 139 | if (fileAttrs) { 140 | NSString *fileType = [fileAttrs fileType]; 141 | if ([fileType isEqualToString:NSFileTypeSymbolicLink]) { 142 | NSError *err; 143 | fileOrFolderPath = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:fileOrFolderPath error:&err]; 144 | if (fileOrFolderPath == nil) { 145 | NSLog(@"Error resolving symlink %@: %@", path, [err localizedDescription]); 146 | fileOrFolderPath = path; 147 | } 148 | } 149 | } 150 | 151 | UInt64 size = 0; 152 | if (isDir) { 153 | NSDirectoryEnumerator *dirEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:fileOrFolderPath]; 154 | while ([dirEnumerator nextObject]) { 155 | if ([NSFileTypeRegular isEqualToString:[[dirEnumerator fileAttributes] fileType]]) { 156 | size += [[dirEnumerator fileAttributes] fileSize]; 157 | } 158 | } 159 | size = [[NSWorkspace sharedWorkspace] nrCalculateFolderSize:fileOrFolderPath]; 160 | } else { 161 | size = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileOrFolderPath error:nil] fileSize]; 162 | } 163 | 164 | return size; 165 | } 166 | 167 | - (NSString *)fileOrFolderSizeAsHumanReadable:(NSString *)path { 168 | return [self fileSizeAsHumanReadableString:[self fileOrFolderSize:path]]; 169 | } 170 | 171 | - (NSString *)fileSizeAsHumanReadableString:(UInt64)size { 172 | NSString *str; 173 | 174 | if (size < 1024ULL) { 175 | str = [NSString stringWithFormat:@"%u bytes", (unsigned int)size]; 176 | } else if (size < 1048576ULL) { 177 | str = [NSString stringWithFormat:@"%llu KB", (UInt64)size / 1024]; 178 | } else if (size < 1073741824ULL) { 179 | str = [NSString stringWithFormat:@"%.1f MB", size / 1048576.0]; 180 | } else { 181 | str = [NSString stringWithFormat:@"%.1f GB", size / 1073741824.0]; 182 | } 183 | return str; 184 | } 185 | 186 | #pragma mark - Default apps for files 187 | 188 | - (NSString *)defaultApplicationForFile:(NSString *)filePath { 189 | CFURLRef appURL; 190 | 191 | OSStatus ret = LSGetApplicationForURL((__bridge CFURLRef)[NSURL fileURLWithPath:filePath], kLSRolesAll, NULL, &appURL); 192 | if (ret != noErr || appURL == nil) { 193 | return nil; 194 | } 195 | 196 | return [(__bridge NSURL *)appURL path]; 197 | } 198 | 199 | - (NSMenu *)openWithMenuForFile:(NSString *)path target:(id)target action:(SEL)selector { 200 | // create open with submenu 201 | NSMenu *submenu = [[NSMenu alloc] initWithTitle: @"Open With"]; 202 | 203 | //get default app for file 204 | NSString *defaultApp = [self defaultApplicationForFile:path]; 205 | NSString *defaultAppName = [NSString stringWithFormat:@"%@", [[NSFileManager defaultManager] displayNameAtPath:defaultApp]]; 206 | 207 | NSMenuItem *defaultAppItem = [submenu addItemWithTitle:defaultAppName action:selector keyEquivalent:@""]; 208 | NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:defaultApp]; 209 | [icon setSize:NSMakeSize(16,16)]; 210 | [defaultAppItem setImage:icon]; 211 | [defaultAppItem setTarget:target]; 212 | [defaultAppItem setToolTip:defaultApp]; 213 | 214 | [submenu addItem:[NSMenuItem separatorItem]]; 215 | 216 | //get all apps that open this file 217 | NSArray *apps = [self applicationsForFile:path]; 218 | if (apps == nil) { 219 | [submenu addItemWithTitle: @"None" action:nil keyEquivalent: @""]; 220 | } 221 | 222 | apps = [apps sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; 223 | 224 | for (int i = 0; i < [apps count]; i++) { 225 | if ([[apps objectAtIndex:i] isEqualToString:defaultApp]) { 226 | continue; 227 | } 228 | 229 | NSString *title = [[NSFileManager defaultManager] displayNameAtPath:[apps objectAtIndex:i]]; 230 | 231 | NSMenuItem *item = [submenu addItemWithTitle:title action:selector keyEquivalent:@""]; 232 | [item setTarget:target]; 233 | [item setToolTip:[apps objectAtIndex:i]]; 234 | 235 | NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[apps objectAtIndex:i]]; 236 | if (icon != nil) { 237 | [icon setSize:NSMakeSize(16,16)]; 238 | [item setImage:icon]; 239 | } 240 | } 241 | 242 | return submenu; 243 | } 244 | 245 | - (NSArray *)applicationsForFile:(NSString *)filePath { 246 | NSURL *url = [NSURL fileURLWithPath:filePath]; 247 | NSMutableArray *appPaths = [NSMutableArray array]; 248 | 249 | NSArray *applications = (__bridge NSArray *)LSCopyApplicationURLsForURL((__bridge CFURLRef)url, kLSRolesAll); 250 | if (applications == nil) { 251 | return nil; 252 | } 253 | 254 | for (int i = 0; i < [applications count]; i++) { 255 | [appPaths addObject: [[applications objectAtIndex:i] path]]; 256 | } 257 | return appPaths; 258 | } 259 | 260 | - (BOOL)getInfoInFinderForFile:(NSString *)path { 261 | BOOL isDir; 262 | if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] == NO) { 263 | NSBeep(); 264 | return NO; 265 | } 266 | 267 | NSString *type = (isDir && ![[NSWorkspace sharedWorkspace] isFilePackageAtPath:path]) ? @"folder" : @"file"; 268 | NSString *osaScript = [NSString stringWithFormat: 269 | @"tell application \"Finder\"\n\ 270 | \tactivate\n\ 271 | \topen the information window of %@ POSIX file \"%@\"\n\ 272 | end tell", type, path, nil]; 273 | 274 | NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:osaScript]; 275 | if (appleScript != nil) { 276 | [appleScript executeAndReturnError:nil]; 277 | return YES; 278 | } else { 279 | NSLog(@"Error running AppleScript"); 280 | return NO; 281 | } 282 | } 283 | 284 | @end 285 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IconScanner 2 | 3 | IconScanner is a Mac application to find and view all Apple icon files (`.icns`) on your system in an image browser view. 4 | 5 | I threw it together quickly at some point back in the day to inspect icon assets used by Mac OS X and native programs. 6 | 7 | ## Download 8 | 9 | * [Download IconScanner 1.2](http://sveinbjorn.org/files/software/IconScanner.zip) (~250 KB, 10.8, Intel 64-bit only) 10 | 11 | ## Screenshot 12 | 13 | 14 | 15 | ## Version history 16 | 17 | ### 21/06/2018 18 | 19 | * Added searchfs search tool option 20 | 21 | ### 21/02/2018 - Version 1.1 22 | 23 | * Modernised codebase and moved to ARC 24 | * Added contextual menu for items 25 | * mdfind now uses UTIs to locate icon files instead of suffix 26 | 27 | ### 09/07/2010 - Version 1.0 28 | 29 | ## BSD License 30 | 31 | Copyright (C) 2010-2018 Sveinbjorn Thordarson 32 | 33 | Redistribution and use in source and binary forms, with or without modification, 34 | are permitted provided that the following conditions are met: 35 | 36 | 1. Redistributions of source code must retain the above copyright notice, this 37 | list of conditions and the following disclaimer. 38 | 39 | 2. Redistributions in binary form must reproduce the above copyright notice, this 40 | list of conditions and the following disclaimer in the documentation and/or other 41 | materials provided with the distribution. 42 | 43 | 3. Neither the name of the copyright holder nor the names of its contributors may 44 | be used to endorse or promote products derived from this software without specific 45 | prior written permission. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 48 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 51 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 52 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 53 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 54 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 55 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 56 | POSSIBILITY OF SUCH DAMAGE. 57 | 58 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | xcodebuild -parallelizeTargets \ 4 | -project "IconScanner.xcodeproj" \ 5 | -target "IconScanner" \ 6 | -configuration "Release" \ 7 | clean \ 8 | build 9 | 10 | open build/Release 11 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // IconScanner 4 | // 5 | // Created by Sveinbjorn Thordarson on 9/7/10. 6 | // Copyright 2010 Sveinbjorn Thordarson. All rights reserved. 7 | // Distributed under a 3-clause BSD License 8 | // 9 | 10 | #import 11 | 12 | int main(int argc, char *argv[]) { 13 | return NSApplicationMain(argc, (const char **) argv); 14 | } 15 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/IconScanner/ee4f821ff35a0dcda59b17e18e4f5636bf9ec01e/screenshot.jpg -------------------------------------------------------------------------------- /searchfs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/IconScanner/ee4f821ff35a0dcda59b17e18e4f5636bf9ec01e/searchfs --------------------------------------------------------------------------------