/*
Copyright (C) 2002-2003, Jeffrey D. Argast.
The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose. This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.
Permission is hereby granted to use, copy, modify, and distribute this
software or portions thereof for any purpose, without fee, subject to these
conditions:
(1) If any part of the source code is distributed, then this
statement must be included, with this copyright and no-warranty notice
unaltered.
(2) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.
*/
#import "PerforceFile.h"
#import "IconController.h"
#import "IconDefs.h"
#import "PerforceAction.h"
#import "PerforceActionFstat.h"
#import "PerforceActionFilelog.h"
#import "PerforceActionPrint.h"
#import "PerforceActionDiff.h"
#import "AppUtils.h"
#import "AppDefaults.h"
#import "MessageDefs.h"
#import "RevisionsController.h"
enum
{
kIsHeadRevision = 0,
kIsOldRevision = 1,
kIsMissingRevision = 2
};
static NSString* kFstatPrefixTag = @"... ";
static NSString* kIndentTag = @"... ... ";
static NSString* kDepotFileTag = @"depotFile";
static NSString* kClientFileTag = @"clientFile";
static NSString* kHeadActionTag = @"headAction";
static NSString* kHeadTypeTag = @"headType";
static NSString* kHeadRevTag = @"headRev";
static NSString* kHeadChangeTag = @"headChange";
static NSString* kHaveRevTag = @"haveRev";
static NSString* kActionTag = @"action";
static NSString* kChangeTag = @"change";
static NSString* kUnresolvedTag = @"unresolved";
static NSString* kOtherOpenTag = @"otherOpen";
static NSString* kOtherOpenSpaceTag = @"otherOpen ";
static NSString* kOtherLockTag = @"otherLock";
static NSString* kOurLockTag = @"ourLock";
static NSString* kOtherActionTag = @"otherAction";
static NSString* kDeleteStatus_Have = @"<-deleted->";
static NSString* kDeleteStatus_Old = @"<-head rev deleted->";
static NSString* kDeleteAction = @"delete";
static NSString* kAddAction = @"add";
static NSString* kEditAction = @"edit";
static NSString* kSpaceTag = @" ";
static NSString* kNewlineTag = @"\n";
static NSString* kZeroRev = @"0";
static NSString* kEmptyString = @"";
@interface PerforceFile (Private)
- (void) releaseData;
@end
@implementation PerforceFile
+ (PerforceFile*) fileWithString: (NSString*) string
{
return [[[PerforceFile alloc] initWithString: string] autorelease];
}
- (id) initWithString: (NSString*) string
{
NSArray* split = [string componentsSeparatedByString: kNewlineTag];
NSMutableDictionary* fileDictionary = [NSMutableDictionary dictionaryWithCapacity:[split count]];
NSMutableArray* otherOpenArray = [NSMutableArray array];
NSMutableArray* otherActionArray = [NSMutableArray array];
NSString* keyString;
NSString* valueString;
NSString* s;
NSEnumerator* e = [split objectEnumerator];
while ( s = [e nextObject] )
{
NSScanner* scanner = [NSScanner scannerWithString: s];
// First check for the indent
if ( [scanner scanString:kIndentTag intoString:nil] )
{
// we only care about otherOpen# where # is one or more digits
if ( ![scanner scanString:kOtherOpenSpaceTag intoString:nil] )
{
NSMutableArray* targetArray = otherOpenArray;
if ( [scanner scanString:kOtherActionTag intoString:nil] )
{
targetArray = otherActionArray;
}
[scanner scanUpToString:kSpaceTag intoString:nil];
[scanner scanString:kSpaceTag intoString:nil];
[scanner scanUpToString:kNewlineTag intoString:&valueString];
[targetArray addObject:valueString];
}
}
else if ( [scanner scanString:kFstatPrefixTag intoString:nil] )
{
[scanner scanUpToString:kSpaceTag intoString:&keyString];
[scanner scanString:kSpaceTag intoString:nil];
[scanner scanUpToString:kNewlineTag intoString:&valueString];
[fileDictionary setObject:valueString forKey:keyString];
}
}
return [self initWithDict:fileDictionary otherOpen:otherOpenArray otherAction:otherActionArray];
}
- (id) initWithDict: (NSDictionary*) fileDictionary
otherOpen: (NSArray*) otherOpenArray
otherAction: (NSArray*) otherActionArray
{
BOOL otherEdit = NO;
BOOL otherDelete = NO;
NSMutableString* displayString = nil;
int revType = 0;
NSString* valueString = nil;
self = [super init];
///////////////////////////////
valueString = [fileDictionary objectForKey:kDepotFileTag];
if ( valueString )
{
fDepotPath = [valueString retain];
}
else
{
fDepotPath = [[NSString alloc] initWithString:kEmptyString];
}
fFileName = ExtractLastPathComponent (fDepotPath);
[fFileName retain];
///////////////////////////////
valueString = [fileDictionary objectForKey:kClientFileTag];
if ( valueString )
{
fClientPath = [valueString retain];
}
else
{
fClientPath = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kHeadActionTag];
if ( valueString )
{
fHeadAction = [valueString retain];
}
else
{
fHeadAction = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kHeadTypeTag];
if ( valueString )
{
fHeadType = [valueString retain];
}
else
{
fHeadType = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kHeadRevTag];
if ( valueString )
{
fHeadRev = [valueString retain];
}
else
{
fHeadRev = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kHeadChangeTag];
if ( valueString )
{
fHeadChange = [valueString retain];
}
else
{
fHeadChange = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kHaveRevTag];
if ( valueString )
{
fHaveRev = [valueString retain];
}
else
{
fHaveRev = [[NSString alloc] initWithString:kZeroRev]; // NOTE: the zero instead of empty string
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kActionTag];
if ( valueString )
{
fAction = [valueString retain];
}
else
{
fAction = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kChangeTag];
if ( valueString )
{
fChange = [valueString retain];
}
else
{
fChange = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kUnresolvedTag];
if ( valueString )
{
fUnresolved = [valueString retain];
}
else
{
fUnresolved = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kOtherOpenTag];
if ( valueString )
{
fOtherOpen = [valueString retain];
}
else
{
fOtherOpen = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kOtherLockTag];
if ( valueString )
{
fOtherLock = [valueString retain];
}
else
{
fOtherLock = [[NSString alloc] initWithString:kEmptyString];
}
///////////////////////////////
valueString = [fileDictionary objectForKey:kOurLockTag];
if ( valueString )
{
fOurLock = [valueString retain];
}
else
{
fOurLock = [[NSString alloc] initWithString:kEmptyString];
}
fOtherUsers = [otherOpenArray retain];
fOtherActions = [otherActionArray retain];
// Is this file equal to head, out of date, or not on clients machine?
if ( [fHaveRev isEqualToString:fHeadRev] )
{
revType = kIsHeadRevision;
}
else if ( [fHaveRev isEqualToString:kZeroRev] )
{
revType = kIsMissingRevision;
}
else
{
revType = kIsOldRevision;
}
// Build the display string
displayString = [[NSMutableString alloc] initWithString:fFileName];
[displayString appendString:@" #"];
[displayString appendString:fHaveRev];
[displayString appendString:@"/"];
[displayString appendString:fHeadRev];
[displayString appendString:@" <"];
[displayString appendString:fHeadType];
[displayString appendString:@"> "];
if ( [fHeadAction isEqualToString:kDeleteAction] )
{
if ( revType == kIsOldRevision )
{
[displayString appendString:kDeleteStatus_Old];
}
else
{
[displayString appendString:kDeleteStatus_Have];
}
}
// force into an NSString so that later when we return it
// it is faster (assuming Cocoa implemented NSString copy
// as a simple retain)
fDisplayString = [[NSString alloc] initWithCString:[displayString cString]];
if ( fOtherActions )
{
int numActions = [fOtherActions count];
int n;
for ( n = 0; (n < numActions) && !(otherEdit && otherDelete); n++ )
{
NSString* otherAction = [fOtherActions objectAtIndex:n];
if ( !otherEdit && [otherAction isEqualTo:kEditAction] )
{
otherEdit = YES;
}
if ( !otherDelete && [otherAction isEqualTo:kDeleteAction] )
{
otherDelete = YES;
}
}
}
int iconID = 0;
// Figure out icon id
if ( revType == kIsHeadRevision )
{
if ( [fAction isEqualTo:kEditAction] )
{
if ( otherDelete )
{
iconID = kHaveFileOurLockOtherDeleteIcon;
}
else if ( otherEdit )
{
iconID = kHaveFileBothLockIcon;
}
else
{
iconID = kHaveFileOurLockIcon;
}
}
else if ( [fAction isEqualTo:kDeleteAction] )
{
if ( otherDelete )
{
iconID = kHaveFileBothDeleteIcon;
}
else if ( otherEdit )
{
iconID = kHaveFileOurDeleteOtherLockIcon;
}
else
{
iconID = kHaveFileDeleteIcon;
}
}
else
{
if ( otherDelete )
{
iconID = kHaveFileOtherDeleteIcon;
}
else if ( otherEdit )
{
iconID = kHaveFileOtherLockIcon;
}
else
{
iconID = kHaveFileIcon;
}
}
}
else if ( revType == kIsOldRevision )
{
if ( [fAction isEqualTo:kEditAction] )
{
if ( otherDelete )
{
iconID = kOldFileOurLockOtherDeleteIcon;
}
else if ( otherEdit )
{
iconID = kOldFileBothLockIcon;
}
else
{
iconID = kOldFileOurLockIcon;
}
}
else if ( [fAction isEqualTo:kDeleteAction] )
{
if ( otherDelete )
{
iconID = kOldFileBothDeleteIcon;
}
else if ( otherEdit )
{
iconID = kOldFileOurDeleteOtherLockIcon;
}
else
{
iconID = kOldFileDeleteIcon;
}
}
else
{
if ( otherDelete )
{
iconID = kOldFileOtherDeleteIcon;
}
else if ( otherEdit )
{
iconID = kOldFileOtherLockIcon;
}
else
{
iconID = kOldFileIcon;
}
}
}
else
{
if ( otherDelete )
{
iconID = kMissingFileOtherDeleteIcon;
}
else if ( otherEdit )
{
iconID = kMissingFileOtherLockIcon;
}
else
{
iconID = kMissingFileIcon;
}
}
IconController* iconController = [IconController defaultIconController];
fIcon = [[iconController getIcon:iconID] retain];
return self;
}
- (void) copyFrom: (PerforceFile*) fromFile
{
[self releaseData];
fFileName = [fromFile->fFileName retain];
fDepotPath = [fromFile->fDepotPath retain];
fClientPath = [fromFile->fClientPath retain];
fHeadAction = [fromFile->fHeadAction retain];
fHeadType = [fromFile->fHeadType retain];
fHeadRev = [fromFile->fHeadRev retain];
fHeadChange = [fromFile->fHeadChange retain];
fHaveRev = [fromFile->fHaveRev retain];
fAction = [fromFile->fAction retain];
fChange = [fromFile->fChange retain];
fUnresolved = [fromFile->fUnresolved retain];
fOtherOpen = [fromFile->fOtherOpen retain];
fOtherLock = [fromFile->fOtherLock retain];
fOurLock = [fromFile->fOurLock retain];
fOtherUsers = [fromFile->fOtherUsers retain];
fOtherActions = [fromFile->fOtherActions retain];
fDisplayString = [fromFile->fDisplayString retain];
fIcon = [fromFile->fIcon retain];
}
- (void) dealloc
{
[self releaseData];
[super dealloc];
}
- (BOOL) isDeleted
{
if ( [fHeadAction isEqualToString:kDeleteAction] && [fHaveRev isEqualToString:kZeroRev] )
{
return YES;
}
return NO;
}
- (BOOL) isAdd
{
return [fAction isEqualToString:kAddAction];
}
- (int) getNumberOfChildren
{
return 0;
}
- (BOOL) hasChildren
{
return NO;
}
- (id) getChildAtIndex: (int) index
{
return nil;
}
- (id) getCellText
{
return fDisplayString;
}
- (id) getCellImage
{
return fIcon;
}
//
// Perforce Protocol
//
//
// data
//
//
// Enablement
//
- (BOOL) canSyncToHead
{
if ( [fHaveRev isEqualTo:fHeadRev] )
{
return NO;
}
if ( [fHeadAction isEqualTo:kDeleteAction] && [fHaveRev isEqualTo:kZeroRev] )
{
return NO;
}
return YES;
}
- (BOOL) canSyncToNone
{
if ( ![fAction isEqualTo:kEmptyString] )
{
return NO;
}
if ( [fHaveRev isEqualTo:kZeroRev] )
{
return NO;
}
return YES;
}
- (BOOL) canEdit
{
if ( ![fAction isEqualTo:kEmptyString] )
{
return NO;
}
if ( [fHaveRev isEqualTo:kZeroRev] )
{
return NO;
}
return YES;
}
- (BOOL) canDelete
{
if ( ![fAction isEqualTo:kEmptyString] )
{
return NO;
}
if ( [fHaveRev isEqualTo:kZeroRev] )
{
return NO;
}
return YES;
}
- (BOOL) canRevert
{
if ( ![fAction isEqualTo:kEmptyString] )
{
return YES;
}
return NO;
}
- (BOOL) canDiff
{
if ( [fAction isEqualTo:kEditAction] )
{
return YES;
}
return NO;
}
- (BOOL) canGetRevisions
{
return YES;
}
- (BOOL) canViewInEditor
{
if ( [fHaveRev isEqualTo:kZeroRev] && [fHeadAction isEqualTo:kDeleteAction] )
{
return NO;
}
return YES;
}
//
// Actions
//
- (void) resultFromPerforceFstat: (PerforceActionFstat*) action
{
if ( [action wasSuccess] )
{
[self copyFrom:[[action getFiles] objectAtIndex:0]];
SendNotificationWithObject (kDepotControllerChildChanged, self);
}
}
- (void) resultFromActionDoFstat: (id) nothing
{
[PerforceActionFstat defaultRunFor:self selector:@selector(resultFromPerforceFstat:) withPath:fDepotPath includeDeletes:YES];
}
- (void) resultFromPerforceFilelog: (PerforceActionFilelog*) action
{
if ( [action wasSuccess] )
{
[RevisionsController showWithRevisions:[action getRevisions]];
}
}
- (void) showRevisionHistory
{
[PerforceActionFilelog defaultRunFor:self selector:@selector(resultFromPerforceFilelog:) withPath:fDepotPath];
}
- (void) resultFromActionDoDiff: (NSString*) outputPath
{
DiffFiles (fClientPath, outputPath);
}
- (void) diff
{
[PerforceActionDiff depotPath:fDepotPath];
}
- (void) viewFromTempFile
{
NSString* fileName = GetTempFileName (fFileName, fHeadRev);
[PerforceActionPrint defaultRunFor:self
selector:@selector(viewAfterPrint:)
withPath:fDepotPath
outputPath:fileName];
}
- (void) viewInEditor
{
if ( [fHaveRev isEqualTo:kZeroRev] )
{
[self viewFromTempFile];
}
else
{
ViewFileInEditor (fClientPath);
}
}
- (NSString*) getPasteboardData
{
return fDepotPath;
}
- (void) viewAfterPrint:(PerforceActionPrint*) action
{
if ( [action wasSuccess] )
{
ViewFileInEditor ([action getOutputPath]);
}
}
- (void) pathChanged:(NSString*)depotPath theFile:(PerforceFile**)theFile
{
if ( [depotPath isEqualToString:fDepotPath] )
{
*theFile = self;
}
}
- (NSString*) getDepotPath
{
return fDepotPath;
}
- (NSString*) getPerforceActionPath
{
return fDepotPath;
}
- (void) expandPath:(NSString*)depotPath
{
if ( [fFileName isEqualTo:[depotPath lastPathComponent]] )
{
SendNotificationWithObject (kDepotControllerExpandChild, self);
}
}
@end
@implementation PerforceFile(Private)
- (void) releaseData
{
[fFileName release];
[fDepotPath release];
[fClientPath release];
[fHeadAction release];
[fHeadType release];
[fHeadRev release];
[fHeadChange release];
[fHaveRev release];
[fAction release];
[fChange release];
[fUnresolved release];
[fOtherOpen release];
[fOtherUsers release];
[fOtherActions release];
[fOtherLock release];
[fOurLock release];
[fDisplayString release];
[fIcon release];
}
@end
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #9 | 4277 | Jeff Argast | Fixed two bugs and updated version to 0.23 | ||
| #8 | 4225 | Jeff Argast |
Added resolve support, reveal in finder, drag and drop edit, show local files, and showing added files. |
||
| #7 | 3980 | Jeff Argast | Added distributed protocol | ||
| #6 | 3941 | Jeff Argast | Simplified fstat, branches, and job parsing. | ||
| #5 | 3134 | Jeff Argast |
Added copy/paste support in the depot view Added expand path to depot view Added bookmarks |
||
| #4 | 3113 | Jeff Argast |
Reduced the times the depot view completely collapses. Now it won't collapse on refresh views or submit, but still collapses when the defaults change. |
||
| #3 | 3111 | Jeff Argast |
Made multiple selection smarter by operating on the entire selection as an atomic operation with the server. Also partially fixed the read only window to not wrap at the window boundary. I did the same for the editable window, but now the problem appears to be that p4 change -o is breaking its output at some character location before the string gets into the editor (at least I think that is the problem). |
||
| #2 | 2741 | Jeff Argast | Fixed viewing files that are not in client | ||
| #1 | 2732 | Jeff Argast | Initial submission of P4Cocoa |