Modernize several classes.

Use modern ObjC syntax.
Use new Pasteboard APIs.
Explicitly declare protocols.
CQTexperiment
Dzmitry Neviadomski 2021-01-31 02:14:08 +03:00
parent 730276a7e7
commit c1da9a66e1
12 changed files with 1210 additions and 1393 deletions

View File

@ -11,16 +11,12 @@
@class PathNode; @class PathNode;
@class PathWatcher; @class PathWatcher;
@interface FileTreeDataSource : NSObject { @interface FileTreeDataSource : NSObject <NSOutlineViewDataSource>
PathNode *rootNode;
@property(nonatomic, weak) IBOutlet NSOutlineView *outlineView;
IBOutlet NSPathControl *pathControl; @property(nonatomic, weak) IBOutlet NSPathControl *pathControl;
IBOutlet PathWatcher *watcher; @property(nonatomic, weak) IBOutlet PathWatcher *watcher;
IBOutlet NSOutlineView *outlineView;
}
- (NSURL *)rootURL;
- (void)setRootURL:(NSURL *)rootURL;
- (void)changeURL:(NSURL *)rootURL; - (void)changeURL:(NSURL *)rootURL;
- (void)reloadPathNode:(PathNode *)item; - (void)reloadPathNode:(PathNode *)item;

View File

