Restore the File Tree, now with a chooser button

Revert "Remove the file tree, as Sandbox does not permit"

This reverts commit 02ec735687.

This also changes how the File Tree choosing works.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
lastfm
Christopher Snowhill 2022-06-30 16:57:51 -07:00
parent 830d3bf3be
commit 0832a8ea34
36 changed files with 2474 additions and 642 deletions

View File

@ -2,6 +2,7 @@
#import <Cocoa/Cocoa.h>
@class FileTreeViewController;
@class PlaybackController;
@class PlaylistController;
@class PlaylistView;
@ -18,6 +19,7 @@
IBOutlet NSWindow *mainWindow;
IBOutlet NSWindow *miniWindow;
IBOutlet NSSplitView *mainView;
IBOutlet NSSegmentedControl *playbackButtons;
IBOutlet NSButton *fileButton;
@ -44,6 +46,8 @@
IBOutlet NSWindowController *spotlightWindowController;
IBOutlet FileTreeViewController *fileTreeViewController;
IBOutlet PreferencesController *preferencesController;
NSOperationQueue *queue; // Since we are the app delegate, we take care of the op queue

View File

@ -1,7 +1,11 @@
#import "AppController.h"
#import "Cog-Swift.h"
#import "FileTreeController.h"
#import "FileTreeOutlineView.h"
#import "FileTreeViewController.h"
#import "FontSizetoLineHeightTransformer.h"
#import "OpenURLPanel.h"
#import "PathNode.h"
#import "PlaybackController.h"
#import "PlaylistController.h"
#import "PlaylistEntry.h"
@ -238,11 +242,50 @@ static AppController *kAppController = nil;
[self setFloatingMiniWindow:[[NSUserDefaults standardUserDefaults]
boolForKey:@"floatingMiniWindow"]];
// We need file tree view to restore its state here
// so attempt to access file tree view controller's root view
// to force it to read nib and create file tree view for us
//
// TODO: there probably is a more elegant way to do all this
// but i'm too stupid/tired to figure it out now
[fileTreeViewController view];
FileTreeOutlineView *outlineView = [fileTreeViewController outlineView];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeExpanded:) name:NSOutlineViewItemDidExpandNotification object:outlineView];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeCollapsed:) name:NSOutlineViewItemDidCollapseNotification object:outlineView];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidBeginNotficiation object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidStopNotficiation object:nil];
[self updateDockMenu:nil];
NSArray *expandedNodesArray = [[NSUserDefaults standardUserDefaults] valueForKey:@"fileTreeViewExpandedNodes"];
if(expandedNodesArray) {
expandedNodes = [[NSMutableSet alloc] initWithArray:expandedNodesArray];
} else {
expandedNodes = [[NSMutableSet alloc] init];
}
DLog(@"Nodes to expand: %@", [expandedNodes description]);
DLog(@"Num of rows: %ld", [outlineView numberOfRows]);
if(!outlineView) {
DLog(@"outlineView is NULL!");
}
[outlineView reloadData];
for(NSInteger i = 0; i < [outlineView numberOfRows]; i++) {
PathNode *pn = [outlineView itemAtRow:i];
NSString *str = [[pn URL] absoluteString];
if([expandedNodes containsObject:str]) {
[outlineView expandItem:pn];
}
}
[self addObserver:self
forKeyPath:@"playlistController.currentEntry"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
@ -330,6 +373,20 @@ static AppController *kAppController = nil;
}
}
- (void)nodeExpanded:(NSNotification *)notification {
PathNode *node = [[notification userInfo] objectForKey:@"NSObject"];
NSString *url = [[node URL] absoluteString];
[expandedNodes addObject:url];
}
- (void)nodeCollapsed:(NSNotification *)notification {
PathNode *node = [[notification userInfo] objectForKey:@"NSObject"];
NSString *url = [[node URL] absoluteString];
[expandedNodes removeObject:url];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
if(playbackController.progressOverall) {
[playbackController.progressOverall addObserver:self forKeyPath:@"finished" options:0 context:kAppControllerContext];
@ -403,6 +460,7 @@ static AppController *kAppController = nil;
DLog(@"Saving expanded nodes: %@", [expandedNodes description]);
[[NSUserDefaults standardUserDefaults] setValue:[expandedNodes allObjects] forKey:@"fileTreeViewExpandedNodes"];
// Workaround window not restoring it's size and position.
[miniWindow setContentSize:NSMakeSize(miniWindow.frame.size.width, 1)];
[miniWindow saveFrameUsingName:@"Mini Window"];

168
Base.lproj/FileTree.xib Normal file
View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="FileTreeViewController">
<connections>
<outlet property="fileTreeOutlineView" destination="69" id="141"/>
<outlet property="firstResponder" destination="69" id="140"/>
<outlet property="view" destination="55" id="103"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="9" userLabel="FileTreeDataSource" customClass="FileTreeDataSource">
<connections>
<outlet property="outlineView" destination="69" id="88"/>
<outlet property="pathControl" destination="65" id="109"/>
<outlet property="watcher" destination="31" id="34"/>
</connections>
</customObject>
<userDefaultsController representsSharedInstance="YES" id="27"/>
<customObject id="31" customClass="PathWatcher">
<connections>
<outlet property="delegate" destination="9" id="33"/>
</connections>
</customObject>
<customView id="55" userLabel="File Tree View">
<rect key="frame" x="0.0" y="0.0" width="300" height="400"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<pathControl focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="65">
<rect key="frame" x="76" y="374" width="224" height="26"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<pathCell key="cell" selectable="YES" enabled="NO" focusRingType="none" alignment="left" pathStyle="popUp" id="66">
<font key="font" metaFont="system"/>
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
</pathCell>
<connections>
<binding destination="27" name="value" keyPath="values.fileTreeRootURL" id="108">
<dictionary key="options">
<string key="NSValueTransformerName">StringToURLTransformer</string>
</dictionary>
</binding>
</connections>
</pathControl>
<box fixedFrame="YES" boxType="custom" borderType="line" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="147">
<rect key="frame" x="0.0" y="373" width="300" height="1"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<view key="contentView" id="Dg2-ay-LZH">
<rect key="frame" x="1" y="1" width="298" height="0.0"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<color key="borderColor" name="scrollBarColor" catalog="System" colorSpace="catalog"/>
<color key="fillColor" name="textColor" catalog="System" colorSpace="catalog"/>
<font key="titleFont" metaFont="system"/>
</box>
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="23" horizontalPageScroll="10" verticalLineScroll="23" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="64">
<rect key="frame" x="0.0" y="0.0" width="300" height="373"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="OYe-Aa-Spw">
<rect key="frame" x="0.0" y="0.0" width="300" height="373"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" columnReordering="NO" autosaveColumns="NO" autosaveName="FileTree" rowHeight="18" indentationPerLevel="14" autoresizesOutlineColumn="YES" outlineTableColumn="70" id="69" customClass="FileTreeOutlineView">
<rect key="frame" x="0.0" y="0.0" width="300" height="373"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="5"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn editable="NO" width="268" minWidth="16" maxWidth="1000" id="70">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="71" customClass="FileIconCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<connections>
<binding destination="27" name="fontSize" keyPath="values.fontSize" id="93"/>
</connections>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<binding destination="27" name="rowHeight" keyPath="values.fontSize" id="86">
<dictionary key="options">
<string key="NSValueTransformerName">FontSizetoLineHeightTransformer</string>
</dictionary>
</binding>
<outlet property="dataSource" destination="9" id="87"/>
<outlet property="delegate" destination="94" id="98"/>
<outlet property="menu" destination="110" id="121"/>
</connections>
</outlineView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="67">
<rect key="frame" x="0.0" y="362" width="306" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="68">
<rect key="frame" x="261" y="0.0" width="15" height="363"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<connections>
<outlet property="nextKeyView" destination="69" id="104"/>
</connections>
</scrollView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tqu-Wl-fBM">
<rect key="frame" x="-2" y="370" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Choose" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vRe-3U-Nxj">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="chooseRootFolder:" target="-2" id="aZ6-tK-wqz"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-310" y="119"/>
</customView>
<customObject id="94" customClass="FileTreeController">
<connections>
<outlet property="controller" destination="-2" id="106"/>
<outlet property="dataSource" destination="9" id="137"/>
<outlet property="outlineView" destination="69" id="95"/>
</connections>
</customObject>
<menu title="Menu" id="110" userLabel="ContextualMenu">
<items>
<menuItem title="Add to Playlist" tag="1" id="119">
<connections>
<action selector="addToPlaylist:" target="94" id="122"/>
</connections>
</menuItem>
<menuItem title="Set as Playlist" tag="2" id="129">
<connections>
<action selector="setAsPlaylist:" target="94" id="130"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="128"/>
<menuItem title="Show in Finder" tag="3" id="112">
<connections>
<action selector="showEntryInFinder:" target="94" id="123"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="126"/>
<menuItem title="Set as Root" tag="4" id="124">
<connections>
<action selector="setAsRoot:" target="94" id="125"/>
</connections>
</menuItem>
</items>
<connections>
<outlet property="delegate" destination="69" id="139"/>
</connections>
</menu>
</objects>
</document>

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@
172A12330F5911D20078EF0C /* RepeatTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = 172A12320F5911D20078EF0C /* RepeatTransformers.m */; };
172A123C0F5912AE0078EF0C /* ShuffleTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = 172A123B0F5912AE0078EF0C /* ShuffleTransformers.m */; };
172A12A90F59AF8A0078EF0C /* NSString+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 172A12A80F59AF8A0078EF0C /* NSString+CogSort.m */; };
173855FF0E0CC81F00488CD4 /* FileTreeOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 173855FE0E0CC81F00488CD4 /* FileTreeOutlineView.m */; };
1752C36C0F59E00100F85F28 /* PlaybackButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = 1752C36B0F59E00100F85F28 /* PlaybackButtons.m */; };
1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 1755E1F70BA0D2B600CA3560 /* PlaylistLoader.m */; };
1770429C0B8BC53600B86321 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 177042980B8BC53600B86321 /* AppController.m */; };
@ -40,8 +41,20 @@
17818A990C0B27AC001C4916 /* shn.icns in Resources */ = {isa = PBXBuildFile; fileRef = 17818A920C0B27AC001C4916 /* shn.icns */; };
17818A9A0C0B27AC001C4916 /* wav.icns in Resources */ = {isa = PBXBuildFile; fileRef = 17818A930C0B27AC001C4916 /* wav.icns */; };
17818A9B0C0B27AC001C4916 /* wv.icns in Resources */ = {isa = PBXBuildFile; fileRef = 17818A940C0B27AC001C4916 /* wv.icns */; };
1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1784560E0F631E24007E8021 /* FileTreeViewController.m */; };
178456120F631E31007E8021 /* SideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 178456110F631E31007E8021 /* SideViewController.m */; };
1791FF900CB43A2C0070BC5C /* MediaKeysApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 1791FF8E0CB43A2C0070BC5C /* MediaKeysApplication.m */; };
179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 179790DF0C087AB7001D6996 /* OpenURLPanel.m */; };
179D031E0E0CB2500064A77A /* ContainedNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D03090E0CB2500064A77A /* ContainedNode.m */; };
179D031F0E0CB2500064A77A /* ContainerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D030B0E0CB2500064A77A /* ContainerNode.m */; };
179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D030D0E0CB2500064A77A /* DirectoryNode.m */; };
179D03210E0CB2500064A77A /* FileIconCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D030F0E0CB2500064A77A /* FileIconCell.m */; };
179D03220E0CB2500064A77A /* FileNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D03110E0CB2500064A77A /* FileNode.m */; };
179D03230E0CB2500064A77A /* FileTreeDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D03130E0CB2500064A77A /* FileTreeDataSource.m */; };
179D03240E0CB2500064A77A /* FileTreeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D03150E0CB2500064A77A /* FileTreeController.m */; };
179D03260E0CB2500064A77A /* PathNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D03190E0CB2500064A77A /* PathNode.m */; };
179D03270E0CB2500064A77A /* PathWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D031B0E0CB2500064A77A /* PathWatcher.m */; };
179D03280E0CB2500064A77A /* SmartFolderNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 179D031D0E0CB2500064A77A /* SmartFolderNode.m */; };
17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CEC0B8A86010009ACB1 /* AudioToolbox.framework */; };
17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF60B8A86350009ACB1 /* AudioUnit.framework */; };
17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */; };
@ -183,6 +196,7 @@
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; };
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; };
83BC5AC120E4CE8700631CD4 /* OpenURLPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342ABD0D5FD36400E8D854 /* OpenURLPanel.xib */; };
83BC5AC220E4CE8A00631CD4 /* FileTree.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17211A7C0D68B7C500911CA9 /* FileTree.xib */; };
83BC5AC320E4CE8D00631CD4 /* SpotlightPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 178456C00F6320B5007E8021 /* SpotlightPanel.xib */; };
83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B1DA0F6330D400694C57 /* Feedback.xib */; };
83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@ -772,6 +786,8 @@
172A123B0F5912AE0078EF0C /* ShuffleTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ShuffleTransformers.m; path = Window/ShuffleTransformers.m; sourceTree = "<group>"; };
172A12A70F59AF8A0078EF0C /* NSString+CogSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+CogSort.h"; sourceTree = "<group>"; };
172A12A80F59AF8A0078EF0C /* NSString+CogSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+CogSort.m"; sourceTree = "<group>"; };
173855FD0E0CC81F00488CD4 /* FileTreeOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileTreeOutlineView.h; path = FileTree/FileTreeOutlineView.h; sourceTree = "<group>"; };
173855FE0E0CC81F00488CD4 /* FileTreeOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileTreeOutlineView.m; path = FileTree/FileTreeOutlineView.m; sourceTree = "<group>"; };
1752C36A0F59E00100F85F28 /* PlaybackButtons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaybackButtons.h; path = Window/PlaybackButtons.h; 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>"; };
@ -797,10 +813,34 @@
17818A920C0B27AC001C4916 /* shn.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = shn.icns; sourceTree = "<group>"; };
17818A930C0B27AC001C4916 /* wav.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = wav.icns; sourceTree = "<group>"; };
17818A940C0B27AC001C4916 /* wv.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = wv.icns; sourceTree = "<group>"; };
1784560D0F631E24007E8021 /* FileTreeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileTreeViewController.h; path = FileTree/FileTreeViewController.h; sourceTree = "<group>"; };
1784560E0F631E24007E8021 /* FileTreeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileTreeViewController.m; path = FileTree/FileTreeViewController.m; sourceTree = "<group>"; };
178456100F631E31007E8021 /* SideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SideViewController.h; sourceTree = "<group>"; };
178456110F631E31007E8021 /* SideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SideViewController.m; sourceTree = "<group>"; };
1791FF8D0CB43A2C0070BC5C /* MediaKeysApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaKeysApplication.h; sourceTree = "<group>"; };
1791FF8E0CB43A2C0070BC5C /* MediaKeysApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaKeysApplication.m; sourceTree = "<group>"; };
179790DE0C087AB7001D6996 /* OpenURLPanel.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OpenURLPanel.h; sourceTree = "<group>"; };
179790DF0C087AB7001D6996 /* OpenURLPanel.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OpenURLPanel.m; sourceTree = "<group>"; };
179D03080E0CB2500064A77A /* ContainedNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContainedNode.h; path = FileTree/ContainedNode.h; sourceTree = "<group>"; };
179D03090E0CB2500064A77A /* ContainedNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContainedNode.m; path = FileTree/ContainedNode.m; sourceTree = "<group>"; };
179D030A0E0CB2500064A77A /* ContainerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContainerNode.h; path = FileTree/ContainerNode.h; sourceTree = "<group>"; };
179D030B0E0CB2500064A77A /* ContainerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ContainerNode.m; path = FileTree/ContainerNode.m; sourceTree = "<group>"; };
179D030C0E0CB2500064A77A /* DirectoryNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryNode.h; path = FileTree/DirectoryNode.h; sourceTree = "<group>"; };
179D030D0E0CB2500064A77A /* DirectoryNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryNode.m; path = FileTree/DirectoryNode.m; sourceTree = "<group>"; };
179D030E0E0CB2500064A77A /* FileIconCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileIconCell.h; path = FileTree/FileIconCell.h; sourceTree = "<group>"; };
179D030F0E0CB2500064A77A /* FileIconCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileIconCell.m; path = FileTree/FileIconCell.m; sourceTree = "<group>"; };
179D03100E0CB2500064A77A /* FileNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileNode.h; path = FileTree/FileNode.h; sourceTree = "<group>"; };
179D03110E0CB2500064A77A /* FileNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileNode.m; path = FileTree/FileNode.m; sourceTree = "<group>"; };
179D03120E0CB2500064A77A /* FileTreeDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileTreeDataSource.h; path = FileTree/FileTreeDataSource.h; sourceTree = "<group>"; };
179D03130E0CB2500064A77A /* FileTreeDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileTreeDataSource.m; path = FileTree/FileTreeDataSource.m; sourceTree = "<group>"; };
179D03140E0CB2500064A77A /* FileTreeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileTreeController.h; path = FileTree/FileTreeController.h; sourceTree = "<group>"; };
179D03150E0CB2500064A77A /* FileTreeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileTreeController.m; path = FileTree/FileTreeController.m; sourceTree = "<group>"; };
179D03180E0CB2500064A77A /* PathNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathNode.h; path = FileTree/PathNode.h; sourceTree = "<group>"; };
179D03190E0CB2500064A77A /* PathNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PathNode.m; path = FileTree/PathNode.m; sourceTree = "<group>"; };
179D031A0E0CB2500064A77A /* PathWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathWatcher.h; path = FileTree/PathWatcher.h; sourceTree = "<group>"; };
179D031B0E0CB2500064A77A /* PathWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PathWatcher.m; path = FileTree/PathWatcher.m; sourceTree = "<group>"; };
179D031C0E0CB2500064A77A /* SmartFolderNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SmartFolderNode.h; path = FileTree/SmartFolderNode.h; sourceTree = "<group>"; };
179D031D0E0CB2500064A77A /* SmartFolderNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SmartFolderNode.m; path = FileTree/SmartFolderNode.m; sourceTree = "<group>"; };
17BB5CEC0B8A86010009ACB1 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = /System/Library/Frameworks/AudioToolbox.framework; sourceTree = "<absolute>"; };
17BB5CF60B8A86350009ACB1 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = /System/Library/Frameworks/AudioUnit.framework; sourceTree = "<absolute>"; };
17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = "<absolute>"; };
@ -1008,6 +1048,7 @@
83BC5AB420E4C91100631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/InfoInspector.xib; sourceTree = "<group>"; };
83BC5AB520E4C91200631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
83BC5AB620E4C91300631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/OpenURLPanel.xib; sourceTree = "<group>"; };
83BC5AB720E4C91400631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/FileTree.xib; sourceTree = "<group>"; };
83BC5AB820E4C91400631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SpotlightPanel.xib; sourceTree = "<group>"; };
83BC5AB920E4C91500631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Feedback.xib; sourceTree = "<group>"; };
83BC5AC620E4D04600631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
@ -1016,6 +1057,8 @@
83BC5ACC20E4D07700631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoInspector.strings; sourceTree = "<group>"; };
83BC5ACE20E4D09700631CD4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/OpenURLPanel.strings; sourceTree = "<group>"; };
83BC5AD020E4D09800631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/OpenURLPanel.strings; sourceTree = "<group>"; };
83BC5AD220E4D0B400631CD4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/FileTree.strings; sourceTree = "<group>"; };
83BC5AD420E4D0B600631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/FileTree.strings; sourceTree = "<group>"; };
83BC5AD620E4D0D800631CD4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
83BC5AD820E4D0D900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
83BC5ADA20E4D0E900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Feedback.strings; sourceTree = "<group>"; };
@ -1097,6 +1140,7 @@
8377C66027B8CF2300E8BC0F /* Visualization */,
8E75752A09F31D5A0080F1EE /* Playlist */,
8E07AAEA0AAC90DC00A4B32F /* Preferences */,
17DDF6400E0CB6F100A2E4AD /* FileTree */,
830C37A227B95E6000E02BB0 /* Equalizer */,
17D1B0FE0F63252900694C57 /* InfoInspector */,
569C52C50D5F2BD500BDBDC9 /* Spotlight */,
@ -1208,6 +1252,8 @@
83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */,
8381A09027C5F72F00A1C530 /* SHA256Digest.h */,
8381A09127C5F72F00A1C530 /* SHA256Digest.m */,
178456100F631E31007E8021 /* SideViewController.h */,
178456110F631E31007E8021 /* SideViewController.m */,
8370D739277419D200245CE0 /* SQLiteStore.h */,
8370D73C277419F700245CE0 /* SQLiteStore.m */,
17FAEBAA0F662985007C8707 /* ToolTipTextField.h */,
@ -1357,6 +1403,37 @@
name = InfoInspector;
sourceTree = "<group>";
};
17DDF6400E0CB6F100A2E4AD /* FileTree */ = {
isa = PBXGroup;
children = (
1784560D0F631E24007E8021 /* FileTreeViewController.h */,
1784560E0F631E24007E8021 /* FileTreeViewController.m */,
179D03140E0CB2500064A77A /* FileTreeController.h */,
179D03150E0CB2500064A77A /* FileTreeController.m */,
173855FD0E0CC81F00488CD4 /* FileTreeOutlineView.h */,
173855FE0E0CC81F00488CD4 /* FileTreeOutlineView.m */,
179D03120E0CB2500064A77A /* FileTreeDataSource.h */,
179D03130E0CB2500064A77A /* FileTreeDataSource.m */,
179D03080E0CB2500064A77A /* ContainedNode.h */,
179D03090E0CB2500064A77A /* ContainedNode.m */,
179D030A0E0CB2500064A77A /* ContainerNode.h */,
179D030B0E0CB2500064A77A /* ContainerNode.m */,
179D030C0E0CB2500064A77A /* DirectoryNode.h */,
179D030D0E0CB2500064A77A /* DirectoryNode.m */,
179D030E0E0CB2500064A77A /* FileIconCell.h */,
179D030F0E0CB2500064A77A /* FileIconCell.m */,
179D03100E0CB2500064A77A /* FileNode.h */,
179D03110E0CB2500064A77A /* FileNode.m */,
179D03180E0CB2500064A77A /* PathNode.h */,
179D03190E0CB2500064A77A /* PathNode.m */,
179D031A0E0CB2500064A77A /* PathWatcher.h */,
179D031B0E0CB2500064A77A /* PathWatcher.m */,
179D031C0E0CB2500064A77A /* SmartFolderNode.h */,
179D031D0E0CB2500064A77A /* SmartFolderNode.m */,
);
name = FileTree;
sourceTree = "<group>";
};
17E0D5D20F520E75005B6FED /* Window */ = {
isa = PBXGroup;
children = (
@ -1483,6 +1560,7 @@
17D1B0D00F6320EA00694C57 /* InfoInspector.xib */,
17342A980D5FD20B00E8D854 /* MainMenu.xib */,
17342ABD0D5FD36400E8D854 /* OpenURLPanel.xib */,
17211A7C0D68B7C500911CA9 /* FileTree.xib */,
178456C00F6320B5007E8021 /* SpotlightPanel.xib */,
17E41E060C130DFF00AC744D /* Credits.html */,
17D1B1DA0F6330D400694C57 /* Feedback.xib */,
@ -2408,6 +2486,7 @@
836F462C28207FA4005B9B87 /* PauseNormal.png in Resources */,
83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */,
83BC5AC320E4CE8D00631CD4 /* SpotlightPanel.xib in Resources */,
83BC5AC220E4CE8A00631CD4 /* FileTree.xib in Resources */,
83BC5AC120E4CE8700631CD4 /* OpenURLPanel.xib in Resources */,
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */,
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */,
@ -2562,12 +2641,23 @@
170B55940D6E5E7B006B9E92 /* StatusImageTransformer.m in Sources */,
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */,
17249F0F0D82E17700F33392 /* ToggleQueueTitleTransformer.m in Sources */,
179D031E0E0CB2500064A77A /* ContainedNode.m in Sources */,
179D031F0E0CB2500064A77A /* ContainerNode.m in Sources */,
834B05EA2859C006000B7DC0 /* TotalTimeTransformer.m in Sources */,
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */,
179D03200E0CB2500064A77A /* DirectoryNode.m in Sources */,
179D03210E0CB2500064A77A /* FileIconCell.m in Sources */,
179D03220E0CB2500064A77A /* FileNode.m in Sources */,
0A9CEA0B286152DF00E47168 /* DraggableView.swift in Sources */,
83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */,
179D03230E0CB2500064A77A /* FileTreeDataSource.m in Sources */,
179D03240E0CB2500064A77A /* FileTreeController.m in Sources */,
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */,
179D03260E0CB2500064A77A /* PathNode.m in Sources */,
179D03270E0CB2500064A77A /* PathWatcher.m in Sources */,
179D03280E0CB2500064A77A /* SmartFolderNode.m in Sources */,
8381A09227C5F72F00A1C530 /* SHA256Digest.m in Sources */,
173855FF0E0CC81F00488CD4 /* FileTreeOutlineView.m in Sources */,
07D971E60ED1DAA800E7602E /* TagEditorController.m in Sources */,
17E0D5EA0F520F02005B6FED /* MainWindow.m in Sources */,
836D28A818086386005B7299 /* MiniModeMenuTitleTransformer.m in Sources */,
@ -2590,6 +2680,8 @@
8307D30E28606148000FF8EB /* SandboxBroker.m in Sources */,
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */,
835F00BB279BD1CD00055FCF /* SecondsFormatter.m in Sources */,
1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */,
178456120F631E31007E8021 /* SideViewController.m in Sources */,
17D1B1010F63255200694C57 /* InfoWindowController.m in Sources */,
835A8FD327957310005B3C39 /* json.c in Sources */,
17D1B25D0F633A4F00694C57 /* PreferencePluginController.m in Sources */,
@ -2772,6 +2864,16 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
17211A7C0D68B7C500911CA9 /* FileTree.xib */ = {
isa = PBXVariantGroup;
children = (
83BC5AB720E4C91400631CD4 /* Base */,
83BC5AD220E4D0B400631CD4 /* es */,
83BC5AD420E4D0B600631CD4 /* en */,
);
name = FileTree.xib;
sourceTree = "<group>";
};
17342A980D5FD20B00E8D854 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (

16
FileTree/ContainedNode.h Normal file
View File

@ -0,0 +1,16 @@
//
// ContainedNode.h
// Cog
//
// Created by Vincent Spader on 10/15/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "PathNode.h"
@interface ContainedNode : PathNode {
}
@end

42
FileTree/ContainedNode.m Normal file
View File

@ -0,0 +1,42 @@
//
// ContainedNode.m
// Cog
//
// Created by Vincent Spader on 10/15/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import "ContainedNode.h"
#import "CogAudio/AudioMetadataReader.h"
@implementation ContainedNode
- (BOOL)isLeaf {
return YES;
}
- (void)setURL:(NSURL *)u {
[super setURL:u];
if([u fragment]) {
NSDictionary *metadata = [AudioMetadataReader metadataForURL:u];
NSString *title = nil;
NSString *artist = nil;
if(metadata) {
title = [metadata valueForKey:@"title"];
artist = [metadata valueForKey:@"artist"];
}
if(title && [title length]) {
if(artist && [artist length]) {
display = [[u fragment] stringByAppendingFormat:@": %@ - %@", artist, title];
} else {
display = [[u fragment] stringByAppendingFormat:@": %@", title];
}
} else {
display = [u fragment];
}
}
}
@end

16
FileTree/ContainerNode.h Normal file
View File

@ -0,0 +1,16 @@
//
// ContainerNode.h
// Cog
//
// Created by Vincent Spader on 10/15/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "PathNode.h"
@interface ContainerNode : PathNode {
}
@end

35
FileTree/ContainerNode.m Normal file
View File

@ -0,0 +1,35 @@
//
// ContainerNode.m
// Cog
//
// Created by Vincent Spader on 10/15/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import "ContainerNode.h"
#import "CogAudio/AudioContainer.h"
#import "ContainedNode.h"
#import "Logging.h"
@implementation ContainerNode
- (BOOL)isLeaf {
return NO;
}
- (void)updatePath {
NSArray *urls = [AudioContainer urlsForContainerURL:url];
NSMutableArray *paths = [[NSMutableArray alloc] init];
for(NSURL *u in urls) {
ContainedNode *node = [[ContainedNode alloc] initWithDataSource:dataSource url:u];
DLog(@"Node: %@", u);
[paths addObject:node];
}
[self setSubpaths:paths];
}
@end

15
FileTree/DirectoryNode.h Normal file
View File

@ -0,0 +1,15 @@
//
// DirectoryNode.h
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "PathNode.h"
#import <Cocoa/Cocoa.h>
@interface DirectoryNode : PathNode {
}
@end

40
FileTree/DirectoryNode.m Normal file
View File

@ -0,0 +1,40 @@
//
// DirectoryNode.m
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "DirectoryNode.h"
#import "FileNode.h"
#import "SmartFolderNode.h"
#import "NSString+FinderCompare.h"
@implementation DirectoryNode
- (BOOL)isLeaf {
return NO;
}
- (void)updatePath {
if(!url) return;
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:url
includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
options:(NSDirectoryEnumerationSkipsSubdirectoryDescendants | NSDirectoryEnumerationSkipsPackageDescendants | NSDirectoryEnumerationSkipsHiddenFiles)
errorHandler:^BOOL(NSURL *url, NSError *error) {
return NO;
}];
NSMutableArray *fullPaths = [[NSMutableArray alloc] init];
for(NSURL *theUrl in enumerator) {
[fullPaths addObject:[theUrl path]];
}
[self processPaths:[fullPaths sortedArrayUsingSelector:@selector(finderCompare:)]];
}
@end

15
FileTree/FileIconCell.h Normal file
View File

@ -0,0 +1,15 @@
//
// FileIconTextCell.h
// Cog
//
// Created by Vincent Spader on 8/20/06.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "ImageTextCell.h"
#import <Cocoa/Cocoa.h>
@interface FileIconCell : ImageTextCell {
}
@end

23
FileTree/FileIconCell.m Normal file
View File

@ -0,0 +1,23 @@
//
// FileIconTextCell.m
// Cog
//
// Created by Vincent Spader on 8/20/06.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "FileIconCell.h"
#import "PathNode.h"
@implementation FileIconCell
- (void)setObjectValue:(PathNode *)o {
if([o respondsToSelector:@selector(icon)] && [o respondsToSelector:@selector(display)]) {
[super setObjectValue:[o display]];
[super setImage:[o icon]];
} else {
[super setObjectValue:(id)o];
}
}
@end

15
FileTree/FileNode.h Normal file
View File

@ -0,0 +1,15 @@
//
// FileNode.h
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "PathNode.h"
#import <Cocoa/Cocoa.h>
@interface FileNode : PathNode {
}
@end

17
FileTree/FileNode.m Normal file
View File

@ -0,0 +1,17 @@
//
// FileNode.m
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "FileNode.h"
@implementation FileNode
- (BOOL)isLeaf {
return YES;
}
@end

View File

@ -0,0 +1,26 @@
//
// FileTreeController.h
// Cog
//
// Created by Vincent Spader on 2/17/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "FileTreeDataSource.h"
#import <Cocoa/Cocoa.h>
@class SideViewController;
@interface FileTreeController : NSObject {
IBOutlet SideViewController *controller;
IBOutlet NSOutlineView *outlineView;
IBOutlet FileTreeDataSource *dataSource;
}
- (IBAction)addToPlaylist:(id)sender;
- (IBAction)addToPlaylistExternal:(id)sender;
- (IBAction)setAsPlaylist:(id)sender;
- (IBAction)playPauseResume:(NSObject *)id;
- (IBAction)showEntryInFinder:(id)sender;
- (IBAction)setAsRoot:(id)sender;
@end

View File

@ -0,0 +1,85 @@
//
// FileTreeController.m
// Cog
//
// Created by Vincent Spader on 2/17/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "FileTreeController.h"
#import "PlaylistController.h"
#import "SideViewController.h"
@implementation FileTreeController
- (IBAction)addToPlaylist:(id)sender {
[self doAddToPlaylist:sender origin:URLOriginInternal];
}
- (void)doAddToPlaylist:(id)sender origin:(URLOrigin)origin {
NSUInteger index;
NSIndexSet *selectedIndexes = [outlineView selectedRowIndexes];
NSMutableArray *urls = [[NSMutableArray alloc] init];
for(index = [selectedIndexes firstIndex];
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex:index]) {
[urls addObject:[[outlineView itemAtRow:index] URL]];
}
[controller doAddToPlaylist:urls origin:origin];
}
- (void)addToPlaylistExternal:(id)sender {
[self doAddToPlaylist:sender origin:URLOriginExternal];
}
- (IBAction)setAsPlaylist:(id)sender {
[controller clear:sender];
[self addToPlaylist:sender];
}
- (IBAction)playPauseResume:(NSObject *)id {
[controller playPauseResume:id];
}
- (IBAction)showEntryInFinder:(id)sender {
NSUInteger index;
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSIndexSet *selectedIndexes = [outlineView selectedRowIndexes];
for(index = [selectedIndexes firstIndex];
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex:index]) {
NSURL *url = [[outlineView itemAtRow:index] URL];
[ws selectFile:[url path] inFileViewerRootedAtPath:[url path]];
}
}
- (IBAction)setAsRoot:(id)sender {
NSUInteger index = [[outlineView selectedRowIndexes] firstIndex];
if(index != NSNotFound) {
[dataSource changeURL:[[outlineView itemAtRow:index] URL]];
}
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
SEL action = [menuItem action];
if([outlineView numberOfSelectedRows] == 0)
return NO;
if(action == @selector(setAsRoot:)) {
BOOL isDir;
NSInteger row = [outlineView selectedRow];
if([outlineView numberOfSelectedRows] > 1)
return NO;
// Only let directories be Set as Root
[[NSFileManager defaultManager] fileExistsAtPath:[[[outlineView itemAtRow:row] URL] path] isDirectory:&isDir];
return isDir;
}
return YES;
}
@end

