Reintroducing App Sandbox, and more

- Implemented App Sandboxing in a more friendly manner.
- All sandboxed paths will need to be set in Preferences. Set as loose
  a path as you want. The shortest path will be preferred.
- Removed Last.fm client support, as it was non-functional by now,
  unfortunately. Maybe something better can come in the future.
- Added support for insecure SSL to the HTTP/S reader, in case anyone
  needs streams which are "protected" by self-signed or expired
  certificates, without having to futz around by adding certificates to
  the system settings, especially for expired certificates that can't
  otherwise be dodged this way.

If you want to import your old playlists to the new version, copy the
contents of `~/Library/Application Support/Cog` to the alternate sandbox
path: `~/Library/Containers/org.cogx.cog/Data/Library/Application `...
...continued...`Support/Cog`. The preferences file will migrate to the
new version automatically.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-20 03:35:29 -07:00
parent 8009d911c2
commit c23bece62c
55 changed files with 767 additions and 676 deletions

View File

@ -12,6 +12,7 @@
#import "PlaylistLoader.h" #import "PlaylistLoader.h"
#import "PlaylistView.h" #import "PlaylistView.h"
#import "SQLiteStore.h" #import "SQLiteStore.h"
#import "SandboxBroker.h"
#import "SpotlightWindowController.h" #import "SpotlightWindowController.h"
#import "StringToURLTransformer.h" #import "StringToURLTransformer.h"
#import <CogAudio/Status.h> #import <CogAudio/Status.h>
@ -189,6 +190,11 @@ BOOL kAppControllerShuttingDown = NO;
} }
} }
SandboxBroker *sandboxBroker = [SandboxBroker sharedSandboxBroker];
if(!sandboxBroker) {
ALog(@"Sandbox broker init failed.");
}
[[playlistController undoManager] enableUndoRegistration]; [[playlistController undoManager] enableUndoRegistration];
int lastStatus = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"]; int lastStatus = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"];
@ -434,6 +440,9 @@ BOOL kAppControllerShuttingDown = NO;
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error]; [[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
DLog(@"Shutting down sandbox broker");
[[SandboxBroker sharedSandboxBroker] shutdown];
DLog(@"Saving expanded nodes: %@", [expandedNodes description]); DLog(@"Saving expanded nodes: %@", [expandedNodes description]);
[[NSUserDefaults standardUserDefaults] setValue:[expandedNodes allObjects] forKey:@"fileTreeViewExpandedNodes"]; [[NSUserDefaults standardUserDefaults] setValue:[expandedNodes allObjects] forKey:@"fileTreeViewExpandedNodes"];

View File

@ -3,7 +3,6 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "AppController.h" #import "AppController.h"
#import "AudioScrobbler.h"
#import "CogAudio/AudioPlayer.h" #import "CogAudio/AudioPlayer.h"
#import "CogAudio/Status.h" #import "CogAudio/Status.h"
#import "TrackingSlider.h" #import "TrackingSlider.h"
@ -15,6 +14,8 @@
#import "EqualizerWindowController.h" #import "EqualizerWindowController.h"
#import "PlaylistEntry.h"
#define DEFAULT_VOLUME_DOWN 5 #define DEFAULT_VOLUME_DOWN 5
#define DEFAULT_VOLUME_UP DEFAULT_VOLUME_DOWN #define DEFAULT_VOLUME_UP DEFAULT_VOLUME_DOWN

View File

@ -11,8 +11,6 @@
#import "PlaybackController.h" #import "PlaybackController.h"
@class AudioScrobbler;
@interface PlaybackEventController @interface PlaybackEventController
: NSObject <NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate> { : NSObject <NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate> {
IBOutlet PlaybackController *playbackController; IBOutlet PlaybackController *playbackController;

View File

@ -7,8 +7,6 @@
#import "PlaybackEventController.h" #import "PlaybackEventController.h"
#import "AudioScrobbler.h"
#import "PlaylistEntry.h" #import "PlaylistEntry.h"
NSString *TrackNotification = @"com.apple.iTunes.playerInfo"; NSString *TrackNotification = @"com.apple.iTunes.playerInfo";
@ -27,8 +25,6 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
TrackStopped }; TrackStopped };
@implementation PlaybackEventController { @implementation PlaybackEventController {
AudioScrobbler *scrobbler;
NSOperationQueue *queue; NSOperationQueue *queue;
PlaylistEntry *entry; PlaylistEntry *entry;
@ -38,8 +34,6 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
- (void)initDefaults { - (void)initDefaults {
NSDictionary *defaultsDictionary = @{ NSDictionary *defaultsDictionary = @{
@"enableAudioScrobbler": @YES,
@"automaticallyLaunchLastFM": @NO,
@"notifications.enable": @YES, @"notifications.enable": @YES,
@"notifications.itunes-style": @YES, @"notifications.itunes-style": @YES,
@"notifications.show-album-art": @YES @"notifications.show-album-art": @YES
@ -85,7 +79,6 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
queue = [[NSOperationQueue alloc] init]; queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1]; [queue setMaxConcurrentOperationCount:1];
scrobbler = [[AudioScrobbler alloc] init];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
entry = nil; entry = nil;
@ -161,11 +154,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults boolForKey:@"notifications.enable"]) { if([defaults boolForKey:@"notifications.enable"]) {
if([defaults boolForKey:@"enableAudioScrobbler"]) {
[scrobbler start:pe];
if([AudioScrobbler isRunning]) return;
}
if(@available(macOS 10.14, *)) { if(@available(macOS 10.14, *)) {
if(didGainUN) { if(didGainUN) {
UNUserNotificationCenter *center = UNUserNotificationCenter *center =
@ -300,9 +288,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
object:nil object:nil
userInfo:[self fillNotificationDictionary:entry status:TrackPaused] userInfo:[self fillNotificationDictionary:entry status:TrackPaused]
deliverImmediately:YES]; deliverImmediately:YES];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
[scrobbler pause];
}
} }
- (void)performPlaybackDidResumeActions { - (void)performPlaybackDidResumeActions {
@ -311,9 +296,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
object:nil object:nil
userInfo:[self fillNotificationDictionary:entry status:TrackPlaying] userInfo:[self fillNotificationDictionary:entry status:TrackPlaying]
deliverImmediately:YES]; deliverImmediately:YES];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
[scrobbler resume];
}
} }
- (void)performPlaybackDidStopActions { - (void)performPlaybackDidStopActions {
@ -323,9 +305,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
userInfo:[self fillNotificationDictionary:entry status:TrackStopped] userInfo:[self fillNotificationDictionary:entry status:TrackStopped]
deliverImmediately:YES]; deliverImmediately:YES];
entry = nil; entry = nil;
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
[scrobbler stop];
}
} }
- (void)awakeFromNib { - (void)awakeFromNib {

View File

@ -1,45 +0,0 @@
/*
* $Id: AudioScrobbler.h 238 2007-01-26 22:55:20Z stephen_booth $
*
* Copyright (C) 2006 - 2007 Stephen F. Booth <me@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import <Cocoa/Cocoa.h>
#import <mach/mach.h>
@class PlaylistEntry;
@interface AudioScrobbler : NSObject {
NSString *_pluginID;
NSMutableArray *_queue;
BOOL _audioScrobblerThreadCompleted;
BOOL _keepProcessingAudioScrobblerCommands;
semaphore_t _semaphore;
}
+ (BOOL)isRunning;
- (void)start:(PlaylistEntry *)pe;
- (void)stop;
- (void)pause;
- (void)resume;
- (void)shutdown;
@end

View File

@ -1,259 +0,0 @@
/*
* $Id: AudioScrobbler.m 238 2007-01-26 22:55:20Z stephen_booth $
*
* Copyright (C) 2006 - 2007 Stephen F. Booth <me@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import "AudioScrobbler.h"
#import "AudioScrobblerClient.h"
#import "PlaylistEntry.h"
#import "Logging.h"
// ========================================
// Symbolic Constants
// ========================================
NSString *const AudioScrobblerRunLoopMode = @"org.cogx.Cog.AudioScrobbler.RunLoopMode";
// ========================================
// Helpers
// ========================================
static NSString *
escapeForLastFM(NSString *string) {
NSMutableString *result = [string mutableCopy];
[result replaceOccurrencesOfString:@"&"
withString:@"&&"
options:NSLiteralSearch
range:NSMakeRange(0, [result length])];
return (nil == result ? @"" : result);
}
@interface AudioScrobbler (Private)
- (NSMutableArray *)queue;
- (NSString *)pluginID;
- (void)sendCommand:(NSString *)command;
- (BOOL)keepProcessingAudioScrobblerCommands;
- (void)setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands;
- (BOOL)audioScrobblerThreadCompleted;
- (void)setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted;
- (semaphore_t)semaphore;
- (void)processAudioScrobblerCommands:(id)unused;
@end
@implementation AudioScrobbler
+ (BOOL)isRunning {
NSArray *launchedApps = [[NSWorkspace sharedWorkspace] runningApplications];
BOOL running = NO;
for(NSRunningApplication *app in launchedApps) {
if([[app bundleIdentifier] isEqualToString:@"fm.last.Last.fm"] ||
[[app bundleIdentifier] isEqualToString:@"fm.last.Scrobbler"]) {
running = YES;
break;
}
}
return running;
}
- (id)init {
if((self = [super init])) {
_pluginID = @"cog";
if([[NSUserDefaults standardUserDefaults] boolForKey:@"automaticallyLaunchLastFM"]) {
if(![AudioScrobbler isRunning]) {
[[NSWorkspace sharedWorkspace] launchApplication:@"Last.fm.app"];
}
}
_keepProcessingAudioScrobblerCommands = YES;
kern_return_t result = semaphore_create(mach_task_self(), &_semaphore, SYNC_POLICY_FIFO, 0);
if(KERN_SUCCESS != result) {
ALog(@"Couldn't create semaphore (%s).", mach_error_type(result));
self = nil;
return nil;
}
[NSThread detachNewThreadSelector:@selector(processAudioScrobblerCommands:) toTarget:self withObject:nil];
}
return self;
}
- (void)dealloc {
if([self keepProcessingAudioScrobblerCommands] || NO == [self audioScrobblerThreadCompleted])
[self shutdown];
_queue = nil;
semaphore_destroy(mach_task_self(), _semaphore);
_semaphore = 0;
}
- (void)start:(PlaylistEntry *)pe {
[self sendCommand:[NSString stringWithFormat:@"START c=%@&a=%@&t=%@&b=%@&m=%@&l=%i&p=%@\n",
[self pluginID],
escapeForLastFM([pe artist]),
escapeForLastFM([pe title]),
escapeForLastFM([pe album]),
@"", // TODO: MusicBrainz support
[[pe length] intValue],
escapeForLastFM([pe.url path])]];
}
- (void)stop {
[self sendCommand:[NSString stringWithFormat:@"STOP c=%@\n", [self pluginID]]];
}
- (void)pause {
[self sendCommand:[NSString stringWithFormat:@"PAUSE c=%@\n", [self pluginID]]];
}
- (void)resume {
[self sendCommand:[NSString stringWithFormat:@"RESUME c=%@\n", [self pluginID]]];
}
- (void)shutdown {
[self setKeepProcessingAudioScrobblerCommands:NO];
semaphore_signal([self semaphore]);
// Wait for the thread to terminate
while(NO == [self audioScrobblerThreadCompleted])
[[NSRunLoop currentRunLoop] runMode:AudioScrobblerRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];
}
@end
@implementation AudioScrobbler (Private)
- (NSMutableArray *)queue {
if(nil == _queue)
_queue = [[NSMutableArray alloc] init];
return _queue;
}
- (NSString *)pluginID {
return _pluginID;
}
- (void)sendCommand:(NSString *)command {
@synchronized([self queue]) {
[[self queue] addObject:command];
}
semaphore_signal([self semaphore]);
}
- (BOOL)keepProcessingAudioScrobblerCommands {
return _keepProcessingAudioScrobblerCommands;
}
- (void)setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands {
_keepProcessingAudioScrobblerCommands = keepProcessingAudioScrobblerCommands;
}
- (BOOL)audioScrobblerThreadCompleted {
return _audioScrobblerThreadCompleted;
}
- (void)setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted {
_audioScrobblerThreadCompleted = audioScrobblerThreadCompleted;
}
- (semaphore_t)semaphore {
return _semaphore;
}
- (void)processAudioScrobblerCommands:(id)unused {
@autoreleasepool {
AudioScrobblerClient *client = [[AudioScrobblerClient alloc] init];
mach_timespec_t timeout = { 5, 0 };
NSString *command = nil;
NSString *response = nil;
in_port_t port = 33367;
while([self keepProcessingAudioScrobblerCommands]) {
@autoreleasepool {
// Get the first command to be sent
@synchronized([self queue]) {
if([[self queue] count]) {
command = [[self queue] objectAtIndex:0];
[[self queue] removeObjectAtIndex:0];
}
}
if(nil != command) {
@try {
if([client connectToHost:@"localhost" port:port]) {
port = [client connectedPort];
[client send:command];
command = nil;
response = [client receive];
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0, 2)])
ALog(@"AudioScrobbler error: %@", response);
[client shutdown];
}
}
@catch(NSException *exception) {
command = nil;
[client shutdown];
// ALog(@"Exception: %@",exception);
continue;
}
}
semaphore_timedwait([self semaphore], timeout);
}
}
// Send a final stop command to cleanup
@try {
if([client connectToHost:@"localhost" port:port]) {
[client send:[NSString stringWithFormat:@"STOP c=%@\n", [self pluginID]]];
response = [client receive];
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0, 2)])
ALog(@"AudioScrobbler error: %@", response);
[client shutdown];
}
}
@catch(NSException *exception) {
[client shutdown];
}
[self setAudioScrobblerThreadCompleted:YES];
}
}
@end

View File

@ -1,41 +0,0 @@
/*
* $Id: AudioScrobblerClient.h 666 2007-04-26 16:35:18Z stephen_booth $
*
* Copyright (C) 2006 - 2007 Stephen F. Booth <me@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import <Cocoa/Cocoa.h>
#include <netdb.h>
@interface AudioScrobblerClient : NSObject {
int _socket;
BOOL _doPortStepping;
in_port_t _port;
}
- (BOOL)connectToHost:(NSString *)hostname port:(in_port_t)port;
- (BOOL)isConnected;
- (in_port_t)connectedPort;
- (void)send:(NSString *)data;
- (NSString *)receive;
- (void)shutdown;
@end

View File

@ -1,212 +0,0 @@
/*
* $Id: AudioScrobblerClient.m 869 2007-06-18 15:51:33Z stephen_booth $
*
* Copyright (C) 2006 - 2007 Stephen F. Booth <me@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This is a port of the BlockingClient client class from
* the Last.fm ScrobSub library by sharevari
*/
#import "AudioScrobblerClient.h"
#include <arpa/inet.h>
#import "Logging.h"
#define kBufferSize 1024
#define kPortsToStep 5
static in_addr_t
addressForHost(NSString *hostname) {
NSCParameterAssert(nil != hostname);
in_addr_t address;
struct hostent *hostinfo;
address = inet_addr([hostname cStringUsingEncoding:NSASCIIStringEncoding]);
if(INADDR_NONE == address) {
hostinfo = gethostbyname([hostname cStringUsingEncoding:NSASCIIStringEncoding]);
if(NULL == hostinfo) {
ALog(@"AudioScrobblerClient error: Unable to resolve address for \"%@\".", hostname);
return INADDR_NONE;
}
address = *((in_addr_t *)hostinfo->h_addr_list[0]);
}
return address;
}
@interface AudioScrobblerClient (Private)
- (BOOL)connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port;
@end
@implementation AudioScrobblerClient
- (id)init {
if((self = [super init])) {
_socket = -1;
_doPortStepping = YES;
}
return self;
}
- (BOOL)connectToHost:(NSString *)hostname port:(in_port_t)port {
NSParameterAssert(nil != hostname);
in_addr_t remoteAddress = addressForHost(hostname);
if(INADDR_NONE != remoteAddress)
return [self connectToSocket:remoteAddress port:port];
return NO;
}
- (BOOL)isConnected {
return (-1 != _socket);
}
- (in_port_t)connectedPort {
return _port;
}
- (void)send:(NSString *)data {
const char *utf8data = [data UTF8String];
unsigned len = (unsigned int)strlen(utf8data);
unsigned bytesToSend = len;
unsigned totalBytesSent = 0;
ssize_t bytesSent = 0;
if(NO == [self isConnected]) {
ALog(@"AudioScrobblerClient error: Can't send data, client not connected");
return;
}
while(totalBytesSent < bytesToSend && -1 != bytesSent) {
bytesSent = send(_socket, utf8data + totalBytesSent, bytesToSend - totalBytesSent, 0);
if(-1 == bytesSent || 0 == bytesSent)
ALog(@"AudioScrobblerClient error: Unable to send data through socket: %s", strerror(errno));
totalBytesSent += bytesSent;
}
}
- (NSString *)receive {
char buffer[kBufferSize];
int readSize = kBufferSize - 1;
ssize_t bytesRead = 0;
BOOL keepGoing = YES;
NSString *result = nil;
if(NO == [self isConnected]) {
ALog(@"AudioScrobblerClient error: Can't receive data, client not connected");
return nil;
}
do {
bytesRead = recv(_socket, buffer, readSize, 0);
if(-1 == bytesRead || 0 == bytesRead) {
ALog(@"AudioScrobblerClient error: Unable to receive data through socket: %s", strerror(errno));
break;
}
if('\n' == buffer[bytesRead - 1]) {
--bytesRead;
keepGoing = NO;
}
buffer[bytesRead] = '\0';
result = [[NSString alloc] initWithUTF8String:buffer];
} while(keepGoing);
return result;
}
- (void)shutdown {
int result;
char buffer[kBufferSize];
ssize_t bytesRead;
if(NO == [self isConnected]) {
return;
}
result = shutdown(_socket, SHUT_WR);
if(-1 == result)
ALog(@"AudioScrobblerClient error: Socket shutdown failed: %s", strerror(errno));
for(;;) {
bytesRead = recv(_socket, buffer, kBufferSize, 0);
if(-1 == bytesRead)
ALog(@"AudioScrobblerClient error: Waiting for shutdown confirmation failed: %s", strerror(errno));
if(0 != bytesRead) {
NSString *received = [[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding];
ALog(@"Received unexpected bytes during shutdown: %@", received);
} else
break;
}
result = close(_socket);
if(-1 == result)
ALog(@"Couldn't close socket (%s)", strerror(errno));
_socket = -1;
_port = 0;
}
@end
@implementation AudioScrobblerClient (Private)
- (BOOL)connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port {
NSParameterAssert(INADDR_NONE != remoteAddress);
_port = port;
int result;
do {
struct sockaddr_in socketAddress;
_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == _socket) {
ALog(@"Unable to create socket (%s)", strerror(errno));
return NO;
}
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = remoteAddress;
socketAddress.sin_port = htons(_port);
result = connect(_socket, (const struct sockaddr *)&socketAddress, sizeof(struct sockaddr_in));
if(-1 == result) {
close(_socket);
_socket = -1;
_port++;
}
} while(YES == _doPortStepping && -1 == result && _port < (port + kPortsToStep));
return (-1 != result);
}
@end

View File

@ -25,17 +25,17 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123"> <splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
<rect key="frame" x="0.0" y="226" width="1000" height="174"/> <rect key="frame" x="0.0" y="277" width="1000" height="123"/>
<subviews> <subviews>
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View"> <scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View">
<rect key="frame" x="0.0" y="0.0" width="1000" height="174"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="123"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY"> <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY">
<rect key="frame" x="0.0" y="0.0" width="1000" height="174"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="123"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView"> <tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView">
<rect key="frame" x="0.0" y="0.0" width="1000" height="157"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="106"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="6"/> <size key="intercellSpacing" width="3" height="6"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>

View File

@ -2,7 +2,19 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.assets.movies.read-only</key>
<true/>
<key>com.apple.security.assets.music.read-only</key>
<true/>
<key>com.apple.security.cs.allow-jit</key> <key>com.apple.security.cs.allow-jit</key>
<true/> <true/>
<key>com.apple.security.files.downloads.read-only</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -23,8 +23,6 @@
173855FF0E0CC81F00488CD4 /* FileTreeOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 173855FE0E0CC81F00488CD4 /* FileTreeOutlineView.m */; }; 173855FF0E0CC81F00488CD4 /* FileTreeOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 173855FE0E0CC81F00488CD4 /* FileTreeOutlineView.m */; };
1752C36C0F59E00100F85F28 /* PlaybackButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = 1752C36B0F59E00100F85F28 /* PlaybackButtons.m */; }; 1752C36C0F59E00100F85F28 /* PlaybackButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = 1752C36B0F59E00100F85F28 /* PlaybackButtons.m */; };
1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 1755E1F70BA0D2B600CA3560 /* PlaylistLoader.m */; }; 1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 1755E1F70BA0D2B600CA3560 /* PlaylistLoader.m */; };
1766C6930B911DF1004A7AE4 /* AudioScrobbler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1766C68F0B911DF1004A7AE4 /* AudioScrobbler.m */; };
1766C6950B911DF1004A7AE4 /* AudioScrobblerClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 1766C6910B911DF1004A7AE4 /* AudioScrobblerClient.m */; };
1770429C0B8BC53600B86321 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 177042980B8BC53600B86321 /* AppController.m */; }; 1770429C0B8BC53600B86321 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 177042980B8BC53600B86321 /* AppController.m */; };
1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1770429A0B8BC53600B86321 /* PlaybackController.m */; }; 1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1770429A0B8BC53600B86321 /* PlaybackController.m */; };
1778D3B00F645A190037E7A0 /* missingArt.png in Resources */ = {isa = PBXBuildFile; fileRef = 1778D3AF0F645A190037E7A0 /* missingArt.png */; }; 1778D3B00F645A190037E7A0 /* missingArt.png in Resources */ = {isa = PBXBuildFile; fileRef = 1778D3AF0F645A190037E7A0 /* missingArt.png */; };
@ -93,6 +91,7 @@
56DB084C0D6717DC00453B6A /* NSNumber+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB084B0D6717DC00453B6A /* NSNumber+CogSort.m */; }; 56DB084C0D6717DC00453B6A /* NSNumber+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB084B0D6717DC00453B6A /* NSNumber+CogSort.m */; };
56DB08550D67185300453B6A /* NSArray+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB08540D67185300453B6A /* NSArray+CogSort.m */; }; 56DB08550D67185300453B6A /* NSArray+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB08540D67185300453B6A /* NSArray+CogSort.m */; };
8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83059639277F011100EBFAAE /* File_Extractor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83059639277F011100EBFAAE /* File_Extractor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
8307D30E28606148000FF8EB /* SandboxBroker.m in Sources */ = {isa = PBXBuildFile; fileRef = 8307D30D28606148000FF8EB /* SandboxBroker.m */; };
830C37A127B95E3000E02BB0 /* Equalizer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 830C379F27B95E3000E02BB0 /* Equalizer.xib */; }; 830C37A127B95E3000E02BB0 /* Equalizer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 830C379F27B95E3000E02BB0 /* Equalizer.xib */; };
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; }; 830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; };
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; }; 830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; };
@ -784,10 +783,6 @@
1752C36B0F59E00100F85F28 /* PlaybackButtons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlaybackButtons.m; path = Window/PlaybackButtons.m; sourceTree = "<group>"; }; 1752C36B0F59E00100F85F28 /* PlaybackButtons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PlaybackButtons.m; path = Window/PlaybackButtons.m; sourceTree = "<group>"; };
1755E1F60BA0D2B600CA3560 /* PlaylistLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PlaylistLoader.h; sourceTree = "<group>"; }; 1755E1F60BA0D2B600CA3560 /* PlaylistLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PlaylistLoader.h; sourceTree = "<group>"; };
1755E1F70BA0D2B600CA3560 /* PlaylistLoader.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PlaylistLoader.m; sourceTree = "<group>"; }; 1755E1F70BA0D2B600CA3560 /* PlaylistLoader.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PlaylistLoader.m; sourceTree = "<group>"; };
1766C68E0B911DF1004A7AE4 /* AudioScrobbler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AudioScrobbler.h; sourceTree = "<group>"; };
1766C68F0B911DF1004A7AE4 /* AudioScrobbler.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AudioScrobbler.m; sourceTree = "<group>"; };
1766C6900B911DF1004A7AE4 /* AudioScrobblerClient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AudioScrobblerClient.h; sourceTree = "<group>"; };
1766C6910B911DF1004A7AE4 /* AudioScrobblerClient.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AudioScrobblerClient.m; sourceTree = "<group>"; };
1770424E0B8BC41800B86321 /* Cog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1770424E0B8BC41800B86321 /* Cog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Cog.app; sourceTree = BUILT_PRODUCTS_DIR; };
177042970B8BC53600B86321 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; }; 177042970B8BC53600B86321 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
177042980B8BC53600B86321 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; }; 177042980B8BC53600B86321 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
@ -909,6 +904,8 @@
56DB08530D67185300453B6A /* NSArray+CogSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+CogSort.h"; path = "Spotlight/NSArray+CogSort.h"; sourceTree = "<group>"; }; 56DB08530D67185300453B6A /* NSArray+CogSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+CogSort.h"; path = "Spotlight/NSArray+CogSort.h"; sourceTree = "<group>"; };
56DB08540D67185300453B6A /* NSArray+CogSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+CogSort.m"; path = "Spotlight/NSArray+CogSort.m"; sourceTree = "<group>"; }; 56DB08540D67185300453B6A /* NSArray+CogSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+CogSort.m"; path = "Spotlight/NSArray+CogSort.m"; sourceTree = "<group>"; };
83059634277F011100EBFAAE /* File_Extractor.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = File_Extractor.xcodeproj; path = Frameworks/File_Extractor/File_Extractor.xcodeproj; sourceTree = "<group>"; }; 83059634277F011100EBFAAE /* File_Extractor.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = File_Extractor.xcodeproj; path = Frameworks/File_Extractor/File_Extractor.xcodeproj; sourceTree = "<group>"; };
8307D30C28606148000FF8EB /* SandboxBroker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SandboxBroker.h; sourceTree = "<group>"; };
8307D30D28606148000FF8EB /* SandboxBroker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SandboxBroker.m; sourceTree = "<group>"; };
830C37A027B95E3000E02BB0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Equalizer.xib; sourceTree = "<group>"; }; 830C37A027B95E3000E02BB0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Equalizer.xib; sourceTree = "<group>"; };
830C37A327B95EB300E02BB0 /* EqualizerWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EqualizerWindowController.h; path = Equalizer/EqualizerWindowController.h; sourceTree = "<group>"; }; 830C37A327B95EB300E02BB0 /* EqualizerWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EqualizerWindowController.h; path = Equalizer/EqualizerWindowController.h; sourceTree = "<group>"; };
830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = EqualizerWindowController.m; path = Equalizer/EqualizerWindowController.m; sourceTree = "<group>"; }; 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = EqualizerWindowController.m; path = Equalizer/EqualizerWindowController.m; sourceTree = "<group>"; };
@ -1136,7 +1133,6 @@
830C37A227B95E6000E02BB0 /* Equalizer */, 830C37A227B95E6000E02BB0 /* Equalizer */,
17D1B0FE0F63252900694C57 /* InfoInspector */, 17D1B0FE0F63252900694C57 /* InfoInspector */,
569C52C50D5F2BD500BDBDC9 /* Spotlight */, 569C52C50D5F2BD500BDBDC9 /* Spotlight */,
1766C68D0B911DF1004A7AE4 /* AudioScrobbler */,
17E0D5F60F520F42005B6FED /* Transformers */, 17E0D5F60F520F42005B6FED /* Transformers */,
177EC0110B8BC2CF0000BC8C /* Utils */, 177EC0110B8BC2CF0000BC8C /* Utils */,
177EBF770B8BC2A70000BC8C /* ThirdParty */, 177EBF770B8BC2A70000BC8C /* ThirdParty */,
@ -1184,17 +1180,6 @@
name = "Other Frameworks"; name = "Other Frameworks";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1766C68D0B911DF1004A7AE4 /* AudioScrobbler */ = {
isa = PBXGroup;
children = (
1766C68E0B911DF1004A7AE4 /* AudioScrobbler.h */,
1766C68F0B911DF1004A7AE4 /* AudioScrobbler.m */,
1766C6900B911DF1004A7AE4 /* AudioScrobblerClient.h */,
1766C6910B911DF1004A7AE4 /* AudioScrobblerClient.m */,
);
path = AudioScrobbler;
sourceTree = "<group>";
};
177042960B8BC53600B86321 /* Application */ = { 177042960B8BC53600B86321 /* Application */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1266,6 +1251,8 @@
177EC01B0B8BC2CF0000BC8C /* TrackingCell.m */, 177EC01B0B8BC2CF0000BC8C /* TrackingCell.m */,
177EC01C0B8BC2CF0000BC8C /* TrackingSlider.h */, 177EC01C0B8BC2CF0000BC8C /* TrackingSlider.h */,
177EC01D0B8BC2CF0000BC8C /* TrackingSlider.m */, 177EC01D0B8BC2CF0000BC8C /* TrackingSlider.m */,
8307D30C28606148000FF8EB /* SandboxBroker.h */,
8307D30D28606148000FF8EB /* SandboxBroker.m */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2610,10 +2597,8 @@
177EC0290B8BC2CF0000BC8C /* TrackingSlider.m in Sources */, 177EC0290B8BC2CF0000BC8C /* TrackingSlider.m in Sources */,
1770429C0B8BC53600B86321 /* AppController.m in Sources */, 1770429C0B8BC53600B86321 /* AppController.m in Sources */,
1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */, 1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */,
1766C6930B911DF1004A7AE4 /* AudioScrobbler.m in Sources */,
8355D6B6180612F300D05687 /* NSData+MD5.m in Sources */, 8355D6B6180612F300D05687 /* NSData+MD5.m in Sources */,
8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */, 8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */,
1766C6950B911DF1004A7AE4 /* AudioScrobblerClient.m in Sources */,
1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */, 1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */,
8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */, 8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */,
179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */, 179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */,
@ -2668,6 +2653,7 @@
171EFE8C0F59FEAE000ADC42 /* DockIconController.m in Sources */, 171EFE8C0F59FEAE000ADC42 /* DockIconController.m in Sources */,
17F6C8070F603701000D9DA9 /* PlaybackEventController.m in Sources */, 17F6C8070F603701000D9DA9 /* PlaybackEventController.m in Sources */,
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */, 83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */,
8307D30E28606148000FF8EB /* SandboxBroker.m in Sources */,
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */, 83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */,
835F00BB279BD1CD00055FCF /* SecondsFormatter.m in Sources */, 835F00BB279BD1CD00055FCF /* SecondsFormatter.m in Sources */,
1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */, 1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */,

View File

@ -60,9 +60,14 @@
<attribute name="volume" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="YES"/> <attribute name="volume" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="YES"/>
<attribute name="year" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="year" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
</entity> </entity>
<entity name="SandboxToken" representedClassName="SandboxToken" syncable="YES" codeGenerationType="class">
<attribute name="bookmark" optional="YES" attributeType="Binary"/>
<attribute name="path" optional="YES" attributeType="String"/>
</entity>
<elements> <elements>
<element name="AlbumArtwork" positionX="0" positionY="207" width="128" height="59"/> <element name="AlbumArtwork" positionX="0" positionY="207" width="128" height="59"/>
<element name="PlaylistEntry" positionX="-36" positionY="9" width="128" height="704"/>
<element name="PlayCount" positionX="-18" positionY="171" width="128" height="134"/> <element name="PlayCount" positionX="-18" positionY="171" width="128" height="134"/>
<element name="PlaylistEntry" positionX="-36" positionY="9" width="128" height="704"/>
<element name="SandboxToken" positionX="-18" positionY="171" width="128" height="59"/>
</elements> </elements>
</model> </model>

View File

@ -19,6 +19,10 @@
@class SpotlightWindowController; @class SpotlightWindowController;
@class PlaybackController; @class PlaybackController;
@interface NSApplication (CoreDataStorageExtension)
- (NSPersistentContainer *_Nonnull)sharedPersistentContainer;
@end
typedef NS_ENUM(NSInteger, RepeatMode) { typedef NS_ENUM(NSInteger, RepeatMode) {
RepeatModeNoRepeat = 0, RepeatModeNoRepeat = 0,
RepeatModeRepeatOne, RepeatModeRepeatOne,
@ -139,7 +143,7 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
- (IBAction)reloadTags:(id _Nullable)sender; - (IBAction)reloadTags:(id _Nullable)sender;
// Play statistics // Play statistics
- (void)updatePlayCountForTrack:(PlaylistEntry *)pe; - (void)updatePlayCountForTrack:(PlaylistEntry *_Nonnull)pe;
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *_Nullable)indexSet - (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *_Nullable)indexSet
toIndex:(NSUInteger)insertIndex; toIndex:(NSUInteger)insertIndex;

View File

@ -27,6 +27,14 @@
extern BOOL kAppControllerShuttingDown; extern BOOL kAppControllerShuttingDown;
NSPersistentContainer *__persistentContainer = nil;
@implementation NSApplication (CoreDataStorageExtension)
- (NSPersistentContainer *_Nonnull)sharedPersistentContainer {
return __persistentContainer;
}
@end
@implementation PlaylistController @implementation PlaylistController
@synthesize currentEntry; @synthesize currentEntry;
@ -35,7 +43,6 @@ extern BOOL kAppControllerShuttingDown;
static NSArray *cellIdentifiers = nil; static NSArray *cellIdentifiers = nil;
NSPersistentContainer *__persistentContainer = nil;
NSMutableDictionary<NSString *, AlbumArtwork *> *__artworkDictionary = nil; NSMutableDictionary<NSString *, AlbumArtwork *> *__artworkDictionary = nil;
static void *playlistControllerContext = &playlistControllerContext; static void *playlistControllerContext = &playlistControllerContext;

View File

@ -32,6 +32,7 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
8307D31A286070EA000FF8EB /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
8359009717FEF6490060F3ED /* ArchiveSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArchiveSource.h; sourceTree = "<group>"; }; 8359009717FEF6490060F3ED /* ArchiveSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArchiveSource.h; sourceTree = "<group>"; };
8359009817FEF6490060F3ED /* ArchiveSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArchiveSource.m; sourceTree = "<group>"; }; 8359009817FEF6490060F3ED /* ArchiveSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArchiveSource.m; sourceTree = "<group>"; };
8359009A17FEFDA80060F3ED /* ArchiveContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArchiveContainer.h; sourceTree = "<group>"; }; 8359009A17FEFDA80060F3ED /* ArchiveContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArchiveContainer.h; sourceTree = "<group>"; };
@ -102,6 +103,7 @@
8359FF2017FEF35C0060F3ED /* ArchiveSource */ = { 8359FF2017FEF35C0060F3ED /* ArchiveSource */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8307D31A286070EA000FF8EB /* SandboxBroker.h */,
8384913518081BA000E7332D /* Logging.h */, 8384913518081BA000E7332D /* Logging.h */,
835900A017FF079C0060F3ED /* Plugin.h */, 835900A017FF079C0060F3ED /* Plugin.h */,
8359009A17FEFDA80060F3ED /* ArchiveContainer.h */, 8359009A17FEFDA80060F3ED /* ArchiveContainer.h */,

View File

@ -12,6 +12,8 @@
#import "Logging.h" #import "Logging.h"
#import "SandboxBroker.h"
static NSString *path_pack_string(NSString *src) { static NSString *path_pack_string(NSString *src) {
return [NSString stringWithFormat:@"|%lu|%@|", [src length], src]; return [NSString stringWithFormat:@"|%lu|%@|", [src length], src];
} }
@ -43,6 +45,11 @@ static NSString *g_make_unpack_path(NSString *archive, NSString *file, NSString
return @[]; return @[];
} }
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url];
fex_t *fex; fex_t *fex;
fex_err_t error = fex_open(&fex, [[url path] UTF8String]); fex_err_t error = fex_open(&fex, [[url path] UTF8String]);
if(error) { if(error) {
@ -61,6 +68,8 @@ static NSString *g_make_unpack_path(NSString *archive, NSString *file, NSString
fex_close(fex); fex_close(fex);
[sandboxBroker endFolderAccess:url];
return files; return files;
} }

View File

@ -20,6 +20,7 @@
NSUInteger size; NSUInteger size;
NSURL *_url; NSURL *_url;
NSURL *fileURL;
} }
@end @end