@ -8,176 +8,159 @@
#import "FileTreeDataSource.h" #import "FileTreeDataSource.h"
#import "DNDArrayController.h"
#import "DirectoryNode.h" #import "DirectoryNode.h"
#import "PathWatcher.h" #import "PathWatcher.h"
#import "Logging.h" #import "Logging.h"
@implementation FileTreeDataSource NSURL *defaultMusicDirectory() {
return [[NSFileManager defaultManager] URLForDirectory:NSMusicDirectory
+ (void)initialize inDomain:NSUserDomainMask
{ appropriateForURL:nil
NSMutableDictionary *userDefaultsValuesDict = [NSMutableDictionary dictionary]; create:NO
error:nil];
[userDefaultsValuesDict setObject:[[NSURL fileURLWithPath:[@"~/Music" stringByExpandingTildeInPath]] absoluteString] forKey:@"fileTreeRootURL"];
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
} }
- (void)awakeFromNib @interface FileTreeDataSource()
{
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.fileTreeRootURL" options:0 context:nil];
[self setRootURL: [NSURL URLWithString:[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"]]];
[pathControl setTarget:self]; @property NSURL *rootURL;
[pathControl setAction:@selector(pathControlAction:)];
@end
@implementation FileTreeDataSource {
PathNode *rootNode;
} }
- (void) observeValueForKeyPath:(NSString *)keyPath + (void)initialize {
ofObject:(id)object NSString *path = [defaultMusicDirectory() absoluteString];
change:(NSDictionary *)change NSDictionary *userDefaultsValuesDict = @{@"fileTreeRootURL": path};
context:(void *)context [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
{
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)changeURL:(NSURL *)url - (void)awakeFromNib {
{ [self.pathControl setTarget:self];
if (url != nil) [self.pathControl setAction:@selector(pathControlAction:)];
{ [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString] forKey:@"fileTreeRootURL"]; forKeyPath:@"values.fileTreeRootURL"
} options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionInitial
context:nil];
} }
- (void)pathControlAction:(id)sender - (void)observeValueForKeyPath:(NSString *)keyPath
{ ofObject:(id)object
if ([pathControl clickedPathComponentCell] != nil && [[pathControl clickedPathComponentCell] URL] != nil) change:(NSDictionary *)change
{ context:(void *)context {
[self changeURL:[[pathControl clickedPathComponentCell] URL]]; 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 - (void)changeURL:(NSURL *)url {
{ if (url != nil) {
return [rootNode URL]; [[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString]
forKey:@"fileTreeRootURL"];
}
} }
- (void)setRootURL: (NSURL *)rootURL - (void)pathControlAction:(id)sender {
{ NSPathControlItem *item = [self.pathControl clickedPathItem];
if (![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) if (item != nil && item.URL != nil) {
rootURL = [NSURL fileURLWithPath:[@"~/Music" stringByExpandingTildeInPath]]; [self changeURL:item.URL];
}
rootNode = [[DirectoryNode alloc] initWithDataSource:self url:rootURL];
[watcher setPath:[rootURL path]];
[self reloadPathNode:rootNode];
} }
- (PathNode *)nodeForPath:(NSString *)path - (NSURL *)rootURL {
{ return [rootNode URL];
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 - (void)setRootURL:(NSURL *)rootURL {
{ if (![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) {
DLog(@"PATH DID CHANGE: %@", path); rootURL = defaultMusicDirectory();
//Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node] }
PathNode *node = [self nodeForPath:path];
DLog(@"NODE IS: %@", node); rootNode = [[DirectoryNode alloc] initWithDataSource:self url:rootURL];
[node updatePath];
[self reloadPathNode:node]; [self.watcher setPath:[rootURL path]];
[self reloadPathNode:rootNode];
} }
- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item - (PathNode *)nodeForPath:(NSString *)path {
{ NSString *relativePath = [[path stringByReplacingOccurrencesOfString:[[[self rootURL] path] stringByAppendingString:@"/"]
PathNode *n = (item == nil ? rootNode : item); 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]; return (int) [[n subpaths] count];
} }
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item PathNode *n = (item == nil ? rootNode : item);
{
PathNode *n = (item == nil ? rootNode : item);
return ([n isLeaf] == NO); return ![n isLeaf];
} }
- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
{ PathNode *n = (item == nil ? rootNode : 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 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
{ PathNode *n = (item == nil ? rootNode : item);
PathNode *n = (item == nil ? rootNode : item);
return n; return n;
} }
//Drag it drop it - (id <NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item {
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard { NSPasteboardItem *paste = [[NSPasteboardItem alloc] init];
//Get selected paths [paste setData:[[item URL] dataRepresentation] forType:NSPasteboardTypeFileURL];
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:[items count]]; return paste;
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;
} }
- (void)reloadPathNode:(PathNode *)item - (void)reloadPathNode:(PathNode *)item {
{ if (item == rootNode) {
if (item == rootNode) [self.outlineView reloadData];
{ } else {
[outlineView reloadData]; [self.outlineView reloadItem:item reloadChildren:YES];
} }
else
{
[outlineView reloadItem:item reloadChildren:YES];
}
} }
@end @end

View File

@ -1,7 +1,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
extern NSString *CogPlaylistItemType; extern NSString *CogDNDIndexType;
extern NSString *CogUrlsPboardType; extern NSString *CogUrlsPboardType;
extern NSString *iTunesDropType; extern NSString *iTunesDropType;
@ -12,13 +12,17 @@ extern NSString *iTunesDropType;
// table view drag and drop support // table view drag and drop support
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView - (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
pasteboardWriterForRow:(NSInteger)row; pasteboardWriterForRow:(NSInteger)row;
- (void)tableView:(NSTableView *)tableView
draggingSession:(NSDraggingSession *)session
willBeginAtPoint:(NSPoint)screenPoint
forRowIndexes:(NSIndexSet *)rowIndexes;
- (NSDragOperation)tableView:(NSTableView *)tableView - (NSDragOperation)tableView:(NSTableView *)tableView
validateDrop:(id <NSDraggingInfo>)info validateDrop:(id <NSDraggingInfo>)info
proposedRow:(int)row proposedRow:(NSInteger)row
proposedDropOperation:(NSTableViewDropOperation)dropOperation; proposedDropOperation:(NSTableViewDropOperation)dropOperation;
- (BOOL)tableView:(NSTableView *)tableView - (BOOL)tableView:(NSTableView *)tableView
acceptDrop:(id <NSDraggingInfo>)info acceptDrop:(id <NSDraggingInfo>)info
row:(int)row row:(NSInteger)row
dropOperation:(NSTableViewDropOperation)dropOperation; dropOperation:(NSTableViewDropOperation)dropOperation;
// utility methods // utility methods

View File

@ -3,45 +3,43 @@
#import "Logging.h" #import "Logging.h"
NSString *CogDNDIndexType = @"org.cogx.cog.dnd-index";
NSString *CogUrlsPboardType = @"org.cogx.cog.url";
NSString *iTunesDropType = @"com.apple.tv.metadata";
@implementation DNDArrayController @implementation DNDArrayController
NSString *CogPlaylistItemType = @"org.cogx.cog.playlist-item"; - (void)awakeFromNib {
NSString *CogUrlsPboardType = @"COG_URLS_TYPE"; [super awakeFromNib];
// @"CorePasteboardFlavorType 0x6974756E" is the "itun" type representing an iTunes plist
NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
- (void)awakeFromNib
{
// register for drag and drop // register for drag and drop
[self.tableView registerForDraggedTypes:@[CogPlaylistItemType, CogUrlsPboardType, [self.tableView registerForDraggedTypes:@[CogDNDIndexType,
NSFilenamesPboardType, iTunesDropType]]; CogUrlsPboardType,
NSPasteboardTypeFileURL,
iTunesDropType]];
} }
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView - (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
pasteboardWriterForRow:(NSInteger)row pasteboardWriterForRow:(NSInteger)row {
{
NSPasteboardItem *item = [[NSPasteboardItem alloc] init]; NSPasteboardItem *item = [[NSPasteboardItem alloc] init];
[item setString:[@(row) stringValue] forType:CogPlaylistItemType]; [item setString:[@(row) stringValue] forType:CogDNDIndexType];
return item; return item;
} }
- (void)tableView:(NSTableView *)tableView - (void)tableView:(NSTableView *)tableView
draggingSession:(NSDraggingSession *)session draggingSession:(NSDraggingSession *)session
willBeginAtPoint:(NSPoint)screenPoint willBeginAtPoint:(NSPoint)screenPoint
forRowIndexes:(NSIndexSet *)rowIndexes forRowIndexes:(NSIndexSet *)rowIndexes {
{
DLog(@"Drag session started with indexes: %@", rowIndexes); DLog(@"Drag session started with indexes: %@", rowIndexes);
} }
- (NSDragOperation)tableView:(NSTableView*)tableView - (NSDragOperation)tableView:(NSTableView *)tableView
validateDrop:(id <NSDraggingInfo>)info validateDrop:(id <NSDraggingInfo>)info
proposedRow:(int)row proposedRow:(NSInteger)row
proposedDropOperation:(NSTableViewDropOperation)dropOperation proposedDropOperation:(NSTableViewDropOperation)dropOperation {
{
NSDragOperation dragOp = NSDragOperationCopy; NSDragOperation dragOp = NSDragOperationCopy;
if ([info draggingSource] == tableView) if ([info draggingSource] == tableView)
@ -56,29 +54,28 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
} }
- (BOOL)tableView:(NSTableView*)tableView - (BOOL)tableView:(NSTableView *)tableView
acceptDrop:(id <NSDraggingInfo>)info acceptDrop:(id <NSDraggingInfo>)info
row:(int)row row:(NSInteger)row
dropOperation:(NSTableViewDropOperation)dropOperation dropOperation:(NSTableViewDropOperation)dropOperation {
{
if (row < 0) { if (row < 0) {
row = 0; row = 0;
} }
NSArray<NSPasteboardItem *> *items = info.draggingPasteboard.pasteboardItems; NSArray<NSPasteboardItem *> *items = info.draggingPasteboard.pasteboardItems;
// if drag source is self, it's a move // if drag source is self, it's a move
if ([info draggingSource] == tableView || items == nil) { if ([info draggingSource] == tableView || items == nil) {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (NSPasteboardItem *item in items) { for (NSPasteboardItem *item in items) {
[indexSet addIndex:[[item stringForType:CogPlaylistItemType] intValue]]; [indexSet addIndex:(NSUInteger) [[item stringForType:CogDNDIndexType] intValue]];
} }
if ([indexSet count] > 0) { if ([indexSet count] > 0) {
DLog(@"INDEX SET ON DROP: %@", indexSet); DLog(@"INDEX SET ON DROP: %@", indexSet);
NSArray *selected = [[self arrangedObjects] objectsAtIndexes:indexSet]; NSArray *selected = [[self arrangedObjects] objectsAtIndexes:indexSet];
[self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:row]; [self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:(unsigned int) row];
[self setSelectedObjects:selected]; [self setSelectedObjects:selected];
DLog(@"ACCEPTING DROP!"); DLog(@"ACCEPTING DROP!");
return YES; return YES;
} }
@ -88,26 +85,25 @@ NSString *iTunesDropType = @"CorePasteboardFlavorType 0x6974756E";
} }
-(void) moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet - (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
toIndex:(unsigned int)insertIndex toIndex:(unsigned int)insertIndex {
{ NSArray *objects = [self arrangedObjects];
NSArray *objects = [self arrangedObjects]; NSUInteger index = [indexSet lastIndex];
NSUInteger index = [indexSet lastIndex];
int aboveInsertIndexCount = 0; NSUInteger aboveInsertIndexCount = 0;
id object; id object;
int removeIndex; NSUInteger removeIndex;
while (NSNotFound != index) { while (NSNotFound != index) {
if (index >= insertIndex) { if (index >= insertIndex) {
removeIndex = (int)(index + aboveInsertIndexCount); removeIndex = index + aboveInsertIndexCount;
aboveInsertIndexCount += 1; aboveInsertIndexCount += 1;
} else { } else {
removeIndex = (int)index; removeIndex = index;
insertIndex -= 1; insertIndex -= 1;
} }
object = [objects objectAtIndex:removeIndex]; object = objects[removeIndex];
[self removeObjectAtArrangedObjectIndex:removeIndex]; [self removeObjectAtArrangedObjectIndex:removeIndex];
[self insertObject:object atArrangedObjectIndex:insertIndex]; [self insertObject:object atArrangedObjectIndex:insertIndex];

View File

@ -15,55 +15,48 @@
@class SpotlightWindowController; @class SpotlightWindowController;
@class PlaybackController; @class PlaybackController;
typedef enum { typedef NS_ENUM(NSInteger, RepeatMode) {
RepeatNone = 0, RepeatModeNoRepeat = 0,
RepeatOne, RepeatModeRepeatOne,
RepeatAlbum, RepeatModeRepeatAlbum,
RepeatAll RepeatModeRepeatAll
} RepeatMode; };
static inline BOOL IsRepeatOneSet() static inline BOOL IsRepeatOneSet() {
{ return [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"] == RepeatModeRepeatOne;
return [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"] == RepeatOne;
} }
typedef enum { typedef enum { ShuffleOff = 0, ShuffleAlbums, ShuffleAll } ShuffleMode;
ShuffleOff = 0,
ShuffleAlbums,
ShuffleAll
} ShuffleMode;
typedef enum { typedef NS_ENUM(NSInteger, URLOrigin) {
URLOriginInternal = 0, URLOriginInternal = 0,
URLOriginExternal, URLOriginExternal
} URLOrigin; };
@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; NSUndoManager *undoManager;
} }
@property(nonatomic, retain) PlaylistEntry *currentEntry; @property(nonatomic, retain) PlaylistEntry *currentEntry;
@property(retain) NSString *totalTime; @property(retain) NSString *totalTime;
//Private Methods // Private Methods
- (void)updateTotalTime; - (void)updateTotalTime;
- (void)updatePlaylistIndexes; - (void)updatePlaylistIndexes;
- (IBAction)stopAfterCurrent:(id)sender; - (IBAction)stopAfterCurrent:(id)sender;
// PUBLIC METHODS
//PUBLIC METHODS
- (void)setShuffle:(ShuffleMode)s; - (void)setShuffle:(ShuffleMode)s;
- (ShuffleMode)shuffle; - (ShuffleMode)shuffle;
- (void)setRepeat:(RepeatMode)r; - (void)setRepeat:(RepeatMode)r;
@ -95,7 +88,7 @@ typedef enum {
- (IBAction)searchByArtist:(id)sender; - (IBAction)searchByArtist:(id)sender;
- (IBAction)searchByAlbum:(id)sender; - (IBAction)searchByAlbum:(id)sender;
//FUN PLAYLIST MANAGEMENT STUFF! // FUN PLAYLIST MANAGEMENT STUFF!
- (BOOL)next; - (BOOL)next;
- (BOOL)prev; - (BOOL)prev;
@ -107,12 +100,15 @@ typedef enum {
- (PlaylistEntry *)entryAtIndex:(int)i; - (PlaylistEntry *)entryAtIndex:(int)i;
// Event inlets: // Event inlets:
- (void)willInsertURLs:(NSArray*)urls origin:(URLOrigin)origin; - (void)willInsertURLs:(NSArray *)urls origin:(URLOrigin)origin;
- (void)didInsertURLs:(NSArray*)urls origin:(URLOrigin)origin; - (void)didInsertURLs:(NSArray *)urls origin:(URLOrigin)origin;
// queue methods // queue methods
- (IBAction)toggleQueued:(id)sender; - (IBAction)toggleQueued:(id)sender;
- (IBAction)emptyQueueList:(id)sender; - (IBAction)emptyQueueList:(id)sender;
- (NSMutableArray *)queueList; - (NSMutableArray *)queueList;
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
toIndex:(unsigned int)insertIndex;
@end @end

File diff suppressed because it is too large Load Diff

View File

@ -13,11 +13,11 @@
#import "PlaylistLoader.h" #import "PlaylistLoader.h"
@interface PlaylistView : NSTableView { @interface PlaylistView : NSTableView {
IBOutlet PlaybackController *playbackController; IBOutlet PlaybackController *playbackController;
IBOutlet PlaylistController *playlistController; IBOutlet PlaylistController *playlistController;
IBOutlet PlaylistLoader *playlistLoader; IBOutlet PlaylistLoader *playlistLoader;
NSMenu *headerContextMenu; NSMenu *headerContextMenu;
} }
- (IBAction)toggleColumn:(id)sender; - (IBAction)toggleColumn:(id)sender;

View File

@ -7,13 +7,11 @@
// //
#import "PlaylistView.h" #import "PlaylistView.h"
#import "PlaybackController.h"
#import "PlaylistController.h"
#import "IndexFormatter.h"
#import "SecondsFormatter.h"
#import "BlankZeroFormatter.h" #import "BlankZeroFormatter.h"
#import "IndexFormatter.h"
#import "PlaylistEntry.h" #import "PlaylistEntry.h"
#import "SecondsFormatter.h"
#import "CogAudio/Status.h" #import "CogAudio/Status.h"
@ -21,377 +19,353 @@
@implementation PlaylistView @implementation PlaylistView
- (void)awakeFromNib - (void)awakeFromNib {
{
[[self menu] setAutoenablesItems:NO]; [[self menu] setAutoenablesItems:NO];
// Configure bindings to scale font size and row height // Configure bindings to scale font size and row height
NSControlSize s = NSControlSizeSmall; NSControlSize s = NSControlSizeSmall;
NSFont *f = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:s]]; NSFont *f = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:s]];
// NSFont *bf = [[NSFontManager sharedFontManager] convertFont:f toHaveTrait:NSBoldFontMask]; // 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] setControlSize:s];
[[col dataCell] setFont:f]; [[col dataCell] setFont:f];
} }
//Set up formatters // Set up formatters
NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init]; NSFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
[[[self tableColumnWithIdentifier:@"length"] dataCell] setFormatter:secondsFormatter]; [[[self tableColumnWithIdentifier:@"length"] dataCell] setFormatter:secondsFormatter];
NSFormatter *indexFormatter = [[IndexFormatter alloc] init]; NSFormatter *indexFormatter = [[IndexFormatter alloc] init];
[[[self tableColumnWithIdentifier:@"index"] dataCell] setFormatter:indexFormatter]; [[[self tableColumnWithIdentifier:@"index"] dataCell] setFormatter:indexFormatter];
NSFormatter *blankZeroFormatter = [[BlankZeroFormatter alloc] init]; NSFormatter *blankZeroFormatter = [[BlankZeroFormatter alloc] init];
[[[self tableColumnWithIdentifier:@"track"] dataCell] setFormatter:blankZeroFormatter]; [[[self tableColumnWithIdentifier:@"track"] dataCell] setFormatter:blankZeroFormatter];
[[[self tableColumnWithIdentifier:@"year"] dataCell] setFormatter:blankZeroFormatter]; [[[self tableColumnWithIdentifier:@"year"] dataCell] setFormatter:blankZeroFormatter];
//end setting up formatters // end setting up formatters
[self setVerticalMotionCanBeginDrag:YES]; [self setVerticalMotionCanBeginDrag:YES];
//Set up header context menu // Set up header context menu
headerContextMenu = [[NSMenu alloc] initWithTitle:@"Playlist Header Context Menu"]; headerContextMenu = [[NSMenu alloc] initWithTitle:@"Playlist Header Context Menu"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"identifier" ascending:YES]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"identifier"
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; ascending:YES];
NSArray *sortDescriptors = @[sortDescriptor];
int visibleTableColumns = 0; int visibleTableColumns = 0;
int menuIndex = 0; int menuIndex = 0;
for (NSTableColumn *col in [[self tableColumns] sortedArrayUsingDescriptors: sortDescriptors]) for (NSTableColumn *col in [[self tableColumns] sortedArrayUsingDescriptors:sortDescriptors]) {
{
NSString *title; NSString *title;
if ([[col identifier] isEqualToString:@"status"]) if ([[col identifier] isEqualToString:@"status"]) {
{
title = @"Status"; title = @"Status";
} } else if ([[col identifier] isEqualToString:@"index"]) {
else if ([[col identifier] isEqualToString:@"index"])
{
title = @"Index"; title = @"Index";
} } else {
else
{
title = [[col headerCell] title]; 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 setTarget:self];
[contextMenuItem setRepresentedObject:col]; [contextMenuItem setRepresentedObject:col];
[contextMenuItem setState:([col isHidden] ? NSOffState : NSOnState)]; [contextMenuItem setState:([col isHidden] ? NSControlStateValueOff : NSControlStateValueOn)];
visibleTableColumns += ![col isHidden]; visibleTableColumns += ![col isHidden];
menuIndex++; menuIndex++;
} }
if (visibleTableColumns == 0) { if (visibleTableColumns == 0) {
for (NSTableColumn *col in [self tableColumns]) { for (NSTableColumn *col in [self tableColumns]) {
[col setHidden:NO]; [col setHidden:NO];
} }
} }
[[self headerView] setMenu:headerContextMenu]; [[self headerView] setMenu:headerContextMenu];
} }
- (IBAction)toggleColumn:(id)sender {
- (IBAction)toggleColumn:(id)sender
{
id tc = [sender representedObject]; id tc = [sender representedObject];
if ([sender state] == NSOffState) if ([sender state] == NSControlStateValueOff) {
{ [sender setState:NSControlStateValueOn];
[sender setState:NSOnState];
[tc setHidden:NO];
[tc setHidden: NO]; } else {
} [sender setState:NSControlStateValueOff];
else
{ [tc setHidden:YES];
[sender setState:NSOffState];
[tc setHidden: YES];
} }
} }
- (BOOL)acceptsFirstResponder - (BOOL)acceptsFirstResponder {
{
return YES; return YES;
} }
- (BOOL)resignFirstResponder - (BOOL)resignFirstResponder {
{
return YES; return YES;
} }
- (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent - (BOOL)acceptsFirstMouse:(NSEvent *)mouseDownEvent {
{
return NO; return NO;
} }
- (void)mouseDown:(NSEvent *)e - (void)mouseDown:(NSEvent *)e {
{
[super mouseDown: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]; [playbackController play:self];
} }
} }
// enables right-click selection for "Show in Finder" contextual menu // enables right-click selection for "Show in Finder" contextual menu
-(NSMenu*)menuForEvent:(NSEvent*)event - (NSMenu *)menuForEvent:(NSEvent *)event {
{ // Find which row is under the cursor
//Find which row is under the cursor
[[self window] makeFirstResponder:self]; [[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]; NSInteger iRow = [self rowAtPoint:menuPoint];
NSMenu* tableViewMenu = [self menu]; NSMenu *tableViewMenu = [self menu];
/* Update the table selection before showing menu /* Update the table selection before showing menu
Preserves the selection if the row under the mouse is selected (to allow for 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 */ 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 (!currentRowIsSelected) {
if (iRow == -1) if (iRow == -1) {
{
[self deselectAll:self]; [self deselectAll:self];
} } else {
else [self selectRowIndexes:[NSIndexSet indexSetWithIndex:(NSUInteger) iRow] byExtendingSelection:NO];
{
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO];
} }
} }
if ([self numberOfSelectedRows] <=0) if ([self numberOfSelectedRows] <= 0) {
{ // No rows are selected, so the table should be displayed with all items disabled
//No rows are selected, so the table should be displayed with all items disabled
int i; int i;
for (i=0;i<[tableViewMenu numberOfItems];i++) { for (i = 0; i < [tableViewMenu numberOfItems]; i++) {
[[tableViewMenu itemAtIndex:i] setEnabled:NO]; [[tableViewMenu itemAtIndex:i] setEnabled:NO];
} }
} }
return tableViewMenu; return tableViewMenu;
} }
- (void)keyDown:(NSEvent *)e - (void)keyDown:(NSEvent *)e {
{ unsigned int modifiers =
unsigned int modifiers = [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption); [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift |
NSString *characters = [e characters]; NSEventModifierFlagControl | NSEventModifierFlagOption);
unichar c; NSString *characters = [e characters];
unichar c;
if ([characters length] != 1)
{ if ([characters length] != 1) {
[super keyDown:e]; [super keyDown:e];
return; return;
} }
c = [characters characterAtIndex:0]; 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]; [playlistController remove:self];
} } else if (modifiers == 0 && c == ' ') {
else if (modifiers == 0 && c == ' ')
{
[playbackController playPauseResume:self]; [playbackController playPauseResume:self];
} } else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) {
else if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter))
{
[playbackController play:self]; [playbackController play:self];
} } else if (modifiers == 0 && c == NSLeftArrowFunctionKey) {
else if (modifiers == 0 && c == NSLeftArrowFunctionKey)
{
[playbackController eventSeekBackward:self]; [playbackController eventSeekBackward:self];
} } else if (modifiers == 0 && c == NSRightArrowFunctionKey) {
else if (modifiers == 0 && c == NSRightArrowFunctionKey)
{
[playbackController eventSeekForward:self]; [playbackController eventSeekForward:self];
} }
// Escape // Escape
else if (modifiers == 0 && c == 0x1b) else if (modifiers == 0 && c == 0x1b) {
{
[playlistController clearFilterPredicate:self]; [playlistController clearFilterPredicate:self];
} } else {
else
{
[super keyDown:e]; [super keyDown:e];
} }
} }
- (IBAction)scrollToCurrentEntry:(id)sender - (IBAction)scrollToCurrentEntry:(id)sender {
{
[self scrollRowToVisible:[[playlistController currentEntry] index]]; [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]; [[playlistController undoManager] undo];
} }
- (IBAction)redo:(id)sender - (IBAction)redo:(id)sender {
{
[[playlistController undoManager] redo]; [[playlistController undoManager] redo];
} }
- (IBAction)copy:(id)sender - (IBAction)copy:(id)sender {
{
NSPasteboard *pboard = [NSPasteboard generalPasteboard]; NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard clearContents]; [pboard clearContents];
NSMutableArray *selectedURLs = [[NSMutableArray alloc] init]; NSArray *entries =
[[playlistController content] objectsAtIndexes:[playlistController selectionIndexes]];
for (PlaylistEntry *pe in [[playlistController content] objectsAtIndexes:[playlistController selectionIndexes]]) NSUInteger capacity = [entries count];
{ NSMutableArray *selectedURLs = [NSMutableArray arrayWithCapacity:capacity];
for (PlaylistEntry *pe in entries) {
[selectedURLs addObject:[pe URL]]; [selectedURLs addObject:[pe URL]];
} }
[pboard setData:[NSArchiver archivedDataWithRootObject:selectedURLs] forType:CogUrlsPboardType]; NSError *error;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:selectedURLs
NSMutableDictionary * tracks = [[NSMutableDictionary alloc] init]; requiringSecureCoding:YES
error:&error];
if (!data) {
DLog(@"Error: %@", error);
}
[pboard setData:data forType:CogUrlsPboardType];
NSMutableDictionary *tracks = [NSMutableDictionary dictionaryWithCapacity:capacity];
unsigned long i = 0; unsigned long i = 0;
for (NSURL *url in selectedURLs) for (NSURL *url in selectedURLs) {
{ tracks[[NSString stringWithFormat:@"%lu", i++]] = @{@"Location": [url absoluteString]};
NSMutableDictionary * track = [NSMutableDictionary dictionaryWithObjectsAndKeys:[url absoluteString], @"Location", nil];
[tracks setObject:track forKey:[NSString stringWithFormat:@"%lu", i]];
++i;
} }
NSMutableDictionary * itunesPlist = [NSMutableDictionary dictionaryWithObjectsAndKeys:tracks, @"Tracks", nil]; NSDictionary *itunesPlist = @{@"Tracks": tracks};
[pboard setPropertyList:itunesPlist forType:iTunesDropType]; [pboard setPropertyList:itunesPlist forType:iTunesDropType];
NSMutableArray *filePaths = [[NSMutableArray alloc] init]; NSMutableArray *filePaths = [NSMutableArray array];
for (NSURL *url in selectedURLs) for (NSURL *url in selectedURLs) {
{ if ([url isFileURL]) {
if ([url isFileURL]) [filePaths addObject:url];
[filePaths addObject:[url path]]; }
}
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]; [self copy:sender];
[playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]]; [playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]];
if ([playlistController shuffle] != ShuffleOff) if ([playlistController shuffle] != ShuffleOff) [playlistController resetShuffleList];
[playlistController resetShuffleList];
} }
- (IBAction)paste:(id)sender - (IBAction)paste:(id)sender {
{
// Determine the type of object that was dropped // 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]; NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; NSPasteboardType bestType = [pboard availableTypeFromArray:supportedTypes];
DLog(@"All types:");
NSMutableArray *acceptedURLs = [[NSMutableArray alloc] init]; 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 // Get files from an file drawer drop
if ([bestType isEqualToString:CogUrlsPboardType]) { if ([bestType isEqualToString:CogUrlsPboardType]) {
NSArray *urls = [NSUnarchiver unarchiveObjectWithData:[pboard dataForType:CogUrlsPboardType]]; NSError *error;
DLog(@"URLS: %@", urls); 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]; //[playlistLoader insertURLs: urls atIndex:row sort:YES];
[acceptedURLs addObjectsFromArray:urls]; [acceptedURLs addObjectsFromArray:urls];
} }
// Get files from a normal file drop (such as from Finder) // Get files from a normal file drop (such as from Finder)
if ([bestType isEqualToString:NSFilenamesPboardType]) { if ([bestType isEqualToString:NSPasteboardTypeFileURL]) {
NSMutableArray *urls = [[NSMutableArray alloc] init]; NSMutableArray *urls = [[NSMutableArray alloc] init];
for (NSString *file in [pboard propertyListForType:NSFilenamesPboardType]) for (NSString *file in [pboard propertyListForType:NSPasteboardTypeFileURL]) {
{
[urls addObject:[NSURL fileURLWithPath:file]]; [urls addObject:[NSURL fileURLWithPath:file]];
} }
//[playlistLoader insertURLs:urls atIndex:row sort:YES]; //[playlistLoader insertURLs:urls atIndex:row sort:YES];
[acceptedURLs addObjectsFromArray:urls]; [acceptedURLs addObjectsFromArray:urls];
} }
// Get files from an iTunes drop // Get files from an iTunes drop
if ([bestType isEqualToString:iTunesDropType]) { if ([bestType isEqualToString:iTunesDropType]) {
NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType]; NSDictionary *iTunesDict = [pboard propertyListForType:iTunesDropType];
NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"]; NSDictionary *tracks = [iTunesDict valueForKey:@"Tracks"];
// Convert the iTunes URLs to URLs....MWAHAHAH! // Convert the iTunes URLs to URLs....MWAHAHAH!
NSMutableArray *urls = [[NSMutableArray alloc] init]; NSMutableArray *urls = [[NSMutableArray alloc] init];
for (NSDictionary *trackInfo in [tracks allValues]) { for (NSDictionary *trackInfo in [tracks allValues]) {
[urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]]; [urls addObject:[NSURL URLWithString:[trackInfo valueForKey:@"Location"]]];
} }
//[playlistLoader insertURLs:urls atIndex:row sort:YES]; //[playlistLoader insertURLs:urls atIndex:row sort:YES];
[acceptedURLs addObjectsFromArray:urls]; [acceptedURLs addObjectsFromArray:urls];
} }
if ([acceptedURLs count]) if ([acceptedURLs count]) {
{
NSUInteger row = [[playlistController content] count]; NSUInteger row = [[playlistController content] count];
[playlistController willInsertURLs:acceptedURLs origin:URLOriginInternal]; [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]; [playlistLoader didInsertURLs:entries origin:URLOriginInternal];
if ([playlistController shuffle] != ShuffleOff) if ([playlistController shuffle] != ShuffleOff) [playlistController resetShuffleList];
[playlistController resetShuffleList];
} }
} }
- (IBAction)delete:(id)sender - (IBAction)delete:(id)sender {
{
[playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]]; [playlistController removeObjectsAtArrangedObjectIndexes:[playlistController selectionIndexes]];
} }
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
-(BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
{
SEL action = [anItem action]; SEL action = [anItem action];
if (action == @selector(undo:)) if (action == @selector(undo:)) {
{ return [[playlistController undoManager] canUndo];
if ([[playlistController undoManager] canUndo])
return YES;
else
return NO;
} }
if (action == @selector(redo:)) if (action == @selector(redo:)) {
{ return [[playlistController undoManager] canRedo];
if ([[playlistController undoManager] canRedo])
return YES;
else
return NO;
} }
if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:)) if (action == @selector(cut:) || action == @selector(copy:) || action == @selector(delete:)) {
{ return [[playlistController selectionIndexes] count] != 0;
if ([[playlistController selectionIndexes] count] == 0)
return NO;
else
return YES;
} }
if (action == @selector(paste:)) if (action == @selector(paste:)) {
{
NSPasteboard *pboard = [NSPasteboard generalPasteboard]; NSPasteboard *pboard = [NSPasteboard generalPasteboard];
NSArray *supportedTypes = [NSArray arrayWithObjects:CogUrlsPboardType, NSFilenamesPboardType, iTunesDropType, nil]; NSArray *supportedTypes = @[CogUrlsPboardType, NSPasteboardTypeFileURL, iTunesDropType];
NSString *bestType = [pboard availableTypeFromArray:supportedTypes]; NSString *bestType = [pboard availableTypeFromArray:supportedTypes];
if (bestType != nil) return bestType != nil;
return YES;
else
return NO;
} }
if (action == @selector(scrollToCurrentEntry:) && (([playbackController playbackStatus] == kCogStatusStopped) || ([playbackController playbackStatus] == kCogStatusStopping))) if (action == @selector(scrollToCurrentEntry:) &&
(([playbackController playbackStatus] == kCogStatusStopped) ||
([playbackController playbackStatus] == kCogStatusStopping)))
return NO; return NO;
return [super validateUserInterfaceItem:anItem]; return [super validateUserInterfaceItem:anItem];
} }
@ -405,5 +379,4 @@
} }
#endif #endif
@end @end