View File

@ -0,0 +1,24 @@
//
// FileTreeDataSource.h
// Cog
//
// Created by Vincent Spader on 10/14/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class PathNode;
@class PathWatcher;
@interface FileTreeDataSource : NSObject <NSOutlineViewDataSource>
@property(nonatomic, weak) IBOutlet NSOutlineView *outlineView;
@property(nonatomic, weak) IBOutlet NSPathControl *pathControl;
@property(nonatomic, weak) IBOutlet PathWatcher *watcher;
- (void)changeURL:(NSURL *)rootURL;
- (void)reloadPathNode:(PathNode *)item;
@end

View File

@ -0,0 +1,201 @@
//
// FileTreeDataSource.m
// Cog
//
// Created by Vincent Spader on 10/14/07.
// Copyright 2007 __MyCompanyName__. All rights reserved.
//
#import "FileTreeDataSource.h"
#import "DirectoryNode.h"
#import "PathWatcher.h"
#import "Logging.h"
#import "AppController.h"
#import "SandboxBroker.h"
static void *kFileTreeDataSourceContext = &kFileTreeDataSourceContext;
// XXX this is only for reference, we have the entitlement for the path anyway
static NSURL *pathEscape(NSString *path) {
NSString *componentsToRemove = [NSString stringWithFormat:@"Library/Containers/%@/Data/", [[NSBundle mainBundle] bundleIdentifier]];
NSRange rangeOfMatch = [path rangeOfString:componentsToRemove];
if(rangeOfMatch.location != NSNotFound)
path = [path stringByReplacingCharactersInRange:rangeOfMatch withString:@""];
return [NSURL fileURLWithPath:path];
}
static NSURL *defaultMusicDirectory(void) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSMusicDirectory, NSUserDomainMask, YES) lastObject];
return pathEscape(path);
}
@interface FileTreeDataSource ()
@property NSURL *rootURL;
@property const void *sbHandle;
@end
@implementation FileTreeDataSource {
PathNode *rootNode;
const void *_sbHandle;
}
+ (void)initialize {
NSString *path = [defaultMusicDirectory() absoluteString];
NSDictionary *userDefaultsValuesDict = @{ @"fileTreeRootURL": path };
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
}
- (void)awakeFromNib {
_sbHandle = NULL;
[self.pathControl setTarget:self];
[self.pathControl setAction:@selector(pathControlAction:)];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
forKeyPath:@"values.fileTreeRootURL"
options:NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionInitial
context:kFileTreeDataSourceContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if(context == kFileTreeDataSourceContext) {
if([keyPath isEqualToString:@"values.fileTreeRootURL"]) {
NSString *url =
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"];
DLog(@"File tree root URL: %@\n", url);
self.rootURL = [NSURL URLWithString:url];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)changeURL:(NSURL *)url {
if(url != nil) {
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString]
forKey:@"fileTreeRootURL"];
}
}
- (void)pathControlAction:(id)sender {
NSPathControlItem *item = [self.pathControl clickedPathItem];
if(item != nil && item.URL != nil) {
[self changeURL:item.URL];
}
}
- (NSURL *)rootURL {
return [rootNode URL];
}
- (void)setRootURL:(NSURL *)rootURL {
SandboxBroker *sharedSandboxBroker = [SandboxBroker sharedSandboxBroker];
if(self.sbHandle) [sharedSandboxBroker endFolderAccess:self.sbHandle];
self.sbHandle = [sharedSandboxBroker beginFolderAccess:rootURL];
if(![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) {
rootURL = defaultMusicDirectory();
}
rootNode = [[DirectoryNode alloc] initWithDataSource:self url:rootURL];
[self.watcher setPath:[rootURL path]];
[self reloadPathNode:rootNode];
}
- (const void *)sbHandle {
return _sbHandle;
}
- (void)setSbHandle:(const void *)sbHandle {
_sbHandle = sbHandle;
}
- (void)dealloc {
if(self.sbHandle) [[SandboxBroker sharedSandboxBroker] endFolderAccess:self.sbHandle];
}
- (PathNode *)nodeForPath:(NSString *)path {
NSString *relativePath = [[path stringByReplacingOccurrencesOfString:[[[self rootURL] path] stringByAppendingString:@"/"]
withString:@""
options:NSAnchoredSearch
range:NSMakeRange(0, [path length])] stringByStandardizingPath];
PathNode *node = rootNode;
DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path);
for(NSString *c in [relativePath pathComponents]) {
DLog(@"COMPONENT: %@", c);
BOOL found = NO;
for(PathNode *subnode in [node subpaths]) {
if([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) {
node = subnode;
found = YES;
}
}
if(!found) {
DLog(@"Not found!");
return nil;
}
}
return node;
}
- (void)pathDidChange:(NSString *)path {
DLog(@"PATH DID CHANGE: %@", path);
// Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node]
PathNode *node = [self nodeForPath:path];
DLog(@"NODE IS: %@", node);
[node updatePath];
[self reloadPathNode:node];
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
PathNode *n = (item == nil ? rootNode : item);
return (int)[[n subpaths] count];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
PathNode *n = (item == nil ? rootNode : item);
return ![n isLeaf];
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
PathNode *n = (item == nil ? rootNode : item);
return [n subpaths][(NSUInteger)index];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
PathNode *n = (item == nil ? rootNode : item);
return n;
}
- (id<NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item {
NSPasteboardItem *paste = [[NSPasteboardItem alloc] init];
[paste setData:[[item URL] dataRepresentation] forType:NSPasteboardTypeFileURL];
return paste;
}
- (void)reloadPathNode:(PathNode *)item {
if(item == rootNode) {
[self.outlineView reloadData];
} else {
[self.outlineView reloadItem:item reloadChildren:YES];
}
}
@end

View File

@ -0,0 +1,16 @@
//
// FileTreeOutlineView.h
// Cog
//
// Created by Vincent Spader on 6/21/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface FileTreeOutlineView : NSOutlineView {
}
- (void)keyDown:(NSEvent *)e;
@end

View File

@ -0,0 +1,66 @@
//
// FileTreeOutlineView.m
// Cog
//
// Created by Vincent Spader on 6/21/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "FileTreeOutlineView.h"
#import "FileTreeController.h"
#import "FileTreeViewController.h"
#import "PlaybackController.h"
@implementation FileTreeOutlineView
- (void)awakeFromNib {
[self setDoubleAction:@selector(addToPlaylistExternal:)];
[self setTarget:[self delegate]];
}
- (void)keyDown:(NSEvent *)e {
unsigned int modifiers = [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption);
NSString *characters = [e characters];
unichar c;
if([characters length] == 1) {
c = [characters characterAtIndex:0];
if(modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) {
[(FileTreeController *)[self delegate] addToPlaylistExternal:self];
return;
} else if(modifiers == 0 && c == ' ') {
[(FileTreeController *)[self delegate] playPauseResume:self];
return;
}
}
[super keyDown:e];
return;
}
// enables right-click selection for "Show in Finder" contextual menu
- (NSMenu *)menuForEvent:(NSEvent *)event {
// Find which row is under the cursor
[[self window] makeFirstResponder:self];
NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
NSInteger iRow = [self rowAtPoint:menuPoint];
NSMenu *contextMenu = [self menu];
/* Update the file tree selection before showing menu
Preserves the selection if the row under the mouse is selected (to allow for
multiple items to be selected), otherwise selects the row under the mouse */
BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:iRow];
if(iRow == -1) {
[self deselectAll:self];
} else if(!currentRowIsSelected) {
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO];
}
return contextMenu;
}
@end

