[Sandbox] Add Playlist Path Suggester
Add a dialog for suggesting sandbox paths to the Preferences general page, in the context menu. Signed-off-by: Christopher Snowhill <kode54@gmail.com>swiftingly
parent
5cb08291bf
commit
e8dacdabda
|
@ -413,8 +413,8 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="toolTip" keyPath="valueToDisplay.playCountInfo" id="ydF-ec-fBX"/>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.playCount" id="Mpe-FA-gRG"/>
|
||||
<binding destination="-2" name="toolTip" keyPath="valueToDisplay.playCountInfo" id="ydF-ec-fBX"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
|
|
|
@ -21,21 +21,21 @@
|
|||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
|
||||
<value key="minSize" type="size" width="750" height="200"/>
|
||||
<stackView key="contentView" distribution="equalSpacing" orientation="vertical" alignment="centerX" spacing="0.0" detachesHiddenViews="YES" id="2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="400"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1015" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
|
||||
<rect key="frame" x="0.0" y="345" width="1000" height="55"/>
|
||||
<rect key="frame" x="0.0" y="350" width="1015" height="50"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="55"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1015" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="55"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1015" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="38"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1015" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="6"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -229,11 +229,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="gpC-Oe-Rog">
|
||||
<rect key="frame" x="235" y="3" width="150" height="18"/>
|
||||
<rect key="frame" x="234.5" y="3" width="149" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1WK-qN-Mgj">
|
||||
<rect key="frame" x="0.0" y="1" width="150" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="149" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="71l-3L-S3g">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -273,7 +273,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="1ed-gX-bct">
|
||||
<rect key="frame" x="388" y="3" width="151" height="18"/>
|
||||
<rect key="frame" x="387" y="3" width="151" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nEt-s5-vRX">
|
||||
|
@ -316,11 +316,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hhB-nv-e78">
|
||||
<rect key="frame" x="542" y="3" width="96" height="18"/>
|
||||
<rect key="frame" x="540.5" y="3" width="95" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tHy-sM-HDB">
|
||||
<rect key="frame" x="0.0" y="1" width="96" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="95" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Igo-5f-yim">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -360,7 +360,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="q93-oh-i5T">
|
||||
<rect key="frame" x="641" y="3" width="94" height="18"/>
|
||||
<rect key="frame" x="639" y="3" width="94" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="bOi-LI-TDx">
|
||||
|
@ -401,7 +401,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="rRl-p9-Awr">
|
||||
<rect key="frame" x="738" y="3" width="144" height="18"/>
|
||||
<rect key="frame" x="735.5" y="3" width="144" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yW6-2w-6mN">
|
||||
|
@ -441,11 +441,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hgh-VE-5kl">
|
||||
<rect key="frame" x="885" y="3" width="39" height="18"/>
|
||||
<rect key="frame" x="882.5" y="3" width="38" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yEY-MI-d3o">
|
||||
<rect key="frame" x="0.0" y="1" width="39" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="38" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="tus-lr-RhS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -628,7 +628,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<tableHeaderView key="headerView" wantsLayer="YES" id="1517">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="17"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1015" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
|
@ -641,7 +641,7 @@
|
|||
</connections>
|
||||
</splitView>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="778">
|
||||
<rect key="frame" x="370" y="4" width="261" height="14"/>
|
||||
<rect key="frame" x="377" y="4" width="261" height="14"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Total Duration: 00 hours 00 minutes 00 seconds" bezelStyle="round" id="1473">
|
||||
<font key="font" metaFont="controlContent" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
|
@ -370,10 +370,16 @@
|
|||
</customObject>
|
||||
<customObject id="tm7-TU-wbV" customClass="GeneralPane">
|
||||
<connections>
|
||||
<outlet property="sandboxBehaviorController" destination="9mC-3k-IQG" id="QzA-u4-vH7"/>
|
||||
<outlet property="pathSuggester" destination="Vx8-Va-nme" id="UWw-bx-h7Z"/>
|
||||
<outlet property="sandboxPathBehaviorController" destination="9mC-3k-IQG" id="xNT-x0-lVA"/>
|
||||
<outlet property="view" destination="0nK-XQ-5MY" id="TDK-Vn-N5G"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="Vx8-Va-nme" customClass="PathSuggester">
|
||||
<connections>
|
||||
<outlet property="sandboxPathBehaviorController" destination="9mC-3k-IQG" id="1Jd-9v-4rm"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<arrayController id="59" userLabel="OutputDevices" customClass="OutputsArrayController">
|
||||
<declaredKeys>
|
||||
<string>name</string>
|
||||
|
@ -426,14 +432,14 @@
|
|||
<rect key="frame" x="1" y="1" width="488" height="113"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" headerView="9rQ-Rq-K6J" id="gHG-xw-OyR">
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" rowSizeStyle="automatic" headerView="9rQ-Rq-K6J" viewBased="YES" id="gHG-xw-OyR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="488" height="85"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="388" minWidth="40" maxWidth="1000" id="Yz2-Ee-bZ0" userLabel="path">
|
||||
<tableColumn identifier="path" width="388" minWidth="40" maxWidth="1000" id="Yz2-Ee-bZ0" userLabel="path">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Path">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -444,11 +450,31 @@
|
|||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="EEt-uJ-j6o">
|
||||
<rect key="frame" x="8" y="0.0" width="386" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7l9-R2-FVF">
|
||||
<rect key="frame" x="0.0" y="4" width="386" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="7un-em-D8r">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="textField" destination="7l9-R2-FVF" id="Yha-df-BGK"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="9mC-3k-IQG" name="value" keyPath="arrangedObjects.path" id="eva-uV-ce2"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn width="41" minWidth="40" maxWidth="1000" id="lEg-E3-TBs" userLabel="valid">
|
||||
<tableColumn identifier="valid" width="41" minWidth="40" maxWidth="1000" id="lEg-E3-TBs" userLabel="valid">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Valid">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -459,11 +485,35 @@
|
|||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="EDi-gi-Vg9">
|
||||
<rect key="frame" x="411" y="0.0" width="38" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0TJ-dK-Rfk">
|
||||
<rect key="frame" x="0.0" y="4" width="38" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="LwT-Qb-47r">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="textField" destination="0TJ-dK-Rfk" id="kDn-6g-04J"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="9mC-3k-IQG" name="value" keyPath="arrangedObjects.valid" id="Kg6-FI-Mcl"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="9mC-3k-IQG" id="2X9-U5-GJP"/>
|
||||
<outlet property="delegate" destination="tm7-TU-wbV" id="h2y-Ql-eEn"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
|
@ -780,12 +830,25 @@
|
|||
<action selector="addPath:" target="tm7-TU-wbV" id="j3o-oG-PA4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete Paths" id="nva-pz-xSS">
|
||||
<menuItem title="Delete Path" id="nva-pz-xSS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="deleteSelectedPaths:" target="tm7-TU-wbV" id="suU-Gb-vMn"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete Invalid Paths" id="JU5-o7-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="removeStaleEntries:" target="tm7-TU-wbV" id="j5v-pf-4FC"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="QVM-CG-8Ga"/>
|
||||
<menuItem title="Suggest Paths" id="qjI-dK-nfG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="showPathSuggester:" target="tm7-TU-wbV" id="TnR-Di-6Ys"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="533" y="-395"/>
|
||||
</menu>
|
||||
|
|
|
@ -7,16 +7,20 @@
|
|||
|
||||
#import "GeneralPreferencePane.h"
|
||||
|
||||
#import "PathSuggester.h"
|
||||
#import "SandboxPathBehaviorController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GeneralPane : GeneralPreferencePane {
|
||||
IBOutlet SandboxPathBehaviorController *sandboxBehaviorController;
|
||||
@interface GeneralPane : GeneralPreferencePane <NSTableViewDelegate> {
|
||||
IBOutlet SandboxPathBehaviorController *sandboxPathBehaviorController;
|
||||
IBOutlet PathSuggester *pathSuggester;
|
||||
}
|
||||
|
||||
- (IBAction)addPath:(id)sender;
|
||||
- (IBAction)deleteSelectedPaths:(id)sender;
|
||||
- (IBAction)removeStaleEntries:(id)sender;
|
||||
- (IBAction)showPathSuggester:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#import "GeneralPane.h"
|
||||
|
||||
#import "PathSuggester.h"
|
||||
|
||||
@implementation GeneralPane
|
||||
|
||||
- (NSString *)title {
|
||||
|
@ -27,18 +29,80 @@
|
|||
[panel setFloatingPanel:YES];
|
||||
NSInteger result = [panel runModal];
|
||||
if(result == NSModalResponseOK) {
|
||||
[sandboxBehaviorController addUrl:[panel URL]];
|
||||
[sandboxPathBehaviorController addUrl:[panel URL]];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)deleteSelectedPaths:(id)sender {
|
||||
NSArray *selectedObjects = [sandboxBehaviorController selectedObjects];
|
||||
NSArray *selectedObjects = [sandboxPathBehaviorController selectedObjects];
|
||||
if(selectedObjects && [selectedObjects count]) {
|
||||
NSArray *paths = [selectedObjects valueForKey:@"path"];
|
||||
for(NSString *path in paths) {
|
||||
[sandboxBehaviorController removePath:path];
|
||||
[sandboxPathBehaviorController removePath:path];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)removeStaleEntries:(id)sender {
|
||||
[sandboxPathBehaviorController removeStaleEntries];
|
||||
}
|
||||
|
||||
- (NSView *_Nullable)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *_Nullable)tableColumn row:(NSInteger)row {
|
||||
NSString *cellIdentifier = @"";
|
||||
NSTextAlignment cellTextAlignment = NSTextAlignmentLeft;
|
||||
|
||||
NSDictionary *item = [[sandboxPathBehaviorController arrangedObjects] objectAtIndex:row];
|
||||
|
||||
float fontSize = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] floatForKey:@"fontSize"];
|
||||
|
||||
NSString *cellText = @"";
|
||||
|
||||
if(item) {
|
||||
cellIdentifier = [tableColumn identifier];
|
||||
if([cellIdentifier isEqualToString:@"path"]) {
|
||||
cellText = [item objectForKey:@"path"];
|
||||
} else if([cellIdentifier isEqualToString:@"valid"]) {
|
||||
cellText = [item objectForKey:@"valid"];
|
||||
}
|
||||
}
|
||||
|
||||
NSView *view = [tableView makeViewWithIdentifier:cellIdentifier owner:nil];
|
||||
if(view) {
|
||||
NSTableCellView *cellView = (NSTableCellView *)view;
|
||||
|
||||
if(cellView.textField) {
|
||||
cellView.textField.allowsDefaultTighteningForTruncation = YES;
|
||||
|
||||
NSFont *font = [NSFont monospacedDigitSystemFontOfSize:fontSize weight:NSFontWeightRegular];
|
||||
|
||||
cellView.textField.font = font;
|
||||
cellView.textField.stringValue = cellText;
|
||||
cellView.textField.alignment = cellTextAlignment;
|
||||
|
||||
if(cellView.textField.intrinsicContentSize.width > cellView.textField.frame.size.width - 4) {
|
||||
cellView.textField.toolTip = cellText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
|
||||
SEL action = [menuItem action];
|
||||
|
||||
if(action == @selector(addPath:) ||
|
||||
action == @selector(deleteSelectedPaths:) ||
|
||||
action == @selector(removeStaleEntries:) ||
|
||||
action == @selector(showPathSuggester:)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (IBAction)showPathSuggester:(id)sender {
|
||||
[pathSuggester beginSuggestion:sender];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// PathSuggester.h
|
||||
// Preferences
|
||||
//
|
||||
// Created by Christopher Snowhill on 6/21/22.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "SandboxPathBehaviorController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PathSuggester : NSWindowController <NSTableViewDelegate> {
|
||||
IBOutlet SandboxPathBehaviorController *sandboxPathBehaviorController;
|
||||
IBOutlet NSArrayController *pathsList;
|
||||
}
|
||||
|
||||
- (IBAction)beginSuggestion:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// PathSuggester.m
|
||||
// Preferences
|
||||
//
|
||||
// Created by Christopher Snowhill on 6/21/22.
|
||||
//
|
||||
|
||||
#import "PathSuggester.h"
|
||||
|
||||
#import "PlaylistController.h"
|
||||
|
||||
#import "SandboxBroker.h"
|
||||
|
||||
// Sync, only declare items we need
|
||||
@interface PlaylistEntry
|
||||
@property(nonatomic) NSURL *_Nullable url;
|
||||
@end
|
||||
|
||||
static NSURL *defaultMusicDirectory(void) {
|
||||
return [[NSFileManager defaultManager] URLForDirectory:NSMusicDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:NO
|
||||
error:nil];
|
||||
}
|
||||
|
||||
static NSURL *defaultDownloadsDirectory(void) {
|
||||
return [[NSFileManager defaultManager] URLForDirectory:NSDownloadsDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:NO
|
||||
error:nil];
|
||||
}
|
||||
|
||||
static NSURL *defaultMoviesDirectory(void) {
|
||||
return [[NSFileManager defaultManager] URLForDirectory:NSMoviesDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:nil
|
||||
create:NO
|
||||
error:nil];
|
||||
}
|
||||
|
||||
@interface PathItem : NSObject
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
@property(nonatomic) BOOL enabled;
|
||||
|
||||
- (IBAction)clickEnable:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation PathItem
|
||||
@synthesize path;
|
||||
@synthesize enabled;
|
||||
|
||||
- (IBAction)clickEnable:(id)sender {
|
||||
self.enabled = !self.enabled;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface PathSuggester ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation PathSuggester
|
||||
|
||||
- (id)init {
|
||||
return [super initWithWindowNibName:@"PathSuggester"];
|
||||
}
|
||||
|
||||
- (IBAction)beginSuggestion:(id)sender {
|
||||
[self showWindow:self];
|
||||
|
||||
[pathsList removeObjects:[pathsList arrangedObjects]];
|
||||
|
||||
NSPersistentContainer *pc = [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
|
||||
|
||||
NSPredicate *hasUrlPredicate = [NSPredicate predicateWithFormat:@"urlString != nil && urlString != %@", @""];
|
||||
NSPredicate *deletedPredicate = [NSPredicate predicateWithFormat:@"deLeted == NO || deLeted == nil"];
|
||||
|
||||
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[deletedPredicate, hasUrlPredicate]];
|
||||
|
||||
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"PlaylistEntry"];
|
||||
request.predicate = predicate;
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
|
||||
|
||||
if(!results || [results count] < 1) return;
|
||||
|
||||
NSURL *defaultMusic = defaultMusicDirectory();
|
||||
NSURL *defaultDownloads = defaultDownloadsDirectory();
|
||||
NSURL *defaultMovies = defaultMoviesDirectory();
|
||||
|
||||
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
|
||||
|
||||
NSMutableArray *items = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *itemPaths = [[NSMutableArray alloc] init];
|
||||
|
||||
for(PlaylistEntry *pe in results) {
|
||||
NSURL *url = [sandboxBrokerClass urlWithoutFragment:pe.url];
|
||||
if([sandboxBrokerClass isPath:url aSubdirectoryOf:defaultMusic] ||
|
||||
[sandboxBrokerClass isPath:url
|
||||
aSubdirectoryOf:defaultDownloads] ||
|
||||
[sandboxBrokerClass isPath:url
|
||||
aSubdirectoryOf:defaultMovies] ||
|
||||
[sandboxPathBehaviorController matchesPath:url])
|
||||
continue;
|
||||
|
||||
NSArray *pathComponents = [url pathComponents];
|
||||
for(size_t i = 2; i < [pathComponents count]; ++i) {
|
||||
NSURL *subUrl = [NSURL fileURLWithPathComponents:[pathComponents subarrayWithRange:NSMakeRange(0, i)]];
|
||||
if(![itemPaths containsObject:subUrl]) {
|
||||
[itemPaths addObject:subUrl];
|
||||
PathItem *item = [[PathItem alloc] init];
|
||||
item.path = [subUrl path];
|
||||
item.enabled = NO;
|
||||
[items addObject:item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:YES];
|
||||
[pathsList setSortDescriptors:@[sortDescriptor]];
|
||||
|
||||
[items sortUsingDescriptors:@[sortDescriptor]];
|
||||
|
||||
[pathsList addObjects:items];
|
||||
|
||||
[pathsList setSelectedObjects:@[]];
|
||||
}
|
||||
|
||||
- (void)windowDidLoad {
|
||||
[super windowDidLoad];
|
||||
}
|
||||
|
||||
- (NSView *_Nullable)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *_Nullable)tableColumn row:(NSInteger)row {
|
||||
NSString *cellIdentifier = @"";
|
||||
NSTextAlignment cellTextAlignment = NSTextAlignmentLeft;
|
||||
|
||||
PathItem *pi = [[pathsList arrangedObjects] objectAtIndex:row];
|
||||
|
||||
float fontSize = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] floatForKey:@"fontSize"];
|
||||
|
||||
NSButton *cellButton = nil;
|
||||
NSString *cellText = @"";
|
||||
|
||||
if(pi) {
|
||||
cellIdentifier = [tableColumn identifier];
|
||||
if([cellIdentifier isEqualToString:@"enabled"]) {
|
||||
cellButton = [NSButton checkboxWithTitle:@"" target:pi action:@selector(clickEnable:)];
|
||||
cellButton.state = pi.enabled ? NSControlStateValueOn : NSControlStateValueOff;
|
||||
} else if([cellIdentifier isEqualToString:@"path"]) {
|
||||
cellText = pi.path;
|
||||
}
|
||||
}
|
||||
|
||||
NSView *view = [tableView makeViewWithIdentifier:cellIdentifier owner:nil];
|
||||
if(view) {
|
||||
NSTableCellView *cellView = (NSTableCellView *)view;
|
||||
|
||||
if(cellView.textField) {
|
||||
cellView.textField.allowsDefaultTighteningForTruncation = YES;
|
||||
|
||||
NSFont *font = [NSFont monospacedDigitSystemFontOfSize:fontSize weight:NSFontWeightRegular];
|
||||
|
||||
cellView.textField.font = font;
|
||||
cellView.textField.stringValue = cellText;
|
||||
cellView.textField.alignment = cellTextAlignment;
|
||||
|
||||
if(cellView.textField.intrinsicContentSize.width > cellView.textField.frame.size.width - 4) {
|
||||
cellView.textField.toolTip = cellText;
|
||||
}
|
||||
}
|
||||
if(cellButton) {
|
||||
[cellView setSubviews:@[cellButton]];
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
- (IBAction)applyPaths:(id)sender {
|
||||
for(PathItem *pi in [pathsList arrangedObjects]) {
|
||||
if(pi.enabled) {
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setCanChooseDirectories:YES];
|
||||
[panel setCanChooseFiles:NO];
|
||||
[panel setFloatingPanel:YES];
|
||||
[panel setDirectoryURL:[NSURL fileURLWithPath:pi.path]];
|
||||
[panel setTitle:@"Open to add path"];
|
||||
NSInteger result = [panel runModal];
|
||||
if(result == NSModalResponseOK) {
|
||||
[sandboxPathBehaviorController addUrl:[panel URL]];
|
||||
}
|
||||
}
|
||||
}
|
||||
[[self window] orderOut:self];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,156 @@
|
|||
<?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" customObjectInstantitationMethod="direct">
|
||||
<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="PathSuggester">
|
||||
<connections>
|
||||
<outlet property="pathsList" destination="co7-fb-ZfR" id="Gfj-wX-lfj"/>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uJf-BX-nGb">
|
||||
<rect key="frame" x="395" y="13" width="69" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Apply" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Jne-2G-yP9">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="applyPaths:" target="-2" id="aP1-hj-jCO"/>
|
||||
</connections>
|
||||
</button>
|
||||
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qDP-bP-gt0">
|
||||
<rect key="frame" x="20" y="53" width="440" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" id="o8s-wX-Hz7">
|
||||
<rect key="frame" x="1" y="1" width="438" height="195"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" rowSizeStyle="automatic" headerView="0f4-Mi-48L" viewBased="YES" id="12l-iE-HGP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="438" height="167"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="enabled" width="51" minWidth="40" maxWidth="1000" id="QWk-Qb-l0y">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="w2T-DH-7x9">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="IEa-52-JSd">
|
||||
<rect key="frame" x="8" y="0.0" width="49" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JEu-SS-Ht2">
|
||||
<rect key="frame" x="0.0" y="4" width="49" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="RLN-Pa-Mhl">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="textField" destination="JEu-SS-Ht2" id="nHb-KP-VcE"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="co7-fb-ZfR" name="value" keyPath="arrangedObjects.enabled" id="AYu-Hb-5Oz"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="path" width="336.5" minWidth="40" maxWidth="1000" id="23r-ai-XoX">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Path">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="2vN-j2-JYP">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="Myn-dz-D1X">
|
||||
<rect key="frame" x="74" y="0.0" width="334" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3sg-yr-7pE">
|
||||
<rect key="frame" x="0.0" y="4" width="334" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="xFm-ed-yBb">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="textField" destination="3sg-yr-7pE" id="QRi-Ud-o8O"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="co7-fb-ZfR" name="value" keyPath="arrangedObjects.path" id="6z5-hZ-eM6"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="co7-fb-ZfR" id="fsu-oW-sXa"/>
|
||||
<outlet property="delegate" destination="-2" id="e4J-bb-n4Z"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Uts-zz-KoA">
|
||||
<rect key="frame" x="1" y="181" width="438" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="UOh-zJ-0eC">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<tableHeaderView key="headerView" wantsLayer="YES" id="0f4-Mi-48L">
|
||||
<rect key="frame" x="0.0" y="0.0" width="438" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="139" y="90"/>
|
||||
</window>
|
||||
<arrayController id="co7-fb-ZfR" userLabel="Paths Array Controller">
|
||||
<declaredKeys>
|
||||
<string>path</string>
|
||||
<string>enabled</string>
|
||||
</declaredKeys>
|
||||
</arrayController>
|
||||
</objects>
|
||||
</document>
|
|
@ -27,6 +27,8 @@
|
|||
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */; };
|
||||
8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; };
|
||||
83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */; };
|
||||
83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AC57382861A54D009D6F50 /* PathSuggester.m */; };
|
||||
83AC573B2861A54D009D6F50 /* PathSuggester.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83AC57392861A54D009D6F50 /* PathSuggester.xib */; };
|
||||
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; };
|
||||
83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; };
|
||||
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; };
|
||||
|
@ -118,6 +120,10 @@
|
|||
8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; };
|
||||
83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorToValueTransformer.h; sourceTree = "<group>"; };
|
||||
83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorToValueTransformer.m; sourceTree = "<group>"; };
|
||||
83AC57372861A54D009D6F50 /* PathSuggester.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PathSuggester.h; sourceTree = "<group>"; };
|
||||
83AC57382861A54D009D6F50 /* PathSuggester.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PathSuggester.m; sourceTree = "<group>"; };
|
||||
83AC57392861A54D009D6F50 /* PathSuggester.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PathSuggester.xib; sourceTree = "<group>"; };
|
||||
83AC573E2861B77E009D6F50 /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
|
||||
83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; };
|
||||
83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = "<group>"; };
|
||||
83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = "<group>"; };
|
||||
|
@ -199,6 +205,7 @@
|
|||
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83AC573E2861B77E009D6F50 /* SandboxBroker.h */,
|
||||
8307D30828604ECF000FF8EB /* PlaylistController.h */,
|
||||
83F27E711810E41A00CEF538 /* Transformers */,
|
||||
8384913618081ECB00E7332D /* Logging.h */,
|
||||
|
@ -266,6 +273,9 @@
|
|||
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */,
|
||||
8307D30328604D8B000FF8EB /* SandboxPathBehaviorController.h */,
|
||||
8307D30428604D8B000FF8EB /* SandboxPathBehaviorController.m */,
|
||||
83AC57372861A54D009D6F50 /* PathSuggester.h */,
|
||||
83AC57382861A54D009D6F50 /* PathSuggester.m */,
|
||||
83AC57392861A54D009D6F50 /* PathSuggester.xib */,
|
||||
);
|
||||
name = Custom;
|
||||
sourceTree = "<group>";
|
||||
|
@ -452,6 +462,7 @@
|
|||
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */,
|
||||
83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */,
|
||||
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */,
|
||||
83AC573B2861A54D009D6F50 /* PathSuggester.xib in Resources */,
|
||||
83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */,
|
||||
17C7E5B00DCCC30A003CBCF7 /* playlist.png in Resources */,
|
||||
83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */,
|
||||
|
@ -465,6 +476,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */,
|
||||
83651DA527322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m in Sources */,
|
||||
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */,
|
||||
8E07AA880AAC8EA200A4B32F /* HotKeyPane.m in Sources */,
|
||||
|
|
|
@ -13,6 +13,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)addUrl:(NSURL *)url;
|
||||
- (void)removePath:(NSString *)path;
|
||||
- (void)removeStaleEntries;
|
||||
|
||||
- (BOOL)matchesPath:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#import "PlaylistController.h"
|
||||
|
||||
#import "SandboxBroker.h"
|
||||
|
||||
@interface SandboxToken : NSManagedObject
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
@property(nonatomic, strong) NSData *bookmark;
|
||||
|
@ -102,4 +104,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)removeStaleEntries {
|
||||
for(NSDictionary *entry in [[self arrangedObjects] copy]) {
|
||||
if([[entry objectForKey:@"valid"] isEqualToString:NSLocalizedPrefString(@"ValidNo")]) {
|
||||
[self removeObject:entry];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)matchesPath:(NSURL *)url {
|
||||
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
|
||||
for(NSDictionary *entry in [self arrangedObjects]) {
|
||||
NSURL *testPath = [NSURL fileURLWithPath:[entry objectForKey:@"path"]];
|
||||
if([sandboxBrokerClass isPath:url aSubdirectoryOf:testPath])
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,6 +15,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (SandboxBroker *)sharedSandboxBroker;
|
||||
|
||||
+ (NSURL *)urlWithoutFragment:(NSURL *)url;
|
||||
+ (BOOL)isPath:(NSURL *)path aSubdirectoryOf:(NSURL *)directory;
|
||||
|
||||
- (id)init;
|
||||
- (void)shutdown;
|
||||
|
||||
|
|
|
@ -19,30 +19,6 @@
|
|||
|
||||
static SandboxBroker *kSharedSandboxBroker = nil;
|
||||
|
||||
static NSURL *urlWithoutFragment(NSURL *u) {
|
||||
if(![u isFileURL]) return u;
|
||||
|
||||
NSNumber *isDirectory;
|
||||
|
||||
BOOL success = [u getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
|
||||
|
||||
if(success && [isDirectory boolValue]) return u;
|
||||
|
||||
NSString *s = [u path];
|
||||
|
||||
NSString *lastComponent = [u lastPathComponent];
|
||||
|
||||
// Find that last component in the string from the end to make sure
|
||||
// to get the last one
|
||||
NSRange fragmentRange = [s rangeOfString:lastComponent
|
||||
options:NSBackwardsSearch];
|
||||
|
||||
// Chop the fragment.
|
||||
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
|
||||
|
||||
return [NSURL fileURLWithPath:newURLString];
|
||||
}
|
||||
|
||||
@interface SandboxEntry : NSObject {
|
||||
SandboxToken *_token;
|
||||
NSInteger _refCount;
|
||||
|
@ -110,6 +86,30 @@ static NSURL *urlWithoutFragment(NSURL *u) {
|
|||
return [NSClassFromString(@"PlaylistController") sharedPersistentContainer];
|
||||
}
|
||||
|
||||
+ (NSURL *)urlWithoutFragment:(NSURL *)url {
|
||||
if(![url isFileURL]) return url;
|
||||
|
||||
NSNumber *isDirectory;
|
||||
|
||||
BOOL success = [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
|
||||
|
||||
if(success && [isDirectory boolValue]) return url;
|
||||
|
||||
NSString *s = [url path];
|
||||
|
||||
NSString *lastComponent = [url lastPathComponent];
|
||||
|
||||
// Find that last component in the string from the end to make sure
|
||||
// to get the last one
|
||||
NSRange fragmentRange = [s rangeOfString:lastComponent
|
||||
options:NSBackwardsSearch];
|
||||
|
||||
// Chop the fragment.
|
||||
NSString *newURLString = [s substringToIndex:fragmentRange.location + fragmentRange.length];
|
||||
|
||||
return [NSURL fileURLWithPath:newURLString];
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
id _self = [super init];
|
||||
if(_self) {
|
||||
|
@ -188,7 +188,7 @@ static NSURL *urlWithoutFragment(NSURL *u) {
|
|||
}
|
||||
|
||||
- (const void *)beginFolderAccess:(NSURL *)fileUrl {
|
||||
NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent];
|
||||
NSURL *folderUrl = [[SandboxBroker urlWithoutFragment:fileUrl] URLByDeletingLastPathComponent];
|
||||
if(![folderUrl isFileURL]) return NULL;
|
||||
|
||||
SandboxEntry *entry;
|
||||
|
|
Loading…
Reference in New Issue