├── .gitignore ├── .idea ├── .name ├── ViFinder.iml ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── xcode.xml ├── README.md ├── ViFinder.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── ViFinder ├── AddFavouriteViewController.h ├── AddFavouriteViewController.m ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── Main.storyboard ├── FavouriteMenuItem.h ├── FavouriteMenuItem.m ├── FileItem.h ├── FileItem.m ├── FileTableView.h ├── FileTableView.m ├── FileViewController.h ├── FileViewController.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── Model.xcdatamodeld │ └── Model.xcdatamodel │ │ └── contents ├── OverwriteFileViewController.h ├── OverwriteFileViewController.m ├── VDKQueue.h ├── VDKQueue.m ├── iTermNewTab.applescript ├── iTermNewWindow.applescript └── main.m └── ViFinderTests ├── Info.plist └── ViFinderTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Objective-C template 3 | # Xcode 4 | # 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | 22 | # CocoaPods 23 | # 24 | # We recommend against adding the Pods directory to your .gitignore. However 25 | # you should judge for yourself, the pros and cons are mentioned at: 26 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 27 | # 28 | #Pods/ 29 | 30 | 31 | ### Xcode template 32 | build/ 33 | *.pbxuser 34 | !default.pbxuser 35 | *.mode1v3 36 | !default.mode1v3 37 | *.mode2v3 38 | !default.mode2v3 39 | *.perspectivev3 40 | !default.perspectivev3 41 | xcuserdata 42 | *.xccheckout 43 | *.moved-aside 44 | DerivedData 45 | *.xcuserstate 46 | 47 | 48 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | ViFinder -------------------------------------------------------------------------------- /.idea/ViFinder.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/xcode.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ViFinder 2 | Vim like Keybinding FileManager for OS X 3 | ![](http://ww4.sinaimg.cn/large/c0f281e2jw1ewuwaooayoj20vn0khakb.jpg) 4 | 5 | ##HotKeys 6 | * j: down 7 | * k: up 8 | * tab: switch left right panel 9 | * enter: open file/folder 10 | * space: toggle selection 11 | * backspace: back to parent path 12 | * gg: go to first row 13 | * G: go to last row 14 | * q: preview 15 | * e: open Terminal here 16 | * y: copy file path to clipboard 17 | * sn: sort by file name 18 | * ss: sort by file size 19 | * se: sort by file extension 20 | * sd: sort by file create date 21 | * d: open fav menu 22 | * ff: copy file to clipboard 23 | * fv: paste from clipboard 24 | * fc: copy file to other panel 25 | * fx: move file to other panel 26 | * x: move file to trash 27 | * /: search/filter file 28 | * [: select files by same file name 29 | * ]: select files by same file extension 30 | * |: clear selection 31 | * \: toggle selection 32 | -------------------------------------------------------------------------------- /ViFinder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2646C32BFE6BB49604D8AD58 /* iTermNewWindow.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 2646C970B764F8222149D26C /* iTermNewWindow.applescript */; }; 11 | 2646CCCDCD91083323507EB3 /* iTermNewTab.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 2646CC9150F96E45347D2157 /* iTermNewTab.applescript */; }; 12 | 834325491B0093DA00301A74 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 834325481B0093DA00301A74 /* AppDelegate.m */; }; 13 | 8343254B1B0093DA00301A74 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8343254A1B0093DA00301A74 /* main.m */; }; 14 | 8343254E1B0093DA00301A74 /* FileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8343254D1B0093DA00301A74 /* FileViewController.m */; }; 15 | 834325501B0093DA00301A74 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8343254F1B0093DA00301A74 /* Images.xcassets */; }; 16 | 834325531B0093DA00301A74 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 834325511B0093DA00301A74 /* Main.storyboard */; }; 17 | 8343255F1B0093DB00301A74 /* ViFinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8343255E1B0093DB00301A74 /* ViFinderTests.m */; }; 18 | 83558F531B149611008FAB6F /* OverwriteFileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83558F521B149611008FAB6F /* OverwriteFileViewController.m */; }; 19 | 8389A2E61B04727D006A78F0 /* FavouriteMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8389A2E51B04727D006A78F0 /* FavouriteMenuItem.m */; }; 20 | 838E45041B043FC7001069EB /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 838E45021B043FC7001069EB /* Model.xcdatamodeld */; }; 21 | 83A72DE91B0B76BF0069BC1A /* VDKQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A72DE81B0B76BF0069BC1A /* VDKQueue.m */; }; 22 | 83FB16481B0575F800DAD460 /* AddFavouriteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FB16471B0575F800DAD460 /* AddFavouriteViewController.m */; }; 23 | 83FB478F1B017AEC0060AFBE /* FileTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FB478E1B017AEC0060AFBE /* FileTableView.m */; }; 24 | 83FB47961B01BEF00060AFBE /* FileItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FB47951B01BEF00060AFBE /* FileItem.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 834325591B0093DB00301A74 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 8343253A1B0093DA00301A74 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 834325411B0093DA00301A74; 33 | remoteInfo = ViFinder; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 2646C970B764F8222149D26C /* iTermNewWindow.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = iTermNewWindow.applescript; sourceTree = ""; }; 39 | 2646CC9150F96E45347D2157 /* iTermNewTab.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = iTermNewTab.applescript; sourceTree = ""; }; 40 | 834325421B0093DA00301A74 /* ViFinder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ViFinder.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 834325461B0093DA00301A74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 834325471B0093DA00301A74 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | 834325481B0093DA00301A74 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | 8343254A1B0093DA00301A74 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | 8343254C1B0093DA00301A74 /* FileViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileViewController.h; sourceTree = ""; }; 46 | 8343254D1B0093DA00301A74 /* FileViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileViewController.m; sourceTree = ""; }; 47 | 8343254F1B0093DA00301A74 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 48 | 834325521B0093DA00301A74 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 834325581B0093DB00301A74 /* ViFinderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ViFinderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 8343255D1B0093DB00301A74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 8343255E1B0093DB00301A74 /* ViFinderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViFinderTests.m; sourceTree = ""; }; 52 | 83558F511B149611008FAB6F /* OverwriteFileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OverwriteFileViewController.h; sourceTree = ""; }; 53 | 83558F521B149611008FAB6F /* OverwriteFileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OverwriteFileViewController.m; sourceTree = ""; }; 54 | 8389A2E41B04727D006A78F0 /* FavouriteMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FavouriteMenuItem.h; sourceTree = ""; }; 55 | 8389A2E51B04727D006A78F0 /* FavouriteMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FavouriteMenuItem.m; sourceTree = ""; }; 56 | 838E45031B043FC7001069EB /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; 57 | 83A72DE71B0B76BE0069BC1A /* VDKQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VDKQueue.h; sourceTree = ""; }; 58 | 83A72DE81B0B76BF0069BC1A /* VDKQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VDKQueue.m; sourceTree = ""; }; 59 | 83FB16461B0575F800DAD460 /* AddFavouriteViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddFavouriteViewController.h; sourceTree = ""; }; 60 | 83FB16471B0575F800DAD460 /* AddFavouriteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddFavouriteViewController.m; sourceTree = ""; }; 61 | 83FB478D1B017AEC0060AFBE /* FileTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileTableView.h; sourceTree = ""; }; 62 | 83FB478E1B017AEC0060AFBE /* FileTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileTableView.m; sourceTree = ""; }; 63 | 83FB47941B01BEF00060AFBE /* FileItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileItem.h; sourceTree = ""; }; 64 | 83FB47951B01BEF00060AFBE /* FileItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileItem.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 8343253F1B0093DA00301A74 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 834325551B0093DB00301A74 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 834325391B0093DA00301A74 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 834325441B0093DA00301A74 /* ViFinder */, 89 | 8343255B1B0093DB00301A74 /* ViFinderTests */, 90 | 834325431B0093DA00301A74 /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 834325431B0093DA00301A74 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 834325421B0093DA00301A74 /* ViFinder.app */, 98 | 834325581B0093DB00301A74 /* ViFinderTests.xctest */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 834325441B0093DA00301A74 /* ViFinder */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 83A72DE71B0B76BE0069BC1A /* VDKQueue.h */, 107 | 83A72DE81B0B76BF0069BC1A /* VDKQueue.m */, 108 | 834325471B0093DA00301A74 /* AppDelegate.h */, 109 | 834325481B0093DA00301A74 /* AppDelegate.m */, 110 | 8343254C1B0093DA00301A74 /* FileViewController.h */, 111 | 8343254D1B0093DA00301A74 /* FileViewController.m */, 112 | 8343254F1B0093DA00301A74 /* Images.xcassets */, 113 | 834325511B0093DA00301A74 /* Main.storyboard */, 114 | 834325451B0093DA00301A74 /* Supporting Files */, 115 | 83FB478D1B017AEC0060AFBE /* FileTableView.h */, 116 | 83FB478E1B017AEC0060AFBE /* FileTableView.m */, 117 | 8389A2E71B04798F006A78F0 /* Model */, 118 | 83FB16461B0575F800DAD460 /* AddFavouriteViewController.h */, 119 | 83FB16471B0575F800DAD460 /* AddFavouriteViewController.m */, 120 | 83558F511B149611008FAB6F /* OverwriteFileViewController.h */, 121 | 83558F521B149611008FAB6F /* OverwriteFileViewController.m */, 122 | ); 123 | path = ViFinder; 124 | sourceTree = ""; 125 | }; 126 | 834325451B0093DA00301A74 /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 834325461B0093DA00301A74 /* Info.plist */, 130 | 8343254A1B0093DA00301A74 /* main.m */, 131 | 2646C970B764F8222149D26C /* iTermNewWindow.applescript */, 132 | 2646CC9150F96E45347D2157 /* iTermNewTab.applescript */, 133 | ); 134 | name = "Supporting Files"; 135 | sourceTree = ""; 136 | }; 137 | 8343255B1B0093DB00301A74 /* ViFinderTests */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 8343255E1B0093DB00301A74 /* ViFinderTests.m */, 141 | 8343255C1B0093DB00301A74 /* Supporting Files */, 142 | ); 143 | path = ViFinderTests; 144 | sourceTree = ""; 145 | }; 146 | 8343255C1B0093DB00301A74 /* Supporting Files */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 8343255D1B0093DB00301A74 /* Info.plist */, 150 | ); 151 | name = "Supporting Files"; 152 | sourceTree = ""; 153 | }; 154 | 8389A2E71B04798F006A78F0 /* Model */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 838E45021B043FC7001069EB /* Model.xcdatamodeld */, 158 | 8389A2E41B04727D006A78F0 /* FavouriteMenuItem.h */, 159 | 8389A2E51B04727D006A78F0 /* FavouriteMenuItem.m */, 160 | 83FB47941B01BEF00060AFBE /* FileItem.h */, 161 | 83FB47951B01BEF00060AFBE /* FileItem.m */, 162 | ); 163 | name = Model; 164 | sourceTree = ""; 165 | }; 166 | /* End PBXGroup section */ 167 | 168 | /* Begin PBXNativeTarget section */ 169 | 834325411B0093DA00301A74 /* ViFinder */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = 834325621B0093DB00301A74 /* Build configuration list for PBXNativeTarget "ViFinder" */; 172 | buildPhases = ( 173 | 8343253E1B0093DA00301A74 /* Sources */, 174 | 8343253F1B0093DA00301A74 /* Frameworks */, 175 | 834325401B0093DA00301A74 /* Resources */, 176 | ); 177 | buildRules = ( 178 | ); 179 | dependencies = ( 180 | ); 181 | name = ViFinder; 182 | productName = ViFinder; 183 | productReference = 834325421B0093DA00301A74 /* ViFinder.app */; 184 | productType = "com.apple.product-type.application"; 185 | }; 186 | 834325571B0093DB00301A74 /* ViFinderTests */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = 834325651B0093DB00301A74 /* Build configuration list for PBXNativeTarget "ViFinderTests" */; 189 | buildPhases = ( 190 | 834325541B0093DB00301A74 /* Sources */, 191 | 834325551B0093DB00301A74 /* Frameworks */, 192 | 834325561B0093DB00301A74 /* Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | 8343255A1B0093DB00301A74 /* PBXTargetDependency */, 198 | ); 199 | name = ViFinderTests; 200 | productName = ViFinderTests; 201 | productReference = 834325581B0093DB00301A74 /* ViFinderTests.xctest */; 202 | productType = "com.apple.product-type.bundle.unit-test"; 203 | }; 204 | /* End PBXNativeTarget section */ 205 | 206 | /* Begin PBXProject section */ 207 | 8343253A1B0093DA00301A74 /* Project object */ = { 208 | isa = PBXProject; 209 | attributes = { 210 | LastUpgradeCheck = 0630; 211 | ORGANIZATIONNAME = likaci; 212 | TargetAttributes = { 213 | 834325411B0093DA00301A74 = { 214 | CreatedOnToolsVersion = 6.3; 215 | }; 216 | 834325571B0093DB00301A74 = { 217 | CreatedOnToolsVersion = 6.3; 218 | TestTargetID = 834325411B0093DA00301A74; 219 | }; 220 | }; 221 | }; 222 | buildConfigurationList = 8343253D1B0093DA00301A74 /* Build configuration list for PBXProject "ViFinder" */; 223 | compatibilityVersion = "Xcode 3.2"; 224 | developmentRegion = English; 225 | hasScannedForEncodings = 0; 226 | knownRegions = ( 227 | en, 228 | Base, 229 | ); 230 | mainGroup = 834325391B0093DA00301A74; 231 | productRefGroup = 834325431B0093DA00301A74 /* Products */; 232 | projectDirPath = ""; 233 | projectRoot = ""; 234 | targets = ( 235 | 834325411B0093DA00301A74 /* ViFinder */, 236 | 834325571B0093DB00301A74 /* ViFinderTests */, 237 | ); 238 | }; 239 | /* End PBXProject section */ 240 | 241 | /* Begin PBXResourcesBuildPhase section */ 242 | 834325401B0093DA00301A74 /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | 834325501B0093DA00301A74 /* Images.xcassets in Resources */, 247 | 834325531B0093DA00301A74 /* Main.storyboard in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 834325561B0093DB00301A74 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXResourcesBuildPhase section */ 259 | 260 | /* Begin PBXSourcesBuildPhase section */ 261 | 8343253E1B0093DA00301A74 /* Sources */ = { 262 | isa = PBXSourcesBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | 83FB47961B01BEF00060AFBE /* FileItem.m in Sources */, 266 | 8343254E1B0093DA00301A74 /* FileViewController.m in Sources */, 267 | 83558F531B149611008FAB6F /* OverwriteFileViewController.m in Sources */, 268 | 83FB478F1B017AEC0060AFBE /* FileTableView.m in Sources */, 269 | 838E45041B043FC7001069EB /* Model.xcdatamodeld in Sources */, 270 | 83A72DE91B0B76BF0069BC1A /* VDKQueue.m in Sources */, 271 | 83FB16481B0575F800DAD460 /* AddFavouriteViewController.m in Sources */, 272 | 8343254B1B0093DA00301A74 /* main.m in Sources */, 273 | 834325491B0093DA00301A74 /* AppDelegate.m in Sources */, 274 | 8389A2E61B04727D006A78F0 /* FavouriteMenuItem.m in Sources */, 275 | 2646C32BFE6BB49604D8AD58 /* iTermNewWindow.applescript in Sources */, 276 | 2646CCCDCD91083323507EB3 /* iTermNewTab.applescript in Sources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 834325541B0093DB00301A74 /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 8343255F1B0093DB00301A74 /* ViFinderTests.m in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin PBXTargetDependency section */ 291 | 8343255A1B0093DB00301A74 /* PBXTargetDependency */ = { 292 | isa = PBXTargetDependency; 293 | target = 834325411B0093DA00301A74 /* ViFinder */; 294 | targetProxy = 834325591B0093DB00301A74 /* PBXContainerItemProxy */; 295 | }; 296 | /* End PBXTargetDependency section */ 297 | 298 | /* Begin PBXVariantGroup section */ 299 | 834325511B0093DA00301A74 /* Main.storyboard */ = { 300 | isa = PBXVariantGroup; 301 | children = ( 302 | 834325521B0093DA00301A74 /* Base */, 303 | ); 304 | name = Main.storyboard; 305 | sourceTree = ""; 306 | }; 307 | /* End PBXVariantGroup section */ 308 | 309 | /* Begin XCBuildConfiguration section */ 310 | 834325601B0093DB00301A74 /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_UNREACHABLE_CODE = YES; 326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 327 | CODE_SIGN_IDENTITY = "-"; 328 | COPY_PHASE_STRIP = NO; 329 | DEBUG_INFORMATION_FORMAT = dwarf; 330 | ENABLE_STRICT_OBJC_MSGSEND = YES; 331 | GCC_C_LANGUAGE_STANDARD = gnu99; 332 | GCC_DYNAMIC_NO_PIC = NO; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_OPTIMIZATION_LEVEL = 0; 335 | GCC_PREPROCESSOR_DEFINITIONS = ( 336 | "DEBUG=1", 337 | "$(inherited)", 338 | ); 339 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | MACOSX_DEPLOYMENT_TARGET = 10.10; 347 | MTL_ENABLE_DEBUG_INFO = YES; 348 | ONLY_ACTIVE_ARCH = YES; 349 | SDKROOT = macosx; 350 | }; 351 | name = Debug; 352 | }; 353 | 834325611B0093DB00301A74 /* Release */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | ALWAYS_SEARCH_USER_PATHS = NO; 357 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 358 | CLANG_CXX_LIBRARY = "libc++"; 359 | CLANG_ENABLE_MODULES = YES; 360 | CLANG_ENABLE_OBJC_ARC = YES; 361 | CLANG_WARN_BOOL_CONVERSION = YES; 362 | CLANG_WARN_CONSTANT_CONVERSION = YES; 363 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 364 | CLANG_WARN_EMPTY_BODY = YES; 365 | CLANG_WARN_ENUM_CONVERSION = YES; 366 | CLANG_WARN_INT_CONVERSION = YES; 367 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 368 | CLANG_WARN_UNREACHABLE_CODE = YES; 369 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 370 | CODE_SIGN_IDENTITY = "-"; 371 | COPY_PHASE_STRIP = NO; 372 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 373 | ENABLE_NS_ASSERTIONS = NO; 374 | ENABLE_STRICT_OBJC_MSGSEND = YES; 375 | GCC_C_LANGUAGE_STANDARD = gnu99; 376 | GCC_NO_COMMON_BLOCKS = YES; 377 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 378 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 379 | GCC_WARN_UNDECLARED_SELECTOR = YES; 380 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 381 | GCC_WARN_UNUSED_FUNCTION = YES; 382 | GCC_WARN_UNUSED_VARIABLE = YES; 383 | MACOSX_DEPLOYMENT_TARGET = 10.10; 384 | MTL_ENABLE_DEBUG_INFO = NO; 385 | SDKROOT = macosx; 386 | }; 387 | name = Release; 388 | }; 389 | 834325631B0093DB00301A74 /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 393 | COMBINE_HIDPI_IMAGES = YES; 394 | INFOPLIST_FILE = ViFinder/Info.plist; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 396 | PRODUCT_NAME = "$(TARGET_NAME)"; 397 | }; 398 | name = Debug; 399 | }; 400 | 834325641B0093DB00301A74 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 404 | COMBINE_HIDPI_IMAGES = YES; 405 | INFOPLIST_FILE = ViFinder/Info.plist; 406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | }; 409 | name = Release; 410 | }; 411 | 834325661B0093DB00301A74 /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | BUNDLE_LOADER = "$(TEST_HOST)"; 415 | COMBINE_HIDPI_IMAGES = YES; 416 | FRAMEWORK_SEARCH_PATHS = ( 417 | "$(DEVELOPER_FRAMEWORKS_DIR)", 418 | "$(inherited)", 419 | ); 420 | GCC_PREPROCESSOR_DEFINITIONS = ( 421 | "DEBUG=1", 422 | "$(inherited)", 423 | ); 424 | INFOPLIST_FILE = ViFinderTests/Info.plist; 425 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ViFinder.app/Contents/MacOS/ViFinder"; 428 | }; 429 | name = Debug; 430 | }; 431 | 834325671B0093DB00301A74 /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | BUNDLE_LOADER = "$(TEST_HOST)"; 435 | COMBINE_HIDPI_IMAGES = YES; 436 | FRAMEWORK_SEARCH_PATHS = ( 437 | "$(DEVELOPER_FRAMEWORKS_DIR)", 438 | "$(inherited)", 439 | ); 440 | INFOPLIST_FILE = ViFinderTests/Info.plist; 441 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ViFinder.app/Contents/MacOS/ViFinder"; 444 | }; 445 | name = Release; 446 | }; 447 | /* End XCBuildConfiguration section */ 448 | 449 | /* Begin XCConfigurationList section */ 450 | 8343253D1B0093DA00301A74 /* Build configuration list for PBXProject "ViFinder" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | 834325601B0093DB00301A74 /* Debug */, 454 | 834325611B0093DB00301A74 /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | 834325621B0093DB00301A74 /* Build configuration list for PBXNativeTarget "ViFinder" */ = { 460 | isa = XCConfigurationList; 461 | buildConfigurations = ( 462 | 834325631B0093DB00301A74 /* Debug */, 463 | 834325641B0093DB00301A74 /* Release */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | 834325651B0093DB00301A74 /* Build configuration list for PBXNativeTarget "ViFinderTests" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | 834325661B0093DB00301A74 /* Debug */, 472 | 834325671B0093DB00301A74 /* Release */, 473 | ); 474 | defaultConfigurationIsVisible = 0; 475 | defaultConfigurationName = Release; 476 | }; 477 | /* End XCConfigurationList section */ 478 | 479 | /* Begin XCVersionGroup section */ 480 | 838E45021B043FC7001069EB /* Model.xcdatamodeld */ = { 481 | isa = XCVersionGroup; 482 | children = ( 483 | 838E45031B043FC7001069EB /* Model.xcdatamodel */, 484 | ); 485 | currentVersion = 838E45031B043FC7001069EB /* Model.xcdatamodel */; 486 | path = Model.xcdatamodeld; 487 | sourceTree = ""; 488 | versionGroupType = wrapper.xcdatamodel; 489 | }; 490 | /* End XCVersionGroup section */ 491 | }; 492 | rootObject = 8343253A1B0093DA00301A74 /* Project object */; 493 | } 494 | -------------------------------------------------------------------------------- /ViFinder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ViFinder/AddFavouriteViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddFavouriteViewController.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/15. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddFavouriteViewController : NSViewController 12 | @property NSString *path; 13 | @property NSString *name; 14 | @property NSString *shortcut; 15 | @property (nonatomic, copy) void (^addFav)(NSString *path,NSString *name,NSString *shortcut); 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ViFinder/AddFavouriteViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddFavouriteViewController.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/15. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "AddFavouriteViewController.h" 10 | 11 | @interface AddFavouriteViewController () 12 | 13 | @end 14 | 15 | @implementation AddFavouriteViewController { 16 | @private 17 | NSString *_path; 18 | NSString *_name; 19 | NSString *_shortcut; 20 | } 21 | 22 | 23 | @synthesize name = _name; 24 | @synthesize shortcut = _shortcut; 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | // Do view setup here. 29 | } 30 | 31 | - (NSString *)path { 32 | return _path; 33 | } 34 | 35 | - (void)setPath:(NSString *)path { 36 | _path = path; 37 | _name = path.lastPathComponent; 38 | } 39 | 40 | - (IBAction)add:(id)sender { 41 | self.addFav(_path,_name,_shortcut); 42 | [self dismissViewController:self]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ViFinder/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @property(nonatomic) NSManagedObjectContext * coreDataContext; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ViFinder/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | - (NSManagedObjectContext *)coreDataContext { 26 | if (_coreDataContext == nil) { 27 | NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; 28 | NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; 29 | NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 30 | NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.xml"]]; 31 | NSError *error = nil; 32 | NSPersistentStore *store = [psc addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]; 33 | if (store == nil) { 34 | [NSException raise:@"添加数据库错误" format:@"%@", [error localizedDescription]]; 35 | } 36 | _coreDataContext = [[NSManagedObjectContext alloc] init]; 37 | _coreDataContext.persistentStoreCoordinator = psc; 38 | } 39 | return _coreDataContext; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ViFinder/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 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 | Default 510 | 511 | 512 | 513 | 514 | 515 | 516 | Left to Right 517 | 518 | 519 | 520 | 521 | 522 | 523 | Right to Left 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | Default 535 | 536 | 537 | 538 | 539 | 540 | 541 | Left to Right 542 | 543 | 544 | 545 | 546 | 547 | 548 | Right to Left 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 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 771 | 775 | 776 | 777 | 778 | 779 | 780 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | predicate 809 | name contains $value 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | icon 843 | name 844 | ext 845 | size 846 | date 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 887 | 898 | 909 | 920 | 933 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1046 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | -------------------------------------------------------------------------------- /ViFinder/FavouriteMenuItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // FavouriteMenuItem.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/14. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | 14 | @interface FavouriteMenuItem : NSManagedObject 15 | 16 | @property (nonatomic, retain) NSString * name; 17 | @property (nonatomic, retain) NSString * path; 18 | @property (nonatomic, retain) NSString * shortcut; 19 | 20 | @property NSMenuItem *menuItem; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ViFinder/FavouriteMenuItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // FavouriteMenuItem.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/14. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "FavouriteMenuItem.h" 10 | 11 | 12 | @implementation FavouriteMenuItem { 13 | @private 14 | NSMenuItem *_menuItem; 15 | NSString *_shortcut; 16 | } 17 | 18 | @dynamic name; 19 | @dynamic path; 20 | 21 | 22 | - (NSString *)shortcut { 23 | if(_shortcut==nil) 24 | _shortcut =@""; 25 | return _shortcut; 26 | } 27 | 28 | - (void)setShortcut:(NSString *)shortcut { 29 | _shortcut = shortcut; 30 | } 31 | 32 | -(NSMenuItem *)menuItem { 33 | if (_menuItem == nil) { 34 | _menuItem = [[NSMenuItem alloc] initWithTitle:self.name action:@selector(favouriteMenuClick:) keyEquivalent:self.shortcut]; 35 | [_menuItem setKeyEquivalentModifierMask:0]; 36 | } 37 | return _menuItem; 38 | } 39 | 40 | - (void)setMenuItem:(NSMenuItem *)menuItem { 41 | _menuItem = menuItem; 42 | } 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ViFinder/FileItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // FileItem.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/12. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface FileItem : NSObject 13 | @property NSString *name; 14 | @property BOOL isDirectiory; 15 | @property NSString *ext; 16 | @property NSURL *previewItemURL; 17 | @property NSString *path; 18 | @property NSImage *icon; 19 | @property unsigned long long int *size; 20 | @property NSDate *date; 21 | 22 | - (instancetype)initWithName:(NSString *)name fileAttribute:(NSDictionary *)aFileAttribute path:(NSString *)path; 23 | 24 | + (instancetype)itemWithName:(NSString *)name fileAttribute:(NSDictionary *)aFileAttribute path:(NSString *)path; 25 | 26 | - (void)trashSelf; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ViFinder/FileItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // FileItem.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/12. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "FileItem.h" 10 | 11 | @implementation FileItem { 12 | NSDictionary *fileAttribute; 13 | } 14 | 15 | - (NSImage *)icon { 16 | NSImage *icon; 17 | if (self.isDirectiory) { 18 | icon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)]; 19 | } else { 20 | icon = [[NSWorkspace sharedWorkspace] iconForFileType:self.ext]; 21 | } 22 | return icon; 23 | } 24 | 25 | - (unsigned long long int)size { 26 | return fileAttribute.fileSize; 27 | } 28 | 29 | - (NSDate *)date { 30 | return fileAttribute.fileModificationDate; 31 | } 32 | 33 | 34 | - (instancetype)initWithName:(NSString *)name fileAttribute:(NSDictionary *)aFileAttribute path:(NSString *)path { 35 | self = [super init]; 36 | if (self) { 37 | self.name = name; 38 | fileAttribute = aFileAttribute; 39 | self.path = [path stringByAppendingFormat:@"/%@", name]; 40 | self.isDirectiory = [[aFileAttribute valueForKey:@"NSFileType"] isEqualToString:NSFileTypeDirectory]; 41 | self.ext = self.isDirectiory ? @"Dir" : [name pathExtension]; 42 | self.previewItemURL = [NSURL fileURLWithPath:self.path]; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | + (instancetype)itemWithName:(NSString *)name fileAttribute:(NSDictionary *)aFileAttribute path:(NSString *)path { 49 | return [[self alloc] initWithName:name fileAttribute:aFileAttribute path:path]; 50 | } 51 | 52 | - (void)trashSelf { 53 | NSFileManager *fileManager = [[NSFileManager alloc] init]; 54 | [fileManager trashItemAtURL:[[NSURL alloc] initFileURLWithPath:self.path] resultingItemURL:nil error:nil]; 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /ViFinder/FileTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FileTableView.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/12. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FileTableView : NSTableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ViFinder/FileTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // FileTableView.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/12. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "FileTableView.h" 10 | #import "FileItem.h" 11 | #import "FileViewController.h" 12 | 13 | 14 | @implementation FileTableView { 15 | FileViewController *_parentViewController; 16 | } 17 | 18 | - (instancetype)initWithCoder:(NSCoder *)coder { 19 | self = [super initWithCoder:coder]; 20 | if (self) { 21 | [self setDelegate:self]; 22 | } 23 | 24 | return self; 25 | } 26 | 27 | - (void)drawRect:(NSRect)dirtyRect { 28 | [super drawRect:dirtyRect]; 29 | 30 | // Drawing code here. 31 | } 32 | 33 | - (void)keyDown:(NSEvent *)theEvent { 34 | [[self parentViewController] keyDown:theEvent]; 35 | } 36 | 37 | - (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect { 38 | if (self.parentViewController.activeRow == self.parentViewController.fileItemsArrayContoller.arrangedObjects[row] 39 | && [[self window] firstResponder] == self) { 40 | NSColor *color = [NSColor redColor]; 41 | [color setFill]; 42 | NSFrameRect([self rectOfRow:row]); 43 | } 44 | [super drawRow:row clipRect:clipRect]; 45 | } 46 | 47 | - (FileViewController *)parentViewController { 48 | if (_parentViewController == nil) { 49 | NSResponder *responder = self; 50 | while ([responder isKindOfClass:[NSView class]]) 51 | responder = [responder nextResponder]; 52 | _parentViewController = (FileViewController *) responder; 53 | } 54 | return _parentViewController; 55 | } 56 | 57 | - (BOOL)becomeFirstResponder { 58 | [self reloadData]; 59 | return [super becomeFirstResponder]; 60 | } 61 | 62 | - (BOOL)resignFirstResponder { 63 | [self reloadData]; 64 | return [super resignFirstResponder]; 65 | } 66 | 67 | - (void)mouseDown:(NSEvent *)theEvent { 68 | if ([theEvent modifierFlags] & NSShiftKeyMask) { 69 | //shift Press 70 | NSPoint downPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 71 | NSInteger mouseDownRow = [self rowAtPoint:downPoint]; 72 | NSInteger activeRow = [self.parentViewController.fileItemsArrayContoller.arrangedObjects indexOfObject:self.parentViewController.activeRow]; 73 | [self.parentViewController.fileItemsArrayContoller setSelectedObjects:nil]; 74 | [self.parentViewController.fileItemsArrayContoller addSelectionIndexes: 75 | [NSIndexSet indexSetWithIndexesInRange: 76 | NSMakeRange(mouseDownRow > activeRow ? activeRow : mouseDownRow, abs(mouseDownRow - activeRow) + 1)]]; 77 | 78 | } else { 79 | //normal click 80 | NSPoint downPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 81 | NSInteger mouseDownRow = [self rowAtPoint:downPoint]; 82 | NSPoint upPoint; 83 | NSInteger mouseUpRow; 84 | theEvent = [[self window] nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask]; 85 | switch ([theEvent type]) { 86 | case NSLeftMouseDragged: 87 | [super mouseDown:theEvent]; 88 | upPoint = [NSEvent mouseLocation]; 89 | upPoint = [self.window convertScreenToBase:upPoint]; 90 | upPoint = [self convertPoint:upPoint fromView:nil]; 91 | mouseUpRow = [self rowAtPoint:upPoint]; 92 | if (mouseUpRow != mouseDownRow) { 93 | self.parentViewController.activeRow = self.parentViewController.fileItemsArrayContoller.arrangedObjects[(NSUInteger) mouseUpRow]; 94 | } 95 | break; 96 | case NSLeftMouseUp: 97 | upPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 98 | mouseUpRow = [self rowAtPoint:upPoint]; 99 | if (mouseUpRow != mouseDownRow) { 100 | [super mouseDown:theEvent]; 101 | } else { 102 | self.parentViewController.activeRow = self.parentViewController.fileItemsArrayContoller.arrangedObjects[(NSUInteger) mouseDownRow]; 103 | } 104 | break; 105 | default: 106 | break; 107 | } 108 | } 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /ViFinder/FileViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FileViewController.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "VDKQueue.h" 13 | 14 | @class FileTableView; 15 | @class FileItem; 16 | 17 | @interface FileViewController : NSViewController { 18 | NSMutableArray *fileItems; 19 | } 20 | @property(strong) IBOutlet FileTableView *fileTableView; 21 | @property(strong) IBOutlet NSMenu *favouriteMenu; 22 | @property(strong) IBOutlet NSButton *favouriteMenuButton; 23 | @property(strong) IBOutlet NSArrayController *fileItemsArrayContoller; 24 | @property(strong) IBOutlet NSSearchField *searchField; 25 | @property int mode; 26 | @property NSString *prefix; 27 | @property NSString *currentPath; 28 | 29 | @property FileItem *activeRow; 30 | 31 | - (void)setFileItems:(NSMutableArray *)items; 32 | 33 | - (void)showFavouriteMenu; 34 | 35 | - (void)showPath:(NSString *)path; 36 | @end 37 | 38 | enum { 39 | NORMAL = 1, 40 | INSERT = 2, 41 | VISUAL = 3, 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /ViFinder/FileViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FileViewController.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "FileViewController.h" 10 | #import "FileTableView.h" 11 | #import "FileItem.h" 12 | #import "FavouriteMenuItem.h" 13 | #import "AppDelegate.h" 14 | #import "AddFavouriteViewController.h" 15 | #import "OverwriteFileViewController.h" 16 | 17 | 18 | @implementation FileViewController { 19 | @private 20 | NSFileManager *fileManager; 21 | FileTableView *_fileTableView; 22 | NSMutableArray *favouriteMenuArray; 23 | NSManagedObjectContext *_favouriteMenuCoreDataContext; 24 | VDKQueue *vdkQueue; 25 | FileItem *_activeRow; 26 | NSString *_currentPath; 27 | FileViewController *_otherPanel; 28 | } 29 | 30 | @synthesize fileTableView = _fileTableView; 31 | 32 | @synthesize currentPath = _currentPath; 33 | 34 | - (FileItem *)activeRow { 35 | if (_activeRow == nil || ![self.fileItemsArrayContoller.arrangedObjects containsObject:_activeRow]) { 36 | _activeRow = [self.fileItemsArrayContoller.arrangedObjects firstObject]; 37 | } 38 | return _activeRow; 39 | } 40 | 41 | - (void)setActiveRow:(FileItem *)activeRow { 42 | _activeRow = activeRow; 43 | [self.fileTableView reloadData]; 44 | [self.fileTableView scrollRowToVisible:[self.fileItemsArrayContoller.arrangedObjects indexOfObject:self.activeRow]]; 45 | } 46 | 47 | - (void)viewDidLoad { 48 | [super viewDidLoad]; 49 | if (fileManager == nil) { 50 | fileManager = [[NSFileManager alloc] init]; 51 | fileItems = [[NSMutableArray alloc] init]; 52 | } 53 | vdkQueue = [[VDKQueue alloc] init]; 54 | [self showPath:@"/"]; 55 | 56 | favouriteMenuArray = [[NSMutableArray alloc] init]; 57 | 58 | self.searchField.delegate = self; 59 | 60 | self.mode = NORMAL; 61 | self.prefix = @""; 62 | 63 | } 64 | 65 | - (void)setRepresentedObject:(id)representedObject { 66 | [super setRepresentedObject:representedObject]; 67 | 68 | } 69 | 70 | - (FileViewController *)getOtherPanel { 71 | if (_otherPanel == nil) { 72 | for (FileViewController *c in self.parentViewController.childViewControllers) { 73 | if (c != self) { 74 | _otherPanel = c; 75 | } 76 | } 77 | } 78 | return _otherPanel; 79 | } 80 | 81 | - (NSString *)getOtherPanelPath { 82 | return self.getOtherPanel.currentPath; 83 | } 84 | 85 | #pragma mark - keyboard 86 | 87 | - (void)keyDown:(NSEvent *)theEvent { 88 | if (self.mode == NORMAL) { 89 | if ([self.prefix isEqualToString:@""]) { 90 | //prefix == "" 91 | if (theEvent.keyCode == kVK_ANSI_Equal) { 92 | [self setOtherPanelPathToCurrent]; 93 | return; 94 | } 95 | 96 | if (theEvent.keyCode == kVK_ANSI_J) { 97 | [self nextRow]; 98 | } 99 | if (theEvent.keyCode == kVK_ANSI_K) { 100 | [self preRow]; 101 | } 102 | if (theEvent.keyCode == kVK_Return) { 103 | [self openActiveRow]; 104 | } 105 | if (theEvent.keyCode == kVK_Delete) { 106 | [self openParentDir]; 107 | } 108 | if (theEvent.keyCode == kVK_ANSI_Q) { 109 | [self preview]; 110 | } 111 | if (theEvent.keyCode == kVK_ANSI_E) { 112 | [self terminalHere]; 113 | } 114 | if (theEvent.keyCode == kVK_ANSI_G) { 115 | if ([theEvent modifierFlags] & NSShiftKeyMask) { 116 | //Shift 117 | [self gotoEnd]; 118 | } else { 119 | self.prefix = @"g"; 120 | } 121 | return; 122 | } 123 | if (theEvent.keyCode == kVK_ANSI_Y) { 124 | [self copyFileName]; 125 | } 126 | if (theEvent.keyCode == kVK_ANSI_S) { 127 | self.prefix = @"s"; 128 | } 129 | if (theEvent.keyCode == kVK_ANSI_D) { 130 | [self showFavouriteMenu]; 131 | } 132 | if (theEvent.keyCode == kVK_ANSI_X) { 133 | [self trashSeleted]; 134 | } 135 | if (theEvent.keyCode == kVK_ANSI_F) { 136 | self.prefix = @"f"; 137 | } 138 | if (theEvent.keyCode == kVK_Tab) { 139 | [self activeAnotherPanel]; 140 | } 141 | if (theEvent.keyCode == kVK_Space) { 142 | [self toggleItemSelection]; 143 | } 144 | if (theEvent.keyCode == kVK_ANSI_Slash) { 145 | [self filterList]; 146 | } 147 | if (theEvent.keyCode == kVK_ANSI_LeftBracket) { 148 | [self selectSameNameItems]; 149 | } 150 | if (theEvent.keyCode == kVK_ANSI_RightBracket) { 151 | [self selectSameExtItems]; 152 | } 153 | if (theEvent.keyCode == kVK_ANSI_Backslash) { 154 | if (theEvent.modifierFlags & NSShiftKeyMask) { 155 | [self clearSelection]; 156 | } else { 157 | [self toggleSelection]; 158 | } 159 | } 160 | } 161 | 162 | else { 163 | //prefix != "" 164 | if ([self.prefix isEqualToString:@"g"]) { 165 | if (theEvent.keyCode == kVK_ANSI_G) { 166 | [self gotoTop]; 167 | self.prefix = @""; 168 | } 169 | } 170 | if ([self.prefix isEqualToString:@"s"]) { 171 | if (theEvent.keyCode == kVK_ANSI_N) { 172 | [self toggleSortColumn:@"name"]; 173 | self.prefix = @""; 174 | } 175 | if (theEvent.keyCode == kVK_ANSI_S) { 176 | [self toggleSortColumn:@"size"]; 177 | self.prefix = @""; 178 | } 179 | if (theEvent.keyCode == kVK_ANSI_E) { 180 | [self toggleSortColumn:@"ext"]; 181 | self.prefix = @""; 182 | } 183 | if (theEvent.keyCode == kVK_ANSI_D) { 184 | [self toggleSortColumn:@"date"]; 185 | self.prefix = @""; 186 | } 187 | } 188 | if ([self.prefix isEqualToString:@"f"]) { 189 | if (theEvent.keyCode == kVK_ANSI_F) { 190 | [self copyToClipBoard]; 191 | self.prefix = @""; 192 | } 193 | if (theEvent.keyCode == kVK_ANSI_V) { 194 | [self pasteFromClipboard]; 195 | self.prefix = @""; 196 | } 197 | if (theEvent.keyCode == kVK_ANSI_C) { 198 | [self copyToOtherPanel]; 199 | self.prefix = @""; 200 | } 201 | if (theEvent.keyCode == kVK_ANSI_X) { 202 | [self moveToOtherPanel]; 203 | self.prefix = @""; 204 | } 205 | } 206 | self.prefix = @""; 207 | } 208 | } 209 | 210 | } 211 | 212 | - (void)setOtherPanelPathToCurrent { 213 | [self.getOtherPanel showPath:self.currentPath]; 214 | } 215 | 216 | - (void)nextRow { 217 | NSUInteger index = [_fileItemsArrayContoller.arrangedObjects indexOfObject:self.activeRow]; 218 | if (index != ((NSArray *) _fileItemsArrayContoller.arrangedObjects).count - 1) { 219 | self.activeRow = _fileItemsArrayContoller.arrangedObjects[index + 1]; 220 | } 221 | } 222 | 223 | - (void)preRow { 224 | NSUInteger index = [_fileItemsArrayContoller.arrangedObjects indexOfObject:self.activeRow]; 225 | if (index != 0) { 226 | self.activeRow = _fileItemsArrayContoller.arrangedObjects[index - 1]; 227 | } 228 | } 229 | 230 | - (void)openActiveRow { 231 | NSString *path = [self.currentPath stringByAppendingPathComponent:self.activeRow.name]; 232 | if (self.activeRow.isDirectiory) { 233 | [self showPath:path]; 234 | } else { 235 | NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; 236 | [workspace openFile:path]; 237 | } 238 | } 239 | 240 | - (void)openParentDir { 241 | NSString *path = [self.currentPath stringByDeletingLastPathComponent]; 242 | [self showPath:path]; 243 | } 244 | 245 | - (void)preview { 246 | if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) { 247 | [[QLPreviewPanel sharedPreviewPanel] orderOut:nil]; 248 | } 249 | else { 250 | [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; 251 | } 252 | } 253 | 254 | - (void)terminalHere { 255 | NSString *iTermNewTab = [NSString stringWithFormat: 256 | @"if application \"iTerm\" is running then\n" 257 | "\ttell application \"iTerm\"\n" 258 | "\t\ttry\n" 259 | "\t\t\ttell the first terminal\n" 260 | "\t\t\t\tlaunch session \"Default Session\"\n" 261 | "\t\t\t\ttell the last session\n" 262 | "\t\t\t\t\twrite text \"cd %@\"\n" 263 | "\t\t\t\tend tell\n" 264 | "\t\t\tend tell\n" 265 | "\t\ton error\n" 266 | "\t\t\tset myterm to (make new terminal)\n" 267 | "\t\t\ttell myterm\n" 268 | "\t\t\t\tlaunch session \"Default Session\"\n" 269 | "\t\t\t\ttell the last session\n" 270 | "\t\t\t\t\twrite text \"cd %@\"\n" 271 | "\t\t\t\tend tell\n" 272 | "\t\t\tend tell\n" 273 | "\t\tend try\n" 274 | "\t\tactivate\n" 275 | "\tend tell\n" 276 | "else\n" 277 | "\ttell application \"iTerm\"\n" 278 | "\t\tactivate\n" 279 | "\t\ttell the first terminal\n" 280 | "\t\t\ttell the first session\n" 281 | "\t\t\t\twrite text \"cd %@\"\n" 282 | "\t\t\tend tell\n" 283 | "\t\tend tell\n" 284 | "\tend tell\n" 285 | "end if", self.currentPath, self.currentPath, self.currentPath]; 286 | 287 | NSString *iTermNewWindow = [NSString stringWithFormat: 288 | @"if application \"iTerm\" is running then\n" 289 | "\ttell application \"iTerm\"\n" 290 | "\t\ttry\n" 291 | "\t\t\tset myterm to (make new terminal)\n" 292 | "\t\t\ttell myterm\n" 293 | "\t\t\t\tlaunch session \"Default Session\"\n" 294 | "\t\t\t\ttell the last session\n" 295 | "\t\t\t\t\twrite text \"cd %@\"\n" 296 | "\t\t\t\tend tell\n" 297 | "\t\t\tend tell\n" 298 | "\t\ton error\n" 299 | "\t\t\tset myterm to (make new terminal)\n" 300 | "\t\t\ttell myterm\n" 301 | "\t\t\t\tlaunch session \"Default Session\"\n" 302 | "\t\t\t\ttell the last session\n" 303 | "\t\t\t\t\twrite text \"cd %@\"\n" 304 | "\t\t\t\tend tell\n" 305 | "\t\t\tend tell\n" 306 | "\t\tend try\n" 307 | "\t\tactivate\n" 308 | "\tend tell\n" 309 | "else\n" 310 | "\ttell application \"iTerm\"\n" 311 | "\t\tactivate\n" 312 | "\t\ttell the first terminal\n" 313 | "\t\t\ttell the first session\n" 314 | "\t\t\t\twrite text \"cd %@\"\n" 315 | "\t\t\tend tell\n" 316 | "\t\tend tell\n" 317 | "\tend tell\n" 318 | "end if", self.currentPath, self.currentPath, self.currentPath]; 319 | 320 | NSString *terminalNewWindow = [NSString stringWithFormat: 321 | @"tell application \"Terminal\"\n" 322 | "\tdo script \"cd %@\"\n" 323 | "\tactivate\n" 324 | "end tell", self.currentPath]; 325 | 326 | NSAppleScript *script = [[NSAppleScript alloc] initWithSource:terminalNewWindow]; 327 | [script executeAndReturnError:nil]; 328 | } 329 | 330 | - (void)copyFileName { 331 | NSString *name = self.activeRow.name; 332 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 333 | [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeString] owner:nil]; 334 | [pasteboard setString:[self.currentPath stringByAppendingPathComponent:name] forType:NSPasteboardTypeString]; 335 | } 336 | 337 | - (void)trashSeleted { 338 | if (self.fileItemsArrayContoller.selectedObjects.count == 0) { 339 | [self.activeRow trashSelf]; 340 | } else { 341 | for (FileItem *f in _fileItemsArrayContoller.selectedObjects) { 342 | [f trashSelf]; 343 | } 344 | } 345 | } 346 | 347 | - (void)gotoTop { 348 | self.activeRow = [self.fileItemsArrayContoller.arrangedObjects firstObject]; 349 | } 350 | 351 | - (void)gotoEnd { 352 | self.activeRow = [self.fileItemsArrayContoller.arrangedObjects lastObject]; 353 | } 354 | 355 | - (void)toggleItemSelection { 356 | if ([self.fileItemsArrayContoller.selectedObjects containsObject:self.activeRow]) { 357 | [self.fileItemsArrayContoller removeSelectedObjects:@[self.activeRow]]; 358 | } else { 359 | [self.fileItemsArrayContoller addSelectedObjects:@[self.activeRow]]; 360 | } 361 | } 362 | 363 | - (void)activeAnotherPanel { 364 | for (FileViewController *controller in self.parentViewController.childViewControllers) { 365 | if (controller != self) { 366 | [controller.view.window makeFirstResponder:controller.fileTableView]; 367 | } 368 | } 369 | } 370 | 371 | - (void)selectSameNameItems { 372 | for (FileItem *item in self.fileItemsArrayContoller.arrangedObjects) { 373 | if ([[item.name stringByDeletingPathExtension] isEqualToString:[self.activeRow.name stringByDeletingPathExtension]]) { 374 | [self.fileItemsArrayContoller addSelectedObjects:@[item]]; 375 | } 376 | } 377 | } 378 | 379 | - (void)selectSameExtItems { 380 | for (FileItem *item in self.fileItemsArrayContoller.arrangedObjects) { 381 | if ([item.ext isEqualToString:self.activeRow.ext]) { 382 | [self.fileItemsArrayContoller addSelectedObjects:@[item]]; 383 | } 384 | } 385 | } 386 | 387 | - (void)toggleSelection { 388 | NSArray *selection = self.fileItemsArrayContoller.selectedObjects; 389 | NSMutableOrderedSet *all = [NSMutableOrderedSet orderedSetWithArray:self.fileItemsArrayContoller.arrangedObjects]; 390 | [all minusSet:[NSSet setWithArray:selection]]; 391 | [self.fileItemsArrayContoller setSelectedObjects:all.set.allObjects]; 392 | return; 393 | } 394 | 395 | - (void)clearSelection { 396 | [self.fileItemsArrayContoller setSelectedObjects:nil]; 397 | } 398 | 399 | - (void)filterList { 400 | [self.view.window makeFirstResponder:self.searchField]; 401 | } 402 | 403 | - (void)toggleSortColumn:(NSString *)name { 404 | NSSortDescriptor *descriptor = [self.fileItemsArrayContoller.sortDescriptors firstObject]; 405 | if ([descriptor.key isEqualToString:name] && descriptor.ascending) { 406 | descriptor = [NSSortDescriptor sortDescriptorWithKey:name 407 | ascending:NO 408 | selector:@selector(compare:)]; 409 | } else { 410 | descriptor = [NSSortDescriptor sortDescriptorWithKey:name 411 | ascending:YES 412 | selector:@selector(compare:)]; 413 | } 414 | [self.fileItemsArrayContoller setSortDescriptors:@[descriptor]]; 415 | } 416 | 417 | - (void)copyToClipBoard { 418 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 419 | [pasteboard clearContents]; 420 | NSMutableArray *items = [[NSMutableArray alloc] init]; 421 | if (self.fileItemsArrayContoller.selectedObjects.count == 0) { 422 | NSURL *url = [[NSURL alloc] initFileURLWithPath:[self.currentPath stringByAppendingPathComponent:self.activeRow.name]]; 423 | [items addObject:url]; 424 | } else { 425 | for (FileItem *item in self.fileItemsArrayContoller.selectedObjects) { 426 | NSURL *url = [[NSURL alloc] initFileURLWithPath:[self.currentPath stringByAppendingPathComponent:item.name]]; 427 | [items addObject:url]; 428 | } 429 | } 430 | [pasteboard writeObjects:items]; 431 | } 432 | 433 | - (void)pasteFromClipboard { 434 | NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 435 | NSArray *classes = [NSArray arrayWithObject:[NSURL class]]; 436 | NSDictionary *options; 437 | options = @{NSPasteboardURLReadingFileURLsOnlyKey : @YES}; 438 | NSArray *urls = [pasteboard readObjectsForClasses:classes options:options]; 439 | __block BOOL isOverWriteAll = FALSE; 440 | __block BOOL isSkipAll = FALSE; 441 | NSMutableArray *sheets = [[NSMutableArray alloc] init]; 442 | for (NSURL *url in urls) { 443 | NSString *fileName = url.lastPathComponent; 444 | NSString *newPath = [self.currentPath stringByAppendingPathComponent:fileName]; 445 | if (!isSkipAll) { 446 | if ([fileManager fileExistsAtPath:newPath] && !isOverWriteAll) { 447 | OverwriteFileViewController *ofvc = [self.storyboard instantiateControllerWithIdentifier:@"OverwriteFileViewController"]; 448 | [sheets addObject:ofvc]; 449 | ofvc.sourcePath = url.path; 450 | ofvc.targetPath = newPath; 451 | 452 | ofvc.overWrite = ^() { 453 | [fileManager removeItemAtPath:newPath error:nil]; 454 | [fileManager copyItemAtPath:url.path toPath:newPath error:nil]; 455 | }; 456 | 457 | ofvc.overWriteAll = ^() { 458 | [fileManager removeItemAtPath:newPath error:nil]; 459 | [fileManager copyItemAtPath:url.path toPath:newPath error:nil]; 460 | isOverWriteAll = TRUE; 461 | }; 462 | 463 | ofvc.skip = ^() { 464 | }; 465 | 466 | ofvc.skipAll = ^() { 467 | isSkipAll = TRUE; 468 | }; 469 | 470 | ofvc.rename = ^() { 471 | }; 472 | [self presentViewControllerAsModalWindow:ofvc]; 473 | CFRunLoopRun(); 474 | } else { 475 | [fileManager copyItemAtPath:url.path toPath:newPath error:nil]; 476 | } 477 | } 478 | } 479 | } 480 | 481 | - (void)copyToOtherPanel { 482 | NSMutableArray *items = [[NSMutableArray alloc] init]; 483 | if (self.fileItemsArrayContoller.selectedObjects.count == 0) { 484 | [items addObject:self.activeRow]; 485 | } else { 486 | items = [self.fileItemsArrayContoller.selectedObjects mutableCopy]; 487 | } 488 | 489 | NSString *otherPanelPath = self.getOtherPanelPath; 490 | __block BOOL isOverWriteAll = FALSE; 491 | __block BOOL isSkipAll = FALSE; 492 | for (FileItem *item in items) { 493 | NSString *newPath = [otherPanelPath stringByAppendingPathComponent:item.name]; 494 | if (!isSkipAll) { 495 | if ([fileManager fileExistsAtPath:newPath] && !isOverWriteAll) { 496 | OverwriteFileViewController *ofvc = [self.storyboard instantiateControllerWithIdentifier:@"OverwriteFileViewController"]; 497 | ofvc.sourcePath = item.path; 498 | ofvc.targetPath = newPath; 499 | 500 | ofvc.overWrite = ^() { 501 | [fileManager removeItemAtPath:newPath error:nil]; 502 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 503 | }; 504 | 505 | ofvc.overWriteAll = ^() { 506 | [fileManager removeItemAtPath:newPath error:nil]; 507 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 508 | isOverWriteAll = TRUE; 509 | }; 510 | 511 | ofvc.skip = ^() { 512 | }; 513 | 514 | ofvc.skipAll = ^() { 515 | isSkipAll = TRUE; 516 | }; 517 | 518 | ofvc.rename = ^() { 519 | }; 520 | [self presentViewControllerAsModalWindow:ofvc]; 521 | CFRunLoopRun(); 522 | } else { 523 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 524 | } 525 | } 526 | 527 | } 528 | } 529 | 530 | - (void)moveToOtherPanel { 531 | NSMutableArray *items = [[NSMutableArray alloc] init]; 532 | if (self.fileItemsArrayContoller.selectedObjects.count == 0) { 533 | [items addObject:self.activeRow]; 534 | } else { 535 | items = [self.fileItemsArrayContoller.selectedObjects mutableCopy]; 536 | } 537 | 538 | NSString *otherPanelPath = self.getOtherPanelPath; 539 | __block BOOL isOverWriteAll = FALSE; 540 | __block BOOL isSkipAll = FALSE; 541 | for (FileItem *item in items) { 542 | NSString *newPath = [otherPanelPath stringByAppendingPathComponent:item.name]; 543 | if (!isSkipAll) { 544 | if ([fileManager fileExistsAtPath:newPath] && !isOverWriteAll) { 545 | OverwriteFileViewController *ofvc = [self.storyboard instantiateControllerWithIdentifier:@"OverwriteFileViewController"]; 546 | ofvc.sourcePath = item.path; 547 | ofvc.targetPath = newPath; 548 | 549 | ofvc.overWrite = ^() { 550 | [fileManager removeItemAtPath:newPath error:nil]; 551 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 552 | [fileManager removeItemAtPath:item.path error:nil]; 553 | }; 554 | 555 | ofvc.overWriteAll = ^() { 556 | [fileManager removeItemAtPath:newPath error:nil]; 557 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 558 | [fileManager removeItemAtPath:item.path error:nil]; 559 | isOverWriteAll = TRUE; 560 | }; 561 | 562 | ofvc.skip = ^() { 563 | }; 564 | 565 | ofvc.skipAll = ^() { 566 | isSkipAll = TRUE; 567 | }; 568 | 569 | ofvc.rename = ^() { 570 | }; 571 | [self presentViewControllerAsModalWindow:ofvc]; 572 | CFRunLoopRun(); 573 | } else { 574 | [fileManager copyItemAtPath:item.path toPath:newPath error:nil]; 575 | [fileManager removeItemAtPath:item.path error:nil]; 576 | } 577 | } 578 | 579 | } 580 | } 581 | 582 | #pragma mark - FavouriteMenu 583 | 584 | - (NSManagedObjectContext *)coreDataContext { 585 | AppDelegate *appDelegate = (AppDelegate *) [[NSApplication sharedApplication] delegate]; 586 | return appDelegate.coreDataContext; 587 | } 588 | 589 | - (NSMutableArray *)fileItems { 590 | return fileItems; 591 | } 592 | 593 | - (void)setFileItems:(NSMutableArray *)items { 594 | if (fileItems == items) 595 | return; 596 | fileItems = items; 597 | } 598 | 599 | - (void)showFavouriteMenu { 600 | //to window 601 | NSPoint p = [self.view convertPoint:_favouriteMenuButton.frame.origin toView:nil]; 602 | //to screen 603 | p = [self.view.window convertBaseToScreen:p]; 604 | 605 | [_favouriteMenu removeAllItems]; 606 | 607 | [[_favouriteMenu addItemWithTitle:@"Add Here" action:@selector(addFavouriteHere:) keyEquivalent:@"a"] setKeyEquivalentModifierMask:0]; 608 | [_favouriteMenu addItem:[NSMenuItem separatorItem]]; 609 | 610 | NSFetchRequest *request = [[NSFetchRequest alloc] init]; 611 | request.entity = [NSEntityDescription entityForName:@"FavouriteMenuItem" inManagedObjectContext:self.coreDataContext]; 612 | NSArray *objs = [self.coreDataContext executeFetchRequest:request error:nil]; 613 | favouriteMenuArray = [objs mutableCopy]; 614 | for (FavouriteMenuItem *menuItem in favouriteMenuArray) { 615 | menuItem.menuItem.menu = nil; 616 | [_favouriteMenu addItem:menuItem.menuItem]; 617 | } 618 | for (FavouriteMenuItem *menuItem in favouriteMenuArray) { 619 | if ([menuItem.path isEqualToString:self.currentPath]) { 620 | [_favouriteMenu addItem:[NSMenuItem separatorItem]]; 621 | [_favouriteMenu addItemWithTitle:@"Remove Here" action:@selector(removeFavouriteHere:) keyEquivalent:@"d"]; 622 | break; 623 | } 624 | } 625 | [_favouriteMenu popUpMenuPositioningItem:nil atLocation:p inView:nil]; 626 | } 627 | 628 | - (void)addFavouriteHere:(id)sender { 629 | AddFavouriteViewController *addFavouriteViewController = [self.storyboard instantiateControllerWithIdentifier:@"AddFavouriteViewController"]; 630 | addFavouriteViewController.path = self.currentPath; 631 | addFavouriteViewController.addFav = ^(NSString *path, NSString *name, NSString *shortcut) { 632 | FavouriteMenuItem *favouriteMenuItem = [NSEntityDescription insertNewObjectForEntityForName:@"FavouriteMenuItem" inManagedObjectContext:self.coreDataContext]; 633 | favouriteMenuItem.name = name; 634 | favouriteMenuItem.path = path; 635 | favouriteMenuItem.shortcut = shortcut; 636 | [self.coreDataContext save:nil]; 637 | }; 638 | [self presentViewControllerAsSheet:addFavouriteViewController]; 639 | } 640 | 641 | - (void)removeFavouriteHere:(id)sender { 642 | NSFetchRequest *request = [[NSFetchRequest alloc] init]; 643 | request.entity = [NSEntityDescription entityForName:@"FavouriteMenuItem" inManagedObjectContext:self.coreDataContext]; 644 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path = %@", self.currentPath]; 645 | request.predicate = predicate; 646 | NSArray *objs = [self.coreDataContext executeFetchRequest:request error:nil]; 647 | for (NSManagedObject *obj in objs) { 648 | [self.coreDataContext deleteObject:obj]; 649 | } 650 | [self.coreDataContext save:nil]; 651 | } 652 | 653 | - (void)favouriteMenuClick:(id)sender { 654 | for (FavouriteMenuItem *item in favouriteMenuArray) { 655 | if (sender == item.menuItem) { 656 | [self showPath:item.path]; 657 | } 658 | } 659 | } 660 | 661 | #pragma mark - FileTableView 662 | 663 | - (void)showPath:(NSString *)path { 664 | if ([path isEqualToString:self.currentPath]) { 665 | [self refreshCurrentPath]; 666 | } else { 667 | self.currentPath = path; 668 | _fileItemsArrayContoller.filterPredicate = nil; 669 | [self setFileItems:[[self getFileListAtPath:path] mutableCopy]]; 670 | [vdkQueue removeAllPaths]; 671 | [vdkQueue addPath:self.currentPath]; 672 | [vdkQueue setDelegate:self]; 673 | } 674 | } 675 | 676 | - (void)refreshCurrentPath { 677 | NSInteger preActiveRowIndex = [self.fileItemsArrayContoller.arrangedObjects indexOfObject:self.activeRow]; 678 | [self setFileItems:[[self getFileListAtPath:self.currentPath] mutableCopy]]; 679 | if (preActiveRowIndex > [self.fileItemsArrayContoller.arrangedObjects count] - 1 || [self.fileItemsArrayContoller.arrangedObjects count] == 0) { 680 | self.activeRow = [self.fileItemsArrayContoller.arrangedObjects lastObject]; 681 | } else { 682 | self.activeRow = [[self.fileItemsArrayContoller arrangedObjects] objectAtIndex:(NSUInteger) preActiveRowIndex]; 683 | } 684 | } 685 | 686 | - (NSArray *)getFileListAtPath:(NSString *)path { 687 | NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:path]; 688 | NSMutableArray *fileList = [[NSMutableArray alloc] init]; 689 | NSString *name; 690 | while ((name = enumerator.nextObject) != nil) { 691 | [enumerator skipDescendants]; 692 | FileItem *item = [FileItem itemWithName:name fileAttribute:enumerator.fileAttributes path:self.currentPath]; 693 | [fileList addObject:item]; 694 | } 695 | return fileList; 696 | } 697 | 698 | #pragma mark - QLPreviewPanel protocol 699 | 700 | - (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel { 701 | return YES; 702 | } 703 | 704 | - (void)beginPreviewPanelControl:(QLPreviewPanel *)panel { 705 | panel.dataSource = self; 706 | } 707 | 708 | - (void)endPreviewPanelControl:(QLPreviewPanel *)panel { 709 | } 710 | 711 | 712 | #pragma mark - QLPreviewItem 713 | 714 | - (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel { 715 | return self.fileItemsArrayContoller.selectedObjects.count == 0 ? 1 : self.fileItemsArrayContoller.selectedObjects.count; 716 | } 717 | 718 | - (id )previewPanel:(QLPreviewPanel *)panel 719 | previewItemAtIndex: 720 | (NSInteger)index { 721 | if (self.fileItemsArrayContoller.selectedObjects.count == 0) { 722 | return self.activeRow; 723 | } else { 724 | return _fileItemsArrayContoller.selectedObjects[(NSUInteger) index]; 725 | } 726 | } 727 | 728 | - (void) VDKQueue:(VDKQueue *)queue 729 | receivedNotification: 730 | (NSString *)noteName 731 | forPath: 732 | (NSString *)fpath { 733 | [self refreshCurrentPath]; 734 | } 735 | 736 | #pragma mark - SearchField 737 | 738 | - (BOOL)control:(NSControl *)control 739 | textView: 740 | (NSTextView *)textView 741 | doCommandBySelector: 742 | (SEL)commandSelector { 743 | if (commandSelector == @selector(cancelOperation:) || commandSelector == @selector(insertNewline:)) { 744 | NSEvent *theEvent = [NSApp currentEvent]; 745 | if (theEvent.keyCode == kVK_Escape || theEvent.keyCode == kVK_Return) { 746 | [self.view.window makeFirstResponder:self.fileTableView]; 747 | return YES; 748 | } else { 749 | return NO; 750 | } 751 | } 752 | return NO; 753 | } 754 | 755 | 756 | @end 757 | -------------------------------------------------------------------------------- /ViFinder/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 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /ViFinder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.xiazhiri.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015年 likaci. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /ViFinder/Model.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ViFinder/OverwriteFileViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OverwriteFileViewController.h 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/26. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OverwriteFileViewController : NSViewController 12 | @property NSString *sourcePath; 13 | @property NSString *targetPath; 14 | @property(strong) IBOutlet NSTextField *sourceName; 15 | @property(strong) IBOutlet NSTextField *targetName; 16 | @property(nonatomic, copy) void (^overWrite)(); 17 | @property(nonatomic, copy) void (^overWriteAll)(); 18 | @property(nonatomic, copy) void (^skip)(); 19 | @property(nonatomic, copy) void (^skipAll)(); 20 | @property(nonatomic, copy) void (^rename)(); 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ViFinder/OverwriteFileViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OverwriteFileViewController.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/26. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import "OverwriteFileViewController.h" 10 | 11 | @interface OverwriteFileViewController () 12 | 13 | @end 14 | 15 | @implementation OverwriteFileViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | [self.sourceName setStringValue:self.sourcePath.lastPathComponent]; 20 | [self.targetName setStringValue:self.targetPath.lastPathComponent]; 21 | } 22 | 23 | - (IBAction)cancel:(id)sender { 24 | [self dismissViewController:self]; 25 | } 26 | 27 | - (IBAction)overWrite:(id)sender { 28 | self.overWrite(); 29 | [self dismissViewController:self]; 30 | } 31 | 32 | - (IBAction)overWriteAll:(id)sender { 33 | self.overWriteAll(); 34 | [self dismissViewController:self]; 35 | } 36 | 37 | - (IBAction)skip:(id)sender { 38 | self.skip(); 39 | [self dismissViewController:self]; 40 | } 41 | 42 | - (IBAction)skipAll:(id)sender { 43 | self.skipAll(); 44 | [self dismissViewController:self]; 45 | } 46 | 47 | - (IBAction)rename:(id)sender { 48 | self.rename(); 49 | [self dismissViewController:self]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /ViFinder/VDKQueue.h: -------------------------------------------------------------------------------- 1 | // VDKQueue.h 2 | // Created by Bryan D K Jones on 28 March 2012 3 | // Copyright 2013 Bryan D K Jones 4 | // 5 | // Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any damages 9 | // arising from the use of this software. 10 | // Permission is granted to anyone to use this software for any purpose, 11 | // including commercial applications, and to alter it and redistribute it 12 | // freely, subject to the following restrictions: 13 | // 1. The origin of this software must not be misrepresented; you must not 14 | // claim that you wrote the original software. If you use this software 15 | // in a product, an acknowledgment in the product documentation would be 16 | // appreciated but is not required. 17 | // 2. Altered source versions must be plainly marked as such, and must not be 18 | // misrepresented as being the original software. 19 | // 3. This notice may not be removed or altered from any source 20 | // distribution. 21 | 22 | // 23 | // BASED ON UKKQUEUE: 24 | // 25 | // This is an updated, modernized and streamlined version of the excellent UKKQueue class, which was authored by Uli Kusterer. 26 | // UKKQueue was written back in 2003 and there have been many, many improvements to Objective-C since then. VDKQueue uses the 27 | // core of Uli's original class, but makes it faster and more efficient. Method calls are reduced. Grand Central Dispatch is used in place 28 | // of Uli's "threadProxy" objects. The memory footprint is roughly halved, as I don't create the overhead that UKKQueue does. 29 | // 30 | // VDKQueue is also simplified. The option to use it as a singleton is removed. You simply alloc/init an instance and add paths you want to 31 | // watch. Your objects can be alerted to changes either by notifications or by a delegate method (or both). See below. 32 | // 33 | // It also fixes several bugs. For one, it won't crash if it can't create a file descriptor to a file you ask it to watch. (By default, an OS X process can only 34 | // have about 3,000 file descriptors open at once. If you hit that limit, UKKQueue will crash. VDKQueue will not.) 35 | // 36 | 37 | // 38 | // DEPENDENCIES: 39 | // 40 | // VDKQueue requires OS 10.6+ because it relies on Grand Central Dispatch. 41 | // 42 | 43 | // 44 | // IMPORTANT NOTE ABOUT ATOMIC OPERATIONS 45 | // 46 | // There are two ways of saving a file on OS X: Atomic and Non-Atomic. In a non-atomic operation, a file is saved by directly overwriting it with new data. 47 | // In an Atomic save, a temporary file is first written to a different location on disk. When that completes successfully, the original file is deleted and the 48 | // temporary one is renamed and moved into place where the original file existed. 49 | // 50 | // This matters a great deal. If you tell VDKQueue to watch file X, then you save file X ATOMICALLY, you'll receive a notification about that event. HOWEVER, you will 51 | // NOT receive any additional notifications for file X from then on. This is because the atomic operation has essentially created a new file that replaced the one you 52 | // told VDKQueue to watch. (This is not an issue for non-atomic operations.) 53 | // 54 | // To handle this, any time you receive a change notification from VDKQueue, you should call -removePath: followed by -addPath: on the file's path, even if the path 55 | // has not changed. This will ensure that if the event that triggered the notification was an atomic operation, VDKQueue will start watching the "new" file that took 56 | // the place of the old one. 57 | // 58 | // Other frameworks out there try to work around this issue by immediately attempting to re-open the file descriptor to the path. This is not bulletproof and may fail; 59 | // it all depends on the timing of disk I/O. Bottom line: you could not rely on it and might miss future changes to the file path you're supposedly watching. That's why 60 | // VDKQueue does not take this approach, but favors the "manual" method of "stop-watching-then-rewatch". 61 | // 62 | 63 | 64 | 65 | #import 66 | #import 67 | #include 68 | #include 69 | 70 | 71 | // ARC Helpers 72 | 73 | #if ! __has_feature(objc_arc) 74 | #define ARCCompatAutorelease(__obj__) [__obj__ autorelease]; 75 | #define ARCCompatRetain(__obj__) [__obj__ retain]; 76 | #define ARCCompatRelease(__obj__) [__obj__ release]; 77 | 78 | #define ARCCompatAutoreleaseInline(__obj__) [__obj__ autorelease] 79 | #define ARCCompatRetainInline(__obj__) [__obj__ retain] 80 | #define ARCCompatReleaseInline(__obj__) [__obj__ release] 81 | 82 | #define ARCCompatBridge(__type__, __obj__) __obj__ 83 | 84 | #define ARCCompatWeakPropertyModifier assign 85 | #define ARCCompatWeakPropertyTypeModifier 86 | #else 87 | #define ARCCompatAutorelease(__obj__) 88 | #define ARCCompatRetain(__obj__) 89 | #define ARCCompatRelease(__obj__) 90 | 91 | #define ARCCompatAutoreleaseInline(__obj__) __obj__ 92 | #define ARCCompatRetainInline(__obj__) __obj__ 93 | #define ARCCompatReleaseInline(__obj__) __obj__ 94 | 95 | #define ARCCompatBridge(__type__, __obj__) (__bridge __type__)__obj__ 96 | 97 | #define ARCCompatWeakPropertyModifier weak 98 | #define ARCCompatWeakPropertyTypeModifier __weak 99 | #endif 100 | 101 | 102 | // 103 | // Logical OR these values into the u_int that you pass in the -addPath:notifyingAbout: method 104 | // to specify the types of notifications you're interested in. Pass the default value to receive all of them. 105 | // 106 | #define VDKQueueNotifyAboutRename NOTE_RENAME // Item was renamed. 107 | #define VDKQueueNotifyAboutWrite NOTE_WRITE // Item contents changed (also folder contents changed). 108 | #define VDKQueueNotifyAboutDelete NOTE_DELETE // item was removed. 109 | #define VDKQueueNotifyAboutAttributeChange NOTE_ATTRIB // Item attributes changed. 110 | #define VDKQueueNotifyAboutSizeIncrease NOTE_EXTEND // Item size increased. 111 | #define VDKQueueNotifyAboutLinkCountChanged NOTE_LINK // Item's link count changed. 112 | #define VDKQueueNotifyAboutAccessRevocation NOTE_REVOKE // Access to item was revoked. 113 | 114 | #define VDKQueueNotifyDefault (VDKQueueNotifyAboutRename | VDKQueueNotifyAboutWrite \ 115 | | VDKQueueNotifyAboutDelete | VDKQueueNotifyAboutAttributeChange \ 116 | | VDKQueueNotifyAboutSizeIncrease | VDKQueueNotifyAboutLinkCountChanged \ 117 | | VDKQueueNotifyAboutAccessRevocation) 118 | 119 | // 120 | // Notifications that this class sends to the NSWORKSPACE notification center. 121 | // Object = the instance of VDKQueue that was watching for changes 122 | // userInfo.path = the file path where the change was observed 123 | // 124 | extern NSString * VDKQueueRenameNotification; 125 | extern NSString * VDKQueueWriteNotification; 126 | extern NSString * VDKQueueDeleteNotification; 127 | extern NSString * VDKQueueAttributeChangeNotification; 128 | extern NSString * VDKQueueSizeIncreaseNotification; 129 | extern NSString * VDKQueueLinkCountChangeNotification; 130 | extern NSString * VDKQueueAccessRevocationNotification; 131 | 132 | 133 | 134 | // 135 | // Or, instead of subscribing to notifications, you can specify a delegate and implement this method to respond to kQueue events. 136 | // Note the required statement! For speed, this class does not check to make sure the delegate implements this method. (When I say "required" I mean it!) 137 | // 138 | @class VDKQueue; 139 | @protocol VDKQueueDelegate 140 | @required 141 | 142 | -(void) VDKQueue:(VDKQueue *)queue receivedNotification:(NSString*)noteName forPath:(NSString*)fpath; 143 | 144 | @end 145 | 146 | 147 | 148 | 149 | 150 | @interface VDKQueue : NSObject 151 | { 152 | ARCCompatWeakPropertyTypeModifier id _delegate; 153 | BOOL _alwaysPostNotifications; // By default, notifications are posted only if there is no delegate set. Set this value to YES to have notes posted even when there is a delegate. 154 | 155 | @private 156 | int _coreQueueFD; // The actual kqueue ID (Unix file descriptor). 157 | NSMutableDictionary *_watchedPathEntries; // List of VDKQueuePathEntries. Keys are NSStrings of the path that each VDKQueuePathEntry is for. 158 | BOOL _keepWatcherThreadRunning; // Set to NO to cancel the thread that watches _coreQueueFD for kQueue events 159 | } 160 | 161 | 162 | // 163 | // Note: there is no need to ask whether a path is already being watched. Just add it or remove it and this class 164 | // will take action only if appropriate. (Add only if we're not already watching it, remove only if we are.) 165 | // 166 | // Warning: You must pass full, root-relative paths. Do not pass tilde-abbreviated paths or file URLs. 167 | // 168 | - (void) addPath:(NSString *)aPath; 169 | - (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags; // See note above for values to pass in "flags" 170 | 171 | - (void) removePath:(NSString *)aPath; 172 | - (void) removeAllPaths; 173 | 174 | 175 | - (NSUInteger) numberOfWatchedPaths; // Returns the number of paths that this VDKQueue instance is actively watching. 176 | 177 | 178 | 179 | @property (ARCCompatWeakPropertyModifier) id delegate; 180 | @property (assign) BOOL alwaysPostNotifications; 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /ViFinder/VDKQueue.m: -------------------------------------------------------------------------------- 1 | // VDKQueue.m 2 | // Created by Bryan D K Jones on 28 March 2012 3 | // Copyright 2013 Bryan D K Jones 4 | // 5 | // Based heavily on UKKQueue, which was created and copyrighted by Uli Kusterer on 21 Dec 2003. 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any damages 9 | // arising from the use of this software. 10 | // Permission is granted to anyone to use this software for any purpose, 11 | // including commercial applications, and to alter it and redistribute it 12 | // freely, subject to the following restrictions: 13 | // 1. The origin of this software must not be misrepresented; you must not 14 | // claim that you wrote the original software. If you use this software 15 | // in a product, an acknowledgment in the product documentation would be 16 | // appreciated but is not required. 17 | // 2. Altered source versions must be plainly marked as such, and must not be 18 | // misrepresented as being the original software. 19 | // 3. This notice may not be removed or altered from any source 20 | // distribution. 21 | 22 | #import "VDKQueue.h" 23 | #import 24 | #import 25 | #include 26 | 27 | 28 | 29 | NSString * VDKQueueRenameNotification = @"VDKQueueFileRenamedNotification"; 30 | NSString * VDKQueueWriteNotification = @"VDKQueueFileWrittenToNotification"; 31 | NSString * VDKQueueDeleteNotification = @"VDKQueueFileDeletedNotification"; 32 | NSString * VDKQueueAttributeChangeNotification = @"VDKQueueFileAttributesChangedNotification"; 33 | NSString * VDKQueueSizeIncreaseNotification = @"VDKQueueFileSizeIncreasedNotification"; 34 | NSString * VDKQueueLinkCountChangeNotification = @"VDKQueueLinkCountChangedNotification"; 35 | NSString * VDKQueueAccessRevocationNotification = @"VDKQueueAccessWasRevokedNotification"; 36 | 37 | 38 | 39 | #pragma mark - 40 | #pragma mark VDKQueuePathEntry 41 | #pragma mark - 42 | #pragma ------------------------------------------------------------------------------------------------------------------------------------------------------------ 43 | 44 | // This is a simple model class used to hold info about each path we watch. 45 | @interface VDKQueuePathEntry : NSObject 46 | { 47 | NSString* _path; 48 | int _watchedFD; 49 | u_int _subscriptionFlags; 50 | } 51 | 52 | - (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags; 53 | 54 | @property (atomic, copy) NSString *path; 55 | @property (atomic, assign) int watchedFD; 56 | @property (atomic, assign) u_int subscriptionFlags; 57 | 58 | @end 59 | 60 | @implementation VDKQueuePathEntry 61 | @synthesize path = _path, watchedFD = _watchedFD, subscriptionFlags = _subscriptionFlags; 62 | 63 | 64 | - (id) initWithPath:(NSString*)inPath andSubscriptionFlags:(u_int)flags; 65 | { 66 | self = [super init]; 67 | if (self) 68 | { 69 | _path = [inPath copy]; 70 | _watchedFD = open([_path fileSystemRepresentation], O_EVTONLY, 0); 71 | if (_watchedFD < 0) 72 | { 73 | ARCCompatAutorelease(self) 74 | return nil; 75 | } 76 | _subscriptionFlags = flags; 77 | } 78 | return self; 79 | } 80 | 81 | -(void) dealloc 82 | { 83 | ARCCompatRelease(_path) 84 | _path = nil; 85 | 86 | if (_watchedFD >= 0) close(_watchedFD); 87 | _watchedFD = -1; 88 | 89 | #if ! __has_feature(objc_arc) 90 | [super dealloc]; 91 | #endif 92 | } 93 | 94 | @end 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | #pragma mark - 107 | #pragma mark VDKQueue 108 | #pragma mark - 109 | #pragma ------------------------------------------------------------------------------------------------------------------------------------------------------------ 110 | 111 | @interface VDKQueue () 112 | - (void) watcherThread:(id)sender; 113 | @end 114 | 115 | 116 | 117 | @implementation VDKQueue 118 | @synthesize delegate = _delegate, alwaysPostNotifications = _alwaysPostNotifications; 119 | 120 | 121 | 122 | #pragma mark - 123 | #pragma mark INIT/DEALLOC 124 | 125 | - (id) init 126 | { 127 | self = [super init]; 128 | if (self) 129 | { 130 | _coreQueueFD = kqueue(); 131 | if (_coreQueueFD == -1) 132 | { 133 | ARCCompatAutorelease(self) 134 | return nil; 135 | } 136 | 137 | _alwaysPostNotifications = NO; 138 | _watchedPathEntries = [[NSMutableDictionary alloc] init]; 139 | } 140 | return self; 141 | } 142 | 143 | 144 | - (void) dealloc 145 | { 146 | // Shut down the thread that's scanning for kQueue events 147 | _keepWatcherThreadRunning = NO; 148 | 149 | // Do this to close all the open file descriptors for files we're watching 150 | [self removeAllPaths]; 151 | 152 | ARCCompatRelease(_watchedPathEntries) 153 | _watchedPathEntries = nil; 154 | 155 | #if ! __has_feature(objc_arc) 156 | [super dealloc]; 157 | #endif 158 | } 159 | 160 | 161 | 162 | 163 | 164 | #pragma mark - 165 | #pragma mark PRIVATE METHODS 166 | 167 | - (VDKQueuePathEntry *) addPathToQueue:(NSString *)path notifyingAbout:(u_int)flags 168 | { 169 | @synchronized(self) 170 | { 171 | // Are we already watching this path? 172 | VDKQueuePathEntry *pathEntry = [_watchedPathEntries objectForKey:path]; 173 | 174 | if (pathEntry) 175 | { 176 | // All flags already set? 177 | if(([pathEntry subscriptionFlags] & flags) == flags) 178 | { 179 | return ARCCompatAutoreleaseInline( ARCCompatRetainInline(pathEntry) ); 180 | } 181 | 182 | flags |= [pathEntry subscriptionFlags]; 183 | } 184 | 185 | struct timespec nullts = { 0, 0 }; 186 | struct kevent ev; 187 | 188 | if (!pathEntry) 189 | { 190 | pathEntry = ARCCompatAutoreleaseInline([[VDKQueuePathEntry alloc] initWithPath:path andSubscriptionFlags:flags]); 191 | } 192 | 193 | if (pathEntry) 194 | { 195 | EV_SET(&ev, [pathEntry watchedFD], EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, ARCCompatBridge(void*, pathEntry)); 196 | 197 | [pathEntry setSubscriptionFlags:flags]; 198 | 199 | [_watchedPathEntries setObject:pathEntry forKey:path]; 200 | kevent(_coreQueueFD, &ev, 1, NULL, 0, &nullts); 201 | 202 | // Start the thread that fetches and processes our events if it's not already running. 203 | if(!_keepWatcherThreadRunning) 204 | { 205 | _keepWatcherThreadRunning = YES; 206 | [NSThread detachNewThreadSelector:@selector(watcherThread:) toTarget:self withObject:nil]; 207 | } 208 | } 209 | 210 | return ARCCompatAutoreleaseInline( ARCCompatRetainInline(pathEntry) ); 211 | } 212 | 213 | return nil; 214 | } 215 | 216 | 217 | // 218 | // WARNING: This thread has no active autorelease pool, so if you make changes, you must manually manage 219 | // memory without relying on autorelease. Otherwise, you will leak! 220 | // 221 | - (void) watcherThread:(id)sender 222 | { 223 | int n; 224 | struct kevent ev; 225 | struct timespec timeout = { 1, 0 }; // 1 second timeout. Should be longer, but we need this thread to exit when a kqueue is dealloced, so 1 second timeout is quite a while to wait. 226 | int theFD = _coreQueueFD; // So we don't have to risk accessing iVars when the thread is terminated. 227 | 228 | NSMutableArray *notesToPost = [[NSMutableArray alloc] initWithCapacity:5]; 229 | 230 | #if DEBUG_LOG_THREAD_LIFETIME 231 | NSLog(@"watcherThread started."); 232 | #endif 233 | 234 | while(_keepWatcherThreadRunning) 235 | { 236 | @try 237 | { 238 | n = kevent(theFD, NULL, 0, &ev, 1, &timeout); 239 | if (n > 0) 240 | { 241 | //NSLog( @"KEVENT returned %d", n ); 242 | if (ev.filter == EVFILT_VNODE) 243 | { 244 | //NSLog( @"KEVENT filter is EVFILT_VNODE" ); 245 | if (ev.fflags) 246 | { 247 | //NSLog( @"KEVENT flags are set" ); 248 | 249 | // 250 | // Note: VDKQueue gets tested by thousands of CodeKit users who each watch several thousand files at once. 251 | // I was receiving about 3 EXC_BAD_ACCESS (SIGSEGV) crash reports a month that listed the 'path' objc_msgSend 252 | // as the culprit. That suggests the KEVENT is being sent back to us with a udata value that is NOT what we assigned 253 | // to the queue, though I don't know why and I don't know why it's intermittent. Regardless, I've added an extra 254 | // check here to try to eliminate this (infrequent) problem. In theory, a KEVENT that does not have a VDKQueuePathEntry 255 | // object attached as the udata parameter is not an event we registered for, so we should not be "missing" any events. In theory. 256 | // 257 | id pe = ARCCompatBridge(id, ev.udata); 258 | if (pe && [pe respondsToSelector:@selector(path)]) 259 | { 260 | NSString *fpath = ARCCompatRetainInline( ((VDKQueuePathEntry *)pe).path ); // Need to retain so it does not disappear while the block at the bottom is waiting to run on the main thread. Released in that block. 261 | if (!fpath) continue; 262 | 263 | #if !TARGET_OS_IPHONE 264 | [[NSWorkspace sharedWorkspace] noteFileSystemChanged:fpath]; 265 | #endif 266 | 267 | // Clear any old notifications 268 | [notesToPost removeAllObjects]; 269 | 270 | // Figure out which notifications we need to issue 271 | if ((ev.fflags & NOTE_RENAME) == NOTE_RENAME) 272 | { 273 | [notesToPost addObject:VDKQueueRenameNotification]; 274 | } 275 | if ((ev.fflags & NOTE_WRITE) == NOTE_WRITE) 276 | { 277 | [notesToPost addObject:VDKQueueWriteNotification]; 278 | } 279 | if ((ev.fflags & NOTE_DELETE) == NOTE_DELETE) 280 | { 281 | [notesToPost addObject:VDKQueueDeleteNotification]; 282 | } 283 | if ((ev.fflags & NOTE_ATTRIB) == NOTE_ATTRIB) 284 | { 285 | [notesToPost addObject:VDKQueueAttributeChangeNotification]; 286 | } 287 | if ((ev.fflags & NOTE_EXTEND) == NOTE_EXTEND) 288 | { 289 | [notesToPost addObject:VDKQueueSizeIncreaseNotification]; 290 | } 291 | if ((ev.fflags & NOTE_LINK) == NOTE_LINK) 292 | { 293 | [notesToPost addObject:VDKQueueLinkCountChangeNotification]; 294 | } 295 | if ((ev.fflags & NOTE_REVOKE) == NOTE_REVOKE) 296 | { 297 | [notesToPost addObject:VDKQueueAccessRevocationNotification]; 298 | } 299 | 300 | 301 | NSArray *notes = [[NSArray alloc] initWithArray:notesToPost]; // notesToPost will be changed in the next loop iteration, which will likely occur before the block below runs. 302 | 303 | 304 | // Post the notifications (or call the delegate method) on the main thread. 305 | dispatch_async(dispatch_get_main_queue(), 306 | ^{ 307 | for (NSString *note in notes) 308 | { 309 | [_delegate VDKQueue:self receivedNotification:note forPath:fpath]; 310 | 311 | if (!_delegate || _alwaysPostNotifications) 312 | { 313 | NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:fpath, @"path", nil]; 314 | #if TARGET_OS_IPHONE 315 | [[NSNotificationCenter defaultCenter] postNotificationName:note object:self userInfo:userInfoDict]; 316 | #else 317 | [[[NSWorkspace sharedWorkspace] notificationCenter] postNotificationName:note object:self userInfo:userInfoDict]; 318 | #endif 319 | ARCCompatRelease(userInfoDict) 320 | } 321 | } 322 | 323 | ARCCompatRelease(fpath) 324 | ARCCompatRelease(notes) 325 | }); 326 | } 327 | } 328 | } 329 | } 330 | } 331 | 332 | @catch (NSException *localException) 333 | { 334 | NSLog(@"Error in VDKQueue watcherThread: %@", localException); 335 | } 336 | } 337 | 338 | // Close our kqueue's file descriptor 339 | if(close(theFD) == -1) { 340 | NSLog(@"VDKQueue watcherThread: Couldn't close main kqueue (%d)", errno); 341 | } 342 | 343 | ARCCompatRelease(notesToPost) 344 | 345 | #if DEBUG_LOG_THREAD_LIFETIME 346 | NSLog(@"watcherThread finished."); 347 | #endif 348 | 349 | } 350 | 351 | 352 | 353 | 354 | 355 | 356 | #pragma mark - 357 | #pragma mark PUBLIC METHODS 358 | #pragma ----------------------------------------------------------------------------------------------------------------------------------------------------- 359 | 360 | 361 | - (void) addPath:(NSString *)aPath 362 | { 363 | if (!aPath) return; 364 | ARCCompatRetain(aPath) 365 | 366 | @synchronized(self) 367 | { 368 | VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; 369 | 370 | // Only add this path if we don't already have it. 371 | if (!entry) 372 | { 373 | entry = [self addPathToQueue:aPath notifyingAbout:VDKQueueNotifyDefault]; 374 | if (!entry) { 375 | NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); 376 | } 377 | } 378 | } 379 | 380 | ARCCompatRelease(aPath) 381 | } 382 | 383 | 384 | - (void) addPath:(NSString *)aPath notifyingAbout:(u_int)flags 385 | { 386 | if (!aPath) return; 387 | ARCCompatRetain(aPath) 388 | 389 | @synchronized(self) 390 | { 391 | VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; 392 | 393 | // Only add this path if we don't already have it. 394 | if (!entry) 395 | { 396 | entry = [self addPathToQueue:aPath notifyingAbout:flags]; 397 | if (!entry) { 398 | NSLog(@"VDKQueue tried to add the path %@ to watchedPathEntries, but the VDKQueuePathEntry was nil. \nIt's possible that the host process has hit its max open file descriptors limit.", aPath); 399 | } 400 | } 401 | } 402 | 403 | ARCCompatRelease(aPath) 404 | } 405 | 406 | 407 | - (void) removePath:(NSString *)aPath 408 | { 409 | if (!aPath) return; 410 | ARCCompatRetain(aPath) 411 | 412 | @synchronized(self) 413 | { 414 | VDKQueuePathEntry *entry = [_watchedPathEntries objectForKey:aPath]; 415 | 416 | // Remove it only if we're watching it. 417 | if (entry) { 418 | [_watchedPathEntries removeObjectForKey:aPath]; 419 | } 420 | } 421 | 422 | ARCCompatRelease(aPath) 423 | } 424 | 425 | 426 | - (void) removeAllPaths 427 | { 428 | @synchronized(self) 429 | { 430 | [_watchedPathEntries removeAllObjects]; 431 | } 432 | } 433 | 434 | 435 | - (NSUInteger) numberOfWatchedPaths 436 | { 437 | NSUInteger count; 438 | 439 | @synchronized(self) 440 | { 441 | count = [_watchedPathEntries count]; 442 | } 443 | 444 | return count; 445 | } 446 | 447 | 448 | 449 | 450 | @end 451 | 452 | -------------------------------------------------------------------------------- /ViFinder/iTermNewTab.applescript: -------------------------------------------------------------------------------- 1 | if application "iTerm" is running then 2 | tell application "iTerm" 3 | try 4 | tell the first terminal 5 | launch session "Default Session" 6 | tell the last session 7 | write text "cd %@" 8 | end tell 9 | end tell 10 | on error 11 | set myterm to (make new terminal) 12 | tell myterm 13 | launch session "Default Session" 14 | tell the last session 15 | write text "cd %@" 16 | end tell 17 | end tell 18 | end try 19 | end tell 20 | else 21 | tell application "iTerm" 22 | activate 23 | tell the first terminal 24 | tell the first session 25 | write text "cd %@" 26 | end tell 27 | end tell 28 | end tell 29 | end if 30 | -------------------------------------------------------------------------------- /ViFinder/iTermNewWindow.applescript: -------------------------------------------------------------------------------- 1 | if application "iTerm" is running then 2 | tell application "iTerm" 3 | try 4 | set myterm to (make new terminal) 5 | tell myterm 6 | launch session "Default Session" 7 | tell the last session 8 | write text "cd %@" 9 | end tell 10 | end tell 11 | on error 12 | set myterm to (make new terminal) 13 | tell myterm 14 | launch session "Default Session" 15 | tell the last session 16 | write text "cd %@" 17 | end tell 18 | end tell 19 | end try 20 | activate 21 | end tell 22 | else 23 | tell application "iTerm" 24 | activate 25 | tell the first terminal 26 | tell the first session 27 | write text "cd %@" 28 | end tell 29 | end tell 30 | end tell 31 | end if 32 | -------------------------------------------------------------------------------- /ViFinder/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ViFinder 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /ViFinderTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.xiazhiri.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ViFinderTests/ViFinderTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViFinderTests.m 3 | // ViFinderTests 4 | // 5 | // Created by liuwencai on 15/5/11. 6 | // Copyright (c) 2015年 likaci. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface ViFinderTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation ViFinderTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------