Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions Base.lproj/GeneralPrefsViewController.xib
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="GeneralPrefsViewController" customModule="Subler" customModuleProvider="target">
<connections>
<outlet property="saveLocationPopUp" destination="saveLocationPopUp" id="saveLocationOutlet"/>
<outlet property="view" destination="76" id="u5x-9E-WAf"/>
</connections>
</customObject>
Expand All @@ -25,11 +26,11 @@
</declaredKeys>
</userDefaultsController>
<customView id="76">
<rect key="frame" x="0.0" y="0.0" width="660" height="328"/>
<rect key="frame" x="0.0" y="0.0" width="660" height="363"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="dxI-5Q-gDD">
<rect key="frame" x="133" y="0.0" width="394" height="328"/>
<rect key="frame" x="133" y="0.0" width="394" height="363"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="754" translatesAutoresizingMaskIntoConstraints="NO" id="XPF-Mh-XYC">
<rect key="frame" x="147" y="156" width="57" height="14"/>
Expand Down Expand Up @@ -220,6 +221,47 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="249" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="saveLocationLabel">
<rect key="frame" x="18" y="200" width="107" height="16"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Save As:" id="saveLocationLabelCell">
<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>
<popUpButton verticalHuggingPriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="saveLocationPopUp">
<rect key="frame" x="128" y="193" width="187" height="25"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="saveLocationLastUsed" id="saveLocationCell">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="message"/>
<menu key="menu" id="saveLocationMenu">
<items>
<menuItem title="Last Used Folder" state="on" id="saveLocationLastUsed">
<connections>
<action selector="setSaveLocation:" target="-2" id="saveLocationLastUsedAction"/>
</connections>
</menuItem>
<menuItem title="Same as File" tag="1" id="saveLocationSameAsFile">
<connections>
<action selector="setSaveLocation:" target="-2" id="saveLocationSameAsFileAction"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="saveLocationSeparator"/>
<menuItem title="Other…" tag="3" id="saveLocationOther">
<connections>
<action selector="chooseSaveLocation:" target="-2" id="saveLocationOtherAction"/>
</connections>
</menuItem>
</items>
</menu>
</popUpButtonCell>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="180" id="saveLocationWidth"/>
</constraints>
<connections>
<accessibilityConnection property="title" destination="saveLocationLabel" id="saveLocationAccessibility"/>
</connections>
</popUpButton>
</subviews>
<constraints>
<constraint firstItem="133" firstAttribute="width" secondItem="136" secondAttribute="width" id="05F-cw-fMw"/>
Expand All @@ -237,7 +279,13 @@
<constraint firstItem="XPF-Mh-XYC" firstAttribute="firstBaseline" secondItem="mNP-6I-gV4" secondAttribute="firstBaseline" id="AFc-cn-ab1"/>
<constraint firstItem="77" firstAttribute="top" secondItem="dxI-5Q-gDD" secondAttribute="top" constant="20" symbolic="YES" id="D9x-E1-HxF"/>
<constraint firstItem="82" firstAttribute="leading" secondItem="JhA-jk-CGt" secondAttribute="leading" constant="-18" id="E8S-ti-ghL"/>
<constraint firstItem="82" firstAttribute="top" secondItem="80" secondAttribute="bottom" constant="10" id="ELH-2G-UJe"/>
<constraint firstItem="82" firstAttribute="top" secondItem="saveLocationPopUp" secondAttribute="bottom" constant="10" id="ELH-2G-UJe"/>
<constraint firstItem="saveLocationPopUp" firstAttribute="top" secondItem="80" secondAttribute="bottom" constant="10" id="saveLocationTop"/>
<constraint firstItem="saveLocationLabel" firstAttribute="leading" secondItem="206" secondAttribute="leading" id="saveLocationLeading"/>
<constraint firstItem="saveLocationLabel" firstAttribute="width" secondItem="206" secondAttribute="width" id="saveLocationLabelWidth"/>
<constraint firstItem="saveLocationPopUp" firstAttribute="leading" secondItem="saveLocationLabel" secondAttribute="trailing" constant="8" symbolic="YES" id="saveLocationHorizontalSpace"/>
<constraint firstItem="saveLocationLabel" firstAttribute="firstBaseline" secondItem="saveLocationPopUp" secondAttribute="firstBaseline" id="saveLocationBaseline"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="saveLocationPopUp" secondAttribute="trailing" id="saveLocationTrailing"/>
<constraint firstItem="mNP-6I-gV4" firstAttribute="firstBaseline" secondItem="tVb-Qq-sU7" secondAttribute="firstBaseline" id="ElQ-mv-Pfd"/>
<constraint firstItem="206" firstAttribute="width" secondItem="logFormatLabel" secondAttribute="width" id="G5L-ax-8Io"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="B5v-6Q-3Qa" secondAttribute="trailing" id="Ies-Bx-zqg"/>
Expand Down
73 changes: 73 additions & 0 deletions Classes/GeneralPrefsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,79 @@ class GeneralPrefsViewController: NSViewController {
return "GeneralPrefsViewController"
}

// MARK: Save As location popUp

@IBOutlet var saveLocationPopUp: NSPopUpButton!

private let customLocationTag = 2

override func viewDidLoad() {
super.viewDidLoad()
prepareSaveLocationPopUp()
}

