         }
     }
 
-    private var badFileTypeError: Error {
-        let reason = NSLocalizedString("Unknown file type.", tableName: "MIDIMonitor", bundle: SMBundleForObject(self), comment: "error reason for unknown file type read or write")
-        return NSError(domain: midiMonitorErrorDomain, code: 1, userInfo: [NSLocalizedFailureReasonErrorKey: reason])
-    }
-
-    private var badFileContentsError: Error {
-        let reason = NSLocalizedString("Can't read the contents of the file.", tableName: "MIDIMonitor", bundle: SMBundleForObject(self), comment: "error reason for unknown file contents")
-        return NSError(domain: midiMonitorErrorDomain, code: 2, userInfo: [NSLocalizedFailureReasonErrorKey: reason])
-    }
-
-    @objc private func sourceListDidChange(_ notification: Notification?) {
-        monitorWindowController?.updateSources()
-
-        // Also, it's possible that the endpoint names went from being unique to non-unique, so we need
-        // to refresh the messages displayed.
-        updateMessages(scrollingToBottom: false)
+}
+
+extension Document {
+
+    // MARK: Input sources
+
+    var inputSourceGroups: [CombinationInputStreamSourceGroup] {
+        return stream.sourceGroups
+    }
+
+    private func autoselectSources() {
+        let groups = inputSourceGroups
+        var sourcesSet: Set<AnyHashable> = []  // TODO Should become Set<SMInputStreamSource>
+
+        let defaults = UserDefaults.standard
+
+        if defaults.bool(forKey: PreferenceKeys.selectOrdinarySourcesInNewDocument) {
+            if groups.count > 0,
+               let sources = groups[0].sources as? [AnyHashable] {
+                sourcesSet.formUnion(sources)
+            }
+        }
+
+        if defaults.bool(forKey: PreferenceKeys.selectVirtualDestinationInNewDocument) {
+            if groups.count > 1,
+               let sources = groups[1].sources as? [AnyHashable] {
+                sourcesSet.formUnion(sources)
+            }
+        }
+
+        if defaults.bool(forKey: PreferenceKeys.selectSpyingDestinationsInNewDocument) {
+            if groups.count > 2,
+               let sources = groups[2].sources as? [AnyHashable] {
+                sourcesSet.formUnion(sources)
+            }
+        }
+
+        selectedInputSources = sourcesSet
     }
 
     private func updateVirtualEndpointName() {

        NotificationCenter.default.removeObserver(self)
    }

    override func makeWindowControllers() {
        addWindowController(MonitorWindowController())
    }

    override func data(ofType typeName: String) throws -> Data {

        monitorWindowController?.updateFilterControls()
    }

    var channelMask: SMChannelMask {
        get {
            return messageFilter.channelMask
        }
        set {
            guard newValue != channelMask else { return }
            undoableSetChannelMaskNumber(NSNumber(value: newValue.rawValue))
        }
    }

    @objc private func undoableSetChannelMaskNumber(_ number: NSNumber) {
        if let undoManager = undoManager {
            undoManager.registerUndo(withTarget: self, selector: #selector(self.undoableSetChannelMaskNumber(_:)), object: NSNumber(value: channelMask.rawValue))
            undoManager.setActionName(NSLocalizedString("Change Channel", tableName: "MIDIMonitor", bundle: SMBundleForObject(self), comment: "change channel undo action"))
        }

        messageFilter.channelMask = SMChannelMask(rawValue: number.uint32Value)
        monitorWindowController?.updateFilterControls()
    }

    var savedMessages: [SMMessage] {
        // Note: The remembered messages are saved with the document, and changes to the messages
        // do dirty the document, but changes are not undoable -- it wouldn't make much sense.
        return history.savedMessages
    }

}

extension Document {

    // MARK: Derived / convenience document functions

    func changeFilterMask(_ maskToChange: SMMessageType, turnBitsOn: Bool) {
        var newMask = messageFilter.filterMask.rawValue
        if turnBitsOn {
