Restore the File Tree, now with a chooser button

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

This reverts commit 35400e1320.

This also changes how the File Tree choosing works.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
xcode15
Christopher Snowhill 2022-06-30 16:57:51 -07:00
parent 73c4360b1d
commit 099588b7bd
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;
@ -19,6 +20,7 @@
IBOutlet NSWindow *mainWindow;
IBOutlet NSWindow *miniWindow;
IBOutlet NSSplitView *mainView;
IBOutlet NSSegmentedControl *playbackButtons;
IBOutlet NSButton *fileButton;
@ -47,6 +49,8 @@
IBOutlet SUUpdater *updater;
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"
@ -245,11 +249,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
@ -337,6 +380,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];
@ -410,6 +467,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 */; };
@ -185,6 +198,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, ); }; };
@ -775,6 +789,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>"; };
@ -800,10 +816,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>"; };
@ -1012,6 +1052,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>"; };
@ -1020,6 +1061,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>"; };
@ -1102,6 +1145,7 @@
8377C66027B8CF2300E8BC0F /* Visualization */,
8E75752A09F31D5A0080F1EE /* Playlist */,
8E07AAEA0AAC90DC00A4B32F /* Preferences */,
17DDF6400E0CB6F100A2E4AD /* FileTree */,
830C37A227B95E6000E02BB0 /* Equalizer */,
17D1B0FE0F63252900694C57 /* InfoInspector */,
569C52C50D5F2BD500BDBDC9 /* Spotlight */,
@ -1214,6 +1258,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 */,
@ -1363,6 +1409,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 = (
@ -1489,6 +1566,7 @@
17D1B0D00F6320EA00694C57 /* InfoInspector.xib */,
17342A980D5FD20B00E8D854 /* MainMenu.xib */,
17342ABD0D5FD36400E8D854 /* OpenURLPanel.xib */,
17211A7C0D68B7C500911CA9 /* FileTree.xib */,
178456C00F6320B5007E8021 /* SpotlightPanel.xib */,
17E41E060C130DFF00AC744D /* Credits.html */,
17D1B1DA0F6330D400694C57 /* Feedback.xib */,
@ -2414,6 +2492,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 */,
@ -2568,12 +2647,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 */,
@ -2596,6 +2686,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 */,
@ -2778,6 +2870,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];
}
[newSubpathsDirs addObjectsFromArray:newSubpaths];
[self setSubpaths:newSubpathsDirs];
}
- (NSArray *)subpaths {
if(subpaths == nil) {
[self updatePath];
}
return subpaths;
}
- (void)setSubpaths:(NSArray *)s {
subpaths = s;
}
- (BOOL)isLeaf {
return YES;
}
- (void)setDisplay:(NSString *)s {
display = s;
}
- (NSString *)display {
return display;
}
- (NSImage *)icon {
return icon;
}
@end

30
FileTree/PathWatcher.h Normal file
View File

@ -0,0 +1,30 @@
//
// PathWatcher.h
// Cog
//
// Created by Vincent Spader on 2/17/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#include <CoreServices/CoreServices.h>
@interface PathWatcher : NSObject {
FSEventStreamRef stream;
FSEventStreamContext *context;
IBOutlet id delegate;
}
- (void)setDelegate:(id)d;
- (id)delegate;
- (void)setPath:(NSString *)path; // Set the path to watch
- (void)cleanUp;
@end
@protocol PathWatcherDelegate
- (void)pathDidChange:(NSString *)path;
@end

84
FileTree/PathWatcher.m Normal file
View File