View File

@ -8,6 +8,8 @@
#import "ArchiveSource.h" #import "ArchiveSource.h"
#import "SandboxBroker.h"
#import "Logging.h" #import "Logging.h"
static NSString *path_unpack_string(NSString *src, NSRange *remainder) { static NSString *path_unpack_string(NSString *src, NSRange *remainder) {
@ -83,6 +85,13 @@ static BOOL g_parse_unpack_path(NSString *src, NSString **archive, NSString **fi
if(![type isEqualToString:@"fex"]) if(![type isEqualToString:@"fex"])
return NO; return NO;
fileURL = [NSURL fileURLWithPath:archive];
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:fileURL];
fex_err_t error; fex_err_t error;
error = fex_open(&fex, [archive UTF8String]); error = fex_open(&fex, [archive UTF8String]);
@ -151,6 +160,13 @@ static BOOL g_parse_unpack_path(NSString *src, NSString **archive, NSString **fi
fex_close(fex); fex_close(fex);
fex = NULL; fex = NULL;
} }
if(fileURL) {
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:fileURL];
}
} }
- (NSURL *)url { - (NSURL *)url {

View File

@ -8,6 +8,8 @@
#import "FileSource.h" #import "FileSource.h"
#import "SandboxBroker.h"
@implementation FileSource @implementation FileSource
+ (void)initialize { + (void)initialize {
@ -26,6 +28,11 @@
- (BOOL)open:(NSURL *)url { - (BOOL)open:(NSURL *)url {
[self setURL:url]; [self setURL:url];
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url];
NSString *path = [url path]; NSString *path = [url path];
fex_type_t type; fex_type_t type;
@ -123,6 +130,11 @@
fex_close(fex); fex_close(fex);
fex = NULL; fex = NULL;
} }
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:_url];
} }
- (NSURL *)url { - (NSURL *)url {

View File

@ -37,6 +37,7 @@
17ADB4180B979AEB00257CA2 /* FileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSource.h; sourceTree = "<group>"; }; 17ADB4180B979AEB00257CA2 /* FileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSource.h; sourceTree = "<group>"; };
17ADB4190B979AEB00257CA2 /* FileSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileSource.m; sourceTree = "<group>"; }; 17ADB4190B979AEB00257CA2 /* FileSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileSource.m; sourceTree = "<group>"; };
32DBCF630370AF2F00C91783 /* FileSource_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSource_Prefix.pch; sourceTree = "<group>"; }; 32DBCF630370AF2F00C91783 /* FileSource_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSource_Prefix.pch; sourceTree = "<group>"; };
8307D31B2860722C000FF8EB /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
8335FF6817FF765A002D8DD2 /* File_Extractor.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = File_Extractor.xcodeproj; path = ../../Frameworks/File_Extractor/File_Extractor.xcodeproj; sourceTree = "<group>"; }; 8335FF6817FF765A002D8DD2 /* File_Extractor.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = File_Extractor.xcodeproj; path = ../../Frameworks/File_Extractor/File_Extractor.xcodeproj; sourceTree = "<group>"; };
8D5B49B6048680CD000E48DA /* FileSource.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FileSource.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B6048680CD000E48DA /* FileSource.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FileSource.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -88,6 +89,7 @@
08FB77AFFE84173DC02AAC07 /* Classes */ = { 08FB77AFFE84173DC02AAC07 /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8307D31B2860722C000FF8EB /* SandboxBroker.h */,
17ADB4080B979A8A00257CA2 /* Plugin.h */, 17ADB4080B979A8A00257CA2 /* Plugin.h */,
17ADB4180B979AEB00257CA2 /* FileSource.h */, 17ADB4180B979AEB00257CA2 /* FileSource.h */,
17ADB4190B979AEB00257CA2 /* FileSource.m */, 17ADB4190B979AEB00257CA2 /* FileSource.m */,

View File

@ -463,6 +463,8 @@ static void http_stream_reset(HTTPSource *fp) {
struct curl_slist *headers = NULL; struct curl_slist *headers = NULL;
struct curl_slist *ok_aliases = curl_slist_append(NULL, "ICY 200 OK"); struct curl_slist *ok_aliases = curl_slist_append(NULL, "ICY 200 OK");
BOOL sslVerify = ![[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"allowInsecureSSL"];
curl_easy_reset(curl); curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, [[URL absoluteString] UTF8String]); curl_easy_setopt(curl, CURLOPT_URL, [[URL absoluteString] UTF8String]);
NSString *ua = [NSString stringWithFormat:@"Cog/%@", [[[NSBundle mainBundle] infoDictionary] valueForKey:(__bridge NSString *)kCFBundleVersionKey]]; NSString *ua = [NSString stringWithFormat:@"Cog/%@", [[[NSBundle mainBundle] infoDictionary] valueForKey:(__bridge NSString *)kCFBundleVersionKey]];
@ -491,6 +493,7 @@ static void http_stream_reset(HTTPSource *fp) {
if(pos > 0 && length >= 0) { if(pos > 0 && length >= 0) {
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, (long)pos); curl_easy_setopt(curl, CURLOPT_RESUME_FROM, (long)pos);
} }
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)sslVerify);
// fp->status = STATUS_INITIAL; // fp->status = STATUS_INITIAL;
DLog(@"curl: calling curl_easy_perform (status=%d)...\n", self->status); DLog(@"curl: calling curl_easy_perform (status=%d)...\n", self->status);
gettimeofday(&last_read_time, NULL); gettimeofday(&last_read_time, NULL);

View File

@ -88,6 +88,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
8307D31E28607377000FF8EB /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
831E2A7F27B4B2B2006F1C86 /* bassmidi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bassmidi.h; sourceTree = "<group>"; }; 831E2A7F27B4B2B2006F1C86 /* bassmidi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bassmidi.h; sourceTree = "<group>"; };
831E2A8027B4B2B2006F1C86 /* sflist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sflist.c; sourceTree = "<group>"; }; 831E2A8027B4B2B2006F1C86 /* sflist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sflist.c; sourceTree = "<group>"; };
831E2A8227B4B2B2006F1C86 /* sflist_rewrite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sflist_rewrite.c; sourceTree = "<group>"; }; 831E2A8227B4B2B2006F1C86 /* sflist_rewrite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sflist_rewrite.c; sourceTree = "<group>"; };
@ -295,6 +296,7 @@
83B06690180D5668008E3612 /* MIDI */ = { 83B06690180D5668008E3612 /* MIDI */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8307D31E28607377000FF8EB /* SandboxBroker.h */,
831E2A9127B4B2FA006F1C86 /* json */, 831E2A9127B4B2FA006F1C86 /* json */,
831E2A7D27B4B2B2006F1C86 /* BASS */, 831E2A7D27B4B2B2006F1C86 /* BASS */,
8356BCC427B352620074E50C /* BMPlayer.cpp */, 8356BCC427B352620074E50C /* BMPlayer.cpp */,

View File

@ -24,6 +24,8 @@ class BMPlayer;
MIDIPlayer* player; MIDIPlayer* player;
midi_container midi_file; midi_container midi_file;
NSURL* sandboxURL;
NSString* globalSoundFontPath; NSString* globalSoundFontPath;
BOOL soundFontsAssigned; BOOL soundFontsAssigned;
BOOL isLooped; BOOL isLooped;

View File

@ -18,6 +18,8 @@
#import "PlaylistController.h" #import "PlaylistController.h"
#import "SandboxBroker.h"
#import <dlfcn.h> #import <dlfcn.h>
static OSType getOSType(const char *in_) { static OSType getOSType(const char *in_) {
@ -165,6 +167,15 @@ static OSType getOSType(const char *in_) {
globalSoundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; globalSoundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"];
if(globalSoundFontPath && [globalSoundFontPath length] > 0) {
sandboxURL = [NSURL fileURLWithPath:[globalSoundFontPath stringByDeletingLastPathComponent]];
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:sandboxURL];
}
// First detect if soundfont has gone AWOL // First detect if soundfont has gone AWOL
if(![[NSFileManager defaultManager] fileExistsAtPath:globalSoundFontPath]) { if(![[NSFileManager defaultManager] fileExistsAtPath:globalSoundFontPath]) {
globalSoundFontPath = nil; globalSoundFontPath = nil;
@ -333,6 +344,15 @@ static OSType getOSType(const char *in_) {
- (void)close { - (void)close {
delete player; delete player;
player = NULL; player = NULL;
if(sandboxURL) {
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:sandboxURL];
sandboxURL = nil;
}
} }
- (void)dealloc { - (void)dealloc {

View File

@ -11,6 +11,7 @@
17C93FC30B90056C008627D6 /* TagLibMetadataReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */; }; 17C93FC30B90056C008627D6 /* TagLibMetadataReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */; };
17F563B40C3BDBB30019975C /* TagLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; }; 17F563B40C3BDBB30019975C /* TagLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; };
17F563B60C3BDBB50019975C /* TagLib.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 17F563B60C3BDBB50019975C /* TagLib.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
8307D31D286072BF000FF8EB /* SandboxBroker.h in Headers */ = {isa = PBXBuildFile; fileRef = 8307D31C286072BF000FF8EB /* SandboxBroker.h */; };
8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */; }; 8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */; };
8356BCE627B377C20074E50C /* TagLibID3v2Reader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */; }; 8356BCE627B377C20074E50C /* TagLibID3v2Reader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */; };
8384913A18081FFC00E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384913918081FFC00E7332D /* Logging.h */; }; 8384913A18081FFC00E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384913918081FFC00E7332D /* Logging.h */; };
@ -58,6 +59,7 @@
17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = TagLibMetadataReader.m; sourceTree = "<group>"; }; 17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = TagLibMetadataReader.m; sourceTree = "<group>"; };
17F563A00C3BDB8F0019975C /* TagLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TagLib.xcodeproj; path = ../../Frameworks/TagLib/TagLib.xcodeproj; sourceTree = SOURCE_ROOT; }; 17F563A00C3BDB8F0019975C /* TagLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TagLib.xcodeproj; path = ../../Frameworks/TagLib/TagLib.xcodeproj; sourceTree = SOURCE_ROOT; };
32DBCF630370AF2F00C91783 /* TagLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TagLib_Prefix.pch; sourceTree = "<group>"; }; 32DBCF630370AF2F00C91783 /* TagLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TagLib_Prefix.pch; sourceTree = "<group>"; };
8307D31C286072BF000FF8EB /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TagLibID3v2Reader.h; sourceTree = "<group>"; }; 8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TagLibID3v2Reader.h; sourceTree = "<group>"; };
8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TagLibID3v2Reader.mm; sourceTree = "<group>"; }; 8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TagLibID3v2Reader.mm; sourceTree = "<group>"; };
8384913918081FFC00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; }; 8384913918081FFC00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
@ -111,6 +113,7 @@
08FB77AFFE84173DC02AAC07 /* Classes */ = { 08FB77AFFE84173DC02AAC07 /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8307D31C286072BF000FF8EB /* SandboxBroker.h */,
8384913918081FFC00E7332D /* Logging.h */, 8384913918081FFC00E7332D /* Logging.h */,
07CACE890ED1AD1000C0F1E8 /* TagLibMetadataWriter.h */, 07CACE890ED1AD1000C0F1E8 /* TagLibMetadataWriter.h */,
07CACE8A0ED1AD1000C0F1E8 /* TagLibMetadataWriter.m */, 07CACE8A0ED1AD1000C0F1E8 /* TagLibMetadataWriter.m */,
@ -174,6 +177,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8384913A18081FFC00E7332D /* Logging.h in Headers */, 8384913A18081FFC00E7332D /* Logging.h in Headers */,
8307D31D286072BF000FF8EB /* SandboxBroker.h in Headers */,
8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */, 8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -19,6 +19,8 @@
#import <taglib/ogg/vorbis/vorbisfile.h> #import <taglib/ogg/vorbis/vorbisfile.h>
#import <taglib/ogg/xiphcomment.h> #import <taglib/ogg/xiphcomment.h>
#import "SandboxBroker.h"
@implementation TagLibMetadataReader @implementation TagLibMetadataReader
+ (NSDictionary *)metadataForURL:(NSURL *)url { + (NSDictionary *)metadataForURL:(NSURL *)url {
@ -26,6 +28,11 @@
return [NSDictionary dictionary]; return [NSDictionary dictionary];
} }
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
// if ( !*TagLib::ascii_encoding ) { // if ( !*TagLib::ascii_encoding ) {
@ -230,6 +237,8 @@
} }
} }
[sandboxBroker endFolderAccess:url];
return dict; return dict;
} }

