//
// P4FileItem.m
// Perforce
//
// Created by Adam Czubernat on 29.05.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "P4FileItem.h"
#import "PSFileEvents.h"
@interface P4FileItem () {
NSColor *overlay;
}
- (id)initWithURL:(NSURL *)url parentItem:(P4Item *)parent;
- (void)loadMetadata:(NSDictionary *)metadata;
- (void)propagatePathChange:(NSString *)path;
@end
@implementation P4FileItem
#pragma mark - P4Item Override
- (id)init {
NSURL *url = [NSURL fileURLWithPath:[P4Workspace sharedInstance].root];
self = [self initWithURL:url parentItem:nil];
if (self) {
flags.directory = YES;
name = @"Workspace";
remotePath = @"//";
[[P4Workspace sharedInstance] addObserver:self];
}
return self;
}
- (NSColor *)overlay {
return overlay;
}
- (BOOL)isEditable {
return YES;
}
- (id)defaultAction {
if (!flags.tracked)
return nil;
if (flags.directory) {
return [P4ItemAction
actionForItem:self name:@"Check out all files"
selector:@selector(checkout)];
} else {
return [P4ItemAction
actionForItem:self name:@"Open and Checkout"
selector:@selector(openWithCheckout)];
}
}
- (NSArray *)actions {
NSMutableArray *actions = (NSMutableArray *)[super actions];
// Override actions
return actions;
}
- (void)loadPath:(NSString *)preloadPath {
NSAssert([preloadPath hasSuffix:@"/"], @"Loading path without trailing slash");
NSString *rootPath = [[P4Workspace sharedInstance] root];
NSString *client = [[P4Workspace sharedInstance] workspace];
NSString *clientPrefix = [NSString stringWithFormat:@"//%@/", client];
NSMutableArray *items = [NSMutableArray array];
children = nil;
flags.loading = YES;
NSString *relative = [preloadPath stringByRemovingPrefix:localPath];
relative = [relative stringByRemovingSuffix:@"/"];
NSString *subPath = localPath;
NSMutableArray *subPaths = [NSMutableArray arrayWithObject:subPath];
for (NSString *component in [relative pathComponents]) {
subPath = [subPath stringByAppendingFormat:@"%@/", component];
[subPaths addObject:subPath];
}
for (NSString *subPath in subPaths) {
__block NSError *enumeratorError = nil;
NSDirectoryEnumerator *enumerator;
enumerator = [[NSFileManager defaultManager]
enumeratorAtURL:[NSURL fileURLWithPath:subPath]
includingPropertiesForKeys:@[
NSURLIsDirectoryKey,
NSURLNameKey,
NSURLLabelColorKey ]
options:(NSDirectoryEnumerationSkipsSubdirectoryDescendants |
NSDirectoryEnumerationSkipsPackageDescendants |
NSDirectoryEnumerationSkipsHiddenFiles)
errorHandler:^(NSURL *url, NSError *error) {
PSLog(@"File enumeration error: %@",
error.localizedDescription);
enumeratorError = error;
return YES;
}];
P4FileItem *subPathParent = self;
for (P4FileItem *item in items.reverseObjectEnumerator) {
if ([item.localPath isEqualToString:subPath]) {
subPathParent = item;
break;
}
}
NSMutableArray *subPathChildren = [NSMutableArray array];
for (NSURL *url in enumerator) {
P4FileItem *item = [[P4FileItem alloc] initWithURL:url parentItem:subPathParent];
item->flags.loading = YES;
[subPathChildren addObject:item];
}
if (enumeratorError) {
[self failWithError:enumeratorError];
children = nil;
return;
}
[items addObjectsFromArray:subPathChildren];
subPathParent->children = subPathChildren;
[subPathParent sortChildren];
}
[[P4Workspace sharedInstance]
listFiles:subPaths
response:^(P4Operation *operation, NSArray *response) {
if (operation.errors && ([self failWithError:operation.error], 1))
return;
NSMutableArray *remotePaths = [NSMutableArray array];
NSMutableDictionary *records = [NSMutableDictionary dictionary];
for (NSDictionary *record in response) {
NSString *remote = [record objectForKey:@"depotFile"];
NSString *local = [record objectForKey:@"clientFile"];
if (!local) {
NSString *clientPath = [record objectForKey:@"dir"];
NSString *clientRelative = [clientPath stringByRemovingPrefix:clientPrefix];
local = [NSString stringWithFormat:@"%@%@/", rootPath, clientRelative];
}
[records setObject:record forKey:local];
if (remote)
[remotePaths addObject:remote];
}
NSArray *unread = nil;
NSArray *shelved = nil;
// Get shelved files list
if (remotePaths.count)
shelved = [[P4Workspace sharedInstance] shelvedForPaths:remotePaths];
// Get unread files list
if (records.count)
unread = [[P4Workspace sharedInstance] unreadForPaths:[records allKeys]];
for (P4FileItem *item in items) {
NSDictionary *record = [records objectForKey:item->localPath];
[item loadMetadata:record];
item->flags.loading = NO;
item->flags.shelved = [shelved containsObject:item->remotePath];
item->flags.unread = [unread containsObject:item->localPath];
}
[self finishLoading];
}];
}
#pragma mark - Private
- (id)initWithURL:(NSURL *)url parentItem:(P4Item *)parentItem {
if (self = [super init]) {
parent = parentItem;
// Get directory info
NSNumber *dir = nil;
[url getResourceValue:&dir forKey:NSURLIsDirectoryKey error:NULL];
flags.directory = dir.boolValue;
NSString *resourceName;
[url getResourceValue:&resourceName forKey:NSURLNameKey error:NULL];
name = resourceName;
NSColor *color;
[url getResourceValue:&color forKey:NSURLLabelColorKey error:NULL];
overlay = color;
localPath = dir.boolValue ? [url.path stringByAppendingString:@"/"] : url.path;
}
return self;
}
- (void)loadMetadata:(NSDictionary *)metadataDictionary {
metadata = metadataDictionary;
[self refreshTags];
remotePath = nil;
status = nil;
flags.tracked = NO;
if (!metadata)
return;
if (flags.directory) {
NSString *client = [[P4Workspace sharedInstance] workspace];
NSString *clientPrefix = [NSString stringWithFormat:@"//%@/", client];
remotePath = [metadata objectForKey:@"dir"];
remotePath = [remotePath stringByRemovingPrefix:clientPrefix];
remotePath = [NSString stringWithFormat:@"//%@/", remotePath];
flags.tracked = remotePath != nil;
} else {
remotePath = [metadata objectForKey:@"depotFile"];
// Set metadata
status = [metadata objectForKey:@"action"];
NSString *user = [metadata objectForKey:@"otherOpen0"];
NSDictionary *info = [[P4Workspace sharedInstance] userInfo:user];
statusOwner = [info objectForKey:@"Email"] ?: user;
flags.tracked = [metadata objectForKey:@"isMapped"] != nil;
}
}
- (void)propagatePathChange:(NSString *)newPath {
if ([newPath isEqualToString:localPath])
return;
name = newPath.lastPathComponent;
localPath = newPath;
if (![self isDirectory])
return;
// Traversing
NSMutableArray *items = [NSMutableArray array];
if (children.count)
[items addObjectsFromArray:children];
int count = 0;
while (items.count) {
P4FileItem *item = [items lastObject];
[items removeLastObject];
item->localPath = [item.parent->localPath stringByAppendingPath:item->name];
if (item->flags.directory)
[item->localPath stringByAppendingString:@"/"];
PSLog(@"%@", item->localPath);
if (item->children.count)
[items addObjectsFromArray:item->children];
count++;
}
}
#pragma mark P4Workspace Events
- (void)fileCreated:(NSString *)filePath {
P4Item *item = [self cachedItemForPath:filePath];
if (item) {
PSLog(@"WARNING! Creating duplicate %@", filePath);
[self fileModified:filePath];
return;
}
NSString *parentPath = [filePath stringByDeletingPath];
P4Item *parentItem = [self cachedItemForPath:parentPath];
if (!parentItem || !parentItem->children) // Not loaded yet
return;
// Update children by adding new item
P4Item *newItem = [[P4FileItem alloc]
initWithURL:[NSURL fileURLWithPath:filePath]
parentItem:parentItem];
// Add item to parent
NSMutableArray *array = [NSMutableArray arrayWithArray:parentItem->children];
[array addObject:newItem];
// Sort files alphanumerically
parentItem->children = array;
[parentItem sortChildren];
[parentItem finishLoading];
}
- (void)fileRemoved:(NSString *)filePath {
P4Item *item = [self cachedItemForPath:filePath];
P4Item *parentItem = (P4Item *)item.parent;
if (item.isUnread)
[item markAsRead];
if (!parentItem || !parentItem->children || !item)
return;
NSMutableArray *array = [NSMutableArray arrayWithArray:parentItem->children];
[array removeObject:item];
parentItem->children = array;
[parentItem finishLoading];
[item invalidate];
}
- (void)fileMoved:(NSString *)oldPath toPath:(NSString *)newPath {
P4Item *oldItem = [self cachedItemForPath:oldPath];
P4Item *oldParentItem = (P4Item *)oldItem.parent;
if (oldItem.isUnread)
[oldItem markAsRead];
// Remove old item from displaying
if (oldParentItem && oldItem) {
NSMutableArray *array = [NSMutableArray arrayWithArray:oldParentItem->children];
[array removeObject:oldItem];
oldParentItem->children = array;
}
[oldParentItem finishLoading];
[oldItem invalidate];
// Create new item for display
NSString *newParentPath = [newPath stringByDeletingPath];
P4Item *newParentItem = [self cachedItemForPath:newParentPath];
if (!newParentItem || !newParentItem->children)
return;
P4Item *newItem = [[P4FileItem alloc]
initWithURL:[NSURL fileURLWithPath:newPath]
parentItem:newParentItem];
// Add item to parent
NSMutableArray *array = [NSMutableArray arrayWithArray:newParentItem->children];
[array addObject:newItem];
// Sort files alphanumerically
newParentItem->children = array;
[newParentItem sortChildren];
[newParentItem finishLoading];
}
- (void)fileRenamed:(NSString *)oldPath toPath:(NSString *)newPath {
P4Item *oldItem = [self cachedItemForPath:oldPath];
if (oldItem.isUnread)
[oldItem markAsRead];
[(P4FileItem *)oldItem propagatePathChange:newPath];
[oldItem finishLoading];
}
- (void)fileModified:(NSString *)path {
P4FileItem *item = (P4FileItem *)[self cachedItemForPath:path];
if (!item)
return;
// Update overlay
NSURL *url = [NSURL fileURLWithPath:path];
NSColor *color;
[url getResourceValue:&color forKey:NSURLLabelColorKey error:NULL];
item->overlay = color;
[item finishLoading];
}
- (void)file:(NSString *)filePath updated:(NSDictionary *)info {
BOOL remote = [filePath hasPrefix:@"//"];
NSString *parentPath = remote ? [remotePath substringFromIndex:1] : localPath;
if ([filePath hasSuffix:@"/"])
filePath = [filePath substringToIndex:filePath.length-1];
if ([filePath isEqualToString:parentPath])
return;
NSString *relative = [filePath substringFromIndex:parentPath.length];
NSArray *components = [relative pathComponents];
// Traversing
P4Item *item = self;
for (NSString *component in components) {
if (!item->children.count) // Not loaded
return;
for (P4Item *child in item->children) {
if ([child->name isEqualToString:component]) {
item = child;
if (item.isDirectory && !item.isUnread) {
if (!item->flags.tracked) {
// Directory wasn't tracked
NSString *relativePath = [filePath stringByRemovingPrefix:item.localPath];
NSString *depotPath = [info objectForKey:@"depotFile"];
depotPath = [depotPath stringByRemovingSuffix:relativePath];
item->remotePath = depotPath;
item->flags.tracked = depotPath.length > 0;
}
// Mark as unread
item->flags.unread = YES;
[item finishLoading];
}
break;
}
}
}
NSString *itemPath = remote ? item->remotePath : item->localPath;
if ([itemPath isEqualToString:filePath]) {
if (!item->metadata) {
item->remotePath = [info objectForKey:@"depotFile"];
item->metadata = info;
[item refreshTags];
}
item->flags.tracked = YES;
item->flags.unread = YES;
[item finishLoading];
}
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/Items/P4FileItem.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|