@ -0,0 +1,84 @@
//
// PathWatcher.m
// Cog
//
// Created by Vincent Spader on 2/17/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "PathWatcher.h"
static void myFSEventCallback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {
int i;
char **paths = eventPaths;
PathWatcher *pathWatcher = (__bridge PathWatcher *)clientCallBackInfo;
printf("Callback called\n");
for(i = 0; i < numEvents; i++) {
NSString *pathString = [[NSString alloc] initWithUTF8String:paths[i]];
[[pathWatcher delegate] pathDidChange:pathString];
}
}
@implementation PathWatcher
- (void)cleanUp {
if(stream) {
FSEventStreamStop(stream);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
stream = NULL;
}
if(context) {
free(context);
context = NULL;
}
}
- (void)setPath:(NSString *)path {
[self cleanUp];
if(!path) return;
// Create FSEvent stream
NSArray *pathsToWatch = @[path];
context = (FSEventStreamContext *)malloc(sizeof(FSEventStreamContext));
context->version = 0;
context->info = (__bridge void *)self;
context->retain = NULL;
context->release = NULL;
// Create the stream, passing in a callback
stream = FSEventStreamCreate(NULL,
&myFSEventCallback,
context,
(__bridge CFArrayRef)pathsToWatch,
kFSEventStreamEventIdSinceNow, // Or a previous event ID
1.0, // latency in seconds
kFSEventStreamCreateFlagNone // Watch this and all its subdirectories
);
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
}
- (void)setDelegate:(id)d {
delegate = d;
}
- (id)delegate {
return delegate;
}
- (void)dealloc {
[self cleanUp];
}
@end

View File

@ -0,0 +1,17 @@
//
// SmartFolderNode.h
// Cog
//
// Created by Vincent Spader on 9/25/06.
// Copyright 2006 __MyCompanyName__. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "PathNode.h"
@interface SmartFolderNode : PathNode {
MDQueryRef _query;
}
@end

View File

@ -0,0 +1,90 @@
//
// SmartFolderNode.m
// Cog
//
// Created by Vincent Spader on 9/25/06.
// Copyright 2006 __MyCompanyName__. All rights reserved.
//
#import "SmartFolderNode.h"
#import "DirectoryNode.h"
#import "FileNode.h"
#import "FileTreeDataSource.h"
#import "Logging.h"
@implementation SmartFolderNode
- (BOOL)isLeaf {
return NO;
}
- (void)updatePath {
NSDictionary *doc = [NSDictionary dictionaryWithContentsOfFile:[url path]];
NSString *rawQuery = [doc objectForKey:@"RawQuery"];
NSArray *searchPaths = [[doc objectForKey:@"SearchCriteria"] objectForKey:@"CurrentFolderPath"];
// Ugh, Carbon from now on...
MDQueryRef query = MDQueryCreate(kCFAllocatorDefault, (CFStringRef)rawQuery, NULL, NULL);
_query = query;
MDQuerySetSearchScope(query, (CFArrayRef)searchPaths, 0);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryFinished:) name:(NSString *)kMDQueryDidFinishNotification object:(__bridge id)query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdate:) name:(NSString *)kMDQueryDidUpdateNotification object:(__bridge id)query];
DLog(@"Making query!");
MDQueryExecute(query, kMDQueryWantsUpdates);
// Note: This is asynchronous!
}
- (void)setSubpaths:(id)s {
subpaths = s;
}
- (unsigned int)countOfSubpaths {
return (unsigned int)[[self subpaths] count];
}
- (PathNode *)objectInSubpathsAtIndex:(unsigned int)index {
return [[self subpaths] objectAtIndex:index];
}
- (void)queryFinished:(NSNotification *)notification {
DLog(@"Query finished!");
MDQueryRef query = (__bridge MDQueryRef)[notification object];
NSMutableArray *results = [NSMutableArray array];
MDQueryDisableUpdates(query);
int c = (int)MDQueryGetResultCount(query);
int i;
for(i = 0; i < c; i++) {
MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(query, i);
NSString *itemPath = (NSString *)CFBridgingRelease(MDItemCopyAttribute(item, kMDItemPath));
[results addObject:itemPath];
}
MDQueryEnableUpdates(query);
DLog(@"Query update!");
[self processPaths:[results sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]];
[dataSource reloadPathNode:self];
}
- (void)queryUpdate:(NSNotification *)notification {
DLog(@"Query update!");
[self queryFinished:notification];
}
- (void)dealloc {
CFRelease(_query);
}
@end

View File

