// // 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 @interface IconViewItem () { P4Item *item; NSTrackingArea *trackingArea; NSArray *btnActions; NSColor *color; __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)setRevision:(NSString *)revision; - (void)textFieldDidEndEditingNotification:(NSNotification *)notification; - (void)backgroundClick; - (void)contentDoubleClick; @end @implementation IconViewItem @synthesize delegate, image, 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"]; }); 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]; if (item) [self setRepresentedObject:item]; [self setSelected:self.selected]; } - (id)copyWithZone:(NSZone *)zone { IconViewItem *copy = [super copyWithZone:zone]; copy.delegate = delegate; return copy; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self name:NSControlTextDidEndEditingNotification object:nil]; } - (void)setRepresentedObject:(P4Item *)anItem { [super setRepresentedObject:anItem]; item = anItem; if (!iconView) return; // View not loaded yet color = backgroundColor; selectionView.backgroundColor = color; // 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; iconView.image = image; } - (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 = color; } } #pragma mark - Public - (void)setImage:(NSImage *)img { image = img; iconView.image = image; } - (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)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 | NSTrackingActiveInKeyWindow); 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 | 15071 | alan_petersen |
Populate -o //guest/perforce_software/piper/... //guest/alan_petersen/piper/.... |
||
//guest/perforce_software/piper/mac/main/Perforce/Classes/ViewControllers/IconView/IconViewItem.m | |||||
#2 | 12961 | alan_petersen |
Piper 2.0 Mega Update New Features/Functionality - Added help menu redirecting to URL. - Added readonly property for creating new workspaces. - Added html hyperlinks for Copy link functionality. - Added functionality for managing Finder Favorite items in sidebar. - Redesigned the way mapping is stored in Piper. - First version of syncing finder sidebar items with workspace mapping. - Small sorting improvements. - Creating Projects directory inside users home folder. - Adding Projects folder to finder sidebar item. - Creating and removing symbolic links accordingly to mapped folders. - Preventing duplicate names in symbolic links. - Refreshing symbolic links on mapping change inside application. - Storing workspace and server details in p4 configuration for other applications to use. - Added contextual menu items for Finder integration. - Added services menu for Adobe Illustrator integration. - Keyboard shortcuts for Illustrator integration. - Code refactoring and fixes for mapping issues. - Added Finder functionality to edit all files in folder. - Added user friendly message when editing a file using Finder outside the workspace. - Implemented hidden automatic login when opening application using Finder integration. - Logging to file in ~/Library/Logs - Unified workspace and all files views to show both local and depot files and folders. - Removed my workspace view references and logic. - Editing unmapped files on server. - First version of adding file to unmapped folders. - Showing opened by and edit actions in column details for all depot files. - Improved mappings functionality. - Enabled same feature options for mapped and unmapped folders and files. - Redesigned from scratch mapping and unmapping procedures for adding and removing files. - Implemented cleaning workspace using new mapping functionality. Removed debug overlay coloring. - Automated workspace creation - Improvements in editing files already mapped to workspace. - Implemented deleting remote files. - Implemented first version of move operation for remote files. - Removing last workspace information when disconnecting from workspace using app menu. - Implemented editing and submitting using symbolic links in project folder. New finder menu service for symbolic links Show in Piper which acts like share link functionality. - New icons for files and folders not tracked in the filesystem. - Improvements in showing file using share link. - Switched to new way of retrieving files in order to show user changes. - Redesigned and implemented new functionality for chaining operations with mapping. - Improvements and redesign of Edit/add actions to use new chaining logic . Fixed issue with file edit. - Improvements in window showing when using services. - Simplified file loading so the local files appears only when remote are also loaded. - Improved deleting of untracked files to avoid mapping and marking for delete. - Enabling simple copy paste and moving of remote and local files. - Added abort for exception handling in order to force crashing application on critical failures - Added custom exception handling for catching runtime errors to log and crash instead of continuing in unstable state. - Changed file copying to use mark for add . - Simplified and fixed responding file representations to mapping changes. Bug Fixes - Fixed crash when synchronizing. - Fixed sync issue when downloading directory without file size information. - Fixed issue with unread list crashing when file is not existing on disk. - Fixed incorrect sync progress calculation. - Removed relative path issues. - Fixed many of case-sensitivity problems. - Fixed deprecated methods and related issues in OS X 10.10. - Fixed folder rename not updating in column view. Revised and fixed many potential problems from implicit casting. - Fixed missing sync button on fast sync completion. - Refreshing mapping on synchronization. Fixed symbolic links not appearing until app is restarted. - Fixed latest crashing of autosync. - Fixed loading indicator issues. - Fixed and redesigned submit dialog to work correctly with Submit All Files option in Finder. - Fixed multiple error messages on network outage. Redesigned showing errors in main window. - Fixed opening random locations when using Finder integration. - Fixed issue when panel was detached from parent window. - Fixed bug when creating new workspace wouldn't store default settings. - Fixed memory issues with network operations. - Fixes in relogging mappings and file listing. - Improvements in editing unmapped files. - Fixed crash when adding file outside workspace. - Fixed breadcrumbs control issue. - Fixed issue with double parent folders when opening unmapped files. - Fixed crashes on sync after mapping new files. - Fixed issue with editing file using Finder -- Merging code and additional fixes in add button functionality. - Fixed unsync not working - Fixed submit panel issue not selecting files with different name case. - Fixed missing revert and sync to workspace actions in some cases. - Fixed issue with Submit and Edit finder actions. Improvements in stability of finder integration. - Fixed issue with unsubmitted folders breaking status of files inside. - Fixed issue with added files not showing correct icon and status. - Fixed bug with file edit resulting in a new directory named exactly like a file. - Fixed issue with reloading of subpath resulting in untracked folders. - Fixed mapping issue when result was always view mapping not relative. - Fixed submit panel showing more than once. - Fixed illustrator services not working. - Fixed userdefaults preferences problem with workspace name being null. - Fixed userdefaults keypath problem of dot-containing workspace names. - Forcing recreating of browser to possibly prevent pre-10.10 errors with automatic workspace selection. - Fixed adding file to depot not presenting correct icon. - Fixed issues with reverting a file that was marked for add. - Presenting error when trying to submit untracked files. - Fixed issue when submit files service crashed when using unmapped files. - Fixed file representation disappearing when removing file. - Fixed issue with symlinks resolving working on 10.10 only. Issue related to workspace selection not showing. - Fixed error panel method calls unavailable in Mac OS versions before 10.10. Issue related to hanging error panels. - Fixed removing a local file resulting in action progress freezing. - Fixed open file not working after edit. - Fixing crash when mapping changed. Issue related to moving local file to unmapped folder and other similar cases. |
||
#1 | 11252 | alan_petersen | Rename/move file(s) | ||
//guest/perforce_software/piper/mac/Perforce/Classes/ViewControllers/IconView/IconViewItem.m | |||||
#1 | 10744 | alan_petersen | Rename/move file(s) | ||
//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. |