Archived
1

Added 'Tags' preferences with saving behaviors for each tag

This commit is contained in:
Kim Wittenburg
2015-09-04 17:23:45 +02:00
parent b851b70ddb
commit 45f664cb10
6 changed files with 453 additions and 81 deletions

View File

@@ -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;
};

View File

@@ -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>

View 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
}
}

View File

@@ -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)!) }
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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