@ -88,6 +88,12 @@
}
// Add other system paths to this setting
NSString *fileTreePath = [[NSUserDefaults standardUserDefaults] stringForKey:@"fileTreeRootURL"];
if(fileTreePath && [fileTreePath length]) {
// Append false name to dodge the directory/fragment trimmer
[array addObject:[NSURL URLWithString:[fileTreePath stringByAppendingPathComponent:@"moo.mp3"]]];
}
NSString *soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"];
if(soundFontPath && [soundFontPath length]) {
[array addObject:[NSURL fileURLWithPath:soundFontPath]];

View File

@ -0,0 +1,34 @@
//
// SideBarController.h
// Cog
//
// Created by Vincent Spader on 6/21/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "PlaylistController.h"
#import <Cocoa/Cocoa.h>
@interface SideViewController : NSViewController {
IBOutlet NSSplitView *splitView;
IBOutlet NSView *mainView;
IBOutlet NSView *firstResponder;
}
- (IBAction)toggleSideView:(id)sender;
- (IBAction)toggleVertical:(id)sender;
- (void)showSideView;
- (void)hideSideView;
- (BOOL)sideViewIsHidden;
- (void)setDividerPosition:(float)position;
- (float)dividerPosition;
- (void)doAddToPlaylist:(NSArray *)urls origin:(URLOrigin)origin;
- (void)clear:(id)sender;
- (void)playPauseResume:(id)sender;
@end

183
Utils/SideViewController.m Normal file
View File

@ -0,0 +1,183 @@
//
// SplitViewController.m
// Cog
//
// Created by Vincent Spader on 6/20/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "SideViewController.h"
#import "Logging.h"
@implementation SideViewController
- (NSString *)showSideViewDefaultsKey {
return [NSString stringWithFormat:@"%@ShowSideView", [self nibName]];
}
- (NSString *)sideViewVerticalDefaultsKey {
return [NSString stringWithFormat:@"%@SideViewVertical", [self nibName]];
}
- (NSString *)sideViewDividerPositionDefaultsKey {
return [NSString stringWithFormat:@"%@SideViewDividerPosition", [self nibName]];
}
- (void)registerDefaults {
NSMutableDictionary *userDefaultsValuesDict = [NSMutableDictionary dictionary];
[userDefaultsValuesDict setObject:@(YES) forKey:[self sideViewVerticalDefaultsKey]];
[userDefaultsValuesDict setObject:@(NO) forKey:[self showSideViewDefaultsKey]];
[userDefaultsValuesDict setObject:@(100.0) forKey:[self sideViewDividerPositionDefaultsKey]];
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
}
- (id)initWithNibName:(NSString *)nib bundle:(NSBundle *)bundle {
self = [super initWithNibName:nib bundle:bundle];
if(self) {
[self registerDefaults];
}
return self;
}
- (void)awakeFromNib {
[splitView setVertical:[[NSUserDefaults standardUserDefaults] boolForKey:[self sideViewVerticalDefaultsKey]]];
if([[NSUserDefaults standardUserDefaults] boolForKey:[self showSideViewDefaultsKey]]) {
[self showSideView];
}
}
- (IBAction)toggleSideView:(id)sender {
// Show/hide current
if([self sideViewIsHidden]) {
[self showSideView];
} else {
[self hideSideView];
}
[splitView adjustSubviews];
}
- (IBAction)toggleVertical:(id)sender {
[splitView setVertical:![splitView isVertical]];
if(![self sideViewIsHidden]) {
[self showSideView];
}
[splitView adjustSubviews];
[[NSUserDefaults standardUserDefaults] setBool:[splitView isVertical] forKey:[self sideViewVerticalDefaultsKey]];
}
- (void)showSideView {
if([splitView isVertical]) {
[splitView setSubviews:@[[self view], mainView]];
} else {
[splitView setSubviews:@[mainView, [self view]]];
}
[self setDividerPosition:[[NSUserDefaults standardUserDefaults] floatForKey:[self sideViewDividerPositionDefaultsKey]]];
[[[self view] window] makeFirstResponder:firstResponder];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:[self showSideViewDefaultsKey]];
}
- (void)hideSideView {
[splitView setSubviews:@[mainView]];
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:[self showSideViewDefaultsKey]];
[[mainView window] makeFirstResponder:mainView];
}
- (BOOL)sideViewIsHidden {
return ([[splitView subviews] count] == 1);
}
- (BOOL)splitView:(NSSplitView *)aSplitView canCollapseSubview:(NSView *)subview {
return (subview != mainView);
}
- (BOOL)splitView:(NSSplitView *)aSplitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex {
return (subview != mainView);
}
- (void)splitViewDidResizeSubviews:(NSNotification *)aNotification {
// Update default
if(![self sideViewIsHidden]) {
[[NSUserDefaults standardUserDefaults] setFloat:[self dividerPosition] forKey:[self sideViewDividerPositionDefaultsKey]];
DLog(@"DIVIDER POSITION: %f", [self dividerPosition]);
}
}
- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize {
if([self sideViewIsHidden]) {
[splitView adjustSubviews];
} else {
CGFloat dividerThickness = [splitView dividerThickness];
NSRect sideRect = [[self view] frame];
NSRect mainRect = [mainView frame];
NSRect newFrame = [splitView frame];
if([splitView isVertical]) {
sideRect.size.width = [[NSUserDefaults standardUserDefaults] floatForKey:[self sideViewDividerPositionDefaultsKey]];
sideRect.size.height = newFrame.size.height;
sideRect.origin = NSMakePoint(0, 0);
mainRect.size.width = newFrame.size.width - sideRect.size.width - dividerThickness;
mainRect.size.height = newFrame.size.height;
mainRect.origin.x = sideRect.size.width + dividerThickness;
} else {
sideRect.size.height = [[NSUserDefaults standardUserDefaults] floatForKey:[self sideViewDividerPositionDefaultsKey]];
sideRect.size.width = newFrame.size.width;
mainRect.origin = NSMakePoint(0, 0);
mainRect.size.width = newFrame.size.width;
mainRect.size.height = newFrame.size.height - sideRect.size.height - dividerThickness;
sideRect.origin.y = mainRect.size.height + dividerThickness;
}
[[self view] setFrame:sideRect];
[mainView setFrame:mainRect];
}
}
- (float)dividerPosition {
if([splitView isVertical]) {
return [[self view] frame].size.width;
}
return [[self view] frame].size.height;
}
- (void)setDividerPosition:(float)position {
float actualPosition = position;
if(![splitView isVertical]) {
actualPosition = ([splitView frame].size.height - position);
}
[splitView adjustSubviews];
[splitView setPosition:actualPosition ofDividerAtIndex:0];
[[NSUserDefaults standardUserDefaults] setFloat:position forKey:[self sideViewDividerPositionDefaultsKey]];
}
// placeholders, implemented by subclass
- (void)doAddToPlaylist:(NSArray *)urls origin:(URLOrigin)origin {
}
- (void)clear:(id)sender {
}
- (void)playPauseResume:(id)sender {
}
@end

