diff --git a/AVRsack.xcodeproj/project.pbxproj b/AVRsack.xcodeproj/project.pbxproj index c35248f..9752e26 100644 --- a/AVRsack.xcodeproj/project.pbxproj +++ b/AVRsack.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 9501D8091A17025C0034C530 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9501D8081A17025C0034C530 /* Images.xcassets */; }; 9501D80C1A17025C0034C530 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9501D80A1A17025C0034C530 /* MainMenu.xib */; }; 9501D8181A17025C0034C530 /* AVRsackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9501D8171A17025C0034C530 /* AVRsackTests.swift */; }; + 95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BF80EA1A185C9E0004A693 /* ASFileTree.swift */; }; 95EA32621A17B90600F66EB0 /* ACEView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95EA325B1A17B8DA00F66EB0 /* ACEView.framework */; }; 95EA32651A17BA8C00F66EB0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95EA32631A17BA8C00F66EB0 /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -67,6 +68,7 @@ 9501D8161A17025C0034C530 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9501D8171A17025C0034C530 /* AVRsackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVRsackTests.swift; sourceTree = ""; }; 958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AVRsack-Bridging-Header.h"; sourceTree = ""; }; + 95BF80EA1A185C9E0004A693 /* ASFileTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASFileTree.swift; sourceTree = ""; }; 95EA32541A17B8DA00F66EB0 /* ACEView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ACEView.xcodeproj; path = ../ACEView/ACEView.xcodeproj; sourceTree = ""; }; 95EA32631A17BA8C00F66EB0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 95EA32641A17BA8C00F66EB0 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; @@ -117,9 +119,8 @@ 958D76811A17DA1C00917D96 /* AVRsack-Bridging-Header.h */, 9501D8011A17025C0034C530 /* ASApplication.swift */, 9501D8031A17025C0034C530 /* ASProjDoc.swift */, - 9501D8051A17025C0034C530 /* ASProjDoc.xib */, - 9501D8081A17025C0034C530 /* Images.xcassets */, - 9501D80A1A17025C0034C530 /* MainMenu.xib */, + 95BF80EA1A185C9E0004A693 /* ASFileTree.swift */, + 95BF80EC1A185CD90004A693 /* Resources */, 9501D7FF1A17025C0034C530 /* Supporting Files */, 95EA32671A17BAA600F66EB0 /* Frameworks */, ); @@ -151,6 +152,16 @@ name = "Supporting Files"; sourceTree = ""; }; + 95BF80EC1A185CD90004A693 /* Resources */ = { + isa = PBXGroup; + children = ( + 9501D8081A17025C0034C530 /* Images.xcassets */, + 9501D80A1A17025C0034C530 /* MainMenu.xib */, + 9501D8051A17025C0034C530 /* ASProjDoc.xib */, + ); + name = Resources; + sourceTree = ""; + }; 95EA32551A17B8DA00F66EB0 /* Products */ = { isa = PBXGroup; children = ( @@ -303,6 +314,7 @@ files = ( 9501D8041A17025C0034C530 /* ASProjDoc.swift in Sources */, 9501D8021A17025C0034C530 /* ASApplication.swift in Sources */, + 95BF80EB1A185C9E0004A693 /* ASFileTree.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AVRsack/ASApplication.swift b/AVRsack/ASApplication.swift index 536da2a..3a6cb13 100644 --- a/AVRsack/ASApplication.swift +++ b/AVRsack/ASApplication.swift @@ -10,9 +10,6 @@ import Cocoa @NSApplicationMain class ASApplication: NSObject, NSApplicationDelegate { - - - func applicationDidFinishLaunching(aNotification: NSNotification) { // Insert code here to initialize your application } @@ -20,7 +17,5 @@ class ASApplication: NSObject, NSApplicationDelegate { func applicationWillTerminate(aNotification: NSNotification) { // Insert code here to tear down your application } - - } diff --git a/AVRsack/ASFileTree.swift b/AVRsack/ASFileTree.swift new file mode 100644 index 0000000..59aad8b --- /dev/null +++ b/AVRsack/ASFileTree.swift @@ -0,0 +1,115 @@ +// +// ASFileTree.swift +// AVRsack +// +// Created by Matthias Neeracher on 11/16/14. +// Copyright (c) 2014 Aere Perennius. All rights reserved. +// + +import Foundation + +enum ASFileType : String { + case Unknown = "" + case Header = "source.h" + case CFile = "source.c" + case Arduino = "source.ino" + case CppFile = "source.c++" + case AsmFile = "source.asm" + case Markdown = "doc.md" + + static func guessForURL(url: NSURL) -> ASFileType { + switch url.pathExtension.lowercaseString { + case "hpp", "hh", "h": + return .Header + case "c": + return .CFile + case "ino": + return .Arduino + case "cpp", "c++", "cxx", "cc": + return .CppFile + case "s": + return .AsmFile + case "md": + return .Markdown + default: + return .Unknown + } + } + + var aceMode : ACEMode { + switch self { + case .Header,.CFile,.CppFile,.Arduino: + return ACEModeCPP + case .Markdown: + return ACEModeMarkdown + default: + return ACEModeASCIIDoc + } + } +} + +class ASFileNode { + func nodeName() -> String { + return "" + } +} + +class ASFileGroup : ASFileNode { + var name : String + var children : [ASFileNode] + var expanded : Bool + + init(name: String) { + self.name = name + self.children = [] + self.expanded = true + } + convenience override init() { + self.init(name: "") + } + override func nodeName() -> String { + return (expanded ? "📂" : "📁")+" "+name + } +} + +class ASFileItem : ASFileNode { + var url : NSURL + var type : ASFileType + + init(url: NSURL, type: ASFileType) { + self.url = url + self.type = type + } + override func nodeName() -> String { + return "📄 "+url.lastPathComponent + } +} + +class ASFileTree : NSObject, NSOutlineViewDataSource { + let root = ASFileGroup() + + func addFileURL(url: NSURL, omitUnknown: Bool = true) { + let type = ASFileType.guessForURL(url) + if !omitUnknown || type != .Unknown { + root.children.append(ASFileItem(url: url, type: type)) + } + } + + func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { + if item == nil { + return root.children.count + } else { + return (item as ASFileGroup).children.count + } + } + func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject { + let group = (item == nil) ? root : (item as ASFileGroup) + return group.children[index] + } + func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool { + return item is ASFileGroup + } + func outlineView(outlineView: NSOutlineView, objectValueForTableColumn tableColumn: NSTableColumn?, byItem item: AnyObject?) -> AnyObject? { + return (item as ASFileItem).nodeName() + } +} \ No newline at end of file diff --git a/AVRsack/ASProjDoc.swift b/AVRsack/ASProjDoc.swift index 71806d1..afa3b70 100644 --- a/AVRsack/ASProjDoc.swift +++ b/AVRsack/ASProjDoc.swift @@ -8,9 +8,11 @@ import Cocoa -class ASProjDoc: NSDocument { - @IBOutlet weak var editor : ACEView! - @IBOutlet weak var outline: NSOutlineView! +class ASProjDoc: NSDocument, NSOutlineViewDelegate { + @IBOutlet weak var editor : ACEView! + @IBOutlet weak var outline : NSOutlineView! + let files : ASFileTree = ASFileTree() + var mainEditor : ASFileNode? override init() { super.init() @@ -19,9 +21,7 @@ class ASProjDoc: NSDocument { override func windowControllerDidLoadNib(aController: NSWindowController) { super.windowControllerDidLoadNib(aController) - editor.setString("Here we go!") - editor.setMode(UInt(ACEModeASCIIDoc)) - editor.setTheme(UInt(ACEThemeXcode)) + outline.setDataSource(files) } override class func autosavesInPlace() -> Bool { @@ -35,20 +35,53 @@ class ASProjDoc: NSDocument { } override func dataOfType(typeName: String, error outError: NSErrorPointer) -> NSData? { - // Insert code here to write your document to data of the specified type. If outError != nil, ensure that you create and set an appropriate error when returning nil. - // You can also choose to override fileWrapperOfType:error:, writeToURL:ofType:error:, or writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead. outError.memory = NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil) return nil } - override func readFromData(data: NSData, ofType typeName: String, error outError: NSErrorPointer) -> Bool { - // Insert code here to read your document from the given data of the specified type. If outError != nil, ensure that you create and set an appropriate error when returning false. - // You can also choose to override readFromFileWrapper:ofType:error: or readFromURL:ofType:error: instead. - // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded. - outError.memory = NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil) - return false + func importProject(url: NSURL, error outError: NSErrorPointer) -> Bool { + let existingProject = url.URLByAppendingPathComponent(url.lastPathComponent+".avrsackproj") + if existingProject.checkResourceIsReachableAndReturnError(nil) { + fileURL = existingProject + return readFromURL(existingProject, ofType:"Project", error:outError) + } + let filesInProject = + NSFileManager.defaultManager().contentsOfDirectoryAtURL(url, includingPropertiesForKeys: nil, + options: .SkipsHiddenFiles, error: nil)! + for file in filesInProject { + files.addFileURL(file as NSURL) + } + return true + } + + override func readFromURL(url: NSURL, ofType typeName: String, error outError: NSErrorPointer) -> Bool { + var success : Bool = false + if typeName == "Arduino Source File" { + let projectURL = url.URLByDeletingPathExtension!.URLByAppendingPathExtension("avrsackproj") + fileURL = projectURL + success = importProject(url.URLByDeletingLastPathComponent!, error: outError) + } else { + success = true + } + return success + } + + func outlineViewSelectionDidChange(notification: NSNotification) { + let selection = outline.itemAtRow(outline.selectedRow) as ASFileNode? + if selection !== mainEditor { + saveCurEditor() + } + if let file = (selection as? ASFileItem) { + var enc : UInt = 0 + editor.setString(NSString(contentsOfURL:file.url, usedEncoding:&enc, error:nil)) + editor.setMode(UInt(file.type.aceMode)) + } + } + + func saveCurEditor() { + if let file = (mainEditor as? ASFileItem) { + editor.string().writeToURL(file.url, atomically: true, encoding: NSUTF8StringEncoding, error: nil) + } } - - } diff --git a/AVRsack/Base.lproj/ASProjDoc.xib b/AVRsack/Base.lproj/ASProjDoc.xib index 70af9bd..c051384 100644 --- a/AVRsack/Base.lproj/ASProjDoc.xib +++ b/AVRsack/Base.lproj/ASProjDoc.xib @@ -34,7 +34,6 @@ - @@ -52,27 +51,11 @@ - - - - - - - - - - - - - - - - - - - + + + diff --git a/AVRsack/Info.plist b/AVRsack/Info.plist index f31c317..e4c9f57 100644 --- a/AVRsack/Info.plist +++ b/AVRsack/Info.plist @@ -24,6 +24,22 @@ NSDocumentClass AVRsack.ASProjDoc + + CFBundleTypeExtensions + + ino + + CFBundleTypeMIMETypes + + text/plain + + CFBundleTypeName + Arduino Source File + CFBundleTypeRole + Viewer + NSDocumentClass + AVRsack.ASProjDoc + CFBundleExecutable $(EXECUTABLE_NAME)