/* |
File: ATComplexTableViewController.m |
Abstract: The basic controller for the demo app. An instance exists inside the MainMenu.xib file. |
|
Version: 1.3 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2012 Apple Inc. All Rights Reserved. |
|
*/ |
|
#import <QuartzCore/QuartzCore.h> |
|
#import "ATComplexTableViewController.h" |
#import "ATColorView.h" |
#import "ATTableCellView.h" |
#import <AppKit/NSTableView.h> |
#import <AppKit/NSTableCellView.h> |
#import "ATObjectTableRowView.h" |
|
@implementation ATComplexTableViewController |
|
- (void)dealloc { |
[_tableContents release]; |
// Stop any observations that we may have |
for (ATDesktopEntity *imageEntity in _observedVisibleItems) { |
if ([imageEntity isKindOfClass:[ATDesktopImageEntity class]]) { |
[imageEntity removeObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage]; |
} |
} |
[_observedVisibleItems release]; |
[_windowForAnimation release]; |
[super dealloc]; |
} |
|
-(NSString *)windowNibName { |
return @"ATComplexTableViewWindow"; |
} |
|
- (void)windowDidLoad { |
[super windowDidLoad]; |
NSURL *url = [NSURL fileURLWithPath:@"/Library/Desktop Pictures" isDirectory:YES]; |
ATDesktopFolderEntity *primaryFolder = [[[ATDesktopFolderEntity alloc] initWithFileURL:url] autorelease]; |
// Create a flat array of ATDesktopFolderEntity and ATDesktopImageEntity objects to display |
_tableContents = [NSMutableArray new]; |
|
// We first do a pass over the children and add all the images under the "Desktop Pictures" category |
[_tableContents addObject:primaryFolder]; |
for (ATDesktopEntity *entity in primaryFolder.children) { |
if ([entity isKindOfClass:[ATDesktopImageEntity class]]) { |
[_tableContents addObject:entity]; |
} |
} |
|
// Then do another pass through and add all the folders -- including their children. A recursive loop could be used too, but we want to only go one level deep |
for (ATDesktopEntity *entity in primaryFolder.children) { |
if ([entity isKindOfClass:[ATDesktopFolderEntity class]]) { |
[_tableContents addObject:entity]; |
ATDesktopFolderEntity *subFolder = (ATDesktopFolderEntity *)entity; |
for (ATDesktopEntity *subFolderChildEntity in subFolder.children) { |
if ([subFolderChildEntity isKindOfClass:[ATDesktopImageEntity class]]) { |
[_tableContents addObject:subFolderChildEntity]; |
} |
} |
} |
} |
|
_colorViewMain.drawBorder = YES; |
_colorViewMain.backgroundColor = [NSColor whiteColor]; |
|
// Initialize the main image view to our current desktop background. |
NSImage *initialImage = [[NSImage alloc] initByReferencingURL:[[NSWorkspace sharedWorkspace] desktopImageURLForScreen:[NSScreen mainScreen]]]; |
[_imageViewMain setImage:initialImage]; |
[initialImage release]; |
[_tableViewMain setDoubleAction:@selector(tblvwDoubleClick:)]; |
[_tableViewMain setTarget:self]; |
[_tableViewMain reloadData]; |
|
// Allow drags to go everywhere |
[_tableViewMain setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; |
} |
|
- (ATDesktopEntity *)_entityForRow:(NSInteger)row { |
return (ATDesktopEntity *)[_tableContents objectAtIndex:row]; |
} |
|
- (ATDesktopImageEntity *)_imageEntityForRow:(NSInteger)row { |
id result = row != -1 ? [_tableContents objectAtIndex:row] : nil; |
if ([result isKindOfClass:[ATDesktopImageEntity class]]) { |
return result; |
} |
return nil; |
} |
|
// NSTableView delegate and datasource methods |
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { |
return _tableContents.count; |
} |
|
- (void)tableView:(NSTableView *)tableView didRemoveRowView:(ATObjectTableRowView *)rowView forRow:(NSInteger)row { |
// Stop observing visible things |
ATDesktopImageEntity *imageEntity = rowView.objectValue; |
NSInteger index = imageEntity ? [_observedVisibleItems indexOfObject:imageEntity] : NSNotFound; |
if (index != NSNotFound) { |
[imageEntity removeObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage]; |
[_observedVisibleItems removeObjectAtIndex:index]; |
} |
} |
|
- (void)_reloadRowForEntity:(id)object { |
NSInteger row = [_tableContents indexOfObject:object]; |
if (row != NSNotFound) { |
ATDesktopImageEntity *entity = [self _imageEntityForRow:row]; |
ATTableCellView *cellView = [_tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
if (cellView) { |
// Fade the imageView in, and fade the progress indicator out |
[NSAnimationContext beginGrouping]; |
[[NSAnimationContext currentContext] setDuration:0.8]; |
[cellView.imageView setAlphaValue:0]; |
cellView.imageView.image = entity.thumbnailImage; |
[cellView.imageView setHidden:NO]; |
[[cellView.imageView animator] setAlphaValue:1.0]; |
[cellView.progessIndicator setHidden:YES]; |
[NSAnimationContext endGrouping]; |
} |
} |
} |
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { |
if ([keyPath isEqualToString:ATEntityPropertyNamedThumbnailImage]) { |
// Find the row and reload it. |
// Note that KVO notifications may be sent from a background thread (in this case, we know they will be) |
// We should only update the UI on the main thread, and in addition, we use NSRunLoopCommonModes to make sure the UI updates when a modal window is up. |
[self performSelectorOnMainThread:@selector(_reloadRowForEntity:) withObject:object waitUntilDone:NO modes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; |
} |
} |
|
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row { |
// Make the row view keep track of our main model object |
ATObjectTableRowView *result = [[ATObjectTableRowView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
result.objectValue = [self _entityForRow:row]; |
return [result autorelease]; |
} |
|
// We want to make "group rows" for the folders |
- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row { |
if ([[self _entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
return YES; |
} else { |
return NO; |
} |
} |
|
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { |
ATDesktopEntity *entity = [self _entityForRow:row]; |
if ([entity isKindOfClass:[ATDesktopFolderEntity class]]) { |
NSTextField *textField = [tableView makeViewWithIdentifier:@"TextCell" owner:self]; |
[textField setStringValue:entity.title]; |
return textField; |
} else { |
ATTableCellView *cellView = [tableView makeViewWithIdentifier:@"MainCell" owner:self]; |
ATDesktopImageEntity *imageEntity = (ATDesktopImageEntity *)entity; |
cellView.textField.stringValue = entity.title; |
cellView.subTitleTextField.stringValue = imageEntity.fillColorName; |
cellView.colorView.backgroundColor = imageEntity.fillColor; |
cellView.colorView.drawBorder = YES; |
|
// Use KVO to observe for changes of the thumbnail image |
if (_observedVisibleItems == nil) { |
_observedVisibleItems = [NSMutableArray new]; |
} |
if (![_observedVisibleItems containsObject:entity]) { |
[imageEntity addObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage options:0 context:NULL]; |
[imageEntity loadImage]; |
[_observedVisibleItems addObject:imageEntity]; |
} |
|
// Hide/show progress based on the thumbnail image being loaded or not. |
if (imageEntity.thumbnailImage == nil) { |
[cellView.progessIndicator setHidden:NO]; |
[cellView.progessIndicator startAnimation:nil]; |
[cellView.imageView setHidden:YES]; |
} else { |
[cellView.imageView setImage:imageEntity.thumbnailImage]; |
} |
|
// Size/hide things based on the row size |
[cellView layoutViewsForSmallSize:_useSmallRowHeight animated:NO]; |
return cellView; |
} |
} |
|
// We make the "group rows" have the standard height, while all other image rows have a larger height |
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { |
if ([[self _entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
return [tableView rowHeight]; |
} else { |
return _useSmallRowHeight ? 30.0 : 75.0; |
} |
} |
|
- (void)_animationDoneTimerFired:(NSTimer *)timer { |
[_animationDoneTimer release]; |
_animationDoneTimer = nil; |
|
// Set the normal one to have the final image and alpha value. Set the image and update us before ordering out the animation window. |
[_imageViewMain setImage:[_imageViewForTransition image]]; |
[_imageViewMain setAlphaValue:1.0]; |
[_imageViewMain.window displayIfNeeded]; // This displays right now, and prevents flicker if the animation window orders out before our display happened |
|
// Hide the animation window |
[_windowForAnimation orderOut:nil]; |
} |
|
- (void)_ensureAnimationWindowCreated { |
if (_windowForAnimation == nil) { |
NSRect contentRect = _imageViewForTransition.frame; |
contentRect.origin = NSZeroPoint; |
_imageViewForTransition.frame = contentRect; |
_windowForAnimation = [[NSWindow alloc] initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; |
[_windowForAnimation setReleasedWhenClosed:NO]; |
[_windowForAnimation setOpaque:NO]; |
[_windowForAnimation setBackgroundColor:[NSColor clearColor]]; |
// For ease of use, we setup the _imageViewForTransition in the nib and add it as a subview |
[[_windowForAnimation contentView] addSubview:_imageViewForTransition]; |
} |
} |
|
- (NSRect)_screenImageRectForRow:(NSInteger)row { |
NSRect result = NSZeroRect; |
// We always want to try to get a view back to do the animation from that rect |
ATTableCellView *cellView = [_tableViewMain viewAtColumn:0 row:row makeIfNecessary:YES]; |
if (cellView) { |
result = [cellView.imageView convertRect:cellView.imageView.bounds toView:nil]; |
// Convert that frame to the right coordinate system |
result.origin = [cellView.window convertBaseToScreen:result.origin]; |
} |
return result; |
} |
|
- (NSRect)_finalScreenImageRect { |
// We are animating to right over the image view's frame. Convert to the right screen coordinates and animate the window there. |
NSRect finalImageFrame = [_imageViewMain.superview convertRect:_imageViewMain.frame toView:nil]; |
finalImageFrame.origin = [_imageViewMain.window convertBaseToScreen:finalImageFrame.origin]; |
return finalImageFrame; |
} |
|
- (void)_stopExistingTimerIfNeeded { |
// We want to stop any previous animations |
if (_animationDoneTimer != nil) { |
[_animationDoneTimer invalidate]; |
[_animationDoneTimer release]; |
_animationDoneTimer = nil; |
} |
} |
|
- (void)_animateImageFromRow:(NSInteger)row { |
[self _stopExistingTimerIfNeeded]; |
|
// We create a window to do the animation. The purpose of using a window is to allow an animation to happen from a non-layer backed view to over a layer-backed view. |
// We easily could use a sibling view if everything was layer backed, or non-layer backed. |
[self _ensureAnimationWindowCreated]; |
|
// Grab our model object for this row |
ATDesktopImageEntity *entity = [self _imageEntityForRow:row]; |
|
// Set some initial state |
NSRect startingWindowFrame = [self _screenImageRectForRow:row]; |
[_windowForAnimation setFrame:startingWindowFrame display:NO]; |
[_imageViewForTransition setImage:entity.thumbnailImage]; |
[_imageViewMain setAlphaValue:1.0]; |
|
// Bring the window above our existing window |
[_windowForAnimation orderFront:nil]; |
|
// We want to sync all the animations together. We use a grouping to do that. |
[NSAnimationContext beginGrouping]; |
{ |
NSTimeInterval animationDuration = 0.4; |
// Do a slow animation if the shift key is down |
if (([NSEvent modifierFlags] & NSShiftKeyMask) != 0) { |
animationDuration *= 4; |
} |
|
[[NSAnimationContext currentContext] setDuration:animationDuration]; |
|
NSRect finalImageFrame = [self _finalScreenImageRect]; |
[[_windowForAnimation animator] setFrame:finalImageFrame display:YES]; |
|
// Alpha/opacity animations only work for layer-backed views |
[[_imageViewMain animator] setAlphaValue:0.25]; |
|
// Also, animate the background color. This is done with the layer. See ATColorView.h/.m |
[[_colorViewMain animator] setBackgroundColor:entity.fillColor]; |
|
// At the end of the animation we want to do some cleanup. We keep track of the timer so we can stop the operation if we need to. |
_animationDoneTimer = [[NSTimer scheduledTimerWithTimeInterval:animationDuration target:self selector:@selector(_animationDoneTimerFired:) userInfo:nil repeats:NO] retain]; |
} |
[NSAnimationContext endGrouping]; |
} |
|
- (IBAction)btnSetAsDesktopWallpaperClick:(id)sender { |
NSInteger selectedRow = [_tableViewMain selectedRow]; |
if (selectedRow != -1) { |
ATDesktopEntity *entity = [_tableContents objectAtIndex:selectedRow]; |
if ([entity isKindOfClass:[ATDesktopImageEntity class]]) { |
ATDesktopImageEntity *desktopImageEntity = (ATDesktopImageEntity *)entity; |
NSError *error; |
NSURL *imageURL = desktopImageEntity.fileURL; |
NSColor *fillColor = desktopImageEntity.fillColor; |
|
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:fillColor, NSWorkspaceDesktopImageFillColorKey, [NSNumber numberWithBool:NO], NSWorkspaceDesktopImageAllowClippingKey, [NSNumber numberWithInteger:NSImageScaleProportionallyUpOrDown], NSWorkspaceDesktopImageScalingKey, nil]; |
BOOL result = [[NSWorkspace sharedWorkspace] setDesktopImageURL:imageURL forScreen:[[NSScreen screens] lastObject] options:options error:&error]; |
if (!result) { |
[NSApp presentError:error]; |
} |
} |
} |
} |
|
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes { |
// We don't want to change the selection if the user clicked in the fill color area |
NSInteger row = [tableView clickedRow]; |
if (row != -1 && ![self tableView:tableView isGroupRow:row]) { |
ATTableCellView *cellView = [_tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
if (cellView) { |
// Use hit testing to see if is a color view; if so, don't let it change the selection |
NSPoint windowPoint = [[NSApp currentEvent] locationInWindow]; |
NSPoint point = [cellView.superview convertPoint:windowPoint fromView:nil]; |
NSView *view = [cellView hitTest:point]; |
if ([view isKindOfClass:[ATColorView class]]) { |
// Don't allow the selection change |
return [tableView selectedRowIndexes]; |
} |
} |
} |
return proposedSelectionIndexes; |
} |
|
- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex { |
return 200; |
} |
|
- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex { |
// Make sure the view on the right has at least 200 px wide |
CGFloat splitViewWidth = splitView.bounds.size.width; |
return splitViewWidth - 200; |
} |
|
- (void)colorTableController:(ATColorTableController *)controller didChooseColor:(NSColor *)color named:(NSString *)colorName { |
if (_rowForEditingColor != -1) { |
// Update our model |
ATDesktopImageEntity *entity = [self _imageEntityForRow:_rowForEditingColor]; |
entity.fillColorName = colorName; |
entity.fillColor = color; |
|
// Update the view; we could reload things, but this is faster |
ATTableCellView *cellView = [_tableViewMain viewAtColumn:0 row:_rowForEditingColor makeIfNecessary:NO]; |
cellView.colorView.backgroundColor = color; |
cellView.subTitleTextField.stringValue = colorName; |
} else { |
// With no row we are just setting the background color. |
_colorViewMain.backgroundColor = color; |
} |
} |
|
- (void)_editColorOnRow:(NSInteger)row { |
_rowForEditingColor = row; |
ATTableCellView *cellView = [_tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
|
NSColor *color = cellView.colorView.backgroundColor; |
[[ATColorTableController sharedColorTableController] setDelegate:self]; |
[[ATColorTableController sharedColorTableController] editColor:color withPositioningView:cellView.colorView]; |
} |
|
- (IBAction)cellColorViewClicked:(id)sender { |
// Find out what row it was in and edit that color with the popup |
NSInteger row = [_tableViewMain rowForView:sender]; |
if (row != -1) { |
[self _editColorOnRow:row]; |
} |
} |
|
- (IBAction)textTitleChanged:(id)sender { |
NSInteger row = [_tableViewMain rowForView:sender]; |
if (row != -1) { |
ATDesktopImageEntity *entity = [self _imageEntityForRow:row]; |
entity.title = [sender stringValue]; |
} |
} |
|
- (IBAction)colorTitleChanged:(id)sender { |
NSInteger row = [_tableViewMain rowForView:sender]; |
if (row != -1) { |
ATDesktopImageEntity *entity = [self _imageEntityForRow:row]; |
entity.fillColorName = [sender stringValue]; |
} |
} |
|
- (void)_selectRowStartingAtRow:(NSInteger)row { |
if ([_tableViewMain selectedRow] == -1) { |
if (row == -1) { |
row = 0; |
} |
|
// Select the same or next row (if possible) but skip group rows |
while (row < [_tableViewMain numberOfRows]) { |
if (![self tableView:_tableViewMain isGroupRow:row]) { |
[_tableViewMain selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; |
return; |
} |
row++; |
} |
row = [_tableViewMain numberOfRows] - 1; |
while (row >= 0) { |
if (![self tableView:_tableViewMain isGroupRow:row]) { |
[_tableViewMain selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; |
return; |
} |
row--; |
} |
} |
} |
|
- (IBAction)btnRemoveRowClick:(id)sender { |
NSInteger row = [_tableViewMain rowForView:sender]; |
if (row != -1) { |
[_tableContents removeObjectAtIndex:row]; |
[_tableViewMain removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] withAnimation:NSTableViewAnimationEffectFade]; |
[self _selectRowStartingAtRow:row]; |
} |
} |
|
- (IBAction)btnRemoveAllSelectedRowsClick:(id)sender { |
[_tableContents removeObjectsAtIndexes:[_tableViewMain selectedRowIndexes]]; |
[_tableViewMain removeRowsAtIndexes:[_tableViewMain selectedRowIndexes] withAnimation:NSTableViewAnimationEffectFade]; |
} |
|
- (IBAction)btnInsertNewRow:(id)sender { |
NSURL *url = [[NSWorkspace sharedWorkspace] desktopImageURLForScreen:[NSScreen mainScreen]]; |
ATDesktopImageEntity *entity = [[ATDesktopImageEntity alloc] initWithFileURL:url]; |
entity.fillColor = [_colorViewMain backgroundColor]; |
entity.fillColorName = @"Untitled Color"; |
NSInteger index = [_tableViewMain selectedRow]; |
if (index == -1) { |
if (_tableViewMain.numberOfRows == 0) { |
index = 0; |
} else { |
index = 1; |
} |
} |
|
[_tableContents insertObject:entity atIndex:index]; |
[entity release]; |
[_tableViewMain beginUpdates]; |
[_tableViewMain insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:index] withAnimation:NSTableViewAnimationEffectFade]; |
[_tableViewMain scrollRowToVisible:index]; |
[_tableViewMain endUpdates]; |
} |
|
- (IBAction)mainColorViewClicked:(id)sender { |
_rowForEditingColor = -1; |
|
NSColor *color = _colorViewMain.backgroundColor; |
|
[[ATColorTableController sharedColorTableController] setDelegate:self]; |
[[ATColorTableController sharedColorTableController] editColor:color withPositioningView:_colorViewMain]; |
} |
|
- (IBAction)cellBtnAnimateImageClick:(id)sender { |
NSInteger selectedRow = [_tableViewMain rowForView:sender]; |
if (selectedRow != -1) { |
[_tableViewMain scrollRowToVisible:selectedRow]; |
// Only animate if the thumbnail image is loaded |
ATDesktopImageEntity *entity = [_tableContents objectAtIndex:selectedRow]; |
if (entity.thumbnailImage != nil) { |
[self _animateImageFromRow:selectedRow]; |
} |
} else { |
[_imageViewMain setImage:nil]; |
} |
} |
|
- (IBAction)chkbxHorizontalGridLineClicked:(id)sender { |
if ([(NSButton *)sender state] == 0) { |
[_tableViewMain setGridStyleMask:NSTableViewGridNone]; |
} else { |
[_tableViewMain setGridStyleMask:NSTableViewSolidHorizontalGridLineMask]; |
} |
} |
|
- (IBAction)chkbxUseSmallRowHeightClicked:(id)sender { |
_useSmallRowHeight = [(NSButton *)sender state] == 1; |
// Reload the height for all non group rows |
NSMutableIndexSet *indexesToNoteHeightChanges = [NSMutableIndexSet indexSet]; |
for (NSInteger row = 0; row < _tableContents.count; row++) { |
if (![[self _entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
[indexesToNoteHeightChanges addIndex:row]; |
} |
} |
// We also want to synchronize our own animations with the height change. We do this by creating our own animation grouping |
[NSAnimationContext beginGrouping]; |
[[NSAnimationContext currentContext] setDuration:1.5]; |
|
// Update all the current visible views animated in sync with the row heights |
[_tableViewMain enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) { |
for (NSInteger i = 0; i < [_tableViewMain tableColumns].count; i++) { |
NSView *view = [_tableViewMain viewAtColumn:i row:row makeIfNecessary:NO]; |
if (view && [view isKindOfClass:[ATTableCellView class]]) { |
[(ATTableCellView *)view layoutViewsForSmallSize:_useSmallRowHeight animated:YES]; |
} |
} |
}]; |
|
[_tableViewMain noteHeightOfRowsWithIndexesChanged:indexesToNoteHeightChanges]; |
|
[NSAnimationContext endGrouping]; |
} |
|
- (IBAction)chkbxFloatGroupRowsClicked:(id)sender { |
BOOL checked = [(NSButton *)sender state] == 1; |
[_tableViewMain setFloatsGroupRows:checked]; |
} |
|
- (IBAction)btnBeginUpdatesClicked:(id)sender { |
[_tableViewMain beginUpdates]; |
} |
|
- (IBAction)btnEndUpdatesClicked:(id)sender { |
[_tableViewMain endUpdates]; |
} |
|
- (IBAction)btnMoveRowClick:(id)sender { |
NSInteger fromRow = [_txtFldFromRow integerValue]; |
NSInteger toRow = [_txtFldToRow integerValue]; |
|
[_tableViewMain beginUpdates]; |
|
[_tableViewMain moveRowAtIndex:fromRow toIndex:toRow]; |
|
id object = [[_tableContents objectAtIndex:fromRow] retain]; |
[_tableContents removeObjectAtIndex:fromRow]; |
[_tableContents insertObject:object atIndex:toRow]; |
[object release]; |
|
[_tableViewMain endUpdates]; |
} |
|
- (IBAction)tblvwDoubleClick:(id)sender { |
NSInteger row = [_tableViewMain selectedRow]; |
if (row != -1) { |
ATDesktopEntity *entity = [self _entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:[entity.fileURL path] inFileViewerRootedAtPath:nil]; |
} |
} |
|
- (IBAction)btnManuallyBeginEditingClick:(id)sender { |
NSInteger row = [_txtFldRowToEdit integerValue]; |
[_tableViewMain editColumn:0 row:row withEvent:nil select:YES]; |
} |
|
- (NSIndexSet *)_indexesToProcessForContextMenu { |
NSIndexSet *selectedIndexes = [_tableViewMain selectedRowIndexes]; |
// If the clicked row was in the selectedIndexes, then we process all selectedIndexes. Otherwise, we process just the clickedRow |
if ([_tableViewMain clickedRow] != -1 && ![selectedIndexes containsIndex:[_tableViewMain clickedRow]]) { |
selectedIndexes = [NSIndexSet indexSetWithIndex:[_tableViewMain clickedRow]]; |
} |
return selectedIndexes; |
} |
|
- (IBAction)mnuRevealInFinderSelected:(id)sender { |
NSIndexSet *selectedIndexes = [self _indexesToProcessForContextMenu]; |
[selectedIndexes enumerateIndexesUsingBlock:^(NSUInteger row, BOOL *stop) { |
ATDesktopEntity *entity = [self _entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:[entity.fileURL path] inFileViewerRootedAtPath:nil]; |
}]; |
} |
|
- (IBAction)btnRevealInFinderSelected:(id)sender { |
NSInteger row = [_tableViewMain rowForView:sender]; |
ATDesktopEntity *entity = [self _entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:[entity.fileURL path] inFileViewerRootedAtPath:nil]; |
} |
|
- (IBAction)mnuRemoveRowSelected:(id)sender { |
NSIndexSet *indexes = [self _indexesToProcessForContextMenu]; |
[_tableViewMain beginUpdates]; |
[_tableContents removeObjectsAtIndexes:indexes]; |
[_tableViewMain removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade]; |
[_tableViewMain endUpdates]; |
} |
|
- (IBAction)btnChangeSelectionAnimated:(id)sender { |
if ([_tableViewMain selectedRow] != -1) { |
[[_tableViewMain animator] selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; |
} else { |
[[_tableViewMain animator] selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; |
} |
} |
|
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row { |
// Support for us being a dragging source |
return [self _entityForRow:row]; |
} |
|
@end |