From 30652c56270664b21115fe037b3c6abb5ddd6d14 Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Mon, 16 Mar 2015 05:39:09 +0100 Subject: [PATCH] Implement printing --- AVRsack.xcodeproj/project.pbxproj | 5 + .../xcshareddata/xcschemes/AVRsack.xcscheme | 12 +- AVRsack/ASFileTree.swift | 49 +++++- AVRsack/ASProjDoc.swift | 152 +++++++++++++++++- AVRsack/Base.lproj/ASProjDoc.xib | 9 +- AVRsack/FileRevision | 33 ++++ 6 files changed, 243 insertions(+), 17 deletions(-) create mode 100755 AVRsack/FileRevision diff --git a/AVRsack.xcodeproj/project.pbxproj b/AVRsack.xcodeproj/project.pbxproj index f8d9009..9b78424 100644 --- a/AVRsack.xcodeproj/project.pbxproj +++ b/AVRsack.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 950AB9271A296A160033A9DA /* ProjIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 950AB9261A296A160033A9DA /* ProjIcon.icns */; }; 951CD1741A23C9FC0066C1A1 /* ASBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */; }; 951CD1771A2615000066C1A1 /* BuildProject in Resources */ = {isa = PBXBuildFile; fileRef = 951CD1761A2615000066C1A1 /* BuildProject */; }; + 95388E5D1AB6882600061435 /* FileRevision in Resources */ = {isa = PBXBuildFile; fileRef = 95388E5C1AB6882600061435 /* FileRevision */; }; 9538E0EC1A8FB215001E02CC /* ACEView.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 95EA325B1A17B8DA00F66EB0 /* ACEView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 95468DDF1A228BE600668EE2 /* ASHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95468DDE1A228BE600668EE2 /* ASHardware.swift */; }; 95468DE31A228E1300668EE2 /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 95468DE21A228E1300668EE2 /* Defaults.plist */; }; @@ -102,6 +103,7 @@ 950AB9261A296A160033A9DA /* ProjIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ProjIcon.icns; path = IconResources/ProjIcon.icns; sourceTree = ""; }; 951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASBuilder.swift; sourceTree = ""; }; 951CD1761A2615000066C1A1 /* BuildProject */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = BuildProject; sourceTree = ""; }; + 95388E5C1AB6882600061435 /* FileRevision */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = FileRevision; sourceTree = ""; }; 95468DDE1A228BE600668EE2 /* ASHardware.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASHardware.swift; sourceTree = ""; }; 95468DE21A228E1300668EE2 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = ""; }; 95539B661A3E7EAF00D8595C /* ASSerial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSerial.h; sourceTree = ""; }; @@ -211,6 +213,7 @@ 951CD1731A23C9FC0066C1A1 /* ASBuilder.swift */, 95A7C6451A3894C900EF1963 /* ASPreferences.swift */, 951CD1761A2615000066C1A1 /* BuildProject */, + 95388E5C1AB6882600061435 /* FileRevision */, 95539B661A3E7EAF00D8595C /* ASSerial.h */, 95539B671A3E7EAF00D8595C /* ASSerial.mm */, 95DF20661A45A6090013D1B5 /* ASSketchBook.swift */, @@ -305,6 +308,7 @@ 9501D7F41A17025C0034C530 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0630; ORGANIZATIONNAME = "Aere Perennius"; TargetAttributes = { @@ -383,6 +387,7 @@ 95CC3F071A6385B8003507AD /* CSrcIcon.icns in Resources */, 951CD1771A2615000066C1A1 /* BuildProject in Resources */, 95CC3F081A6385B8003507AD /* HSrcIcon.icns in Resources */, + 95388E5D1AB6882600061435 /* FileRevision in Resources */, 95CC3F061A6385B8003507AD /* CppSrcIcon.icns in Resources */, 95CC3F051A6385B8003507AD /* AsmSrcIcon.icns in Resources */, ); diff --git a/AVRsack.xcodeproj/xcshareddata/xcschemes/AVRsack.xcscheme b/AVRsack.xcodeproj/xcshareddata/xcschemes/AVRsack.xcscheme index 7309108..bf3cb0a 100644 --- a/AVRsack.xcodeproj/xcshareddata/xcschemes/AVRsack.xcscheme +++ b/AVRsack.xcodeproj/xcshareddata/xcschemes/AVRsack.xcscheme @@ -1,11 +1,10 @@ + LastUpgradeVersion = "0700" + version = "1.3"> + buildImplicitDependencies = "YES"> + + String { return "" } @@ -87,23 +93,33 @@ private let kNameKey = "Name" func exists() -> Bool { return true } + func modDate() -> NSDate? { + return nil; + } + func revision() -> String? { + return nil; + } } class ASLogNode : ASFileNode { - var name : String var path : String init(name: String, path: String) { - self.name = name self.path = path + super.init(name: name) } override func nodeName() -> String { return "📜 "+name } + override func modDate() -> NSDate? { + let url = NSURL(fileURLWithPath: path) + var date: AnyObject? + url?.getResourceValue(&date, forKey: NSURLContentModificationDateKey, error: nil) + return date as? NSDate + } } class ASFileGroup : ASFileNode { - var name : String var children : [ASFileNode] var expanded : Bool @@ -111,18 +127,18 @@ class ASFileGroup : ASFileNode { private let kExpandedKey = "Expanded" private var kNodeType : String { return kNodeTypeGroup } - init(name: String = "") { - self.name = name + override init(name: String = "") { self.children = [] self.expanded = true + super.init(name: name) } init(_ prop: NSDictionary, withRootURL rootURL: NSURL) { - name = prop[kNameKey] as! String expanded = prop[kExpandedKey] as! Bool children = [] for child in (prop[kChildrenKey] as! [NSDictionary]) { children.append(ASFileNode.readPropertyList(child, rootURL: rootURL)) } + super.init(name: prop[kNameKey] as! String) } override func nodeName() -> String { return (expanded ? "📂" : "📁")+" "+name @@ -167,6 +183,7 @@ class ASFileItem : ASFileNode { init(url: NSURL, type: ASFileType) { self.url = url self.type = type + super.init(name:url.lastPathComponent!) } init(_ prop: NSDictionary, withRootURL rootURL: NSURL) { type = ASFileType(rawValue: prop[kKindKey] as! String)! @@ -175,9 +192,10 @@ class ASFileItem : ASFileNode { } else { url = NSURL(fileURLWithPath:(prop[kPathKey] as! String))!.URLByStandardizingPath! } + super.init(name:url.lastPathComponent!) } override func nodeName() -> String { - return "📄 "+url.lastPathComponent! + return "📄 "+name } func relativePath(relativeTo: String) -> String { @@ -211,6 +229,23 @@ class ASFileItem : ASFileNode { override func exists() -> Bool { return url.checkResourceIsReachableAndReturnError(nil) } + override func modDate() -> NSDate? { + var date: AnyObject? + url.getResourceValue(&date, forKey: NSURLContentModificationDateKey, error: nil) + return date as? NSDate + } + override func revision() -> String? { + let task = NSTask() + task.launchPath = NSBundle.mainBundle().pathForResource("FileRevision", ofType: "")! + let outputPipe = NSPipe() + task.standardOutput = outputPipe + task.standardError = NSFileHandle.fileHandleWithNullDevice() + task.arguments = [url.path!] + task.launch() + + return NSString(data: outputPipe.fileHandleForReading.readDataToEndOfFile(), + encoding: NSUTF8StringEncoding) as? String + } } class ASFileTree : NSObject, NSOutlineViewDataSource { diff --git a/AVRsack/ASProjDoc.swift b/AVRsack/ASProjDoc.swift index fee5289..dde775b 100644 --- a/AVRsack/ASProjDoc.swift +++ b/AVRsack/ASProjDoc.swift @@ -26,7 +26,7 @@ func pushToFront(inout list: [String], front: String) { list.insert(front, atIndex: 0) } -class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePanelDelegate { +class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePanelDelegate, ACEViewDelegate { @IBOutlet weak var editor : ACEView! @IBOutlet weak var outline : NSOutlineView! @IBOutlet weak var boardTool: NSPopUpButton! @@ -49,7 +49,10 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa var logModified = NSDate.distantPast() as! NSDate var logSize = 0 var updateLogTimer : NSTimer? - + var printingDone : () -> () = {} + var printModDate : NSDate? + var printRevision : String? + let kVersionKey = "Version" let kCurVersion = 1.0 let kFilesKey = "Files" @@ -107,6 +110,8 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa editor.setTheme(currentTheme) editor.setKeyboardHandler(keyboardHandler) editor.setFontSize(fontSize) + editor.delegate = self + outline.setDataSource(files) files.apply() { node in if let group = node as? ASFileGroup { @@ -277,8 +282,147 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa // MARK: Printing - override func printDocument(sender: AnyObject?) { - editor.print(sender) + override func printDocumentWithSettings(printSettings: [NSObject : AnyObject], showPrintPanel: Bool, delegate: AnyObject?, didPrintSelector: Selector, contextInfo: UnsafeMutablePointer) { + // + // Thanks to Erica Sadun for showing me how to call a selector in Swift + // + printingDone = + { () -> () in + if let del : AnyObject = delegate { + NSThread.detachNewThreadSelector(didPrintSelector, toTarget: del, withObject: contextInfo as? AnyObject) + } + } + printModDate = mainEditor?.modDate() + printRevision = mainEditor?.revision() + + editor.print(self) + } + + func printSettings() -> NSPrintInfo! { + var info = printInfo.copy() as! NSPrintInfo + + // + // Minimize margins + // + let kXMargin : CGFloat = 50.0 + let kYMargin : CGFloat = 50.0 + let paperSize = info.paperSize + var maxBounds = info.imageablePageBounds + + if paperSize.width - maxBounds.size.width < kXMargin { + let adjust = kXMargin-paperSize.width+maxBounds.size.width + maxBounds.origin.x += 0.5*adjust + maxBounds.size.width -= adjust + } + if paperSize.height - maxBounds.size.height < kYMargin { + let adjust = kYMargin-paperSize.height+maxBounds.size.height + maxBounds.origin.y += 0.5*adjust + maxBounds.size.height -= adjust + } + info.leftMargin = maxBounds.origin.x + info.bottomMargin = maxBounds.origin.y + info.topMargin = paperSize.height-maxBounds.size.height-info.bottomMargin + info.rightMargin = paperSize.width-maxBounds.size.width-info.leftMargin + + return info + } + + func printJobTitle() -> String! { + return mainEditor?.nodeName() ?? + fileURL?.lastPathComponent?.stringByDeletingPathExtension ?? + "Untitled" + } + + func printHeaderHeight() -> Float { + return 41.0 + } + + func printFooterHeight() -> Float { + return 20.0 + } + + func drawPrintHeaderForPage(pageNo: Int32, inRect r: NSRect) { + var rect = r + rect.origin.y += 5.0 + rect.size.height -= 5.0 + + let ctx = NSGraphicsContext.currentContext()! + ctx.saveGraphicsState() + NSColor(white: 0.95, alpha: 1.0).setFill() + var wideBox = rect + wideBox.size.height = 20.0 + wideBox.origin.y += 0.5*(rect.size.height-wideBox.size.height) + NSRectFill(wideBox) + + NSColor(white: 0.7, alpha: 1.0).setFill() + var pageNoBox = rect + pageNoBox.size.width = 50.0 + pageNoBox.origin.x += 0.5*(rect.size.width-pageNoBox.size.width) + NSRectFill(pageNoBox) + ctx.restoreGraphicsState() + + let pageNoFont = NSFont.userFixedPitchFontOfSize(25.0)! + let pageNoAttr = [ + NSFontAttributeName: pageNoFont, + NSForegroundColorAttributeName: NSColor.whiteColor(), + NSStrokeWidthAttributeName: -5.0] + let pageNoStr = "\(pageNo)" + let pageNoSize = pageNoStr.sizeWithAttributes(pageNoAttr) + let pageNoAt = NSPoint( + x: pageNoBox.origin.x+0.5*(pageNoBox.size.width-pageNoSize.width), + y: pageNoBox.origin.y+3.5) + pageNoStr.drawAtPoint(pageNoAt, withAttributes:pageNoAttr) + + let kXOffset : CGFloat = 5.0 + let titleFont = NSFont.userFontOfSize(12.0)! + let titleAttr = [NSFontAttributeName:titleFont] + var titleAt = NSPoint( + x: wideBox.origin.x+kXOffset, + y: wideBox.origin.y+0.5*(wideBox.size.height-titleFont.ascender+titleFont.descender)) + + if let fileNameStr = mainEditor?.name { + fileNameStr.drawAtPoint(titleAt, withAttributes:titleAttr) + } + if let projectNameStr = fileURL?.lastPathComponent?.stringByDeletingPathExtension { + let projectNameSize = projectNameStr.sizeWithAttributes(titleAttr) + titleAt.x = wideBox.origin.x+wideBox.size.width-projectNameSize.width-kXOffset + projectNameStr.drawAtPoint(titleAt, withAttributes:titleAttr) + } + } + + func drawPrintFooterForPage(pageNo: Int32, inRect r: NSRect) { + var rect = r + rect.size.height -= 5.0 + + let ctx = NSGraphicsContext.currentContext()! + ctx.saveGraphicsState() + NSColor(white: 0.95, alpha: 1.0).setFill() + NSRectFill(rect) + ctx.restoreGraphicsState() + + let kXOffset : CGFloat = 5.0 + let footFont = NSFont.userFixedPitchFontOfSize(10.0)! + let footAttr = [NSFontAttributeName:footFont] + var footAt = NSPoint( + x: rect.origin.x+kXOffset, + y: rect.origin.y+0.5*(rect.size.height-footFont.ascender+footFont.descender)) + + if let revisionStr = printRevision { + revisionStr.drawAtPoint(footAt, withAttributes:footAttr) + } + if let modDate = printModDate + { + let dateFormatter = NSDateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" + let modDateStr = dateFormatter.stringFromDate(modDate) + let modDateSize = modDateStr.sizeWithAttributes(footAttr) + footAt.x = rect.origin.x+rect.size.width-modDateSize.width-kXOffset + modDateStr.drawAtPoint(footAt, withAttributes:footAttr) + } + } + + func printingComplete() { + printingDone() } // MARK: Outline View Delegate diff --git a/AVRsack/Base.lproj/ASProjDoc.xib b/AVRsack/Base.lproj/ASProjDoc.xib index 49fecfa..d900466 100644 --- a/AVRsack/Base.lproj/ASProjDoc.xib +++ b/AVRsack/Base.lproj/ASProjDoc.xib @@ -1,7 +1,7 @@ - + - + @@ -11,6 +11,7 @@ + @@ -278,6 +279,10 @@ + + + + diff --git a/AVRsack/FileRevision b/AVRsack/FileRevision new file mode 100755 index 0000000..a913abe --- /dev/null +++ b/AVRsack/FileRevision @@ -0,0 +1,33 @@ +#!/usr/bin/ruby +# +# FileRevision FILE +# +# AVRsack +# +# Created by Matthias Neeracher on 03/16/15. +# Copyright © 2015 Aere Perennius. All rights reserved. +# + +file = ARGV[0] +if file =~ %r|(.*)/(.*)| + Dir.chdir($1) + file = $2 +end +rev = '' +IO.popen("git log -1 --pretty=oneline '#{file}'", 'r') do |io| + if line = io.gets + rev = line[0..7] + end +end +if $? == 0 && rev.size > 0 + print rev +else + IO.popen("svn info '#{file}'") do |io| + io.each_line do |line| + if line =~ /Last Changed Rev:\s+(\S+)/ + print "r"+$1 + exit 0 + end + end + end +end