21
en.lproj/FileTree.strings Normal file
View File

@ -0,0 +1,21 @@
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "71"; */
"71.title" = "Text Cell";
/* Class = "NSMenu"; title = "Menu"; ObjectID = "110"; */
"110.title" = "Menu";
/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "112"; */
"112.title" = "Show in Finder";
/* Class = "NSMenuItem"; title = "Add to Playlist"; ObjectID = "119"; */
"119.title" = "Add to Playlist";
/* Class = "NSMenuItem"; title = "Set as Root"; ObjectID = "124"; */
"124.title" = "Set as Root";
/* Class = "NSMenuItem"; title = "Set as Playlist"; ObjectID = "129"; */
"129.title" = "Set as Playlist";
/* Class = "NSBox"; title = "Box"; ObjectID = "147"; */
"147.title" = "Box";

View File

@ -335,6 +335,12 @@
/* Class = "NSToolbarItem"; paletteLabel = "Info Inspector"; ObjectID = "1629"; */
"1629.paletteLabel" = "Info Inspector";
/* Class = "NSToolbarItem"; label = "File Tree"; ObjectID = "1630"; */
"1630.label" = "File Tree";
/* Class = "NSToolbarItem"; paletteLabel = "File Tree"; ObjectID = "1630"; */
"1630.paletteLabel" = "File Tree";
/* Class = "NSToolbarItem"; label = "Shuffle"; ObjectID = "1636"; */
"1636.label" = "Shuffle";
@ -473,6 +479,9 @@
/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Not Playing"; ObjectID = "2085"; */
"2085.ibShadowedIsNilPlaceholder" = "Not Playing";
/* Class = "NSMenuItem"; title = "Toggle File Tree Orientation"; ObjectID = "2160"; */
"2160.title" = "Toggle File Tree Orientation";
/* Class = "NSMenuItem"; title = "Information"; ObjectID = "2212"; */
"2212.title" = "Information";
@ -539,6 +548,9 @@
/* Class = "CocoaBindingsConnection"; ibShadowedNotApplicablePlaceholder = "Cog"; ObjectID = "2374"; */
"2374.ibShadowedNotApplicablePlaceholder" = "Cog";
/* Class = "NSMenuItem"; title = "Show File Tree"; ObjectID = "2417"; */
"2417.title" = "Show File Tree";
/* Class = "NSToolbarItem"; label = "Info Inspector"; ObjectID = "2429"; */
"2429.label" = "Info Inspector";