View File

@ -0,0 +1,25 @@
//
// SideBarController.h
// Cog
//
// Created by Vincent Spader on 6/21/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "SideViewController.h"
#import <Cocoa/Cocoa.h>
@class PlaylistLoader;
@class PlaybackController;
@class FileTreeOutlineView;
@interface FileTreeViewController : SideViewController {
IBOutlet PlaylistLoader *playlistLoader;
IBOutlet PlaybackController *playbackController;
IBOutlet FileTreeOutlineView *fileTreeOutlineView;
}
- (FileTreeOutlineView *)outlineView;
- (IBAction)chooseRootFolder:(id)sender;
@end

View File

@ -0,0 +1,64 @@
//
// SplitViewController.m
// Cog
//
// Created by Vincent Spader on 6/20/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "FileTreeViewController.h"
#import "PlaybackController.h"
#import "PlaylistLoader.h"
#import "SandboxBroker.h"
@implementation FileTreeViewController
- (id)init {
return [super initWithNibName:@"FileTree" bundle:[NSBundle mainBundle]];
}
- (void)addToPlaylistInternal:(NSArray *)urls {
[self doAddToPlaylist:urls origin:URLOriginInternal];
}
- (void)addToPlaylistExternal:(NSArray *)urls {
[self doAddToPlaylist:urls origin:URLOriginExternal];
}
- (void)doAddToPlaylist:(NSArray *)urls origin:(URLOrigin)origin {
[playlistLoader willInsertURLs:urls origin:origin];
[playlistLoader didInsertURLs:[playlistLoader addURLs:urls sort:YES] origin:origin];
}
- (void)clear:(id)sender {
[playlistLoader clear:sender];
}
- (void)playPauseResume:(NSObject *)id {
[playbackController playPauseResume:id];
}
- (FileTreeOutlineView *)outlineView {
return fileTreeOutlineView;
}
- (IBAction)chooseRootFolder:(id)sender {
NSString *path = [[NSUserDefaults standardUserDefaults] stringForKey:@"fileTreeRootURL"];
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setFloatingPanel:YES];
if(path) {
[panel setDirectoryURL:[NSURL fileURLWithPath:path]];
}
[panel setTitle:@"Open to choose tree path"];
NSInteger result = [panel runModal];
if(result == NSModalResponseOK) {
[[SandboxBroker sharedSandboxBroker] addFolderIfMissing:[panel URL]];
[[NSUserDefaults standardUserDefaults] setValue:[[panel URL] absoluteString] forKey:@"fileTreeRootURL"];
}
}
@end