View File

@ -8,9 +8,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface XmlContainer : NSObject { @interface XmlContainer : NSObject
}
+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename; + (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename;

View File

@ -8,101 +8,109 @@
#import "XmlContainer.h" #import "XmlContainer.h"
#import "PlaylistEntry.h"
#import "Logging.h" #import "Logging.h"
@implementation XmlContainer @implementation XmlContainer
+ (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename + (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename {
{ NSRange protocolRange = [path rangeOfString:@"://"];
NSRange protocolRange = [path rangeOfString:@"://"]; if (protocolRange.location != NSNotFound) {
if (protocolRange.location != NSNotFound) return [NSURL URLWithString:path];
{ }
return [NSURL URLWithString:path];
}
NSMutableString *unixPath = [path mutableCopy]; NSMutableString *unixPath = [path mutableCopy];
//Get the fragment //Get the fragment
NSString *fragment = @""; NSString *fragment = @"";
NSScanner *scanner = [NSScanner scannerWithString:unixPath]; NSScanner *scanner = [NSScanner scannerWithString:unixPath];
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"#1234567890"]; NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"#1234567890"];
while (![scanner isAtEnd]) { while (![scanner isAtEnd]) {
NSString *possibleFragment; NSString *possibleFragment;
[scanner scanUpToString:@"#" intoString:nil]; [scanner scanUpToString:@"#" intoString:nil];
if ([scanner scanCharactersFromSet:characterSet intoString:&possibleFragment] && [scanner isAtEnd]) if ([scanner scanCharactersFromSet:characterSet intoString:&possibleFragment] && [scanner isAtEnd]) {
{ fragment = possibleFragment;
fragment = possibleFragment; [unixPath deleteCharactersInRange:NSMakeRange([scanner scanLocation] - [possibleFragment length], [possibleFragment length])];
[unixPath deleteCharactersInRange:NSMakeRange([scanner scanLocation] - [possibleFragment length], [possibleFragment length])]; break;
break; }
} }
} DLog(@"Fragment: %@", fragment);
DLog(@"Fragment: %@", fragment);
if (![unixPath hasPrefix:@"/"]) { if (![unixPath hasPrefix:@"/"]) {
//Only relative paths would have windows backslashes. //Only relative paths would have windows backslashes.
[unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])]; [unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])];
NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"];
[unixPath insertString:basePath atIndex:0]; 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; //Append the fragment
NSURL *url = [NSURL URLWithString:[[[NSURL fileURLWithPath:unixPath] absoluteString] stringByAppendingString:fragment]];
return url;
} }
+ (NSDictionary *)entriesForContainerURL:(NSURL *)url + (NSDictionary *)entriesForContainerURL:(NSURL *)url {
{ if (![url isFileURL])
if (![url isFileURL]) return nil;
return [NSDictionary dictionary];
NSError *nserr; NSError *error;
NSString *error; NSString *filename = [url path];
NSString *filename = [url path]; NSString *contents = [NSString stringWithContentsOfFile:filename
encoding:NSUTF8StringEncoding
NSString * contents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&nserr]; error:&error];
if (!contents) {
NSData* plistData = [contents dataUsingEncoding:NSUTF8StringEncoding]; ALog(@"Error: %@", error);
NSPropertyListFormat format;
id plist = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(!plist){
ALog(@"Error: %@",error);
return nil; 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 isArray = [plist isKindOfClass:[NSArray class]];
BOOL isDict = [plist isKindOfClass:[NSDictionary 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"]; if (!isDict && !isArray) return nil;
NSMutableArray *entries = [NSMutableArray array]; NSArray *items;
NSDictionary *albumArt;
for (NSDictionary *entry in items) NSArray *queueList;
{ if (isArray) {
NSMutableDictionary * preparedEntry = [NSMutableDictionary dictionaryWithDictionary:entry]; items = (NSArray *) plist;
albumArt = nil;
[preparedEntry setObject:[self urlForPath:[preparedEntry objectForKey:@"URL"] relativeTo:filename] forKey:@"URL"]; queueList = [NSArray array];
} else {
if (albumArt && [preparedEntry objectForKey:@"albumArt"]) NSDictionary *dict = (NSDictionary *) plist;
[preparedEntry setObject:[albumArt objectForKey:[preparedEntry objectForKey:@"albumArt"]] forKey:@"albumArt"]; 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]]; [entries addObject:[NSDictionary dictionaryWithDictionary:preparedEntry]];
} }
return [NSDictionary dictionaryWithObjectsAndKeys:entries, @"entries", queueList, @"queue", nil]; return @{@"entries": entries, @"queue": queueList};
} }
@end @end

View File

@ -10,9 +10,7 @@
#import "PlaylistController.h" #import "PlaylistController.h"
@interface RepeatModeTransformer : NSValueTransformer { @interface RepeatModeTransformer : NSValueTransformer
RepeatMode repeatMode;
}
- (id)initWithMode:(RepeatMode) r; - (id)initWithMode:(RepeatMode) r;

View File

@ -7,55 +7,47 @@
// //
#import "RepeatTransformers.h" #import "RepeatTransformers.h"
#import "PlaylistController.h"
#import "Logging.h" #import "Logging.h"
@implementation RepeatModeTransformer @implementation RepeatModeTransformer {
RepeatMode repeatMode;
}
+ (Class)transformedValueClass { return [NSNumber class]; } + (Class)transformedValueClass { return [NSNumber class]; }
+ (BOOL)allowsReverseTransformation { return YES; } + (BOOL)allowsReverseTransformation { return YES; }
- (id)initWithMode:(RepeatMode) r - (id)initWithMode:(RepeatMode)r {
{ self = [super init];
self = [super init]; if (self) {
if (self) repeatMode = r;
{ }
repeatMode = r;
} return self;
return self;
} }
// Convert from RepeatMode to BOOL // Convert from RepeatMode to BOOL
- (id)transformedValue:(id)value { - (id)transformedValue:(id)value {
DLog(@"Transforming value: %@", value); DLog(@"Transforming value: %@", value);
if (value == nil) return nil;
RepeatMode mode = (RepeatMode) [value integerValue];
if (repeatMode == mode) {
return [NSNumber numberWithBool:YES];
}
return [NSNumber numberWithBool:NO]; if (value == nil) return nil;
RepeatMode mode = (RepeatMode) [value integerValue];
return @(repeatMode == mode);
} }
- (id)reverseTransformedValue:(id)value { - (id)reverseTransformedValue:(id)value {
if (value == nil) return nil; if (value == nil) return nil;
BOOL enabled = [value boolValue]; BOOL enabled = [value boolValue];
if (enabled) { if (enabled) {
return [NSNumber numberWithInt:repeatMode]; return @(repeatMode);
} } else if (repeatMode == RepeatModeNoRepeat) {
else if(repeatMode == RepeatNone) { return @(RepeatModeRepeatAll);
return [NSNumber numberWithInt:RepeatAll]; } else {
} return @(RepeatModeNoRepeat);
else { }
return [NSNumber numberWithInt:RepeatNone];
}
} }
@end @end
@ -67,26 +59,26 @@
// Convert from string to RepeatMode // Convert from string to RepeatMode
- (id)transformedValue:(id)value { - (id)transformedValue:(id)value {
DLog(@"Transforming value: %@", value); DLog(@"Transforming value: %@", value);
if (value == nil) return nil; if (value == nil) return nil;
RepeatMode mode = (RepeatMode) [value integerValue]; 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"];
}
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 @end