Skip to content
Closed
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
33 changes: 33 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build

# Compile-only verification, no signing/notarization/tests. Runs on every
# push and PR so a branch (main, a PR branch, a personal-build branch,
# whatever) shows red/green on GitHub before anyone has to open Xcode.
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
workflow_dispatch:

jobs:
build:
runs-on: macos-14

steps:
- name: Check out repo (with submodules)
uses: actions/checkout@v4
with:
submodules: recursive

- name: Show Xcode version
run: xcodebuild -version

- name: Build Subler
run: |
xcodebuild \
-project Subler.xcodeproj \
-target Subler \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
build
26 changes: 26 additions & 0 deletions Classes/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,26 @@ final class Document: NSDocument {
save(self)
}

/// Whether the next completed save should prompt to open another file
/// once it finishes (see saveAndOpen(_:) below). Consumed and reset to
/// false as soon as that save finishes, so a plain save triggered
/// afterwards (Cmd-S, etc.) doesn't also bring up the Open panel.
private var openAfterSave: Bool = false

/// Saves the current document, then -- once that finishes -- brings up
/// the same Open panel Subler shows an untitled launch with
/// (AppDelegate.applicationShouldOpenUntitledFile(_:) /
/// NSDocumentController.openDocument(_:)), so picking up the next file
/// in a batch never needs a trip to File > Open. This previously
/// opened the just-saved file in its default external application
/// instead (e.g. QuickTime) -- a different, unrelated feature that
/// didn't serve the actual goal here: an easy way to move on to
/// another file once the current one is done.
@IBAction func saveAndOpen(_ sender: Any) {
openAfterSave = true
save(self)
}

override func canAsynchronouslyWrite(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType) -> Bool { return true }

override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, completionHandler: @escaping (Error?) -> Void) {
Expand All @@ -125,6 +145,10 @@ final class Document: NSDocument {
let reloadedFile = try MP42File(url: url)
self.mp4 = reloadedFile
docController?.reloadData()
if self.openAfterSave {
self.openAfterSave = false
NSDocumentController.shared.openDocument(self)
}
completionHandler(error)
} catch {
completionHandler(error)
Expand Down Expand Up @@ -260,6 +284,8 @@ final class Document: NSDocument {
return true
case #selector(save(_:)) where isDocumentEdited == true:
return true
case #selector(saveAndOpen(_:)) where isDocumentEdited == true:
return true
case #selector(saveAs(_:)),
#selector(sendToQueue(_:)):
return true
Expand Down
13 changes: 11 additions & 2 deletions Classes/DocumentToolbarDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private extension NSToolbarItem.Identifier {
static let searchMetadata: NSToolbarItem.Identifier = NSToolbarItem.Identifier(rawValue: "SearchMetadata")
static let sendToQueue: NSToolbarItem.Identifier = NSToolbarItem.Identifier(rawValue: "SendToQueue")
static let showQueue: NSToolbarItem.Identifier = NSToolbarItem.Identifier(rawValue: "ShowQueue")
static let saveAndOpen: NSToolbarItem.Identifier = NSToolbarItem.Identifier(rawValue: "SaveAndOpen")
}

class DocumentToolbarDelegate: NSObject, NSToolbarDelegate {
Expand Down Expand Up @@ -143,17 +144,25 @@ class DocumentToolbarDelegate: NSObject, NSToolbarDelegate {
symbolName: "photo.stack",
target: nil,
action: #selector(AppDelegate.showQueueWindow(_:)))
} else if itemIdentifier == .saveAndOpen {
return ButtonToolbarItem(itemIdentifier: itemIdentifier,
label: NSLocalizedString("Open and Save", comment: "Toolbar"),
toolTip: NSLocalizedString("Save the document and open the file", comment: "Toolbar"),
image: "NSShareTemplate",
symbolName: "arrow.up.forward.app",
target: target,
action: #selector(Document.saveAndOpen(_:)))
}

return nil
}

@MainActor func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.importTracks, .searchMetadata, .space, .actions, .space, .deleteTracks, .flexibleSpace, .sendToQueue, .showQueue,]
return [.importTracks, .searchMetadata, .space, .actions, .space, .deleteTracks, .flexibleSpace, .sendToQueue, .saveAndOpen, .showQueue,]
}

@MainActor func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.importTracks, .deleteTracks, .actions, .searchMetadata, .sendToQueue, .showQueue, .flexibleSpace, .space]
return [.importTracks, .deleteTracks, .actions, .searchMetadata, .sendToQueue, .saveAndOpen, .showQueue, .flexibleSpace, .space]
}

}
22 changes: 21 additions & 1 deletion Classes/DocumentWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ final class DocumentWindowController: NSWindowController, TracksViewControllerDe

toolbarDelegate.target = self

let toolbar = NSToolbar(identifier: "SublerDocumentToolbar")
// Identifier bumped (was "SublerDocumentToolbar") so that anyone
// with a toolbar layout already saved from before "Open and
// Save" existed gets the current toolbarDefaultItemIdentifiers()
// set again -- with autosavesConfiguration on, NSToolbar only
// ever falls back to the delegate's defaults for a layout it
// hasn't seen before, otherwise it keeps replaying whatever was
// saved under this exact identifier indefinitely, new default
// items and all.
let toolbar = NSToolbar(identifier: "SublerDocumentToolbar2")
toolbar.delegate = toolbarDelegate
toolbar.allowsUserCustomization = true
toolbar.autosavesConfiguration = true
Expand Down Expand Up @@ -299,6 +307,18 @@ final class DocumentWindowController: NSWindowController, TracksViewControllerDe
doc.sendToQueue(self)
}

/// The "Open and Save" toolbar item's target is this window controller
/// (see DocumentToolbarDelegate), not the document, following the same
/// pattern as sendToQueue(_:) above -- so this forwarding method has to
/// exist here under the same selector name for the click to actually
/// reach Document.saveAndOpen(_:). Without it, NSApplication.sendAction
/// silently does nothing when the toolbar item's explicit target
/// doesn't respond to the action, even though menu/toolbar validation
/// (which walks the responder chain) still reports the item as enabled.
@IBAction func saveAndOpen(_ sender: Any) {
doc.saveAndOpen(self)
}

@IBAction func sendToExternalApp(_ sender: Any) {
if let fileURL = doc.fileURL {
_ = sendToFileExternalApp(fileURL: fileURL)
Expand Down