//
// IconViewItem.m
// Perforce
//
// Created by Adam Czubernat on 07.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "IconViewItem.h"
#import "IconViewController.h"
#pragma mark - IconBackgroundView
// View that is sending click actions only when not clicking on the
// subviews (deselect) and is not passing events to superviews. View is
// sending doubleClick actions only on subviews.
@interface IconBackgroundView : PSView
@end
@implementation IconBackgroundView
- (void)mouseDown:(NSEvent *)theEvent {
// Check if clicked on subviews
if ([self hitTest:[self.superview convertPoint:theEvent.locationInWindow
fromView:nil]] != self) {
[super mouseDown:theEvent]; // Act normally
if ([theEvent clickCount] > 1)
[super sendActionsForEvents:PSViewEventDoubleClick];
return;
}
// Clicked outside subviews (on view itself)
if ([theEvent clickCount] == 1)
[super sendActionsForEvents:PSViewEventClick]; // Clicks only outside
// Don't pass event further
}
- (void)mouseDragged:(NSEvent *)theEvent {
[super mouseDown:theEvent];
}
- (void)sendActionsForEvents:(PSViewEvent)events {
// Disable executing actions through [super mouseDown]
}
@end
#pragma mark - IconViewItemController
const char * IconItemGlobalQueueLabel = "com.perforce.PreviewQueue";
dispatch_queue_t IconItemGlobalQueue = NULL;
@interface IconViewItem () {
P4Item *item;
NSTrackingArea *trackingArea;
NSArray *btnActions;
NSImage *image;
__weak IBOutlet PSView *iconView;
__weak IBOutlet PSView *selectionView;
__weak IBOutlet PSView *statusView;
__weak IBOutlet NSTextField *versionLabel;
__weak IBOutlet NSTextField *titleLabel;
__weak IBOutlet PSView *versionSeparator;
__weak IBOutlet PSView *horizontalLine;
__weak IBOutlet NSImageView *unreadBadge;
__weak IBOutlet NSImageView *tagBadge;
__weak IBOutlet PSView *actionBtsContent;
__weak IBOutlet NSTextField *firstTitle;
__weak IBOutlet NSTextField *firstLabel;
__weak IBOutlet NSTextField *secondTitle;
__weak IBOutlet NSTextField *secondLabel;
}
- (void)loadDirectoryItem;
- (void)loadFileItem;
- (void)loadImage;
- (void)setRevision:(NSString *)revision;
- (void)textFieldDidEndEditingNotification:(NSNotification *)notification;
- (void)backgroundClick;
- (void)contentDoubleClick;
@end
@implementation IconViewItem
@synthesize delegate, iconView;
static NSColor *textColor;
static NSColor *textColorSecondary;
static NSColor *backgroundColor;
static NSColor *backgroundColorSelected;
static NSColor *lineColor;
static NSColor *statusColorAvailable;
static NSColor *statusColorCheckedOut;
static NSColor *statusColorCheckedOutSomeone;
static NSColor *statusColorShelved;
#pragma mark - Overrides
- (NSString *)nibName {
return NSStringFromClass([self class]);
}
- (void)loadView {
[super loadView];
// Load images and colors
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
textColor = [NSUserDefaults colorForKey:kColorTextColumn];
textColorSecondary = [NSUserDefaults colorForKey:kColorTextIconSecondary];
backgroundColor = [NSUserDefaults colorForKey:kColorBackgroundIcon];
backgroundColorSelected = [NSUserDefaults colorForKey:kColorBackgroundSelected];
lineColor = [NSColor colorWithHexString:@"#CCC"];
statusColorAvailable = [NSUserDefaults colorForKey:kColorStatusAvailable];
statusColorCheckedOut = [NSUserDefaults colorForKey:kColorStatusCheckedOut];
statusColorCheckedOutSomeone = [NSUserDefaults colorForKey:kColorStatusCheckedOutSomeone];
statusColorShelved = [NSColor colorWithHexString:@"#ffd700"];
});
[self setSelected:NO];
selectionView.backgroundColor = backgroundColor;
statusView.backgroundColor = statusColorAvailable;
horizontalLine.backgroundColor =
versionSeparator.backgroundColor = lineColor;
actionBtsContent.backgroundColor = [NSUserDefaults colorForKey:kColorBackgroundIconViewActionButton];
//colorWithAlphaComponent: 0.5];
[actionBtsContent setHidden:YES];
[self initializeMouseTracking];
// Add actions
PSView *view = (PSView *)self.view;
[view addTarget:self action:@selector(backgroundClick) forEvents:PSViewEventClick];
[view addTarget:self action:@selector(contentDoubleClick) forEvents:PSViewEventDoubleClick];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(textFieldDidEndEditingNotification:)
name:NSControlTextDidEndEditingNotification
object:titleLabel];
}
- (id)copyWithZone:(NSZone *)zone {
IconViewItem *copy = [super copyWithZone:zone];
copy.delegate = delegate;
[copy view]; // Force loading views
return copy;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSControlTextDidEndEditingNotification
object:nil];
}
- (void)setRepresentedObject:(P4Item *)anItem {
[super setRepresentedObject:anItem];
item = anItem;
// Load determined by type
if ([anItem isDirectory])
[self loadDirectoryItem];
else
[self loadFileItem];
[unreadBadge setHidden:!item.isUnread];
if ([(BrowserViewController *)delegate hasFilteredTagsForItem:item]) {
CGRect frame = unreadBadge.frame;
if (item.isUnread)
frame.origin.x -= frame.size.width + 2.0f;
[tagBadge setFrame:frame];
[tagBadge setHidden:NO];
} else {
[tagBadge setHidden:YES];
}
// Load common values for two types
titleLabel.stringValue = anItem.name;
[self loadImage];
}
- (void)setSelected:(BOOL)selected {
if (selected && [delegate respondsToSelector:@selector(iconViewItemSelected:)])
[delegate iconViewItemSelected:self];
[super setSelected:selected];
if (selected) {
firstTitle.textColor =
secondTitle.textColor =
versionLabel.textColor =
firstLabel.textColor =
secondLabel.textColor =
titleLabel.textColor = [NSColor whiteColor];
selectionView.backgroundColor = backgroundColorSelected;
} else {
if (titleLabel.isEditable)
[self.view.window makeFirstResponder:self.collectionView];
firstTitle.textColor =
secondTitle.textColor =
versionLabel.textColor = textColorSecondary;
firstLabel.textColor =
secondLabel.textColor =
titleLabel.textColor = textColor;
selectionView.backgroundColor = backgroundColor;
}
}
#pragma mark - Public
- (NSString *)title {
return titleLabel.stringValue;
}
- (void)beginEditing {
[titleLabel setEditable:YES];
[self.view.window makeFirstResponder:titleLabel];
}
- (void)abortEditing {
[titleLabel abortEditing];
[titleLabel setEditable:NO];
}
#pragma mark - Click Actions
- (void)loadDirectoryItem {
[self setRevision:nil];
firstTitle.stringValue =
firstLabel.stringValue =
secondTitle.stringValue =
secondLabel.stringValue = @"";
statusView.backgroundColor = statusColorAvailable;
}
- (void)loadFileItem {
NSDictionary *metadata = item.metadata;
[self setRevision:[metadata objectForKey:@"haveRev"]];
statusView.backgroundColor = statusColorAvailable;
if (item.isTracked) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterLongStyle;
NSNumber *headTime = [metadata objectForKey:@"headModTime"];
NSDate *headDate = headTime ? [NSDate dateWithTimeIntervalSince1970:
headTime.doubleValue] : nil;
firstTitle.stringValue = @"MODIFIED IN";
firstLabel.stringValue = [formatter stringFromDate:headDate] ?: @"";
NSString *title, *label;
if (item.status) {
title = @"CHECKED OUT BY";
label = @"You";
} else if (item.statusOwner) {
title = @"CHECKED OUT BY";
label = item.statusOwner;
} else {
title = @"CHECKED IN";
label = @"Available";
}
secondTitle.stringValue = title ?: @"";
secondLabel.stringValue = label ?: @"";
if (item.statusOwner) {
[secondLabel setAttributedStringValue:
[[NSAttributedString alloc]
initWithString:item.statusOwner
attributes:@{
NSFontAttributeName : secondLabel.font,
NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle),
NSLinkAttributeName : [NSString stringWithFormat:@"mailto:%@", item.statusOwner],
}]];
[secondLabel setAllowsEditingTextAttributes:YES];
[secondLabel setSelectable:YES];
}
} else if (item.status) {
firstTitle.stringValue = @"ACTION";
firstLabel.stringValue = item.status ?: @"";
secondTitle.stringValue = @"UNTRACKED";
secondLabel.stringValue = @"";
} else {
firstTitle.stringValue = @"UNTRACKED";
firstLabel.stringValue =
secondTitle.stringValue =
secondLabel.stringValue = @"";
}
if (item.isShelved) {
statusView.backgroundColor = statusColorShelved;
} else if (item.statusOwner) {
statusView.backgroundColor = statusColorCheckedOutSomeone;
} else if (item.status) {
statusView.backgroundColor = statusColorCheckedOut;
}
}
- (void)loadImage {
if (!iconView)
return;
// Load image asynchronously
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
IconItemGlobalQueue = dispatch_queue_create(IconItemGlobalQueueLabel, NULL);
});
dispatch_async(IconItemGlobalQueue, ^{
if (!image)
image = [item previewWithSize:iconView.bounds.size];
dispatch_async(dispatch_get_main_queue(), ^{
iconView.image = image;
});
});
}
- (void)setRevision:(NSString *)revision {
if (!revision) {
titleLabel.frame = CGRectUnion(titleLabel.frame, versionLabel.frame);
titleLabel.alignment = NSCenterTextAlignment;
[versionLabel setHidden:YES];
[versionSeparator setHidden:YES];
return;
}
titleLabel.alignment = NSLeftTextAlignment;
[versionLabel setHidden:NO];
[versionSeparator setHidden:NO];
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 += 7.0f;
frame = titleLabel.frame;
frame.size.width = CGRectGetMaxX(frame) - offset;
frame.origin.x = offset;
titleLabel.frame = frame;
}
- (void)textFieldDidEndEditingNotification:(NSNotification *)notification {
if ([delegate respondsToSelector:@selector(iconViewItemDidEndEditing:)])
[delegate iconViewItemDidEndEditing:self];
[titleLabel setEditable:NO];
[self.view.window makeFirstResponder:self.collectionView];
}
- (void)backgroundClick {
self.collectionView.selectionIndexes = nil;
}
- (void)contentDoubleClick {
if ([delegate respondsToSelector:@selector(iconViewItemOpened:)])
[delegate iconViewItemOpened:self];
}
#pragma mark - TrackingArea setup
- (void) initializeMouseTracking
{
if(trackingArea != nil) {
[selectionView removeTrackingArea:trackingArea];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
trackingArea = [ [NSTrackingArea alloc] initWithRect:[selectionView bounds]
options:opts
owner:self
userInfo:nil];
[selectionView addTrackingArea:trackingArea];
}
-(void)mouseEntered:(NSEvent *)theEvent {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showActionButtons) object:nil];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetActionButtons) object:nil];
[self performSelector:@selector(showActionButtons) withObject:nil afterDelay:0.5f];
}
-(void)mouseExited:(NSEvent *)theEvent {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showActionButtons) object:nil];
if (!actionBtsContent.isHidden) {
[PSView animateWithDuration:0.1f animations:^{
[[actionBtsContent animator] setAlphaValue:0.0f];
} completion:^{
[actionBtsContent setHidden:YES];
}];
[self performSelector:@selector(resetActionButtons) withObject:nil afterDelay:0.25f];
}
}
#pragma mark - Private
- (void)resetActionButtons {
[actionBtsContent.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
}
- (void)showActionButtons {
[self setActionBtns];
[actionBtsContent setHidden:NO];
[actionBtsContent setAlphaValue:0.0f];
[PSView animateWithDuration:0.25f animations:^{
[[actionBtsContent animator] setAlphaValue:1.0f];
}];
}
#define btnWidth 48
#define separatorWidth 5
- (void)setActionBtns {
btnActions = item.actions;
NSButton *tmpBtn;
NSRect initialFrame;
NSRect separatorFrame;
NSRect separatorImageContentFrame;
NSImageView *separator;
PSView *separatorImageContent;
int xOffset = 0;
[actionBtsContent.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
for (P4ItemAction *p4ItemAction in btnActions) {
NSImage *actionImage;
SEL itemActionSelector = p4ItemAction.selector;
if(itemActionSelector == @selector(showInFinder)) {
actionImage = [NSImage imageNamed:@"IconFinder.png"];
} else if(itemActionSelector == @selector(deleteItem)) {
actionImage = [NSImage imageNamed:@"IconTrash.png"];
} else if(itemActionSelector == @selector(checkIn) ||
itemActionSelector == @selector(checkInAll)) {
actionImage = [NSImage imageNamed:@"IconCheckin.png"];
} else if(itemActionSelector == @selector(checkout)) {
actionImage = [NSImage imageNamed:@"IconCheckout.png"];
} else if(itemActionSelector == @selector(open)) {
actionImage = [NSImage imageNamed:@"IconReadonly.png"];
} else if(itemActionSelector == @selector(revert)) {
actionImage = [NSImage imageNamed:@"IconRevert.png"];
} else if((!item.isTracked && itemActionSelector == @selector(unmapFromWorkspace)) ||
itemActionSelector == @selector(mapToWorkspace)) {
actionImage = [NSImage imageNamed:@"IconFavorite.png"];
} else {
continue;
}
p4ItemAction.delegate = [self.view.window windowController];
initialFrame = CGRectMake(xOffset, 4.0f, btnWidth, actionBtsContent.frame.size.height-8.0f);
xOffset += btnWidth;
if(p4ItemAction != [btnActions lastObject]) {
separatorImageContentFrame = CGRectMake(xOffset, 0, separatorWidth, actionBtsContent.frame.size.height);
separatorFrame = CGRectMake(0, 0, separatorWidth, actionBtsContent.frame.size.height);
xOffset += separatorWidth;
separator = [[NSImageView alloc] init];
[separator setImage:[NSImage imageNamed:@"BarSeparator.png"]];
[separator setFrame:separatorFrame];
separatorImageContent = [[PSView alloc] initWithFrame:separatorImageContentFrame];
separatorImageContent.backgroundColor = [NSUserDefaults colorForKey:kColorBackgroundIconViewActionButton];
[separatorImageContent addSubview:separator];
[actionBtsContent addSubview:separatorImageContent];
}
tmpBtn = [[NSButton alloc] init];
[tmpBtn setFrame:initialFrame];
[tmpBtn setBordered:NO];
[tmpBtn.cell setBackgroundColor:[NSUserDefaults colorForKey:kColorBackgroundIconViewActionButton]];
[tmpBtn.cell setImageScaling:NSImageScaleProportionallyDown];
[tmpBtn setTarget:self];
[tmpBtn setAction:@selector(performItemAction:)];
[tmpBtn setTag:[btnActions indexOfObjectIdenticalTo:p4ItemAction]];
[tmpBtn setImage:actionImage];
[tmpBtn setToolTip:p4ItemAction.name];
[actionBtsContent addSubview:tmpBtn];
// [actionBtsContent addSubview:separatorImageContent];
}
}
- (void)performItemAction:(NSButton*)sender {
P4ItemAction *action = [btnActions objectAtIndex:sender.tag];
[action performAction];
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/ViewControllers/IconView/IconViewItem.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|