private func menuItem(url: URL) -> NSMenuItem {
let icon = NSWorkspace.shared.icon(forFile: url.path)
icon.size = NSSize(width: 16, height: 16)
let item = NSMenuItem(title: url.lastPathComponent, action: #selector(setSaveLocation(_:)), keyEquivalent: "")
item.image = icon
item.target = self
item.representedObject = url
item.toolTip = url.path
item.tag = customLocationTag
return item
}

/// Rebuilds the custom folder item, then selects the current location.
private func prepareSaveLocationPopUp() {
guard let menu = saveLocationPopUp.menu else { return }

if let item = menu.items.first(where: { $0.tag == customLocationTag }) {
menu.removeItem(item)
}

if let url = Prefs.saveAsCustomLocation {
menu.insertItem(menuItem(url: url), at: 2)
}

selectCurrentSaveLocation()
}

private func selectCurrentSaveLocation() {
let location = Prefs.saveAsLocation

if location == .custom && Prefs.saveAsCustomLocation == nil {
saveLocationPopUp.selectItem(withTag: SaveAsLocation.lastUsed.rawValue)
} else {
saveLocationPopUp.selectItem(withTag: location.rawValue)
}
}

@IBAction func setSaveLocation(_ sender: NSMenuItem) {
if let url = sender.representedObject as? URL {
Prefs.saveAsCustomLocation = url
Prefs.saveAsLocation = .custom
} else {
Prefs.saveAsLocation = SaveAsLocation(rawValue: sender.tag) ?? .lastUsed
}
}

@IBAction func chooseSaveLocation(_ sender: Any) {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.canCreateDirectories = true

panel.begin { response in
if response == .OK, let url = panel.url {
Prefs.saveAsCustomLocation = url
Prefs.saveAsLocation = .custom
}
self.prepareSaveLocationPopUp()
}
}

@IBAction func clearRecentSearches(_ sender: Any) {
MetadataSearchController.clearRecentSearches()
}
Expand Down
4 changes: 4 additions & 0 deletions Classes/MP42FileAdditions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ extension MP42File {
}
}

func preferredFileDirectory() -> URL? {
return tracks.compactMap { $0.url }.first?.deletingLastPathComponent()
}

func formattedFileName() -> String? {
guard let mediaKindValue = metadata.metadataItemsFiltered(byIdentifier: MP42MetadataKeyMediaKind).first?.numberValue?.intValue,
let mediaKind = MediaKind(rawValue: mediaKindValue)
Expand Down
47 changes: 46 additions & 1 deletion Classes/Prefs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import MP42Foundation

private let ud = UserDefaults.standard

Expand Down Expand Up @@ -70,6 +71,16 @@ struct StoredCodable<T: Codable> : Registable {
}
}

/// The folder the Save As… panel opens at.
enum SaveAsLocation: Int {
/// The last folder used, the standard AppKit behaviour.
case lastUsed = 0
/// The folder holding the file the tracks were imported from.
case sameAsFile = 1
/// A folder picked by the user.
case custom = 2
}

enum Prefs {

static func register() {
Expand All @@ -78,7 +89,7 @@ enum Prefs {
_audioBitrate, _audioDRC, _audioConvertAC3, _audioKeepAC3, _audioConvertDts,
_audioDtsOptions, _subtitleConvertBitmap, _ratingsCountry, _chaptersPreviewPosition,
_chaptersPreviewTrack, _mp464bitOffset, _mp464bitTimes, _mp4SaveAsOptimize, _forceHvc1,
_logFormat])
_logFormat, _saveAsLocationValue])
}

@Stored(key: "NSApplicationCrashOnException", defaultValue: true)
Expand Down Expand Up @@ -161,6 +172,40 @@ enum Prefs {

@Stored(key: "SBLogFormat", defaultValue: 0)
static var logFormat: Int // 0 = Time Only, 1 = Date and Time

@Stored(key: "SBSaveAsLocationMode", defaultValue: 0)
private static var saveAsLocationValue: Int

static var saveAsLocation: SaveAsLocation {
get { SaveAsLocation(rawValue: saveAsLocationValue) ?? .lastUsed }
set { saveAsLocationValue = newValue.rawValue }
}

private static let saveAsCustomLocationKey = "SBSaveAsCustomLocation"

/// The folder used when saveAsLocation is set to custom, stored as a
/// security scoped bookmark so it stays reachable after a relaunch.
static var saveAsCustomLocation: URL? {
get {
guard let bookmark = ud.data(forKey: saveAsCustomLocationKey) else { return nil }

var stale = ObjCBool(false)
guard let url = try? MP42SecurityAccessToken.url(fromBookmark: bookmark, bookmarkDataIsStale: &stale) else { return nil }

if stale.boolValue, let refreshed = try? MP42SecurityAccessToken.bookmark(from: url) {
ud.set(refreshed, forKey: saveAsCustomLocationKey)
}

return url
}
set {
if let url = newValue, let bookmark = try? MP42SecurityAccessToken.bookmark(from: url) {
ud.set(bookmark, forKey: saveAsCustomLocationKey)
} else {
ud.removeObject(forKey: saveAsCustomLocationKey)
}
}
}
}

enum MetadataPrefs {
Expand Down
17 changes: 17 additions & 0 deletions Classes/SaveOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ final class SaveOptions: NSViewController {
}
}

/// The folder the save panel should open at, or nil to leave it
/// at the last folder used.
private func preferredDirectory(for doc: Document) -> URL? {
switch Prefs.saveAsLocation {
case .lastUsed:
return nil
case .sameAsFile:
return doc.fileURL?.deletingLastPathComponent() ?? doc.mp4.preferredFileDirectory()
case .custom:
return Prefs.saveAsCustomLocation
}
}

override func viewDidLoad() {
super.viewDidLoad()

Expand All @@ -65,6 +78,10 @@ final class SaveOptions: NSViewController {
savePanel?.nameFieldStringValue = filename
}

if let directory = preferredDirectory(for: doc) {
savePanel?.directoryURL = directory
}

setFileType(filenameExtension: fileType)

_64bit_data.state = Prefs.mp464bitOffset ? .on : .off
Expand Down