//
// SidebarViewController.m
// Perforce
//
// Created by Adam Czubernat on 16/12/2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "SidebarViewController.h"
#import "SidebarViewCell.h"
#import "PSTableRowView.h"
#import "P4Workspace.h"
#import "P4WorkspaceDefaults.h"
#import "P4FileItem.h"
#import "P4DepotItem.h"
#import "P4ChangelistItem.h"
#import "P4UnreadItem.h"
NSString * const P4PasteboardTypeFavoriteFolder = @"P4PasteboardTypeFavoriteFolder";
NSString * const P4PasteboardTypeFavoriteTag = @"P4PasteboardTypeFavoriteTag";
@interface SidebarViewController () <NSTableViewDataSource, NSTableViewDelegate> {
NSString *workingPath;
NSMutableArray *tableItems;
id tableItemWorkspace;
id tableItemAllFiles;
id tableItemChanges;
id tableItemUnread;
id tableItemDeleted;
NSRange tableItemRangeFolders;
NSRange tableItemRangeTags;
NSString *draggedOutFavorite;
__weak IBOutlet NSTableView *tableView;
NSPopover *tagPopover;
IBOutlet NSView *tagView;
__weak IBOutlet NSTextField *tagTextField;
__weak IBOutlet NSButton *tagButton;
}
- (void)addTag:(NSString *)tag atIndex:(NSInteger)idx;
- (void)moveFavorite:(NSString *)keyPath atIndex:(NSInteger)idx toIndex:(NSInteger)newIdx tableOffset:(NSInteger)offset;
- (IBAction)editFavoriteFolderAction:(id)sender;
- (IBAction)addFavoriteTagAction:(id)sender;
- (void)updateFavoritesNotification:(NSNotification *)notification;
- (void)updateIndicatorsNotification:(NSNotification *)notification;
@end
@implementation SidebarViewController
@synthesize delegate;
- (id)init {
return self = [self initWithNibName:NSStringFromClass([self class]) bundle:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)loadView {
[super loadView];
[tableView setBackgroundColor:[NSUserDefaults colorForKey:kColorBackgroundSideMenu]];
[tableView registerForDraggedTypes:@[
P4PasteboardTypeTag,
P4PasteboardTypeFavoriteFolder,
P4PasteboardTypeFavoriteTag
]];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(updateIndicatorsNotification:)
name:P4ChangelistUpdatedNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(updateIndicatorsNotification:)
name:P4UnreadUpdatedNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(updateFavoritesNotification:)
name:P4WorkspaceDefaultsChangedNotification
object:nil];
[self reload];
}
- (void)mouseEntered:(NSEvent *)theEvent {
NSTableCellView *cell = [theEvent userData];
NSButton *button = [cell viewWithTag:1];
[button setTarget:self];
[button setAction:@selector(addFavoriteTagAction:)];
[button setHidden:NO];
}
- (void)mouseExited:(NSEvent *)theEvent {
NSTableCellView *cell = [theEvent userData];
NSButton *button = [cell viewWithTag:1];
[button setTarget:nil];
[button setHidden:YES];
}
#pragma mark - Public
- (void)reload {
P4Workspace *workspace = [P4Workspace sharedInstance];
tableItems = [NSMutableArray array];
[tableItems addObjectsFromArray:@[
@"Files View",
tableItemWorkspace = @{
@"title" : @"My Workspace",
@"path" : [workspace root] ?: @"" },
tableItemAllFiles = @{
@"title" : @"All Files",
@"path" : @"//" },
[NSNull null], // Separator
@"Files Status",
tableItemChanges = @{
@"title" : @"My Pending Changes",
@"path" : @"changelist://nondeleted",
@"indicator" : @([workspace openedFilesCount] + [workspace addedFilesCount]) }.mutableCopy,
tableItemUnread = @{
@"title" : @"All Unread",
@"path" : @"unread://",
@"indicator" : @([workspace unreadCount]) }.mutableCopy,
tableItemDeleted = @{
@"title" : @"Marked for Delete",
@"path" : @"changelist://deleted",
@"indicator" : @([workspace deletedFilesCount]) }.mutableCopy,
]];
// Favorite folders
[tableItems addObjectsFromArray:@[ [NSNull null], @"Favorite Folders" ]];
NSArray *favoriteFolders = [[P4WorkspaceDefaults sharedInstance] favoriteFolders];
NSDictionary *favoriteNames = [[P4WorkspaceDefaults sharedInstance] favoriteNames];
tableItemRangeFolders = (NSRange) { tableItems.count, favoriteFolders.count };
for (NSString *folder in favoriteFolders)
[tableItems addObject:@{
@"title" : [favoriteNames objectForKey:folder] ?: folder.lastPathComponent,
@"path" : folder
}.mutableCopy];
// Favorite tags
[tableItems addObjectsFromArray:@[ [NSNull null], @"Favorite Tags" ]];
NSArray *favoriteTags = [[P4WorkspaceDefaults sharedInstance] favoriteTags];
tableItemRangeTags = (NSRange) { tableItems.count, favoriteTags.count };
for (NSString *tag in favoriteTags)
[tableItems addObject:@{
@"title" : tag,
@"path" : [@"tag://" stringByAppendingString:tag]
}.mutableCopy];
[tableView reloadData];
[self setWorkingPath:workingPath];
}
- (void)setWorkingPath:(NSString *)path {
workingPath = path;
NSInteger idx = [tableItems indexOfObjectIdenticalTo:tableItemWorkspace];
if ([path hasPrefix:@"//"])
idx = [tableItems indexOfObjectIdenticalTo:tableItemAllFiles];
else if ([path hasPrefix:@"/"])
;
else if ([path hasPrefix:@"changelist://nondeleted"])
idx = [tableItems indexOfObjectIdenticalTo:tableItemChanges];
else if ([path hasPrefix:@"unread://"])
idx = [tableItems indexOfObjectIdenticalTo:tableItemUnread];
else if ([path hasPrefix:@"changelist://deleted"])
idx = [tableItems indexOfObjectIdenticalTo:tableItemDeleted];
else if ([path hasPrefix:@"search://"])
idx = 0;
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:idx]
byExtendingSelection:NO];
}
#pragma mark - Private
- (void)addTag:(NSString *)tag atIndex:(NSInteger)newIdx {
// Check if tag is already in favorites
NSArray *favoriteTags = [P4WorkspaceDefaults sharedInstance].favoriteTags;
NSArray *lowercaseTags = [favoriteTags valueForKeyPath:@"lowercaseString"];
NSInteger index = [lowercaseTags indexOfObject:[tag lowercaseString]];
if (!favoriteTags || index == NSNotFound) {
// Insert new tag
NSMutableArray *mutableTags = [NSMutableArray arrayWithArray:favoriteTags];
[mutableTags insertObject:tag atIndex:newIdx];
[P4WorkspaceDefaults sharedInstance].favoriteTags = mutableTags;
// Insert table row
NSInteger row = tableItemRangeTags.location + newIdx;
NSString *path = [@"tag://" stringByAppendingString:tag];
[tableItems insertObject:@{ @"title" : tag, @"path" : path }.mutableCopy atIndex:row];
tableItemRangeTags.length++;
[tableView insertRowsFromIndex:row toIndex:row withAnimation:NSTableViewAnimationSlideLeft];
[tableView reloadDataForRowsInRange:tableItemRangeTags]; // Some couldn't un-hover
} else {
// Move existing tag into drop destination
[self moveFavorite:@"favoriteTags"
atIndex:index
toIndex:newIdx
tableOffset:tableItemRangeTags.location];
}
}
- (void)moveFavorite:(NSString *)keyPath atIndex:(NSInteger)idx toIndex:(NSInteger)newIdx tableOffset:(NSInteger)offset {
if (idx < newIdx)
newIdx--;
// Rearrange defaults
NSMutableArray *defaults = [[[P4WorkspaceDefaults sharedInstance] valueForKey:keyPath] mutableCopy];
id object = [defaults objectAtIndex:idx];
[defaults removeObjectAtIndex:idx];
[defaults insertObject:object atIndex:newIdx];
[[P4WorkspaceDefaults sharedInstance] setValue:defaults forKeyPath:keyPath];
idx += offset;
newIdx += offset;
// Rearrange pane items
object = [tableItems objectAtIndex:idx];
[tableItems removeObjectAtIndex:idx];
[tableItems insertObject:object atIndex:newIdx];
// Rearrange rows
[tableView moveRowAtIndex:idx toIndex:newIdx];
[tableView reloadDataForRowsFromIndex:MIN(idx, newIdx) toIndex:MAX(idx, newIdx)];
}
- (void)editFavoriteFolderAction:(SidebarViewCell *)cell {
NSString *value = cell.textField.stringValue;
NSInteger row = [tableView rowForView:cell];
NSMutableDictionary *tableItem = [tableItems objectAtIndex:row];
if ([value isEmptyString]) {
[cell.textField setStringValue:[tableItem objectForKey:@"title"]];
return; // Revert changes
}
[tableItem setObject:value forKey:@"title"];
NSString *path = [tableItem objectForKey:@"path"];
[[P4WorkspaceDefaults sharedInstance] renameFavoriteFolder:path name:value];
}
- (void)addFavoriteTagAction:(id)sender {
if (sender == tagButton) {
NSString *tag = tagTextField.stringValue;
[tagPopover close];
tagPopover = nil;
[self addTag:tag atIndex:0];
} else {
NSViewController *controller = [[NSViewController alloc] init];
[controller setView:tagView];
tagPopover = [[NSPopover alloc] init];
[tagPopover setBehavior:NSPopoverBehaviorSemitransient];
[tagPopover setContentViewController:controller];
[tagPopover showRelativeToRect:[sender frame]
ofView:[sender superview] preferredEdge:NSMinXEdge];
tagTextField.stringValue = @"";
[tagButton setEnabled:NO];
}
}
- (void)updateFavoritesNotification:(NSNotification *)notification {
if (notification.object != draggedOutFavorite)
[self reload];
draggedOutFavorite = nil;
}
- (void)updateIndicatorsNotification:(NSNotification *)notification {
NSInteger indicator;
P4Workspace *workspace = [P4Workspace sharedInstance];
indicator = [workspace openedFilesCount] + [workspace addedFilesCount];
[tableItemChanges setObject:@(indicator) forKey:@"indicator"];
indicator = [workspace unreadCount];
[tableItemUnread setObject:@(indicator) forKey:@"indicator"];
indicator = [workspace deletedFilesCount];
[tableItemDeleted setObject:@(indicator) forKey:@"indicator"];
NSRange range = { [tableItems indexOfObjectIdenticalTo:tableItemChanges], 3 };
[tableView reloadDataForRowsInRange:range];
}
#pragma mark - NSTextField delegate
- (void)controlTextDidChange:(NSNotification *)notification {
NSString *text = tagTextField.stringValue;
if (text.length >= 3 &&
[text rangeOfCharacterFromSet:
[NSCharacterSet whitespaceCharacterSet]].location == NSNotFound)
[tagButton setEnabled:YES];
else
[tagButton setEnabled:NO];
}
#pragma mark - NSTableView data source
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return tableItems.count;
}
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
id tableItem = [tableItems objectAtIndex:row];
return tableItem == [NSNull null] ? 16.0f : 34.0f;
}
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row {
PSTableRowView *rowView = [[PSTableRowView alloc] init];
rowView.selectionColor = nil;
return rowView;
}
- (NSView *)tableView:(NSTableView *)aTableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
SidebarViewCell *cell;
id tableItem = [tableItems objectAtIndex:row];
if (tableItem == [NSNull null]) {
cell = [tableView makeViewWithIdentifier:@"SeparatorCell" owner:self];
static NSImage *separator;
if (!separator) {
separator = [NSImage imageNamed:@"SeparatorHorizontalDark.png"];
separator = [separator resizableImageWithLeftCap:10.0f rightCap:10.0f];
}
PSView *view = cell.subviews.lastObject;
[view setContentMode:PSViewContentModeFillHorizontal];
[view setImage:separator];
} else if ([tableItem isKindOfClass:[NSString class]]) {
cell = [tableView makeViewWithIdentifier:@"HeaderCell" owner:self];
cell.textField.stringValue = [tableItem uppercaseString];
cell.textField.textColor = [NSUserDefaults colorForKey:kColorTextSideMenuHeader];
NSTrackingArea *trackingArea = [[cell trackingAreas] lastObject];
if (row != tableItemRangeTags.location-1)
[cell removeTrackingArea:trackingArea];
else if (!trackingArea) {
trackingArea = [[NSTrackingArea alloc]
initWithRect:CGRectZero
options:NSTrackingInVisibleRect |
NSTrackingMouseEnteredAndExited |
NSTrackingActiveInKeyWindow
owner:self
userInfo:(id)cell];
[cell addTrackingArea:trackingArea];
}
} else {
cell = [tableView makeViewWithIdentifier:@"Cell" owner:self];
cell.textField.stringValue = [tableItem objectForKey:@"title"];
if (NSLocationInRange(row, tableItemRangeFolders)) {
[cell makeEditable];
[cell setTarget:self action:@selector(editFavoriteFolderAction:)];
} else if (NSLocationInRange(row, tableItemRangeTags)) {
[cell makeSelectable];
NSString *tag = [tableItem objectForKey:@"title"];
P4WorkspaceDefaults *defaults = [P4WorkspaceDefaults sharedInstance];
cell.selected = [defaults.filteredTags containsObject:tag];
} else {
[cell makeIndicating];
}
id indicator = [tableItem objectForKey:@"indicator"];
[cell setIndicatorText:[indicator integerValue] ? [indicator stringValue] : nil];
}
return cell;
}
#pragma mark - NSTableView delegate
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row {
id tableItem = [tableItems objectAtIndex:row];
return [tableItem isKindOfClass:[NSDictionary class]];
}
- (void)tableViewSelectionIsChanging:(NSNotification *)notification {
NSInteger row = [tableView selectedRow];
id tableItem = [tableItems objectAtIndex:row];
if (![tableItem isKindOfClass:[NSDictionary class]])
return;
NSString *path = [tableItem objectForKey:@"path"];
if ([path hasPrefix:@"tag://"]) {
NSString *tag = [tableItem objectForKey:@"title"];
[[P4WorkspaceDefaults sharedInstance] setFilteredTag:tag];
SidebarViewCell *cell = [tableView viewAtColumn:0 row:row makeIfNecessary:NO];
[cell setSelected:!cell.selected];
[self setWorkingPath:workingPath];
} else if ([path isEqualToString:workingPath]) {
[self setWorkingPath:workingPath];
} else {
if ([delegate respondsToSelector:@selector(sidebarDidSelectPath:)])
[delegate sidebarDidSelectPath:path];
}
}
#pragma mark - NSTableView drag and drop
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
NSUInteger location = [rowIndexes firstIndex];
if (NSLocationInRange(location, tableItemRangeFolders))
[pboard setPropertyList:@(location - tableItemRangeFolders.location)
forType:P4PasteboardTypeFavoriteFolder];
else if (NSLocationInRange(location, tableItemRangeTags))
[pboard setPropertyList:@(location - tableItemRangeTags.location)
forType:P4PasteboardTypeFavoriteTag];
else
return NO;
return YES;
}
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
NSDragOperation operation = NSDragOperationNone;
if (dropOperation == NSTableViewDropOn)
return operation;
NSPasteboard *pboard = [info draggingPasteboard];
if (row - tableItemRangeFolders.location <= tableItemRangeFolders.length) {
if ([pboard stringForType:P4PasteboardTypeFavoriteFolder])
operation = NSDragOperationMove;
} else if (row - tableItemRangeTags.location <= tableItemRangeTags.length) {
if ([pboard stringForType:P4PasteboardTypeFavoriteTag])
operation = NSDragOperationMove;
else if ([pboard stringForType:P4PasteboardTypeTag])
operation = NSDragOperationCopy;
}
return operation;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation {
NSPasteboard *pboard = [info draggingPasteboard];
id pboardObject = nil;
if ((pboardObject = [pboard propertyListForType:P4PasteboardTypeFavoriteFolder])) {
[self moveFavorite:@"favoriteFolders"
atIndex:[pboardObject integerValue]
toIndex:row - tableItemRangeFolders.location
tableOffset:tableItemRangeFolders.location];
} else if ((pboardObject = [pboard propertyListForType:P4PasteboardTypeFavoriteTag])) {
[self moveFavorite:@"favoriteTags"
atIndex:[pboardObject integerValue]
toIndex:row - tableItemRangeTags.location
tableOffset:tableItemRangeTags.location];
} else if ((pboardObject = [pboard propertyListForType:P4PasteboardTypeTag])) {
[self addTag:pboardObject atIndex:row - tableItemRangeTags.location];
}
return YES;
}
- (void)tableView:(NSTableView *)aTableView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation {
CGRect rect = [self.view convertRect:tableView.frame toView:nil];
rect = [self.view.window convertRectToScreen:rect];
if (CGRectContainsPoint(rect, screenPoint))
return;
NSPasteboard *pboard = [session draggingPasteboard];
NSInteger idx, row;
NSNumber *pboardIdx = nil;
if ((pboardIdx = [pboard propertyListForType:P4PasteboardTypeFavoriteFolder])) {
idx = [pboardIdx integerValue];
row = idx + tableItemRangeFolders.location;
tableItemRangeFolders.length--;
tableItemRangeTags.location--;
} else if ((pboardIdx = [pboard propertyListForType:P4PasteboardTypeFavoriteTag])) {
idx = [pboardIdx integerValue];
row = idx + tableItemRangeTags.location;
tableItemRangeTags.length--;
} else {
return;
}
[tableItems removeObjectAtIndex:row];
[tableView removeRowsFromIndex:row toIndex:row withAnimation:NSTableViewAnimationEffectFade];
if ([pboard propertyListForType:P4PasteboardTypeFavoriteFolder]) {
P4WorkspaceDefaults *defaults = [P4WorkspaceDefaults sharedInstance];
NSString *path = [defaults.favoriteFolders objectAtIndex:idx];
draggedOutFavorite = path;
[defaults removeFavoriteFolder:path];
} else if ([pboard propertyListForType:P4PasteboardTypeFavoriteTag]) {
P4WorkspaceDefaults *defaults = [P4WorkspaceDefaults sharedInstance];
NSString *tag = [defaults.favoriteTags objectAtIndex:idx];
[defaults removeFavoriteTag:tag];
}
}
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/ViewControllers/Sidebar/SidebarViewController.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|