View File

@ -9,13 +9,13 @@
<customObject id="-2" userLabel="File's Owner" customClass="GeneralPreferencesPlugin"> <customObject id="-2" userLabel="File's Owner" customClass="GeneralPreferencesPlugin">
<connections> <connections>
<outlet property="appearanceView" destination="CgN-sy-RmM" id="wvB-aW-Gfx"/> <outlet property="appearanceView" destination="CgN-sy-RmM" id="wvB-aW-Gfx"/>
<outlet property="generalPane" destination="tm7-TU-wbV" id="jTO-w4-xwD"/>
<outlet property="hotKeyPane" destination="6" id="14"/> <outlet property="hotKeyPane" destination="6" id="14"/>
<outlet property="iTunesStyleCheck" destination="AIz-WH-Wqk" id="2n1-pY-cZR"/> <outlet property="iTunesStyleCheck" destination="AIz-WH-Wqk" id="2n1-pY-cZR"/>
<outlet property="midiPane" destination="i5B-ga-Atm" id="rbe-uK-5n2"/> <outlet property="midiPane" destination="i5B-ga-Atm" id="rbe-uK-5n2"/>
<outlet property="notificationsView" destination="U4w-jw-ca5" id="wVJ-GH-A21"/> <outlet property="notificationsView" destination="U4w-jw-ca5" id="wVJ-GH-A21"/>
<outlet property="outputPane" destination="57" id="75"/> <outlet property="outputPane" destination="57" id="75"/>
<outlet property="playlistView" destination="231" id="244"/> <outlet property="playlistView" destination="231" id="244"/>
<outlet property="scrobblerView" destination="0nK-XQ-5MY" id="dFj-Pk-FCG"/>
<outlet property="updatesView" destination="50" id="99"/> <outlet property="updatesView" destination="50" id="99"/>
</connections> </connections>
</customObject> </customObject>
@ -368,6 +368,12 @@
<outlet property="view" destination="JXu-ar-J3Y" id="uI3-0O-h4x"/> <outlet property="view" destination="JXu-ar-J3Y" id="uI3-0O-h4x"/>
</connections> </connections>
</customObject> </customObject>
<customObject id="tm7-TU-wbV" customClass="GeneralPane">
<connections>
<outlet property="sandboxBehaviorController" destination="9mC-3k-IQG" id="QzA-u4-vH7"/>
<outlet property="view" destination="0nK-XQ-5MY" id="TDK-Vn-N5G"/>
</connections>
</customObject>
<arrayController id="59" userLabel="OutputDevices" customClass="OutputsArrayController"> <arrayController id="59" userLabel="OutputDevices" customClass="OutputsArrayController">
<declaredKeys> <declaredKeys>
<string>name</string> <string>name</string>
@ -389,45 +395,96 @@
</declaredKeys> </declaredKeys>
<classReference key="objectClass" className="NSDictionary"/> <classReference key="objectClass" className="NSDictionary"/>
</arrayController> </arrayController>
<customView id="0nK-XQ-5MY" userLabel="ScrobblerView"> <customView id="0nK-XQ-5MY" userLabel="GeneralView">
<rect key="frame" x="0.0" y="0.0" width="530" height="99"/> <rect key="frame" x="0.0" y="0.0" width="530" height="202"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3gi-0d-78Z"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dIu-uT-8YW">
<rect key="frame" x="18" y="41" width="396" height="18"/> <rect key="frame" x="18" y="165" width="226" height="18"/>
<autoresizingMask key="autoresizingMask"/>
<buttonCell key="cell" type="check" title="Automatically launch Last.fm client" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="ze8-9p-SeX">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="52" name="value" keyPath="values.automaticallyLaunchLastFM" id="Ere-1R-mgh"/>
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ABj-cO-MaV">
<rect key="frame" x="18" y="69" width="396" height="18"/>
<autoresizingMask key="autoresizingMask"/>
<buttonCell key="cell" type="check" title="Enable Last.fm support (Last.fm client must be installed)" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="2N5-DH-IO6">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="52" name="value" keyPath="values.enableAudioScrobbler" id="R2p-gP-5oR"/>
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hi4-pa-uDu">
<rect key="frame" x="18" y="13" width="389" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Allow Last.fm client to control media keys" bezelStyle="regularSquare" imagePosition="left" inset="2" id="4Yi-67-ivc"> <buttonCell key="cell" type="check" title="Allow insecure SSL connections" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="sva-DV-ina">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
</buttonCell> </buttonCell>
<connections> <connections>
<binding destination="52" name="value" keyPath="values.allowLastfmMediaKeys" id="OZH-0Q-thF"/> <binding destination="52" name="value" keyPath="values.allowInsecureSSL" id="78K-25-qki"/>
</connections> </connections>
</button> </button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="crf-C9-9YF">
<rect key="frame" x="20" y="142" width="170" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sandbox allow paths:" id="wWm-AD-cD4">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6b3-9W-Flc">
<rect key="frame" x="20" y="20" width="490" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" id="gUE-Yu-LLA">
<rect key="frame" x="1" y="1" width="488" height="113"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" headerView="9rQ-Rq-K6J" id="gHG-xw-OyR">
<rect key="frame" x="0.0" y="0.0" width="488" height="85"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="17" height="0.0"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="388" minWidth="40" maxWidth="1000" id="Yz2-Ee-bZ0" userLabel="path">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Path">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Dhd-k8-ZXq">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="9mC-3k-IQG" name="value" keyPath="arrangedObjects.path" id="eva-uV-ce2"/>
</connections>
</tableColumn>
<tableColumn width="41" minWidth="40" maxWidth="1000" id="lEg-E3-TBs" userLabel="valid">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Valid">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="bWQ-dT-X3w">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="9mC-3k-IQG" name="value" keyPath="arrangedObjects.valid" id="Kg6-FI-Mcl"/>
</connections>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="OdN-bV-v6B">
<rect key="frame" x="1" y="99" width="488" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="rJY-00-GfQ">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="9rQ-Rq-K6J">
<rect key="frame" x="0.0" y="0.0" width="488" height="28"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
<connections>
<outlet property="menu" destination="ape-AV-kEr" id="hVx-ll-1cd"/>
</connections>
</scrollView>
</subviews> </subviews>
<point key="canvasLocation" x="450" y="-262"/> <point key="canvasLocation" x="450" y="-211"/>
</customView> </customView>
<customView id="U4w-jw-ca5" userLabel="NotificationsView"> <customView id="U4w-jw-ca5" userLabel="NotificationsView">
<rect key="frame" x="0.0" y="0.0" width="530" height="99"/> <rect key="frame" x="0.0" y="0.0" width="530" height="99"/>
@ -709,5 +766,28 @@
</declaredKeys> </declaredKeys>
<classReference key="objectClass" className="NSDictionary"/> <classReference key="objectClass" className="NSDictionary"/>
</arrayController> </arrayController>
<arrayController id="9mC-3k-IQG" customClass="SandboxPathBehaviorController">
<declaredKeys>
<string>path</string>
<string>valid</string>
</declaredKeys>
</arrayController>
<menu id="ape-AV-kEr" userLabel="SandboxContextMenu">
<items>
<menuItem title="Add Path" id="JxP-0t-mTB">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="addPath:" target="tm7-TU-wbV" id="j3o-oG-PA4"/>
</connections>
</menuItem>
<menuItem title="Delete Paths" id="nva-pz-xSS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="deleteSelectedPaths:" target="tm7-TU-wbV" id="suU-Gb-vMn"/>
</connections>
</menuItem>
</items>
<point key="canvasLocation" x="533" y="-395"/>
</menu>
</objects> </objects>
</document> </document>

