Added 'Tags' preferences with saving behaviors for each tag
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
3B285DB81B9128C100F0A2F1 /* PreferencesTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B285DB71B9128C100F0A2F1 /* PreferencesTabViewController.swift */; };
|
||||
3B285DB81B9128C100F0A2F1 /* Preference Controllers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B285DB71B9128C100F0A2F1 /* Preference Controllers.swift */; };
|
||||
3B285DBF1B912AB700F0A2F1 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B285DBE1B912AB700F0A2F1 /* Preferences.swift */; };
|
||||
3B489DBF1B90B055002B7EB3 /* TrackTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B489DBD1B90B055002B7EB3 /* TrackTableCellView.swift */; };
|
||||
3B489DC31B90B116002B7EB3 /* Artwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B489DC01B90B116002B7EB3 /* Artwork.swift */; };
|
||||
@@ -44,7 +44,7 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
3B285DB71B9128C100F0A2F1 /* PreferencesTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesTabViewController.swift; sourceTree = "<group>"; };
|
||||
3B285DB71B9128C100F0A2F1 /* Preference Controllers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Preference Controllers.swift"; sourceTree = "<group>"; };
|
||||
3B285DBE1B912AB700F0A2F1 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||
3B489DBD1B90B055002B7EB3 /* TrackTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TrackTableCellView.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
3B489DC01B90B116002B7EB3 /* Artwork.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Artwork.swift; sourceTree = "<group>"; };
|
||||
@@ -178,7 +178,7 @@
|
||||
children = (
|
||||
3B76C7721B909B280025D550 /* AppDelegate.swift */,
|
||||
3B76C7741B909B280025D550 /* MainViewController.swift */,
|
||||
3B285DB71B9128C100F0A2F1 /* PreferencesTabViewController.swift */,
|
||||
3B285DB71B9128C100F0A2F1 /* Preference Controllers.swift */,
|
||||
);
|
||||
name = Controller;
|
||||
sourceTree = "<group>";
|
||||
@@ -341,7 +341,7 @@
|
||||
3B489DC41B90B116002B7EB3 /* Album.swift in Sources */,
|
||||
3B489DC51B90B116002B7EB3 /* Track.swift in Sources */,
|
||||
3B489DC71B90B38C002B7EB3 /* iTunes.swift in Sources */,
|
||||
3B285DB81B9128C100F0A2F1 /* PreferencesTabViewController.swift in Sources */,
|
||||
3B285DB81B9128C100F0A2F1 /* Preference Controllers.swift in Sources */,
|
||||
3B489DCB1B90B3E3002B7EB3 /* iTunes.m in Sources */,
|
||||
3B489DD61B90E0D8002B7EB3 /* AlbumTableCellView.swift in Sources */,
|
||||
);
|
||||
@@ -476,12 +476,14 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = TagTunes/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = wittenburg.kim.TagTunes;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "TagTunes/TagTunes-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -491,11 +493,13 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = TagTunes/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = wittenburg.kim.TagTunes;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "TagTunes/TagTunes-Bridging-Header.h";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -725,6 +725,7 @@ CA
|
||||
<tabViewController tabStyle="toolbar" id="qfM-ma-lyn" customClass="PreferencesViewController" customModule="AppKitPlus" sceneMemberID="viewController">
|
||||
<tabViewItems>
|
||||
<tabViewItem image="NSPreferencesGeneral" id="YSn-2h-2B5"/>
|
||||
<tabViewItem image="PreferenceTags" id="6Mu-Ps-mDj"/>
|
||||
</tabViewItems>
|
||||
<viewControllerTransitionOptions key="transitionOptions" allowUserInteraction="YES"/>
|
||||
<tabView key="tabView" type="noTabsNoBorder" id="UTE-Sb-ieY">
|
||||
@@ -738,6 +739,7 @@ CA
|
||||
</tabView>
|
||||
<connections>
|
||||
<segue destination="tzd-4a-CRb" kind="relationship" relationship="tabItems" id="8pq-ZH-tzf"/>
|
||||
<segue destination="qlM-h3-Tfw" kind="relationship" relationship="tabItems" id="Qgp-Md-Wfq"/>
|
||||
</connections>
|
||||
</tabViewController>
|
||||
<customObject id="5zh-wl-nwU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
@@ -838,6 +840,172 @@ CA
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1031" y="1051"/>
|
||||
</scene>
|
||||
<!--Tags-->
|
||||
<scene sceneID="oNy-iK-NsG">
|
||||
<objects>
|
||||
<viewController title="Tags" id="qlM-h3-Tfw" customClass="TagsPreferencesViewController" customModule="TagTunes" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="caz-ze-bnI" customClass="PreferenceView" customModule="AppKitPlus">
|
||||
<rect key="frame" x="0.0" y="0.0" width="450" height="325"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="0Za-Q2-FET">
|
||||
<rect key="frame" x="18" y="289" width="157" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Use Censored Names" bezelStyle="regularSquare" imagePosition="left" enabled="NO" state="on" inset="2" id="UOJ-Ol-UjW">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="7Ts-a9-Cpv" name="value" keyPath="useCensoredNames" id="LVi-CS-iz7">
|
||||
<dictionary key="options">
|
||||
<bool key="NSConditionallySetsEnabled" value="NO"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wkM-Sb-z0R">
|
||||
<rect key="frame" x="18" y="241" width="414" height="34"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Some tags are not provided by the iTunes Search API. TagTunes can only clear these tags." id="8L8-Nr-zgu">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="23" horizontalPageScroll="10" verticalLineScroll="23" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cTv-EP-MQX">
|
||||
<rect key="frame" x="20" y="20" width="410" height="213"/>
|
||||
<clipView key="contentView" ambiguous="YES" id="awx-Qa-0a0">
|
||||
<rect key="frame" x="1" y="1" width="408" height="211"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="21" rowSizeStyle="automatic" viewBased="YES" id="WCb-HH-YPh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="408" height="211"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="tagColumn" editable="NO" width="292" minWidth="40" maxWidth="1000" id="GgR-1E-8ur">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<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="QH5-6N-kJ8">
|
||||
<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 identifier="textCell" id="Zbr-i2-c1G">
|
||||
<rect key="frame" x="1" y="1" width="292" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vww-dF-uIe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="100" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="hxe-jr-7rI">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vww-dF-uIe" firstAttribute="centerY" secondItem="Zbr-i2-c1G" secondAttribute="centerY" id="GmY-MF-H9J"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vww-dF-uIe" secondAttribute="trailing" constant="83" id="Zem-p3-9DR"/>
|
||||
<constraint firstItem="vww-dF-uIe" firstAttribute="leading" secondItem="Zbr-i2-c1G" secondAttribute="leading" constant="2" id="ib0-Lg-5Ts"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="vww-dF-uIe" id="a8W-tw-hGo"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="savingBehaviorColumn" width="110" minWidth="40" maxWidth="1000" id="IPU-Kp-l3A">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<popUpButtonCell key="dataCell" type="bevel" title="Ignore" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="bezel" imageScaling="proportionallyDown" inset="2" arrowPosition="arrowAtCenter" preferredEdge="maxY" selectedItem="idt-SA-nnz" id="1Sz-1n-thR">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="3zQ-Pc-Wgf">
|
||||
<items>
|
||||
<menuItem title="Save" id="dA3-hx-uqV"/>
|
||||
<menuItem title="Clear" id="ZNC-Us-FDj"/>
|
||||
<menuItem title="Ignore" state="on" id="idt-SA-nnz"/>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="qlM-h3-Tfw" id="dAa-aM-qIx"/>
|
||||
</connections>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<popUpButton identifier="popupCell" id="Cxj-hO-zFk">
|
||||
<rect key="frame" x="296" y="1" width="110" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="bevel" title="Ignore" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" selectedItem="Zxi-Bn-dST" id="Och-nm-1Kl">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="E1q-CP-zfN">
|
||||
<items>
|
||||
<menuItem title="Save" id="zVz-3W-UA1"/>
|
||||
<menuItem title="Clear" id="5fq-yr-okX"/>
|
||||
<menuItem title="Ignore" state="on" id="Zxi-Bn-dST"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="savingBehaviorChanged:" target="qlM-h3-Tfw" id="B4Y-w0-fay"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="qlM-h3-Tfw" id="QYi-UE-Crv"/>
|
||||
<outlet property="delegate" destination="qlM-h3-Tfw" id="yBy-gW-UJu"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Q38-Np-JBF">
|
||||
<rect key="frame" x="1" y="196" width="408" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="255-EG-UwP">
|
||||
<rect key="frame" x="-15" y="17" width="16" height="0.0"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="0Za-Q2-FET" firstAttribute="top" secondItem="caz-ze-bnI" secondAttribute="top" constant="20" symbolic="YES" id="4hi-D6-aIL"/>
|
||||
<constraint firstItem="wkM-Sb-z0R" firstAttribute="top" secondItem="0Za-Q2-FET" secondAttribute="bottom" constant="16" id="AFD-39-mPY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wkM-Sb-z0R" secondAttribute="trailing" constant="20" symbolic="YES" id="PzL-bB-J8O"/>
|
||||
<constraint firstItem="0Za-Q2-FET" firstAttribute="leading" secondItem="wkM-Sb-z0R" secondAttribute="leading" id="bGY-79-Gc5"/>
|
||||
<constraint firstItem="0Za-Q2-FET" firstAttribute="leading" secondItem="caz-ze-bnI" secondAttribute="leading" constant="20" symbolic="YES" id="zoK-yx-jUT"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="width">
|
||||
<real key="value" value="450"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="height">
|
||||
<real key="value" value="325"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="WCb-HH-YPh" id="Jje-KL-XOf"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="w7s-ZN-87L" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<customObject id="7Ts-a9-Cpv" customClass="Preferences" customModule="TagTunes" customModuleProvider="target"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-470" y="1149.5"/>
|
||||
</scene>
|
||||
<!--Main View Controller-->
|
||||
<scene sceneID="hIz-AP-VOD">
|
||||
<objects>
|
||||
@@ -925,6 +1093,7 @@ CA
|
||||
<image name="Cross" width="1024" height="1024"/>
|
||||
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
||||
<image name="Note" width="1024" height="1024"/>
|
||||
<image name="PreferenceTags" width="730" height="730"/>
|
||||
<image name="Save" width="1024" height="1024"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
119
TagTunes/Preference Controllers.swift
Normal file
119
TagTunes/Preference Controllers.swift
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// PreferencesTabViewController.swift
|
||||
// Harmony
|
||||
//
|
||||
// Created by Kim Wittenburg on 26.01.15.
|
||||
// Copyright (c) 2015 Das Code Kollektiv. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
internal class GeneralPreferencesViewController: NSViewController {
|
||||
|
||||
// MARK: IBOutlets
|
||||
|
||||
@IBOutlet internal weak var artworkPathControl: NSPathControl!
|
||||
@IBOutlet internal weak var chooseArtworkButton: NSButton!
|
||||
|
||||
override internal func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
artworkPathControl.URL = Preferences.sharedPreferences.artworkTarget
|
||||
saveArtworkStateChanged(self)
|
||||
}
|
||||
|
||||
@IBAction internal func saveArtworkStateChanged(sender: AnyObject) {
|
||||
artworkPathControl.enabled = Preferences.sharedPreferences.saveArtwork
|
||||
chooseArtworkButton.enabled = Preferences.sharedPreferences.saveArtwork
|
||||
}
|
||||
|
||||
@IBAction internal func chooseArtworkPath(sender: AnyObject) {
|
||||
let openPanel = NSOpenPanel()
|
||||
openPanel.canChooseDirectories = true
|
||||
openPanel.canChooseFiles = false
|
||||
openPanel.canCreateDirectories = true
|
||||
openPanel.prompt = NSLocalizedString("Choose…", comment: "Button title prompting the user to choose a directory")
|
||||
openPanel.beginSheetModalForWindow(view.window!) {
|
||||
result in
|
||||
if result == NSModalResponseOK {
|
||||
Preferences.sharedPreferences.artworkTarget = openPanel.URL!.filePathURL!
|
||||
self.artworkPathControl.URL = openPanel.URL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class TagsPreferencesViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate, NSMenuDelegate {
|
||||
|
||||
// MARK: Types
|
||||
|
||||
private struct TableViewConstants {
|
||||
|
||||
static let textTableCellViewIdentifier = "textCell"
|
||||
|
||||
static let popupTableCellViewIdentifier = "popupCell"
|
||||
|
||||
static let tagTableColumnIdentifier = "tagColumn"
|
||||
|
||||
static let savingBehaviorTableColumnIdentifier = "savingBehaviorColumn"
|
||||
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
|
||||
// MARK: Table View
|
||||
|
||||
internal func numberOfRowsInTableView(tableView: NSTableView) -> Int {
|
||||
return Track.Tag.allTags.count
|
||||
}
|
||||
|
||||
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
let tag = Track.Tag.allTags[row]
|
||||
if tableColumn?.identifier == TableViewConstants.tagTableColumnIdentifier {
|
||||
let view = tableView.makeViewWithIdentifier(TableViewConstants.textTableCellViewIdentifier, owner: nil) as? NSTableCellView
|
||||
view?.textField?.stringValue = tag.localizedName
|
||||
return view
|
||||
} else if tableColumn?.identifier == TableViewConstants.savingBehaviorTableColumnIdentifier {
|
||||
let popupButton = tableView.makeViewWithIdentifier(TableViewConstants.popupTableCellViewIdentifier, owner: nil) as? NSPopUpButton
|
||||
popupButton?.removeAllItems()
|
||||
if tag.isReturnedBySearchAPI {
|
||||
popupButton?.addItemWithTitle(NSLocalizedString("Save", comment: "Menu item title for a tag that is going to be saved"))
|
||||
}
|
||||
popupButton?.addItemsWithTitles([
|
||||
NSLocalizedString("Clear", comment: "Menu item title for a tag that is going to be cleared"),
|
||||
NSLocalizedString("Ignore", comment: "Menu item title for a tag that is not going to be saved")])
|
||||
var selectedIndex: Int
|
||||
switch Preferences.sharedPreferences.tagSavingBehaviors[tag]! {
|
||||
case .Save:
|
||||
selectedIndex = 0
|
||||
case .Clear:
|
||||
selectedIndex = tag.isReturnedBySearchAPI ? 1 : 0
|
||||
case .Ignore:
|
||||
selectedIndex = tag.isReturnedBySearchAPI ? 2 : 1
|
||||
}
|
||||
popupButton?.selectItemAtIndex(selectedIndex)
|
||||
return popupButton
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@IBAction private func savingBehaviorChanged(sender: NSPopUpButton) {
|
||||
let tag = Track.Tag.allTags[tableView.rowForView(sender)]
|
||||
let selectedIndex = sender.indexOfItem(sender.selectedItem!)
|
||||
var savingBehavior = Preferences.sharedPreferences.tagSavingBehaviors[tag]!
|
||||
switch selectedIndex {
|
||||
case 0:
|
||||
savingBehavior = tag.isReturnedBySearchAPI ? .Save : .Clear
|
||||
case 1:
|
||||
savingBehavior = tag.isReturnedBySearchAPI ? .Clear : .Ignore
|
||||
case 2:
|
||||
savingBehavior = .Ignore
|
||||
default:
|
||||
break
|
||||
}
|
||||
Preferences.sharedPreferences.tagSavingBehaviors[tag] = savingBehavior
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,38 @@ import Cocoa
|
||||
/// All properties in this class are KCO compliant.
|
||||
@objc public class Preferences: NSObject {
|
||||
|
||||
// MARK: Types
|
||||
|
||||
private struct UserDefaultsConstants {
|
||||
|
||||
static let saveArtworkKey = "Save Artwork"
|
||||
|
||||
static let keepSearchResultsKey = "Keep Search Results"
|
||||
|
||||
static let removeSavedAlbumsKey = "Remove Saved Albums"
|
||||
|
||||
static let artworkTargetKey = "Artwork Target"
|
||||
|
||||
static let useCensoredNamesKey = "Use Censored Names"
|
||||
|
||||
static let tagSavingBehaviorsKey = "Tag Saving Behaviors"
|
||||
}
|
||||
|
||||
public enum TagSavingBehavior: String {
|
||||
|
||||
/// Sets the tag's value to the value returned from the Search API.
|
||||
case Save = "save"
|
||||
|
||||
/// Sets the tag's value to an empty string.
|
||||
case Clear = "clear"
|
||||
|
||||
/// Does not alter the tag's value.
|
||||
case Ignore = "ignore"
|
||||
|
||||
}
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
public static var sharedPreferences = Preferences()
|
||||
|
||||
/// Initializes the default preferences. This method must be called the very
|
||||
@@ -22,24 +54,35 @@ import Cocoa
|
||||
/// this method every time the application launches. Existing values are not
|
||||
/// overridden.
|
||||
public func initializeDefaultValues() {
|
||||
let initialArtworkFolder = NSURL.fileURLWithPath(NSHomeDirectory(), isDirectory: true)
|
||||
NSUserDefaults.standardUserDefaults().registerDefaults([
|
||||
"Save Artwork": false,
|
||||
"Keep Search Results": false,
|
||||
"Remove Saved Albums": false])
|
||||
if NSUserDefaults.standardUserDefaults().URLForKey("Artwork Target") == nil {
|
||||
NSUserDefaults.standardUserDefaults().setURL(initialArtworkFolder, forKey: "Artwork Target")
|
||||
UserDefaultsConstants.saveArtworkKey: false,
|
||||
UserDefaultsConstants.keepSearchResultsKey: false,
|
||||
UserDefaultsConstants.removeSavedAlbumsKey: false,
|
||||
UserDefaultsConstants.useCensoredNamesKey: false
|
||||
])
|
||||
if NSUserDefaults.standardUserDefaults().dictionaryForKey(UserDefaultsConstants.tagSavingBehaviorsKey) == nil {
|
||||
var savingBehaviors: [Track.Tag: TagSavingBehavior] = [:]
|
||||
for tag in Track.Tag.allTags {
|
||||
savingBehaviors[tag] = tag.isReturnedBySearchAPI ? .Save : .Clear
|
||||
}
|
||||
tagSavingBehaviors = savingBehaviors
|
||||
}
|
||||
let initialArtworkFolder = NSURL.fileURLWithPath(NSFileManager.defaultManager().URLsForDirectory(.DownloadsDirectory, inDomains: .UserDomainMask)[0].filePathURL!.path!, isDirectory: true)
|
||||
if NSUserDefaults.standardUserDefaults().URLForKey(UserDefaultsConstants.artworkTargetKey) == nil {
|
||||
artworkTarget = initialArtworkFolder
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: General Preferences
|
||||
|
||||
/// If `true` the album artwork should be saved to the `artworkTarget` URL
|
||||
/// when an item is saved.
|
||||
public dynamic var saveArtwork: Bool {
|
||||
set {
|
||||
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: "Save Artwork")
|
||||
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: UserDefaultsConstants.saveArtworkKey)
|
||||
}
|
||||
get {
|
||||
return NSUserDefaults.standardUserDefaults().boolForKey("Save Artwork")
|
||||
return NSUserDefaults.standardUserDefaults().boolForKey(UserDefaultsConstants.saveArtworkKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +91,10 @@ import Cocoa
|
||||
/// The URL must be a valid file URL pointing to a directory.
|
||||
public dynamic var artworkTarget: NSURL {
|
||||
set {
|
||||
NSUserDefaults.standardUserDefaults().setURL(newValue, forKey: "Artwork Target")
|
||||
NSUserDefaults.standardUserDefaults().setURL(newValue, forKey: UserDefaultsConstants.artworkTargetKey)
|
||||
}
|
||||
get {
|
||||
return NSUserDefaults.standardUserDefaults().URLForKey("Artwork Target")!
|
||||
return NSUserDefaults.standardUserDefaults().URLForKey(UserDefaultsConstants.artworkTargetKey)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,11 +102,36 @@ import Cocoa
|
||||
/// when the user selects a result.
|
||||
public dynamic var keepSearchResults: Bool {
|
||||
set {
|
||||
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: "Keep Search Results")
|
||||
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: UserDefaultsConstants.keepSearchResultsKey)
|
||||
}
|
||||
get {
|
||||
return NSUserDefaults.standardUserDefaults().boolForKey("Keep Search Results")
|
||||
return NSUserDefaults.standardUserDefaults().boolForKey(UserDefaultsConstants.keepSearchResultsKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Tag Preferences
|
||||
|
||||
/// If `true` TagTunes displays and saves censored names instead of the
|
||||
/// original names.
|
||||
public dynamic var useCensoredNames: Bool {
|
||||
set {
|
||||
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: UserDefaultsConstants.useCensoredNamesKey)
|
||||
}
|
||||
get {
|
||||
return NSUserDefaults.standardUserDefaults().boolForKey(UserDefaultsConstants.useCensoredNamesKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// The ways different tags are saved (or not saved).
|
||||
public var tagSavingBehaviors: [Track.Tag: TagSavingBehavior] {
|
||||
set {
|
||||
let savableData = newValue.map { ($0.rawValue, $1.rawValue) }
|
||||
NSUserDefaults.standardUserDefaults().setObject(savableData, forKey: UserDefaultsConstants.tagSavingBehaviorsKey)
|
||||
}
|
||||
get {
|
||||
let savableData = NSUserDefaults.standardUserDefaults().dictionaryForKey(UserDefaultsConstants.tagSavingBehaviorsKey)!
|
||||
return savableData.map { (Track.Tag(rawValue: $0)!, TagSavingBehavior(rawValue: $1 as! String)!) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// PreferencesTabViewController.swift
|
||||
// Harmony
|
||||
//
|
||||
// Created by Kim Wittenburg on 26.01.15.
|
||||
// Copyright (c) 2015 Das Code Kollektiv. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
internal class GeneralPreferencesViewController: NSViewController {
|
||||
|
||||
// MARK: IBOutlets
|
||||
|
||||
@IBOutlet internal weak var artworkPathControl: NSPathControl!
|
||||
@IBOutlet internal weak var chooseArtworkButton: NSButton!
|
||||
|
||||
override internal func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
artworkPathControl.URL = Preferences.sharedPreferences.artworkTarget
|
||||
saveArtworkStateChanged(self)
|
||||
}
|
||||
|
||||
@IBAction internal func saveArtworkStateChanged(sender: AnyObject) {
|
||||
artworkPathControl.enabled = Preferences.sharedPreferences.saveArtwork
|
||||
chooseArtworkButton.enabled = Preferences.sharedPreferences.saveArtwork
|
||||
}
|
||||
|
||||
@IBAction internal func chooseArtworkPath(sender: AnyObject) {
|
||||
let openPanel = NSOpenPanel()
|
||||
openPanel.canChooseDirectories = true
|
||||
openPanel.canChooseFiles = false
|
||||
openPanel.canCreateDirectories = true
|
||||
openPanel.prompt = NSLocalizedString("Choose…", comment: "Button title prompting the user to choose a directory")
|
||||
openPanel.beginSheetModalForWindow(view.window!) {
|
||||
result in
|
||||
if result == NSModalResponseOK {
|
||||
Preferences.sharedPreferences.artworkTarget = openPanel.URL!.filePathURL!
|
||||
self.artworkPathControl.URL = openPanel.URL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -12,6 +12,46 @@ import Cocoa
|
||||
/// [Search API](https://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html).
|
||||
public class Track: iTunesType {
|
||||
|
||||
// MARK: Types
|
||||
|
||||
public enum Tag: String {
|
||||
case Name = "name", Artist = "artist", Year = "year", TrackNumber = "trackNumber", TrackCount = "trackCount", DiscNumber = "discNumber", DiscCount = "discCount", Genre = "genre", AlbumName = "album", AlbumArtist = "albumArtist"
|
||||
case SortName = "sortName", SortArtist = "sortArtist", SortAlbumName = "sortAlbum", SortAlbumArtist = "sortAlbumArtist", Composer = "composer", SortComposer = "sortComposer", Comment = "comment"
|
||||
|
||||
/// Returns `true` for tags that are returned from the
|
||||
/// [Search API](https://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html).
|
||||
public var isReturnedBySearchAPI: Bool {
|
||||
switch self {
|
||||
case .Name, .Artist, .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount, .Genre, .AlbumName, .AlbumArtist:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a string identifying the respective tag that can be displayed
|
||||
/// to the user.
|
||||
public var localizedName: String {
|
||||
return NSLocalizedString(self.rawValue, comment: "")
|
||||
}
|
||||
|
||||
/// Returns the object that should be saved to *clear* the tag.
|
||||
public var clearedValue: AnyObject? {
|
||||
switch self {
|
||||
case .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount:
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array of all tags.
|
||||
public static var allTags: [Tag] {
|
||||
return [.Name, .Artist, .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount, .Genre, .AlbumName, .AlbumArtist, .SortName, .SortArtist, .SortAlbumName, .SortAlbumArtist, .Composer, .SortComposer, .Comment]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
public let id: iTunesId
|
||||
@@ -82,24 +122,42 @@ public class Track: iTunesType {
|
||||
/// This method respects the user's preferences (See `Preferences` class).
|
||||
public func saveToTrack(track: iTunesTrack) {
|
||||
let components = NSCalendar.currentCalendar().components(.Year, fromDate: releaseDate)
|
||||
track.name = name
|
||||
track.artist = artistName
|
||||
track.year = components.year
|
||||
track.trackNumber = trackNumber
|
||||
track.trackCount = trackCount
|
||||
track.discNumber = discNumber
|
||||
track.discCount = discCount
|
||||
track.genre = genre
|
||||
track.album = album?.name
|
||||
track.albumArtist = album?.artistName
|
||||
track.sortName = ""
|
||||
track.sortAlbum = ""
|
||||
track.sortAlbumArtist = ""
|
||||
track.sortArtist = ""
|
||||
track.composer = ""
|
||||
track.sortComposer = ""
|
||||
track.comment = ""
|
||||
track.artworks().removeAllObjects()
|
||||
let trackName = Preferences.sharedPreferences.useCensoredNames ? censoredName : name
|
||||
saveTag(.Name, toTrack: track, value: trackName)
|
||||
saveTag(.Artist, toTrack: track, value: artistName)
|
||||
saveTag(.Year, toTrack: track, value: components.year)
|
||||
saveTag(.TrackNumber, toTrack: track, value: trackNumber)
|
||||
saveTag(.TrackCount, toTrack: track, value: trackCount)
|
||||
saveTag(.DiscNumber, toTrack: track, value: discNumber)
|
||||
saveTag(.DiscCount, toTrack: track, value: discCount)
|
||||
saveTag(.Genre, toTrack: track, value: genre)
|
||||
let albumName = Preferences.sharedPreferences.useCensoredNames ? album.censoredName : album.name
|
||||
saveTag(.AlbumName, toTrack: track, value: albumName)
|
||||
saveTag(.AlbumArtist, toTrack: track, value: album.artistName)
|
||||
saveTag(.SortName, toTrack: track, value: nil)
|
||||
saveTag(.SortArtist, toTrack: track, value: nil)
|
||||
saveTag(.SortAlbumName, toTrack: track, value: nil)
|
||||
saveTag(.SortAlbumArtist, toTrack: track, value: nil)
|
||||
saveTag(.Composer, toTrack: track, value: nil)
|
||||
saveTag(.SortComposer, toTrack: track, value: nil)
|
||||
saveTag(.Comment, toTrack: track, value: nil)
|
||||
|
||||
// TODO: Deal with artworks
|
||||
// if Preferences.sharedPreferences.clearArtworks {
|
||||
// track.artworks().removeAllObjects()
|
||||
// }
|
||||
}
|
||||
|
||||
private func saveTag(tag: Tag, toTrack track: iTunesTrack, value: AnyObject?) {
|
||||
switch Preferences.sharedPreferences.tagSavingBehaviors[tag]! {
|
||||
case .Save:
|
||||
track.setValue(value, forKey: tag.rawValue)
|
||||
case .Clear:
|
||||
track.setValue(tag.clearedValue, forKey: tag.rawValue)
|
||||
case .Ignore:
|
||||
break
|
||||
}
|
||||
track.setValue(value, forKey: tag.rawValue)
|
||||
}
|
||||
|
||||
/// Returns `true` if all `associatedTrack`s contain the same values as the
|
||||
|
||||
Reference in New Issue
Block a user