AVRSack/AVRsack/ASSerialWin.swift

253 lines
9.0 KiB
Swift
Raw Normal View History

2014-12-29 06:35:07 +00:00
//
// ASSerialWin.swift
// AVRsack
//
// Created by Matthias Neeracher on 27/12/14.
// Copyright (c) 2014 Aere Perennius. All rights reserved.
//
import Cocoa
2014-12-31 03:16:43 +00:00
private var serialInstances = [String : ASSerialWin]()
private var keyboardHandler : ACEKeyboardHandler = .Ace
2014-12-29 06:35:07 +00:00
class ASSerialWin: NSWindowController {
@IBOutlet weak var inputLine : NSTextField!
@IBOutlet weak var logView : ACEView!
2014-12-31 07:40:08 +00:00
var baudRate : Int = 9600 {
2014-12-29 06:35:07 +00:00
didSet(oldRate) {
if portHandle != nil {
connect(self) // Disconnect existing
connect(self) // Reconnect
}
2014-12-31 07:40:08 +00:00
portDefaults["BaudRate"] = baudRate
updatePortDefaults()
2014-12-29 06:35:07 +00:00
}
}
var sendCR = false
var sendLF = true
2014-12-31 03:16:43 +00:00
var scrollToBottom : Bool = true {
didSet(oldScroll) {
if scrollToBottom {
logView.gotoLine(1000000000, column: 0, animated: true)
}
}
}
2014-12-29 06:35:07 +00:00
var port = ""
var serialData = ""
var serialObserver : AnyObject!
dynamic var portHandle : NSFileHandle?
2014-12-31 03:16:43 +00:00
var currentTheme : UInt = 0
var fontSize : UInt = 12
var portDefaults = [String: AnyObject]()
var shouldReconnect = false
2014-12-29 06:35:07 +00:00
class func showWindowWithPort(port: String) {
if let existing = serialInstances[port] {
existing.showWindow(self)
} else {
let newInstance = ASSerialWin(port:port)
serialInstances[port] = newInstance
newInstance.showWindow(self)
}
}
class func portNeededForUpload(port: String) {
if let existing = serialInstances[port] {
existing.disconnectTemporarily()
}
}
class func portAvailableAfterUpload(port: String) {
if let existing = serialInstances[port] {
existing.reconnect()
}
}
2014-12-29 06:35:07 +00:00
convenience init(port: String) {
self.init(windowNibName:"ASSerialWin")
2014-12-29 06:35:07 +00:00
self.port = port
2014-12-31 03:16:43 +00:00
let userDefaults = NSUserDefaults.standardUserDefaults()
if let portDef = (userDefaults.objectForKey("SerialDefaults") as NSDictionary).objectForKey(port) as? [String: AnyObject] {
portDefaults = portDef
} else {
portDefaults["Theme"] = userDefaults.stringForKey("SerialTheme")
portDefaults["FontSize"] = userDefaults.objectForKey("FontSize")
portDefaults["SendCR"] = sendCR
portDefaults["SendLF"] = sendLF
2014-12-31 07:40:08 +00:00
portDefaults["BaudRate"] = 19200
2014-12-31 03:16:43 +00:00
}
if let themeId = ACEView.themeIdByName(portDefaults["Theme"] as String) {
currentTheme = themeId
}
fontSize = portDefaults["FontSize"] as UInt
sendCR = portDefaults["SendCR"] as Bool
sendLF = portDefaults["SendLF"] as Bool
2014-12-31 07:40:08 +00:00
baudRate = portDefaults["BaudRate"] as Int
2014-12-31 03:16:43 +00:00
if let handlerName = userDefaults.stringForKey("Bindings") {
if let handlerId = ACEView.handlerIdByName(handlerName) {
keyboardHandler = handlerId
}
}
2014-12-29 06:35:07 +00:00
var nc = NSNotificationCenter.defaultCenter()
serialObserver = nc.addObserverForName(kASSerialPortsChanged, object: nil, queue: nil, usingBlock: { (NSNotification) in
self.willChangeValueForKey("hasValidPort")
self.didChangeValueForKey("hasValidPort")
if self.hasValidPort {
self.reconnect()
} else {
self.disconnectTemporarily()
}
2014-12-29 06:35:07 +00:00
})
}
override func finalize() {
NSNotificationCenter.defaultCenter().removeObserver(serialObserver)
serialInstances.removeValueForKey(port)
}
override func windowDidLoad() {
logView.setReadOnly(true)
logView.setShowPrintMargin(false)
2014-12-31 03:16:43 +00:00
logView.setTheme(currentTheme)
logView.setKeyboardHandler(keyboardHandler)
logView.setFontSize(fontSize)
logView.setMode(UInt(ACEModeText))
logView.alphaValue = 0.8
2014-12-29 06:35:07 +00:00
window?.title = port
connect(self)
super.windowDidLoad()
}
@IBAction func selectPort(item: AnyObject) {
port = (item as NSPopUpButton).titleOfSelectedItem!
window?.title = port
}
@IBAction func sendInput(AnyObject) {
2014-12-29 07:33:35 +00:00
let line = inputLine.stringValue + (sendCR ? "\r" : "") + (sendLF ? "\n" : "")
let data = line.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!
portHandle?.writeData(data)
2014-12-29 06:35:07 +00:00
}
@IBAction func connect(AnyObject) {
shouldReconnect = false
2014-12-29 06:35:07 +00:00
if portHandle != nil {
ASSerial.restorePort(portHandle!.fileDescriptor)
portHandle!.closeFile()
portHandle = nil
} else {
2014-12-31 07:40:08 +00:00
portHandle = ASSerial.openPort(port, withSpeed: Int32(baudRate))
2014-12-29 06:35:07 +00:00
if portHandle != nil {
serialData = ""
logView.setString(serialData)
portHandle!.readabilityHandler = {(handle) in
let newData = handle.availableData
let newString = NSString(data: newData, encoding: NSASCIIStringEncoding)!
self.serialData += newString
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.logView.setString(self.serialData)
2014-12-31 03:16:43 +00:00
if self.scrollToBottom {
self.logView.gotoLine(1000000000, column: 0, animated: true)
}
2014-12-29 06:35:07 +00:00
})
}
}
}
}
func disconnectTemporarily() {
if portHandle != nil {
connect(self) // Disconnect temporarily
shouldReconnect = true // But express interest to reconnect
}
}
func reconnect() {
if portHandle == nil && shouldReconnect {
connect(self) // Reconnect
}
}
2014-12-29 06:35:07 +00:00
var connectButtonTitle : String {
get {
return (portHandle != nil) ? "Disconnect" : "Connect"
}
}
class func keyPathsForValuesAffectingConnectButtonTitle() -> NSSet {
return NSSet(object: "portHandle")
}
var hasValidPort : Bool {
get {
return (ASSerial.ports() as NSArray).containsObject(port)
}
}
2014-12-31 03:16:43 +00:00
// MARK: Editor configuration
@IBAction func changeTheme(item: NSMenuItem) {
let userDefaults = NSUserDefaults.standardUserDefaults()
currentTheme = UInt(item.tag)
logView.setTheme(currentTheme)
let themeName = ACEThemeNames.humanNameForTheme(currentTheme)
userDefaults.setObject(themeName, forKey: "SerialTheme")
portDefaults["Theme"] = themeName
updatePortDefaults()
}
@IBAction func changeKeyboardHandler(item: NSMenuItem) {
keyboardHandler = ACEKeyboardHandler(rawValue: UInt(item.tag))!
NSUserDefaults.standardUserDefaults().setObject(
ACEKeyboardHandlerNames.humanNameForKeyboardHandler(keyboardHandler), forKey: "Bindings")
NSNotificationCenter.defaultCenter().postNotificationName("Bindings", object: item)
}
func validateUserInterfaceItem(anItem: NSValidatedUserInterfaceItem) -> Bool {
if let menuItem = anItem as? NSMenuItem {
if menuItem.action == "changeTheme:" {
menuItem.state = (menuItem.tag == Int(currentTheme) ? NSOnState : NSOffState)
return true
} else if menuItem.action == "changeKeyboardHandler:" {
menuItem.state = (menuItem.tag == Int(keyboardHandler.rawValue) ? NSOnState : NSOffState)
return true
}
}
return true
}
@IBAction func makeTextLarger(AnyObject) {
fontSize += 1
logView.setFontSize(fontSize)
portDefaults["FontSize"] = fontSize
updatePortDefaults()
}
@IBAction func makeTextSmaller(AnyObject) {
if fontSize > 6 {
fontSize -= 1
logView.setFontSize(fontSize)
portDefaults["FontSize"] = fontSize
updatePortDefaults()
}
}
func updatePortDefaults() {
let userDefaults = NSUserDefaults.standardUserDefaults()
2014-12-31 07:40:08 +00:00
let serialDefaults = NSMutableDictionary(dictionary:userDefaults.objectForKey("SerialDefaults") as NSDictionary)
2014-12-31 03:16:43 +00:00
serialDefaults.setValue(portDefaults, forKey:port)
userDefaults.setObject(serialDefaults, forKey:"SerialDefaults")
}
2014-12-31 11:56:20 +00:00
@IBAction func saveDocument(AnyObject) {
let savePanel = NSSavePanel()
savePanel.allowedFileTypes = ["log"]
savePanel.allowsOtherFileTypes = true
savePanel.extensionHidden = false
savePanel.beginSheetModalForWindow(window!, completionHandler: { (returnCode) -> Void in
if returnCode == NSFileHandlingPanelOKButton {
self.serialData.writeToURL(savePanel.URL!, atomically:false, encoding:NSUTF8StringEncoding, error:nil)
}
})
}
2014-12-29 06:35:07 +00:00
}