// // 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" #import "HistoryViewController.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; NSViewController *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 - (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)showFolderHistory { if (versionsController) [versionsController.view removeFromSuperview]; versionsController = [[HistoryViewController alloc] initWithDirectoryItem: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 NSImage *horizontalSeparatorImage = [NSImage imageNamed:@"SeparatorHorizontalDark.png"]; horizontalSeparatorImage = [horizontalSeparatorImage resizableImageWithLeftCap:10.0f rightCap:1.0f]; NSColor *backgroundColor = [NSUserDefaults colorForKey:kColorBackgroundColumn]; NSColor *textColor = [NSUserDefaults colorForKey:kColorTextColumn]; NSColor *textColorSecondary = [NSUserDefaults colorForKey:kColorTextIconSecondary]; NSColor *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]; } // determine if tags view should be visible if (item.isDirectory && ![item.status isEqualToString:@"add"] && ![item.status isEqualToString:@"move/add"]) { [tagsHeader removeFromSuperview]; [tagsView removeFromSuperview]; tagsHeader = tagsView = nil; } else { if(item.isTracked) { [tagsView setAllowsAdding:item.isEditable || item.status]; } else { [tagsView setAllowsAdding:NO]; } [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; id revision = nil; if([metadata objectForKey:@"haveRev"]) { revision = [metadata objectForKey:@"haveRev"]; } else if([metadata objectForKey:@"headRev"]) { revision = [metadata objectForKey:@"headRev"]; } [self setRevision:revision]; // Add concretes NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.dateStyle = NSDateFormatterLongStyle; if (!item.isTracked) [self addMetadataWithTitle:@"UNTRACKED" value:@""]; if ([item.metadata objectForKey:@"unresolved"]) [self addMetadataWithTitle:@"UNRESOLVED" value:@""]; 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]; else if ([metadata objectForKey:@"otherAction0"]) [self addMetadataWithTitle:@"ACTION" value:[metadata objectForKey:@"otherAction0"]]; NSString *movedFile = [item.metadata objectForKey:@"movedFile"]; if (movedFile.length) { NSString *parentPath = [item.remotePath stringByDeletingPath]; if ([[movedFile stringByDeletingPath] isEqualCaseInsensitive: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 = fmin(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) || actionSelector == @selector(showFolderHistory)) { 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)menuNeedsUpdate:(NSMenu *)menu { [menu setAllowsContextMenuPlugIns:NO]; [menu setItem:item delegate:[self.view.window windowController]]; } @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/ColumnView/ColumnViewDetails.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/ColumnView/ColumnViewDetails.m | |||||
#1 | 10744 | alan_petersen | Rename/move file(s) | ||
//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. |