21
es.lproj/FileTree.strings Normal file
View File

@ -0,0 +1,21 @@
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "71"; */
"71.title" = "Text Cell";
/* Class = "NSMenu"; title = "Menu"; ObjectID = "110"; */
"110.title" = "Menu";
/* Class = "NSMenuItem"; title = "Show in Finder"; ObjectID = "112"; */
"112.title" = "Show in Finder";
/* Class = "NSMenuItem"; title = "Add to Playlist"; ObjectID = "119"; */
"119.title" = "Add to Playlist";
/* Class = "NSMenuItem"; title = "Set as Root"; ObjectID = "124"; */
"124.title" = "Set as Root";
/* Class = "NSMenuItem"; title = "Set as Playlist"; ObjectID = "129"; */
"129.title" = "Set as Playlist";
/* Class = "NSBox"; title = "Box"; ObjectID = "147"; */
"147.title" = "Box";

View File

@ -335,6 +335,12 @@
/* Class = "NSToolbarItem"; paletteLabel = "Info Inspector"; ObjectID = "1629"; */
"1629.paletteLabel" = "Info Inspector";
/* Class = "NSToolbarItem"; label = "File Tree"; ObjectID = "1630"; */
"1630.label" = "File Tree";
/* Class = "NSToolbarItem"; paletteLabel = "File Tree"; ObjectID = "1630"; */
"1630.paletteLabel" = "File Tree";
/* Class = "NSToolbarItem"; label = "Shuffle"; ObjectID = "1636"; */
"1636.label" = "Shuffle";
@ -473,6 +479,9 @@
/* Class = "CocoaBindingsConnection"; ibShadowedIsNilPlaceholder = "Not Playing"; ObjectID = "2085"; */
"2085.ibShadowedIsNilPlaceholder" = "Not Playing";
/* Class = "NSMenuItem"; title = "Toggle File Tree Orientation"; ObjectID = "2160"; */
"2160.title" = "Toggle File Tree Orientation";
/* Class = "NSMenuItem"; title = "Information"; ObjectID = "2212"; */
"2212.title" = "Information";
@ -539,6 +548,9 @@
/* Class = "CocoaBindingsConnection"; ibShadowedNotApplicablePlaceholder = "Cog"; ObjectID = "2374"; */
"2374.ibShadowedNotApplicablePlaceholder" = "Cog";
/* Class = "NSMenuItem"; title = "Show File Tree"; ObjectID = "2417"; */
"2417.title" = "Show File Tree";
/* Class = "NSToolbarItem"; label = "Info Inspector"; ObjectID = "2429"; */
"2429.label" = "Info Inspector";