//
// ColumnViewDetails.m
// Perforce
//
// Created by Adam Czubernat on 03.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "ColumnViewDetails.h"
#import "TagsView.h"
#import "VersionsViewController.h"
const char * DetailsGlobalQueueLabel = "com.perforce.PreviewQueue";
dispatch_queue_t DetailsGlobalQueue = NULL;
@interface ColumnViewDetails () <NSMenuDelegate> {
P4Item *item;
__unsafe_unretained id <P4ItemActionDelegate> actionDelegate;
NSImage *image;
NSArray *itemActions;
VersionsViewController *versionsController;
// Outlets
__weak IBOutlet PSView *contentView;
__weak IBOutlet NSScrollView *contentScrollView;
__weak IBOutlet NSTextField *versionLabel;
__weak IBOutlet NSTextField *titleLabel;
__weak IBOutlet PSView *versionSeparator;
__weak IBOutlet PSView *titleSeparator;
__weak IBOutlet PSView *imageView;
__weak IBOutlet NSView *actionsHeader;
__weak IBOutlet NSTextField *actionsHeaderTitle;
__weak IBOutlet NSView *tagsHeader;
__weak IBOutlet NSTextField *tagsHeaderTitle;
__weak IBOutlet TagsView *tagsView;
IBOutlet NSView *prototypeMetadataView;
__weak IBOutlet NSTextField *prototypeMetadataTitle;
__weak IBOutlet NSTextField *prototypeMetadataValue;
IBOutlet NSView *prototypeActionView;
__weak IBOutlet NSImageView *prototypeActionImage;
__weak IBOutlet NSTextField *prototypeActionTitle;
__weak IBOutlet NSButton *prototypeActionButton;
__weak IBOutlet PSView *prototypeActionSeparator;
}
- (void)loadDirectoryItem;
- (void)loadFileItem;
- (void)loadImage;
- (void)setRevision:(NSString *)revision;
- (NSView *)addPrototypeView:(NSView *)prototype;
- (void)fitSubviews;
- (void)addMetadataWithTitle:(NSString *)title value:(NSString *)value;
- (void)addAction:(P4ItemAction *)action;
@end
@implementation ColumnViewDetails
static NSImage *horizontalSeparatorImage;
static NSColor *backgroundColor;
static NSColor *selectionColor;
static NSColor *textColor;
static NSColor *textColorSelected;
static NSColor *textColorSecondary;
static NSColor *textColorTag;
static NSColor *textColorCheckedOut;
- (id)initWithItem:(P4Item *)anItem actionDelegate:(id<P4ItemActionDelegate>)delegate {
if (self = [self initWithNibName:NSStringFromClass([self class]) bundle:nil]) {
item = anItem;
actionDelegate = delegate;
}
return self;
}
- (void)showVersions {
if (versionsController)
[versionsController.view removeFromSuperview];
versionsController = [[VersionsViewController alloc]
initWithItem:item
actionDelegate:actionDelegate];
CGRect frame = versionsController.view.frame;
frame.origin.x = contentScrollView.frame.size.width;
frame.size.height = self.view.frame.size.height;
versionsController.view.frame = frame;
[self.view addSubview:versionsController.view];
frame = self.view.frame;
frame.size.width = CGRectGetMaxX(versionsController.view.frame);
self.view.frame = frame;
}
- (void)loadView {
[super loadView];
// Load images and colors
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
horizontalSeparatorImage = [NSImage imageNamed:@"SeparatorHorizontalDark.png"];
horizontalSeparatorImage = [horizontalSeparatorImage resizableImageWithLeftCap:10.0f rightCap:1.0f];
backgroundColor = [NSUserDefaults colorForKey:kColorBackgroundColumn];
selectionColor = [NSUserDefaults colorForKey:kColorBackgroundColumnSelected];
textColor = [NSUserDefaults colorForKey:kColorTextColumn];
textColorSelected = [NSUserDefaults colorForKey:kColorTextColumnSelected];
textColorSecondary = [NSUserDefaults colorForKey:kColorTextIconSecondary];
textColorTag = [NSUserDefaults colorForKey:kColorBackgroundTagDark];
textColorCheckedOut = [NSUserDefaults colorForKey:kColorStatusCheckedOut];
});
// Set images and colors
[contentScrollView setBackgroundColor:backgroundColor];
[titleSeparator setImage:horizontalSeparatorImage];
[titleSeparator setContentMode:PSViewContentModeFillHorizontal];
[prototypeActionSeparator setImage:horizontalSeparatorImage];
[prototypeActionSeparator setContentMode:PSViewContentModeFillHorizontal];
[versionSeparator setBackgroundColor:textColorCheckedOut];
titleLabel.textColor =
prototypeMetadataValue.textColor =
prototypeActionTitle.textColor = textColor;
prototypeMetadataTitle.textColor =
actionsHeaderTitle.textColor =
tagsHeaderTitle.textColor = textColorSecondary;
versionLabel.textColor = textColorCheckedOut;
// Load determined by type
if ([item isDirectory])
[self loadDirectoryItem];
else
[self loadFileItem];
// Load common values for two types
titleLabel.stringValue = item.name;
itemActions = item.actions;
for (P4ItemAction *action in itemActions) {
[self addAction:action];
}
if ((!item.isTracked || item.isDirectory) &&
![item.status isEqualToString:@"add"] &&
![item.status isEqualToString:@"move/add"]) {
[tagsHeader removeFromSuperview];
[tagsView removeFromSuperview];
tagsHeader = tagsView = nil;
} else {
[tagsView setAllowsAdding:item.isEditable || item.status];
[tagsView setItem:item];
}
// Resize content view
[self fitSubviews];
// Remove prototypes
[self removePrototypeView:prototypeMetadataView];
[self removePrototypeView:prototypeActionView];
[self loadImage];
}
#pragma mark - Private
- (void)loadDirectoryItem {
[self setRevision:nil];
}
- (void)loadFileItem {
NSDictionary *metadata = item.metadata;
[self setRevision:[metadata objectForKey:@"haveRev"]];
// Add concretes
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterLongStyle;
if (!item.isTracked)
[self addMetadataWithTitle:@"UNTRACKED" value:@""];
if (item.isTracked) {
NSNumber *headTime = [metadata objectForKey:@"headModTime"];
NSDate *headDate = headTime ? [NSDate dateWithTimeIntervalSince1970:
headTime.doubleValue] : nil;
[self addMetadataWithTitle:@"MODIFIED IN" value:[formatter stringFromDate:headDate]];
if (item.status) {
[self addMetadataWithTitle:@"CHECKED OUT BY" value:@"You"];
} else if (item.statusOwner) {
prototypeMetadataTitle.stringValue = @"CHECKED OUT BY";
[prototypeMetadataValue setAttributedStringValue:
[[NSAttributedString alloc]
initWithString:item.statusOwner
attributes:@{
NSFontAttributeName : prototypeMetadataValue.font,
NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle),
NSLinkAttributeName : [NSString stringWithFormat:@"mailto:%@", item.statusOwner],
}]];
[prototypeMetadataValue setAllowsEditingTextAttributes:YES];
[prototypeMetadataValue setSelectable:YES];
[self addPrototypeView:prototypeMetadataView];
} else
[self addMetadataWithTitle:@"CHECKED IN" value:@"Available"];
}
if (item.status)
[self addMetadataWithTitle:@"ACTION" value:item.status];
NSString *movedFile = [item.metadata objectForKey:@"movedFile"];
if (movedFile.length) {
NSString *parentPath = [item.remotePath stringByDeletingPath];
if ([[movedFile stringByDeletingPath] isEqualToString:parentPath])
[self addMetadataWithTitle:@"RENAMED FROM" value:[movedFile lastPathComponent]];
else if ([item.status isEqualToString:@"move/delete"])
[self addMetadataWithTitle:@"MOVED TO" value:movedFile];
else
[self addMetadataWithTitle:@"MOVED FROM" value:movedFile];
}
}
- (void)loadImage {
if (!imageView)
return;
// Load image asynchronously
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
DetailsGlobalQueue = dispatch_queue_create(DetailsGlobalQueueLabel, NULL);
});
dispatch_async(DetailsGlobalQueue, ^{
if (!image)
image = [item previewWithSize:imageView.bounds.size];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
});
}
- (void)setRevision:(NSString *)revision {
if (!revision) {
titleLabel.frame = CGRectUnion(titleLabel.frame, versionLabel.frame);
[versionLabel removeFromSuperview];
[versionSeparator removeFromSuperview];
return;
}
versionLabel.stringValue = [NSString stringWithFormat:@"V.%@", revision];
[versionLabel sizeToFit];
CGFloat offset = CGRectGetMaxX(versionLabel.frame) + 6.0f;
CGRect frame = versionSeparator.frame;
frame.origin.x = offset;
versionSeparator.frame = frame;
offset += 8.0f;
frame = titleLabel.frame;
frame.size.width = CGRectGetMaxX(frame) - offset;
frame.origin.x = offset;
titleLabel.frame = frame;
}
- (NSView *)addPrototypeView:(NSView *)prototype {
NSData *archivedView = [NSKeyedArchiver archivedDataWithRootObject:prototype];
NSView *copy = [NSKeyedUnarchiver unarchiveObjectWithData:archivedView];
if (prototype.superview) {
[prototype removeFromSuperview];
} else {
// Move subviews below
CGFloat height = copy.frame.size.height;
CGFloat offset = copy.frame.origin.y + height;
for (NSView *subview in contentView.subviews) {
CGRect frame = subview.frame;
if (frame.origin.y >= offset)
continue;
frame.origin.y -= height;
subview.frame = frame;
}
}
CGRect frame = prototype.frame;
frame.origin.y -= frame.size.height;
prototype.frame = frame;
[contentView addSubview:copy];
return copy;
}
- (void)removePrototypeView:(NSView *)prototype {
if (!prototype.superview)
return;
[prototype removeFromSuperview];
CGFloat height = prototype.frame.size.height;
CGFloat offset = prototype.frame.origin.y + height;
for (NSView *subview in contentView.subviews) {
CGRect frame = subview.frame;
if (frame.origin.y >= offset)
continue;
frame.origin.y += height;
subview.frame = frame;
}
}
- (void)fitSubviews {
CGFloat min = 0.0f;
for (NSView *subview in contentView.subviews) {
min = fminf(subview.frame.origin.y, min);
}
if (min > 0.0f)
return;
// Resize content container to fit
CGRect frame = contentView.frame;
frame.size.height -= min;
contentView.frame = frame;
}
- (void)addMetadataWithTitle:(NSString *)title value:(NSString *)value {
if (!title || !value)
return;
prototypeMetadataTitle.stringValue = title;
prototypeMetadataValue.stringValue = value;
[self addPrototypeView:prototypeMetadataView];
}
- (void)addAction:(P4ItemAction *)action {
if (action.disabled)
return;
action.delegate = actionDelegate;
NSImage *actionImage;
SEL actionSelector = action.selector;
if (actionSelector == @selector(open)) {
actionImage = [NSImage imageNamed:@"IconReadonlyDark48"];
} else if (actionSelector == @selector(openWithCheckout)) {
actionImage = [NSImage imageNamed:@"IconOpenDark"];
} else if (actionSelector == @selector(openFromDepot)) {
actionImage = [NSImage imageNamed:@"IconOpenFromDepotDark"];
} else if (actionSelector == @selector(addItem)) {
actionImage = [NSImage imageNamed:@"IconMarkForAddDark"];
} else if (actionSelector == @selector(revertIfUnchanged)) {
actionImage = [NSImage imageNamed:@"IconUndoCheckoutDark"];
} else if (actionSelector == @selector(mapToWorkspace)) {
actionImage = [NSImage imageNamed:@"IconMapToWorkspaceDark"];
} else if (actionSelector == @selector(unmapFromWorkspace)) {
actionImage = [NSImage imageNamed:@"IconUnmapFromWorkspaceDark"];
} else if (actionSelector == @selector(shelve)) {
actionImage = [NSImage imageNamed:@"IconShelveDark"];
} else if (actionSelector == @selector(unshelve)) {
actionImage = [NSImage imageNamed:@"IconUnshelveDark"];
} else if (actionSelector == @selector(discardShelve)) {
actionImage = [NSImage imageNamed:@"IconShelveDiscardDark"];
} else if (actionSelector == @selector(showVersions)) {
actionImage = [NSImage imageNamed:@"IconVersionsDark"];
} else if (actionSelector == @selector(createDirectory)) {
actionImage = [NSImage imageNamed:@"IconNewDirectoryDark"];
} else if (actionSelector == @selector(showInFinder)) {
actionImage = [NSImage imageNamed:@"IconFinderDark48.png"];
} else if (actionSelector == @selector(deleteItem)) {
actionImage = [NSImage imageNamed:@"IconTrashDark48.png"];
} else if (actionSelector == @selector(checkIn) ||
actionSelector == @selector(checkInAll)) {
actionImage = [NSImage imageNamed:@"IconCheckinDark48.png"];
} else if (actionSelector == @selector(checkout)) {
actionImage = [NSImage imageNamed:@"IconCheckoutDark48.png"];
} else if (actionSelector == @selector(open)) {
actionImage = [NSImage imageNamed:@"IconReadonlyDark48.png"];
} else if (actionSelector == @selector(revert)) {
actionImage = [NSImage imageNamed:@"IconRevertDark.png"];
} else if (actionSelector == @selector(copyShareLink)) {
actionImage = [NSImage imageNamed:@"IconCopyLinkDark"];
} else {
actionImage = [NSImage imageNamed:@"IconFavoriteDark.png"];
}
prototypeActionTitle.stringValue = action.name;
prototypeActionImage.image = actionImage;
prototypeActionButton.tag = 666; // Number of the Beast
prototypeActionButton.alphaValue = 0.5f;
NSView *copy = [self addPrototypeView:prototypeActionView];
NSButton *actionButton = [copy viewWithTag:666];
[actionButton setTarget:action];
[actionButton setAction:@selector(performAction)];
}
#pragma mark - Menu delegate
- (void)menuWillOpen:(NSMenu *)menu {
[menu setAllowsContextMenuPlugIns:NO];
[menu setItem:item delegate:[self.view.window windowController]];
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/ViewControllers/ColumnView/ColumnViewDetails.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|