├── Screenshots └── sample.png ├── Examples ├── unicode │ ├── images │ │ ├── unicode-checker.png │ │ ├── unicode-explained.jpg │ │ └── the-unicode-standard.png │ └── slides.swift └── attr-string-in-swift │ └── slides.swift ├── Truffaut ├── Truffaut-Bridging-Header.h ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Slice 1128w.png │ │ ├── Slice 116w.png │ │ ├── Slice 1256w.png │ │ ├── Slice 132w.png │ │ ├── Slice 1512w.png │ │ ├── Slice 164w.png │ │ ├── Slice 11024w.png │ │ ├── Slice 1256w-1.png │ │ ├── Slice 132w-1.png │ │ ├── Slice 1512w-1.png │ │ └── Contents.json ├── PreferenceWindowController.swift ├── NSWindow+Style.swift ├── syntax-hl.rb ├── MainWindowController.swift ├── SlidesWindowController.swift ├── MainViewController.swift ├── AppDelegate.swift ├── StoryboardLoading.swift ├── PreferenceViewController.swift ├── UserPreference.swift ├── Info.plist ├── Shell.swift ├── ExportController.swift ├── Font.swift ├── ReadController.swift ├── SyntaxHighlighter.swift ├── PresentationDocument.swift ├── SlidesViewController.swift ├── PageViewController.xib ├── PageViewController.swift └── Base.lproj │ └── Main.storyboard ├── Truffaut.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ ├── Truffaut.xcscheme │ │ └── TruffautSupport.xcscheme └── project.pbxproj ├── TruffautSupport ├── Page.swift ├── TruffautSupport.h ├── Info.plist ├── Presentation.swift └── Content.swift ├── TruffautSupportTests ├── Info.plist └── TruffautSupportTests.swift ├── LICENSE ├── README.md ├── .gitignore └── Documentations └── TruffautSupport-API-Reference.md /Screenshots/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Screenshots/sample.png -------------------------------------------------------------------------------- /Examples/unicode/images/unicode-checker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/unicode-checker.png -------------------------------------------------------------------------------- /Examples/unicode/images/unicode-explained.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/unicode-explained.jpg -------------------------------------------------------------------------------- /Examples/unicode/images/the-unicode-standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Examples/unicode/images/the-unicode-standard.png -------------------------------------------------------------------------------- /Truffaut/Truffaut-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1128w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1128w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 116w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 116w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 164w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 164w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 11024w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 11024w.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1256w-1.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 132w-1.png -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codezerker/Truffaut/HEAD/Truffaut/Assets.xcassets/AppIcon.appiconset/Slice 1512w-1.png -------------------------------------------------------------------------------- /Truffaut.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Truffaut/PreferenceWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferenceWindowController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 29/12/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PreferenceWindowController: NSWindowController { 12 | 13 | // ... 14 | } 15 | 16 | extension PreferenceWindowController: StoryboardInstantiatable { 17 | 18 | typealias ControllerType = PreferenceWindowController 19 | } 20 | -------------------------------------------------------------------------------- /Truffaut/NSWindow+Style.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSWindow+Style.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 29/12/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | extension NSWindow { 12 | 13 | func applyTransparentTitlebarStyle() { 14 | styleMask.insert(NSWindow.StyleMask.fullSizeContentView) 15 | titlebarAppearsTransparent = true 16 | titleVisibility = .hidden 17 | isMovable = true 18 | isMovableByWindowBackground = true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TruffautSupport/Page.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Page.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Page: Codable { 12 | 13 | public let title: String? 14 | public let subtitle: String? 15 | public let contents: [Content]? 16 | 17 | public init(title: String? = nil, subtitle: String? = nil, contents: [Content]? = nil) { 18 | self.title = title 19 | self.subtitle = subtitle 20 | self.contents = contents 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TruffautSupport/TruffautSupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // TruffautSupport.h 3 | // TruffautSupport 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TruffautSupport. 12 | FOUNDATION_EXPORT double TruffautSupportVersionNumber; 13 | 14 | //! Project version string for TruffautSupport. 15 | FOUNDATION_EXPORT const unsigned char TruffautSupportVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Truffaut/syntax-hl.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rouge' 4 | 5 | source = ARGV.shift 6 | sourceFileType = ARGV.shift 7 | 8 | case sourceFileType 9 | when 'c' 10 | lexer = Rouge::Lexers::C.new 11 | when 'cpp' 12 | lexer = Rouge::Lexers::Cpp.new 13 | when 'javaScript' 14 | lexer = Rouge::Lexers::Javascript.new 15 | when 'objc' 16 | lexer = Rouge::Lexers::ObjectiveC.new 17 | when 'rust' 18 | lexer = Rouge::Lexers::Rust.new 19 | when 'shell' 20 | lexer = Rouge::Lexers::Shell.new 21 | when 'swift' 22 | lexer = Rouge::Lexers::Swift.new 23 | end 24 | 25 | formatter = Rouge::Formatters::HTMLInline.new Rouge::Themes::MonokaiSublime.new 26 | 27 | result = formatter.format(lexer.lex(source)) 28 | puts result 29 | -------------------------------------------------------------------------------- /Truffaut/MainWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindowController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/15/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainWindowController: NSWindowController { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | 16 | window?.applyTransparentTitlebarStyle() 17 | 18 | if let contentViewController = contentViewController as? MainViewController { 19 | contentViewController.windowController = self 20 | } 21 | } 22 | } 23 | 24 | extension MainWindowController: StoryboardInstantiatable { 25 | 26 | typealias ControllerType = MainWindowController 27 | } 28 | -------------------------------------------------------------------------------- /Truffaut/SlidesWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlidesWindowController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/15/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SlidesWindowController: NSWindowController { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | 16 | window?.applyTransparentTitlebarStyle() 17 | 18 | if let contentViewController = contentViewController as? SlidesViewController { 19 | contentViewController.windowController = self 20 | } 21 | } 22 | } 23 | 24 | extension SlidesWindowController: StoryboardInstantiatable { 25 | 26 | typealias ControllerType = SlidesWindowController 27 | } 28 | -------------------------------------------------------------------------------- /TruffautSupportTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 204 21 | 22 | 23 | -------------------------------------------------------------------------------- /TruffautSupport/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 204 21 | NSHumanReadableCopyright 22 | Copyright © 2017 Codezerker. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TruffautSupport/Presentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Presentation.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Presentation: Codable { 12 | 13 | public let title: String 14 | public let authors: [String] 15 | public let pages: [Page] 16 | 17 | public init(title: String = "", authors: [String] = [], pages: [Page]) { 18 | self.title = title 19 | self.authors = authors 20 | self.pages = pages 21 | 22 | // dump self as a JSON String to STDOUT 23 | dump() 24 | } 25 | } 26 | 27 | private extension Presentation { 28 | 29 | private func dump() { 30 | let encoder = JSONEncoder() 31 | encoder.outputFormatting = .prettyPrinted 32 | guard let jsonData = try? encoder.encode(self), 33 | let jsonString = String(data: jsonData, encoding: .utf8) else { 34 | return 35 | } 36 | print(jsonString) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Truffaut/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/14/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainViewController: NSViewController { 12 | 13 | @IBOutlet weak var pathControl: NSPathControl! 14 | 15 | weak var windowController: MainWindowController? 16 | 17 | private lazy var slidesWindowController: SlidesWindowController = { 18 | return SlidesWindowController.loadFromStoryboard() 19 | }() 20 | 21 | override func viewWillAppear() { 22 | super.viewWillAppear() 23 | 24 | if let fileURL = (windowController?.document as? PresentationDocument)?.fileURL { 25 | pathControl.url = fileURL 26 | } 27 | } 28 | 29 | @IBAction func showSlides(_ sender: AnyObject?) { 30 | windowController?.document?.addWindowController(slidesWindowController) 31 | slidesWindowController.showWindow(nil) 32 | 33 | windowController?.window?.orderOut(nil) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Truffaut/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/14/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | // MARK: - App Delegate 10 | 11 | import Cocoa 12 | 13 | @NSApplicationMain 14 | class AppDelegate: NSObject, NSApplicationDelegate { 15 | 16 | private var preferenceWindowController: PreferenceWindowController? 17 | 18 | func applicationDidFinishLaunching(_ notification: Notification) { 19 | openDocumentIfNeeded() 20 | } 21 | 22 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { 23 | if flag { 24 | return true 25 | } else { 26 | openDocumentIfNeeded() 27 | return false 28 | } 29 | } 30 | } 31 | 32 | fileprivate extension AppDelegate { 33 | 34 | func openDocumentIfNeeded() { 35 | NSDocumentController.shared.openDocument(nil) 36 | } 37 | } 38 | 39 | fileprivate extension AppDelegate { 40 | 41 | @IBAction func showPreferenceWindow(_ sender: Any?) { 42 | preferenceWindowController = PreferenceWindowController.loadFromStoryboard() 43 | preferenceWindowController?.window?.makeKeyAndOrderFront(sender) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Truffaut/StoryboardLoading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Definitions.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/15/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | 10 | // MARK: - Storyboard 11 | 12 | struct Storyboard { 13 | 14 | struct Names { 15 | static let main = "Main" 16 | } 17 | 18 | struct Identifiers { 19 | static let mainWindowController = "MainWindowController" 20 | static let slidesWindowController = "SlideWindowController" 21 | } 22 | } 23 | 24 | protocol StoryboardInstantiatable { 25 | associatedtype ControllerType 26 | static var storyboardName: String { get } 27 | static var storyboardIdentifier: String { get } 28 | static func loadFromStoryboard() -> ControllerType 29 | } 30 | 31 | import AppKit 32 | 33 | extension StoryboardInstantiatable { 34 | 35 | static var storyboardName: String { 36 | return Storyboard.Names.main 37 | } 38 | 39 | static var storyboardIdentifier: String { 40 | return "\(Self.self)" 41 | } 42 | 43 | static func loadFromStoryboard() -> ControllerType { 44 | let controller = NSStoryboard(name: storyboardName, bundle: nil).instantiateController(withIdentifier: storyboardIdentifier) as! ControllerType 45 | return controller 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Codezerker Limited 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Truffaut/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "Slice 116w.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "Slice 132w.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "Slice 132w-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "Slice 164w.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "Slice 1128w.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "Slice 1256w.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "Slice 1256w-1.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "Slice 1512w.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "Slice 1512w-1.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "Slice 11024w.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Truffaut/PreferenceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferenceViewController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 29/12/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PreferenceViewController: NSViewController { 12 | 13 | @IBOutlet private weak var swiftcPathTextField: NSTextField! 14 | @IBOutlet private weak var activeSwiftcPathLabel: NSTextField! 15 | @IBOutlet private weak var saveSwiftcPathButton: NSButton! 16 | 17 | @IBOutlet private weak var rubyPathTextField: NSTextField! 18 | @IBOutlet private weak var activeRubyPathLabel: NSTextField! 19 | @IBOutlet private weak var saveRubyPathButton: NSButton! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | updateViews() 24 | } 25 | 26 | private func updateViews() { 27 | activeSwiftcPathLabel.stringValue = Shell.Environment.swiftc 28 | activeRubyPathLabel.stringValue = Shell.Environment.ruby 29 | 30 | if let customSwiftcPath = UserPreference.customSwiftcPath { 31 | swiftcPathTextField.stringValue = customSwiftcPath 32 | } 33 | if let customRubyPath = UserPreference.customRubyPath { 34 | rubyPathTextField.stringValue = customRubyPath 35 | } 36 | } 37 | 38 | @IBAction private func saveCustomSwiftcPath(_ sender: Any?) { 39 | UserPreference.customSwiftcPath = swiftcPathTextField.stringValue 40 | updateViews() 41 | } 42 | 43 | @IBAction private func saveCustomRubyPath(_ sender: Any?) { 44 | UserPreference.customRubyPath = rubyPathTextField.stringValue 45 | updateViews() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Truffaut/UserPreference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserPreference.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 29/12/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UserPreference { 12 | 13 | struct UserDefaultKeys { 14 | static let customSwiftcPath = "com.codezerker.paths.swiftc" 15 | static let customRubyPath = "com.codezerker.paths.ruby" 16 | } 17 | 18 | static var customSwiftcPath: String? { 19 | set { 20 | guard let newValue = newValue, !newValue.isEmpty else { 21 | UserDefaults.standard.removeObject(forKey: UserDefaultKeys.customSwiftcPath) 22 | return 23 | } 24 | UserDefaults.standard.setValue(newValue, forKey: UserDefaultKeys.customSwiftcPath) 25 | } 26 | get { 27 | guard let customPath = UserDefaults.standard.string(forKey: UserDefaultKeys.customSwiftcPath), 28 | !customPath.isEmpty else { 29 | return nil 30 | } 31 | return customPath 32 | } 33 | } 34 | 35 | static var customRubyPath: String? { 36 | set { 37 | guard let newValue = newValue, !newValue.isEmpty else { 38 | UserDefaults.standard.removeObject(forKey: UserDefaultKeys.customRubyPath) 39 | return 40 | } 41 | UserDefaults.standard.setValue(newValue, forKey: UserDefaultKeys.customRubyPath) 42 | } 43 | get { 44 | guard let customPath = UserDefaults.standard.string(forKey: UserDefaultKeys.customRubyPath), 45 | !customPath.isEmpty else { 46 | return nil 47 | } 48 | return customPath 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Truffaut/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | swift 13 | 14 | CFBundleTypeName 15 | Swift 16 | CFBundleTypeRole 17 | Viewer 18 | LSTypeIsPackage 19 | 0 20 | NSDocumentClass 21 | $(PRODUCT_MODULE_NAME).PresentationDocument 22 | 23 | 24 | CFBundleExecutable 25 | $(EXECUTABLE_NAME) 26 | CFBundleIconFile 27 | 28 | CFBundleIdentifier 29 | $(PRODUCT_BUNDLE_IDENTIFIER) 30 | CFBundleInfoDictionaryVersion 31 | 6.0 32 | CFBundleName 33 | $(PRODUCT_NAME) 34 | CFBundlePackageType 35 | APPL 36 | CFBundleShortVersionString 37 | 0.7.1 38 | CFBundleSignature 39 | ???? 40 | CFBundleVersion 41 | 204 42 | LSApplicationCategoryType 43 | public.app-category.developer-tools 44 | LSMinimumSystemVersion 45 | $(MACOSX_DEPLOYMENT_TARGET) 46 | NSHumanReadableCopyright 47 | Copyright © 2017 Codezerker. All rights reserved. 48 | NSMainStoryboardFile 49 | Main 50 | NSPrincipalClass 51 | NSApplication 52 | SUEnableAutomaticChecks 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Truffaut/Shell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reader.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ShellRuntimeError: Error { 12 | case brokenPipe 13 | case processError(stderr: String?) 14 | } 15 | 16 | class Shell: NSObject { 17 | 18 | struct Environment { 19 | 20 | static var swiftc: String { 21 | return UserPreference.customSwiftcPath ?? "/usr/bin/swiftc" 22 | } 23 | 24 | static var ruby: String { 25 | return UserPreference.customRubyPath ?? "/usr/local/bin/ruby" 26 | } 27 | } 28 | 29 | public static func call(command: String, arguments: [String] = [], currentDirectoryPath: String? = nil) throws -> String { 30 | let process = Process() 31 | process.launchPath = command 32 | process.arguments = arguments 33 | if let currentDirectoryPath = currentDirectoryPath { 34 | process.currentDirectoryPath = currentDirectoryPath 35 | } 36 | 37 | let stdout = Pipe() 38 | let stderr = Pipe() 39 | process.standardOutput = stdout 40 | process.standardError = stderr 41 | 42 | try process.run() 43 | process.waitUntilExit() 44 | 45 | let stderrData = stderr.fileHandleForReading.readDataToEndOfFile() 46 | guard stderrData.count == 0 else { 47 | throw ShellRuntimeError.processError(stderr: String(data: stderrData, encoding: .utf8)) 48 | } 49 | 50 | let stdoutData = stdout.fileHandleForReading.readDataToEndOfFile() 51 | guard let output = String(data: stdoutData, encoding: .utf8) else { 52 | throw ShellRuntimeError.brokenPipe 53 | } 54 | 55 | return output 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Truffaut 2 | 3 | A humble tool to help you presenting ideas by writing Swift 4 | 5 | ![](Screenshots/sample.png) 6 | 7 | ## Requirements 8 | 9 | - **Xcode 10.2.1**, the embedded `swiftc` will be used to interpret the slides manifest file 10 | - Ruby gem [**rouge**](https://github.com/jneen/rouge), this gem will be used to provide syntax highlighting for source code blocks 11 | 12 | ## Usage 13 | 14 | ### Get Truffaut.app 15 | 16 | Clone this repo and build the target `Truffaut`. 17 | 18 | ### Create slides manifest 19 | 20 | Create a Swift file: 21 | 22 | ```sh 23 | $ touch slides.swift 24 | ``` 25 | 26 | Import the supporting module: 27 | 28 | ```swift 29 | import TruffautSupport 30 | ``` 31 | 32 | Initialize a presentation with pages: 33 | 34 | ```swift 35 | let presentation = Presentation(pages: [ 36 | Page(title: "Hello World", subtitle: "A Swift Slide"), 37 | ]) 38 | ``` 39 | 40 | ## Manifest file reference 41 | 42 | For a full reference of the `TruffautSupport` supporting module, please check [here](Documentations/TruffautSupport-API-Reference.md). 43 | 44 | ## Examples 45 | 46 | For real-life Truffaut slides, please check the [Examples](Examples/). 47 | 48 | ## Caveats and Known Issues 49 | 50 | - The Xcode version selected by `xcode-select` must be the same as the one used to build the application. 51 | - Otherwise opening slides may fail with errors like this: `slides.swift:1:8: error: cannot load underlying module for 'TruffautSupport'` 52 | - In Swift 5.1, this issue is likely to be fixed by [Library Evolution](https://github.com/apple/swift-evolution/blob/master/proposals/0260-library-evolution.md). 53 | - Currently the syntax highlighter is assuming ruby is installed at path `/usr/local/bin/ruby` which is usually true if the ruby is intalled with [homebrew](https://brew.sh/). 54 | - If this is not true for you, you can modify the ruby path in the preference (`⌘ + ,`). 55 | - Export to PDF is not implemented. 56 | -------------------------------------------------------------------------------- /Truffaut/ExportController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 23/07/2016. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Quartz 11 | 12 | protocol ExportControllerDataSource { 13 | 14 | func numberOfPagesToExport() -> Int 15 | func viewForPageToExport(atIndex index: Int) -> NSView? 16 | } 17 | 18 | struct ExportController { 19 | 20 | enum ExportingType { 21 | case pdf 22 | 23 | var fileExtension: String { 24 | switch self { 25 | case .pdf: 26 | return "pdf" 27 | } 28 | } 29 | } 30 | 31 | func exportToPDF(withDataSource dataSource: ExportControllerDataSource) -> PDFDocument { 32 | let pdfDocument = PDFDocument() 33 | let numberOfPages = dataSource.numberOfPagesToExport() 34 | for i in (0.. PDFPage? { 53 | view.frame = NSRect(origin: .zero, size: Layout.defaultExportContentSize) 54 | 55 | // FIXME: in some cases the layout is incorrect, 56 | // we don't know the reason yet 57 | view.updateConstraintsForSubtreeIfNeeded() 58 | view.layoutSubtreeIfNeeded() 59 | 60 | let pdfData = view.dataWithPDF(inside: view.bounds) 61 | guard let pdfRep = NSPDFImageRep(data: pdfData) else { 62 | return nil 63 | } 64 | 65 | let pdfImage = NSImage(size: view.bounds.size) 66 | pdfImage.addRepresentation(pdfRep) 67 | return PDFPage(image: pdfImage) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Truffaut/Font.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Font.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 17/08/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | struct DynamicLayout { 12 | 13 | private static let defaultScreenSize = NSSize(width: 800, height: 600) 14 | static var currentScreenSize = DynamicLayout.defaultScreenSize 15 | 16 | static func sizeFittingCurrentScreenSize(originalSize: CGFloat) -> CGFloat { 17 | return min(originalSize / DynamicLayout.defaultScreenSize.width * DynamicLayout.currentScreenSize.width, 18 | originalSize / DynamicLayout.defaultScreenSize.height * DynamicLayout.currentScreenSize.height) 19 | } 20 | } 21 | 22 | struct Font { 23 | 24 | struct Cover { 25 | 26 | static var title: NSFont { 27 | return NSFont.boldSystemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 42)) 28 | } 29 | 30 | static var subtitle: NSFont { 31 | return NSFont.systemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 24)) 32 | } 33 | } 34 | 35 | struct Page { 36 | 37 | static var title: NSFont { 38 | return NSFont.boldSystemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 38)) 39 | } 40 | 41 | static var text: NSFont { 42 | return NSFont.systemFont(ofSize: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 24)) 43 | } 44 | 45 | static var source: NSFont { 46 | guard let sfMono = NSFont(name: "SF Mono", size: DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 18)) else { 47 | return text 48 | } 49 | return sfMono 50 | } 51 | } 52 | } 53 | 54 | struct TextColor { 55 | 56 | struct Display { 57 | static let title = NSColor.white 58 | static let subtitle = NSColor.white 59 | static let text = NSColor.white 60 | static let source = NSColor.white 61 | } 62 | 63 | struct Export { 64 | static let title = NSColor.black 65 | static let subtitle = NSColor.black 66 | static let text = NSColor.black 67 | static let source = NSColor.black 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Truffaut/ReadController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReadController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/14/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import TruffautSupport 10 | 11 | struct ReadController { 12 | 13 | enum ReadingError: Error { 14 | case unableToLoadSupportModule 15 | case malformedManifestData(output: String) 16 | } 17 | 18 | static func read(from url: URL, completion: @escaping (Presentation?, Error?) -> Void) { 19 | ManifestReading.queue.async { 20 | do { 21 | let cmd = Shell.Environment.swiftc 22 | let args = try ManifestReading.commandArguments(with: url) 23 | let output = try Shell.call(command: cmd, arguments: args) 24 | guard let outputData = output.data(using: .utf8) else { 25 | DispatchQueue.main.async { 26 | completion(nil, ReadingError.malformedManifestData(output: output)) 27 | } 28 | return 29 | } 30 | let presentation = try JSONDecoder().decode(Presentation.self, from: outputData) 31 | DispatchQueue.main.async { 32 | completion(presentation, nil) 33 | } 34 | } catch { 35 | DispatchQueue.main.async { 36 | completion(nil, error) 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | import Foundation 44 | 45 | fileprivate extension ReadController { 46 | 47 | struct ManifestReading { 48 | 49 | static let queue = DispatchQueue(label: "com.codezerker.truffaut.manifestReading") 50 | 51 | private static let supportModuleName = "TruffautSupport" 52 | private static var searchPath: String? { 53 | return Bundle.main.privateFrameworksURL?.appendingPathComponent("\(supportModuleName).framework/Library", isDirectory: true).path 54 | } 55 | 56 | static func commandArguments(with url: URL) throws -> [String] { 57 | guard let searchPath = searchPath else { 58 | throw ReadingError.unableToLoadSupportModule 59 | } 60 | return [ 61 | /* swiftc settings */ 62 | "--driver-mode=swift", 63 | "-target", "x86_64-apple-macosx10.14.4", 64 | 65 | /* link options */ 66 | "-I", searchPath, 67 | "-L", searchPath, 68 | "-l\(supportModuleName)", 69 | 70 | /* manifest file path */ 71 | url.path 72 | ] 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /TruffautSupport/Content.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Content.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Content { 12 | 13 | public enum FileType: String, Codable { 14 | case plainText 15 | 16 | case c 17 | case cpp 18 | case javaScript 19 | case objc 20 | case rust 21 | case shell 22 | case swift 23 | } 24 | 25 | case title(String) 26 | case text(String) 27 | case image(String) 28 | case sourceCode(FileType, String) 29 | 30 | indirect case indent([Content]) 31 | } 32 | 33 | extension Content: Codable { 34 | 35 | private enum CodingKeys: String, CodingKey { 36 | case title 37 | case image 38 | case text 39 | 40 | case sourceCode 41 | case fileType 42 | 43 | case indent 44 | } 45 | 46 | public init(from decoder: Decoder) throws { 47 | let values = try decoder.container(keyedBy: CodingKeys.self) 48 | if let title = try values.decodeIfPresent(String.self, forKey: .title) { 49 | self = .title(title) 50 | } else if let imageFilePath = try values.decodeIfPresent(String.self, forKey: .image) { 51 | self = .image(imageFilePath) 52 | } else if let text = try values.decodeIfPresent(String.self, forKey: .text) { 53 | self = .text(text) 54 | } else if let sourceCode = try values.decodeIfPresent(String.self, forKey: .sourceCode), 55 | let fileType = try values.decodeIfPresent(FileType.self, forKey: .fileType) { 56 | self = .sourceCode(fileType, sourceCode) 57 | } else if let contents = try values.decodeIfPresent([Content].self, forKey: .indent) { 58 | self = .indent(contents) 59 | } else { 60 | let context = DecodingError.Context(codingPath: values.codingPath, 61 | debugDescription: "Unexpected content") 62 | throw DecodingError.dataCorrupted(context) 63 | } 64 | } 65 | 66 | public func encode(to encoder: Encoder) throws { 67 | var container = encoder.container(keyedBy: CodingKeys.self) 68 | switch self { 69 | case .title(let title): 70 | try container.encode(title, forKey: .title) 71 | case .image(let imageFilePath): 72 | try container.encode(imageFilePath, forKey: .image) 73 | case .text(let text): 74 | try container.encode(text, forKey: .text) 75 | case .sourceCode(let fileType, let sourceCode): 76 | try container.encode(fileType, forKey: .fileType) 77 | try container.encode(sourceCode, forKey: .sourceCode) 78 | case .indent(let contents): 79 | try container.encode(contents, forKey: .indent) 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,swift,vim,xcode 3 | 4 | ### OSX ### 5 | *.DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | ### Swift ### 33 | # Xcode 34 | # 35 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 36 | 37 | ## Build generated 38 | build/ 39 | DerivedData/ 40 | 41 | ## Various settings 42 | *.pbxuser 43 | !default.pbxuser 44 | *.mode1v3 45 | !default.mode1v3 46 | *.mode2v3 47 | !default.mode2v3 48 | *.perspectivev3 49 | !default.perspectivev3 50 | xcuserdata/ 51 | 52 | ## Other 53 | *.moved-aside 54 | *.xccheckout 55 | *.xcscmblueprint 56 | 57 | ## Obj-C/Swift specific 58 | *.hmap 59 | *.ipa 60 | *.dSYM.zip 61 | *.dSYM 62 | 63 | ## Playgrounds 64 | timeline.xctimeline 65 | playground.xcworkspace 66 | 67 | # Swift Package Manager 68 | # 69 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 70 | # Packages/ 71 | # Package.pins 72 | .build/ 73 | 74 | # CocoaPods 75 | # 76 | # We recommend against adding the Pods directory to your .gitignore. However 77 | # you should judge for yourself, the pros and cons are mentioned at: 78 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 79 | # 80 | # Pods/ 81 | 82 | # Carthage 83 | # 84 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 85 | Carthage/Checkouts 86 | Carthage/Build 87 | 88 | # fastlane 89 | # 90 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 91 | # screenshots whenever they are needed. 92 | # For more information about the recommended setup visit: 93 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 94 | 95 | fastlane/report.xml 96 | fastlane/Preview.html 97 | fastlane/screenshots 98 | fastlane/test_output 99 | 100 | ### Vim ### 101 | # swap 102 | [._]*.s[a-v][a-z] 103 | [._]*.sw[a-p] 104 | [._]s[a-v][a-z] 105 | [._]sw[a-p] 106 | # session 107 | Session.vim 108 | # temporary 109 | .netrwhist 110 | *~ 111 | # auto-generated tag files 112 | tags 113 | 114 | ### Xcode ### 115 | # Xcode 116 | # 117 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 118 | 119 | ## Build generated 120 | 121 | ## Various settings 122 | 123 | ## Other 124 | 125 | ### Xcode Patch ### 126 | *.xcodeproj/* 127 | !*.xcodeproj/project.pbxproj 128 | !*.xcodeproj/xcshareddata/ 129 | !*.xcworkspace/contents.xcworkspacedata 130 | /*.gcno 131 | 132 | # End of https://www.gitignore.io/api/osx,swift,vim,xcode 133 | -------------------------------------------------------------------------------- /TruffautSupportTests/TruffautSupportTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TruffautSupportTests.swift 3 | // TruffautSupportTests 4 | // 5 | // Created by Yan Li on 4/3/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import TruffautSupport 11 | 12 | class TruffautSupportTests: XCTestCase { 13 | 14 | func testJSONRepresentation() { 15 | let sourceCode = 16 | """ 17 | extension NSAttributedString: _CFBridgeable { 18 | var _cfObject: CFAttributedString { 19 | return unsafeBitCast(self, to: CFAttributedString.self) 20 | } 21 | } 22 | """ 23 | 24 | let presentation = Presentation( 25 | title: "presentation title", 26 | authors: ["Yan Li ", "@eyeplum"], 27 | pages: [ 28 | Page(title: "hello", 29 | subtitle: "say hello", 30 | contents: [ 31 | .indent([ 32 | .image("./image/url.png"), 33 | .text("hello"), 34 | .sourceCode(.swift, sourceCode), 35 | .sourceCode(.javaScript, sourceCode), 36 | ]), 37 | ]), 38 | ]) 39 | 40 | guard let data = try? JSONEncoder().encode(presentation), 41 | let newPresentation = try? JSONDecoder().decode(Presentation.self, from: data) else { 42 | XCTAssert(false) 43 | return 44 | } 45 | 46 | XCTAssertEqual(presentation.title, newPresentation.title) 47 | XCTAssertEqual(presentation.authors, newPresentation.authors) 48 | XCTAssertEqual(presentation.pages.count, newPresentation.pages.count) 49 | XCTAssertEqual(presentation.pages[0].title, newPresentation.pages[0].title) 50 | XCTAssertEqual(presentation.pages[0].subtitle, newPresentation.pages[0].subtitle) 51 | XCTAssertEqual(presentation.pages[0].contents?.count, newPresentation.pages[0].contents?.count) 52 | 53 | guard let indent = newPresentation.pages[0].contents?.first, 54 | case let .indent(contents) = indent else { 55 | XCTAssert(false) 56 | return 57 | } 58 | XCTAssertEqual(contents.count, 4) 59 | 60 | guard case let .image(path) = contents[0] else { 61 | XCTAssert(false) 62 | return 63 | } 64 | XCTAssertEqual(path, "./image/url.png") 65 | 66 | guard case let .text(string) = contents[1] else { 67 | XCTAssert(false) 68 | return 69 | } 70 | XCTAssertEqual(string, "hello") 71 | 72 | guard case let .sourceCode(fileType, source) = contents[2] else { 73 | XCTAssert(false) 74 | return 75 | } 76 | XCTAssertEqual(fileType.rawValue, "swift") 77 | XCTAssertEqual(source, sourceCode) 78 | 79 | guard case let .sourceCode(jsFileType, _) = contents[3] else { 80 | XCTAssert(false) 81 | return 82 | } 83 | XCTAssertEqual(jsFileType.rawValue, "javaScript") 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Truffaut/SyntaxHighlighter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyntaxHighlighter.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 29/08/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import TruffautSupport 11 | 12 | struct SyntaxHighlighter { 13 | 14 | private static let syntaxHighlightingQueue = DispatchQueue(label: "com.codezerker.truffaut.syntaxHighlighting", 15 | attributes: .concurrent) 16 | 17 | static func highlight(sourceCode: String, 18 | ofType type: Content.FileType, 19 | withFont font: NSFont, 20 | completion: @escaping (NSAttributedString?) -> Void) { 21 | syntaxHighlightingQueue.async { 22 | guard let scriptPath = Bundle.main.path(forResource: "syntax-hl", ofType: "rb") else { 23 | DispatchQueue.main.async { 24 | completion(nil) 25 | } 26 | return 27 | } 28 | do { 29 | var output = try Shell.call(command: Shell.Environment.ruby, arguments: [ 30 | scriptPath, 31 | sourceCode, 32 | type.rawValue, 33 | ]) 34 | 35 | guard !output.isEmpty else { 36 | DispatchQueue.main.async { 37 | completion(nil) 38 | } 39 | return 40 | } 41 | output.removeLast() // remove '\n' 42 | 43 | let result = self.attributedString(from: output, font: font) 44 | DispatchQueue.main.async { 45 | completion(result) 46 | } 47 | } catch { 48 | DispatchQueue.main.async { 49 | completion(nil) 50 | } 51 | } 52 | } 53 | } 54 | 55 | private static func attributedString(from htmlString: String, font: NSFont) -> NSAttributedString? { 56 | let renderableString = htmlString.replacingOccurrences(of: "\n", with: "
") 57 | .replacingOccurrences(of: " ", with: "  ") 58 | guard let htmlData = renderableString.data(using: .utf8), 59 | let attrString = NSMutableAttributedString(html: htmlData, options: [.characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else { 60 | return nil 61 | } 62 | let fullStringRange = NSRange(location: 0, length: attrString.length) 63 | attrString.addAttribute(.font, value: font, range: fullStringRange) 64 | attrString.enumerateAttribute(.foregroundColor, in: fullStringRange, options: []) { attr, range, _ in 65 | guard let foregroundColor = attr as? NSColor, 66 | foregroundColor.redComponent == 0, 67 | foregroundColor.greenComponent == 0, 68 | foregroundColor.blueComponent == 0, 69 | foregroundColor.alphaComponent == 1 else { 70 | return 71 | } 72 | attrString.addAttribute(.foregroundColor, value: TextColor.Display.source, range: range) 73 | } 74 | return attrString 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Truffaut/PresentationDocument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Document.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/14/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import TruffautSupport 11 | 12 | class PresentationDocument: NSDocument { 13 | 14 | enum ParsingError: Error { 15 | case InvalidData 16 | } 17 | 18 | var fileNameWithoutExtension: String? { 19 | return fileURL?.deletingPathExtension().lastPathComponent 20 | } 21 | 22 | var presentation: Presentation? 23 | 24 | override class var autosavesInPlace: Bool { 25 | return true 26 | } 27 | 28 | override func makeWindowControllers() { 29 | let windowController = MainWindowController.loadFromStoryboard() 30 | addWindowController(windowController) 31 | } 32 | 33 | override func data(ofType typeName: String) throws -> Data { 34 | throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil) 35 | } 36 | 37 | override func read(from url: URL, ofType typeName: String) throws { 38 | reload {} 39 | } 40 | 41 | func reload(completion: @escaping () -> Void) { 42 | guard let url = fileURL else { 43 | completion() 44 | return 45 | } 46 | 47 | ReadController.read(from: url) { presentation, error in 48 | guard let presentation = presentation else { 49 | let alert: NSAlert 50 | 51 | if let error = error as? ShellRuntimeError { 52 | alert = NSAlert() 53 | alert.messageText = "Failed to load presentation." 54 | switch error { 55 | case .brokenPipe: 56 | alert.informativeText = "Broken pipe." 57 | case .processError(let stderr): 58 | alert.informativeText = stderr ?? "Unkown shell error happened when parsing the manifest file." 59 | } 60 | } else if let error = error { 61 | alert = NSAlert(error: error) 62 | } else { 63 | alert = NSAlert() 64 | alert.messageText = "Failed to load presentation (unexpected error)." 65 | } 66 | 67 | alert.alertStyle = .critical 68 | alert.showsHelp = true 69 | alert.delegate = self 70 | 71 | if let lastWindow = self.windowControllers.last?.window { 72 | alert.beginSheetModal(for: lastWindow, completionHandler: nil) 73 | } else { 74 | alert.runModal() 75 | } 76 | 77 | return 78 | } 79 | 80 | self.presentation = presentation 81 | completion() 82 | } 83 | } 84 | } 85 | 86 | extension PresentationDocument: NSAlertDelegate { 87 | 88 | static let documentationURLString = "https://github.com/Codezerker/Truffaut/blob/master/Documentations/TruffautSupport-API-Reference.md" 89 | 90 | public func alertShowHelp(_ alert: NSAlert) -> Bool { 91 | if let helpURL = URL(string: PresentationDocument.documentationURLString) { 92 | NSWorkspace.shared.open(helpURL) 93 | } 94 | return true 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Truffaut.xcodeproj/xcshareddata/xcschemes/Truffaut.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Truffaut.xcodeproj/xcshareddata/xcschemes/TruffautSupport.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Examples/attr-string-in-swift/slides.swift: -------------------------------------------------------------------------------- 1 | let cfAttrStrDef = """ 2 | struct __CFAttributedString { 3 | CFRuntimeBase base; 4 | CFStringRef string; 5 | CFRunArrayRef attributeArray; 6 | }; 7 | """ 8 | 9 | let cfAttrStrClassDef = """ 10 | static const CFRuntimeClass __CFAttributedStringClass = { 11 | 0, 12 | "CFAttributedString", 13 | NULL, // init 14 | NULL, // copy 15 | __CFAttributedStringDeallocate, 16 | __CFAttributedStringEqual, // <----- THIS!! 17 | __CFAttributedStringHash, 18 | NULL, 19 | __CFAttributedStringCopyDescription 20 | }; 21 | """ 22 | 23 | let copyIsRetain = """ 24 | // NSAttributedString 25 | 26 | open override func copy() -> Any { 27 | return copy(with: nil) 28 | } 29 | 30 | open func copy(with zone: NSZone? = nil) -> Any { 31 | return self 32 | } 33 | 34 | // NSMutableAttributedString 35 | 36 | open override func copy(with zone: NSZone? = nil) -> Any { 37 | return NSAttributedString(attributedString: self) 38 | } 39 | """ 40 | 41 | import TruffautSupport 42 | 43 | let presentation = Presentation(pages: [ 44 | 45 | Page(title: "NSAttributedString.swift", 46 | subtitle: "A implementation story"), 47 | 48 | Page(title: "swift-corelibs-foundation", contents: [ 49 | .text("A Swift re-implementation of Foundation.framework"), 50 | .text("For platforms without Objective-C runtime"), 51 | .text("Interface matching DarwinFoundation"), 52 | .text("Implementation unrelated"), 53 | ]), 54 | 55 | Page(title: "CoreFoundation", contents: [ 56 | .text("C library, Foundation's \"core\""), 57 | .text("Provides common data types for all frameworks (Darwin)"), 58 | .text("Toll-free bridging"), 59 | .indent([ 60 | .text("Bridgable: NSString is-a CFStringRef"), 61 | .text("Non-bridgable: NSRunLoop has-a CFRunLoop"), 62 | ]), 63 | ]), 64 | 65 | Page(title: "CFAttributedString.c", contents: [ 66 | .text("CFAttributedString and CFMutableAttributedString are the same"), 67 | .sourceCode(.c, cfAttrStrDef), 68 | ]), 69 | 70 | Page(title: "_CFInfo", contents: [ 71 | .text("Essentially 64 bit of in-memory data"), 72 | .text("Type meta data of all CFTypes"), 73 | .indent([ 74 | .text("Is this CFTypeRef a __CFAttributedString?"), 75 | .text("Is this __CFAttributedString mutable?"), 76 | ]), 77 | ]), 78 | 79 | Page(title: "class NSAttributedString {}", contents: [ 80 | .text("It's a string"), 81 | .indent([ 82 | .sourceCode(.swift, "var _string: NSString"), 83 | ]), 84 | .text("It has attributes"), 85 | .indent([ 86 | .sourceCode(.swift, "var _attributes: CFRunArray"), 87 | .text("The \"Run\" is the same as in \"CTRunRef\""), 88 | ]), 89 | .text("It is-a CFAttributedStringRef"), 90 | .indent([ 91 | .sourceCode(.swift, "let _cfinfo: _CFInfo"), 92 | ]), 93 | ]), 94 | 95 | Page(title: "init(...)", contents: [ 96 | .sourceCode(.swift, "self = CFAttributedStringCreate(...)"), 97 | .sourceCode(.plainText, "~~~~ ^ error: cannot assign to value: 'self' is immutable"), 98 | .text("We need to re-implement CFAttributedStringCreate(...) in Swift!"), 99 | ]), 100 | 101 | Page(title: "func attribute(...)", contents: [ 102 | .sourceCode(.swift, "typealias NSRangePointer = UnsafeMutablePointer"), 103 | .text("Time for a pointer dance!"), 104 | ]), 105 | 106 | Page(title: "func enumerateAttributes(...)", contents: [ 107 | .text("A peek of DarwinFoundation"), 108 | .text("So excited!!"), 109 | .text("Remember the implementation!"), 110 | .text("Annnnnnd, the code is messy :troll:"), 111 | ]), 112 | 113 | Page(title: "class NSMutableAttributedString: NSAttributedString {}", contents: [ 114 | .sourceCode(.swift, "var mutableString: NSMutableString"), 115 | .text("It is-a CFMutableAttributedString"), 116 | ]), 117 | 118 | Page(title: "func isEqual(to:)", contents: [ 119 | .sourceCode(.c, cfAttrStrClassDef), 120 | ]), 121 | 122 | Page(title: "NSCopying", contents: [ 123 | .text("You say copy, I say retain."), 124 | .sourceCode(.swift, copyIsRetain), 125 | ]), 126 | 127 | Page(title: "What's NeXT", contents: [ 128 | .text("Make some swift packages to use NS[Mutable]AttributedString"), 129 | .sourceCode(.swift, "let string = \"hello\" as NSString"), 130 | .text("5 non-trivial pull requests -> commit access!") 131 | ]), 132 | 133 | Page(title: "NSAttributedString(string: \"Thanks!\")"), 134 | ]) 135 | -------------------------------------------------------------------------------- /Truffaut/SlidesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlidesViewController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 4/15/16. 6 | // Copyright © 2016 Codezerker. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import TruffautSupport 11 | 12 | class SlidesViewController: NSViewController { 13 | 14 | weak var windowController: NSWindowController? 15 | 16 | fileprivate var currentPage = 0 17 | fileprivate weak var currentPageViewController: PageViewController? 18 | 19 | fileprivate var presentation: Presentation? { 20 | return (windowController?.document as? PresentationDocument)?.presentation 21 | } 22 | 23 | fileprivate var fileName: String? { 24 | return (windowController?.document as? PresentationDocument)?.fileNameWithoutExtension 25 | } 26 | 27 | fileprivate var documentFolderURL: URL? { 28 | guard let document = windowController?.document, 29 | let documentURL = document.fileURL, 30 | let folderURL = documentURL?.deletingLastPathComponent() else { 31 | return nil 32 | } 33 | return folderURL 34 | } 35 | 36 | override func viewWillAppear() { 37 | super.viewWillAppear() 38 | reload(nil) 39 | } 40 | } 41 | 42 | extension SlidesViewController { 43 | 44 | @IBAction func showPreviousPage(_ sender: Any?) { 45 | show(pageAtIndex: currentPage - 1) 46 | } 47 | 48 | @IBAction func showNextPage(_ sender: Any?) { 49 | show(pageAtIndex: currentPage + 1) 50 | } 51 | 52 | @IBAction func reload(_ sender: Any?) { 53 | (windowController?.document as? PresentationDocument)?.reload { [weak self] in 54 | guard let validSelf = self else { 55 | return 56 | } 57 | validSelf.show(pageAtIndex: validSelf.currentPage) 58 | } 59 | } 60 | 61 | // FIXME: 62 | // because the document view for the page view is flipped, the PDF image is messed up. 63 | // Disable export for now. 64 | /* 65 | @IBAction func exportPresentationToPDF(_ sender: Any?) { 66 | guard let fileName = self.fileName else { 67 | return 68 | } 69 | let openPanel = NSSavePanel() 70 | openPanel.allowedFileTypes = [ExportController.ExportingType.pdf.fileExtension] 71 | openPanel.allowsOtherFileTypes = false 72 | openPanel.nameFieldStringValue = fileName 73 | openPanel.begin { result in 74 | guard result == NSApplication.ModalResponse.OK, 75 | let exportURL = openPanel.url else { 76 | return 77 | } 78 | let pdf = ExportController().exportToPDF(withDataSource: self) 79 | pdf.write(to: exportURL) 80 | } 81 | } 82 | */ 83 | } 84 | 85 | extension SlidesViewController: ExportControllerDataSource { 86 | 87 | func numberOfPagesToExport() -> Int { 88 | guard let pages = presentation?.pages else { 89 | return 0 90 | } 91 | return pages.count 92 | } 93 | 94 | private struct ExportLayout { 95 | static let width: CGFloat = 1280 96 | static let height: CGFloat = 720 97 | } 98 | 99 | func viewForPageToExport(atIndex index: Int) -> NSView? { 100 | guard let page = presentation?.pages[index], 101 | let documentFolderURL = documentFolderURL else { 102 | return nil 103 | } 104 | let pageViewController = PageViewController(page: page, imageBaseURL: documentFolderURL, isExporting: true) 105 | return pageViewController.view 106 | } 107 | } 108 | 109 | extension SlidesViewController { 110 | 111 | fileprivate func show(pageAtIndex index: Int) { 112 | guard let pages = presentation?.pages, 113 | let documentFolderURL = documentFolderURL, 114 | index >= 0 && index < pages.count else { 115 | return 116 | } 117 | 118 | currentPage = index 119 | 120 | let pageViewController = PageViewController(page: pages[index], imageBaseURL: documentFolderURL) 121 | pageViewController.view.translatesAutoresizingMaskIntoConstraints = false 122 | view.addSubview(pageViewController.view) 123 | addChild(pageViewController) 124 | 125 | NSLayoutConstraint.activate([ 126 | pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor), 127 | pageViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor), 128 | pageViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), 129 | pageViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor), 130 | ]) 131 | 132 | self.currentPageViewController?.removeFromParent() 133 | self.currentPageViewController?.view.removeFromSuperview() 134 | 135 | self.currentPage = index 136 | self.currentPageViewController = pageViewController 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Truffaut/PageViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Documentations/TruffautSupport-API-Reference.md: -------------------------------------------------------------------------------- 1 | # TruffautSupport 2 | 3 | Describe your presentation slides with Swift. 4 | 5 | ## Overview 6 | 7 | `TruffautSupport` contains data types for describing presentation slides. 8 | 9 | It provides the vocabulary for the manifest file. 10 | 11 | ## Data Types 12 | 13 | ### Presentation 14 | 15 | [View source](../TruffautSupport/Presentation.swift) 16 | 17 | The top-level data type that holds all necessary information about the slides. 18 | 19 | It is also a crucial part of the manifest file interpreting process, as its designated initializer is responsible for printing out its JSON representation so that the manifest can be passed from the `swiftc` interpreter to `Truffaut.app`. 20 | 21 | #### title 22 | 23 | - Reserved for future use. 24 | 25 | #### authors 26 | 27 | - Reserved for future use. 28 | 29 | #### pages 30 | 31 | - An array of `Page`s that needs to be presented in the slides. 32 | 33 | #### init(title: String = "", authors: [String] = [], pages: [Page]) 34 | 35 | - Designated initializer for `Presentation`. 36 | - `title`: optional, reserved for future use, default value is an empty string. 37 | - `authors`: optional, reserved for future use, default value is an empty array of strings. 38 | - `pages`: required, an array of `Page`s that needs to be presented in the slides. 39 | 40 | ### Page 41 | 42 | [View source](../TruffautSupport/Page.swift) 43 | 44 | A `Page` describes what should be displayed in a single slide. 45 | 46 | Currently there are two types of pages: **cover** and **normal**. 47 | 48 | - A **cover** page is a page that only displayes its `title` and `subtitle`, and all the contents are vertically and horizontally centered. 49 | - A **normal** page is a page that can display a `title` and a tree of `contents`. The title is aligned to the top-left corner. 50 | 51 | The detection of page types is implicit: **if `contents` is `nil`** the page will be recognized as a **cover**, otherwise it will be recognized as a `normal` page. 52 | 53 | #### title 54 | 55 | - The title of the page. 56 | 57 | #### subtitle 58 | 59 | - Subtitle of the page. 60 | - This value is ignored if the page is a **normal** page. 61 | 62 | #### contents 63 | 64 | - An array of `Content` to be displayed in the page. 65 | - If this value is `nil`, the page will be recognized as a **cover**, otherwise it will be recognized as a **normal** page. 66 | 67 | #### init(title: String? = nil, subtitle: String? = nil, contents: [Content]? = nil) 68 | 69 | - Designated initializer for `Page`. 70 | - `title`: optional, the title of the page, default value is `nil`. 71 | - `subtitle`: optional, the subtitle of the page, default value is `nil`. 72 | - `contents`: optional, an array of `Content`, default value is `nil`. 73 | 74 | ### Content 75 | 76 | [View source](../TruffautSupport/Content.swift) 77 | 78 | A `Content` describes an entry in the slide. 79 | 80 | It can be a text (`.text`), a monospaced text with optional syntax highlighting (`.sourceCode`), or an image (`.image`). 81 | 82 | To make it more flexible to layout the entries, it is also possible to wrap contents with indentations (`.indent`). All contents can be wrapped in a `.indent`, including the `.indent` itself. 83 | 84 | #### Content.title(String) 85 | 86 | - Reserved for future use. 87 | 88 | #### Content.text(String) 89 | 90 | - A text entry rendered with system font. 91 | - The associated `String` will be used to render the text entry. 92 | 93 | #### Content.image(String) 94 | 95 | - A image entry. 96 | - The associated `String` will be used as the image's relative file path based on the manifest file's path. 97 | 98 | #### Content.sourceCode(FileType, String) 99 | 100 | - A text entry rendered with monospaced font. 101 | - The first associated `FileType` value will be used to determine the syntax highlighting type. 102 | - The second associated `String` will be used to render the text entry. 103 | 104 | #### Content.indent([Content]) 105 | 106 | - An indent entry to make its content visually indented by one level. 107 | - The associated `Content` array will be indented. 108 | 109 | #### Content.FileType 110 | 111 | A `Content.FileType` will be mapped to [**rouge**](https://github.com/jneen/rouge)'s syntax highlighting lexers. 112 | 113 | ##### FileType.plainText 114 | 115 | - The content is plain text, no lexer will be applied (no syntax highlighting). 116 | 117 | ##### FileType.c 118 | 119 | - The content is C source code, `Rouge::Lexers::C` will be applied. 120 | 121 | ##### FileType.cpp 122 | 123 | - The content is C++ source code, `Rouge::Lexers::Cpp` will be applied. 124 | 125 | ##### FileType.javaScript 126 | 127 | - The content is JavaScript source code, `Rouge::Lexers::Javascript` will be applied. 128 | 129 | ##### FileType.objc 130 | 131 | - The content is Objective-C source code, `Rouge::Lexers::ObjectiveC` will be applied. 132 | 133 | ##### FileType.rust 134 | 135 | - The content is Rust source code, `Rouge::Lexers::Rust` will be applied. 136 | 137 | ##### FileType.shell 138 | 139 | - The content is shell script, `Rouge::Lexers::Shell` will be applied. 140 | 141 | ##### FileType.swift 142 | 143 | - The content is Swift source code, `Rouge::Lexers::Swift` will be applied. 144 | -------------------------------------------------------------------------------- /Truffaut/PageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.swift 3 | // Truffaut 4 | // 5 | // Created by Yan Li on 17/08/17. 6 | // Copyright © 2017 Codezerker. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import TruffautSupport 11 | 12 | class PageViewController: NSViewController { 13 | 14 | @IBOutlet private weak var visualEffectView: NSVisualEffectView! 15 | @IBOutlet fileprivate weak var scrollView: NSScrollView! 16 | 17 | let page: Page 18 | let imageBaseURL: URL 19 | let isExporting: Bool 20 | 21 | init(page: Page, imageBaseURL: URL, isExporting: Bool = false) { 22 | self.page = page 23 | self.imageBaseURL = imageBaseURL 24 | self.isExporting = isExporting 25 | super.init(nibName: nil, bundle: nil) 26 | } 27 | 28 | required init?(coder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | override func viewWillLayout() { 33 | super.viewWillLayout() 34 | DynamicLayout.currentScreenSize = view.bounds.size 35 | setUpViews() 36 | } 37 | } 38 | 39 | fileprivate class FlippedView: NSView { 40 | 41 | override var isFlipped: Bool { 42 | return true 43 | } 44 | } 45 | 46 | fileprivate extension PageViewController { 47 | 48 | private struct LayoutConstants { 49 | 50 | static var pageMargin: CGFloat { 51 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 42) 52 | } 53 | 54 | static var spacing: CGFloat { 55 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 16) 56 | } 57 | 58 | static var indentOffset: CGFloat { 59 | return DynamicLayout.sizeFittingCurrentScreenSize(originalSize: 32) 60 | } 61 | } 62 | 63 | private func setUpViews() { 64 | scrollView.documentView = nil 65 | 66 | let contentStackView = NSStackView(frame: .zero) 67 | contentStackView.translatesAutoresizingMaskIntoConstraints = false 68 | contentStackView.orientation = .vertical 69 | contentStackView.spacing = LayoutConstants.spacing 70 | contentStackView.edgeInsets = NSEdgeInsets(top: LayoutConstants.pageMargin, 71 | left: LayoutConstants.pageMargin, 72 | bottom: LayoutConstants.pageMargin, 73 | right: LayoutConstants.pageMargin) 74 | 75 | if page.contents != nil { 76 | layoutPage(in: contentStackView) 77 | } else { 78 | layoutPageAsCover(in: contentStackView) 79 | } 80 | 81 | let documentView = FlippedView(frame: view.bounds) 82 | documentView.translatesAutoresizingMaskIntoConstraints = false 83 | documentView.addSubview(contentStackView) 84 | NSLayoutConstraint.activate([ 85 | documentView.widthAnchor.constraint(greaterThanOrEqualToConstant: view.bounds.width), 86 | documentView.heightAnchor.constraint(greaterThanOrEqualToConstant: view.bounds.height), 87 | 88 | contentStackView.topAnchor.constraint(equalTo: documentView.topAnchor), 89 | contentStackView.leftAnchor.constraint(equalTo: documentView.leftAnchor), 90 | contentStackView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor), 91 | contentStackView.rightAnchor.constraint(equalTo: documentView.rightAnchor), 92 | ]) 93 | 94 | scrollView.documentView = documentView 95 | } 96 | 97 | private func layoutPageAsCover(in contentStackView: NSStackView) { 98 | contentStackView.alignment = .centerX 99 | contentStackView.distribution = .gravityAreas 100 | 101 | let titleLabel = NSTextField(wrappingLabelWithString: page.title ?? "") 102 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 103 | titleLabel.font = Font.Cover.title 104 | titleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.title 105 | contentStackView.addView(titleLabel, in: .center) 106 | 107 | let subtitleLabel = NSTextField(wrappingLabelWithString: page.subtitle ?? "") 108 | subtitleLabel.translatesAutoresizingMaskIntoConstraints = false 109 | subtitleLabel.font = Font.Cover.subtitle 110 | subtitleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.subtitle 111 | contentStackView.addView(subtitleLabel, in: .center) 112 | } 113 | 114 | private func layoutPage(in contentStackView: NSStackView) { 115 | guard let contents = page.contents else { 116 | return 117 | } 118 | 119 | let pageGravity: NSStackView.Gravity 120 | if let title = page.title { 121 | contentStackView.alignment = .leading 122 | contentStackView.distribution = .gravityAreas 123 | 124 | pageGravity = .top 125 | 126 | let titleLabel = NSTextField(wrappingLabelWithString: page.title ?? "") 127 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 128 | titleLabel.font = Font.Page.title 129 | titleLabel.textColor = isExporting ? TextColor.Export.title : TextColor.Display.title 130 | contentStackView.addView(titleLabel, in: pageGravity) 131 | } else { 132 | contentStackView.alignment = .centerX 133 | contentStackView.distribution = .gravityAreas 134 | 135 | pageGravity = .center 136 | } 137 | 138 | // FIXME: layout subtitle 139 | 140 | func addContent(content: Content, to stackView: NSStackView, isLast: Bool) { 141 | switch content { 142 | case .indent(let nestedContents): 143 | let indentStackView = NSStackView(views: []) 144 | indentStackView.translatesAutoresizingMaskIntoConstraints = false 145 | indentStackView.orientation = .vertical 146 | indentStackView.alignment = .leading 147 | indentStackView.distribution = .gravityAreas 148 | indentStackView.edgeInsets = NSEdgeInsets(top: 0, left: LayoutConstants.indentOffset, bottom: 0, right: 0) 149 | for nestedContent in nestedContents { 150 | addContent(content: nestedContent, to: indentStackView, isLast: false) 151 | } 152 | stackView.addView(indentStackView, in: pageGravity) 153 | case .text(let text): 154 | let displayText = text.replacingOccurrences(of: "->", with: " ➞ ") 155 | let label = NSTextField(wrappingLabelWithString: displayText) 156 | label.translatesAutoresizingMaskIntoConstraints = false 157 | label.font = Font.Page.text 158 | label.textColor = isExporting ? TextColor.Export.text : TextColor.Display.text 159 | stackView.addView(label, in: pageGravity) 160 | case .sourceCode(let fileType, let source): 161 | let label: NSTextField 162 | switch fileType { 163 | case .plainText: 164 | label = NSTextField(labelWithString: source) 165 | default: 166 | label = NSTextField(labelWithAttributedString: NSAttributedString(string: source)) 167 | SyntaxHighlighter.highlight(sourceCode: source, 168 | ofType: fileType, 169 | withFont: Font.Page.source) { result in 170 | guard let result = result else { 171 | return 172 | } 173 | label.attributedStringValue = result 174 | } 175 | } 176 | label.translatesAutoresizingMaskIntoConstraints = false 177 | label.font = Font.Page.source 178 | label.textColor = isExporting ? TextColor.Export.source : TextColor.Display.source 179 | stackView.addView(label, in: pageGravity) 180 | case .image(let relativePath): 181 | let imageURL = imageBaseURL.appendingPathComponent(relativePath, isDirectory: false) 182 | guard let image = NSImage(contentsOf: imageURL) else { 183 | break 184 | } 185 | let imageView = NSImageView(image: image) 186 | imageView.translatesAutoresizingMaskIntoConstraints = false 187 | imageView.imageScaling = .scaleProportionallyUpOrDown 188 | stackView.addView(imageView, in: pageGravity) 189 | default: 190 | break 191 | } 192 | 193 | if isLast { 194 | // workaround: insert a vertically growable dummy view 195 | // this will make sure there is no ambiguity in vertical layout 196 | let bottomView = NSView() 197 | bottomView.translatesAutoresizingMaskIntoConstraints = false 198 | bottomView.setContentHuggingPriority(.defaultLow, for: .vertical) 199 | stackView.addView(bottomView, in: .bottom) 200 | } 201 | } 202 | 203 | for (index, content) in contents.enumerated() { 204 | addContent(content: content, to: contentStackView, isLast: index + 1 == contents.count) 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Examples/unicode/slides.swift: -------------------------------------------------------------------------------- 1 | import TruffautSupport 2 | 3 | let presentation = Presentation(pages: [ 4 | 5 | // intro 6 | 7 | Page(title: "Unicode and You", subtitle: "try? explain(Unicode.self)"), 8 | 9 | Page(title: "Why does this function throw?", contents: [ 10 | .image("images/the-unicode-standard.png"), 11 | .text("1,044 pages"), 12 | ]), 13 | 14 | Page(title: "Topics", contents: [ 15 | .text("The Unicode Standard"), 16 | .text("Cocoa and Unicode"), 17 | .text("Books and Apps"), 18 | ]), 19 | 20 | // Unicode Standard 21 | 22 | Page(title: "The Unicode Standard", subtitle: "A Whirlwind Tour"), 23 | 24 | Page(title: "What is Unicode?", contents: [ 25 | .text("A way to represent characters"), 26 | .text("Multilingual"), 27 | .text("Character composition"), 28 | .indent([ 29 | .sourceCode(.plainText, "e(U+0065) + ´(U+00B4) = é(U+00E9)"), 30 | ]), 31 | .text("A super set of ASCII"), 32 | ]), 33 | 34 | Page(title: "Character number and Character name", contents: [ 35 | .text("Every character in Unicode (called a code point) has two unique identifiers:"), 36 | .indent([ 37 | .text("Character number"), 38 | .text("Character name"), 39 | ]), 40 | .text("Example:"), 41 | .indent([ 42 | .sourceCode(.plainText, " Character : é"), 43 | .sourceCode(.plainText, "Character number : U+00E9 (233 in decimal)"), 44 | .sourceCode(.plainText, " Character name : LATIN SMALL LETTER E WITH ACUTE"), 45 | ]), 46 | ]), 47 | 48 | Page(title: "Ranges of Character number", contents: [ 49 | .text("Coding space"), 50 | .indent([ 51 | .sourceCode(.plainText, "U+0000..U+10FFFF"), 52 | .text("1,114,111 in total"), 53 | ]), 54 | .text("Coding space is divided into 17 planes, each has 0xFFFF(65535) of space"), 55 | .indent([ 56 | .sourceCode(.plainText, "U+0000..U+FFFF"), 57 | .text("Basic Multilingual Plane (BMP): most used characters"), 58 | .text(""), 59 | .sourceCode(.plainText, "U+10000..U+1FFFF"), 60 | .text("Supplementary Multilingual Plane (SMP): special symbols"), 61 | .text(""), 62 | .sourceCode(.plainText, "U+20000..U+2FFFF"), 63 | .text("Supplementary Ideographic Plane (SIP): less used CJK characters"), 64 | .text("CJK: Chinese-Japanese-Korean"), 65 | .text(""), 66 | .sourceCode(.plainText, "U+30000..U+DFFFF"), 67 | .text("Unassigned, reserved for future use"), 68 | .text(""), 69 | .sourceCode(.plainText, "U+E0000..U+EFFFF"), 70 | .text("Supplementary Special-Purpose Plane (SSPP): reserved for internal use"), 71 | .text(""), 72 | .sourceCode(.plainText, "U+F0000..U+10FFFF"), 73 | .text("Designed for custom private use"), 74 | .text(""), 75 | ]), 76 | ]), 77 | 78 | Page(title: "UnicodeData.txt", contents: [ 79 | .text("The Unicode database basic file"), 80 | .text("Example:"), 81 | .indent([ 82 | .sourceCode(.plainText, "00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;...;00D6;;00D6"), 83 | .sourceCode(.plainText, " 00F6 - Character number"), 84 | .sourceCode(.plainText, "LATIN...DIAERESIS - Character name"), 85 | .sourceCode(.plainText, " Ll - Letter, lowercase"), 86 | .sourceCode(.plainText, " 006F 0308 - Canonical decomposition to o(U+006F) + ̈(U+0308)"), 87 | .sourceCode(.plainText, " 00D6 - Simple case mapping, uppercase and title case both map to Ö(U+00D6)"), 88 | ]), 89 | ]), 90 | 91 | Page(title: "Word boundaries and Line-breaks", contents: [ 92 | .text("Also defined in the Unicode database, e.g.:"), 93 | .indent([ 94 | .sourceCode(.plainText, "GraphemeBreakProperty.txt"), 95 | .sourceCode(.plainText, "WordBreakProperty.txt"), 96 | .sourceCode(.plainText, "SentenceBreakProperty.txt"), 97 | .sourceCode(.plainText, "LineBreak.txt"), 98 | ]), 99 | ]), 100 | 101 | Page(title: "Grapheme cluster", contents: [ 102 | .text("Multiple Unicode code points act as one character"), 103 | .text("Example:"), 104 | .indent([ 105 | .sourceCode(.plainText, "é = e´"), 106 | .sourceCode(.plainText, "Uppercase ß => SS"), 107 | .sourceCode(.plainText, "🇳🇿 => 🇳 🇿"), 108 | .indent([ 109 | .sourceCode(.plainText, "U+1F1F3 REGIONAL INDICATOR SYMBOL LETTER N"), 110 | .sourceCode(.plainText, "U+1F1FF REGIONAL INDICATOR SYMBOL LETTER Z"), 111 | ]), 112 | .sourceCode(.plainText, "👨‍👩‍👧‍👦 => 👨U+200D👩U+200D👧U+200D👦"), 113 | .indent([ 114 | .sourceCode(.plainText, "U+200D ZERO WIDTH JOINER"), 115 | ]), 116 | ]), 117 | ]), 118 | 119 | Page(title: "Unicode encodings: UTF-32", contents: [ 120 | .text("32-bit -> 4 bytes"), 121 | .text("Unicode number is 21-bit"), 122 | .indent([ 123 | .text("First 7-bit is plane number"), 124 | .text("The following 16-bit is the location in the plane"), 125 | ]), 126 | .text("32-bit fits"), 127 | .indent([ 128 | .text("+ Fast encoding and decoding (direct mapping)"), 129 | .text("+ Random access is O(1)"), 130 | .text("+ Robust"), 131 | .text("- Waste of space"), 132 | ]), 133 | ]), 134 | 135 | Page(title: "Unicode encodings: UTF-16", contents: [ 136 | .text("BMP (U+0000..U+FFFF): 16-bit -> 2 bytes"), 137 | .text("Outside BMP (U+1FFFF..U+10FFFF): a pair of 16-bit -> 2 * 2 bytes"), 138 | .text("Surrogate pair:"), 139 | .indent([ 140 | .sourceCode(.plainText, "𝐅 U+1D405 MATHEMATICAL BOLD CAPITAL F"), 141 | .sourceCode(.plainText, "000011101010000000101 // U+1D405 in binary"), 142 | .sourceCode(.plainText, "u1 = 00001 | u2 = 110101 | u3 = 0000000101"), 143 | .sourceCode(.plainText, "u1 -= 1"), 144 | .sourceCode(.plainText, "110110u1u2 = 1101100000110101 // U+D835, high surrogate"), 145 | .sourceCode(.plainText, " 110111u3 = 1101110000000101 // U+DC05, low surrogate"), 146 | ]), 147 | .text("+ BMP encoding and decoding is fast (direct mapping)"), 148 | .text("+ Robust, high surrogate must always be followed by low surrogate"), 149 | .text("- Random access is O(n)"), 150 | ]), 151 | 152 | Page(title: "Unicode encodings: UTF-8", contents: [ 153 | .text("Basic Latin (ASCII) (U+0000..U+007F): 8-bit -> 1 byte"), 154 | .text("Outside ASCII: up to 32-bit -> up to 4 bytes"), 155 | .indent([ 156 | .sourceCode(.plainText, " Code number in binary | Octec 1 Octec 2 Octec 3 Octec 4"), 157 | .sourceCode(.plainText, " 00000000 0xxxxxxx | 0xxxxxxx"), 158 | .sourceCode(.plainText, " 00000yyy yyxxxxxx | 110yyyyy 10xxxxxx"), 159 | .sourceCode(.plainText, " zzzzyyyy yyxxxxxx | 1110zzzz 10yyyyyy 10xxxxxx"), 160 | .sourceCode(.plainText, "uuuww zzzzyyyy yyxxxxxx | 11110uuu 10wwzzzz 10yyyyyy 10xxxxxx"), 161 | ]), 162 | .text("+ ASCII encoding and decoding is fast (direct mapping) and efficient (1 byte)"), 163 | .text("+ Robust, bit pattern enforces completeness"), 164 | .text("- Random access is O(n)"), 165 | ]), 166 | 167 | Page(title: "URL Encoding, Base64 and Punycode", contents: [ 168 | .text("Emerge because of the World Wide Web"), 169 | .text("Punycode"), 170 | .indent([ 171 | .sourceCode(.plainText, "http://👴🏻👀.ws/ -> http://xn--mn8hkeyf.ws/"), 172 | ]), 173 | .text("RFCs"), 174 | .indent([ 175 | .text("URI: RFC 3986"), 176 | .text("Base64: RFC 3548"), 177 | .text("Punycode: RFC 3492"), 178 | ]), 179 | ]), 180 | 181 | // Cocoa and Unicode 182 | 183 | Page(title: "Cocoa and Unicode", subtitle: "CFStringRef -> NSString -> Swift.String"), 184 | 185 | Page(title: "CFStringRef and NSString", contents: [ 186 | .text("String manipulation APIs are based on UTF-16"), 187 | .text("Not fully compliant to Unicode"), 188 | .text("Use with care!!"), 189 | .indent([ 190 | .sourceCode(.plainText, "import Foundation"), 191 | .sourceCode(.plainText, "let string = \"👴🏻\" as NSString"), 192 | .sourceCode(.plainText, "string.length // => 4"), 193 | ]), 194 | .text("Cocoa was initially implemented in the late 1990s, UTF-16 was popular by then!"), 195 | ]), 196 | 197 | Page(title: "Swift.String", contents: [ 198 | .text("Fully compliant to Unicode"), 199 | .text("Provide APIs to manipulate Unicode scalars"), 200 | .indent([ 201 | .sourceCode(.plainText, "let string = \"👴🏻\""), 202 | .sourceCode(.plainText, "string.count // => 1"), 203 | .sourceCode(.plainText, "string.unicodeScalars // => \u{0001F474} \u{0001F3FB}"), 204 | .sourceCode(.plainText, "// 👴 U+1F474 OLDER MAN"), 205 | .sourceCode(.plainText, "// 🏻 U+1F3FB EMOJI MODIFIER FITZPATRICK TYPE-1-2"), 206 | ]), 207 | ]), 208 | 209 | // Books and Apps 210 | 211 | Page(title: "Books and Apps", subtitle: "📖 🔨"), 212 | 213 | Page(contents: [ 214 | .image("images/unicode-explained.jpg"), 215 | .text("Chapter 4, 5, 6"), 216 | ]), 217 | 218 | Page(contents: [ 219 | .image("images/unicode-checker.png"), 220 | .text("UnicodeChecker by earthlingsoft"), 221 | ]), 222 | 223 | Page(title: "Thank you!", subtitle: "@eyeplum"), 224 | ]) 225 | -------------------------------------------------------------------------------- /Truffaut.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0B0712891F557C290074EC7A /* SyntaxHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */; }; 11 | 0B07128B1F557D730074EC7A /* syntax-hl.rb in Resources */ = {isa = PBXBuildFile; fileRef = 0B07128A1F557D730074EC7A /* syntax-hl.rb */; }; 12 | 0B14BCDE1E92360700EB0C6A /* TruffautSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 0B14BCE11E92360700EB0C6A /* TruffautSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; }; 14 | 0B14BCE21E92360700EB0C6A /* TruffautSupport.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 0B14BCE81E923A1200EB0C6A /* Presentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCE71E923A1200EB0C6A /* Presentation.swift */; }; 16 | 0B14BCEA1E923A2100EB0C6A /* Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCE91E923A2100EB0C6A /* Page.swift */; }; 17 | 0B14BCEC1E923A6F00EB0C6A /* Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BCEB1E923A6F00EB0C6A /* Content.swift */; }; 18 | 0B14BD041E92499300EB0C6A /* TruffautSupportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */; }; 19 | 0B14BD061E92499300EB0C6A /* TruffautSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; }; 20 | 0B14BD131E9398FE00EB0C6A /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B14BD121E9398FE00EB0C6A /* Shell.swift */; }; 21 | 0B62141A1F45978A00B66C9B /* PageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6214181F45978A00B66C9B /* PageViewController.swift */; }; 22 | 0B62141B1F45978A00B66C9B /* PageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0B6214191F45978A00B66C9B /* PageViewController.xib */; }; 23 | 0B62141D1F45A39000B66C9B /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B62141C1F45A39000B66C9B /* Font.swift */; }; 24 | 0B7C8BDE1CBFD166000FE312 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */; }; 25 | 0B7C8BE01CBFD166000FE312 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */; }; 26 | 0B7C8BE21CBFD166000FE312 /* PresentationDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */; }; 27 | 0B7C8BE41CBFD166000FE312 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */; }; 28 | 0B7C8BE71CBFD166000FE312 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B7C8BE51CBFD166000FE312 /* Main.storyboard */; }; 29 | 0B7C8BF91CC13E14000FE312 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */; }; 30 | 0B7C8BFE1CC14006000FE312 /* SlidesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */; }; 31 | 0B7C8C001CC14016000FE312 /* SlidesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */; }; 32 | 0B7C8C021CC1422B000FE312 /* StoryboardLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */; }; 33 | 0B7FADEB1D43472300000884 /* ExportController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7FADEA1D43472300000884 /* ExportController.swift */; }; 34 | 0BAAD2341FF5DF270099C191 /* PreferenceWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */; }; 35 | 0BAAD2371FF5E49F0099C191 /* NSWindow+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */; }; 36 | 0BAAD2391FF5E62E0099C191 /* PreferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */; }; 37 | 0BAAD23B1FF5F34B0099C191 /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */; }; 38 | 0BAB64AF1EA08D7C001CE96A /* ReadController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | 0B14BCDF1E92360700EB0C6A /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 0B7C8BD21CBFD166000FE312 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 0B14BCD91E92360700EB0C6A; 47 | remoteInfo = TruffautSupport; 48 | }; 49 | 0B14BD071E92499300EB0C6A /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 0B7C8BD21CBFD166000FE312 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = 0B14BCD91E92360700EB0C6A; 54 | remoteInfo = TruffautSupport; 55 | }; 56 | /* End PBXContainerItemProxy section */ 57 | 58 | /* Begin PBXCopyFilesBuildPhase section */ 59 | 0B7C8BF71CC139FD000FE312 /* Copy Frameworks */ = { 60 | isa = PBXCopyFilesBuildPhase; 61 | buildActionMask = 2147483647; 62 | dstPath = ""; 63 | dstSubfolderSpec = 10; 64 | files = ( 65 | 0B14BCE21E92360700EB0C6A /* TruffautSupport.framework in Copy Frameworks */, 66 | ); 67 | name = "Copy Frameworks"; 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXCopyFilesBuildPhase section */ 71 | 72 | /* Begin PBXFileReference section */ 73 | 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyntaxHighlighter.swift; sourceTree = ""; }; 74 | 0B07128A1F557D730074EC7A /* syntax-hl.rb */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text.script.ruby; path = "syntax-hl.rb"; sourceTree = ""; tabWidth = 2; }; 75 | 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TruffautSupport.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TruffautSupport.h; sourceTree = ""; }; 77 | 0B14BCDD1E92360700EB0C6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | 0B14BCE71E923A1200EB0C6A /* Presentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentation.swift; sourceTree = ""; }; 79 | 0B14BCE91E923A2100EB0C6A /* Page.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Page.swift; sourceTree = ""; }; 80 | 0B14BCEB1E923A6F00EB0C6A /* Content.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Content.swift; sourceTree = ""; }; 81 | 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TruffautSupportTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TruffautSupportTests.swift; sourceTree = ""; }; 83 | 0B14BD051E92499300EB0C6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84 | 0B14BD121E9398FE00EB0C6A /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; 85 | 0B6214181F45978A00B66C9B /* PageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageViewController.swift; sourceTree = ""; }; 86 | 0B6214191F45978A00B66C9B /* PageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PageViewController.xib; sourceTree = ""; }; 87 | 0B62141C1F45A39000B66C9B /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; 88 | 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Truffaut.app; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 90 | 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 91 | 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationDocument.swift; sourceTree = ""; }; 92 | 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 93 | 0B7C8BE61CBFD166000FE312 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 94 | 0B7C8BE81CBFD166000FE312 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 95 | 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; }; 96 | 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlidesWindowController.swift; sourceTree = ""; }; 97 | 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlidesViewController.swift; sourceTree = ""; }; 98 | 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardLoading.swift; sourceTree = ""; }; 99 | 0B7C8C0B1CC20865000FE312 /* Truffaut-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Truffaut-Bridging-Header.h"; sourceTree = ""; }; 100 | 0B7FADEA1D43472300000884 /* ExportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExportController.swift; sourceTree = ""; }; 101 | 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceWindowController.swift; sourceTree = ""; }; 102 | 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Style.swift"; sourceTree = ""; }; 103 | 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceViewController.swift; sourceTree = ""; }; 104 | 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; }; 105 | 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadController.swift; sourceTree = ""; }; 106 | /* End PBXFileReference section */ 107 | 108 | /* Begin PBXFrameworksBuildPhase section */ 109 | 0B14BCD61E92360700EB0C6A /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | 0B14BCFE1E92499300EB0C6A /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | 0B14BD061E92499300EB0C6A /* TruffautSupport.framework in Frameworks */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | 0B7C8BD71CBFD166000FE312 /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | 0B14BCE11E92360700EB0C6A /* TruffautSupport.framework in Frameworks */, 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXFrameworksBuildPhase section */ 133 | 134 | /* Begin PBXGroup section */ 135 | 0B14BCDB1E92360700EB0C6A /* TruffautSupport */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 0B14BCDC1E92360700EB0C6A /* TruffautSupport.h */, 139 | 0B14BCE71E923A1200EB0C6A /* Presentation.swift */, 140 | 0B14BCE91E923A2100EB0C6A /* Page.swift */, 141 | 0B14BCEB1E923A6F00EB0C6A /* Content.swift */, 142 | 0B14BCDD1E92360700EB0C6A /* Info.plist */, 143 | ); 144 | path = TruffautSupport; 145 | sourceTree = ""; 146 | }; 147 | 0B14BD021E92499300EB0C6A /* TruffautSupportTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 0B14BD031E92499300EB0C6A /* TruffautSupportTests.swift */, 151 | 0B14BD051E92499300EB0C6A /* Info.plist */, 152 | ); 153 | path = TruffautSupportTests; 154 | sourceTree = ""; 155 | }; 156 | 0B6EED841CC26C83001BC00B /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 0B07128A1F557D730074EC7A /* syntax-hl.rb */, 160 | 0B7C8C0B1CC20865000FE312 /* Truffaut-Bridging-Header.h */, 161 | 0B7C8BE31CBFD166000FE312 /* Assets.xcassets */, 162 | 0B7C8BE81CBFD166000FE312 /* Info.plist */, 163 | ); 164 | name = "Supporting Files"; 165 | sourceTree = ""; 166 | }; 167 | 0B7C8BD11CBFD166000FE312 = { 168 | isa = PBXGroup; 169 | children = ( 170 | 0B7C8BDC1CBFD166000FE312 /* Truffaut */, 171 | 0B14BCDB1E92360700EB0C6A /* TruffautSupport */, 172 | 0B14BD021E92499300EB0C6A /* TruffautSupportTests */, 173 | 0B7C8BDB1CBFD166000FE312 /* Products */, 174 | ); 175 | sourceTree = ""; 176 | }; 177 | 0B7C8BDB1CBFD166000FE312 /* Products */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */, 181 | 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */, 182 | 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */, 183 | ); 184 | name = Products; 185 | sourceTree = ""; 186 | }; 187 | 0B7C8BDC1CBFD166000FE312 /* Truffaut */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 0B7C8BDD1CBFD166000FE312 /* AppDelegate.swift */, 191 | 0BAB64B21EA0C200001CE96A /* Interface */, 192 | 0BAB64B11EA0C1F0001CE96A /* Utilities */, 193 | 0B7C8BFB1CC13F5D000FE312 /* Document */, 194 | 0B7C8BFA1CC13F4A000FE312 /* Main Window */, 195 | 0B7C8BFC1CC13F91000FE312 /* Slides Window */, 196 | 0BAAD2311FF5DE440099C191 /* Preference Window */, 197 | 0B6EED841CC26C83001BC00B /* Supporting Files */, 198 | ); 199 | path = Truffaut; 200 | sourceTree = ""; 201 | }; 202 | 0B7C8BFA1CC13F4A000FE312 /* Main Window */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 0B7C8BF81CC13E14000FE312 /* MainWindowController.swift */, 206 | 0B7C8BDF1CBFD166000FE312 /* MainViewController.swift */, 207 | ); 208 | name = "Main Window"; 209 | sourceTree = ""; 210 | }; 211 | 0B7C8BFB1CC13F5D000FE312 /* Document */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 0B7C8BE11CBFD166000FE312 /* PresentationDocument.swift */, 215 | ); 216 | name = Document; 217 | sourceTree = ""; 218 | }; 219 | 0B7C8BFC1CC13F91000FE312 /* Slides Window */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 0B7C8BFD1CC14006000FE312 /* SlidesWindowController.swift */, 223 | 0B7C8BFF1CC14016000FE312 /* SlidesViewController.swift */, 224 | 0B6214181F45978A00B66C9B /* PageViewController.swift */, 225 | 0B6214191F45978A00B66C9B /* PageViewController.xib */, 226 | ); 227 | name = "Slides Window"; 228 | sourceTree = ""; 229 | }; 230 | 0BAAD2311FF5DE440099C191 /* Preference Window */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 0BAAD2321FF5DF270099C191 /* PreferenceWindowController.swift */, 234 | 0BAAD2381FF5E62E0099C191 /* PreferenceViewController.swift */, 235 | ); 236 | name = "Preference Window"; 237 | sourceTree = ""; 238 | }; 239 | 0BAB64B11EA0C1F0001CE96A /* Utilities */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 0B62141C1F45A39000B66C9B /* Font.swift */, 243 | 0B7C8C011CC1422B000FE312 /* StoryboardLoading.swift */, 244 | 0BAB64AE1EA08D7C001CE96A /* ReadController.swift */, 245 | 0B0712881F557C290074EC7A /* SyntaxHighlighter.swift */, 246 | 0B14BD121E9398FE00EB0C6A /* Shell.swift */, 247 | 0B7FADEA1D43472300000884 /* ExportController.swift */, 248 | 0BAAD2361FF5E49F0099C191 /* NSWindow+Style.swift */, 249 | 0BAAD23A1FF5F34B0099C191 /* UserPreference.swift */, 250 | ); 251 | name = Utilities; 252 | sourceTree = ""; 253 | }; 254 | 0BAB64B21EA0C200001CE96A /* Interface */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | 0B7C8BE51CBFD166000FE312 /* Main.storyboard */, 258 | ); 259 | name = Interface; 260 | sourceTree = ""; 261 | }; 262 | /* End PBXGroup section */ 263 | 264 | /* Begin PBXHeadersBuildPhase section */ 265 | 0B14BCD71E92360700EB0C6A /* Headers */ = { 266 | isa = PBXHeadersBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | 0B14BCDE1E92360700EB0C6A /* TruffautSupport.h in Headers */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | /* End PBXHeadersBuildPhase section */ 274 | 275 | /* Begin PBXNativeTarget section */ 276 | 0B14BCD91E92360700EB0C6A /* TruffautSupport */ = { 277 | isa = PBXNativeTarget; 278 | buildConfigurationList = 0B14BCE31E92360700EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupport" */; 279 | buildPhases = ( 280 | 0B14BCD51E92360700EB0C6A /* Sources */, 281 | 0B14BCD61E92360700EB0C6A /* Frameworks */, 282 | 0B14BCD71E92360700EB0C6A /* Headers */, 283 | 0B14BCD81E92360700EB0C6A /* Resources */, 284 | 0B14BD0F1E9380D100EB0C6A /* Create libTruffautSupport.dylib and TruffautSupport.swiftmodule */, 285 | ); 286 | buildRules = ( 287 | ); 288 | dependencies = ( 289 | ); 290 | name = TruffautSupport; 291 | productName = TruffautSupport; 292 | productReference = 0B14BCDA1E92360700EB0C6A /* TruffautSupport.framework */; 293 | productType = "com.apple.product-type.framework"; 294 | }; 295 | 0B14BD001E92499300EB0C6A /* TruffautSupportTests */ = { 296 | isa = PBXNativeTarget; 297 | buildConfigurationList = 0B14BD091E92499300EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupportTests" */; 298 | buildPhases = ( 299 | 0B14BCFD1E92499300EB0C6A /* Sources */, 300 | 0B14BCFE1E92499300EB0C6A /* Frameworks */, 301 | 0B14BCFF1E92499300EB0C6A /* Resources */, 302 | ); 303 | buildRules = ( 304 | ); 305 | dependencies = ( 306 | 0B14BD081E92499300EB0C6A /* PBXTargetDependency */, 307 | ); 308 | name = TruffautSupportTests; 309 | productName = TruffautSupportTests; 310 | productReference = 0B14BD011E92499300EB0C6A /* TruffautSupportTests.xctest */; 311 | productType = "com.apple.product-type.bundle.unit-test"; 312 | }; 313 | 0B7C8BD91CBFD166000FE312 /* Truffaut */ = { 314 | isa = PBXNativeTarget; 315 | buildConfigurationList = 0B7C8BEB1CBFD166000FE312 /* Build configuration list for PBXNativeTarget "Truffaut" */; 316 | buildPhases = ( 317 | 0B7C8BD61CBFD166000FE312 /* Sources */, 318 | 0B7C8BD71CBFD166000FE312 /* Frameworks */, 319 | 0B7C8BD81CBFD166000FE312 /* Resources */, 320 | 0B7C8BF71CC139FD000FE312 /* Copy Frameworks */, 321 | ); 322 | buildRules = ( 323 | ); 324 | dependencies = ( 325 | 0B14BCE01E92360700EB0C6A /* PBXTargetDependency */, 326 | ); 327 | name = Truffaut; 328 | productName = Truffaut; 329 | productReference = 0B7C8BDA1CBFD166000FE312 /* Truffaut.app */; 330 | productType = "com.apple.product-type.application"; 331 | }; 332 | /* End PBXNativeTarget section */ 333 | 334 | /* Begin PBXProject section */ 335 | 0B7C8BD21CBFD166000FE312 /* Project object */ = { 336 | isa = PBXProject; 337 | attributes = { 338 | LastSwiftUpdateCheck = 0830; 339 | LastUpgradeCheck = 1030; 340 | ORGANIZATIONNAME = Codezerker; 341 | TargetAttributes = { 342 | 0B14BCD91E92360700EB0C6A = { 343 | CreatedOnToolsVersion = 8.3; 344 | DevelopmentTeam = 55724LR9PN; 345 | LastSwiftMigration = 1020; 346 | ProvisioningStyle = Automatic; 347 | }; 348 | 0B14BD001E92499300EB0C6A = { 349 | CreatedOnToolsVersion = 8.3; 350 | DevelopmentTeam = 55724LR9PN; 351 | ProvisioningStyle = Automatic; 352 | }; 353 | 0B7C8BD91CBFD166000FE312 = { 354 | CreatedOnToolsVersion = 7.3; 355 | DevelopmentTeam = 55724LR9PN; 356 | LastSwiftMigration = 1020; 357 | ProvisioningStyle = Automatic; 358 | }; 359 | }; 360 | }; 361 | buildConfigurationList = 0B7C8BD51CBFD166000FE312 /* Build configuration list for PBXProject "Truffaut" */; 362 | compatibilityVersion = "Xcode 3.2"; 363 | developmentRegion = en; 364 | hasScannedForEncodings = 0; 365 | knownRegions = ( 366 | en, 367 | Base, 368 | ); 369 | mainGroup = 0B7C8BD11CBFD166000FE312; 370 | productRefGroup = 0B7C8BDB1CBFD166000FE312 /* Products */; 371 | projectDirPath = ""; 372 | projectRoot = ""; 373 | targets = ( 374 | 0B7C8BD91CBFD166000FE312 /* Truffaut */, 375 | 0B14BCD91E92360700EB0C6A /* TruffautSupport */, 376 | 0B14BD001E92499300EB0C6A /* TruffautSupportTests */, 377 | ); 378 | }; 379 | /* End PBXProject section */ 380 | 381 | /* Begin PBXResourcesBuildPhase section */ 382 | 0B14BCD81E92360700EB0C6A /* Resources */ = { 383 | isa = PBXResourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | 0B14BCFF1E92499300EB0C6A /* Resources */ = { 390 | isa = PBXResourcesBuildPhase; 391 | buildActionMask = 2147483647; 392 | files = ( 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | }; 396 | 0B7C8BD81CBFD166000FE312 /* Resources */ = { 397 | isa = PBXResourcesBuildPhase; 398 | buildActionMask = 2147483647; 399 | files = ( 400 | 0B62141B1F45978A00B66C9B /* PageViewController.xib in Resources */, 401 | 0B7C8BE41CBFD166000FE312 /* Assets.xcassets in Resources */, 402 | 0B07128B1F557D730074EC7A /* syntax-hl.rb in Resources */, 403 | 0B7C8BE71CBFD166000FE312 /* Main.storyboard in Resources */, 404 | ); 405 | runOnlyForDeploymentPostprocessing = 0; 406 | }; 407 | /* End PBXResourcesBuildPhase section */ 408 | 409 | /* Begin PBXShellScriptBuildPhase section */ 410 | 0B14BD0F1E9380D100EB0C6A /* Create libTruffautSupport.dylib and TruffautSupport.swiftmodule */ = { 411 | isa = PBXShellScriptBuildPhase; 412 | buildActionMask = 2147483647; 413 | files = ( 414 | ); 415 | inputPaths = ( 416 | ); 417 | name = "Create libTruffautSupport.dylib and TruffautSupport.swiftmodule"; 418 | outputPaths = ( 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | shellPath = /bin/sh; 422 | shellScript = "EXTERN_LIB=$TARGET_BUILD_DIR/$PRODUCT_NAME.framework/Library\nmkdir -p $EXTERN_LIB\n\nLIB_PATH=$TARGET_BUILD_DIR/$EXECUTABLE_PATH\nMODULE_PATH=$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/Modules/$PRODUCT_NAME.swiftmodule/x86_64.swiftmodule\n\ncp $LIB_PATH $EXTERN_LIB/lib$PRODUCT_NAME.dylib\ncp $MODULE_PATH $EXTERN_LIB/$PRODUCT_NAME.swiftmodule\n"; 423 | }; 424 | /* End PBXShellScriptBuildPhase section */ 425 | 426 | /* Begin PBXSourcesBuildPhase section */ 427 | 0B14BCD51E92360700EB0C6A /* Sources */ = { 428 | isa = PBXSourcesBuildPhase; 429 | buildActionMask = 2147483647; 430 | files = ( 431 | 0B14BCEA1E923A2100EB0C6A /* Page.swift in Sources */, 432 | 0B14BCE81E923A1200EB0C6A /* Presentation.swift in Sources */, 433 | 0B14BCEC1E923A6F00EB0C6A /* Content.swift in Sources */, 434 | ); 435 | runOnlyForDeploymentPostprocessing = 0; 436 | }; 437 | 0B14BCFD1E92499300EB0C6A /* Sources */ = { 438 | isa = PBXSourcesBuildPhase; 439 | buildActionMask = 2147483647; 440 | files = ( 441 | 0B14BD041E92499300EB0C6A /* TruffautSupportTests.swift in Sources */, 442 | ); 443 | runOnlyForDeploymentPostprocessing = 0; 444 | }; 445 | 0B7C8BD61CBFD166000FE312 /* Sources */ = { 446 | isa = PBXSourcesBuildPhase; 447 | buildActionMask = 2147483647; 448 | files = ( 449 | 0B7C8C021CC1422B000FE312 /* StoryboardLoading.swift in Sources */, 450 | 0B0712891F557C290074EC7A /* SyntaxHighlighter.swift in Sources */, 451 | 0B7C8BFE1CC14006000FE312 /* SlidesWindowController.swift in Sources */, 452 | 0B7C8BE01CBFD166000FE312 /* MainViewController.swift in Sources */, 453 | 0B62141A1F45978A00B66C9B /* PageViewController.swift in Sources */, 454 | 0BAB64AF1EA08D7C001CE96A /* ReadController.swift in Sources */, 455 | 0B14BD131E9398FE00EB0C6A /* Shell.swift in Sources */, 456 | 0BAAD2391FF5E62E0099C191 /* PreferenceViewController.swift in Sources */, 457 | 0B7FADEB1D43472300000884 /* ExportController.swift in Sources */, 458 | 0BAAD2341FF5DF270099C191 /* PreferenceWindowController.swift in Sources */, 459 | 0BAAD23B1FF5F34B0099C191 /* UserPreference.swift in Sources */, 460 | 0B7C8C001CC14016000FE312 /* SlidesViewController.swift in Sources */, 461 | 0B62141D1F45A39000B66C9B /* Font.swift in Sources */, 462 | 0B7C8BDE1CBFD166000FE312 /* AppDelegate.swift in Sources */, 463 | 0BAAD2371FF5E49F0099C191 /* NSWindow+Style.swift in Sources */, 464 | 0B7C8BF91CC13E14000FE312 /* MainWindowController.swift in Sources */, 465 | 0B7C8BE21CBFD166000FE312 /* PresentationDocument.swift in Sources */, 466 | ); 467 | runOnlyForDeploymentPostprocessing = 0; 468 | }; 469 | /* End PBXSourcesBuildPhase section */ 470 | 471 | /* Begin PBXTargetDependency section */ 472 | 0B14BCE01E92360700EB0C6A /* PBXTargetDependency */ = { 473 | isa = PBXTargetDependency; 474 | target = 0B14BCD91E92360700EB0C6A /* TruffautSupport */; 475 | targetProxy = 0B14BCDF1E92360700EB0C6A /* PBXContainerItemProxy */; 476 | }; 477 | 0B14BD081E92499300EB0C6A /* PBXTargetDependency */ = { 478 | isa = PBXTargetDependency; 479 | target = 0B14BCD91E92360700EB0C6A /* TruffautSupport */; 480 | targetProxy = 0B14BD071E92499300EB0C6A /* PBXContainerItemProxy */; 481 | }; 482 | /* End PBXTargetDependency section */ 483 | 484 | /* Begin PBXVariantGroup section */ 485 | 0B7C8BE51CBFD166000FE312 /* Main.storyboard */ = { 486 | isa = PBXVariantGroup; 487 | children = ( 488 | 0B7C8BE61CBFD166000FE312 /* Base */, 489 | ); 490 | name = Main.storyboard; 491 | sourceTree = ""; 492 | }; 493 | /* End PBXVariantGroup section */ 494 | 495 | /* Begin XCBuildConfiguration section */ 496 | 0B14BCE41E92360700EB0C6A /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 500 | CLANG_ENABLE_MODULES = YES; 501 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 502 | CODE_SIGN_IDENTITY = ""; 503 | COMBINE_HIDPI_IMAGES = YES; 504 | DEBUG_INFORMATION_FORMAT = dwarf; 505 | DEFINES_MODULE = YES; 506 | DEVELOPMENT_TEAM = 55724LR9PN; 507 | DYLIB_COMPATIBILITY_VERSION = 1; 508 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 509 | FRAMEWORK_VERSION = A; 510 | INFOPLIST_FILE = TruffautSupport/Info.plist; 511 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 513 | MACOSX_DEPLOYMENT_TARGET = 10.14.4; 514 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupport; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | SKIP_INSTALL = YES; 517 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 518 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 519 | SWIFT_VERSION = 5.0; 520 | VERSIONING_SYSTEM = "apple-generic"; 521 | VERSION_INFO_PREFIX = ""; 522 | }; 523 | name = Debug; 524 | }; 525 | 0B14BCE51E92360700EB0C6A /* Release */ = { 526 | isa = XCBuildConfiguration; 527 | buildSettings = { 528 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 529 | CLANG_ENABLE_MODULES = YES; 530 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 531 | CODE_SIGN_IDENTITY = ""; 532 | COMBINE_HIDPI_IMAGES = YES; 533 | DEFINES_MODULE = YES; 534 | DEVELOPMENT_TEAM = 55724LR9PN; 535 | DYLIB_COMPATIBILITY_VERSION = 1; 536 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 537 | FRAMEWORK_VERSION = A; 538 | INFOPLIST_FILE = TruffautSupport/Info.plist; 539 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 541 | MACOSX_DEPLOYMENT_TARGET = 10.14.4; 542 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupport; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | SKIP_INSTALL = YES; 545 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 546 | SWIFT_VERSION = 5.0; 547 | VERSIONING_SYSTEM = "apple-generic"; 548 | VERSION_INFO_PREFIX = ""; 549 | }; 550 | name = Release; 551 | }; 552 | 0B14BD0A1E92499300EB0C6A /* Debug */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 556 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 557 | COMBINE_HIDPI_IMAGES = YES; 558 | DEBUG_INFORMATION_FORMAT = dwarf; 559 | DEVELOPMENT_TEAM = 55724LR9PN; 560 | INFOPLIST_FILE = TruffautSupportTests/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 562 | MACOSX_DEPLOYMENT_TARGET = 10.13; 563 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupportTests; 564 | PRODUCT_NAME = "$(TARGET_NAME)"; 565 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 566 | SWIFT_VERSION = 4.0; 567 | }; 568 | name = Debug; 569 | }; 570 | 0B14BD0B1E92499300EB0C6A /* Release */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 574 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 575 | COMBINE_HIDPI_IMAGES = YES; 576 | DEVELOPMENT_TEAM = 55724LR9PN; 577 | INFOPLIST_FILE = TruffautSupportTests/Info.plist; 578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 579 | MACOSX_DEPLOYMENT_TARGET = 10.13; 580 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.TruffautSupportTests; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | SWIFT_VERSION = 4.0; 583 | }; 584 | name = Release; 585 | }; 586 | 0B7C8BE91CBFD166000FE312 /* Debug */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | ALWAYS_SEARCH_USER_PATHS = NO; 590 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 591 | CLANG_ANALYZER_NONNULL = YES; 592 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 593 | CLANG_CXX_LIBRARY = "libc++"; 594 | CLANG_ENABLE_MODULES = YES; 595 | CLANG_ENABLE_OBJC_ARC = YES; 596 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 597 | CLANG_WARN_BOOL_CONVERSION = YES; 598 | CLANG_WARN_COMMA = YES; 599 | CLANG_WARN_CONSTANT_CONVERSION = YES; 600 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 601 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 602 | CLANG_WARN_EMPTY_BODY = YES; 603 | CLANG_WARN_ENUM_CONVERSION = YES; 604 | CLANG_WARN_INFINITE_RECURSION = YES; 605 | CLANG_WARN_INT_CONVERSION = YES; 606 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 607 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 608 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 609 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 610 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 611 | CLANG_WARN_STRICT_PROTOTYPES = YES; 612 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 613 | CLANG_WARN_UNREACHABLE_CODE = YES; 614 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 615 | CODE_SIGN_IDENTITY = "-"; 616 | COPY_PHASE_STRIP = NO; 617 | CURRENT_PROJECT_VERSION = 204; 618 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 619 | ENABLE_STRICT_OBJC_MSGSEND = YES; 620 | ENABLE_TESTABILITY = YES; 621 | GCC_C_LANGUAGE_STANDARD = gnu99; 622 | GCC_DYNAMIC_NO_PIC = NO; 623 | GCC_NO_COMMON_BLOCKS = YES; 624 | GCC_OPTIMIZATION_LEVEL = 0; 625 | GCC_PREPROCESSOR_DEFINITIONS = ( 626 | "DEBUG=1", 627 | "$(inherited)", 628 | ); 629 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 630 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 631 | GCC_WARN_UNDECLARED_SELECTOR = YES; 632 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 633 | GCC_WARN_UNUSED_FUNCTION = YES; 634 | GCC_WARN_UNUSED_VARIABLE = YES; 635 | MACOSX_DEPLOYMENT_TARGET = 10.11; 636 | MTL_ENABLE_DEBUG_INFO = YES; 637 | ONLY_ACTIVE_ARCH = YES; 638 | SDKROOT = macosx; 639 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 640 | SWIFT_VERSION = 3.0; 641 | VERSIONING_SYSTEM = "apple-generic"; 642 | }; 643 | name = Debug; 644 | }; 645 | 0B7C8BEA1CBFD166000FE312 /* Release */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | ALWAYS_SEARCH_USER_PATHS = NO; 649 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 650 | CLANG_ANALYZER_NONNULL = YES; 651 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 652 | CLANG_CXX_LIBRARY = "libc++"; 653 | CLANG_ENABLE_MODULES = YES; 654 | CLANG_ENABLE_OBJC_ARC = YES; 655 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 656 | CLANG_WARN_BOOL_CONVERSION = YES; 657 | CLANG_WARN_COMMA = YES; 658 | CLANG_WARN_CONSTANT_CONVERSION = YES; 659 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 660 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 661 | CLANG_WARN_EMPTY_BODY = YES; 662 | CLANG_WARN_ENUM_CONVERSION = YES; 663 | CLANG_WARN_INFINITE_RECURSION = YES; 664 | CLANG_WARN_INT_CONVERSION = YES; 665 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 666 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 667 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 668 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 669 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 670 | CLANG_WARN_STRICT_PROTOTYPES = YES; 671 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 672 | CLANG_WARN_UNREACHABLE_CODE = YES; 673 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 674 | CODE_SIGN_IDENTITY = "-"; 675 | COPY_PHASE_STRIP = NO; 676 | CURRENT_PROJECT_VERSION = 204; 677 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 678 | ENABLE_NS_ASSERTIONS = NO; 679 | ENABLE_STRICT_OBJC_MSGSEND = YES; 680 | GCC_C_LANGUAGE_STANDARD = gnu99; 681 | GCC_NO_COMMON_BLOCKS = YES; 682 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 683 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 684 | GCC_WARN_UNDECLARED_SELECTOR = YES; 685 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 686 | GCC_WARN_UNUSED_FUNCTION = YES; 687 | GCC_WARN_UNUSED_VARIABLE = YES; 688 | MACOSX_DEPLOYMENT_TARGET = 10.11; 689 | MTL_ENABLE_DEBUG_INFO = NO; 690 | SDKROOT = macosx; 691 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 692 | SWIFT_VERSION = 3.0; 693 | VERSIONING_SYSTEM = "apple-generic"; 694 | }; 695 | name = Release; 696 | }; 697 | 0B7C8BEC1CBFD166000FE312 /* Debug */ = { 698 | isa = XCBuildConfiguration; 699 | buildSettings = { 700 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 701 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 702 | CLANG_ENABLE_MODULES = YES; 703 | CODE_SIGN_IDENTITY = "Mac Developer"; 704 | COMBINE_HIDPI_IMAGES = YES; 705 | DEVELOPMENT_TEAM = 55724LR9PN; 706 | ENABLE_HARDENED_RUNTIME = YES; 707 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 708 | INFOPLIST_FILE = Truffaut/Info.plist; 709 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 710 | MACOSX_DEPLOYMENT_TARGET = 10.14.4; 711 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.Truffaut; 712 | PRODUCT_NAME = "$(TARGET_NAME)"; 713 | SWIFT_OBJC_BRIDGING_HEADER = "Truffaut/Truffaut-Bridging-Header.h"; 714 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 715 | SWIFT_VERSION = 5.0; 716 | }; 717 | name = Debug; 718 | }; 719 | 0B7C8BED1CBFD166000FE312 /* Release */ = { 720 | isa = XCBuildConfiguration; 721 | buildSettings = { 722 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 723 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 724 | CLANG_ENABLE_MODULES = YES; 725 | CODE_SIGN_IDENTITY = "Mac Developer"; 726 | COMBINE_HIDPI_IMAGES = YES; 727 | DEVELOPMENT_TEAM = 55724LR9PN; 728 | ENABLE_HARDENED_RUNTIME = YES; 729 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 730 | INFOPLIST_FILE = Truffaut/Info.plist; 731 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 732 | MACOSX_DEPLOYMENT_TARGET = 10.14.4; 733 | PRODUCT_BUNDLE_IDENTIFIER = com.codezerker.Truffaut; 734 | PRODUCT_NAME = "$(TARGET_NAME)"; 735 | SWIFT_OBJC_BRIDGING_HEADER = "Truffaut/Truffaut-Bridging-Header.h"; 736 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 737 | SWIFT_VERSION = 5.0; 738 | }; 739 | name = Release; 740 | }; 741 | /* End XCBuildConfiguration section */ 742 | 743 | /* Begin XCConfigurationList section */ 744 | 0B14BCE31E92360700EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupport" */ = { 745 | isa = XCConfigurationList; 746 | buildConfigurations = ( 747 | 0B14BCE41E92360700EB0C6A /* Debug */, 748 | 0B14BCE51E92360700EB0C6A /* Release */, 749 | ); 750 | defaultConfigurationIsVisible = 0; 751 | defaultConfigurationName = Release; 752 | }; 753 | 0B14BD091E92499300EB0C6A /* Build configuration list for PBXNativeTarget "TruffautSupportTests" */ = { 754 | isa = XCConfigurationList; 755 | buildConfigurations = ( 756 | 0B14BD0A1E92499300EB0C6A /* Debug */, 757 | 0B14BD0B1E92499300EB0C6A /* Release */, 758 | ); 759 | defaultConfigurationIsVisible = 0; 760 | defaultConfigurationName = Release; 761 | }; 762 | 0B7C8BD51CBFD166000FE312 /* Build configuration list for PBXProject "Truffaut" */ = { 763 | isa = XCConfigurationList; 764 | buildConfigurations = ( 765 | 0B7C8BE91CBFD166000FE312 /* Debug */, 766 | 0B7C8BEA1CBFD166000FE312 /* Release */, 767 | ); 768 | defaultConfigurationIsVisible = 0; 769 | defaultConfigurationName = Release; 770 | }; 771 | 0B7C8BEB1CBFD166000FE312 /* Build configuration list for PBXNativeTarget "Truffaut" */ = { 772 | isa = XCConfigurationList; 773 | buildConfigurations = ( 774 | 0B7C8BEC1CBFD166000FE312 /* Debug */, 775 | 0B7C8BED1CBFD166000FE312 /* Release */, 776 | ); 777 | defaultConfigurationIsVisible = 0; 778 | defaultConfigurationName = Release; 779 | }; 780 | /* End XCConfigurationList section */ 781 | }; 782 | rootObject = 0B7C8BD21CBFD166000FE312 /* Project object */; 783 | } 784 | -------------------------------------------------------------------------------- /Truffaut/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | --------------------------------------------------------------------------------