[File Tree] Significantly improve the watcher

- Switch to fine grained folder and file watching responses
- Navigate the PathNode tree using a fast dictionary of path components
- Quickly refresh the file tree by locating parent nodes to refresh

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
main
Christopher Snowhill 2022-07-06 22:39:47 -07:00
parent 1c3717eb84
commit da053d7282
6 changed files with 64 additions and 20 deletions

View File

@ -130,34 +130,40 @@ static NSURL *defaultMusicDirectory(void) {
withString:@"" withString:@""
options:NSAnchoredSearch options:NSAnchoredSearch
range:NSMakeRange(0, [path length])] stringByStandardizingPath]; range:NSMakeRange(0, [path length])] stringByStandardizingPath];
if([relativePath isEqualToString:[[self rootURL] path]])
relativePath = @"";
PathNode *node = rootNode; PathNode *node = rootNode;
DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path); DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path);
for(NSString *c in [relativePath pathComponents]) { for(NSString *c in [relativePath pathComponents]) {
DLog(@"COMPONENT: %@", c); DLog(@"COMPONENT: %@", c);
BOOL found = NO; PathNode *subnode = [[node subpathsLookup] objectForKey:c];
for(PathNode *subnode in [node subpaths]) { if(!subnode) return nil;
if([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) {
node = subnode; node = subnode;
found = YES;
}
}
if(!found) {
DLog(@"Not found!");
return nil;
}
} }
return node; return node;
} }
- (void)pathDidChange:(NSString *)path { - (void)pathDidChange:(NSString *)path flags:(FSEventStreamEventFlags)flags {
DLog(@"PATH DID CHANGE: %@", path); DLog(@"PATH DID CHANGE: %@", path);
// Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node] // Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node]
PathNode *node = [self nodeForPath:path]; PathNode *node;
do {
node = [self nodeForPath:path];
path = [path stringByDeletingLastPathComponent];
if(!path || [path length] < 2) return;
} while(!node);
if(flags & kFSEventStreamEventFlagItemRemoved) {
DLog(@"Removing node: %@", node);
PathNode *parentNode = [self nodeForPath:path];
[parentNode updatePath];
[self reloadPathNode:parentNode];
} else {
DLog(@"NODE IS: %@", node); DLog(@"NODE IS: %@", node);
[node updatePath]; [node updatePath];
[self reloadPathNode:node]; [self reloadPathNode:node];
}
} }
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {

View File

@ -16,9 +16,13 @@
NSURL *url; NSURL *url;
NSString *display; // The pretty path to display. NSString *display; // The pretty path to display.
NSString *lastPathComponent;
NSImage *icon; NSImage *icon;
NSArray *subpaths; NSArray *subpaths;
NSMutableDictionary *subpathsLookup;
} }
- (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u; - (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u;
@ -34,6 +38,12 @@
- (NSString *)display; - (NSString *)display;
- (void)setDisplay:(NSString *)s; - (void)setDisplay:(NSString *)s;
- (NSString *)lastPathComponent;
- (void)setLastPathComponent:(NSString *)s;
- (NSDictionary *)subpathsLookup;
- (void)setSubpathsLookup:(NSMutableDictionary *)d;
- (NSImage *)icon; - (NSImage *)icon;
- (BOOL)isLeaf; - (BOOL)isLeaf;

View File

@ -60,6 +60,8 @@ NSURL *resolveAliases(NSURL *url) {
display = [[NSFileManager defaultManager] displayNameAtPath:[u path]]; display = [[NSFileManager defaultManager] displayNameAtPath:[u path]];
lastPathComponent = [[u path] lastPathComponent];
icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]]; icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
[icon setSize:NSMakeSize(16.0, 16.0)]; [icon setSize:NSMakeSize(16.0, 16.0)];
@ -134,6 +136,19 @@ NSURL *resolveAliases(NSURL *url) {
- (void)setSubpaths:(NSArray *)s { - (void)setSubpaths:(NSArray *)s {
subpaths = s; subpaths = s;
subpathsLookup = [[NSMutableDictionary alloc] init];
for(PathNode *node in s) {
[subpathsLookup setObject:node forKey:node.lastPathComponent];
}
}
- (NSDictionary *)subpathsLookup {
return subpathsLookup;
}
- (void)setSubpathsLookup:(NSMutableDictionary *)d {
subpathsLookup = d;
} }
- (BOOL)isLeaf { - (BOOL)isLeaf {
@ -152,4 +167,12 @@ NSURL *resolveAliases(NSURL *url) {
return icon; return icon;
} }
- (NSString *)lastPathComponent {
return lastPathComponent;
}
- (void)setLastPathComponent:(NSString *)s {
lastPathComponent = s;
}
@end @end

View File

@ -25,6 +25,6 @@
@protocol PathWatcherDelegate @protocol PathWatcherDelegate
- (void)pathDidChange:(NSString *)path; - (void)pathDidChange:(NSString *)path flags:(FSEventStreamEventFlags)flags;
@end @end

View File

@ -22,7 +22,7 @@ const FSEventStreamEventId eventIds[]) {
printf("Callback called\n"); printf("Callback called\n");
for(i = 0; i < numEvents; i++) { for(i = 0; i < numEvents; i++) {
NSString *pathString = [[NSString alloc] initWithUTF8String:paths[i]]; NSString *pathString = [[NSString alloc] initWithUTF8String:paths[i]];
[[pathWatcher delegate] pathDidChange:pathString]; [[pathWatcher delegate] pathDidChange:pathString flags:eventFlags[i]];
} }
} }
@ -62,7 +62,7 @@ const FSEventStreamEventId eventIds[]) {
(__bridge CFArrayRef)pathsToWatch, (__bridge CFArrayRef)pathsToWatch,
kFSEventStreamEventIdSinceNow, // Or a previous event ID kFSEventStreamEventIdSinceNow, // Or a previous event ID
1.0, // latency in seconds 1.0, // latency in seconds
kFSEventStreamCreateFlagNone // Watch this and all its subdirectories kFSEventStreamCreateFlagFileEvents // Watch this and all its subdirectories
); );
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);

View File

@ -41,6 +41,11 @@
- (void)setSubpaths:(id)s { - (void)setSubpaths:(id)s {
subpaths = s; subpaths = s;
subpathsLookup = [[NSMutableDictionary alloc] init];
for(PathNode *node in s) {
[subpathsLookup setObject:node forKey:node.lastPathComponent];
}
} }
- (unsigned int)countOfSubpaths { - (unsigned int)countOfSubpaths {