43
FileTree/PathNode.h Normal file
View File

@ -0,0 +1,43 @@
//
// Node.h
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class FileTreeDataSource;
@interface PathNode : NSObject {
FileTreeDataSource *dataSource;
NSURL *url;
NSString *display; // The pretty path to display.
NSImage *icon;
NSArray *subpaths;
}
- (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u;
- (NSURL *)URL;
- (void)setURL:(NSURL *)url;
- (void)processPaths:(NSArray *)contents;
- (NSArray *)subpaths;
- (void)setSubpaths:(NSArray *)s;
- (NSString *)display;
- (void)setDisplay:(NSString *)s;
- (NSImage *)icon;
- (BOOL)isLeaf;
- (void)updatePath;
@end

155
FileTree/PathNode.m Normal file
View File

@ -0,0 +1,155 @@
//
// Node.m
// Cog
//
// Created by Vincent Spader on 8/20/2006.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "PathNode.h"
#import "CogAudio/AudioPlayer.h"
#import "FileTreeDataSource.h"
#import "ContainerNode.h"
#import "DirectoryNode.h"
#import "FileNode.h"
#import "SmartFolderNode.h"
#import "Logging.h"
@implementation PathNode
// From http://developer.apple.com/documentation/Cocoa/Conceptual/LowLevelFileMgmt/Tasks/ResolvingAliases.html
// Updated 2018-06-28
NSURL *resolveAliases(NSURL *url) {
CFErrorRef error;
CFDataRef bookmarkRef = CFURLCreateBookmarkDataFromFile(kCFAllocatorDefault, (__bridge CFURLRef)url, &error);
if(bookmarkRef) {
Boolean isStale;
CFURLRef urlRef = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkRef, kCFURLBookmarkResolutionWithSecurityScope, NULL, NULL, &isStale, &error);
CFRelease(bookmarkRef);
if(urlRef) {
if(!isStale) {
return (NSURL *)CFBridgingRelease(urlRef);
} else {
CFRelease(urlRef);
}
}
}
// DLog(@"Not resolved");
return url;
}
- (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u {
self = [super init];
if(self) {
dataSource = ds;
[self setURL:u];
}
return self;
}
- (void)setURL:(NSURL *)u {
url = u;
display = [[NSFileManager defaultManager] displayNameAtPath:[u path]];
icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
[icon setSize:NSMakeSize(16.0, 16.0)];
}
- (NSURL *)URL {
return url;
}
- (void)updatePath {
}
- (void)processPaths:(NSArray *)contents {
NSMutableArray *newSubpathsDirs = [[NSMutableArray alloc] init];
NSMutableArray *newSubpaths = [[NSMutableArray alloc] init];
for(NSString *s in contents) {
if([s characterAtIndex:0] == '.') {
continue;
}
NSURL *u = [NSURL fileURLWithPath:s];
NSString *displayName = [[NSFileManager defaultManager] displayNameAtPath:[u path]];
PathNode *newNode;
// DLog(@"Before: %@", u);
u = resolveAliases(u);
// DLog(@"After: %@", u);
BOOL isDir;
if([[s pathExtension] caseInsensitiveCompare:@"savedSearch"] == NSOrderedSame) {
DLog(@"Smart folder!");
newNode = [[SmartFolderNode alloc] initWithDataSource:dataSource url:u];
isDir = NO;
} else {
[[NSFileManager defaultManager] fileExistsAtPath:[u path] isDirectory:&isDir];
if(!isDir && ![[AudioPlayer fileTypes] containsObject:[[u pathExtension] lowercaseString]]) {
continue;
}
if(isDir) {
newNode = [[DirectoryNode alloc] initWithDataSource:dataSource url:u];
} else if([[AudioPlayer containerTypes] containsObject:[[u pathExtension] lowercaseString]]) {
newNode = [[ContainerNode alloc] initWithDataSource:dataSource url:u];
} else {
newNode = [[FileNode alloc] initWithDataSource:dataSource url:u];
}
}
[newNode setDisplay:displayName];
if(isDir)
[newSubpathsDirs addObject:newNode];
else
[newSubpaths addObject:newNode];