diff --git a/Application/MediaKeysApplication.m b/Application/MediaKeysApplication.m index a62117c8c..57d0df88f 100644 --- a/Application/MediaKeysApplication.m +++ b/Application/MediaKeysApplication.m @@ -101,17 +101,17 @@ { BOOL shouldHandleMediaKeyEventLocally = ![SPMediaKeyTap usesGlobalMediaKeyTap]; - if(shouldHandleMediaKeyEventLocally && [event type] == NSSystemDefined && [event subtype] == 8 ) - { - [self mediaKeyTap:nil receivedMediaKeyEvent:event]; - } + if(shouldHandleMediaKeyEventLocally && [event type] == NSEventTypeSystemDefined && [event subtype] == 8 ) + { + [self mediaKeyTap:nil receivedMediaKeyEvent:event]; + } - [super sendEvent: event]; + [super sendEvent: event]; } -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; { - NSAssert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:"); + NSAssert([event type] == NSEventTypeSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys, @"Unexpected NSEvent in mediaKeyTap:receivedMediaKeyEvent:"); int keyCode = (([event data1] & 0xFFFF0000) >> 16); int keyFlags = ([event data1] & 0x0000FFFF); diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index 6474b5fb8..8e9453678 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -9,6 +9,7 @@ #import "Node.h" #import "Logging.h" +#import "BufferChain.h" @implementation Node diff --git a/Audio/CogAudio.xcodeproj/xcshareddata/xcschemes/CogAudio Framework.xcscheme b/Audio/CogAudio.xcodeproj/xcshareddata/xcschemes/CogAudio Framework.xcscheme index cb4f4a562..df25b15b9 100644 --- a/Audio/CogAudio.xcodeproj/xcshareddata/xcschemes/CogAudio Framework.xcscheme +++ b/Audio/CogAudio.xcodeproj/xcshareddata/xcschemes/CogAudio Framework.xcscheme @@ -1,6 +1,6 @@ #import #endif diff --git a/Cog.xcodeproj/xcshareddata/xcschemes/Cog.xcscheme b/Cog.xcodeproj/xcshareddata/xcschemes/Cog.xcscheme index 0924d7133..bda36aac4 100644 --- a/Cog.xcodeproj/xcshareddata/xcschemes/Cog.xcscheme +++ b/Cog.xcodeproj/xcshareddata/xcschemes/Cog.xcscheme @@ -1,6 +1,6 @@ + +@property(nonatomic, weak) IBOutlet NSOutlineView *outlineView; +@property(nonatomic, weak) IBOutlet NSPathControl *pathControl; +@property(nonatomic, weak) IBOutlet PathWatcher *watcher; -- (NSURL *)rootURL; -- (void)setRootURL:(NSURL *)rootURL; - (void)changeURL:(NSURL *)rootURL; - (void)reloadPathNode:(PathNode *)item; diff --git a/FileTree/FileTreeDataSource.m b/FileTree/FileTreeDataSource.m index 6c644c3b3..9bfb4e82a 100644 --- a/FileTree/FileTreeDataSource.m +++ b/FileTree/FileTreeDataSource.m @@ -8,176 +8,159 @@ #import "FileTreeDataSource.h" -#import "DNDArrayController.h" - #import "DirectoryNode.h" #import "PathWatcher.h" #import "Logging.h" -@implementation FileTreeDataSource - -+ (void)initialize -{ - NSMutableDictionary *userDefaultsValuesDict = [NSMutableDictionary dictionary]; - - [userDefaultsValuesDict setObject:[[NSURL fileURLWithPath:[@"~/Music" stringByExpandingTildeInPath]] absoluteString] forKey:@"fileTreeRootURL"]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; +NSURL *defaultMusicDirectory() { + return [[NSFileManager defaultManager] URLForDirectory:NSMusicDirectory + inDomain:NSUserDomainMask + appropriateForURL:nil + create:NO + error:nil]; } -- (void)awakeFromNib -{ - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.fileTreeRootURL" options:0 context:nil]; - - [self setRootURL: [NSURL URLWithString:[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"]]]; +@interface FileTreeDataSource() - [pathControl setTarget:self]; - [pathControl setAction:@selector(pathControlAction:)]; +@property NSURL *rootURL; + +@end + +@implementation FileTreeDataSource { + PathNode *rootNode; } -- (void) observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - DLog(@"File tree root URL: %@\n", [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"]); - if ([keyPath isEqualToString:@"values.fileTreeRootURL"]) { - [self setRootURL:[NSURL URLWithString:[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"]]]; - } ++ (void)initialize { + NSString *path = [defaultMusicDirectory() absoluteString]; + NSDictionary *userDefaultsValuesDict = @{@"fileTreeRootURL": path}; + [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; } -- (void)changeURL:(NSURL *)url -{ - if (url != nil) - { - [[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString] forKey:@"fileTreeRootURL"]; - } +- (void)awakeFromNib { + [self.pathControl setTarget:self]; + [self.pathControl setAction:@selector(pathControlAction:)]; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self + forKeyPath:@"values.fileTreeRootURL" + options:NSKeyValueObservingOptionNew | + NSKeyValueObservingOptionInitial + context:nil]; } -- (void)pathControlAction:(id)sender -{ - if ([pathControl clickedPathComponentCell] != nil && [[pathControl clickedPathComponentCell] URL] != nil) - { - [self changeURL:[[pathControl clickedPathComponentCell] URL]]; - } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if ([keyPath isEqualToString:@"values.fileTreeRootURL"]) { + NSString *url = + [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"]; + DLog(@"File tree root URL: %@\n", url); + self.rootURL = [NSURL URLWithString:url]; + } } -- (NSURL *)rootURL -{ - return [rootNode URL]; +- (void)changeURL:(NSURL *)url { + if (url != nil) { + [[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString] + forKey:@"fileTreeRootURL"]; + } } -- (void)setRootURL: (NSURL *)rootURL -{ - if (![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) - rootURL = [NSURL fileURLWithPath:[@"~/Music" stringByExpandingTildeInPath]]; - - rootNode = [[DirectoryNode alloc] initWithDataSource:self url:rootURL]; - - [watcher setPath:[rootURL path]]; - - [self reloadPathNode:rootNode]; +- (void)pathControlAction:(id)sender { + NSPathControlItem *item = [self.pathControl clickedPathItem]; + if (item != nil && item.URL != nil) { + [self changeURL:item.URL]; + } } -- (PathNode *)nodeForPath:(NSString *)path -{ - NSString *relativePath = [[path stringByReplacingOccurrencesOfString:[[[self rootURL] path] stringByAppendingString:@"/"] - withString:@"" - options:NSAnchoredSearch - range:NSMakeRange(0, [path length]) - ] stringByStandardizingPath]; - PathNode *node = rootNode; - DLog(@"Root | Relative | Path: %@ | %@ | %@",[[self rootURL] path], relativePath, path); - for (NSString *c in [relativePath pathComponents]) - { - DLog(@"COMPONENT: %@", c); - BOOL found = NO; - for (PathNode *subnode in [node subpaths]) { - if ([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) { - node = subnode; - found = YES; - } - } - - if (!found) - { - DLog(@"Not found!"); - return nil; - } - } - - return node; +- (NSURL *)rootURL { + return [rootNode URL]; } -- (void)pathDidChange:(NSString *)path -{ - DLog(@"PATH DID CHANGE: %@", path); - //Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node] - PathNode *node = [self nodeForPath:path]; - DLog(@"NODE IS: %@", node); - [node updatePath]; - [self reloadPathNode:node]; +- (void)setRootURL:(NSURL *)rootURL { + if (![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) { + rootURL = defaultMusicDirectory(); + } + + rootNode = [[DirectoryNode alloc] initWithDataSource:self url:rootURL]; + + [self.watcher setPath:[rootURL path]]; + + [self reloadPathNode:rootNode]; } -- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item -{ - PathNode *n = (item == nil ? rootNode : item); +- (PathNode *)nodeForPath:(NSString *)path { + NSString *relativePath = [[path stringByReplacingOccurrencesOfString:[[[self rootURL] path] stringByAppendingString:@"/"] + withString:@"" + options:NSAnchoredSearch + range:NSMakeRange(0, [path length]) + ] stringByStandardizingPath]; + PathNode *node = rootNode; + DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path); + for (NSString *c in [relativePath pathComponents]) { + DLog(@"COMPONENT: %@", c); + BOOL found = NO; + for (PathNode *subnode in [node subpaths]) { + if ([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) { + node = subnode; + found = YES; + } + } + + if (!found) { + DLog(@"Not found!"); + return nil; + } + } + + return node; +} + +- (void)pathDidChange:(NSString *)path { + DLog(@"PATH DID CHANGE: %@", path); + //Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node] + PathNode *node = [self nodeForPath:path]; + DLog(@"NODE IS: %@", node); + [node updatePath]; + [self reloadPathNode:node]; +} + +- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { + PathNode *n = (item == nil ? rootNode : item); return (int) [[n subpaths] count]; } - -- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item -{ - PathNode *n = (item == nil ? rootNode : item); +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { + PathNode *n = (item == nil ? rootNode : item); - return ([n isLeaf] == NO); + return ![n isLeaf]; } -- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item -{ - PathNode *n = (item == nil ? rootNode : item); +- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { + PathNode *n = (item == nil ? rootNode : item); - return [[n subpaths] objectAtIndex:index]; + return [n subpaths][(NSUInteger) index]; } -- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item -{ - PathNode *n = (item == nil ? rootNode : item); +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { + PathNode *n = (item == nil ? rootNode : item); - return n; + return n; } -//Drag it drop it -- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard { - //Get selected paths - NSMutableArray *urls = [NSMutableArray arrayWithCapacity:[items count]]; - NSMutableArray *paths = [NSMutableArray arrayWithCapacity:[items count]]; - - for (id p in items) { - [urls addObject:[p URL]]; - [paths addObject:[[p URL] path]]; - } - DLog(@"Paths: %@", paths); - [pboard declareTypes:[NSArray arrayWithObjects:CogUrlsPboardType,nil] owner:nil]; //add it to pboard - [pboard setData:[NSArchiver archivedDataWithRootObject:urls] forType:CogUrlsPboardType]; - [pboard addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self]; - [pboard setPropertyList:paths forType:NSFilenamesPboardType]; - - return YES; +- (id )outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item { + NSPasteboardItem *paste = [[NSPasteboardItem alloc] init]; + [paste setData:[[item URL] dataRepresentation] forType:NSPasteboardTypeFileURL]; + return paste; } -- (void)reloadPathNode:(PathNode *)item -{ - if (item == rootNode) - { - [outlineView reloadData]; - } - else - { - [outlineView reloadItem:item reloadChildren:YES]; - } +- (void)reloadPathNode:(PathNode *)item { + if (item == rootNode) { + [self.outlineView reloadData]; + } else { + [self.outlineView reloadItem:item reloadChildren:YES]; + } } @end diff --git a/FileTree/FileTreeOutlineView.m b/FileTree/FileTreeOutlineView.m index 6a21949ab..286c27f0c 100644 --- a/FileTree/FileTreeOutlineView.m +++ b/FileTree/FileTreeOutlineView.m @@ -15,62 +15,62 @@ - (void)awakeFromNib { - [self setDoubleAction:@selector(addToPlaylistExternal:)]; - [self setTarget:[self delegate]]; + [self setDoubleAction:@selector(addToPlaylistExternal:)]; + [self setTarget:[self delegate]]; } - (void)keyDown:(NSEvent *)e { - unsigned int modifiers = [e modifierFlags] & (NSCommandKeyMask | NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask); + unsigned int modifiers = [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption); NSString *characters = [e characters]; - unichar c; - - if ([characters length] == 1) - { - c = [characters characterAtIndex:0]; - - if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) - { - [(FileTreeController *)[self delegate] addToPlaylistExternal:self]; - - return; - } - else if (modifiers == 0 && c == ' ') - { - [(FileTreeController *)[self delegate] playPauseResume:self]; - return; - } - } - - [super keyDown:e]; - - return; + unichar c; + + if ([characters length] == 1) + { + c = [characters characterAtIndex:0]; + + if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) + { + [(FileTreeController *)[self delegate] addToPlaylistExternal:self]; + + return; + } + else if (modifiers == 0 && c == ' ') + { + [(FileTreeController *)[self delegate] playPauseResume:self]; + return; + } + } + + [super keyDown:e]; + + return; } // enables right-click selection for "Show in Finder" contextual menu -(NSMenu*)menuForEvent:(NSEvent*)event { - //Find which row is under the cursor - [[self window] makeFirstResponder:self]; - NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil]; - NSInteger iRow = [self rowAtPoint:menuPoint]; - NSMenu* contextMenu = [self menu]; - - /* Update the file tree selection before showing menu - Preserves the selection if the row under the mouse is selected (to allow for - multiple items to be selected), otherwise selects the row under the mouse */ - BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:iRow]; - - if (iRow == -1) - { - [self deselectAll:self]; - } - else if (!currentRowIsSelected) - { - [self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO]; - } - - return contextMenu; + //Find which row is under the cursor + [[self window] makeFirstResponder:self]; + NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil]; + NSInteger iRow = [self rowAtPoint:menuPoint]; + NSMenu* contextMenu = [self menu]; + + /* Update the file tree selection before showing menu + Preserves the selection if the row under the mouse is selected (to allow for + multiple items to be selected), otherwise selects the row under the mouse */ + BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:iRow]; + + if (iRow == -1) + { + [self deselectAll:self]; + } + else if (!currentRowIsSelected) + { + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO]; + } + + return contextMenu; } @end diff --git a/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj b/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj index e423e13c5..8bc81e002 100644 --- a/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj +++ b/Frameworks/AdPlug/libAdPlug.xcodeproj/project.pbxproj @@ -738,6 +738,7 @@ ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; @@ -792,6 +793,7 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; diff --git a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj index a4a09673d..7606f500f 100644 --- a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj +++ b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj @@ -606,6 +606,7 @@ GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -645,6 +646,7 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; diff --git a/Frameworks/FLAC/flac.xcodeproj/xcshareddata/xcschemes/FLAC Framework.xcscheme b/Frameworks/FLAC/flac.xcodeproj/xcshareddata/xcschemes/FLAC Framework.xcscheme index 16d78d369..0ac0699d9 100644 --- a/Frameworks/FLAC/flac.xcodeproj/xcshareddata/xcschemes/FLAC Framework.xcscheme +++ b/Frameworks/FLAC/flac.xcodeproj/xcshareddata/xcschemes/FLAC Framework.xcscheme @@ -1,6 +1,6 @@ -extern NSString *MovedRowsType; +extern NSString *CogDNDIndexType; extern NSString *CogUrlsPboardType; extern NSString *iTunesDropType; -@interface DNDArrayController : NSArrayController -{ - IBOutlet NSTableView *tableView; -} +@interface DNDArrayController : NSArrayController + +@property IBOutlet NSTableView *tableView; // table view drag and drop support -- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard; -- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op; -- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)op; - +- (id )tableView:(NSTableView *)tableView + pasteboardWriterForRow:(NSInteger)row; +- (void)tableView:(NSTableView *)tableView + draggingSession:(NSDraggingSession *)session + willBeginAtPoint:(NSPoint)screenPoint + forRowIndexes:(NSIndexSet *)rowIndexes; +- (NSDragOperation)tableView:(NSTableView *)tableView + validateDrop:(id )info + proposedRow:(NSInteger)row + proposedDropOperation:(NSTableViewDropOperation)dropOperation; +- (BOOL)tableView:(NSTableView *)tableView + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)dropOperation; // utility methods --(void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet*)indexSet toIndex:(unsigned int)insertIndex; +-(void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet + toIndex:(unsigned int)insertIndex; @end diff --git a/Playlist/DNDArrayController.m b/Playlist/DNDArrayController.m index 664177829..bda5ac7e1 100755 --- a/Playlist/DNDArrayController.m +++ b/Playlist/DNDArrayController.m @@ -3,113 +3,112 @@ #import "Logging.h" +NSString *CogDNDIndexType = @"org.cogx.cog.dnd-index"; +NSString *CogUrlsPboardType = @"org.cogx.cog.url"; +NSString *iTunesDropType = @"com.apple.tv.metadata"; + @implementation DNDArrayController -NSString *MovedRowsType = @"MOVED_ROWS_TYPE"; -NSString *CogUrlsPboardType = @"COG_URLS_TYPE"; - -// @"CorePasteboardFlavorType 0x6974756E" is the "itun" type representing an iTunes plist -NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E"; - -- (void)awakeFromNib -{ +- (void)awakeFromNib { + [super awakeFromNib]; // register for drag and drop - [tableView registerForDraggedTypes:[NSArray arrayWithObjects:MovedRowsType, CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil]]; + [self.tableView registerForDraggedTypes:@[CogDNDIndexType, + CogUrlsPboardType, + NSPasteboardTypeFileURL, + iTunesDropType]]; } -- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard -{ - DLog(@"INDEX SET ON DRAG: %@", rowIndexes); - - NSData *data = [NSArchiver archivedDataWithRootObject:rowIndexes]; - - [pboard declareTypes: [NSArray arrayWithObjects:MovedRowsType, nil] owner:self]; - [pboard setData:data forType: MovedRowsType]; +- (id )tableView:(NSTableView *)tableView + pasteboardWriterForRow:(NSInteger)row { + NSPasteboardItem *item = [[NSPasteboardItem alloc] init]; + [item setString:[@(row) stringValue] forType:CogDNDIndexType]; - return YES; + return item; } -- (NSDragOperation)tableView:(NSTableView*)tv - validateDrop:(id )info - proposedRow:(int)row - proposedDropOperation:(NSTableViewDropOperation)op -{ - NSDragOperation dragOp = NSDragOperationCopy; - - if ([info draggingSource] == tv) - dragOp = NSDragOperationMove; - - DLog(@"VALIDATING DROP!"); +- (void)tableView:(NSTableView *)tableView + draggingSession:(NSDraggingSession *)session + willBeginAtPoint:(NSPoint)screenPoint + forRowIndexes:(NSIndexSet *)rowIndexes { + DLog(@"Drag session started with indexes: %@", rowIndexes); +} + + +- (NSDragOperation)tableView:(NSTableView *)tableView + validateDrop:(id )info + proposedRow:(NSInteger)row + proposedDropOperation:(NSTableViewDropOperation)dropOperation { + NSDragOperation dragOp = NSDragOperationCopy; + + if ([info draggingSource] == tableView) + dragOp = NSDragOperationMove; + + DLog(@"VALIDATING DROP!"); // we want to put the object at, not over, - // the current row (contrast NSTableViewDropOn) - [tv setDropRow:row dropOperation:NSTableViewDropAbove]; - + // the current row (contrast NSTableViewDropOn) + [tableView setDropRow:row dropOperation:NSTableViewDropAbove]; + return dragOp; } -- (BOOL)tableView:(NSTableView*)tv - acceptDrop:(id )info - row:(int)row - dropOperation:(NSTableViewDropOperation)op -{ - if (row < 0) - { - row = 0; - } +- (BOOL)tableView:(NSTableView *)tableView + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)dropOperation { + if (row < 0) { + row = 0; + } + NSArray *items = info.draggingPasteboard.pasteboardItems; // if drag source is self, it's a move - if ([info draggingSource] == tableView) - { - NSIndexSet *indexSet = [NSUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType:MovedRowsType]]; - if (indexSet) - { - DLog(@"INDEX SET ON DROP: %@", indexSet); - NSArray *selected = [[self arrangedObjects] objectsAtIndexes:indexSet]; - [self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:row]; - - [self setSelectedObjects:selected]; - - DLog(@"ACCEPTING DROP!"); - return YES; - } - } - DLog(@"REJECTING DROP!"); - return NO; + if ([info draggingSource] == tableView || items == nil) { + NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; + for (NSPasteboardItem *item in items) { + [indexSet addIndex:(NSUInteger) [[item stringForType:CogDNDIndexType] intValue]]; + } + if ([indexSet count] > 0) { + DLog(@"INDEX SET ON DROP: %@", indexSet); + NSArray *selected = [[self arrangedObjects] objectsAtIndexes:indexSet]; + [self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:(unsigned int) row]; + + [self setSelectedObjects:selected]; + + DLog(@"ACCEPTING DROP!"); + return YES; + } + } + DLog(@"REJECTING DROP!"); + return NO; } --(void) moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet*)indexSet - toIndex:(unsigned int)insertIndex -{ - - NSArray *objects = [self arrangedObjects]; - NSUInteger index = [indexSet lastIndex]; - - int aboveInsertIndexCount = 0; - id object; - int removeIndex; - - while (NSNotFound != index) - { - if (index >= insertIndex) { - removeIndex = (int)(index + aboveInsertIndexCount); - aboveInsertIndexCount += 1; - } - else - { - removeIndex = (int)index; - insertIndex -= 1; - } - - object = [objects objectAtIndex:removeIndex]; +- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet + toIndex:(unsigned int)insertIndex { + NSArray *objects = [self arrangedObjects]; + NSUInteger index = [indexSet lastIndex]; - [self removeObjectAtArrangedObjectIndex:removeIndex]; - [self insertObject:object atArrangedObjectIndex:insertIndex]; - - index = [indexSet indexLessThanIndex:index]; + NSUInteger aboveInsertIndexCount = 0; + id object; + NSUInteger removeIndex; + + while (NSNotFound != index) { + if (index >= insertIndex) { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount += 1; + } else { + removeIndex = index; + insertIndex -= 1; + } + + object = objects[removeIndex]; + + [self removeObjectAtArrangedObjectIndex:removeIndex]; + [self insertObject:object atArrangedObjectIndex:insertIndex]; + + index = [indexSet indexLessThanIndex:index]; } } diff --git a/Playlist/PlaylistController.h b/Playlist/PlaylistController.h index f8054cf89..9852342a9 100644 --- a/Playlist/PlaylistController.h +++ b/Playlist/PlaylistController.h @@ -15,55 +15,48 @@ @class SpotlightWindowController; @class PlaybackController; -typedef enum { - RepeatNone = 0, - RepeatOne, - RepeatAlbum, - RepeatAll -} RepeatMode; +typedef NS_ENUM(NSInteger, RepeatMode) { + RepeatModeNoRepeat = 0, + RepeatModeRepeatOne, + RepeatModeRepeatAlbum, + RepeatModeRepeatAll +}; -static inline BOOL IsRepeatOneSet() -{ - return [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"] == RepeatOne; +static inline BOOL IsRepeatOneSet() { + return [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"] == RepeatModeRepeatOne; } -typedef enum { - ShuffleOff = 0, - ShuffleAlbums, - ShuffleAll -} ShuffleMode; - +typedef enum { ShuffleOff = 0, ShuffleAlbums, ShuffleAll } ShuffleMode; -typedef enum { - URLOriginInternal = 0, - URLOriginExternal, -} URLOrigin; +typedef NS_ENUM(NSInteger, URLOrigin) { + URLOriginInternal = 0, + URLOriginExternal +}; + +@interface PlaylistController : DNDArrayController { + IBOutlet PlaylistLoader *playlistLoader; + IBOutlet SpotlightWindowController *spotlightWindowController; + IBOutlet PlaybackController *playbackController; + + NSMutableArray *shuffleList; + NSMutableArray *queueList; + + NSString *totalTime; + + PlaylistEntry *currentEntry; -@interface PlaylistController : DNDArrayController { - IBOutlet PlaylistLoader *playlistLoader; - IBOutlet SpotlightWindowController *spotlightWindowController; - IBOutlet PlaybackController *playbackController; - - NSMutableArray *shuffleList; - NSMutableArray *queueList; - - NSString *totalTime; - - PlaylistEntry *currentEntry; - NSUndoManager *undoManager; } @property(nonatomic, retain) PlaylistEntry *currentEntry; @property(retain) NSString *totalTime; -//Private Methods +// Private Methods - (void)updateTotalTime; - (void)updatePlaylistIndexes; - (IBAction)stopAfterCurrent:(id)sender; - -//PUBLIC METHODS +// PUBLIC METHODS - (void)setShuffle:(ShuffleMode)s; - (ShuffleMode)shuffle; - (void)setRepeat:(RepeatMode)r; @@ -95,7 +88,7 @@ typedef enum { - (IBAction)searchByArtist:(id)sender; - (IBAction)searchByAlbum:(id)sender; -//FUN PLAYLIST MANAGEMENT STUFF! +// FUN PLAYLIST MANAGEMENT STUFF! - (BOOL)next; - (BOOL)prev; @@ -107,12 +100,15 @@ typedef enum { - (PlaylistEntry *)entryAtIndex:(int)i; // Event inlets: -- (void)willInsertURLs:(NSArray*)urls origin:(URLOrigin)origin; -- (void)didInsertURLs:(NSArray*)urls origin:(URLOrigin)origin; +- (void)willInsertURLs:(NSArray *)urls origin:(URLOrigin)origin; +- (void)didInsertURLs:(NSArray *)urls origin:(URLOrigin)origin; // queue methods - (IBAction)toggleQueued:(id)sender; - (IBAction)emptyQueueList:(id)sender; - (NSMutableArray *)queueList; +- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet + toIndex:(unsigned int)insertIndex; + @end diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index ad860f37c..15a3dfc3e 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -7,19 +7,18 @@ // #import "PlaylistController.h" +#import "PlaybackController.h" #import "PlaylistEntry.h" #import "PlaylistLoader.h" -#import "PlaybackController.h" -#import "Shuffle.h" -#import "SpotlightWindowController.h" #import "RepeatTransformers.h" +#import "Shuffle.h" #import "ShuffleTransformers.h" +#import "SpotlightWindowController.h" #import "StatusImageTransformer.h" #import "ToggleQueueTitleTransformer.h" #import "Logging.h" - #define UNDO_STACK_LIMIT 0 @implementation PlaylistController @@ -28,486 +27,446 @@ @synthesize totalTime; + (void)initialize { - NSValueTransformer *repeatNoneTransformer = [[RepeatModeTransformer alloc] initWithMode:RepeatNone]; - [NSValueTransformer setValueTransformer:repeatNoneTransformer - forName:@"RepeatNoneTransformer"]; + NSValueTransformer *repeatNoneTransformer = + [[RepeatModeTransformer alloc] initWithMode:RepeatModeNoRepeat]; + [NSValueTransformer setValueTransformer:repeatNoneTransformer forName:@"RepeatNoneTransformer"]; - NSValueTransformer *repeatOneTransformer = [[RepeatModeTransformer alloc] initWithMode:RepeatOne]; - [NSValueTransformer setValueTransformer:repeatOneTransformer - forName:@"RepeatOneTransformer"]; + NSValueTransformer *repeatOneTransformer = + [[RepeatModeTransformer alloc] initWithMode:RepeatModeRepeatOne]; + [NSValueTransformer setValueTransformer:repeatOneTransformer forName:@"RepeatOneTransformer"]; - NSValueTransformer *repeatAlbumTransformer = [[RepeatModeTransformer alloc] initWithMode:RepeatAlbum]; + NSValueTransformer *repeatAlbumTransformer = + [[RepeatModeTransformer alloc] initWithMode:RepeatModeRepeatAlbum]; [NSValueTransformer setValueTransformer:repeatAlbumTransformer forName:@"RepeatAlbumTransformer"]; - NSValueTransformer *repeatAllTransformer = [[RepeatModeTransformer alloc] initWithMode:RepeatAll]; - [NSValueTransformer setValueTransformer:repeatAllTransformer - forName:@"RepeatAllTransformer"]; + NSValueTransformer *repeatAllTransformer = + [[RepeatModeTransformer alloc] initWithMode:RepeatModeRepeatAll]; + [NSValueTransformer setValueTransformer:repeatAllTransformer forName:@"RepeatAllTransformer"]; - NSValueTransformer *repeatModeImageTransformer = [[RepeatModeImageTransformer alloc] init]; + NSValueTransformer *repeatModeImageTransformer = [[RepeatModeImageTransformer alloc] init]; [NSValueTransformer setValueTransformer:repeatModeImageTransformer forName:@"RepeatModeImageTransformer"]; - - NSValueTransformer *shuffleOffTransformer = [[ShuffleModeTransformer alloc] initWithMode:ShuffleOff]; - [NSValueTransformer setValueTransformer:shuffleOffTransformer - forName:@"ShuffleOffTransformer"]; - - NSValueTransformer *shuffleAlbumsTransformer = [[ShuffleModeTransformer alloc] initWithMode:ShuffleAlbums]; + NSValueTransformer *shuffleOffTransformer = + [[ShuffleModeTransformer alloc] initWithMode:ShuffleOff]; + [NSValueTransformer setValueTransformer:shuffleOffTransformer forName:@"ShuffleOffTransformer"]; + + NSValueTransformer *shuffleAlbumsTransformer = + [[ShuffleModeTransformer alloc] initWithMode:ShuffleAlbums]; [NSValueTransformer setValueTransformer:shuffleAlbumsTransformer forName:@"ShuffleAlbumsTransformer"]; - - NSValueTransformer *shuffleAllTransformer = [[ShuffleModeTransformer alloc] initWithMode:ShuffleAll]; - [NSValueTransformer setValueTransformer:shuffleAllTransformer - forName:@"ShuffleAllTransformer"]; - NSValueTransformer *shuffleImageTransformer = [[ShuffleImageTransformer alloc] init]; + NSValueTransformer *shuffleAllTransformer = + [[ShuffleModeTransformer alloc] initWithMode:ShuffleAll]; + [NSValueTransformer setValueTransformer:shuffleAllTransformer forName:@"ShuffleAllTransformer"]; + + NSValueTransformer *shuffleImageTransformer = [[ShuffleImageTransformer alloc] init]; [NSValueTransformer setValueTransformer:shuffleImageTransformer forName:@"ShuffleImageTransformer"]; - - - - NSValueTransformer *statusImageTransformer = [[StatusImageTransformer alloc] init]; + + NSValueTransformer *statusImageTransformer = [[StatusImageTransformer alloc] init]; [NSValueTransformer setValueTransformer:statusImageTransformer forName:@"StatusImageTransformer"]; - - NSValueTransformer *toggleQueueTitleTransformer = [[ToggleQueueTitleTransformer alloc] init]; + + NSValueTransformer *toggleQueueTitleTransformer = [[ToggleQueueTitleTransformer alloc] init]; [NSValueTransformer setValueTransformer:toggleQueueTitleTransformer forName:@"ToggleQueueTitleTransformer"]; } +- (void)initDefaults { + NSDictionary *defaultsDictionary = @{@"repeat": @(RepeatModeNoRepeat), @"shuffle": @(ShuffleOff)}; -- (void)initDefaults -{ - NSDictionary *defaultsDictionary = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInteger:RepeatNone], @"repeat", - [NSNumber numberWithInteger:ShuffleOff], @"shuffle", - nil]; - - [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary]; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary]; } +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; -- (id)initWithCoder:(NSCoder *)decoder -{ - self = [super initWithCoder:decoder]; - - if (self) - { - shuffleList = [[NSMutableArray alloc] init]; - queueList = [[NSMutableArray alloc] init]; + if (self) { + shuffleList = [[NSMutableArray alloc] init]; + queueList = [[NSMutableArray alloc] init]; undoManager = [[NSUndoManager alloc] init]; [undoManager setLevelsOfUndo:UNDO_STACK_LIMIT]; - [self initDefaults]; - } - - return self; -} - - -- (void)awakeFromNib -{ - [super awakeFromNib]; - - [self addObserver:self forKeyPath:@"arrangedObjects" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ - if ([keyPath isEqualToString:@"arrangedObjects"]) - { - [self updatePlaylistIndexes]; - [self updateTotalTime]; - } -} - -- (void)updatePlaylistIndexes -{ - int i; - NSArray *arranged = [self arrangedObjects]; - for (i = 0; i < [arranged count]; i++) - { - PlaylistEntry *pe = [arranged objectAtIndex:i]; - if (pe.index != i) //Make sure we don't get into some kind of crazy observing loop... - pe.index = i; - } -} - -- (void)updateTotalTime -{ - double tt = 0; - ldiv_t hoursAndMinutes; - ldiv_t daysAndHours; - - for (PlaylistEntry *pe in [self arrangedObjects]) { - if (!isnan([pe.length doubleValue])) - tt += [pe.length doubleValue]; - } - - long sec = (long)(tt); - hoursAndMinutes = ldiv(sec/60, 60); - - if ( hoursAndMinutes.quot >= 24 ) - { - daysAndHours = ldiv(hoursAndMinutes.quot, 24); - [self setTotalTime:[NSString stringWithFormat:@"%ld days %ld hours %ld minutes %ld seconds", daysAndHours.quot, daysAndHours.rem, hoursAndMinutes.rem, sec%60]]; + [self initDefaults]; } - else - [self setTotalTime:[NSString stringWithFormat:@"%ld hours %ld minutes %ld seconds", hoursAndMinutes.quot, hoursAndMinutes.rem, sec%60]]; + + return self; } -- (void)tableView:(NSTableView *)tableView - didClickTableColumn:(NSTableColumn *)tableColumn -{ - if ([self shuffle] != ShuffleOff) - [self resetShuffleList]; +- (void)awakeFromNib { + [super awakeFromNib]; + + [self addObserver:self + forKeyPath:@"arrangedObjects" + options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context:nil]; } -- (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation -{ - DLog(@"GETTING STATUS FOR ROW: %i: %@!", row, [[[self arrangedObjects] objectAtIndex:row] statusMessage]); - return [[[self arrangedObjects] objectAtIndex:row] statusMessage]; +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if ([keyPath isEqualToString:@"arrangedObjects"]) { + [self updatePlaylistIndexes]; + [self updateTotalTime]; + } } --(void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet*)indexSet - toIndex:(unsigned int)insertIndex -{ - [super moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex]; - - NSUInteger lowerIndex = insertIndex; - NSUInteger index = insertIndex; - - while (NSNotFound != lowerIndex) { - lowerIndex = [indexSet indexLessThanIndex:lowerIndex]; - - if (lowerIndex != NSNotFound) - index = lowerIndex; - } - - [playbackController playlistDidChange:self]; +- (void)updatePlaylistIndexes { + NSArray *arranged = [self arrangedObjects]; + NSUInteger n = [arranged count]; + for (NSUInteger i = 0; i < n; i++) { + PlaylistEntry *pe = arranged[i]; + if (pe.index != i) // Make sure we don't get into some kind of crazy observing loop... + pe.index = (int) i; + } } -- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard -{ - [super tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard]; +- (void)updateTotalTime { + double tt = 0; + ldiv_t hoursAndMinutes; + ldiv_t daysAndHours; - NSMutableArray *filenames = [NSMutableArray array]; - NSInteger row; - for (row = [rowIndexes firstIndex]; - row <= [rowIndexes lastIndex]; - row = [rowIndexes indexGreaterThanIndex:row]) - { - PlaylistEntry *song = [[self arrangedObjects] objectAtIndex:row]; - [filenames addObject:[[song path] stringByExpandingTildeInPath]]; - } + for (PlaylistEntry *pe in [self arrangedObjects]) { + if (!isnan([pe.length doubleValue])) tt += [pe.length doubleValue]; + } - [pboard addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:self]; - [pboard setPropertyList:filenames forType:NSFilenamesPboardType]; - - return YES; + long sec = (long) (tt); + hoursAndMinutes = ldiv(sec / 60, 60); + + if (hoursAndMinutes.quot >= 24) { + daysAndHours = ldiv(hoursAndMinutes.quot, 24); + [self setTotalTime:[NSString stringWithFormat:@"%ld days %ld hours %ld minutes %ld seconds", + daysAndHours.quot, daysAndHours.rem, + hoursAndMinutes.rem, sec % 60]]; + } else { + [self setTotalTime:[NSString stringWithFormat:@"%ld hours %ld minutes %ld seconds", + hoursAndMinutes.quot, hoursAndMinutes.rem, + sec % 60]]; + } } -- (BOOL)tableView:(NSTableView*)tv - acceptDrop:(id )info - row:(int)row - dropOperation:(NSTableViewDropOperation)op -{ - //Check if DNDArrayController handles it. - if ([super tableView:tv acceptDrop:info row:row dropOperation:op]) - return YES; - - if (row < 0) - row = 0; - - - // Determine the type of object that was dropped - NSArray *supportedTypes = [NSArray arrayWithObjects:CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil]; - NSPasteboard *pboard = [info draggingPasteboard]; - NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; - - NSMutableArray *acceptedURLs = [[NSMutableArray alloc] init]; - - // Get files from an file drawer drop - if ([bestType isEqualToString:CogUrlsPboardType]) { - NSArray *urls = [NSUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType:CogUrlsPboardType]]; - DLog(@"URLS: %@", urls); - //[playlistLoader insertURLs: urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - // Get files from a normal file drop (such as from Finder) - if ([bestType isEqualToString:NSFilenamesPboardType]) { - NSMutableArray *urls = [[NSMutableArray alloc] init]; - - for (NSString *file in [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType]) - { - [urls addObject:[NSURL fileURLWithPath:file]]; - } - - //[playlistLoader insertURLs:urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - // Get files from an iTunes drop - if ([bestType isEqualToString:iTunesDropType]) { - NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType]; - NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"]; - - // Convert the iTunes URLs to URLs....MWAHAHAH! - NSMutableArray *urls = [[NSMutableArray alloc] init]; - - for (NSDictionary *trackInfo in [tracks allValues]) { - [urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]]; - } - - //[playlistLoader insertURLs:urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - if ([acceptedURLs count]) - { - [self willInsertURLs:acceptedURLs origin:URLOriginInternal]; - - if (![[self content] count]) { - row = 0; - } - - NSArray* entries = [playlistLoader insertURLs:acceptedURLs atIndex:row sort:YES]; - [self didInsertURLs:entries origin:URLOriginInternal]; - } - - if ([self shuffle] != ShuffleOff) - [self resetShuffleList]; - - return YES; +- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn { + if ([self shuffle] != ShuffleOff) [self resetShuffleList]; } -- (NSUndoManager *)undoManager -{ - return undoManager; +- (NSString *)tableView:(NSTableView *)tv + toolTipForCell:(NSCell *)cell + rect:(NSRectPointer)rect + tableColumn:(NSTableColumn *)tc + row:(NSInteger)row + mouseLocation:(NSPoint)mouseLocation { + DLog(@"GETTING STATUS FOR ROW: %i: %@!", row, + [[[self arrangedObjects] objectAtIndex:row] statusMessage]); + return [[[self arrangedObjects] objectAtIndex:row] statusMessage]; } -- (NSIndexSet *)disarrangeIndexes:(NSIndexSet *)indexes -{ - if ([[self arrangedObjects] count] <= [indexes lastIndex]) - return indexes; - +- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet + toIndex:(unsigned int)insertIndex { + [super moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:insertIndex]; + + [playbackController playlistDidChange:self]; +} + +- (id )tableView:(NSTableView *)tableView + pasteboardWriterForRow:(NSInteger)row { + NSPasteboardItem *item = (NSPasteboardItem *) [super tableView:tableView + pasteboardWriterForRow:row]; + if (!item) { + item = [[NSPasteboardItem alloc] init]; + } + + NSMutableArray *filenames = [NSMutableArray array]; + PlaylistEntry *song = [[self arrangedObjects] objectAtIndex:row]; + [filenames addObject:[[song path] stringByExpandingTildeInPath]]; + + [item setData:[song.URL dataRepresentation] forType:NSPasteboardTypeFileURL]; + + return item; +} + +- (BOOL)tableView:(NSTableView *)tv + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)op { + // Check if DNDArrayController handles it. + if ([super tableView:tv acceptDrop:info row:row dropOperation:op]) return YES; + + if (row < 0) row = 0; + + // Determine the type of object that was dropped + NSArray *supportedTypes = + @[CogUrlsPboardType, NSPasteboardTypeFileURL, iTunesDropType]; + NSPasteboard *pboard = [info draggingPasteboard]; + NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; + + NSMutableArray *acceptedURLs = [[NSMutableArray alloc] init]; + + // Get files from an file drawer drop + if ([bestType isEqualToString:CogUrlsPboardType]) { + NSError *error; + NSData *data = [pboard dataForType:CogUrlsPboardType]; + NSArray *urls; + if (@available(macOS 11.0, *)) { + urls = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:[NSURL class] + fromData:data + error:&error]; + } else { + NSSet *allowed = [NSSet setWithArray:@[[NSArray class], [NSURL class]]]; + urls = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowed fromData:data error:&error]; + } + if (!urls) { + DLog(@"%@", error); + } else { + DLog(@"URLS: %@", urls); + } + //[playlistLoader insertURLs: urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + // Get files from a normal file drop (such as from Finder) + if ([bestType isEqualToString:NSPasteboardTypeFileURL]) { + NSMutableArray *urls = [[NSMutableArray alloc] init]; + + for (NSString *file in + [[info draggingPasteboard] propertyListForType:NSPasteboardTypeFileURL]) { + [urls addObject:[NSURL fileURLWithPath:file]]; + } + + //[playlistLoader insertURLs:urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + // Get files from an iTunes drop + if ([bestType isEqualToString:iTunesDropType]) { + NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType]; + NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"]; + + // Convert the iTunes URLs to URLs....MWAHAHAH! + NSMutableArray *urls = [[NSMutableArray alloc] init]; + + for (NSDictionary *trackInfo in [tracks allValues]) { + [urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]]; + } + + //[playlistLoader insertURLs:urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + if ([acceptedURLs count]) { + [self willInsertURLs:acceptedURLs origin:URLOriginInternal]; + + if (![[self content] count]) { + row = 0; + } + + NSArray *entries = [playlistLoader insertURLs:acceptedURLs atIndex:row sort:YES]; + [self didInsertURLs:entries origin:URLOriginInternal]; + } + + if ([self shuffle] != ShuffleOff) [self resetShuffleList]; + + return YES; +} + +- (NSUndoManager *)undoManager { + return undoManager; +} + +- (NSIndexSet *)disarrangeIndexes:(NSIndexSet *)indexes { + if ([[self arrangedObjects] count] <= [indexes lastIndex]) return indexes; + NSMutableIndexSet *disarrangedIndexes = [[NSMutableIndexSet alloc] init]; - + NSUInteger index = [indexes firstIndex]; - while (index != NSNotFound) - { - [disarrangedIndexes addIndex:[[self content] indexOfObject:[[self arrangedObjects] objectAtIndex:index]]]; + while (index != NSNotFound) { + [disarrangedIndexes + addIndex:[[self content] indexOfObject:[[self arrangedObjects] objectAtIndex:index]]]; index = [indexes indexGreaterThanIndex:index]; } - + return disarrangedIndexes; } -- (NSArray *)disarrangeObjects:(NSArray *)objects -{ +- (NSArray *)disarrangeObjects:(NSArray *)objects { NSMutableArray *disarrangedObjects = [[NSMutableArray alloc] init]; - - for (PlaylistEntry *pe in [self content]) - { - if ([objects containsObject:pe]) - [disarrangedObjects addObject:pe]; + + for (PlaylistEntry *pe in [self content]) { + if ([objects containsObject:pe]) [disarrangedObjects addObject:pe]; } - + return disarrangedObjects; } -- (NSIndexSet *)rearrangeIndexes:(NSIndexSet *)indexes -{ - if ([[self content] count] <= [indexes lastIndex]) - return indexes; - +- (NSIndexSet *)rearrangeIndexes:(NSIndexSet *)indexes { + if ([[self content] count] <= [indexes lastIndex]) return indexes; + NSMutableIndexSet *rearrangedIndexes = [[NSMutableIndexSet alloc] init]; - + NSUInteger index = [indexes firstIndex]; - while (index != NSNotFound) - { - [rearrangedIndexes addIndex:[[self arrangedObjects] indexOfObject:[[self content] objectAtIndex:index]]]; + while (index != NSNotFound) { + [rearrangedIndexes + addIndex:[[self arrangedObjects] indexOfObject:[[self content] objectAtIndex:index]]]; index = [indexes indexGreaterThanIndex:index]; } - + return rearrangedIndexes; } -- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes -{ +- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes { [self insertObjects:objects atArrangedObjectIndexes:indexes]; [self rearrangeObjects]; } -- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes -{ - [[[self undoManager] prepareWithInvocationTarget:self] removeObjectsAtIndexes:[self disarrangeIndexes:indexes]]; - NSString *actionName = [NSString stringWithFormat:@"Adding %lu entries", (unsigned long)[objects count]]; +- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes { + [[[self undoManager] prepareWithInvocationTarget:self] + removeObjectsAtIndexes:[self disarrangeIndexes:indexes]]; + NSString *actionName = + [NSString stringWithFormat:@"Adding %lu entries", (unsigned long) [objects count]]; [[self undoManager] setActionName:actionName]; [super insertObjects:objects atArrangedObjectIndexes:indexes]; - if ([self shuffle] != ShuffleOff) - [self resetShuffleList]; + if ([self shuffle] != ShuffleOff) [self resetShuffleList]; } -- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes -{ +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { [self removeObjectsAtArrangedObjectIndexes:[self rearrangeIndexes:indexes]]; } -- (void)removeObjectsAtArrangedObjectIndexes:(NSIndexSet *)indexes -{ +- (void)removeObjectsAtArrangedObjectIndexes:(NSIndexSet *)indexes { NSArray *objects = [[self arrangedObjects] objectsAtIndexes:indexes]; - [[[self undoManager] prepareWithInvocationTarget:self] insertObjects:[self disarrangeObjects:objects] atIndexes:[self disarrangeIndexes:indexes]]; - NSString *actionName = [NSString stringWithFormat:@"Removing %lu entries", (unsigned long)[indexes count]]; + [[[self undoManager] prepareWithInvocationTarget:self] + insertObjects:[self disarrangeObjects:objects] + atIndexes:[self disarrangeIndexes:indexes]]; + NSString *actionName = + [NSString stringWithFormat:@"Removing %lu entries", (unsigned long) [indexes count]]; [[self undoManager] setActionName:actionName]; - + DLog(@"Removing indexes: %@", indexes); DLog(@"Current index: %i", currentEntry.index); - + NSMutableIndexSet *unarrangedIndexes = [[NSMutableIndexSet alloc] init]; - for (PlaylistEntry *pe in objects) - { + for (PlaylistEntry *pe in objects) { [unarrangedIndexes addIndex:[pe index]]; } - - if (currentEntry.index >= 0 && [unarrangedIndexes containsIndex:currentEntry.index]) - { + + if (currentEntry.index >= 0 && [unarrangedIndexes containsIndex:currentEntry.index]) { currentEntry.index = -currentEntry.index - 1; DLog(@"Current removed: %i", currentEntry.index); } - - if (currentEntry.index < 0) //Need to update the negative index + + if (currentEntry.index < 0) // Need to update the negative index { int i = -currentEntry.index - 1; DLog(@"I is %i", i); int j; - for (j = i - 1; j >= 0; j--) - { + for (j = i - 1; j >= 0; j--) { if ([unarrangedIndexes containsIndex:j]) { DLog(@"Removing 1"); i--; } } currentEntry.index = -i - 1; - } - + [super removeObjectsAtArrangedObjectIndexes:indexes]; - if ([self shuffle] != ShuffleOff) - [self resetShuffleList]; + if ([self shuffle] != ShuffleOff) [self resetShuffleList]; [playbackController playlistDidChange:self]; } -- (void)setSortDescriptors:(NSArray *)sortDescriptors -{ - DLog(@"Current: %@, setting: %@", [self sortDescriptors], sortDescriptors); +- (void)setSortDescriptors:(NSArray *)sortDescriptors { + DLog(@"Current: %@, setting: %@", [self sortDescriptors], sortDescriptors); - //Cheap hack so the index column isn't sorted - if (([sortDescriptors count] != 0) && [[[sortDescriptors objectAtIndex:0] key] caseInsensitiveCompare:@"index"] == NSOrderedSame) - { - //Remove the sort descriptors - [super setSortDescriptors:[NSArray array]]; - [self rearrangeObjects]; - - return; - } + // Cheap hack so the index column isn't sorted + if (([sortDescriptors count] != 0) && [[sortDescriptors[0] key] + caseInsensitiveCompare:@"index"] == NSOrderedSame) { + // Remove the sort descriptors + [super setSortDescriptors:[NSArray array]]; + [self rearrangeObjects]; - [super setSortDescriptors:sortDescriptors]; - [self rearrangeObjects]; + return; + } - [playbackController playlistDidChange:self]; + [super setSortDescriptors:sortDescriptors]; + [self rearrangeObjects]; + + [playbackController playlistDidChange:self]; } - -- (IBAction)randomizeList:(id)sender -{ - [self setSortDescriptors:[NSArray array]]; + +- (IBAction)randomizeList:(id)sender { + [self setSortDescriptors:[NSArray array]]; NSArray *unrandomized = [self content]; [[[self undoManager] prepareWithInvocationTarget:self] unrandomizeList:unrandomized]; [self setContent:[Shuffle shuffleList:[self content]]]; - if ([self shuffle] != ShuffleOff) - [self resetShuffleList]; + if ([self shuffle] != ShuffleOff) [self resetShuffleList]; [[self undoManager] setActionName:@"Playlist Randomization"]; } -- (void)unrandomizeList:(NSArray *)entries -{ +- (void)unrandomizeList:(NSArray *)entries { [[[self undoManager] prepareWithInvocationTarget:self] randomizeList:self]; [self setContent:entries]; } -- (IBAction)toggleShuffle:(id)sender -{ - ShuffleMode shuffle = [self shuffle]; - - if (shuffle == ShuffleOff) { - [self setShuffle: ShuffleAlbums]; - } - else if (shuffle == ShuffleAlbums) { - [self setShuffle: ShuffleAll]; - } - else if (shuffle == ShuffleAll) { - [self setShuffle: ShuffleOff]; - } +- (IBAction)toggleShuffle:(id)sender { + ShuffleMode shuffle = [self shuffle]; + + if (shuffle == ShuffleOff) { + [self setShuffle:ShuffleAlbums]; + } else if (shuffle == ShuffleAlbums) { + [self setShuffle:ShuffleAll]; + } else if (shuffle == ShuffleAll) { + [self setShuffle:ShuffleOff]; + } } -- (IBAction)toggleRepeat:(id)sender -{ - RepeatMode repeat = [self repeat]; - - if (repeat == RepeatNone) { - [self setRepeat: RepeatOne]; - } - else if (repeat == RepeatOne) { - [self setRepeat: RepeatAlbum]; - } - else if (repeat == RepeatAlbum) { - [self setRepeat: RepeatAll]; - } - else if (repeat == RepeatAll) { - [self setRepeat: RepeatNone]; - } +- (IBAction)toggleRepeat:(id)sender { + RepeatMode repeat = [self repeat]; + + if (repeat == RepeatModeNoRepeat) { + [self setRepeat:RepeatModeRepeatOne]; + } else if (repeat == RepeatModeRepeatOne) { + [self setRepeat:RepeatModeRepeatAlbum]; + } else if (repeat == RepeatModeRepeatAlbum) { + [self setRepeat:RepeatModeRepeatAll]; + } else if (repeat == RepeatModeRepeatAll) { + [self setRepeat:RepeatModeNoRepeat]; + } } -- (PlaylistEntry *)entryAtIndex:(int)i -{ - RepeatMode repeat = [self repeat]; - - if (i < 0 || i >= [[self arrangedObjects] count] ) { - if ( repeat != RepeatAll ) - return nil; - - while ( i < 0 ) - i += [[self arrangedObjects] count]; - if ( i >= [[self arrangedObjects] count]) - i %= [[self arrangedObjects] count]; - } - - return [[self arrangedObjects] objectAtIndex:i]; +- (PlaylistEntry *)entryAtIndex:(int)i { + RepeatMode repeat = [self repeat]; + + if (i < 0 || i >= [[self arrangedObjects] count]) { + if (repeat != RepeatModeRepeatAll) return nil; + + while (i < 0) i += [[self arrangedObjects] count]; + if (i >= [[self arrangedObjects] count]) i %= [[self arrangedObjects] count]; + } + + return [[self arrangedObjects] objectAtIndex:i]; } - (void)remove:(id)sender { // It's a kind of magic. // Plain old NSArrayController's remove: isn't working properly for some reason. - // The method is definitely called but (overridden) removeObjectsAtArrangedObjectIndexes: isn't called - // and no entries are removed. - // Putting explicit call to removeObjectsAtArrangedObjectIndexes: here for now. + // The method is definitely called but (overridden) removeObjectsAtArrangedObjectIndexes: isn't + // called and no entries are removed. Putting explicit call to + // removeObjectsAtArrangedObjectIndexes: here for now. // TODO: figure it out NSIndexSet *selected = [self selectionIndexes]; - if ([selected count] > 0) - { + if ([selected count] > 0) { [self removeObjectsAtArrangedObjectIndexes:selected]; } } @@ -515,21 +474,18 @@ - (IBAction)removeDuplicates:(id)sender { NSMutableArray *originals = [[NSMutableArray alloc] init]; NSMutableArray *duplicates = [[NSMutableArray alloc] init]; - - for (PlaylistEntry *pe in [self content]) - { + + for (PlaylistEntry *pe in [self content]) { if ([originals containsObject:[pe URL]]) [duplicates addObject:pe]; else [originals addObject:[pe URL]]; } - - if ([duplicates count] > 0) - { - NSArray * arrangedContent = [self arrangedObjects]; - NSMutableIndexSet * duplicatesIndex = [[NSMutableIndexSet alloc] init]; - for (PlaylistEntry *pe in duplicates) - { + + if ([duplicates count] > 0) { + NSArray *arrangedContent = [self arrangedObjects]; + NSMutableIndexSet *duplicatesIndex = [[NSMutableIndexSet alloc] init]; + for (PlaylistEntry *pe in duplicates) { [duplicatesIndex addIndex:[arrangedContent indexOfObject:pe]]; } [self removeObjectsAtArrangedObjectIndexes:duplicatesIndex]; @@ -538,592 +494,509 @@ - (IBAction)removeDeadItems:(id)sender { NSMutableArray *deadItems = [[NSMutableArray alloc] init]; - - for (PlaylistEntry *pe in [self content]) - { + + for (PlaylistEntry *pe in [self content]) { NSURL *url = [pe URL]; if ([url isFileURL]) if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) [deadItems addObject:pe]; } - - if ([deadItems count] > 0) - { - NSArray * arrangedContent = [self arrangedObjects]; - NSMutableIndexSet * deadItemsIndex = [[NSMutableIndexSet alloc] init]; - for (PlaylistEntry *pe in deadItems) - { + + if ([deadItems count] > 0) { + NSArray *arrangedContent = [self arrangedObjects]; + NSMutableIndexSet *deadItemsIndex = [[NSMutableIndexSet alloc] init]; + for (PlaylistEntry *pe in deadItems) { [deadItemsIndex addIndex:[arrangedContent indexOfObject:pe]]; } [self removeObjectsAtArrangedObjectIndexes:deadItemsIndex]; } } -- (PlaylistEntry *)shuffledEntryAtIndex:(int)i -{ - RepeatMode repeat = [self repeat]; - - while (i < 0) - { - if (repeat == RepeatAll) - { - [self addShuffledListToFront]; - //change i appropriately - i += [[self arrangedObjects] count]; - } - else - { - return nil; - } - } - while (i >= [shuffleList count]) - { - if (repeat == RepeatAll) - { - [self addShuffledListToBack]; - } - else - { - return nil; - } - } - - return [shuffleList objectAtIndex:i]; +- (PlaylistEntry *)shuffledEntryAtIndex:(int)i { + RepeatMode repeat = [self repeat]; + + while (i < 0) { + if (repeat == RepeatModeRepeatAll) { + [self addShuffledListToFront]; + // change i appropriately + i += [[self arrangedObjects] count]; + } else { + return nil; + } + } + while (i >= [shuffleList count]) { + if (repeat == RepeatModeRepeatAll) { + [self addShuffledListToBack]; + } else { + return nil; + } + } + + return shuffleList[i]; } -- (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe -{ +- (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe { return [self getNextEntry:pe ignoreRepeatOne:NO]; } -- (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne -{ - if (!ignoreRepeatOne && [self repeat] == RepeatOne) - { +- (PlaylistEntry *)getNextEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne { + if (!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) { return pe; } - - if ([queueList count] > 0) - { - - pe = [queueList objectAtIndex:0]; - [queueList removeObjectAtIndex:0]; - pe.queued = NO; - [pe setQueuePosition:-1]; - - int i; - for (i = 0; i < [queueList count]; i++) - { - PlaylistEntry *queueItem = [queueList objectAtIndex:i]; - [queueItem setQueuePosition: i]; - } - - return pe; - } - - if ([self shuffle] != ShuffleOff) - { - return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)]; - } - else - { - int i; - if (pe.index < 0) //Was a current entry, now removed. - { - i = -pe.index - 1; - } - else - { - i = pe.index + 1; - } - - if ([self repeat] == RepeatAlbum) - { - PlaylistEntry *next = [self entryAtIndex:i]; - - if ((i > [[self arrangedObjects] count]-1) || ([[next album] caseInsensitiveCompare:[pe album]]) || ([next album] == nil)) - { - NSArray *filtered = [self filterPlaylistOnAlbum:[pe album]]; - if ([pe album] == nil) - i--; - else - i = [(PlaylistEntry *)[filtered objectAtIndex:0] index]; - } - - } - return [self entryAtIndex:i]; - } + if ([queueList count] > 0) { + pe = queueList[0]; + [queueList removeObjectAtIndex:0]; + pe.queued = NO; + [pe setQueuePosition:-1]; + + int i; + for (i = 0; i < [queueList count]; i++) { + PlaylistEntry *queueItem = queueList[i]; + [queueItem setQueuePosition:i]; + } + + return pe; + } + + if ([self shuffle] != ShuffleOff) { + return [self shuffledEntryAtIndex:(pe.shuffleIndex + 1)]; + } else { + int i; + if (pe.index < 0) // Was a current entry, now removed. + { + i = -pe.index - 1; + } else { + i = pe.index + 1; + } + + if ([self repeat] == RepeatModeRepeatAlbum) { + PlaylistEntry *next = [self entryAtIndex:i]; + + if ((i > [[self arrangedObjects] count] - 1) || + ([[next album] caseInsensitiveCompare:[pe album]]) || ([next album] == nil)) { + NSArray *filtered = [self filterPlaylistOnAlbum:[pe album]]; + if ([pe album] == nil) + i--; + else + i = [(PlaylistEntry *) filtered[0] index]; + } + } + + return [self entryAtIndex:i]; + } } -- (NSArray *)filterPlaylistOnAlbum:(NSString *)album -{ - NSPredicate *predicate; - if ([album length] > 0) - predicate = [NSPredicate predicateWithFormat:@"album like %@", - album]; - else - predicate = [NSPredicate predicateWithFormat:@"album == nil"]; - return [[self arrangedObjects] filteredArrayUsingPredicate:predicate]; +- (NSArray *)filterPlaylistOnAlbum:(NSString *)album { + NSPredicate *predicate; + if ([album length] > 0) + predicate = [NSPredicate predicateWithFormat:@"album like %@", album]; + else + predicate = [NSPredicate predicateWithFormat:@"album == nil"]; + return [[self arrangedObjects] filteredArrayUsingPredicate:predicate]; } -- (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe -{ +- (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe { return [self getPrevEntry:pe ignoreRepeatOne:NO]; } -- (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne -{ - if (!ignoreRepeatOne && [self repeat] == RepeatOne) - { - return pe; - } - - if ([self shuffle] != ShuffleOff) - { - return [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)]; - } - else - { - int i; - if (pe.index < 0) //Was a current entry, now removed. - { - i = -pe.index - 2; - } - else - { - i = pe.index - 1; - } - - return [self entryAtIndex:i]; - } +- (PlaylistEntry *)getPrevEntry:(PlaylistEntry *)pe ignoreRepeatOne:(BOOL)ignoreRepeatOne { + if (!ignoreRepeatOne && [self repeat] == RepeatModeRepeatOne) { + return pe; + } + + if ([self shuffle] != ShuffleOff) { + return [self shuffledEntryAtIndex:(pe.shuffleIndex - 1)]; + } else { + int i; + if (pe.index < 0) // Was a current entry, now removed. + { + i = -pe.index - 2; + } else { + i = pe.index - 1; + } + + return [self entryAtIndex:i]; + } } -- (BOOL)next -{ - PlaylistEntry *pe; - - pe = [self getNextEntry:[self currentEntry] ignoreRepeatOne:YES]; - - if (pe == nil) - return NO; - - [self setCurrentEntry:pe]; - - return YES; +- (BOOL)next { + PlaylistEntry *pe; + + pe = [self getNextEntry:[self currentEntry] ignoreRepeatOne:YES]; + + if (pe == nil) return NO; + + [self setCurrentEntry:pe]; + + return YES; } -- (BOOL)prev -{ - PlaylistEntry *pe; - +- (BOOL)prev { + PlaylistEntry *pe; + pe = [self getPrevEntry:[self currentEntry] ignoreRepeatOne:YES]; - if (pe == nil) - return NO; - - [self setCurrentEntry:pe]; - - return YES; + if (pe == nil) return NO; + + [self setCurrentEntry:pe]; + + return YES; } -- (NSArray *)shuffleAlbums -{ - NSArray * newList = [self arrangedObjects]; - NSMutableArray * temp = [[NSMutableArray alloc] init]; - NSMutableArray * albums = [[NSMutableArray alloc] init]; - NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" ascending:YES]; - for (unsigned long i = 0, j = [newList count]; i < j; ++i) { - PlaylistEntry * pe = [newList objectAtIndex:i]; - NSString * album = [pe album]; - if (!album) - album = @""; - if ([albums containsObject:album]) continue; - [albums addObject:album]; - NSArray * albumContent = [self filterPlaylistOnAlbum:album]; - NSArray * sortedContent = [albumContent sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; - [temp addObject:[sortedContent objectAtIndex:0]]; - } - NSArray * tempList = [Shuffle shuffleList:temp]; - temp = [[NSMutableArray alloc] init]; - for (unsigned long i = 0, j = [tempList count]; i < j; ++i) { - PlaylistEntry * pe = [tempList objectAtIndex:i]; - NSString * album = [pe album]; - NSArray * albumContent = [self filterPlaylistOnAlbum:album]; - NSArray * sortedContent = [albumContent sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; - [temp addObjectsFromArray:sortedContent]; - } - return temp; +- (NSArray *)shuffleAlbums { + NSArray *newList = [self arrangedObjects]; + NSMutableArray *temp = [[NSMutableArray alloc] init]; + NSMutableArray *albums = [[NSMutableArray alloc] init]; + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"track" + ascending:YES]; + for (unsigned long i = 0, j = [newList count]; i < j; ++i) { + PlaylistEntry *pe = newList[i]; + NSString *album = [pe album]; + if (!album) album = @""; + if ([albums containsObject:album]) continue; + [albums addObject:album]; + NSArray *albumContent = [self filterPlaylistOnAlbum:album]; + NSArray *sortedContent = + [albumContent sortedArrayUsingDescriptors:@[sortDescriptor]]; + [temp addObject:sortedContent[0]]; + } + NSArray *tempList = [Shuffle shuffleList:temp]; + temp = [[NSMutableArray alloc] init]; + for (unsigned long i = 0, j = [tempList count]; i < j; ++i) { + PlaylistEntry *pe = tempList[i]; + NSString *album = [pe album]; + NSArray *albumContent = [self filterPlaylistOnAlbum:album]; + NSArray *sortedContent = + [albumContent sortedArrayUsingDescriptors:@[sortDescriptor]]; + [temp addObjectsFromArray:sortedContent]; + } + return temp; } -- (void)addShuffledListToFront -{ - NSArray *newList; - NSIndexSet *indexSet; - - if ([self shuffle] == ShuffleAlbums) { - newList = [self shuffleAlbums]; - } - else { - newList = [Shuffle shuffleList:[self arrangedObjects]]; - } - - indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [newList count])]; - - [shuffleList insertObjects:newList atIndexes:indexSet]; - - int i; - for (i = 0; i < [shuffleList count]; i++) - { - [[shuffleList objectAtIndex:i] setShuffleIndex:i]; - } +- (void)addShuffledListToFront { + NSArray *newList; + NSIndexSet *indexSet; + + if ([self shuffle] == ShuffleAlbums) { + newList = [self shuffleAlbums]; + } else { + newList = [Shuffle shuffleList:[self arrangedObjects]]; + } + + indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [newList count])]; + + [shuffleList insertObjects:newList atIndexes:indexSet]; + + int i; + for (i = 0; i < [shuffleList count]; i++) { + [shuffleList[i] setShuffleIndex:i]; + } } -- (void)addShuffledListToBack -{ - NSArray *newList; - NSIndexSet *indexSet; - - if ([self shuffle] == ShuffleAlbums) { - newList = [self shuffleAlbums]; - } - else { - newList = [Shuffle shuffleList:[self arrangedObjects]]; - } - - indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange([shuffleList count], [newList count])]; - - [shuffleList insertObjects:newList atIndexes:indexSet]; - - unsigned long i; - for (i = ([shuffleList count] - [newList count]); i < [shuffleList count]; i++) - { - [[shuffleList objectAtIndex:i] setShuffleIndex:(int)i]; - } +- (void)addShuffledListToBack { + NSArray *newList; + NSIndexSet *indexSet; + + if ([self shuffle] == ShuffleAlbums) { + newList = [self shuffleAlbums]; + } else { + newList = [Shuffle shuffleList:[self arrangedObjects]]; + } + + indexSet = + [NSIndexSet indexSetWithIndexesInRange:NSMakeRange([shuffleList count], [newList count])]; + + [shuffleList insertObjects:newList atIndexes:indexSet]; + + unsigned long i; + for (i = ([shuffleList count] - [newList count]); i < [shuffleList count]; i++) { + [shuffleList[i] setShuffleIndex:(int) i]; + } } -- (void)resetShuffleList -{ - [shuffleList removeAllObjects]; +- (void)resetShuffleList { + [shuffleList removeAllObjects]; - [self addShuffledListToFront]; + [self addShuffledListToFront]; - if (currentEntry && currentEntry.index >= 0) - { + if (currentEntry && currentEntry.index >= 0) { if ([self shuffle] == ShuffleAlbums) { - NSString * currentAlbum = currentEntry.album; - if (!currentAlbum) - currentAlbum = @""; - - NSArray * wholeAlbum = [self filterPlaylistOnAlbum:currentAlbum]; - + NSString *currentAlbum = currentEntry.album; + if (!currentAlbum) currentAlbum = @""; + + NSArray *wholeAlbum = [self filterPlaylistOnAlbum:currentAlbum]; + // First prune the shuffle list of the currently playing album long i, j; for (i = 0; i < [shuffleList count];) { - if ([wholeAlbum containsObject:[shuffleList objectAtIndex:i]]) { + if ([wholeAlbum containsObject:shuffleList[i]]) { [shuffleList removeObjectAtIndex:i]; - } - else { + } else { ++i; } } - + // Then insert the playing album at the start - NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [wholeAlbum count])]; - + NSIndexSet *indexSet = + [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [wholeAlbum count])]; + [shuffleList insertObjects:wholeAlbum atIndexes:indexSet]; // Oops, gotta reset the shuffle indexes for (i = 0, j = [shuffleList count]; i < j; ++i) { - [[shuffleList objectAtIndex:i] setShuffleIndex:(int)i]; + [shuffleList[i] setShuffleIndex:(int) i]; } - } - else { + } else { [shuffleList insertObject:currentEntry atIndex:0]; [currentEntry setShuffleIndex:0]; - //Need to rejigger so the current entry is at the start now... + // Need to rejigger so the current entry is at the start now... long i, j; BOOL found = NO; - for (i = 1, j = [shuffleList count]; i < j && !found; i++) - { - if ([shuffleList objectAtIndex:i] == currentEntry) - { + for (i = 1, j = [shuffleList count]; i < j && !found; i++) { + if (shuffleList[i] == currentEntry) { found = YES; [shuffleList removeObjectAtIndex:i]; - } - else { - [[shuffleList objectAtIndex:i] setShuffleIndex: (int)i]; + } else { + [shuffleList[i] setShuffleIndex:(int) i]; } } } - } + } } -- (void)setCurrentEntry:(PlaylistEntry *)pe -{ - currentEntry.current = NO; - currentEntry.stopAfter = NO; - - pe.current = YES; - - if (pe != nil) - [tableView scrollRowToVisible:pe.index]; - - currentEntry = pe; +- (void)setCurrentEntry:(PlaylistEntry *)pe { + currentEntry.current = NO; + currentEntry.stopAfter = NO; + + pe.current = YES; + + if (pe != nil) [self.tableView scrollRowToVisible:pe.index]; + + currentEntry = pe; } -- (void)setShuffle:(ShuffleMode)s -{ - [[NSUserDefaults standardUserDefaults] setInteger:s forKey:@"shuffle"]; - if (s != ShuffleOff) - [self resetShuffleList]; - - [playbackController playlistDidChange:self]; -} -- (ShuffleMode)shuffle -{ - return (ShuffleMode) [[NSUserDefaults standardUserDefaults] integerForKey:@"shuffle"]; -} -- (void)setRepeat:(RepeatMode)r -{ - [[NSUserDefaults standardUserDefaults] setInteger:r forKey:@"repeat"]; - [playbackController playlistDidChange:self]; -} -- (RepeatMode)repeat -{ - return (RepeatMode) [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"]; +- (void)setShuffle:(ShuffleMode)s { + [[NSUserDefaults standardUserDefaults] setInteger:s forKey:@"shuffle"]; + if (s != ShuffleOff) [self resetShuffleList]; + + [playbackController playlistDidChange:self]; } -- (IBAction)clear:(id)sender -{ - [self setFilterPredicate:nil]; - - [self removeObjectsAtArrangedObjectIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [[self arrangedObjects] count])]]; +- (ShuffleMode)shuffle { + return (ShuffleMode) [[NSUserDefaults standardUserDefaults] integerForKey:@"shuffle"]; } -- (IBAction)clearFilterPredicate:(id)sender -{ - [self setFilterPredicate:nil]; +- (void)setRepeat:(RepeatMode)r { + [[NSUserDefaults standardUserDefaults] setInteger:r forKey:@"repeat"]; + [playbackController playlistDidChange:self]; } -- (void)setFilterPredicate:(NSPredicate *)filterPredicate -{ - [super setFilterPredicate:filterPredicate]; +- (RepeatMode)repeat { + return (RepeatMode) [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"]; } -- (IBAction)showEntryInFinder:(id)sender -{ - NSWorkspace* ws = [NSWorkspace sharedWorkspace]; - - NSURL *url = [[[self selectedObjects] objectAtIndex:0] URL]; - if ([url isFileURL]) - [ws selectFile:[url path] inFileViewerRootedAtPath:[url path]]; +- (IBAction)clear:(id)sender { + [self setFilterPredicate:nil]; + + [self + removeObjectsAtArrangedObjectIndexes: + [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [[self arrangedObjects] count])]]; } + +- (IBAction)clearFilterPredicate:(id)sender { + [self setFilterPredicate:nil]; +} + +- (void)setFilterPredicate:(NSPredicate *)filterPredicate { + [super setFilterPredicate:filterPredicate]; +} + +- (IBAction)showEntryInFinder:(id)sender { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + + NSURL *url = [[self selectedObjects][0] URL]; + if ([url isFileURL]) [ws selectFile:[url path] inFileViewerRootedAtPath:[url path]]; +} + /* - (IBAction)showTagEditor:(id)sender { // call the editor & pass the url - if ([self selectionIndex] < 0) - return; - - NSURL *url = [[[self selectedObjects] objectAtIndex:0] URL]; - if ([url isFileURL]) - [TagEditorController openTagEditor:url sender:sender]; - + if ([self selectionIndex] < 0) + return; + + NSURL *url = [[[self selectedObjects] objectAtIndex:0] URL]; + if ([url isFileURL]) + [TagEditorController openTagEditor:url sender:sender]; + } */ -- (IBAction)searchByArtist:(id)sender; -{ +- (IBAction)searchByArtist:(id)sender; { PlaylistEntry *entry = [[self arrangedObjects] objectAtIndex:[self selectionIndex]]; [spotlightWindowController searchForArtist:[entry artist]]; } -- (IBAction)searchByAlbum:(id)sender; -{ + +- (IBAction)searchByAlbum:(id)sender; { PlaylistEntry *entry = [[self arrangedObjects] objectAtIndex:[self selectionIndex]]; [spotlightWindowController searchForAlbum:[entry album]]; } -- (NSMutableArray *)queueList -{ - return queueList; +- (NSMutableArray *)queueList { + return queueList; } -- (IBAction)emptyQueueList:(id)sender -{ - for (PlaylistEntry *queueItem in queueList) - { - queueItem.queued = NO; - [queueItem setQueuePosition:-1]; - } +- (IBAction)emptyQueueList:(id)sender { + for (PlaylistEntry *queueItem in queueList) { + queueItem.queued = NO; + [queueItem setQueuePosition:-1]; + } - [queueList removeAllObjects]; + [queueList removeAllObjects]; } +- (IBAction)toggleQueued:(id)sender { + for (PlaylistEntry *queueItem in [self selectedObjects]) { + if (queueItem.queued) { + queueItem.queued = NO; + queueItem.queuePosition = -1; -- (IBAction)toggleQueued:(id)sender -{ - for (PlaylistEntry *queueItem in [self selectedObjects]) - { - if (queueItem.queued) - { - queueItem.queued = NO; - queueItem.queuePosition = -1; + [queueList removeObject:queueItem]; + } else { + queueItem.queued = YES; + queueItem.queuePosition = (int) [queueList count]; - [queueList removeObject:queueItem]; - } - else - { - queueItem.queued = YES; - queueItem.queuePosition = (int) [queueList count]; - - [queueList addObject:queueItem]; - } - - DLog(@"TOGGLE QUEUED: %i", queueItem.queued); - } + [queueList addObject:queueItem]; + } - int i = 0; - for (PlaylistEntry *cur in queueList) - { - cur.queuePosition = i++; - } + DLog(@"TOGGLE QUEUED: %i", queueItem.queued); + } + + int i = 0; + for (PlaylistEntry *cur in queueList) { + cur.queuePosition = i++; + } } -- (IBAction)removeFromQueue:(id)sender -{ - for (PlaylistEntry *queueItem in [self selectedObjects]) - { +- (IBAction)removeFromQueue:(id)sender { + for (PlaylistEntry *queueItem in [self selectedObjects]) { queueItem.queued = NO; queueItem.queuePosition = -1; - + [queueList removeObject:queueItem]; } - + int i = 0; - for (PlaylistEntry *cur in queueList) - { + for (PlaylistEntry *cur in queueList) { cur.queuePosition = i++; } } -- (IBAction)addToQueue:(id)sender -{ - for (PlaylistEntry *queueItem in [self selectedObjects]) - { +- (IBAction)addToQueue:(id)sender { + for (PlaylistEntry *queueItem in [self selectedObjects]) { queueItem.queued = YES; queueItem.queuePosition = (int) [queueList count]; - + [queueList addObject:queueItem]; } - + int i = 0; - for (PlaylistEntry *cur in queueList) - { + for (PlaylistEntry *cur in queueList) { cur.queuePosition = i++; } } -- (IBAction)stopAfterCurrent:(id)sender -{ - currentEntry.stopAfter = !currentEntry.stopAfter; +- (IBAction)stopAfterCurrent:(id)sender { + currentEntry.stopAfter = !currentEntry.stopAfter; } --(BOOL)validateMenuItem:(NSMenuItem*)menuItem -{ - SEL action = [menuItem action]; - - if (action == @selector(removeFromQueue:)) - { - for (PlaylistEntry *q in [self selectedObjects]) - if (q.queuePosition >= 0) - return YES; +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { + SEL action = [menuItem action]; - return NO; - } + if (action == @selector(removeFromQueue:)) { + for (PlaylistEntry *q in [self selectedObjects]) + if (q.queuePosition >= 0) return YES; - if (action == @selector(emptyQueueList:) && ([queueList count] < 1)) - return NO; - - if (action == @selector(stopAfterCurrent:) && currentEntry.stopAfter) - return NO; - - // if nothing is selected, gray out these - if ([[self selectedObjects] count] < 1) - { - - if (action == @selector(remove:)) - return NO; - - if (action == @selector(addToQueue:)) - return NO; + return NO; + } - if (action == @selector(searchByArtist:)) - return NO; + if (action == @selector(emptyQueueList:) && ([queueList count] < 1)) return NO; - if (action == @selector(searchByAlbum:)) - return NO; - } - - return YES; + if (action == @selector(stopAfterCurrent:) && currentEntry.stopAfter) return NO; + + // if nothing is selected, gray out these + if ([[self selectedObjects] count] < 1) { + if (action == @selector(remove:)) return NO; + + if (action == @selector(addToQueue:)) return NO; + + if (action == @selector(searchByArtist:)) return NO; + + if (action == @selector(searchByAlbum:)) return NO; + } + + return YES; } // Event inlets: -- (void)willInsertURLs:(NSArray*)urls origin:(URLOrigin)origin -{ - if (![urls count]) - return; +- (void)willInsertURLs:(NSArray *)urls origin:(URLOrigin)origin { + if (![urls count]) return; - CGEventRef event = CGEventCreate(NULL /*default event source*/); - CGEventFlags mods = CGEventGetFlags(event); - CFRelease(event); - - BOOL modifierPressed = ((mods & kCGEventFlagMaskCommand)!=0)&((mods & kCGEventFlagMaskControl)!=0); - modifierPressed |= ((mods & kCGEventFlagMaskShift)!=0); + CGEventRef event = CGEventCreate(NULL /*default event source*/); + CGEventFlags mods = CGEventGetFlags(event); + CFRelease(event); - NSString *behavior = [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesBehavior"]; - if (modifierPressed) { - behavior = [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesAlteredBehavior"]; - } - - - BOOL shouldClear = modifierPressed; // By default, internal sources should not clear the playlist - if (origin == URLOriginExternal) { // For external insertions, we look at the preference - //possible settings are "clearAndPlay", "enqueue", "enqueueAndPlay" - shouldClear = [behavior isEqualToString:@"clearAndPlay"]; - } - - if (shouldClear) { - [self clear:self]; - } + BOOL modifierPressed = + ((mods & kCGEventFlagMaskCommand) != 0) & ((mods & kCGEventFlagMaskControl) != 0); + modifierPressed |= ((mods & kCGEventFlagMaskShift) != 0); + + NSString *behavior = + [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesBehavior"]; + if (modifierPressed) { + behavior = + [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesAlteredBehavior"]; + } + + BOOL shouldClear = + modifierPressed; // By default, internal sources should not clear the playlist + if (origin == URLOriginExternal) { // For external insertions, we look at the preference + // possible settings are "clearAndPlay", "enqueue", "enqueueAndPlay" + shouldClear = [behavior isEqualToString:@"clearAndPlay"]; + } + + if (shouldClear) { + [self clear:self]; + } } -- (void)didInsertURLs:(NSArray*)urls origin:(URLOrigin)origin -{ - if (![urls count]) - return; - - CGEventRef event = CGEventCreate(NULL); - CGEventFlags mods = CGEventGetFlags(event); - CFRelease(event); - - BOOL modifierPressed = ((mods & kCGEventFlagMaskCommand)!=0)&((mods & kCGEventFlagMaskControl)!=0); - modifierPressed |= ((mods & kCGEventFlagMaskShift)!=0); - - NSString *behavior = [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesBehavior"]; - if (modifierPressed) { - behavior = [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesAlteredBehavior"]; - } - - BOOL shouldPlay = modifierPressed; // The default is NO for internal insertions - if (origin == URLOriginExternal) { // For external insertions, we look at the preference - shouldPlay = [behavior isEqualToString:@"clearAndPlay"] || [behavior isEqualToString:@"enqueueAndPlay"];; - } +- (void)didInsertURLs:(NSArray *)urls origin:(URLOrigin)origin { + if (![urls count]) return; - //Auto start playback - if (shouldPlay && [[self content] count] > 0) { - [playbackController playEntry: [urls objectAtIndex:0]]; - } + CGEventRef event = CGEventCreate(NULL); + CGEventFlags mods = CGEventGetFlags(event); + CFRelease(event); + + BOOL modifierPressed = + ((mods & kCGEventFlagMaskCommand) != 0) & ((mods & kCGEventFlagMaskControl) != 0); + modifierPressed |= ((mods & kCGEventFlagMaskShift) != 0); + + NSString *behavior = + [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesBehavior"]; + if (modifierPressed) { + behavior = + [[NSUserDefaults standardUserDefaults] valueForKey:@"openingFilesAlteredBehavior"]; + } + + BOOL shouldPlay = modifierPressed; // The default is NO for internal insertions + if (origin == URLOriginExternal) { // For external insertions, we look at the preference + shouldPlay = [behavior isEqualToString:@"clearAndPlay"] || + [behavior isEqualToString:@"enqueueAndPlay"];; + } + + // Auto start playback + if (shouldPlay && [[self content] count] > 0) { + [playbackController playEntry:urls[0]]; + } } - @end diff --git a/Playlist/PlaylistView.h b/Playlist/PlaylistView.h index 83e2ca41a..ed39842e1 100644 --- a/Playlist/PlaylistView.h +++ b/Playlist/PlaylistView.h @@ -13,11 +13,11 @@ #import "PlaylistLoader.h" @interface PlaylistView : NSTableView { - IBOutlet PlaybackController *playbackController; - IBOutlet PlaylistController *playlistController; + IBOutlet PlaybackController *playbackController; + IBOutlet PlaylistController *playlistController; IBOutlet PlaylistLoader *playlistLoader; - - NSMenu *headerContextMenu; + + NSMenu *headerContextMenu; } - (IBAction)toggleColumn:(id)sender; diff --git a/Playlist/PlaylistView.m b/Playlist/PlaylistView.m index 55a8b2a8d..84daa8ecb 100644 --- a/Playlist/PlaylistView.m +++ b/Playlist/PlaylistView.m @@ -7,13 +7,11 @@ // #import "PlaylistView.h" -#import "PlaybackController.h" -#import "PlaylistController.h" -#import "IndexFormatter.h" -#import "SecondsFormatter.h" #import "BlankZeroFormatter.h" +#import "IndexFormatter.h" #import "PlaylistEntry.h" +#import "SecondsFormatter.h" #import "CogAudio/Status.h" @@ -21,389 +19,364 @@ @implementation PlaylistView -- (void)awakeFromNib -{ - [[self menu] setAutoenablesItems:NO]; - +- (void)awakeFromNib { + [[self menu] setAutoenablesItems:NO]; + // Configure bindings to scale font size and row height - NSControlSize s = NSSmallControlSize; - NSFont *f = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:s]]; + NSControlSize s = NSControlSizeSmall; + NSFont *f = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:s]]; // NSFont *bf = [[NSFontManager sharedFontManager] convertFont:f toHaveTrait:NSBoldFontMask]; - - for (NSTableColumn *col in [self tableColumns]) { + + for (NSTableColumn *col in [self tableColumns]) { [[col dataCell] setControlSize:s]; [[col dataCell] setFont:f]; - } + } - //Set up formatters - NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init]; - [[[self tableColumnWithIdentifier:@"length"] dataCell] setFormatter:secondsFormatter]; - - NSFormatter *indexFormatter = [[IndexFormatter alloc] init]; - [[[self tableColumnWithIdentifier:@"index"] dataCell] setFormatter:indexFormatter]; - - NSFormatter *blankZeroFormatter = [[BlankZeroFormatter alloc] init]; - [[[self tableColumnWithIdentifier:@"track"] dataCell] setFormatter:blankZeroFormatter]; - [[[self tableColumnWithIdentifier:@"year"] dataCell] setFormatter:blankZeroFormatter]; - //end setting up formatters + // Set up formatters + NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init]; + [[[self tableColumnWithIdentifier:@"length"] dataCell] setFormatter:secondsFormatter]; - [self setVerticalMotionCanBeginDrag:YES]; - - //Set up header context menu - headerContextMenu = [[NSMenu alloc] initWithTitle:@"Playlist Header Context Menu"]; - - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"identifier" ascending:YES]; - NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; - - int visibleTableColumns = 0; - int menuIndex = 0; - for (NSTableColumn *col in [[self tableColumns] sortedArrayUsingDescriptors: sortDescriptors]) - { - NSString *title; - if ([[col identifier] isEqualToString:@"status"]) - { - title = @"Status"; - } - else if ([[col identifier] isEqualToString:@"index"]) - { - title = @"Index"; - } - else - { - title = [[col headerCell] title]; - } - - NSMenuItem *contextMenuItem = [headerContextMenu insertItemWithTitle:title action:@selector(toggleColumn:) keyEquivalent:@"" atIndex:menuIndex]; - - [contextMenuItem setTarget:self]; - [contextMenuItem setRepresentedObject:col]; - [contextMenuItem setState:([col isHidden] ? NSOffState : NSOnState)]; + NSFormatter *indexFormatter = [[IndexFormatter alloc] init]; + [[[self tableColumnWithIdentifier:@"index"] dataCell] setFormatter:indexFormatter]; - visibleTableColumns += ![col isHidden]; - menuIndex++; - } - - if (visibleTableColumns == 0) { - for (NSTableColumn *col in [self tableColumns]) { - [col setHidden:NO]; - } - } - - [[self headerView] setMenu:headerContextMenu]; + NSFormatter *blankZeroFormatter = [[BlankZeroFormatter alloc] init]; + [[[self tableColumnWithIdentifier:@"track"] dataCell] setFormatter:blankZeroFormatter]; + [[[self tableColumnWithIdentifier:@"year"] dataCell] setFormatter:blankZeroFormatter]; + // end setting up formatters + + [self setVerticalMotionCanBeginDrag:YES]; + + // Set up header context menu + headerContextMenu = [[NSMenu alloc] initWithTitle:@"Playlist Header Context Menu"]; + + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"identifier" + ascending:YES]; + NSArray *sortDescriptors = @[sortDescriptor]; + + int visibleTableColumns = 0; + int menuIndex = 0; + for (NSTableColumn *col in [[self tableColumns] sortedArrayUsingDescriptors:sortDescriptors]) { + NSString *title; + if ([[col identifier] isEqualToString:@"status"]) { + title = @"Status"; + } else if ([[col identifier] isEqualToString:@"index"]) { + title = @"Index"; + } else { + title = [[col headerCell] title]; + } + + NSMenuItem *contextMenuItem = + [headerContextMenu insertItemWithTitle:title + action:@selector(toggleColumn:) + keyEquivalent:@"" + atIndex:menuIndex]; + + [contextMenuItem setTarget:self]; + [contextMenuItem setRepresentedObject:col]; + [contextMenuItem setState:([col isHidden] ? NSControlStateValueOff : NSControlStateValueOn)]; + + visibleTableColumns += ![col isHidden]; + menuIndex++; + } + + if (visibleTableColumns == 0) { + for (NSTableColumn *col in [self tableColumns]) { + [col setHidden:NO]; + } + } + + [[self headerView] setMenu:headerContextMenu]; } +- (IBAction)toggleColumn:(id)sender { + id tc = [sender representedObject]; -- (IBAction)toggleColumn:(id)sender -{ - id tc = [sender representedObject]; - - if ([sender state] == NSOffState) - { - [sender setState:NSOnState]; + if ([sender state] == NSControlStateValueOff) { + [sender setState:NSControlStateValueOn]; - [tc setHidden: NO]; - } - else - { - [sender setState:NSOffState]; - - [tc setHidden: YES]; - } + [tc setHidden:NO]; + } else { + [sender setState:NSControlStateValueOff]; + + [tc setHidden:YES]; + } } -- (BOOL)acceptsFirstResponder -{ - return YES; +- (BOOL)acceptsFirstResponder { + return YES; } -- (BOOL)resignFirstResponder -{ - return YES; +- (BOOL)resignFirstResponder { + return YES; } -- (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent -{ - return NO; +- (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent { + return NO; } -- (void)mouseDown:(NSEvent *)e -{ - [super mouseDown:e]; - - if ([e type] == NSLeftMouseDown && [e clickCount] == 2 && [[self selectedRowIndexes] count] == 1) - { - [playbackController play:self]; - } +- (void)mouseDown:(NSEvent *)e { + [super mouseDown:e]; + + if ([e type] == NSEventTypeLeftMouseDown && [e clickCount] == 2 && + [[self selectedRowIndexes] count] == 1) { + [playbackController play:self]; + } } // enables right-click selection for "Show in Finder" contextual menu --(NSMenu*)menuForEvent:(NSEvent*)event -{ - //Find which row is under the cursor - [[self window] makeFirstResponder:self]; - NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil]; - NSInteger iRow = [self rowAtPoint:menuPoint]; - NSMenu* tableViewMenu = [self menu]; - - /* Update the table selection before showing menu - Preserves the selection if the row under the mouse is selected (to allow for - multiple items to be selected), otherwise selects the row under the mouse */ - BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:iRow]; - if (!currentRowIsSelected) { - if (iRow == -1) - { - [self deselectAll:self]; - } - else - { - [self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO]; - } - } +- (NSMenu *)menuForEvent:(NSEvent *)event { + // Find which row is under the cursor + [[self window] makeFirstResponder:self]; + NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil]; + NSInteger iRow = [self rowAtPoint:menuPoint]; + NSMenu *tableViewMenu = [self menu]; - if ([self numberOfSelectedRows] <=0) - { - //No rows are selected, so the table should be displayed with all items disabled - int i; - for (i=0;i<[tableViewMenu numberOfItems];i++) { - [[tableViewMenu itemAtIndex:i] setEnabled:NO]; - } - } - - return tableViewMenu; + /* Update the table selection before showing menu + Preserves the selection if the row under the mouse is selected (to allow for + multiple items to be selected), otherwise selects the row under the mouse */ + BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:(NSUInteger) iRow]; + if (!currentRowIsSelected) { + if (iRow == -1) { + [self deselectAll:self]; + } else { + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:(NSUInteger) iRow] byExtendingSelection:NO]; + } + } + + if ([self numberOfSelectedRows] <= 0) { + // No rows are selected, so the table should be displayed with all items disabled + int i; + for (i = 0; i < [tableViewMenu numberOfItems]; i++) { + [[tableViewMenu itemAtIndex:i] setEnabled:NO]; + } + } + + return tableViewMenu; } -- (void)keyDown:(NSEvent *)e -{ - unsigned int modifiers = [e modifierFlags] & (NSCommandKeyMask | NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask); - NSString *characters = [e characters]; - unichar c; +- (void)keyDown:(NSEvent *)e { + unsigned int modifiers = + [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | + NSEventModifierFlagControl | NSEventModifierFlagOption); + NSString *characters = [e characters]; + unichar c; - if ([characters length] != 1) - { - [super keyDown:e]; - - return; - } - - c = [characters characterAtIndex:0]; - if (modifiers == 0 && (c == NSDeleteCharacter || c == NSBackspaceCharacter || c == NSDeleteFunctionKey)) - { - [playlistController remove:self]; - } - else if (modifiers == 0 && c == ' ') - { - [playbackController playPauseResume:self]; - } - else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) - { - [playbackController play:self]; - } - else if (modifiers == 0 && c == NSLeftArrowFunctionKey) - { - [playbackController eventSeekBackward:self]; + if ([characters length] != 1) { + [super keyDown:e]; + + return; } - else if (modifiers == 0 && c == NSRightArrowFunctionKey) - { + + c = [characters characterAtIndex:0]; + if (modifiers == 0 && + (c == NSDeleteCharacter || c == NSBackspaceCharacter || c == NSDeleteFunctionKey)) { + [playlistController remove:self]; + } else if (modifiers == 0 && c == ' ') { + [playbackController playPauseResume:self]; + } else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) { + [playbackController play:self]; + } else if (modifiers == 0 && c == NSLeftArrowFunctionKey) { + [playbackController eventSeekBackward:self]; + } else if (modifiers == 0 && c == NSRightArrowFunctionKey) { [playbackController eventSeekForward:self]; } - // Escape - else if (modifiers == 0 && c == 0x1b) - { - [playlistController clearFilterPredicate:self]; - } - else - { - [super keyDown:e]; - } + // Escape + else if (modifiers == 0 && c == 0x1b) { + [playlistController clearFilterPredicate:self]; + } else { + [super keyDown:e]; + } } -- (IBAction)scrollToCurrentEntry:(id)sender -{ - [self scrollRowToVisible:[[playlistController currentEntry] index]]; - [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[[playlistController currentEntry] index]] byExtendingSelection:NO]; +- (IBAction)scrollToCurrentEntry:(id)sender { + [self scrollRowToVisible:[[playlistController currentEntry] index]]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:(NSUInteger) [[playlistController currentEntry] index]] + byExtendingSelection:NO]; } -- (IBAction)undo:(id)sender -{ - [[playlistController undoManager] undo]; +- (IBAction)undo:(id)sender { + [[playlistController undoManager] undo]; } -- (IBAction)redo:(id)sender -{ - [[playlistController undoManager] redo]; +- (IBAction)redo:(id)sender { + [[playlistController undoManager] redo]; } -- (IBAction)copy:(id)sender -{ +- (IBAction)copy:(id)sender { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; - [pboard clearContents]; - - NSMutableArray *selectedURLs = [[NSMutableArray alloc] init]; - - for (PlaylistEntry *pe in [[playlistController content] objectsAtIndexes:[playlistController selectionIndexes]]) - { + + NSArray *entries = + [[playlistController content] objectsAtIndexes:[playlistController selectionIndexes]]; + NSUInteger capacity = [entries count]; + NSMutableArray *selectedURLs = [NSMutableArray arrayWithCapacity:capacity]; + + for (PlaylistEntry *pe in entries) { [selectedURLs addObject:[pe URL]]; } - - [pboard setData:[NSArchiver archivedDataWithRootObject:selectedURLs] forType:CogUrlsPboardType]; - - NSMutableDictionary * tracks = [[NSMutableDictionary alloc] init]; - + + NSError *error; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:selectedURLs + requiringSecureCoding:YES + error:&error]; + if (!data) { + DLog(@"Error: %@", error); + } + [pboard setData:data forType:CogUrlsPboardType]; + + NSMutableDictionary *tracks = [NSMutableDictionary dictionaryWithCapacity:capacity]; + unsigned long i = 0; - for (NSURL *url in selectedURLs) - { - NSMutableDictionary * track = [NSMutableDictionary dictionaryWithObjectsAndKeys:[url absoluteString], @"Location", nil]; - [tracks setObject:track forKey:[NSString stringWithFormat:@"%lu", i]]; - ++i; + for (NSURL *url in selectedURLs) { + tracks[[NSString stringWithFormat:@"%lu", i++]] = @{@"Location": [url absoluteString]}; } - NSMutableDictionary * itunesPlist = [NSMutableDictionary dictionaryWithObjectsAndKeys:tracks, @"Tracks", nil]; - + NSDictionary *itunesPlist = @{@"Tracks": tracks}; + [pboard setPropertyList:itunesPlist forType:iTunesDropType]; - - NSMutableArray *filePaths = [[NSMutableArray alloc] init]; - - for (NSURL *url in selectedURLs) - { - if ([url isFileURL]) - [filePaths addObject:[url path]]; + + NSMutableArray *filePaths = [NSMutableArray array]; + + for (NSURL *url in selectedURLs) { + if ([url isFileURL]) { + [filePaths addObject:url]; + } + } + + if ([filePaths count]) { + [pboard writeObjects:filePaths]; } - - if ([filePaths count]) - [pboard setPropertyList:filePaths forType:NSFilenamesPboardType]; } -- (IBAction)cut:(id)sender -{ +- (IBAction)cut:(id)sender { [self copy:sender]; - + [playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]]; - if ([playlistController shuffle] != ShuffleOff) - [playlistController resetShuffleList]; + if ([playlistController shuffle] != ShuffleOff) [playlistController resetShuffleList]; } -- (IBAction)paste:(id)sender -{ - // Determine the type of object that was dropped - NSArray *supportedTypes = [NSArray arrayWithObjects:CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil]; - NSPasteboard *pboard = [NSPasteboard generalPasteboard]; - NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; - - NSMutableArray *acceptedURLs = [[NSMutableArray alloc] init]; - - // Get files from an file drawer drop - if ([bestType isEqualToString:CogUrlsPboardType]) { - NSArray *urls = [NSUnarchiver unarchiveObjectWithData:[pboard dataForType:CogUrlsPboardType]]; - DLog(@"URLS: %@", urls); - //[playlistLoader insertURLs: urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - // Get files from a normal file drop (such as from Finder) - if ([bestType isEqualToString:NSFilenamesPboardType]) { - NSMutableArray *urls = [[NSMutableArray alloc] init]; - - for (NSString *file in [pboard propertyListForType:NSFilenamesPboardType]) - { - [urls addObject:[NSURL fileURLWithPath:file]]; - } - - //[playlistLoader insertURLs:urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - // Get files from an iTunes drop - if ([bestType isEqualToString:iTunesDropType]) { - NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType]; - NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"]; - - // Convert the iTunes URLs to URLs....MWAHAHAH! - NSMutableArray *urls = [[NSMutableArray alloc] init]; - - for (NSDictionary *trackInfo in [tracks allValues]) { - [urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]]; - } - - //[playlistLoader insertURLs:urls atIndex:row sort:YES]; - [acceptedURLs addObjectsFromArray:urls]; - } - - if ([acceptedURLs count]) - { - NSUInteger row = [[playlistController content] count]; - - [playlistController willInsertURLs:acceptedURLs origin:URLOriginInternal]; - - NSArray* entries = [playlistLoader insertURLs:acceptedURLs atIndex:(int)row sort:NO]; - [playlistLoader didInsertURLs:entries origin:URLOriginInternal]; - - if ([playlistController shuffle] != ShuffleOff) - [playlistController resetShuffleList]; - } -} - -- (IBAction)delete:(id)sender -{ - [playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]]; -} - - --(BOOL)validateUserInterfaceItem:(id )anItem -{ - SEL action = [anItem action]; - - if (action == @selector(undo:)) - { - if ([[playlistController undoManager] canUndo]) - return YES; - else - return NO; - } - if (action == @selector(redo:)) - { - if ([[playlistController undoManager] canRedo]) - return YES; - else - return NO; - } - if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:)) - { - if ([[playlistController selectionIndexes] count] == 0) - return NO; - else - return YES; +- (IBAction)paste:(id)sender { + // Determine the type of object that was dropped + NSArray *supportedTypes = @[CogUrlsPboardType, NSPasteboardTypeFileURL, iTunesDropType]; + NSPasteboard *pboard = [NSPasteboard generalPasteboard]; + NSPasteboardType bestType = [pboard availableTypeFromArray:supportedTypes]; + DLog(@"All types:"); + for (NSPasteboardType type in [pboard types]) { + DLog(@" Type: %@", type); } - if (action == @selector(paste:)) - { + DLog(@"Supported types:"); + for (NSPasteboardType type in supportedTypes) { + DLog(@" Type: %@", type); + } + DLog(@"Best type: %@", bestType); + + NSMutableArray *acceptedURLs = [NSMutableArray array]; + + // Get files from an file drawer drop + if ([bestType isEqualToString:CogUrlsPboardType]) { + NSError *error; + NSData *data = [pboard dataForType:CogUrlsPboardType]; + NSArray *urls; + if (@available(macOS 11.0, *)) { + urls = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:[NSURL class] + fromData:data + error:&error]; + } else { + NSSet *allowed = [NSSet setWithArray:@[[NSArray class], [NSURL class]]]; + urls = [NSKeyedUnarchiver unarchivedObjectOfClasses:allowed fromData:data error:&error]; + } + if (!urls) { + DLog(@"%@", error); + } else { + DLog(@"URLS: %@", urls); + } + //[playlistLoader insertURLs: urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + // Get files from a normal file drop (such as from Finder) + if ([bestType isEqualToString:NSPasteboardTypeFileURL]) { + NSMutableArray *urls = [[NSMutableArray alloc] init]; + + for (NSString *file in [pboard propertyListForType:NSPasteboardTypeFileURL]) { + [urls addObject:[NSURL fileURLWithPath:file]]; + } + + //[playlistLoader insertURLs:urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + // Get files from an iTunes drop + if ([bestType isEqualToString:iTunesDropType]) { + NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType]; + NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"]; + + // Convert the iTunes URLs to URLs....MWAHAHAH! + NSMutableArray *urls = [[NSMutableArray alloc] init]; + + for (NSDictionary *trackInfo in [tracks allValues]) { + [urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]]; + } + + //[playlistLoader insertURLs:urls atIndex:row sort:YES]; + [acceptedURLs addObjectsFromArray:urls]; + } + + if ([acceptedURLs count]) { + NSUInteger row = [[playlistController content] count]; + + [playlistController willInsertURLs:acceptedURLs origin:URLOriginInternal]; + + NSArray *entries = [playlistLoader insertURLs:acceptedURLs atIndex:(int) row sort:NO]; + [playlistLoader didInsertURLs:entries origin:URLOriginInternal]; + + if ([playlistController shuffle] != ShuffleOff) [playlistController resetShuffleList]; + } +} + +- (IBAction)delete:(id)sender { + [playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]]; +} + +- (BOOL)validateUserInterfaceItem:(id )anItem { + SEL action = [anItem action]; + + if (action == @selector(undo:)) { + return [[playlistController undoManager] canUndo]; + } + if (action == @selector(redo:)) { + return [[playlistController undoManager] canRedo]; + } + if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:)) { + return [[playlistController selectionIndexes] count] != 0; + } + if (action == @selector(paste:)) { NSPasteboard *pboard = [NSPasteboard generalPasteboard]; - NSArray *supportedTypes = [NSArray arrayWithObjects:CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil]; - + NSArray *supportedTypes = @[CogUrlsPboardType, NSPasteboardTypeFileURL, iTunesDropType]; + NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; - - if (bestType != nil) - return YES; - else - return NO; + + return bestType != nil; } - - if (action == @selector(scrollToCurrentEntry:) && (([playbackController playbackStatus] == kCogStatusStopped) || ([playbackController playbackStatus] == kCogStatusStopping))) - return NO; - - return [super validateUserInterfaceItem:anItem]; + + if (action == @selector(scrollToCurrentEntry:) && + (([playbackController playbackStatus] == kCogStatusStopped) || + ([playbackController playbackStatus] == kCogStatusStopping))) + return NO; + + return [super validateUserInterfaceItem:anItem]; } #if 0 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { - if (isLocal) - return NSDragOperationNone; - else - return NSDragOperationCopy; + if (isLocal) + return NSDragOperationNone; + else + return NSDragOperationCopy; } #endif - @end diff --git a/Playlist/XmlContainer.h b/Playlist/XmlContainer.h index 9d1420294..0216dde0a 100644 --- a/Playlist/XmlContainer.h +++ b/Playlist/XmlContainer.h @@ -8,9 +8,7 @@ #import -@interface XmlContainer : NSObject { - -} +@interface XmlContainer : NSObject + (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename; diff --git a/Playlist/XmlContainer.m b/Playlist/XmlContainer.m index 4fcc2858d..a52b39c4d 100644 --- a/Playlist/XmlContainer.m +++ b/Playlist/XmlContainer.m @@ -8,101 +8,109 @@ #import "XmlContainer.h" -#import "PlaylistEntry.h" - #import "Logging.h" @implementation XmlContainer -+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename -{ - NSRange protocolRange = [path rangeOfString:@"://"]; - if (protocolRange.location != NSNotFound) - { - return [NSURL URLWithString:path]; - } ++ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename { + NSRange protocolRange = [path rangeOfString:@"://"]; + if (protocolRange.location != NSNotFound) { + return [NSURL URLWithString:path]; + } - NSMutableString *unixPath = [path mutableCopy]; + NSMutableString *unixPath = [path mutableCopy]; - //Get the fragment - NSString *fragment = @""; - NSScanner *scanner = [NSScanner scannerWithString:unixPath]; - NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"#1234567890"]; - while (![scanner isAtEnd]) { - NSString *possibleFragment; - [scanner scanUpToString:@"#" intoString:nil]; + //Get the fragment + NSString *fragment = @""; + NSScanner *scanner = [NSScanner scannerWithString:unixPath]; + NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"#1234567890"]; + while (![scanner isAtEnd]) { + NSString *possibleFragment; + [scanner scanUpToString:@"#" intoString:nil]; - if ([scanner scanCharactersFromSet:characterSet intoString:&possibleFragment] && [scanner isAtEnd]) - { - fragment = possibleFragment; - [unixPath deleteCharactersInRange:NSMakeRange([scanner scanLocation] - [possibleFragment length], [possibleFragment length])]; - break; - } - } - DLog(@"Fragment: %@", fragment); + if ([scanner scanCharactersFromSet:characterSet intoString:&possibleFragment] && [scanner isAtEnd]) { + fragment = possibleFragment; + [unixPath deleteCharactersInRange:NSMakeRange([scanner scanLocation] - [possibleFragment length], [possibleFragment length])]; + break; + } + } + DLog(@"Fragment: %@", fragment); - if (![unixPath hasPrefix:@"/"]) { - //Only relative paths would have windows backslashes. - [unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])]; - - NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"]; + if (![unixPath hasPrefix:@"/"]) { + //Only relative paths would have windows backslashes. + [unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])]; - [unixPath insertString:basePath atIndex:0]; - } - - //Append the fragment - NSURL *url = [NSURL URLWithString:[[[NSURL fileURLWithPath:unixPath] absoluteString] stringByAppendingString: fragment]]; - return url; + NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"]; + + [unixPath insertString:basePath atIndex:0]; + } + + //Append the fragment + NSURL *url = [NSURL URLWithString:[[[NSURL fileURLWithPath:unixPath] absoluteString] stringByAppendingString:fragment]]; + return url; } -+ (NSDictionary *)entriesForContainerURL:(NSURL *)url -{ - if (![url isFileURL]) - return [NSDictionary dictionary]; ++ (NSDictionary *)entriesForContainerURL:(NSURL *)url { + if (![url isFileURL]) + return nil; - NSError *nserr; - - NSString *error; - - NSString *filename = [url path]; - - NSString * contents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&nserr]; - - NSData* plistData = [contents dataUsingEncoding:NSUTF8StringEncoding]; - - NSPropertyListFormat format; - id plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error]; - if(!plist){ - ALog(@"Error: %@",error); + NSError *error; + + NSString *filename = [url path]; + + NSString *contents = [NSString stringWithContentsOfFile:filename + encoding:NSUTF8StringEncoding + error:&error]; + if (!contents) { + ALog(@"Error: %@", error); return nil; } - + + NSData *plistData = [contents dataUsingEncoding:NSUTF8StringEncoding]; + + NSPropertyListFormat format; + id plist = [NSPropertyListSerialization propertyListWithData:plistData + options:NSPropertyListImmutable + format:&format + error:&error]; + if (!plist) { + ALog(@"Error: %@", error); + return nil; + } + BOOL isArray = [plist isKindOfClass:[NSArray class]]; BOOL isDict = [plist isKindOfClass:[NSDictionary class]]; - - if (!isDict && !isArray) return nil; - - NSArray * items = (isArray) ? (NSArray*)plist : [(NSDictionary *)plist objectForKey:@"items"]; - - NSDictionary *albumArt = (isArray) ? nil : [(NSDictionary *)plist objectForKey:@"albumArt"]; - NSArray *queueList = (isArray) ? [NSArray array] : [(NSDictionary *)plist objectForKey:@"queue"]; - - NSMutableArray *entries = [NSMutableArray array]; - - for (NSDictionary *entry in items) - { - NSMutableDictionary * preparedEntry = [NSMutableDictionary dictionaryWithDictionary:entry]; - - [preparedEntry setObject:[self urlForPath:[preparedEntry objectForKey:@"URL"] relativeTo:filename] forKey:@"URL"]; - - if (albumArt && [preparedEntry objectForKey:@"albumArt"]) - [preparedEntry setObject:[albumArt objectForKey:[preparedEntry objectForKey:@"albumArt"]] forKey:@"albumArt"]; - + if (!isDict && !isArray) return nil; + + NSArray *items; + NSDictionary *albumArt; + NSArray *queueList; + if (isArray) { + items = (NSArray *) plist; + albumArt = nil; + queueList = [NSArray array]; + } else { + NSDictionary *dict = (NSDictionary *) plist; + items = dict[@"items"]; + albumArt = dict[@"albumArt"]; + queueList = dict[@"queue"]; + } + + NSMutableArray *entries = [NSMutableArray array]; + + for (NSDictionary *entry in items) { + NSMutableDictionary *preparedEntry = [NSMutableDictionary dictionaryWithDictionary:entry]; + + preparedEntry[@"URL"] = [self urlForPath:preparedEntry[@"URL"] relativeTo:filename]; + + if (albumArt && preparedEntry[@"albumArt"]) + preparedEntry[@"albumArt"] = albumArt[preparedEntry[@"albumArt"]]; + [entries addObject:[NSDictionary dictionaryWithDictionary:preparedEntry]]; - } - - return [NSDictionary dictionaryWithObjectsAndKeys:entries, @"entries", queueList, @"queue", nil]; + } + + return @{@"entries": entries, @"queue": queueList}; } @end diff --git a/Plugins/APL/APL.xcodeproj/xcshareddata/xcschemes/APL Plugin.xcscheme b/Plugins/APL/APL.xcodeproj/xcshareddata/xcschemes/APL Plugin.xcscheme index c64ac88d9..551b73606 100644 --- a/Plugins/APL/APL.xcodeproj/xcshareddata/xcschemes/APL Plugin.xcscheme +++ b/Plugins/APL/APL.xcodeproj/xcshareddata/xcschemes/APL Plugin.xcscheme @@ -1,6 +1,6 @@ time_base); AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id); if (!codec) { diff --git a/Plugins/FileSource/FileSource.xcodeproj/xcshareddata/xcschemes/FileSource Plugin.xcscheme b/Plugins/FileSource/FileSource.xcodeproj/xcshareddata/xcschemes/FileSource Plugin.xcscheme index 60e7b0832..0368d9a45 100644 --- a/Plugins/FileSource/FileSource.xcodeproj/xcshareddata/xcschemes/FileSource Plugin.xcscheme +++ b/Plugins/FileSource/FileSource.xcodeproj/xcshareddata/xcschemes/FileSource Plugin.xcscheme @@ -1,6 +1,6 @@ & list) mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, 100 ); mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, interp ); mod->set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, -1 ); - mod->ctl_set( "render.resampler.emulate_amiga", "1" ); + mod->ctl_set_boolean("render.resampler.emulate_amiga", true); left.resize( 1024 ); right.resize( 1024 ); diff --git a/Plugins/Opus/OpusPlugin.xcodeproj/xcshareddata/xcschemes/Opus.xcscheme b/Plugins/Opus/OpusPlugin.xcodeproj/xcshareddata/xcschemes/Opus.xcscheme index cd63fc03f..398982698 100644 --- a/Plugins/Opus/OpusPlugin.xcodeproj/xcshareddata/xcschemes/Opus.xcscheme +++ b/Plugins/Opus/OpusPlugin.xcodeproj/xcshareddata/xcschemes/Opus.xcscheme @@ -1,6 +1,6 @@ -#import +#import +#import #import "Logging.h" diff --git a/Plugins/Vorbis/VorbisPlugin.xcodeproj/xcshareddata/xcschemes/Vorbis Plugin.xcscheme b/Plugins/Vorbis/VorbisPlugin.xcodeproj/xcshareddata/xcschemes/Vorbis Plugin.xcscheme index ce8e142f0..651c614cb 100644 --- a/Plugins/Vorbis/VorbisPlugin.xcodeproj/xcshareddata/xcschemes/Vorbis Plugin.xcscheme +++ b/Plugins/Vorbis/VorbisPlugin.xcodeproj/xcshareddata/xcschemes/Vorbis Plugin.xcscheme @@ -1,6 +1,6 @@ kMaximumURLs) + { + [mUrlArray removeObjectAtIndex:0]; + [mUrlComboBox removeItemAtIndex:0]; + } + } + else + { + // move the url to the bottom of the list + [mUrlArray removeObject:urlString]; + [mUrlArray addObject:urlString]; + [mUrlComboBox removeItemWithObjectValue:urlString]; + [mUrlComboBox addItemWithObjectValue:urlString]; + } + } + else + { + NSAlert *alert = [[NSAlert alloc] init]; + alert.messageText = NSLocalizedString(@"InvalidURLShort", @""); + alert.informativeText = NSLocalizedString(@"InvalidURLLong", @""); + if (mIsSheet) + [alert runModal]; + else + [alert beginSheetModalForWindow:mPanel completionHandler:nil]; - if ([sender tag] == NSOKButton) - { - // validate the URL - url = [self url]; - urlString = [self urlString]; - - if (url) - { - // save the url - if (![mUrlArray containsObject:urlString]) - { - // save the url - [mUrlArray addObject:urlString]; - - // add the url to the combo box - [mUrlComboBox addItemWithObjectValue:urlString]; - - // remove the oldest url if the maximum has been exceeded - if ([mUrlArray count] > kMaximumURLs) - { - [mUrlArray removeObjectAtIndex:0]; - [mUrlComboBox removeItemAtIndex:0]; - } - } - else - { - // move the url to the bottom of the list - [mUrlArray removeObject:urlString]; - [mUrlArray addObject:urlString]; - [mUrlComboBox removeItemWithObjectValue:urlString]; - [mUrlComboBox addItemWithObjectValue:urlString]; - } - } - else - { - if (mIsSheet) - NSRunAlertPanel(NSLocalizedString(@"InvalidURLShort", @""), NSLocalizedString(@"InvalidURLLong", @""), nil, nil, nil); - else - NSBeginAlertSheet(NSLocalizedString(@"InvalidURLShort", @""), nil, nil, nil, mPanel, nil, nil, nil, nil, NSLocalizedString(@"InvalidURLLong", @"")); - - informDelegate = NO; - } - } - - // inform the delegate - if (informDelegate && mDelegate && mDidEndSelector) - { - callback = (myIMP) [mDelegate methodForSelector:mDidEndSelector]; - callback(mDelegate, mDidEndSelector, self, [sender tag], mContextInfo); - - [self close]; - } + informDelegate = NO; + } + } + + // inform the delegate + if (informDelegate && mDelegate && mDidEndSelector) + { + callback = (myIMP) [mDelegate methodForSelector:mDidEndSelector]; + callback(mDelegate, mDidEndSelector, self, [sender tag], mContextInfo); + + [self close]; + } } // methods - (void)beginSheetWithWindow:(NSWindow *)window delegate:(id)delegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo { - // will this run as a sheet - mIsSheet = (window ? YES : NO); + // will this run as a sheet + mIsSheet = (window ? YES : NO); - // save the delegate, did end selector, and context info - mDelegate = delegate; - mDidEndSelector = (didEndSelector); - mContextInfo = contextInfo; + // save the delegate, did end selector, and context info + mDelegate = delegate; + mDidEndSelector = (didEndSelector); + mContextInfo = contextInfo; NSArray *objects; - // load the bundle (if necessary) - if (mPanel == nil) + // load the bundle (if necessary) + if (mPanel == nil) [[NSBundle mainBundle] loadNibNamed:@"OpenURLPanel" owner:self topLevelObjects:&objects]; - // start the sheet (or window) - [NSApp beginSheet:mPanel modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil]; + // start the sheet (or window) + [window beginSheet:mPanel completionHandler:nil]; } - (void)close { - // close it down - [NSApp endSheet:mPanel]; - [mPanel close]; + // close it down + [NSApp endSheet:mPanel]; + [mPanel close]; } @end diff --git a/Window/RepeatTransformers.h b/Window/RepeatTransformers.h index 0cbf53662..f06a6b153 100644 --- a/Window/RepeatTransformers.h +++ b/Window/RepeatTransformers.h @@ -10,9 +10,7 @@ #import "PlaylistController.h" -@interface RepeatModeTransformer : NSValueTransformer { - RepeatMode repeatMode; -} +@interface RepeatModeTransformer : NSValueTransformer - (id)initWithMode:(RepeatMode) r; diff --git a/Window/RepeatTransformers.m b/Window/RepeatTransformers.m index d5ac8a8ac..f4eb9e8f5 100644 --- a/Window/RepeatTransformers.m +++ b/Window/RepeatTransformers.m @@ -7,55 +7,47 @@ // #import "RepeatTransformers.h" -#import "PlaylistController.h" #import "Logging.h" -@implementation RepeatModeTransformer +@implementation RepeatModeTransformer { + RepeatMode repeatMode; +} + (Class)transformedValueClass { return [NSNumber class]; } + (BOOL)allowsReverseTransformation { return YES; } -- (id)initWithMode:(RepeatMode) r -{ - self = [super init]; - if (self) - { - repeatMode = r; - } - - return self; +- (id)initWithMode:(RepeatMode)r { + self = [super init]; + if (self) { + repeatMode = r; + } + + return self; } // Convert from RepeatMode to BOOL - (id)transformedValue:(id)value { - DLog(@"Transforming value: %@", value); - - if (value == nil) return nil; - - RepeatMode mode = (RepeatMode) [value integerValue]; - - if (repeatMode == mode) { - return [NSNumber numberWithBool:YES]; - } - + DLog(@"Transforming value: %@", value); - return [NSNumber numberWithBool:NO]; + if (value == nil) return nil; + + RepeatMode mode = (RepeatMode) [value integerValue]; + + return @(repeatMode == mode); } - (id)reverseTransformedValue:(id)value { if (value == nil) return nil; - - BOOL enabled = [value boolValue]; - if (enabled) { - return [NSNumber numberWithInt:repeatMode]; - } - else if(repeatMode == RepeatNone) { - return [NSNumber numberWithInt:RepeatAll]; - } - else { - return [NSNumber numberWithInt:RepeatNone]; - } + + BOOL enabled = [value boolValue]; + if (enabled) { + return @(repeatMode); + } else if (repeatMode == RepeatModeNoRepeat) { + return @(RepeatModeRepeatAll); + } else { + return @(RepeatModeNoRepeat); + } } @end @@ -67,26 +59,26 @@ // Convert from string to RepeatMode - (id)transformedValue:(id)value { - DLog(@"Transforming value: %@", value); - + DLog(@"Transforming value: %@", value); + if (value == nil) return nil; - RepeatMode mode = (RepeatMode) [value integerValue]; - - if (mode == RepeatNone) { - return [NSImage imageNamed:@"repeatModeOffTemplate"]; - } - else if (mode == RepeatOne) { - return [NSImage imageNamed:@"repeatModeOneTemplate"]; - } - else if (mode == RepeatAlbum) { - return [NSImage imageNamed:@"repeatModeAlbumTemplate"]; - } - else if (mode == RepeatAll) { - return [NSImage imageNamed:@"repeatModeAllTemplate"]; - } + RepeatMode mode = (RepeatMode) [value integerValue]; - return nil; + if (mode == RepeatModeNoRepeat) { + return [NSImage imageNamed:@"repeatModeOffTemplate"]; + } + else if (mode == RepeatModeRepeatOne) { + return [NSImage imageNamed:@"repeatModeOneTemplate"]; + } + else if (mode == RepeatModeRepeatAlbum) { + return [NSImage imageNamed:@"repeatModeAlbumTemplate"]; + } + else if (mode == RepeatModeRepeatAll) { + return [NSImage imageNamed:@"repeatModeAllTemplate"]; + } + + return nil; } @end