├── .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 |
4 |
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 | 
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 |
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 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
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 |
857 |
860 |
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 |
--------------------------------------------------------------------------------