View File

@ -0,0 +1,23 @@
//
// GeneralPane.h
// Preferences
//
// Created by Christopher Snowhill on 6/20/22.
//
#import "GeneralPreferencePane.h"
#import "SandboxPathBehaviorController.h"
NS_ASSUME_NONNULL_BEGIN
@interface GeneralPane : GeneralPreferencePane {
IBOutlet SandboxPathBehaviorController *sandboxBehaviorController;
}
- (IBAction)addPath:(id)sender;
- (IBAction)deleteSelectedPaths:(id)sender;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,44 @@
//
// GeneralPane.m
// Preferences
//
// Created by Christopher Snowhill on 6/20/22.
//
#import "GeneralPane.h"
@implementation GeneralPane
- (NSString *)title {
return NSLocalizedPrefString(@"General");
}
- (NSImage *)icon {
if(@available(macOS 11.0, *))
return [NSImage imageWithSystemSymbolName:@"gearshape.fill" accessibilityDescription:nil];
return [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForImageResource:@"general"]];
}
- (IBAction)addPath:(id)sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setFloatingPanel:YES];
NSInteger result = [panel runModal];
if(result == NSModalResponseOK) {
[sandboxBehaviorController addUrl:[panel URL]];
}
}
- (IBAction)deleteSelectedPaths:(id)sender {
NSArray *selectedObjects = [sandboxBehaviorController selectedObjects];
if(selectedObjects && [selectedObjects count]) {
NSArray *paths = [selectedObjects valueForKey:@"path"];
for(NSString *path in paths) {
[sandboxBehaviorController removePath:path];
}
}
}
@end

View File

@ -10,6 +10,7 @@
#import "PreferencePanePlugin.h" #import "PreferencePanePlugin.h"
#import "GeneralPane.h"
#import "HotKeyPane.h" #import "HotKeyPane.h"
#import "MIDIPane.h" #import "MIDIPane.h"
#import "OutputPane.h" #import "OutputPane.h"
@ -18,9 +19,9 @@
IBOutlet HotKeyPane *hotKeyPane; IBOutlet HotKeyPane *hotKeyPane;
IBOutlet OutputPane *outputPane; IBOutlet OutputPane *outputPane;
IBOutlet MIDIPane *midiPane; IBOutlet MIDIPane *midiPane;
IBOutlet GeneralPane *generalPane;
IBOutlet NSView *playlistView; IBOutlet NSView *playlistView;
IBOutlet NSView *scrobblerView;
IBOutlet NSView *updatesView; IBOutlet NSView *updatesView;
IBOutlet NSView *notificationsView; IBOutlet NSView *notificationsView;
IBOutlet NSView *appearanceView; IBOutlet NSView *appearanceView;
@ -31,9 +32,9 @@
- (HotKeyPane *)hotKeyPane; - (HotKeyPane *)hotKeyPane;
- (OutputPane *)outputPane; - (OutputPane *)outputPane;
- (MIDIPane *)midiPane; - (MIDIPane *)midiPane;
- (GeneralPane *)generalPane;
- (GeneralPreferencePane *)updatesPane; - (GeneralPreferencePane *)updatesPane;
- (GeneralPreferencePane *)scrobblerPane;
- (GeneralPreferencePane *)playlistPane; - (GeneralPreferencePane *)playlistPane;
- (GeneralPreferencePane *)notificationsPane; - (GeneralPreferencePane *)notificationsPane;
- (GeneralPreferencePane *)appearancePane; - (GeneralPreferencePane *)appearancePane;

View File

@ -32,7 +32,7 @@
[plugin hotKeyPane], [plugin hotKeyPane],
[plugin updatesPane], [plugin updatesPane],
[plugin outputPane], [plugin outputPane],
[plugin scrobblerPane], [plugin generalPane],
[plugin notificationsPane], [plugin notificationsPane],
[plugin appearancePane], [plugin appearancePane],
[plugin midiPane]]; [plugin midiPane]];
@ -50,6 +50,10 @@
return midiPane; return midiPane;
} }
- (GeneralPane *)generalPane {
return generalPane;
}
- (GeneralPreferencePane *)updatesPane { - (GeneralPreferencePane *)updatesPane {
return [GeneralPreferencePane preferencePaneWithView:updatesView return [GeneralPreferencePane preferencePaneWithView:updatesView
title:NSLocalizedPrefString(@"Updates") title:NSLocalizedPrefString(@"Updates")
@ -57,13 +61,6 @@
orOldIconNamed:@"updates"]; orOldIconNamed:@"updates"];
} }
- (GeneralPreferencePane *)scrobblerPane {
return [GeneralPreferencePane preferencePaneWithView:scrobblerView
title:NSLocalizedPrefString(@"Scrobble")
systemIconName:@"dot.radiowaves.left.and.right"
orOldIconNamed:@"lastfm"];
}
- (GeneralPreferencePane *)playlistPane { - (GeneralPreferencePane *)playlistPane {
return [GeneralPreferencePane preferencePaneWithView:playlistView return [GeneralPreferencePane preferencePaneWithView:playlistView
title:NSLocalizedPrefString(@"Playlist") title:NSLocalizedPrefString(@"Playlist")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -7,7 +7,6 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1766C7A80B912A71004A7AE4 /* lastfm.png in Resources */ = {isa = PBXBuildFile; fileRef = 1766C7A70B912A71004A7AE4 /* lastfm.png */; };
178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 178E386D0C3DA64500EE6711 /* InfoPlist.strings */; }; 178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 178E386D0C3DA64500EE6711 /* InfoPlist.strings */; };
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C643360B8A77CC00C53518 /* OutputsArrayController.m */; }; 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C643360B8A77CC00C53518 /* OutputsArrayController.m */; };
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C6433E0B8A783F00C53518 /* OutputPane.m */; }; 17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C6433E0B8A783F00C53518 /* OutputPane.m */; };
@ -17,18 +16,21 @@
17E41DB80C130AA500AC744D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17E41DB70C130AA500AC744D /* Localizable.strings */; }; 17E41DB80C130AA500AC744D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 17E41DB70C130AA500AC744D /* Localizable.strings */; };
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; }; 17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */ = {isa = PBXBuildFile; fileRef = 17E78A7D0D68BE3C005C5A59 /* file_tree.png */; };
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; }; 17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E78B680D68C1E3005C5A59 /* Preferences.xib */; };
8307D30528604D8B000FF8EB /* SandboxPathBehaviorController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8307D30428604D8B000FF8EB /* SandboxPathBehaviorController.m */; };
8307D30B286057B5000FF8EB /* GeneralPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8307D30A286057B5000FF8EB /* GeneralPane.m */; };
8307D31628606EAF000FF8EB /* general@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8307D31228606EAF000FF8EB /* general@2x.png */; };
8307D31728606EAF000FF8EB /* growl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8307D31328606EAF000FF8EB /* growl@2x.png */; };
8307D31828606EAF000FF8EB /* general.png in Resources */ = {isa = PBXBuildFile; fileRef = 8307D31428606EAF000FF8EB /* general.png */; };
8307D31928606EAF000FF8EB /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8307D31528606EAF000FF8EB /* growl.png */; };
83651DA527322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83651DA327322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m */; }; 83651DA527322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83651DA327322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m */; };
8372053718E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */; }; 8372053718E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */; };
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */; }; 837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */; };
8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; }; 8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; };
8384917818084D9F00E7332D /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917618084D9F00E7332D /* growl.png */; };
83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */; }; 83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */; };
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; }; 83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; };
83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; }; 83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; };
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; }; 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; };
83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E651810DD3A00CEF538 /* appearance@2x.png */; }; 83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E651810DD3A00CEF538 /* appearance@2x.png */; };
83F27E6C1810DD3A00CEF538 /* growl@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E661810DD3A00CEF538 /* growl@2x.png */; };
83F27E6D1810DD3A00CEF538 /* lastfm@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E671810DD3A00CEF538 /* lastfm@2x.png */; };
83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E681810DD3A00CEF538 /* midi@2x.png */; }; 83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E681810DD3A00CEF538 /* midi@2x.png */; };
83F27E6F1810DD3A00CEF538 /* playlist@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E691810DD3A00CEF538 /* playlist@2x.png */; }; 83F27E6F1810DD3A00CEF538 /* playlist@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E691810DD3A00CEF538 /* playlist@2x.png */; };
83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E6A1810DD3A00CEF538 /* updates@2x.png */; }; 83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E6A1810DD3A00CEF538 /* updates@2x.png */; };
@ -78,7 +80,6 @@
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; }; 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; }; 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
1766C7A70B912A71004A7AE4 /* lastfm.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = lastfm.png; path = Icons/lastfm.png; sourceTree = "<group>"; };
17C643360B8A77CC00C53518 /* OutputsArrayController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputsArrayController.m; sourceTree = "<group>"; }; 17C643360B8A77CC00C53518 /* OutputsArrayController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputsArrayController.m; sourceTree = "<group>"; };
17C643370B8A77CC00C53518 /* OutputsArrayController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputsArrayController.h; sourceTree = "<group>"; }; 17C643370B8A77CC00C53518 /* OutputsArrayController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputsArrayController.h; sourceTree = "<group>"; };
17C6433D0B8A783F00C53518 /* OutputPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputPane.h; sourceTree = "<group>"; }; 17C6433D0B8A783F00C53518 /* OutputPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputPane.h; sourceTree = "<group>"; };
@ -91,6 +92,15 @@
32DBCF630370AF2F00C91783 /* Preferences_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preferences_Prefix.pch; sourceTree = "<group>"; }; 32DBCF630370AF2F00C91783 /* Preferences_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preferences_Prefix.pch; sourceTree = "<group>"; };
3DFAC48E235B6B8100A29416 /* DEVELOPMENT_TEAM.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.xcconfig; sourceTree = "<group>"; }; 3DFAC48E235B6B8100A29416 /* DEVELOPMENT_TEAM.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DEVELOPMENT_TEAM.xcconfig; sourceTree = "<group>"; };
3DFAC48F235B6B8100A29416 /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; }; 3DFAC48F235B6B8100A29416 /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
8307D30328604D8B000FF8EB /* SandboxPathBehaviorController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SandboxPathBehaviorController.h; sourceTree = "<group>"; };
8307D30428604D8B000FF8EB /* SandboxPathBehaviorController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SandboxPathBehaviorController.m; sourceTree = "<group>"; };
8307D30828604ECF000FF8EB /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../Playlist/PlaylistController.h; sourceTree = "<group>"; };
8307D309286057B5000FF8EB /* GeneralPane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneralPane.h; sourceTree = "<group>"; };
8307D30A286057B5000FF8EB /* GeneralPane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GeneralPane.m; sourceTree = "<group>"; };
8307D31228606EAF000FF8EB /* general@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "general@2x.png"; path = "Icons/general@2x.png"; sourceTree = "<group>"; };
8307D31328606EAF000FF8EB /* growl@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "growl@2x.png"; path = "Icons/growl@2x.png"; sourceTree = "<group>"; };
8307D31428606EAF000FF8EB /* general.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = general.png; path = Icons/general.png; sourceTree = "<group>"; };
8307D31528606EAF000FF8EB /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = "<group>"; };
833F681B1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 833F681B1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
833F681C1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; }; 833F681C1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
8347435D20E6D58800063D45 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Preferences.strings; sourceTree = "<group>"; }; 8347435D20E6D58800063D45 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Preferences.strings; sourceTree = "<group>"; };
@ -106,7 +116,6 @@
837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPluginBehaviorArrayController.m; sourceTree = "<group>"; }; 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPluginBehaviorArrayController.m; sourceTree = "<group>"; };
8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; }; 8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; }; 8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; };
8384917618084D9F00E7332D /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = "<group>"; };
83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorToValueTransformer.h; sourceTree = "<group>"; }; 83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorToValueTransformer.h; sourceTree = "<group>"; };
83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorToValueTransformer.m; sourceTree = "<group>"; }; 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorToValueTransformer.m; sourceTree = "<group>"; };
83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; }; 83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; };
@ -117,8 +126,6 @@
83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = "<group>"; }; 83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = "<group>"; };
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = "<group>"; }; 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = "<group>"; };
83F27E651810DD3A00CEF538 /* appearance@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "appearance@2x.png"; path = "Icons/appearance@2x.png"; sourceTree = "<group>"; }; 83F27E651810DD3A00CEF538 /* appearance@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "appearance@2x.png"; path = "Icons/appearance@2x.png"; sourceTree = "<group>"; };
83F27E661810DD3A00CEF538 /* growl@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "growl@2x.png"; path = "Icons/growl@2x.png"; sourceTree = "<group>"; };
83F27E671810DD3A00CEF538 /* lastfm@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "lastfm@2x.png"; path = "Icons/lastfm@2x.png"; sourceTree = "<group>"; };
83F27E681810DD3A00CEF538 /* midi@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "midi@2x.png"; path = "Icons/midi@2x.png"; sourceTree = "<group>"; }; 83F27E681810DD3A00CEF538 /* midi@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "midi@2x.png"; path = "Icons/midi@2x.png"; sourceTree = "<group>"; };
83F27E691810DD3A00CEF538 /* playlist@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "playlist@2x.png"; path = "Icons/playlist@2x.png"; sourceTree = "<group>"; }; 83F27E691810DD3A00CEF538 /* playlist@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "playlist@2x.png"; path = "Icons/playlist@2x.png"; sourceTree = "<group>"; };
83F27E6A1810DD3A00CEF538 /* updates@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "updates@2x.png"; path = "Icons/updates@2x.png"; sourceTree = "<group>"; }; 83F27E6A1810DD3A00CEF538 /* updates@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "updates@2x.png"; path = "Icons/updates@2x.png"; sourceTree = "<group>"; };
@ -192,6 +199,7 @@
08FB77AFFE84173DC02AAC07 /* Classes */ = { 08FB77AFFE84173DC02AAC07 /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8307D30828604ECF000FF8EB /* PlaylistController.h */,
83F27E711810E41A00CEF538 /* Transformers */, 83F27E711810E41A00CEF538 /* Transformers */,
8384913618081ECB00E7332D /* Logging.h */, 8384913618081ECB00E7332D /* Logging.h */,
83D34B7D27A10FA700784D34 /* HeadphoneFilter.h */, 83D34B7D27A10FA700784D34 /* HeadphoneFilter.h */,
@ -235,6 +243,8 @@
17C6433E0B8A783F00C53518 /* OutputPane.m */, 17C6433E0B8A783F00C53518 /* OutputPane.m */,
83B06727180D85B8008E3612 /* MIDIPane.h */, 83B06727180D85B8008E3612 /* MIDIPane.h */,
83B06728180D85B8008E3612 /* MIDIPane.m */, 83B06728180D85B8008E3612 /* MIDIPane.m */,
8307D309286057B5000FF8EB /* GeneralPane.h */,
8307D30A286057B5000FF8EB /* GeneralPane.m */,
); );
name = Panes; name = Panes;
sourceTree = "<group>"; sourceTree = "<group>";
@ -254,6 +264,8 @@
8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */, 8372053618E3DEAF007EFAD4 /* ResamplerBehaviorArrayController.m */,
83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */, 83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */,
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */, 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */,
8307D30328604D8B000FF8EB /* SandboxPathBehaviorController.h */,
8307D30428604D8B000FF8EB /* SandboxPathBehaviorController.m */,
); );
name = Custom; name = Custom;
sourceTree = "<group>"; sourceTree = "<group>";
@ -300,21 +312,21 @@
8E07ABD90AAC95AF00A4B32F /* Icons */ = { 8E07ABD90AAC95AF00A4B32F /* Icons */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
83F27E651810DD3A00CEF538 /* appearance@2x.png */,
83F27E661810DD3A00CEF538 /* growl@2x.png */,
83F27E671810DD3A00CEF538 /* lastfm@2x.png */,
83F27E681810DD3A00CEF538 /* midi@2x.png */,
83F27E691810DD3A00CEF538 /* playlist@2x.png */,
83F27E6A1810DD3A00CEF538 /* updates@2x.png */,
83B0672A180D8B39008E3612 /* midi.png */,
8384917518084D9F00E7332D /* appearance.png */, 8384917518084D9F00E7332D /* appearance.png */,
8384917618084D9F00E7332D /* growl.png */, 83F27E651810DD3A00CEF538 /* appearance@2x.png */,
17C7E5AF0DCCC30A003CBCF7 /* playlist.png */,
17E78A7D0D68BE3C005C5A59 /* file_tree.png */, 17E78A7D0D68BE3C005C5A59 /* file_tree.png */,
1766C7A70B912A71004A7AE4 /* lastfm.png */, 8307D31428606EAF000FF8EB /* general.png */,
17C643680B8A788000C53518 /* output.png */, 8307D31228606EAF000FF8EB /* general@2x.png */,
8E15A86B0B894768006DC802 /* updates.png */, 8307D31528606EAF000FF8EB /* growl.png */,
8307D31328606EAF000FF8EB /* growl@2x.png */,
8E07ABDB0AAC95BC00A4B32F /* hot_keys.png */, 8E07ABDB0AAC95BC00A4B32F /* hot_keys.png */,
83B0672A180D8B39008E3612 /* midi.png */,
83F27E681810DD3A00CEF538 /* midi@2x.png */,
17C643680B8A788000C53518 /* output.png */,
17C7E5AF0DCCC30A003CBCF7 /* playlist.png */,
83F27E691810DD3A00CEF538 /* playlist@2x.png */,
8E15A86B0B894768006DC802 /* updates.png */,
83F27E6A1810DD3A00CEF538 /* updates@2x.png */,
); );
name = Icons; name = Icons;
sourceTree = "<group>"; sourceTree = "<group>";
@ -425,21 +437,21 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8307D31628606EAF000FF8EB /* general@2x.png in Resources */,
83B0672B180D8B39008E3612 /* midi.png in Resources */, 83B0672B180D8B39008E3612 /* midi.png in Resources */,
83F27E6D1810DD3A00CEF538 /* lastfm@2x.png in Resources */,
178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */, 178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */,
8307D31928606EAF000FF8EB /* growl.png in Resources */,
8E07ABDD0AAC95BC00A4B32F /* hot_keys.png in Resources */, 8E07ABDD0AAC95BC00A4B32F /* hot_keys.png in Resources */,
8384917818084D9F00E7332D /* growl.png in Resources */, 8307D31728606EAF000FF8EB /* growl@2x.png in Resources */,
8E15A86C0B894768006DC802 /* updates.png in Resources */, 8E15A86C0B894768006DC802 /* updates.png in Resources */,
8384917718084D9F00E7332D /* appearance.png in Resources */, 8384917718084D9F00E7332D /* appearance.png in Resources */,
17C643690B8A788000C53518 /* output.png in Resources */, 17C643690B8A788000C53518 /* output.png in Resources */,
1766C7A80B912A71004A7AE4 /* lastfm.png in Resources */,
83F27E6F1810DD3A00CEF538 /* playlist@2x.png in Resources */, 83F27E6F1810DD3A00CEF538 /* playlist@2x.png in Resources */,
17E41DB80C130AA500AC744D /* Localizable.strings in Resources */, 17E41DB80C130AA500AC744D /* Localizable.strings in Resources */,
8307D31828606EAF000FF8EB /* general.png in Resources */,
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */, 17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */,
83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */, 83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */,
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */, 17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */,
83F27E6C1810DD3A00CEF538 /* growl@2x.png in Resources */,
83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */, 83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */,
17C7E5B00DCCC30A003CBCF7 /* playlist.png in Resources */, 17C7E5B00DCCC30A003CBCF7 /* playlist.png in Resources */,
83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */, 83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */,
@ -461,6 +473,8 @@
8E07AA8A0AAC8EA200A4B32F /* GeneralPreferencesPlugin.m in Sources */, 8E07AA8A0AAC8EA200A4B32F /* GeneralPreferencesPlugin.m in Sources */,
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */, 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */,
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */, 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
8307D30528604D8B000FF8EB /* SandboxPathBehaviorController.m in Sources */,
8307D30B286057B5000FF8EB /* GeneralPane.m in Sources */,
83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */, 83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */,
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */, 837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */,
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */, 17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,

View File

@ -0,0 +1,19 @@
//
// SandboxPathBehaviorController.h
// Preferences
//
// Created by Christopher Snowhill on 6/19/22.
//
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface SandboxPathBehaviorController : NSArrayController
- (void)addUrl:(NSURL *)url;
- (void)removePath:(NSString *)path;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,113 @@
//
// SandboxPathBehaviorController.m
// Preferences
//
// Created by Christopher Snowhill on 6/19/22.
//
#import "PreferencePanePlugin.h"
#import "SandboxPathBehaviorController.h"
#import "PlaylistController.h"
#import <CoreData/CoreData.h>
#import "Logging.h"
#import "PlaylistController.h"
@interface SandboxToken : NSManagedObject
@property(nonatomic, strong) NSString *path;
@property(nonatomic, strong) NSData *bookmark;
@end
@implementation SandboxPathBehaviorController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:YES];
[self setSortDescriptors:@[sortDescriptor]];
if([NSApp respondsToSelector:@selector(sharedPersistentContainer)]) {
NSPersistentContainer *pc = [NSApp sharedPersistentContainer];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
NSError *error = nil;
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results && [results count] > 0) {
for(SandboxToken *token in results) {
BOOL isStale = YES;
NSError *err = nil;
NSURL *bookmarkUrl = [NSURL URLByResolvingBookmarkData:token.bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&err];
if(!bookmarkUrl) {
ALog(@"Stale bookmark for path: %@, with error: %@", token.path, [err localizedDescription]);
continue;
}
[self addObject:@{ @"path": token.path, @"valid": (isStale ? NSLocalizedPrefString(@"ValidNo") : NSLocalizedPrefString(@"ValidYes")) }];
}
}
}
}
- (void)addUrl:(NSURL *)url {
NSError *err = nil;
NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&err];
if(!bookmark && err) {
ALog(@"Failed to add bookmark for URL: %@, with error: %@", url, [err localizedDescription]);
return;
}
if(![NSApp respondsToSelector:@selector(sharedPersistentContainer)])
return;
NSPersistentContainer *pc = [NSApp sharedPersistentContainer];
SandboxToken *token = [NSEntityDescription insertNewObjectForEntityForName:@"SandboxToken" inManagedObjectContext:pc.viewContext];
if(token) {
token.path = [url path];
token.bookmark = bookmark;
[pc.viewContext save:&err];
if(err) {
ALog(@"Error saving bookmark: %@", [err localizedDescription]);
[pc.viewContext deleteObject:token];
} else {
[self addObject:@{ @"path": [url path], @"valid": NSLocalizedPrefString(@"ValidYes") }];
}
}
}
- (void)removePath:(NSString *)path {
if(![NSApp respondsToSelector:@selector(sharedPersistentContainer)])
return;
NSPersistentContainer *pc = [NSApp sharedPersistentContainer];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path == %@", path];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
request.predicate = predicate;
NSError *error = nil;
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results && [results count] > 0) {
for(SandboxToken *token in results) {
[pc.viewContext deleteObject:token];
}
}
NSArray *objects = [self arrangedObjects];
for(NSDictionary *obj in objects) {
if([[obj objectForKey:@"path"] isEqualToString:path]) {
[self removeObject:obj];
break;
}
}
}
@end

