diff --git a/Application/AppController.h b/Application/AppController.h index d035ba8ea..356692583 100644 --- a/Application/AppController.h +++ b/Application/AppController.h @@ -7,6 +7,7 @@ @class PlaylistController; @class PlaylistView; @class PlaylistLoader; +@class PreferencesController; @interface AppController : NSObject { IBOutlet NSObjectController *currentEntryController; @@ -47,6 +48,8 @@ IBOutlet FileTreeViewController *fileTreeViewController; + IBOutlet PreferencesController *preferencesController; + NSOperationQueue *queue; // Since we are the app delegate, we take care of the op queue NSMutableSet *expandedNodes; @@ -94,6 +97,9 @@ - (IBAction)toggleMiniMode:(id)sender; - (IBAction)toggleToolbarStyle:(id)sender; +- (void)showPathSuggester; ++ (void)globalShowPathSuggester; + @property NSWindow *mainWindow; @property NSWindow *miniWindow; diff --git a/Application/AppController.m b/Application/AppController.m index e4843358d..419da0abe 100644 --- a/Application/AppController.m +++ b/Application/AppController.m @@ -28,12 +28,16 @@ #import "Shortcuts.h" #import +#import "PreferencesController.h" + @import Firebase; void *kAppControllerContext = &kAppControllerContext; BOOL kAppControllerShuttingDown = NO; +static AppController *kAppController = nil; + @implementation AppController { BOOL _isFullToolbarStyle; } @@ -70,6 +74,8 @@ BOOL kAppControllerShuttingDown = NO; [self initDefaults]; queue = [[NSOperationQueue alloc] init]; + + kAppController = self; } return self; @@ -705,4 +711,12 @@ BOOL kAppControllerShuttingDown = NO; } } +- (void)showPathSuggester { + [preferencesController showPathSuggester:self]; +} + ++ (void)globalShowPathSuggester { + [kAppController showPathSuggester]; +} + @end diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index eef85baee..ff413ce55 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -25,17 +25,17 @@ - + - + - - + + - + @@ -229,7 +229,7 @@ - + @@ -316,7 +316,7 @@ - + @@ -401,7 +401,7 @@ - + @@ -441,7 +441,7 @@ - + @@ -628,7 +628,7 @@ - + @@ -641,7 +641,7 @@ - + @@ -1970,6 +1970,7 @@ Gw + @@ -2004,6 +2005,7 @@ Gw + diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 29369198f..5f5922682 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -1025,6 +1025,7 @@ 8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = ""; }; 839DA7CB274A2D4C001B18E5 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Merge.h"; sourceTree = ""; }; 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Merge.m"; sourceTree = ""; }; + 839E3B53286595D700880EA2 /* GeneralPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneralPane.h; path = Preferences/Preferences/GeneralPane.h; sourceTree = ""; }; 83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ColorToValueTransformer.m; path = Preferences/Preferences/ColorToValueTransformer.m; sourceTree = ""; }; 83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ColorToValueTransformer.h; path = Preferences/Preferences/ColorToValueTransformer.h; sourceTree = ""; }; 83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = ""; }; @@ -1883,6 +1884,7 @@ 8E07AAEA0AAC90DC00A4B32F /* Preferences */ = { isa = PBXGroup; children = ( + 839E3B53286595D700880EA2 /* GeneralPane.h */, ED69CF0925BE74BB0090B90D /* Shortcuts.h */, 17D1B2610F633D2C00694C57 /* PreferencePanePlugin.h */, 17D1B25B0F633A4F00694C57 /* PreferencePluginController.h */, diff --git a/FileTree/FileTreeDataSource.m b/FileTree/FileTreeDataSource.m index 03947eaf9..ac329dd06 100644 --- a/FileTree/FileTreeDataSource.m +++ b/FileTree/FileTreeDataSource.m @@ -15,6 +15,8 @@ #import "SandboxBroker.h" +#import "AppController.h" + static void *kFileTreeDataSourceContext = &kFileTreeDataSourceContext; static NSURL *defaultMusicDirectory(void) { @@ -75,6 +77,10 @@ static NSURL *defaultMusicDirectory(void) { if(url != nil) { [[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString] forKey:@"fileTreeRootURL"]; + + if(![[SandboxBroker sharedSandboxBroker] areAllPathsSafe:@[url]]) { + [AppController globalShowPathSuggester]; + } } } diff --git a/Playlist/PlaylistController.h b/Playlist/PlaylistController.h index 0976ef38e..a5e0195aa 100644 --- a/Playlist/PlaylistController.h +++ b/Playlist/PlaylistController.h @@ -18,6 +18,7 @@ @class PlaylistEntry; @class SpotlightWindowController; @class PlaybackController; +@class AppController; typedef NS_ENUM(NSInteger, RepeatMode) { RepeatModeNoRepeat = 0, @@ -43,6 +44,7 @@ typedef NS_ENUM(NSInteger, URLOrigin) { IBOutlet PlaylistLoader *playlistLoader; IBOutlet SpotlightWindowController *spotlightWindowController; IBOutlet PlaybackController *playbackController; + IBOutlet AppController *appController; NSValueTransformer *statusImageTransformer; diff --git a/Playlist/PlaylistController.m b/Playlist/PlaylistController.m index ef3376f08..019c912a7 100644 --- a/Playlist/PlaylistController.m +++ b/Playlist/PlaylistController.m @@ -23,6 +23,9 @@ #import "Cog-Swift.h" +#import "AppController.h" +#import "SandboxBroker.h" + #define UNDO_STACK_LIMIT 0 extern BOOL kAppControllerShuttingDown; @@ -1677,6 +1680,11 @@ static void *playlistControllerContext = &playlistControllerContext; [self firstSawTrack:pe]; } + NSArray *nsurls = [urls valueForKey:@"url"]; + if(![[SandboxBroker sharedSandboxBroker] areAllPathsSafe:nsurls]) { + [appController showPathSuggester]; + } + CGEventRef event = CGEventCreate(NULL); CGEventFlags mods = CGEventGetFlags(event); CFRelease(event); diff --git a/Preferences/PreferencePanePlugin.h b/Preferences/PreferencePanePlugin.h index 0c29057bf..13af84002 100644 --- a/Preferences/PreferencePanePlugin.h +++ b/Preferences/PreferencePanePlugin.h @@ -17,6 +17,9 @@ @property(readonly, copy) NSString *title; @property(readonly) NSImage *icon; +@optional +- (IBAction)showPathSuggester:(id)sender; + @end @protocol PreferencePanePlugin diff --git a/Preferences/Preferences/MIDIPane.m b/Preferences/Preferences/MIDIPane.m index b79363900..f6ef876dd 100644 --- a/Preferences/Preferences/MIDIPane.m +++ b/Preferences/Preferences/MIDIPane.m @@ -8,6 +8,10 @@ #import "MIDIPane.h" +#import "SandboxBroker.h" + +#import "AppController.h" + @implementation MIDIPane - (NSString *)title { @@ -34,6 +38,13 @@ NSInteger result = [panel runModal]; if(result == NSModalResponseOK) { [[NSUserDefaults standardUserDefaults] setValue:[[panel URL] path] forKey:@"soundFontPath"]; + + id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); + NSURL *pathUrl = [panel URL]; + if(![[sandboxBrokerClass sharedSandboxBroker] areAllPathsSafe:@[pathUrl]]) { + id appControllerClass = NSClassFromString(@"AppController"); + [appControllerClass globalShowPathSuggester]; + } } } diff --git a/Preferences/Preferences/PathSuggester.m b/Preferences/Preferences/PathSuggester.m index 38e321865..fe0223243 100644 --- a/Preferences/Preferences/PathSuggester.m +++ b/Preferences/Preferences/PathSuggester.m @@ -95,8 +95,21 @@ static NSURL *defaultMoviesDirectory(void) { NSMutableArray *items = [[NSMutableArray alloc] init]; NSMutableArray *itemPaths = [[NSMutableArray alloc] init]; - for(PlaylistEntry *pe in results) { - NSURL *url = [sandboxBrokerClass urlWithoutFragment:pe.url]; + NSMutableArray *array = [[results valueForKey:@"url"] mutableCopy]; + + // Add other system paths to this setting + NSString *fileTreePath = [[NSUserDefaults standardUserDefaults] stringForKey:@"fileTreeRootURL"]; + if(fileTreePath && [fileTreePath length]) { + [array addObject:[NSURL URLWithString:fileTreePath]]; + } + + NSString *soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; + if(soundFontPath && [soundFontPath length]) { + [array addObject:[NSURL fileURLWithPath:soundFontPath]]; + } + + for(NSURL *fileUrl in array) { + NSURL *url = [sandboxBrokerClass urlWithoutFragment:fileUrl]; if([sandboxBrokerClass isPath:url aSubdirectoryOf:defaultMusic] || [sandboxBrokerClass isPath:url aSubdirectoryOf:defaultDownloads] || diff --git a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj index 070a80d88..cc920f0b2 100644 --- a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj +++ b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj @@ -116,6 +116,7 @@ 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPluginBehaviorArrayController.m; sourceTree = ""; }; 8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = ""; }; + 839E3B5728659B2700880EA2 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppController.h; path = ../../Application/AppController.h; sourceTree = ""; }; 83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorToValueTransformer.h; sourceTree = ""; }; 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorToValueTransformer.m; sourceTree = ""; }; 83AC57372861A54D009D6F50 /* PathSuggester.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PathSuggester.h; sourceTree = ""; }; @@ -202,6 +203,7 @@ 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( + 839E3B5728659B2700880EA2 /* AppController.h */, 83AC573E2861B77E009D6F50 /* SandboxBroker.h */, 8307D30828604ECF000FF8EB /* PlaylistController.h */, 83F27E711810E41A00CEF538 /* Transformers */, @@ -287,7 +289,6 @@ 32C88E010371C26100C91783 /* Other Sources */ = { isa = PBXGroup; children = ( - 8363BABF284E450E00E5C9DD /* pffft.h */, ED69CF0B25BE75330090B90D /* Shortcuts.h */, 32DBCF630370AF2F00C91783 /* Preferences_Prefix.pch */, ); diff --git a/Preferences/PreferencesController.h b/Preferences/PreferencesController.h index 44f1f2e43..1c71c8004 100644 --- a/Preferences/PreferencesController.h +++ b/Preferences/PreferencesController.h @@ -14,5 +14,6 @@ } - (IBAction)showPreferences:(id)sender; +- (IBAction)showPathSuggester:(id)sender; @end diff --git a/Preferences/PreferencesController.m b/Preferences/PreferencesController.m index ad6956715..717a81a3b 100644 --- a/Preferences/PreferencesController.m +++ b/Preferences/PreferencesController.m @@ -12,7 +12,7 @@ @implementation PreferencesController -- (IBAction)showPreferences:(id)sender { +- (void)initWindow { if(nil == window) { // Determine path to the sample preference panes NSString *pluginPath = [[NSBundle mainBundle] pathForResource:@"Preferences" ofType:@"preferencePane"]; @@ -22,9 +22,20 @@ window = [[PreferencesWindow alloc] initWithPreferencePanes:[pluginController preferencePanes]]; } +} + +- (IBAction)showPreferences:(id)sender { + [self initWindow]; // Show the preferences window. [window show]; } +- (IBAction)showPathSuggester:(id)sender { + [self initWindow]; + + // Show the path suggester + [window showPathSuggester]; +} + @end diff --git a/Preferences/PreferencesWindow.h b/Preferences/PreferencesWindow.h index eb6a859e4..da494f56e 100644 --- a/Preferences/PreferencesWindow.h +++ b/Preferences/PreferencesWindow.h @@ -22,5 +22,6 @@ defer:(BOOL)flag NS_UNAVAILABLE; - (void)show; +- (void)showPathSuggester; @end diff --git a/Preferences/PreferencesWindow.m b/Preferences/PreferencesWindow.m index c46e4151a..6d7083dd8 100644 --- a/Preferences/PreferencesWindow.m +++ b/Preferences/PreferencesWindow.m @@ -151,6 +151,19 @@ [self makeKeyAndOrderFront:self]; } +- (void)showPathSuggester { + NSString *name = NSLocalizedPrefString(@"General"); + + [self loadPaneNamed:name display:NO]; + + [self makeKeyAndOrderFront:self]; + + id pane = preferencePanes[name]; + if(pane && [pane respondsToSelector:@selector(showPathSuggester:)]) { + [pane showPathSuggester:self]; + } +} + // Close on Esc pressed. - (void)cancelOperation:(id)sender { [self close]; diff --git a/Utils/SandboxBroker.h b/Utils/SandboxBroker.h index 5f9812c3b..5272c466c 100644 --- a/Utils/SandboxBroker.h +++ b/Utils/SandboxBroker.h @@ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN - (const void *)beginFolderAccess:(NSURL *)fileUrl; - (void)endFolderAccess:(const void *)handle; +- (BOOL)areAllPathsSafe:(NSArray *)urls; + @end NS_ASSUME_NONNULL_END diff --git a/Utils/SandboxBroker.m b/Utils/SandboxBroker.m index 3ae9a3435..c160ecfe6 100644 --- a/Utils/SandboxBroker.m +++ b/Utils/SandboxBroker.m @@ -151,13 +151,6 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc } - (SandboxEntry *)recursivePathTest:(NSURL *)url { - for(SandboxEntry *entry in storage) { - if(entry.path && [SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) { - entry.refCount += 1; - return entry; - } - } - __block SandboxEntry *ret = nil; dispatch_sync_reentrant(dispatch_get_main_queue(), ^{ @@ -194,10 +187,6 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc ret.secureUrl = secureUrl; - [storage addObject:ret]; - - [secureUrl startAccessingSecurityScopedResource]; - return ret; } @@ -208,14 +197,28 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc NSURL *folderUrl = [[SandboxBroker urlWithoutFragment:fileUrl] URLByDeletingLastPathComponent]; if(![folderUrl isFileURL]) return NULL; - SandboxEntry *entry; + SandboxEntry *_entry = nil; @synchronized(self) { - entry = [self recursivePathTest:folderUrl]; + for(SandboxEntry *entry in storage) { + if(entry.path && [SandboxBroker isPath:folderUrl aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) { + entry.refCount += 1; + _entry = entry; + break; + } + } + + if(!_entry) { + _entry = [self recursivePathTest:folderUrl]; + } } - if(entry) { - return CFBridgingRetain(entry); + if(_entry) { + [storage addObject:_entry]; + + [_entry.secureUrl startAccessingSecurityScopedResource]; + + return CFBridgingRetain(_entry); } else { return NULL; } @@ -242,4 +245,13 @@ static inline void dispatch_sync_reentrant(dispatch_queue_t queue, dispatch_bloc } } +- (BOOL)areAllPathsSafe:(NSArray *)urls { + for(NSURL *url in urls) { + if(![self recursivePathTest:url]) { + return NO; + } + } + return YES; +} + @end