22 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
23 |
*/ |
*/ |
24 |
|
|
|
|
|
25 |
#include <Foundation/Foundation.h> |
#include <Foundation/Foundation.h> |
26 |
#include <AppKit/AppKit.h> |
#include <AppKit/AppKit.h> |
27 |
#include "GWLib.h" |
#include "GWLib.h" |
28 |
|
#include "GWFunctions.h" |
29 |
|
#include "FSWatcher.h" |
30 |
#include "GWProtocol.h" |
#include "GWProtocol.h" |
31 |
#include "GNUstep.h" |
#include "GNUstep.h" |
32 |
#ifndef GNUSTEP |
#ifndef GNUSTEP |
33 |
#include "OSXCompatibility.h" |
#include "OSXCompatibility.h" |
34 |
#endif |
#endif |
35 |
|
|
|
static id gwapp = nil; |
|
|
static NSString *gwName = @"GWorkspace"; |
|
|
|
|
36 |
#define CHECKGW \ |
#define CHECKGW \ |
37 |
if (gwapp == nil) \ |
if (gwapp == nil) \ |
38 |
gwapp = (id <GWProtocol>)[[GWLib class] gworkspaceApplication]; \ |
gwapp = (id <GWProtocol>)[[GWLib class] gworkspaceApplication]; \ |
43 |
gwapp = (id <GWProtocol>)[[GWLib class] gworkspaceApplication]; \ |
gwapp = (id <GWProtocol>)[[GWLib class] gworkspaceApplication]; \ |
44 |
if (gwapp == nil) return x |
if (gwapp == nil) return x |
45 |
|
|
46 |
|
#ifndef CACHED_MAX |
47 |
|
#define CACHED_MAX 20; |
48 |
|
#endif |
49 |
|
|
50 |
|
#ifndef byname |
51 |
|
#define byname 0 |
52 |
|
#define bykind 1 |
53 |
|
#define bydate 2 |
54 |
|
#define bysize 3 |
55 |
|
#define byowner 4 |
56 |
|
#endif |
57 |
|
|
58 |
|
id instance = nil; |
59 |
|
static id gwapp = nil; |
60 |
|
static NSString *gwName = @"GWorkspace"; |
61 |
|
|
62 |
|
@interface GWLib (PrivateMethods) |
63 |
|
|
64 |
|
- (void)setCachedMax:(int)cmax; |
65 |
|
|
66 |
|
- (NSMutableDictionary *)cachedRepresentationForPath:(NSString *)path; |
67 |
|
|
68 |
|
- (void)addCachedRepresentation:(NSDictionary *)contentsDict |
69 |
|
ofDirectory:(NSString *)path; |
70 |
|
|
71 |
|
- (void)removeCachedRepresentationForPath:(NSString *)path; |
72 |
|
|
73 |
|
- (void)removeOlderCache; |
74 |
|
|
75 |
|
- (void)clearCache; |
76 |
|
|
77 |
|
- (NSArray *)sortedDirectoryContentsAtPath:(NSString *)path; |
78 |
|
|
79 |
|
- (BOOL)isLockedPath:(NSString *)path; |
80 |
|
|
81 |
|
- (void)addWatcherForPath:(NSString *)path; |
82 |
|
|
83 |
|
- (void)removeWatcherForPath:(NSString *)path; |
84 |
|
|
85 |
|
- (void)watcherTimeOut:(id)sender; |
86 |
|
|
87 |
|
- (void)removeWatcher:(FSWatcher *)awatcher; |
88 |
|
|
89 |
|
- (FSWatcher *)watcherForPath:(NSString *)path; |
90 |
|
|
91 |
|
- (NSTimer *)timerForPath:(NSString *)path; |
92 |
|
|
93 |
|
- (int)sortTypeForDirectoryAtPath:(NSString *)aPath; |
94 |
|
|
95 |
|
- (void)setDefSortType:(int)type; |
96 |
|
|
97 |
|
@end |
98 |
|
|
99 |
|
@implementation GWLib (PrivateMethods) |
100 |
|
|
101 |
|
- (void)setCachedMax:(int)cmax |
102 |
|
{ |
103 |
|
cachedMax = cmax; |
104 |
|
} |
105 |
|
|
106 |
|
- (NSMutableDictionary *)cachedRepresentationForPath:(NSString *)path |
107 |
|
{ |
108 |
|
NSMutableDictionary *contents = [cachedContents objectForKey: path]; |
109 |
|
|
110 |
|
if (contents) { |
111 |
|
NSDate *modDate = [contents objectForKey: @"moddate"]; |
112 |
|
NSDictionary *attributes = [fm fileAttributesAtPath: path |
113 |
|
traverseLink: YES]; |
114 |
|
NSDate *date = [attributes fileModificationDate]; |
115 |
|
|
116 |
|
if ([modDate isEqualToDate: date]) { |
117 |
|
return contents; |
118 |
|
} else { |
119 |
|
[cachedContents removeObjectForKey: path]; |
120 |
|
} |
121 |
|
} |
122 |
|
|
123 |
|
return nil; |
124 |
|
} |
125 |
|
|
126 |
|
- (void)addCachedRepresentation:(NSDictionary *)contentsDict |
127 |
|
ofDirectory:(NSString *)path |
128 |
|
{ |
129 |
|
[cachedContents setObject: contentsDict forKey: path]; |
130 |
|
|
131 |
|
if ([watchedPaths containsObject: path] == NO) { |
132 |
|
[watchedPaths addObject: path]; |
133 |
|
[self addWatcherForPath: path]; |
134 |
|
} |
135 |
|
} |
136 |
|
|
137 |
|
- (void)removeCachedRepresentationForPath:(NSString *)path |
138 |
|
{ |
139 |
|
[cachedContents removeObjectForKey: path]; |
140 |
|
|
141 |
|
if ([watchedPaths containsObject: path]) { |
142 |
|
[watchedPaths removeObject: path]; |
143 |
|
[self removeWatcherForPath: path]; |
144 |
|
} |
145 |
|
} |
146 |
|
|
147 |
|
- (void)removeOlderCache |
148 |
|
{ |
149 |
|
NSArray *keys = [cachedContents allKeys]; |
150 |
|
NSDate *date = [NSDate date]; |
151 |
|
NSString *removeKey = nil; |
152 |
|
int i; |
153 |
|
|
154 |
|
if ([keys count]) { |
155 |
|
for (i = 0; i < [keys count]; i++) { |
156 |
|
NSString *key = [keys objectAtIndex: i]; |
157 |
|
NSDate *stamp = [[cachedContents objectForKey: key] objectForKey: @"datestamp"]; |
158 |
|
NSDate *d = [date earlierDate: stamp]; |
159 |
|
|
160 |
|
if ([date isEqualToDate: d] == NO) { |
161 |
|
date = d; |
162 |
|
removeKey = key; |
163 |
|
} |
164 |
|
} |
165 |
|
|
166 |
|
if (removeKey == nil) { |
167 |
|
removeKey = [keys objectAtIndex: 0]; |
168 |
|
} |
169 |
|
|
170 |
|
[cachedContents removeObjectForKey: removeKey]; |
171 |
|
|
172 |
|
if ([watchedPaths containsObject: removeKey]) { |
173 |
|
[watchedPaths removeObject: removeKey]; |
174 |
|
[self removeWatcherForPath: removeKey]; |
175 |
|
} |
176 |
|
} |
177 |
|
} |
178 |
|
|
179 |
|
- (void)clearCache |
180 |
|
{ |
181 |
|
NSArray *keys = [cachedContents allKeys]; |
182 |
|
int i; |
183 |
|
|
184 |
|
for (i = 0; i < [keys count]; i++) { |
185 |
|
[self removeWatcherForPath: [keys objectAtIndex: i]]; |
186 |
|
} |
187 |
|
|
188 |
|
DESTROY (cachedContents); |
189 |
|
cachedContents = [NSMutableDictionary new]; |
190 |
|
} |
191 |
|
|
192 |
|
- (NSArray *)sortedDirectoryContentsAtPath:(NSString *)path |
193 |
|
{ |
194 |
|
NSMutableDictionary *contentsDict = [self cachedRepresentationForPath: path]; |
195 |
|
|
196 |
|
if (contentsDict) { |
197 |
|
return [contentsDict objectForKey: @"files"]; |
198 |
|
|
199 |
|
} else { |
200 |
|
NSArray *files = [fm directoryContentsAtPath: path]; |
201 |
|
int stype = [self sortTypeForDirectoryAtPath: path]; |
202 |
|
int count = [files count]; |
203 |
|
NSMutableArray *paths = [NSMutableArray arrayWithCapacity: count]; |
204 |
|
NSMutableArray *sortfiles = [NSMutableArray arrayWithCapacity: count]; |
205 |
|
NSArray *sortPaths = nil; |
206 |
|
NSDictionary *attributes = nil; |
207 |
|
NSDate *date = nil; |
208 |
|
SEL appendPathCompSel = @selector(stringByAppendingPathComponent:); |
209 |
|
IMP appendPathComp = [[NSString class] instanceMethodForSelector: appendPathCompSel]; |
210 |
|
SEL lastPathCompSel = @selector(lastPathComponent); |
211 |
|
IMP lastPathComp = [[NSString class] instanceMethodForSelector: lastPathCompSel]; |
212 |
|
int i; |
213 |
|
|
214 |
|
for (i = 0; i < count; i++) { |
215 |
|
NSString *s = (*appendPathComp)(path, appendPathCompSel, [files objectAtIndex: i]); |
216 |
|
[paths addObject: s]; |
217 |
|
} |
218 |
|
|
219 |
|
sortPaths = [paths sortedArrayUsingFunction: (int (*)(id, id, void*))comparePaths |
220 |
|
context: (void *)stype]; |
221 |
|
|
222 |
|
for (i = 0; i < count; i++) { |
223 |
|
NSString *s = (*lastPathComp)([sortPaths objectAtIndex: i], lastPathCompSel); |
224 |
|
[sortfiles addObject: s]; |
225 |
|
} |
226 |
|
|
227 |
|
contentsDict = [NSMutableDictionary dictionary]; |
228 |
|
[contentsDict setObject: [NSDate date] forKey: @"datestamp"]; |
229 |
|
attributes = [fm fileAttributesAtPath: path traverseLink: YES]; |
230 |
|
date = [attributes fileModificationDate]; |
231 |
|
[contentsDict setObject: date forKey: @"moddate"]; |
232 |
|
[contentsDict setObject: sortfiles forKey: @"files"]; |
233 |
|
|
234 |
|
if ([cachedContents count] >= cachedMax) { |
235 |
|
[self removeOlderCache]; |
236 |
|
} |
237 |
|
|
238 |
|
[self addCachedRepresentation: contentsDict ofDirectory: path]; |
239 |
|
|
240 |
|
return sortfiles; |
241 |
|
} |
242 |
|
|
243 |
|
return nil; |
244 |
|
} |
245 |
|
|
246 |
|
- (BOOL)isLockedPath:(NSString *)path |
247 |
|
{ |
248 |
|
int i; |
249 |
|
|
250 |
|
if ([lockedPaths containsObject: path]) { |
251 |
|
return YES; |
252 |
|
} |
253 |
|
|
254 |
|
for (i = 0; i < [lockedPaths count]; i++) { |
255 |
|
NSString *lpath = [lockedPaths objectAtIndex: i]; |
256 |
|
|
257 |
|
if (subPathOfPath(lpath, path)) { |
258 |
|
return YES; |
259 |
|
} |
260 |
|
} |
261 |
|
|
262 |
|
return NO; |
263 |
|
} |
264 |
|
|
265 |
|
- (void)addWatcherForPath:(NSString *)path |
266 |
|
{ |
267 |
|
FSWatcher *watcher = [self watcherForPath: path]; |
268 |
|
|
269 |
|
if ((watcher != nil) && ([watcher isOld] == NO)) { |
270 |
|
[watcher addListener]; |
271 |
|
return; |
272 |
|
} else { |
273 |
|
BOOL isdir; |
274 |
|
|
275 |
|
if ([fm fileExistsAtPath: path isDirectory: &isdir] && isdir) { |
276 |
|
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 |
277 |
|
target: self selector: @selector(watcherTimeOut:) |
278 |
|
userInfo: path repeats: YES]; |
279 |
|
[watchTimers addObject: timer]; |
280 |
|
|
281 |
|
watcher = [[FSWatcher alloc] initForWatchAtPath: path]; |
282 |
|
[watchers addObject: watcher]; |
283 |
|
RELEASE (watcher); |
284 |
|
} |
285 |
|
} |
286 |
|
} |
287 |
|
|
288 |
|
- (void)removeWatcherForPath:(NSString *)path |
289 |
|
{ |
290 |
|
FSWatcher *watcher = [self watcherForPath: path]; |
291 |
|
|
292 |
|
if ((watcher != nil) && ([watcher isOld] == NO)) { |
293 |
|
[watcher removeListener]; |
294 |
|
} |
295 |
|
} |
296 |
|
|
297 |
|
- (void)watcherTimeOut:(id)sender |
298 |
|
{ |
299 |
|
NSString *watchedPath = (NSString *)[sender userInfo]; |
300 |
|
|
301 |
|
if (watchedPath != nil) { |
302 |
|
FSWatcher *watcher = [self watcherForPath: watchedPath]; |
303 |
|
|
304 |
|
if (watcher != nil) { |
305 |
|
if ([watcher isOld]) { |
306 |
|
[self removeWatcher: watcher]; |
307 |
|
} else { |
308 |
|
[watcher watchFile]; |
309 |
|
} |
310 |
|
} |
311 |
|
} |
312 |
|
} |
313 |
|
|
314 |
|
- (void)removeWatcher:(FSWatcher *)awatcher |
315 |
|
{ |
316 |
|
NSString *watchedPath = [awatcher watchedPath]; |
317 |
|
NSTimer *timer = [self timerForPath: watchedPath]; |
318 |
|
|
319 |
|
if (timer && [timer isValid]) { |
320 |
|
[timer invalidate]; |
321 |
|
[watchTimers removeObject: timer]; |
322 |
|
} |
323 |
|
|
324 |
|
[watchers removeObject: awatcher]; |
325 |
|
} |
326 |
|
|
327 |
|
- (FSWatcher *)watcherForPath:(NSString *)path |
328 |
|
{ |
329 |
|
int i; |
330 |
|
|
331 |
|
for (i = 0; i < [watchers count]; i++) { |
332 |
|
FSWatcher *watcher = [watchers objectAtIndex: i]; |
333 |
|
if ([watcher isWathcingPath: path]) { |
334 |
|
return watcher; |
335 |
|
} |
336 |
|
} |
337 |
|
|
338 |
|
return nil; |
339 |
|
} |
340 |
|
|
341 |
|
- (NSTimer *)timerForPath:(NSString *)path |
342 |
|
{ |
343 |
|
int i; |
344 |
|
|
345 |
|
for (i = 0; i < [watchTimers count]; i++) { |
346 |
|
NSTimer *t = [watchTimers objectAtIndex: i]; |
347 |
|
|
348 |
|
if (([t isValid]) && ([(NSString *)[t userInfo] isEqual: path])) { |
349 |
|
return t; |
350 |
|
} |
351 |
|
} |
352 |
|
|
353 |
|
return nil; |
354 |
|
} |
355 |
|
|
356 |
|
- (int)sortTypeForDirectoryAtPath:(NSString *)aPath |
357 |
|
{ |
358 |
|
if ([fm isWritableFileAtPath: aPath]) { |
359 |
|
NSString *dictPath = [aPath stringByAppendingPathComponent: @".gwsort"]; |
360 |
|
|
361 |
|
if ([fm fileExistsAtPath: dictPath]) { |
362 |
|
NSDictionary *sortDict = [NSDictionary dictionaryWithContentsOfFile: dictPath]; |
363 |
|
|
364 |
|
if (sortDict) { |
365 |
|
return [[sortDict objectForKey: @"sort"] intValue]; |
366 |
|
} |
367 |
|
} |
368 |
|
} |
369 |
|
|
370 |
|
return defSortType; |
371 |
|
} |
372 |
|
|
373 |
|
- (void)setDefSortType:(int)type |
374 |
|
{ |
375 |
|
defSortType = type; |
376 |
|
} |
377 |
|
|
378 |
|
@end |
379 |
|
|
380 |
|
|
381 |
@implementation GWLib |
@implementation GWLib |
382 |
|
|
383 |
|
+ (GWLib *)instance |
384 |
|
{ |
385 |
|
if (instance == nil) { |
386 |
|
instance = [[GWLib alloc] init]; |
387 |
|
} |
388 |
|
return instance; |
389 |
|
} |
390 |
|
|
391 |
|
- (void)dealloc |
392 |
|
{ |
393 |
|
[nc removeObserver: self]; |
394 |
|
|
395 |
|
RELEASE (cachedContents); |
396 |
|
RELEASE (watchers); |
397 |
|
RELEASE (watchTimers); |
398 |
|
RELEASE (watchedPaths); |
399 |
|
RELEASE (lockedPaths); |
400 |
|
RELEASE (tumbsCache); |
401 |
|
RELEASE (thumbnailDir); |
402 |
|
|
403 |
|
[super dealloc]; |
404 |
|
} |
405 |
|
|
406 |
|
- (id)init |
407 |
|
{ |
408 |
|
self = [super init]; |
409 |
|
|
410 |
|
if (self) { |
411 |
|
cachedContents = [NSMutableDictionary new]; |
412 |
|
cachedMax = CACHED_MAX; |
413 |
|
defSortType = byname; |
414 |
|
|
415 |
|
watchers = [NSMutableArray new]; |
416 |
|
watchTimers = [NSMutableArray new]; |
417 |
|
watchedPaths = [NSMutableArray new]; |
418 |
|
|
419 |
|
lockedPaths = [NSMutableArray new]; |
420 |
|
|
421 |
|
tumbsCache = [NSMutableDictionary new]; |
422 |
|
thumbnailDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; |
423 |
|
thumbnailDir = [thumbnailDir stringByAppendingPathComponent: @"Thumbnails"]; |
424 |
|
RETAIN (thumbnailDir); |
425 |
|
usesThumbnails = NO; |
426 |
|
|
427 |
|
fm = [NSFileManager defaultManager]; |
428 |
|
ws = [NSWorkspace sharedWorkspace]; |
429 |
|
nc = [NSNotificationCenter defaultCenter]; |
430 |
|
} |
431 |
|
|
432 |
|
return self; |
433 |
|
} |
434 |
|
|
435 |
|
+ (void)setCachedMax:(int)cmax |
436 |
|
{ |
437 |
|
[[self instance] setCachedMax: cmax]; |
438 |
|
} |
439 |
|
|
440 |
|
+ (void)setDefSortType:(int)type |
441 |
|
{ |
442 |
|
[[self instance] setDefSortType: type]; |
443 |
|
} |
444 |
|
|
445 |
|
+ (BOOL)isLockedPath:(NSString *)path |
446 |
|
{ |
447 |
|
return [[self instance] isLockedPath: path]; |
448 |
|
} |
449 |
|
|
450 |
|
|
451 |
+ (id)gworkspaceApplication |
+ (id)gworkspaceApplication |
452 |
{ |
{ |
453 |
if (gwapp == nil) { |
if (gwapp == nil) { |