View File

@ -8,13 +8,18 @@
"Updates" = "Updates"; "Updates" = "Updates";
"Last.fm" = "Last.fm"; "Last.fm" = "Last.fm";
"Scrobble" = "Scrobble";
"Playlist" = "Playlist"; "Playlist" = "Playlist";
"Growl" = "Growl"; "Growl" = "Growl";
"Appearance" = "Appearance"; "Appearance" = "Appearance";
"MIDI" = "MIDI"; "MIDI" = "MIDI";
"General" = "General";
"Press Key..." = "Press Key..."; "Press Key..." = "Press Key...";
"Clear playlist and play" = "Clear playlist and play"; "Clear playlist and play" = "Clear playlist and play";
"Enqueue" = "Enqueue"; "Enqueue" = "Enqueue";
"Enqueue and play" = "Enqueue and play"; "Enqueue and play" = "Enqueue and play";
"ValidNo" = "No";
"ValidYes" = "Yes";

View File

@ -8,13 +8,18 @@
"Updates" = "Updates"; "Updates" = "Updates";
"Last.fm" = "Last.fm"; "Last.fm" = "Last.fm";
"Scrobble" = "Scrobble";
"Playlist" = "Playlist"; "Playlist" = "Playlist";
"Growl" = "Growl"; "Growl" = "Growl";
"Appearance" = "Appearance"; "Appearance" = "Appearance";
"MIDI" = "MIDI"; "MIDI" = "MIDI";
"General" = "General";
"Press Key..." = "Press Key..."; "Press Key..." = "Press Key...";
"Clear playlist and play" = "Clear playlist and play"; "Clear playlist and play" = "Clear playlist and play";
"Enqueue" = "Enqueue"; "Enqueue" = "Enqueue";
"Enqueue and play" = "Enqueue and play"; "Enqueue and play" = "Enqueue and play";
"ValidNo" = "No";
"ValidYes" = "Yes";

View File

@ -134,8 +134,9 @@
if([lastPane isEqualToString:NSLocalizedPrefString(@"Growl")]) { if([lastPane isEqualToString:NSLocalizedPrefString(@"Growl")]) {
lastPane = NSLocalizedPrefString(@"Notifications"); lastPane = NSLocalizedPrefString(@"Notifications");
} }
if([lastPane isEqualToString:NSLocalizedPrefString(@"Last.fm")]) { if([lastPane isEqualToString:NSLocalizedPrefString(@"Last.fm")] ||
lastPane = NSLocalizedPrefString(@"Scrobble"); [lastPane isEqualToString:NSLocalizedPrefString(@"Scrobble")]) {
lastPane = NSLocalizedPrefString(@"General");
} }
if(nil == lastPane) { if(nil == lastPane) {
if(0 >= [preferencePaneOrder count]) { if(0 >= [preferencePaneOrder count]) {

26
Utils/SandboxBroker.h Normal file
View File

@ -0,0 +1,26 @@
//
// SandboxBroker.h
// Cog
//
// Created by Christopher Snowhill on 6/20/22.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SandboxBroker : NSObject {
NSMutableArray *storage;
}
+ (SandboxBroker *)sharedSandboxBroker;
- (id)init;
- (void)shutdown;
- (void)beginFolderAccess:(NSURL *)fileUrl;
- (void)endFolderAccess:(NSURL *)fileUrl;
@end
NS_ASSUME_NONNULL_END

237
Utils/SandboxBroker.m Normal file
View File

@ -0,0 +1,237 @@
//
// SandboxBroker.m
// Cog
//
// Created by Christopher Snowhill on 6/20/22.
//
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "SandboxBroker.h"
#import "Logging.h"
#import "Cog-Swift.h"
#import "PlaylistController.h"
static SandboxBroker *__sharedSandboxBroker = nil;
@interface NSApplication (SandboxBrokerExtension)
- (SandboxBroker *)sharedSandboxBroker;
@end
@implementation NSApplication (SandboxBrokerExtension)
- (SandboxBroker *)sharedSandboxBroker {
return __sharedSandboxBroker;
}
@end
static NSURL *urlWithoutFragment(NSURL *u) {
NSString *s = [u path];
NSString *lastComponent = [u lastPathComponent];
// Find that last component in the string from the end to make sure
// to get the last one
NSRange fragmentRange = [s rangeOfString:lastComponent
options:NSBackwardsSearch];
// Chop the fragment.
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
return [NSURL fileURLWithPath:newURLString];
}
@interface SandboxEntry : NSObject {
SandboxToken *_token;
NSInteger _refCount;
NSURL *_secureUrl;
};
@property(readonly) SandboxToken *token;
@property NSURL *secureUrl;
@property(readonly) NSString *path;
@property NSInteger refCount;
- (id)initWithToken:(SandboxToken *)token;
@end
@implementation SandboxEntry
- (id)initWithToken:(SandboxToken *)token {
SandboxEntry *obj = [super init];
if(obj) {
obj->_refCount = 1;
obj->_secureUrl = nil;
obj->_token = token;
}
return obj;
}
- (NSInteger)refCount {
return _refCount;
}
- (void)setRefCount:(NSInteger)refCount {
_refCount = refCount;
}
- (NSURL *)secureUrl {
return _secureUrl;
}
- (void)setSecureUrl:(NSURL *)url {
_secureUrl = url;
}
- (SandboxToken *)token {
return _token;
}
- (NSString *)path {
return _token.path;
}
@end
@implementation SandboxBroker
+ (id)sharedSandboxBroker {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__sharedSandboxBroker = [[self alloc] init];
});
return [NSApp sharedSandboxBroker];
}
- (id)init {
id _self = [super init];
if(_self) {
storage = [[NSMutableArray alloc] init];
}
return _self;
}
- (void)shutdown {
for(SandboxEntry *obj in storage) {
if([obj secureUrl]) {
[[obj secureUrl] stopAccessingSecurityScopedResource];
}
}
}
- (void)recursivePathTest:(NSURL *)url removing:(BOOL)removing {
NSArray *pathComponents = [url pathComponents];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:NO];
for(size_t i = 1; i <= [pathComponents count]; ++i) {
NSArray *partialComponents = [pathComponents subarrayWithRange:NSMakeRange(0, i)];
NSURL *partialUrl = [NSURL fileURLWithPathComponents:partialComponents];
NSString *matchString = [[partialUrl path] stringByAppendingString:@"*"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path like %@", matchString];
NSArray *matchingObjects = [storage filteredArrayUsingPredicate:predicate];
if(matchingObjects && [matchingObjects count] > 0) {
if([matchingObjects count] > 1) {
matchingObjects = [matchingObjects sortedArrayUsingDescriptors:@[sortDescriptor]];
}
for(SandboxEntry *entry in matchingObjects) {
if([entry.path isEqualToString:[partialUrl path]]) {
if(!removing) {
entry.refCount += 1;
return;
} else {
if(entry.refCount > 1) {
entry.refCount -= 1;
return;
} else {
if(entry.secureUrl) {
[entry.secureUrl stopAccessingSecurityScopedResource];
entry.secureUrl = nil;
}
entry.refCount = 0;
[storage removeObject:entry];
return;
}
}
}
}
}
}
if(removing) return;
NSPersistentContainer *pc = [NSApp sharedPersistentContainer];
for(size_t i = 1; i <= [pathComponents count]; ++i) {
NSArray *partialComponents = [pathComponents subarrayWithRange:NSMakeRange(0, i)];
NSURL *partialUrl = [NSURL fileURLWithPathComponents:partialComponents];
NSString *matchString = [[partialUrl path] stringByAppendingString:@"*"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path like %@", matchString];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
request.predicate = predicate;
request.sortDescriptors = @[sortDescriptor];
NSError *error = nil;
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
if(results && [results count] > 0) {
for(SandboxToken *token in results) {
if([token.path isEqualToString:[partialUrl path]]) {
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
[storage addObject:entry];
BOOL isStale;
NSError *err = nil;
NSURL *secureUrl = [NSURL URLByResolvingBookmarkData:token.bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&err];
if(!secureUrl && err) {
ALog(@"Failed to access bookmark for URL: %@, error: %@", token.path, [err localizedDescription]);
return;
}
entry.secureUrl = secureUrl;
[secureUrl startAccessingSecurityScopedResource];
return;
}
}
}
}
return;
}
- (void)beginFolderAccess:(NSURL *)fileUrl {
NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent];
if(![folderUrl isFileURL]) return;
if(![NSApp respondsToSelector:@selector(sharedPersistentContainer)]) return;
@synchronized(self) {
[self recursivePathTest:folderUrl removing:NO];
}
}
- (void)endFolderAccess:(NSURL *)fileUrl {
NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent];
if(![folderUrl isFileURL]) return;
@synchronized(self) {
[self recursivePathTest:folderUrl removing:YES];
}
}
@end