Modernize several classes.
Use modern ObjC syntax. Use new Pasteboard APIs. Explicitly declare protocols.CQTexperiment
parent
730276a7e7
commit
c1da9a66e1
|
@ -11,16 +11,12 @@
|
|||
@class PathNode;
|
||||
@class PathWatcher;
|
||||
|
||||
@interface FileTreeDataSource : NSObject {
|
||||
PathNode *rootNode;
|
||||
|
||||
IBOutlet NSPathControl *pathControl;
|
||||
IBOutlet PathWatcher *watcher;
|
||||
IBOutlet NSOutlineView *outlineView;
|
||||
}
|
||||
@interface FileTreeDataSource : NSObject <NSOutlineViewDataSource>
|
||||
|
||||
@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;
|
||||
|
|
|
@ -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 <NSPasteboardWriting>)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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
extern NSString *CogPlaylistItemType;
|
||||
extern NSString *CogDNDIndexType;
|
||||
extern NSString *CogUrlsPboardType;
|
||||
extern NSString *iTunesDropType;
|
||||
|
||||
|
@ -12,13 +12,17 @@ extern NSString *iTunesDropType;
|
|||
// table view drag and drop support
|
||||
- (id <NSPasteboardWriting>)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 <NSDraggingInfo>)info
|
||||
proposedRow:(int)row
|
||||
proposedRow:(NSInteger)row
|
||||
proposedDropOperation:(NSTableViewDropOperation)dropOperation;
|
||||
- (BOOL)tableView:(NSTableView *)tableView
|
||||
acceptDrop:(id <NSDraggingInfo>)info
|
||||
row:(int)row
|
||||
row:(NSInteger)row
|
||||
dropOperation:(NSTableViewDropOperation)dropOperation;
|
||||
|
||||
// utility methods
|
||||
|
|
|
@ -3,45 +3,43 @@
|
|||
|
||||
#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 *CogPlaylistItemType = @"org.cogx.cog.playlist-item";
|
||||
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
|
||||
[self.tableView registerForDraggedTypes:@[CogPlaylistItemType, CogUrlsPboardType,
|
||||
NSFilenamesPboardType, iTunesDropType]];
|
||||
[self.tableView registerForDraggedTypes:@[CogDNDIndexType,
|
||||
CogUrlsPboardType,
|
||||
NSPasteboardTypeFileURL,
|
||||
iTunesDropType]];
|
||||
}
|
||||
|
||||
|
||||
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
|
||||
pasteboardWriterForRow:(NSInteger)row
|
||||
{
|
||||
pasteboardWriterForRow:(NSInteger)row {
|
||||
NSPasteboardItem *item = [[NSPasteboardItem alloc] init];
|
||||
[item setString:[@(row) stringValue] forType:CogPlaylistItemType];
|
||||
|
||||
[item setString:[@(row) stringValue] forType:CogDNDIndexType];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
- (void)tableView:(NSTableView *)tableView
|
||||
draggingSession:(NSDraggingSession *)session
|
||||
willBeginAtPoint:(NSPoint)screenPoint
|
||||
forRowIndexes:(NSIndexSet *)rowIndexes
|
||||
{
|
||||
draggingSession:(NSDraggingSession *)session
|
||||
willBeginAtPoint:(NSPoint)screenPoint
|
||||
forRowIndexes:(NSIndexSet *)rowIndexes {
|
||||
DLog(@"Drag session started with indexes: %@", rowIndexes);
|
||||
}
|
||||
|
||||
|
||||
- (NSDragOperation)tableView:(NSTableView*)tableView
|
||||
- (NSDragOperation)tableView:(NSTableView *)tableView
|
||||
validateDrop:(id <NSDraggingInfo>)info
|
||||
proposedRow:(int)row
|
||||
proposedDropOperation:(NSTableViewDropOperation)dropOperation
|
||||
{
|
||||
proposedRow:(NSInteger)row
|
||||
proposedDropOperation:(NSTableViewDropOperation)dropOperation {
|
||||
NSDragOperation dragOp = NSDragOperationCopy;
|
||||
|
||||
if ([info draggingSource] == tableView)
|
||||
|
@ -56,29 +54,28 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
|
|||
}
|
||||
|
||||
|
||||
- (BOOL)tableView:(NSTableView*)tableView
|
||||
- (BOOL)tableView:(NSTableView *)tableView
|
||||
acceptDrop:(id <NSDraggingInfo>)info
|
||||
row:(int)row
|
||||
dropOperation:(NSTableViewDropOperation)dropOperation
|
||||
{
|
||||
row:(NSInteger)row
|
||||
dropOperation:(NSTableViewDropOperation)dropOperation {
|
||||
if (row < 0) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
|
||||
NSArray<NSPasteboardItem *> *items = info.draggingPasteboard.pasteboardItems;
|
||||
// if drag source is self, it's a move
|
||||
if ([info draggingSource] == tableView || items == nil) {
|
||||
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
|
||||
for (NSPasteboardItem *item in items) {
|
||||
[indexSet addIndex:[[item stringForType:CogPlaylistItemType] intValue]];
|
||||
[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:row];
|
||||
|
||||
[self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:(unsigned int) row];
|
||||
|
||||
[self setSelectedObjects:selected];
|
||||
|
||||
|
||||
DLog(@"ACCEPTING DROP!");
|
||||
return YES;
|
||||
}
|
||||
|
@ -88,26 +85,25 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
|
|||
}
|
||||
|
||||
|
||||
-(void) moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
toIndex:(unsigned int)insertIndex
|
||||
{
|
||||
NSArray *objects = [self arrangedObjects];
|
||||
NSUInteger index = [indexSet lastIndex];
|
||||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
toIndex:(unsigned int)insertIndex {
|
||||
NSArray *objects = [self arrangedObjects];
|
||||
NSUInteger index = [indexSet lastIndex];
|
||||
|
||||
int aboveInsertIndexCount = 0;
|
||||
id object;
|
||||
int removeIndex;
|
||||
NSUInteger aboveInsertIndexCount = 0;
|
||||
id object;
|
||||
NSUInteger removeIndex;
|
||||
|
||||
while (NSNotFound != index) {
|
||||
if (index >= insertIndex) {
|
||||
removeIndex = (int)(index + aboveInsertIndexCount);
|
||||
removeIndex = index + aboveInsertIndexCount;
|
||||
aboveInsertIndexCount += 1;
|
||||
} else {
|
||||
removeIndex = (int)index;
|
||||
removeIndex = index;
|
||||
insertIndex -= 1;
|
||||
}
|
||||
|
||||
object = [objects objectAtIndex:removeIndex];
|
||||
object = objects[removeIndex];
|
||||
|
||||
[self removeObjectAtArrangedObjectIndex:removeIndex];
|
||||
[self insertObject:object atArrangedObjectIndex:insertIndex];
|
||||
|
|
|
@ -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 <NSTableViewDelegate> {
|
||||
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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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,377 +19,353 @@
|
|||
|
||||
@implementation PlaylistView
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
[[self menu] setAutoenablesItems:NO];
|
||||
|
||||
|
||||
// Configure bindings to scale font size and row height
|
||||
NSControlSize s = NSControlSizeSmall;
|
||||
NSFont *f = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:s]];
|
||||
// NSFont *bf = [[NSFontManager sharedFontManager] convertFont:f toHaveTrait:NSBoldFontMask];
|
||||
|
||||
|
||||
for (NSTableColumn *col in [self tableColumns]) {
|
||||
[[col dataCell] setControlSize:s];
|
||||
[[col dataCell] setFont:f];
|
||||
}
|
||||
|
||||
//Set up formatters
|
||||
|
||||
// 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
|
||||
|
||||
// end setting up formatters
|
||||
|
||||
[self setVerticalMotionCanBeginDrag:YES];
|
||||
|
||||
//Set up header context menu
|
||||
|
||||
// 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];
|
||||
|
||||
|
||||
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])
|
||||
{
|
||||
for (NSTableColumn *col in [[self tableColumns] sortedArrayUsingDescriptors:sortDescriptors]) {
|
||||
NSString *title;
|
||||
if ([[col identifier] isEqualToString:@"status"])
|
||||
{
|
||||
if ([[col identifier] isEqualToString:@"status"]) {
|
||||
title = @"Status";
|
||||
}
|
||||
else if ([[col identifier] isEqualToString:@"index"])
|
||||
{
|
||||
} else if ([[col identifier] isEqualToString:@"index"]) {
|
||||
title = @"Index";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
title = [[col headerCell] title];
|
||||
}
|
||||
|
||||
NSMenuItem *contextMenuItem = [headerContextMenu insertItemWithTitle:title action:@selector(toggleColumn:) keyEquivalent:@"" atIndex:menuIndex];
|
||||
|
||||
|
||||
NSMenuItem *contextMenuItem =
|
||||
[headerContextMenu insertItemWithTitle:title
|
||||
action:@selector(toggleColumn:)
|
||||
keyEquivalent:@""
|
||||
atIndex:menuIndex];
|
||||
|
||||
[contextMenuItem setTarget:self];
|
||||
[contextMenuItem setRepresentedObject:col];
|
||||
[contextMenuItem setState:([col isHidden] ? NSOffState : NSOnState)];
|
||||
|
||||
[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
|
||||
{
|
||||
- (IBAction)toggleColumn:(id)sender {
|
||||
id tc = [sender representedObject];
|
||||
|
||||
if ([sender state] == NSOffState)
|
||||
{
|
||||
[sender setState:NSOnState];
|
||||
|
||||
[tc setHidden: NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[sender setState:NSOffState];
|
||||
|
||||
[tc setHidden: YES];
|
||||
|
||||
if ([sender state] == NSControlStateValueOff) {
|
||||
[sender setState:NSControlStateValueOn];
|
||||
|
||||
[tc setHidden:NO];
|
||||
} else {
|
||||
[sender setState:NSControlStateValueOff];
|
||||
|
||||
[tc setHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
- (BOOL)acceptsFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
- (BOOL)resignFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent
|
||||
{
|
||||
- (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)e
|
||||
{
|
||||
- (void)mouseDown:(NSEvent *)e {
|
||||
[super mouseDown:e];
|
||||
|
||||
if ([e type] == NSEventTypeLeftMouseDown && [e clickCount] == 2 && [[self selectedRowIndexes] count] == 1)
|
||||
{
|
||||
|
||||
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
|
||||
- (NSMenu *)menuForEvent:(NSEvent *)event {
|
||||
// Find which row is under the cursor
|
||||
[[self window] makeFirstResponder:self];
|
||||
NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
|
||||
NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
|
||||
NSInteger iRow = [self rowAtPoint:menuPoint];
|
||||
NSMenu* tableViewMenu = [self menu];
|
||||
|
||||
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];
|
||||
BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:(NSUInteger) iRow];
|
||||
if (!currentRowIsSelected) {
|
||||
if (iRow == -1)
|
||||
{
|
||||
if (iRow == -1) {
|
||||
[self deselectAll:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO];
|
||||
} 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
|
||||
|
||||
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++) {
|
||||
for (i = 0; i < [tableViewMenu numberOfItems]; i++) {
|
||||
[[tableViewMenu itemAtIndex:i] setEnabled:NO];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return tableViewMenu;
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)e
|
||||
{
|
||||
unsigned int modifiers = [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption);
|
||||
NSString *characters = [e characters];
|
||||
unichar c;
|
||||
|
||||
if ([characters length] != 1)
|
||||
{
|
||||
- (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))
|
||||
{
|
||||
if (modifiers == 0 &&
|
||||
(c == NSDeleteCharacter || c == NSBackspaceCharacter || c == NSDeleteFunctionKey)) {
|
||||
[playlistController remove:self];
|
||||
}
|
||||
else if (modifiers == 0 && c == ' ')
|
||||
{
|
||||
} else if (modifiers == 0 && c == ' ') {
|
||||
[playbackController playPauseResume:self];
|
||||
}
|
||||
else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter))
|
||||
{
|
||||
} else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) {
|
||||
[playbackController play:self];
|
||||
}
|
||||
else if (modifiers == 0 && c == NSLeftArrowFunctionKey)
|
||||
{
|
||||
} else if (modifiers == 0 && c == NSLeftArrowFunctionKey) {
|
||||
[playbackController eventSeekBackward:self];
|
||||
}
|
||||
else if (modifiers == 0 && c == NSRightArrowFunctionKey)
|
||||
{
|
||||
} else if (modifiers == 0 && c == NSRightArrowFunctionKey) {
|
||||
[playbackController eventSeekForward:self];
|
||||
}
|
||||
// Escape
|
||||
else if (modifiers == 0 && c == 0x1b)
|
||||
{
|
||||
// Escape
|
||||
else if (modifiers == 0 && c == 0x1b) {
|
||||
[playlistController clearFilterPredicate:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[super keyDown:e];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)scrollToCurrentEntry:(id)sender
|
||||
{
|
||||
- (IBAction)scrollToCurrentEntry:(id)sender {
|
||||
[self scrollRowToVisible:[[playlistController currentEntry] index]];
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:[[playlistController currentEntry] index]] byExtendingSelection:NO];
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:(NSUInteger) [[playlistController currentEntry] index]]
|
||||
byExtendingSelection:NO];
|
||||
}
|
||||
|
||||
- (IBAction)undo:(id)sender
|
||||
{
|
||||
- (IBAction)undo:(id)sender {
|
||||
[[playlistController undoManager] undo];
|
||||
}
|
||||
|
||||
- (IBAction)redo:(id)sender
|
||||
{
|
||||
- (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
|
||||
{
|
||||
- (IBAction)paste:(id)sender {
|
||||
// Determine the type of object that was dropped
|
||||
NSArray *supportedTypes = [NSArray arrayWithObjects:CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil];
|
||||
NSArray *supportedTypes = @[CogUrlsPboardType, NSPasteboardTypeFileURL, iTunesDropType];
|
||||
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
|
||||
NSString *bestType = [pboard availableTypeFromArray:supportedTypes];
|
||||
|
||||
NSMutableArray *acceptedURLs = [[NSMutableArray alloc] init];
|
||||
|
||||
NSPasteboardType bestType = [pboard availableTypeFromArray:supportedTypes];
|
||||
DLog(@"All types:");
|
||||
for (NSPasteboardType type in [pboard types]) {
|
||||
DLog(@" Type: %@", type);
|
||||
}
|
||||
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]) {
|
||||
NSArray *urls = [NSUnarchiver unarchiveObjectWithData:[pboard dataForType:CogUrlsPboardType]];
|
||||
DLog(@"URLS: %@", urls);
|
||||
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:NSFilenamesPboardType]) {
|
||||
if ([bestType isEqualToString:NSPasteboardTypeFileURL]) {
|
||||
NSMutableArray *urls = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSString *file in [pboard propertyListForType:NSFilenamesPboardType])
|
||||
{
|
||||
|
||||
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])
|
||||
{
|
||||
|
||||
if ([acceptedURLs count]) {
|
||||
NSUInteger row = [[playlistController content] count];
|
||||
|
||||
|
||||
[playlistController willInsertURLs:acceptedURLs origin:URLOriginInternal];
|
||||
|
||||
NSArray* entries = [playlistLoader insertURLs:acceptedURLs atIndex:(int)row sort:NO];
|
||||
|
||||
NSArray *entries = [playlistLoader insertURLs:acceptedURLs atIndex:(int) row sort:NO];
|
||||
[playlistLoader didInsertURLs:entries origin:URLOriginInternal];
|
||||
|
||||
if ([playlistController shuffle] != ShuffleOff)
|
||||
[playlistController resetShuffleList];
|
||||
|
||||
if ([playlistController shuffle] != ShuffleOff) [playlistController resetShuffleList];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)delete:(id)sender
|
||||
{
|
||||
- (IBAction)delete:(id)sender {
|
||||
[playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]];
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
|
||||
{
|
||||
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
|
||||
SEL action = [anItem action];
|
||||
|
||||
if (action == @selector(undo:))
|
||||
{
|
||||
if ([[playlistController undoManager] canUndo])
|
||||
return YES;
|
||||
else
|
||||
return NO;
|
||||
|
||||
if (action == @selector(undo:)) {
|
||||
return [[playlistController undoManager] canUndo];
|
||||
}
|
||||
if (action == @selector(redo:))
|
||||
{
|
||||
if ([[playlistController undoManager] canRedo])
|
||||
return YES;
|
||||
else
|
||||
return NO;
|
||||
if (action == @selector(redo:)) {
|
||||
return [[playlistController undoManager] canRedo];
|
||||
}
|
||||
if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:))
|
||||
{
|
||||
if ([[playlistController selectionIndexes] count] == 0)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:)) {
|
||||
return [[playlistController selectionIndexes] count] != 0;
|
||||
}
|
||||
if (action == @selector(paste:))
|
||||
{
|
||||
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)))
|
||||
|
||||
if (action == @selector(scrollToCurrentEntry:) &&
|
||||
(([playbackController playbackStatus] == kCogStatusStopped) ||
|
||||
([playbackController playbackStatus] == kCogStatusStopping)))
|
||||
return NO;
|
||||
|
||||
|
||||
return [super validateUserInterfaceItem:anItem];
|
||||
}
|
||||
|
||||
|
@ -405,5 +379,4 @@
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface XmlContainer : NSObject {
|
||||
|
||||
}
|
||||
@interface XmlContainer : NSObject
|
||||
|
||||
+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
|
||||
#import "PlaylistController.h"
|
||||
|
||||
@interface RepeatModeTransformer : NSValueTransformer {
|
||||
RepeatMode repeatMode;
|
||||
}
|
||||
@interface RepeatModeTransformer : NSValueTransformer
|
||||
|
||||
- (id)initWithMode:(RepeatMode) r;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue