Handle board/programmer/port selection

This commit is contained in:
Matthias Neeracher 2014-12-07 06:20:57 +01:00 committed by Matthias Neeracher
parent 2fd2a10432
commit 8325cf1113
4 changed files with 234 additions and 63 deletions

View File

@ -26,6 +26,15 @@ class ASPropertyEntry {
typealias ASProperties = [String: ASPropertyEntry]
extension NSMenu {
func addSortedChoices(choices:[ASPropertyEntry], target: AnyObject, selector: Selector) {
for choice in choices.sorted({ $0["name"] < $1["name"] }) {
let item = self.addItemWithTitle(choice["name"], action: selector, keyEquivalent: "")
item?.target = target
}
}
}
private func subdirectories(path: NSString) -> [NSString] {
let fileManager = NSFileManager.defaultManager()
var subDirs = [NSString]()
@ -66,7 +75,8 @@ class ASHardware {
// Gather board declarations
//
for dir in directories {
let boardsPath = dir+"/boards.txt"
let boardsPath = dir+"/boards.txt"
let provenience = dir.lastPathComponent
if let boardsFile = NSString(contentsOfFile: boardsPath, usedEncoding: nil, error: nil) {
var seen = [String: Bool]()
for line in boardsFile.componentsSeparatedByString("\n") as [NSString] {
@ -75,18 +85,21 @@ class ASHardware {
let property = line.substringWithRange(match.rangeAtIndex(2))
let value = line.substringWithRange(match.rangeAtIndex(3))
if seen.updateValue(true, forKey: board) == nil {
boards[board] = ASPropertyEntry()
boards[board] = ASPropertyEntry()
boards[board]!["provenience"] = provenience
}
boards[board]![property] = value
}
}
}
}
//
// Gather programmer declarations
//
for dir in directories {
let programmersPath = dir+"/programmers.txt"
let provenience = dir.lastPathComponent
if let programmersFile = NSString(contentsOfFile: programmersPath, usedEncoding: nil, error: nil) {
var seen = [String: Bool]()
for line in programmersFile.componentsSeparatedByString("\n") as [NSString] {
@ -96,7 +109,7 @@ class ASHardware {
let value = line.substringWithRange(match.rangeAtIndex(3))
if seen.updateValue(true, forKey: programmer) == nil {
programmers[programmer] = ASPropertyEntry()
seen[programmer] = true
programmers[programmer]!["provenience"] = provenience
}
programmers[programmer]![property] = value
}
@ -104,6 +117,43 @@ class ASHardware {
}
}
}
func buildMenu(menu:NSMenu, choices:ASProperties, recentChoices:[String], target: AnyObject, selector: Selector) {
menu.removeAllItems()
if choices.count <= 10 {
menu.addSortedChoices([ASPropertyEntry](choices.values), target: target, selector: selector)
} else {
menu.addSortedChoices(recentChoices.map({ (recent: String) in choices[recent]! }), target: target, selector: selector)
menu.addItem(NSMenuItem.separatorItem())
var seen = [String: Bool]()
for prop in choices.values {
seen[prop["provenience"]] = true
}
var sortedKeys = [String](seen.keys)
sortedKeys.sort { $0 < $1 }
for provenience in sortedKeys {
var subset = [ASPropertyEntry]()
for prop in choices.values {
if prop["provenience"] == provenience {
subset.append(prop)
}
}
let item = menu.addItemWithTitle(provenience, action: nil, keyEquivalent: "")!
let submenu = NSMenu()
submenu.autoenablesItems = false
submenu.addSortedChoices(subset, target: target, selector: selector)
menu.setSubmenu(submenu, forItem: item)
}
}
}
func buildBoardsMenu(menu:NSMenu, recentBoards:[String], target: AnyObject, selector: Selector) {
buildMenu(menu, choices:boards, recentChoices:recentBoards, target: target, selector: selector)
}
func buildProgrammersMenu(menu:NSMenu, recentProgrammers:[String], target: AnyObject, selector: Selector) {
buildMenu(menu, choices:programmers, recentChoices:recentProgrammers, target: target, selector: selector)
}
}
private let librariesInstance = ASLibraries()

View File

@ -6,34 +6,58 @@
// Copyright (c) 2014 Aere Perennius. All rights reserved.
//
import Swift
import Cocoa
private var keyboardHandler : ACEKeyboardHandler = .Ace
class ASProjDoc: NSDocument, NSOutlineViewDelegate {
func pushToFront(inout list: [String], front: String) {
let kMaxRecents = 8
if let existing = find(list, front) {
if existing == 0 {
return
} else {
list.removeAtIndex(existing)
}
} else if list.count >= kMaxRecents {
list.removeLast()
}
list.insert(front, atIndex: 0)
}
class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate {
@IBOutlet weak var editor : ACEView!
@IBOutlet weak var outline : NSOutlineView!
@IBOutlet weak var boardTool: NSPopUpButton!
@IBOutlet weak var progTool : NSPopUpButton!
@IBOutlet weak var portTool : NSPopUpButton!
let files = ASFileTree()
let builder = ASBuilder()
var mainEditor : ASFileNode?
var currentTheme : UInt = 0
var fontSize : UInt = 12
var themeObserver : AnyObject?
var board : String = "uno"
var programmer : String = ""
var port : String = ""
var board = "uno"
var programmer = "arduino"
var port = ""
var recentBoards = [String]()
var recentProgrammers = [String]()
var logModified = NSDate.distantPast() as NSDate
var updateLogTimer : NSTimer?
let kVersionKey = "Version"
let kCurVersion = 1.0
let kFilesKey = "Files"
let kThemeKey = "Theme"
let kFontSizeKey = "FontSize"
let kBindingsKey = "Bindings"
let kBoardKey = "Board"
let kProgrammerKey = "Programmer"
let kPortKey = "Port"
let kVersionKey = "Version"
let kCurVersion = 1.0
let kFilesKey = "Files"
let kThemeKey = "Theme"
let kFontSizeKey = "FontSize"
let kBindingsKey = "Bindings"
let kBoardKey = "Board"
let kProgrammerKey = "Programmer"
let kPortKey = "Port"
let kRecentBoardsKey = "RecentBoards"
let kRecentProgrammersKey = "RecentProgrammers"
// MARK: Initialization / Finalization
@ -60,9 +84,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
themeObserver = NSNotificationCenter.defaultCenter().addObserverForName(kBindingsKey, object: nil, queue: nil, usingBlock: { (NSNotification) in
self.editor.setKeyboardHandler(keyboardHandler)
})
board = userDefaults.stringForKey(kBoardKey)!
programmer = userDefaults.stringForKey(kProgrammerKey)!
port = userDefaults.stringForKey(kPortKey)!
board = userDefaults.stringForKey(kBoardKey)!
programmer = userDefaults.stringForKey(kProgrammerKey)!
port = userDefaults.stringForKey(kPortKey)!
recentBoards = userDefaults.objectForKey(kRecentBoardsKey) as [String]
recentProgrammers = userDefaults.objectForKey(kRecentProgrammersKey) as [String]
updateLogTimer =
NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateLog:", userInfo: nil, repeats: true)
@ -88,6 +114,12 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
}
updateChangeCount(.ChangeCleared)
outlineViewSelectionDidChange(NSNotification(name: "", object: nil))
menuNeedsUpdate(boardTool.menu!)
menuNeedsUpdate(progTool.menu!)
portTool.removeAllItems()
portTool.addItemWithTitle("Title")
portTool.addItemsWithTitles(ASSerial.ports())
portTool.setTitle(port)
}
override class func autosavesInPlace() -> Bool {
@ -112,7 +144,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
let data = [kVersionKey: kCurVersion,
kThemeKey: ACEThemeNames.nameForTheme(currentTheme),
kFontSizeKey: fontSize,
kFilesKey: files.propertyList()]
kFilesKey: files.propertyList(),
kBoardKey: board,
kProgrammerKey: programmer,
kPortKey: port
]
return NSPropertyListSerialization.dataFromPropertyList(data, format: .XMLFormat_v1_0, errorDescription: nil)
}
@ -168,6 +204,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
fontSize = UInt(fontSz)
}
files.readPropertyList(projectData[kFilesKey] as NSDictionary)
board = (projectData[kBoardKey] as? String) ?? board
programmer = (projectData[kProgrammerKey] as? String) ?? programmer
port = (projectData[kPortKey] as? String) ?? port
recentBoards = (projectData[kRecentBoardsKey] as? [String]) ?? recentBoards
recentProgrammers = (projectData[kRecentProgrammersKey] as? [String]) ?? recentProgrammers
updateChangeCount(.ChangeCleared)
return true
@ -293,25 +334,85 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate {
selectNode(files.buildLog)
}
func serialPorts() -> [String] {
return ASSerial.ports()
}
func boards() -> [String] {
var result = [String]()
for (ident, prop) in ASHardware.instance().boards {
result.append(prop["name"])
func menuNeedsUpdate(menu: NSMenu) {
switch menu.title {
case "Boards":
ASHardware.instance().buildBoardsMenu(menu, recentBoards: recentBoards,
target: self, selector: "selectBoard:")
boardTool.setTitle(selectedBoard)
case "Programmers":
ASHardware.instance().buildProgrammersMenu(menu, recentProgrammers: recentProgrammers,
target: self, selector: "selectProgrammer:")
progTool.setTitle(selectedProgrammer)
default:
break
}
return result
}
func programmers() -> [String] {
var result = [String]()
for (ident, prop) in ASHardware.instance().programmers {
result.append(prop["name"])
var selectedBoard : String {
get {
let boardProps = ASHardware.instance().boards[board]
return boardProps?["name"] ?? ""
}
set (newBoard) {
for (ident, prop) in ASHardware.instance().boards {
if prop["name"] == newBoard {
board = ident
pushToFront(&recentBoards, board)
let userDefaults = NSUserDefaults.standardUserDefaults()
var globalBoards = userDefaults.objectForKey(kRecentBoardsKey) as [String]
pushToFront(&globalBoards, board)
userDefaults.setObject(globalBoards, forKey: kRecentBoardsKey)
updateChangeCount(.ChangeDone)
menuNeedsUpdate(boardTool.menu!)
break
}
}
}
return result
}
@IBAction func selectBoard(item: AnyObject) {
selectedBoard = (item as NSMenuItem).title
}
var selectedProgrammer : String {
get {
let progProps = ASHardware.instance().programmers[programmer]
return progProps?["name"] ?? ""
}
set (newProg) {
for (ident, prop) in ASHardware.instance().programmers {
if prop["name"] == newProg {
programmer = ident
pushToFront(&recentProgrammers, programmer)
let userDefaults = NSUserDefaults.standardUserDefaults()
var globalProgs = userDefaults.objectForKey(kRecentProgrammersKey) as [String]
pushToFront(&globalProgs, programmer)
userDefaults.setObject(globalProgs, forKey: kRecentProgrammersKey)
updateChangeCount(.ChangeDone)
progTool.setTitle(newProg)
menuNeedsUpdate(progTool.menu!)
break
}
}
}
}
@IBAction func selectProgrammer(item: AnyObject) {
selectedProgrammer = (item as NSMenuItem).title
}
@IBAction func selectPort(item: AnyObject) {
port = (item as NSPopUpButton).titleOfSelectedItem!
portTool.setTitle(port)
}
}

View File

@ -6,8 +6,11 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ASProjDoc" customModule="AVRsack">
<connections>
<outlet property="boardTool" destination="xO5-xB-HHj" id="xzX-eY-Ofe"/>
<outlet property="editor" destination="Nhg-qn-6A8" id="eUR-Gk-IBw"/>
<outlet property="outline" destination="nij-C2-Fna" id="sVi-eL-hqZ"/>
<outlet property="portTool" destination="4rZ-U5-AH6" id="8Nb-iI-pJH"/>
<outlet property="progTool" destination="08n-xg-fNl" id="959-as-OtP"/>
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
</connections>
</customObject>
@ -112,70 +115,70 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="A3F4B25E-669B-4AEB-877C-CA0AF13A9E49" label="Board" paletteLabel="Board" id="vAE-60-8kd">
<nil key="toolTip"/>
<size key="minSize" width="100" height="28"/>
<size key="maxSize" width="100" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" id="xO5-xB-HHj">
<rect key="frame" x="0.0" y="0.0" width="100" height="28"/>
<size key="minSize" width="75" height="28"/>
<size key="maxSize" width="400" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" allowsExpansionToolTips="YES" id="xO5-xB-HHj">
<rect key="frame" x="0.0" y="14" width="75" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="EIo-LP-BL2" id="70v-IV-ury">
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" pullsDown="YES" selectedItem="BI3-Mj-Hjl" id="70v-IV-ury">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="0W3-Jd-NvF">
<menu key="menu" title="Boards" id="0W3-Jd-NvF">
<items>
<menuItem title="Item 1" state="on" id="EIo-LP-BL2"/>
<menuItem title="Item 2" id="BI3-Mj-Hjl"/>
<menuItem title="Item 1" hidden="YES" id="EIo-LP-BL2"/>
<menuItem title="Item 2" state="on" id="BI3-Mj-Hjl"/>
<menuItem title="Item 3" id="JVV-uK-XFS"/>
</items>
<connections>
<outlet property="delegate" destination="-2" id="9Wl-yy-I09"/>
</connections>
</menu>
</popUpButtonCell>
<connections>
<binding destination="-2" name="contentValues" keyPath="boards" id="I0F-IB-VDu"/>
</connections>
</popUpButton>
</toolbarItem>
<toolbarItem implicitItemIdentifier="BD8B4827-8CD2-4E50-B435-165CD744B39D" label="Programmer" paletteLabel="Programmer" id="Lt2-Z7-7Pg">
<nil key="toolTip"/>
<size key="minSize" width="100" height="28"/>
<size key="maxSize" width="100" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" id="08n-xg-fNl">
<rect key="frame" x="0.0" y="0.0" width="100" height="28"/>
<size key="minSize" width="75" height="28"/>
<size key="maxSize" width="200" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" allowsExpansionToolTips="YES" id="08n-xg-fNl">
<rect key="frame" x="0.0" y="14" width="75" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="DL9-Dk-EGd" id="drB-K8-prb">
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" pullsDown="YES" id="drB-K8-prb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="I2g-9Y-t88">
<menu key="menu" title="Programmers" id="I2g-9Y-t88">
<items>
<menuItem title="Item 1" state="on" id="DL9-Dk-EGd"/>
<menuItem title="Item 1" hidden="YES" id="DL9-Dk-EGd"/>
<menuItem title="Item 2" id="cbL-7g-Dbs"/>
<menuItem title="Item 3" id="VZO-b8-lTE"/>
</items>
<connections>
<outlet property="delegate" destination="-2" id="C5g-FS-Zb7"/>
</connections>
</menu>
</popUpButtonCell>
<connections>
<binding destination="-2" name="contentValues" keyPath="programmers" id="6XS-xq-UIC"/>
</connections>
</popUpButton>
</toolbarItem>
<toolbarItem implicitItemIdentifier="1E69CE88-D7A1-4231-B56C-89C813E75589" label="Serial Port" paletteLabel="Serial Port" id="r8E-ar-tLo">
<nil key="toolTip"/>
<size key="minSize" width="100" height="28"/>
<size key="maxSize" width="100" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" id="4rZ-U5-AH6">
<rect key="frame" x="0.0" y="0.0" width="100" height="28"/>
<size key="minSize" width="75" height="28"/>
<size key="maxSize" width="400" height="28"/>
<popUpButton key="view" verticalHuggingPriority="750" allowsExpansionToolTips="YES" id="4rZ-U5-AH6">
<rect key="frame" x="0.0" y="14" width="75" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="7U0-rg-90a" id="XSm-NS-ZtW">
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" pullsDown="YES" selectedItem="7U0-rg-90a" id="XSm-NS-ZtW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="VO0-bS-zYz">
<items>
<menuItem title="Item 1" state="on" id="7U0-rg-90a"/>
<menuItem title="Item 1" state="on" hidden="YES" id="7U0-rg-90a"/>
<menuItem title="Item 2" id="rck-gJ-Wwo"/>
<menuItem title="Item 3" id="PoP-ir-bnB"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="-2" name="contentValues" keyPath="serialPorts" id="d9a-Yy-DeA"/>
<action selector="selectPort:" target="-2" id="tRd-x3-Oig"/>
</connections>
</popUpButton>
</toolbarItem>
@ -188,11 +191,11 @@
<toolbarItem reference="69r-aN-6vG"/>
<toolbarItem reference="FYt-ZW-Epr"/>
<toolbarItem reference="lGv-lT-Eh9"/>
<toolbarItem reference="2D6-o4-n09"/>
<toolbarItem reference="FYt-ZW-Epr"/>
<toolbarItem reference="vAE-60-8kd"/>
<toolbarItem reference="Lt2-Z7-7Pg"/>
<toolbarItem reference="r8E-ar-tLo"/>
<toolbarItem reference="2D6-o4-n09"/>
<toolbarItem reference="FYt-ZW-Epr"/>
<toolbarItem reference="ln2-Ky-MBg"/>
</defaultToolbarItems>
</toolbar>

View File

@ -2,6 +2,23 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RecentProgrammers</key>
<array>
<string>usbasp</string>
<string>usbtinyisp</string>
<string>avrispmkii</string>
<string>avrisp</string>
<string>scratchmonkey</string>
<string>arduinoisp</string>
<string>arduino</string>
</array>
<key>RecentBoards</key>
<array>
<string>micro</string>
<string>leonardo</string>
<string>mega2560</string>
<string>uno</string>
</array>
<key>Port</key>
<string></string>
<key>Programmer</key>