//
// IconViewController.m
// Perforce
//
// Created by Adam Czubernat on 05.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "IconViewController.h"
#import "PSCollectionView.h"
#import "PSCustomScrollView.h"
#import "VersionsViewController.h"
@interface IconViewController () <P4ItemDelegate, PSCollectionViewDelegate, QLPreviewPanelDataSource, QLPreviewPanelDelegate, NSMenuDelegate, NSPopoverDelegate> {
__weak IBOutlet PSCollectionView *collectionView;
IBOutlet NSView *loadingOverlay;
__weak IBOutlet NSProgressIndicator *loadingOverlayIndicator;
NSMutableArray *childrenItems;
id loadingItem;
NSPopover *popover;
// Will Load
NSString *reloadPath;
NSArray *reloadSelection;
// QuickLook
QLPreviewPanel *quickLookPanel;
NSArray *quickLookItems;
NSImage *quickLookIconImage;
}
- (void)showLoadingOverlay;
@end
@implementation IconViewController
- (void)loadView {
[super loadView];
[collectionView setFocusRingType:NSFocusRingTypeNone];
// [(id)collectionView setBorderType:NSNoBorder];
[collectionView registerForDraggedTypes:@[ NSFilenamesPboardType ]];
[collectionView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
[collectionView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO];
[collectionView setContent:childrenItems];
[collectionView setBackgroundColors:@[
[NSUserDefaults colorForKey:kColorBackgroundIconContainer]
]];
[collectionView
addObserver:self
forKeyPath:@"selectionIndexes"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:NULL];
}
- (void)dealloc {
[collectionView removeObserver:self forKeyPath:@"selectionIndexes"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSArray *selection = [self selectedItems];
// Quicklook
if (!quickLookPanel && [QLPreviewPanel sharedPreviewPanelExists])
[[QLPreviewPanel sharedPreviewPanel] updateController];
if (quickLookPanel) {
quickLookItems = selection;
[quickLookPanel reloadData];
}
[super selectionChanged:selection.lastObject ?: workingItem];
}
#pragma mark - Private
- (void)showLoadingOverlay {
[loadingOverlay setFrame:self.view.bounds];
[self.view addSubview:loadingOverlay];
[loadingOverlayIndicator startAnimation:nil];
}
#pragma mark - NSCollectionView delegate
- (BOOL)collectionView:(NSCollectionView *)aCollectionView writeItemsAtIndexes:(NSIndexSet *)indexes toPasteboard:(NSPasteboard *)pasteboard {
NSMutableArray *filenames = [NSMutableArray array];
[indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
P4Item *item = [childrenItems objectAtIndex:idx];
if ([item isEditable])
[filenames addObject:item.localPath];
}];
[pasteboard declareTypes:@[NSFilenamesPboardType] owner:self];
[pasteboard setPropertyList:filenames forType:NSFilenamesPboardType];
return YES;
}
- (BOOL)collectionView:(NSCollectionView *)aCollectionView canDragItemsAtIndexes:(NSIndexSet *)indexes withEvent:(NSEvent *)event {
NSMutableArray *items = [NSMutableArray array];
[indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
P4Item *item = [childrenItems objectAtIndex:idx];
if ([item isEditable])
[items addObject:item];
}];
return items.count;
}
- (NSImage *)collectionView:(NSCollectionView *)aCollectionView draggingImageForItemsAtIndexes:(NSIndexSet *)indexes withEvent:(NSEvent *)event offset:(NSPointPointer)dragImageOffset {
NSImage *result = [collectionView draggingImageForItemsAtIndexes:indexes withEvent:event offset:dragImageOffset];
PSLog(@"");
[result lockFocus];
if (indexes.count > 1) {
NSPoint mouse = (CGPoint) {
result.size.width * 0.5f - dragImageOffset->x,
result.size.height * 0.5f - dragImageOffset->y,
};
NSShadow *shadow = [[NSShadow alloc] init];
[shadow setShadowOffset:NSMakeSize(0.5, 0.5)];
[shadow setShadowBlurRadius:5.0];
[shadow setShadowColor:[NSColor blackColor]];
NSDictionary *attrs = @{
NSShadowAttributeName : shadow,
NSForegroundColorAttributeName : [NSColor whiteColor],
};
NSInteger cornerSize = 10.0f;
NSGradient *gradient = [[NSGradient alloc]
initWithStartingColor:[NSColor colorWithHexString:@"#FCC"]
endingColor:[NSColor redColor]];
NSBezierPath *bezier = [NSBezierPath
bezierPathWithRoundedRect:(CGRect) { mouse, {20.0f, 20.0f} }
xRadius:cornerSize
yRadius:cornerSize];
[gradient drawInBezierPath:bezier angle:90.0f];
BOOL copy = [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask;
NSString *str = [NSString stringWithFormat:@"%@%ld", copy ? @"+": @"", indexes.count];
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
[attrStr drawAtPoint:(CGPoint) {
mouse.x + (20.0f - attrStr.size.width) / 2.0f,
mouse.y + (20.0f - attrStr.size.height) / 2.0f + 1,
}];
[result unlockFocus];
}
return result;
}
- (NSDragOperation)collectionView:(NSCollectionView *)aCollectionView validateDrop:(id<NSDraggingInfo>)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation {
// PSLog(@"dropindex %ld operation %d", (long)*proposedDropIndex, (int)*proposedDropOperation);
NSDragOperation operation = NSDragOperationEvery;
// Check if option key is pressed
if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
operation = NSDragOperationCopy;
// Accept only file types
if ([[[draggingInfo draggingPasteboard] types] indexOfObject:NSFilenamesPboardType] == -1)
return NSDragOperationNone;
if (*proposedDropIndex == -1)
return NSDragOperationNone;
if (*proposedDropOperation == NSCollectionViewDropBefore) {
*proposedDropIndex = 0;
return operation;
}
P4Item *target = [childrenItems objectAtIndex:*proposedDropIndex];
if (![target isDirectory] || ![target isEditable]) {
// Can't drop onto a file. Retarget to the column
*proposedDropIndex = 0;
*proposedDropOperation = NSCollectionViewDropBefore;
if (![workingItem isEditable])
return NSDragOperationNone;
}
return operation;
}
- (BOOL)collectionView:(NSCollectionView *)aCollectionView acceptDrop:(id<NSDraggingInfo>)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation {
// Check if option key is pressed
BOOL copy = draggingInfo.draggingSourceOperationMask == NSDragOperationCopy;
NSArray *paths = [draggingInfo.draggingPasteboard propertyListForType:NSFilenamesPboardType];
if (!paths.count)
return NO;
// Find the target folder
P4Item *target = nil;
if (dropOperation == NSCollectionViewDropBefore)
target = workingItem;
else
target = [childrenItems objectAtIndex:index];
return [self insertFiles:paths intoItem:target copy:copy];
}
#pragma mark Keyboard support
- (void)cancelOperation:(id)sender {
NSIndexSet *indexes = [collectionView selectionIndexes];
if ([indexes count] != 1)
return;
NSInteger index = [indexes lastIndex];
IconViewItem *viewItem = (IconViewItem *)[collectionView itemAtIndex:index];
[viewItem abortEditing];
[self.view.window makeFirstResponder:collectionView];
}
- (void)insertNewline:(id)sender {
NSIndexSet *indexes = [collectionView selectionIndexes];
if ([indexes count] != 1)
return;
NSInteger index = [indexes lastIndex];
IconViewItem *viewItem = (IconViewItem *)[collectionView itemAtIndex:index];
P4Item *item = viewItem.representedObject;
if (![item isEditable])
return;
[viewItem beginEditing];
}
- (void)collectionViewDidReceiveCommandDeleteKey:(NSCollectionView *)aCollectionView {
if (![collectionView selectionIndexes])
return;
NSArray *selectedItems = [self selectedItems];
[rootItem performAction:@selector(deleteItems:)
items:selectedItems
delegate:[self.view.window windowController]];
}
- (void)collectionViewDidReceiveSpacebarKey:(NSCollectionView *)aCollectionView {
QLPreviewPanel *previewPanel = [QLPreviewPanel sharedPreviewPanel];
// Dismiss if visible
if ([QLPreviewPanel sharedPreviewPanelExists] && [previewPanel isVisible]) {
[previewPanel orderOut:nil];
return;
}
if (![collectionView selectionIndexes])
return;
[previewPanel updateController];
[previewPanel makeKeyAndOrderFront:nil];
}
#pragma mark - BrowserViewController Overrides
- (NSArray *)selectedItems {
NSMutableIndexSet *indexes = [[collectionView selectionIndexes] mutableCopy];
// Clip indexes beyond childrens array
[indexes removeIndexesInRange:(NSRange) { childrenItems.count, NSIntegerMax }];
if (!indexes.count)
return workingItem ? @[ workingItem ] : nil;
return [childrenItems objectsAtIndexes:indexes];
}
- (void)setRootItem:(P4Item *)item {
[super setRootItem:item];
childrenItems = workingItem.children.mutableCopy;
[collectionView setContent:childrenItems];
[collectionView setSelectionIndexes:nil];
}
- (void)setWorkingItem:(P4Item *)item {
[super setWorkingItem:item];
[self selectionChanged:item];
if (!workingItem)
return;
childrenItems = workingItem.children.mutableCopy;
[collectionView setContent:childrenItems];
[collectionView setSelectionIndexes:nil];
}
- (void)setSelectedIndexes:(NSIndexSet *)indexes {
[collectionView setSelectionIndexes:indexes];
[collectionView scrollRectToVisible:[collectionView frameForItemAtIndex:indexes.firstIndex]];
}
- (void)refresh {
[childrenItems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[[collectionView itemAtIndex:idx] setRepresentedObject:obj];
}];
}
- (void)willLoadPath:(NSString *)path {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showLoadingOverlay) object:nil];
[self performSelector:@selector(showLoadingOverlay) withObject:nil afterDelay:0.25f];
reloadPath = path;
if (isReloading) {
reloadSelection = [[self selectedItems] valueForKey:@"path"];
}
}
- (void)showVersions:(P4Item *)item {
NSInteger idx = [[collectionView selectionIndexes] firstIndex];
IconViewItem *iconItem = (IconViewItem *)[collectionView itemAtIndex:idx];
VersionsViewController *controller = [[VersionsViewController alloc]
initWithItem:item
actionDelegate:[self.view.window windowController]];
popover = [[NSPopover alloc] init];
[popover setBehavior:NSPopoverBehaviorTransient];
[popover setContentViewController:controller];
[popover showRelativeToRect:[iconItem.view frame]
ofView:[iconItem.view superview]
preferredEdge:NSMaxYEdge];
popover.delegate = self;
[(PSCustomScrollView *)self.view setScrollDisabled:YES];
// [popover setBehavior:NSPopoverBehaviorApplicationDefined];
// NSWindow *popoverWindow = popover.contentViewController.view.window;
// [NSApp runModalForWindow:popoverWindow];
}
- (void)editItemName:(P4Item*)directoryItem {
NSInteger index = [childrenItems indexOfObjectIdenticalTo:directoryItem];
if (index == NSNotFound)
return;
[collectionView scrollRectToVisible:[collectionView frameForItemAtIndex:index]];
IconViewItem *viewItem = (IconViewItem *)[collectionView itemAtIndex:index];
P4Item *item = viewItem.representedObject;
if (![item isEditable])
return;
[viewItem beginEditing];
}
#pragma mark - NSPopoverDelegate
- (void)popoverDidClose:(NSNotification *)notification {
[(PSCustomScrollView *)self.view setScrollDisabled:NO];
}
#pragma mark - IconViewItemDelegate
- (void)iconViewItemSelected:(IconViewItem *)iconItem {
}
- (void)iconViewItemOpened:(IconViewItem *)iconItem {
P4Item *item = iconItem.representedObject;
if ([item isDirectory]) {
[self setWorkingItem:item];
} else {
// Perform default item action
P4ItemAction *action = [item defaultAction];
action.delegate = [self.view.window windowController];
[action performAction];
}
}
- (void)iconViewItemDidEndEditing:(IconViewItem *)iconViewItem {
[self renameItem:iconViewItem.representedObject name:iconViewItem.title];
}
#pragma mark - P4Item delegate
- (void)itemDidLoad:(P4Item *)item {
[super itemDidLoad:item];
if (isLoading) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showLoadingOverlay) object:nil];
[loadingOverlay removeFromSuperview];
P4Item *item = [rootItem cachedItemForPath:reloadPath];
if (!item) {
[self failWithError:
[NSError errorWithFormat:@"Couldn't find file at %@", reloadPath]];
isLoading = isReloading = NO;
return;
}
isLoading = NO;
workingItem = nil;
[self setSelectedItems:@[ item ]];
if (isReloading) {
isReloading = NO;
// Find items for previously selected paths
NSInteger idx = 0;
NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
for (P4Item *child in item.children) {
if ([reloadSelection containsObject:child.path])
[indexes addIndex:idx];
idx++;
}
[collectionView setSelectionIndexes:indexes];
}
return;
}
if (item == workingItem) {
childrenItems = [item children].mutableCopy;
[collectionView setContent:childrenItems];
[childrenItems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[[collectionView itemAtIndex:idx] setRepresentedObject:obj];
}];
} else if (item.parent == workingItem) {
NSInteger idx = [childrenItems indexOfObjectIdenticalTo:item];
if (idx != NSNotFound)
[[collectionView itemAtIndex:idx] setRepresentedObject:item];
}
}
- (void)itemDidInvalidate:(P4Item *)item {
}
- (void)item:(id)item didFailWithError:(NSError *)error {
[self failWithError:error];
[self itemDidLoad:item];
PSLog(@"IconView error: %@", error.localizedDescription);
}
#pragma mark - Quicklook Panel delegate
- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel {
return YES;
}
- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel {
quickLookPanel = panel;
panel.delegate = self;
panel.dataSource = self;
}
- (void)endPreviewPanelControl:(QLPreviewPanel *)panel {
quickLookPanel = nil;
}
- (BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {
// Redirect all key down events to the browser
if ([event type] == NSKeyDown) {
[collectionView keyDown:event];
return YES;
}
return NO;
}
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel {
quickLookItems = [self selectedItems];
return quickLookItems.count;
}
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index {
return [quickLookItems objectAtIndex:index];
}
- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item {
NSIndexSet *indexes = [collectionView selectionIndexes];
if (!indexes)
return CGRectZero;
IconViewItem *iconItem = (id)[collectionView itemAtIndex:indexes.firstIndex];
quickLookIconImage = iconItem.iconView.image;
// Get image frame
CGRect imageRect = iconItem.iconView.frame;
imageRect.origin = iconItem.iconView.superview.frame.origin;
CGRect itemRect = [collectionView frameForItemAtIndex:indexes.firstIndex];
imageRect.origin.x += itemRect.origin.x;
imageRect.origin.y += itemRect.origin.y;
// Check that the icon rect is visible on screen
CGRect visibleRect = [collectionView visibleRect];
if (!NSIntersectsRect(visibleRect, imageRect))
return CGRectZero;
// Convert icon rect to screen coordinates
imageRect = [collectionView convertRectToBase:imageRect];
imageRect.origin = [[collectionView window] convertBaseToScreen:imageRect.origin];
return imageRect;
}
- (id)previewPanel:(QLPreviewPanel *)panel transitionImageForPreviewItem:(id <QLPreviewItem>)item contentRect:(NSRect *)contentRect {
return quickLookIconImage;
}
#pragma mark - Menu delegate
- (void)menuWillOpen:(NSMenu *)menu {
[menu setAllowsContextMenuPlugIns:NO];
P4Item *item = nil;
NSInteger index = [collectionView clickedIndex];
if (index == NSNotFound) {
item = workingItem;
[collectionView setSelectionIndexes:nil];
} else {
NSIndexSet *set = [collectionView selectionIndexes];
if (set.count > 1 && [set containsIndex:index]) {
// Multiple
[menu setItems:[childrenItems objectsAtIndexes:set]
delegate:[self.view.window windowController]];
return;
} else {
[collectionView setSelectionIndexes:[NSIndexSet indexSetWithIndex:index]];
item = [childrenItems objectAtIndex:index];
}
}
[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/IconView/IconViewController.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|