├── syncthing └── .gitkeep ├── Resources └── syncthing-bar.pdf ├── CONTRIBUTORS ├── syncthing-bar ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── icon_16x16.png │ │ ├── icon_32x32.png │ │ ├── icon_128x128.png │ │ ├── icon_256x256.png │ │ ├── icon_512x512.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_256x256@2x.png │ │ ├── icon_32x32@2x.png │ │ ├── icon_512x512@2x.png │ │ └── Contents.json │ ├── syncthing-bar.iconset │ │ ├── icon_128x128.png │ │ ├── icon_16x16.png │ │ ├── icon_256x256.png │ │ ├── icon_32x32.png │ │ ├── icon_512x512.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_32x32@2x.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_256x256@2x.png │ │ └── icon_512x512@2x.png │ └── syncthing-bar-invert.iconset │ │ ├── icon_16x16.png │ │ ├── icon_32x32.png │ │ ├── icon_128x128.png │ │ ├── icon_256x256.png │ │ ├── icon_512x512.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_256x256@2x.png │ │ ├── icon_32x32@2x.png │ │ └── icon_512x512@2x.png ├── syncthing-bar-Bridging-Header.h ├── SyncthingFolder.swift ├── SyncthingLog.swift ├── LogWindowController.swift ├── Info.plist ├── BatteryMonitor.swift ├── PortFinder.swift ├── SettingsWindowController.swift ├── MonitorRunner.swift ├── LogWindow.xib ├── SyncthingSettings.swift ├── AppDelegate.swift ├── SyncthingBar.swift ├── SettingsWindow.xib ├── SyncthingRunner.swift └── Base.lproj │ └── Main.storyboard ├── .travis.yml ├── mkrelease.sh ├── LICENSE ├── .gitignore ├── syncthing-barTests ├── Info.plist └── SyncthingBarTests.swift ├── scripts └── postinstall ├── README.md └── syncthing-bar.xcodeproj ├── xcshareddata └── xcschemes │ └── syncthing-bar.xcscheme └── project.pbxproj /syncthing/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Resources/syncthing-bar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/Resources/syncthing-bar.pdf -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Andreas Streichardt (m0ppers) 2 | Arminius (syncthing forum) 3 | @shish 4 | Christoph Russ 5 | Thomas Bouve 6 | Sascha Hagedorn 7 | -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_128x128.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_16x16.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_256x256.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_32x32.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_512x512.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_16x16.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_32x32.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar.iconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_128x128.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_256x256.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_512x512.png -------------------------------------------------------------------------------- /syncthing-bar/syncthing-bar-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 | 5 | #import 6 | -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m0ppers/syncthing-bar/HEAD/syncthing-bar/Images.xcassets/syncthing-bar-invert.iconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | 4 | script: 5 | - touch syncthing/syncthing 6 | - xcodebuild test -configuration Debug -project syncthing-bar.xcodeproj -scheme syncthing-bar -destination 'platform=OS X,arch=x86_64' 7 | -------------------------------------------------------------------------------- /mkrelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | xcodebuild install 4 | pkgbuild --analyze --root /tmp/syncthing-bar.dst syncthing-bar.plist 5 | pkgbuild --root /tmp/syncthing-bar.dst --component-plist syncthing-bar.plist --scripts scripts --version $1 syncthing-bar-$1.pkg 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ## License 2 | "THE (extended) BEER-WARE LICENSE" (Revision 42.0815): [mop](mailto:andreas.streichardt@gmail.com) contributed to this project. 3 | 4 | As long as you retain this notice you can do whatever you want with this stuff. 5 | If we meet some day, and you think this stuff is worth it, you can buy me some beers in return. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syncthing/syncthing 2 | syncthing-bar.plist 3 | syncthing-bar.pkg 4 | # Xcode 5 | .DS_Store 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | *.xcworkspace 16 | !default.xcworkspace 17 | xcuserdata 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | .idea/ 22 | # Pods - for those of you who use CocoaPods 23 | Pods 24 | -------------------------------------------------------------------------------- /syncthing-bar/SyncthingFolder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingFolder.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 14.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class SyncthingFolder { 12 | var id: NSString 13 | var label: NSString 14 | var path: NSString 15 | 16 | public init(id: NSString, path: NSString, label: NSString) { 17 | self.id = id 18 | self.path = path 19 | self.label = label 20 | } 21 | } -------------------------------------------------------------------------------- /syncthing-bar/SyncthingLog.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingLog.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 15.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class SyncthingLog { 12 | var logBuffer : Array = [] 13 | 14 | public init() { 15 | } 16 | 17 | func log(line: String) { 18 | logBuffer.append(line) 19 | if logBuffer.count >= 10000 { 20 | logBuffer.removeAtIndex(0) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /syncthing-barTests/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /scripts/postinstall: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # hmmm not sure if this is smart...but syncthing needs to be able to update itself :S 777 is of course worst case scenario but i am not sure how to fix it differently 3 | chmod -R 777 /Applications/syncthing-bar.app/Contents/Resources/syncthing 4 | cat > /Library/LaunchAgents/koeln.mop.syncthing-bar.agent.plist << EOT 5 | 6 | 7 | 8 | 9 | Label 10 | koeln.mop.syncthing-bar.agent 11 | ProgramArguments 12 | 13 | /Applications/syncthing-bar.app/Contents/MacOS/syncthing-bar 14 | 15 | RunAtLoad 16 | 17 | 18 | 19 | EOT 20 | -------------------------------------------------------------------------------- /syncthing-bar/LogWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsWindowController.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 13.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class LogWindowController: NSWindowController { 12 | @IBOutlet var view: NSTextView! 13 | var log : SyncthingLog 14 | 15 | // mop: found in some blog...some workaround because windowNibName is not a designated init func 16 | override var windowNibName : String! { 17 | return "LogWindow" 18 | } 19 | 20 | init(log : SyncthingLog) { 21 | self.log = log 22 | super.init(window: nil) 23 | } 24 | 25 | required init?(coder: NSCoder) { 26 | fatalError("Can't create from coder. I am too dumb and don't even know what it is.") 27 | } 28 | 29 | override func windowDidLoad() { 30 | // mop: mehhh...textview doesn't scale when resizing :S no idea yet... 31 | let joiner = "\n" 32 | view.insertText(log.logBuffer.joinWithSeparator(joiner)) 33 | super.windowDidLoad() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /syncthing-barTests/SyncthingBarTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingBarTests.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 23.06.15. 6 | // Copyright (c) 2015 mop. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import syncthing_bar 12 | 13 | class DummyWorkspace: NSWorkspace { 14 | internal var openedUrl : NSURL? 15 | 16 | override func openURL(url: NSURL) -> Bool { 17 | self.openedUrl = url 18 | return true 19 | } 20 | } 21 | 22 | class SyncthingBarTests: XCTestCase { 23 | func testOpenWhitespacedFolder() { 24 | let log : SyncthingLog = SyncthingLog() 25 | let syncthingBar = SyncthingBar(log: log); 26 | 27 | let sender = NSMenuItem(); 28 | sender.representedObject = SyncthingFolder(id: NSString(string: "1"), path: NSString(string: "/der hans"), label: NSString(string: "testung")); 29 | 30 | let workspace = DummyWorkspace() 31 | syncthingBar.workspace = workspace 32 | syncthingBar.openFolderAction(sender) 33 | XCTAssertEqual(workspace.openedUrl!.absoluteString, "file:///der%20hans") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /syncthing-bar/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2014 mop. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | NSAppTransportSecurity 34 | 35 | NSExceptionDomains 36 | 37 | localhost 38 | 39 | NSExceptionAllowsInsecureHTTPLoads 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /syncthing-bar/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /syncthing-bar/BatteryMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BatteryMonitor.swift 3 | // syncthing-bar 4 | // 5 | // Created by Sascha Hagedorn on 09/06/16. 6 | // Copyright © 2016 mop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BatteryMonitor: NSObject { 12 | var timer_interval: Double 13 | var monitorTimer : NSTimer? 14 | var notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter() 15 | 16 | override init() { 17 | self.timer_interval = 4.0 18 | 19 | super.init() 20 | } 21 | 22 | func startMonitor() { 23 | if (monitorTimer != nil && monitorTimer!.valid) { 24 | return 25 | } 26 | 27 | monitorTimer = NSTimer.scheduledTimerWithTimeInterval(self.timer_interval, 28 | target: self, 29 | selector: #selector(BatteryMonitor.checkBattery(_:)), 30 | userInfo: nil, 31 | repeats: true) 32 | } 33 | 34 | func stopMonitor() { 35 | if (monitorTimer != nil && monitorTimer!.valid) { 36 | monitorTimer!.invalidate() 37 | } 38 | } 39 | 40 | func checkBattery(timer: NSTimer) { 41 | if (!timer.valid) { 42 | return 43 | } 44 | 45 | let startStopData = ["pause" : isOnBattery()] 46 | notificationCenter.postNotificationName(StartStop, object: self, userInfo: startStopData) 47 | } 48 | 49 | func isOnBattery() -> Bool { 50 | let timeRemaining: CFTimeInterval = IOPSGetTimeRemainingEstimate() 51 | 52 | if timeRemaining == -2.0 { 53 | return false 54 | } 55 | 56 | return true 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | syncthing-bar [![Build Status](https://travis-ci.org/m0ppers/syncthing-bar.svg?branch=master)](https://travis-ci.org/m0ppers/syncthing-bar) 2 | ============= 3 | 4 | **THIS PROJECT IS OBSOLETE NOW AS THERE IS AN OFFICIAL CLIENT: https://github.com/syncthing/syncthing-macos** 5 | 6 | A little statusbar for http://syncthing.net/ on OSX 7 | 8 | Be aware that i am NOT a swift developer. I am not even a cocoa developer. This is more or less a weekend experiment. It should be useable but the code is a mess :O (so much i can tell :D) 9 | 10 | ## What will it do? 11 | 12 | Syncthing bar has syncthing bundled. Once started it will try to upgrade and then keep the bundled syncthing running. It will automatically select a port. 13 | When clicking on the statusbar icon it will offer quick access to the UI and will allow you to open any shared folder in finder. Syncthing log may be examined as well. That's it 14 | 15 | ## Requirements 16 | 17 | OS X 10.10 is required 18 | 19 | ## To build/run 20 | 21 | 1. Clone the repository in X-Code 22 | 2. Download syncthing from http://syncthing.net/ 23 | 3. Extract the archive 24 | 4. Locate the "syncthing" binary 25 | 5. Copy the binary to your syncthing-bar source repository in the folder "syncthing" 26 | 6. Open X-Code (binary/syncthing should NOT be marked RED anymore) 27 | 7. Hit the fancy play button :S 28 | 8. it SHOULD run :S 29 | 30 | ## Demo :O 31 | 32 | ![alt tag](https://m0ppers.github.io/syncthing-bar.gif) 33 | 34 | ## Caveats 35 | 36 | Syncthingbar is intended for local usage only. It will hardcode host and port and apikey. If you want to access the UI from within your network use a standalone syncthing. 37 | 38 | ## Installation Package 39 | 40 | The latest release can be found on the [releases tab](https://github.com/m0ppers/syncthing-bar/releases) 41 | 42 | ## Installation using homebrew 43 | 44 | After you have installed [homebrew](http://brew.sh) you can install syncthing-bar with the `brew` command: 45 | 46 | brew tap caskroom/cask 47 | brew cask install syncthing-bar 48 | -------------------------------------------------------------------------------- /syncthing-bar/PortFinder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PortFinder.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 13.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class PortFinder { 12 | var startPort: NSInteger 13 | 14 | init(startPort: NSInteger) { 15 | self.startPort = startPort 16 | } 17 | 18 | // https://github.com/glock45/swifter/blob/master/Common/Socket.swift 19 | func port_htons(port: in_port_t) -> in_port_t { 20 | let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian 21 | return isLittleEndian ? _OSSwapInt16(port) : port 22 | } 23 | 24 | func findPort() -> (port: NSInteger, err: NSString?) { 25 | for i in 0...100 { 26 | // mop: ahhh so this is apple usability 27 | let socket = CFSocketCreate(nil, 0, 0, 0, 0, nil, nil) 28 | if (socket == nil) { 29 | return (0, "Could not create test socket") 30 | } 31 | let port = startPort + i 32 | 33 | var addr = sockaddr_in(sin_len: __uint8_t(sizeof(sockaddr_in)), sin_family: sa_family_t(AF_INET), 34 | sin_port: port_htons(in_port_t(port)), sin_addr: in_addr(s_addr: inet_addr("127.0.0.1")), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) 35 | 36 | let result = withUnsafePointer(&addr) { (pointer: UnsafePointer) -> (Bool) in 37 | let cfData = CFDataCreate(nil, UnsafePointer(pointer), sizeof(sockaddr_in)) 38 | let error = CFSocketSetAddress(socket, cfData) 39 | 40 | // mop: ok ... X-Codes autocompletion crashes continously and the docs seem to be wrong 41 | // this is what i would expect here normally 42 | // return error == CFSocketError.kCFSocketSuccess 43 | // doesn't work...maybe i am just too dumb for swift 44 | if (error.rawValue == 0) { 45 | CFSocketInvalidate(socket) 46 | return true 47 | } else { 48 | return false 49 | } 50 | } 51 | 52 | if (result) { 53 | return (port, nil) 54 | } 55 | } 56 | return (0, "Could not find an open port :S") 57 | } 58 | } -------------------------------------------------------------------------------- /syncthing-bar/SettingsWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsWindowController.swift 3 | // syncthing-bar 4 | // 5 | // Created by Christoph Russ on 1/06/2015. 6 | // Copyright (c) 2015 CR. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SettingsWindowController: NSWindowController { 12 | @IBOutlet var bw_icon_check: NSButton! 13 | @IBOutlet var invert_icon_check: NSButton! 14 | @IBOutlet var port_field: NSTextField! 15 | @IBOutlet var confirm_exit_check: NSButton! 16 | @IBOutlet var monitoring_check: NSButton! 17 | @IBOutlet var monitor_apps: NSTextField! 18 | @IBOutlet var pause_on_battery_check: NSButton! 19 | 20 | var settings: SyncthingSettings = SyncthingSettings() 21 | 22 | var notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter() 23 | 24 | // mop: found in some blog...some workaround because windowNibName is not a designated init func 25 | override var windowNibName : String! { 26 | return "SettingsWindow" 27 | } 28 | 29 | init(settings: SyncthingSettings) { 30 | 31 | self.settings = settings //SyncthingSettings(bw_icon: bw_icon, invert_icon: invert_icon, port: port, confirm_exit: confirm_exit) 32 | 33 | super.init(window: nil) 34 | //super.init(windowNibName: "SettingsWindow") 35 | } 36 | 37 | required init?(coder: NSCoder) { 38 | fatalError("Can't create from coder. I am too dumb and don't even know what it is.") 39 | } 40 | 41 | override func windowDidLoad() { 42 | //var joiner = "\n" 43 | //view.insertText(joiner.join(log.logBuffer)) 44 | self.applySettings() 45 | super.windowDidLoad() 46 | } 47 | 48 | func applySettings() { 49 | 50 | settings.applySettings(self) 51 | } 52 | 53 | //settings actions 54 | 55 | @IBAction func bw_icon_checked(sender: NSButton) { 56 | self.settings.bw_icon = (sender.state == NSOnState) 57 | 58 | // FIX: this doesn't work - don't know how to fix right now 59 | /*if (bw_icon) { 60 | invert_icon_check.enabled = false 61 | } else { 62 | invert_icon_check.enabled = true 63 | }*/ 64 | 65 | postSettings() 66 | } 67 | 68 | @IBAction func invert_icon_checked(sender: NSButton) { 69 | self.settings.invert_icon = (sender.state == NSOnState) 70 | 71 | postSettings() 72 | } 73 | 74 | @IBAction func port_changed(sender: NSTextField) { 75 | self.settings.port = sender.stringValue 76 | 77 | postSettings() 78 | } 79 | 80 | @IBAction func confirm_exit_checked(sender: NSButton) { 81 | self.settings.confirm_exit = (sender.state == NSOnState) 82 | 83 | postSettings() 84 | } 85 | 86 | @IBAction func monitoring_checked(sender: NSButton) { 87 | self.settings.monitoring = (sender.state == NSOnState) 88 | 89 | postSettings() 90 | } 91 | 92 | @IBAction func monitor_apps_changed(sender: NSTextField) { 93 | self.settings.monitor_apps = sender.stringValue 94 | 95 | postSettings() 96 | } 97 | 98 | @IBAction func pause_on_battery_checked(sender: NSButton) { 99 | self.settings.pause_on_battery = (sender.state == NSOnState) 100 | 101 | postSettings() 102 | } 103 | 104 | func postSettings() { 105 | let settingsData = ["settings": self.settings] 106 | self.notificationCenter.postNotificationName(SettingsSet, object: self, userInfo: settingsData) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /syncthing-bar/MonitorRunner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MonitorRunner.swift 3 | // syncthing-bar 4 | // 5 | // Created by Christoph Russ on 3/06/2015. 6 | // Copyright (c) 2015 CR. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | class MonitorRunner: NSObject { 13 | var activated: Bool 14 | var timer_interval: Double 15 | var apps: [String] 16 | var monitorTimer : NSTimer? 17 | var notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter() 18 | 19 | init(monitor_apps: String?) { 20 | self.activated = false 21 | self.timer_interval = 4.0 22 | self.apps = [] 23 | 24 | super.init() 25 | 26 | self.set_apps(monitor_apps) 27 | //self.startMonitor() 28 | } 29 | 30 | func set_apps(monitor_apps: String?) { 31 | self.apps.removeAll() 32 | 33 | let separators = NSCharacterSet(charactersInString: ",;\n\t") 34 | let whitespaceNewLine = NSCharacterSet.whitespaceAndNewlineCharacterSet() 35 | let app_array = monitor_apps!.componentsSeparatedByCharactersInSet(separators) 36 | 37 | for app in app_array { 38 | if !app.isEmpty { 39 | let app_trim = app.stringByTrimmingCharactersInSet(whitespaceNewLine); 40 | self.apps.append(app_trim) 41 | } 42 | } 43 | } 44 | 45 | func startMonitor() { 46 | if (monitorTimer != nil && monitorTimer!.valid) { 47 | return 48 | } 49 | //print("Starting TIMER for app monitor.\n") 50 | let appData : [String: String] = ["":""]; 51 | monitorTimer = NSTimer.scheduledTimerWithTimeInterval(self.timer_interval, target: self, selector: #selector(MonitorRunner.checkApps(_:)), userInfo: appData, repeats: true) 52 | } 53 | 54 | func stopMonitor() { 55 | //print("Stopping TIMER for app monitor.\n") 56 | if (monitorTimer != nil && monitorTimer!.valid) { 57 | monitorTimer!.invalidate() 58 | } 59 | } 60 | 61 | func checkApps(timer: NSTimer) { 62 | if (!timer.valid) { 63 | return 64 | } 65 | 66 | let running_apps: [String] = self.getRunningAppNames() 67 | 68 | for monitor_app in self.apps { 69 | if running_apps.contains(monitor_app) { 70 | let startStopData = ["pause": true] 71 | notificationCenter.postNotificationName(StartStop, object: self, userInfo: startStopData) 72 | self.activated = true 73 | return 74 | } 75 | } 76 | 77 | if self.activated { 78 | // it appears non of these apps are active any-longer - so we can continue .. 79 | self.activated = false 80 | let startStopData = ["pause": false] 81 | notificationCenter.postNotificationName(StartStop, object: self, userInfo: startStopData) 82 | } 83 | 84 | } 85 | 86 | func getRunningAppNames() -> [String] { 87 | let r_apps = NSWorkspace().runningApplications 88 | var r_app_names: [String] = [] 89 | 90 | for r_app in r_apps { 91 | let r_app_name: String = r_app.localizedName! //bundleIdentifier (e.g. com.apple.iTunes) 92 | r_app_names.append(r_app_name) 93 | 94 | // this would check for ANY appearance of the app name 95 | /*if r_app_name.rangeOfString(...) != nil{ } 96 | if contains(self.apps,r_app_name) { 97 | //println(r_app_name) 98 | return true 99 | }*/ 100 | } 101 | 102 | return r_app_names 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /syncthing-bar/LogWindow.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 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /syncthing-bar.xcodeproj/xcshareddata/xcschemes/syncthing-bar.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /syncthing-bar/SyncthingSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingSettings.swift 3 | // syncthing-bar 4 | // 5 | // Created by Christoph Russ on 1/06/2015. 6 | // Copyright (c) 2015 CR. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | class SyncthingSettings { 13 | 14 | var bw_icon_key: String = "BlackWhiteIcon" 15 | var bw_icon: Bool = true 16 | 17 | var invert_icon_key: String = "InvertIcon" 18 | var invert_icon: Bool = false 19 | 20 | var port_key: String = "PortNumber" 21 | var port: String = "8084" 22 | 23 | var confirm_exit_key: String = "ConfirmExit" 24 | var confirm_exit: Bool = true 25 | 26 | var monitoring_key: String = "Monitoring" 27 | var monitoring: Bool = false 28 | 29 | var monitor_apps_key: String = "MonitorApps" 30 | var monitor_apps = "iPhoto; iMovie\nLightroom" 31 | 32 | var pause_on_battery_key: String = "PauseOnBattery" 33 | var pause_on_battery: Bool = false 34 | 35 | init() { 36 | self.bw_icon_key = "BlackWhiteIcon" 37 | self.invert_icon_key = "InvertIcon" 38 | self.port_key = "PortNumber" 39 | self.confirm_exit_key = "ConfirmExit" 40 | self.monitoring_key = "Monitoring" 41 | self.monitor_apps_key = "MonitorApps" 42 | self.pause_on_battery_key = "PauseOnBattery" 43 | 44 | self.bw_icon = true 45 | self.invert_icon = false 46 | self.port = "8084" 47 | self.confirm_exit = true 48 | self.monitoring = false 49 | self.monitor_apps = "iPhoto; iMovie,\niTunes, Lightroom" 50 | self.pause_on_battery = false 51 | 52 | self.loadSettings() 53 | } 54 | 55 | init(bw_icon: Bool, invert_icon: Bool, port: String, confirm_exit: Bool, monitoring: Bool, monitor_apps: String, pause_on_battery: Bool) { 56 | self.bw_icon = bw_icon 57 | self.invert_icon = invert_icon 58 | self.port = port 59 | self.confirm_exit = confirm_exit 60 | self.monitoring = monitoring 61 | self.monitor_apps = monitor_apps 62 | self.pause_on_battery = pause_on_battery 63 | 64 | self.saveSettings() 65 | } 66 | 67 | func loadSettings() { 68 | let defaults = NSUserDefaults.standardUserDefaults() 69 | 70 | if let bw_icon = defaults.valueForKey(self.bw_icon_key) as? Bool { 71 | self.bw_icon = bw_icon 72 | } 73 | 74 | if let invert_icon = defaults.valueForKey(self.invert_icon_key) as? Bool { 75 | self.invert_icon = invert_icon 76 | } 77 | 78 | if let port_string = defaults.valueForKey(self.port_key) as? String { 79 | self.port = port_string 80 | } 81 | 82 | if let confirm_exit = defaults.valueForKey(self.confirm_exit_key) as? Bool { 83 | self.confirm_exit = confirm_exit 84 | } 85 | 86 | if let monitoring = defaults.valueForKey(self.monitoring_key) as? Bool { 87 | self.monitoring = monitoring 88 | } 89 | 90 | if let monitor_apps_string = defaults.valueForKey(self.monitor_apps_key) as? String { 91 | self.monitor_apps = monitor_apps_string 92 | } 93 | 94 | if let pause_on_battery = defaults.valueForKey(self.pause_on_battery_key) as? Bool { 95 | self.pause_on_battery = pause_on_battery 96 | } 97 | } 98 | 99 | func saveSettings() { 100 | // stored in preference folder 101 | // (e.g. ~/Library/Preferences/koeln.mop.syncthing-bar.plist) 102 | let defaults = NSUserDefaults.standardUserDefaults() 103 | 104 | defaults.setValue(self.bw_icon, forKey: self.bw_icon_key) 105 | defaults.setValue(self.invert_icon, forKey: self.invert_icon_key) 106 | defaults.setValue(self.port, forKey: self.port_key) 107 | defaults.setValue(self.confirm_exit, forKey: self.confirm_exit_key) 108 | defaults.setValue(self.monitoring, forKey: self.monitoring_key) 109 | defaults.setValue(self.monitor_apps, forKey: self.monitor_apps_key) 110 | defaults.setValue(self.pause_on_battery, forKey: self.pause_on_battery_key) 111 | 112 | defaults.synchronize() 113 | } 114 | 115 | func applySettings(wndCtrl: SettingsWindowController) { 116 | if (self.bw_icon) { 117 | wndCtrl.bw_icon_check?.state = NSOnState 118 | } else { 119 | wndCtrl.bw_icon_check?.state = NSOffState 120 | } 121 | 122 | if (self.invert_icon) { 123 | wndCtrl.invert_icon_check?.state = NSOnState 124 | } else { 125 | wndCtrl.invert_icon_check?.state = NSOffState 126 | } 127 | 128 | wndCtrl.port_field?.stringValue = self.port as String 129 | 130 | if (self.confirm_exit) { 131 | wndCtrl.confirm_exit_check?.state = NSOnState 132 | } else { 133 | wndCtrl.confirm_exit_check?.state = NSOffState 134 | } 135 | 136 | if (self.monitoring) { 137 | wndCtrl.monitoring_check?.state = NSOnState 138 | } else { 139 | wndCtrl.monitoring_check?.state = NSOffState 140 | } 141 | 142 | wndCtrl.monitor_apps?.stringValue = self.monitor_apps as String 143 | 144 | if (self.pause_on_battery) { 145 | wndCtrl.pause_on_battery_check?.state = NSOnState 146 | } else { 147 | wndCtrl.pause_on_battery_check?.state = NSOffState 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /syncthing-bar/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // syncthing-statusbar 4 | // 5 | // Created by Andreas Streichardt on 12.12.14. 6 | // Copyright (c) 2014 Andreas Streichardt. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import AppKit 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | //var settingsWindowController = SettingsWindowController() //windowNibName: "Settings") 15 | var runner : SyncthingRunner? 16 | var syncthingBar : SyncthingBar? 17 | var log : SyncthingLog = SyncthingLog() 18 | var monitor : MonitorRunner? 19 | var batteryMonitor : BatteryMonitor? 20 | 21 | func applicationWillFinishLaunching(aNotification: NSNotification) { 22 | NSApp.setActivationPolicy(NSApplicationActivationPolicy.Accessory) 23 | } 24 | 25 | 26 | func applicationDidFinishLaunching(aNotification: NSNotification) { 27 | syncthingBar = SyncthingBar(log: log) 28 | runner = SyncthingRunner(log: log) 29 | let result = runner!.ensureRunning() 30 | if (result != nil) { 31 | let alert = NSAlert() 32 | alert.addButtonWithTitle("Ok :(") 33 | alert.messageText = "Got a fatal error: \(result!) :( Exiting" 34 | alert.alertStyle = NSAlertStyle.WarningAlertStyle 35 | _ = alert.runModal() 36 | self.quit() 37 | } 38 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.tooManyErrors(_:)), name: TooManyErrorsNotification, object: runner) 39 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.foldersDetermined(_:)), name: FoldersDetermined, object: runner) 40 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.httpChanged(_:)), name: HttpChanged, object: runner) 41 | 42 | self.monitor = MonitorRunner(monitor_apps: syncthingBar?.settings?.monitor_apps) 43 | if self.syncthingBar!.settings!.monitoring { 44 | self.monitor?.startMonitor() 45 | } 46 | 47 | self.batteryMonitor = BatteryMonitor() 48 | if self.syncthingBar!.settings!.pause_on_battery { 49 | self.batteryMonitor?.startMonitor() 50 | } 51 | 52 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.settingsSet(_:)), name: SettingsSet, object: syncthingBar?.setter) 53 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.startStop(_:)), name: StartStop, object: syncthingBar) 54 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.startStop(_:)), name: StartStop, object: monitor) 55 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(AppDelegate.startStop(_:)), name: StartStop, object: batteryMonitor) 56 | } 57 | 58 | func stop() { 59 | runner?.stop() 60 | } 61 | 62 | 63 | func applicationWillTerminate(aNotification: NSNotification) { 64 | // mop: i don't get it .... this will only get called when quitting via UI. SIGTERM will NOT land here and i fail installing a proper signal handler :| 65 | self.stop() 66 | } 67 | 68 | func settingsAction(sender : AnyObject) { 69 | //settingsWindowController.showWindow(sender) 70 | } 71 | 72 | func quitAction(sender : AnyObject) { 73 | if (syncthingBar!.settings!.confirm_exit) { 74 | let alert = NSAlert() 75 | alert.addButtonWithTitle("Yes") 76 | alert.addButtonWithTitle("Cancel") 77 | alert.messageText = "Are you sure you want to quit?" 78 | 79 | // FIX: THIS DOESN'T WORK ... 80 | //var remember_btn: NSButton = alert.addButtonWithTitle("Remember my decision.") 81 | //remember_btn.setButtonType(NSButtonType.OnOffButton) 82 | 83 | alert.alertStyle = NSAlertStyle.WarningAlertStyle 84 | 85 | let response = alert.runModal() 86 | if (response != NSAlertFirstButtonReturn) { 87 | return 88 | } 89 | } 90 | self.quit() 91 | } 92 | 93 | func tooManyErrors(sender : AnyObject) { 94 | let alert = NSAlert() 95 | alert.addButtonWithTitle("Ok :(") 96 | alert.messageText = "Syncthing could not run. There were too many errors. Check log, and restart :(" 97 | alert.alertStyle = NSAlertStyle.WarningAlertStyle 98 | 99 | _ = alert.runModal() 100 | } 101 | 102 | func genericError(errorMessage: String) { 103 | let alert = NSAlert() 104 | alert.addButtonWithTitle("Ok :(") 105 | alert.messageText = errorMessage 106 | alert.alertStyle = NSAlertStyle.WarningAlertStyle 107 | 108 | _ = alert.runModal() 109 | } 110 | 111 | func httpChanged(notification: NSNotification) { 112 | if notification.userInfo != nil { 113 | let host = notification.userInfo!["host"] as! NSString 114 | let port = notification.userInfo!["port"] as! NSString 115 | 116 | self.syncthingBar!.settings!.port = port as String 117 | 118 | syncthingBar!.enableUIOpener("http://\(host):\(port)") 119 | } else { 120 | syncthingBar!.disableUIOpener() 121 | } 122 | } 123 | 124 | func foldersDetermined(notification: NSNotification) { 125 | if let folders = notification.userInfo!["folders"] as? Array { 126 | syncthingBar!.setFolders(folders) 127 | } 128 | } 129 | 130 | func settingsSet(notification: NSNotification) { 131 | // ctp: maybe we should have a Settings class ... 132 | if let settings_ntfc = notification.userInfo!["settings"] as? SyncthingSettings { 133 | 134 | var valid_port : Bool = true 135 | let port_ntfc : String = settings_ntfc.port 136 | 137 | if ((port_ntfc.characters.count < 3) || (port_ntfc.characters.count > 5)) { 138 | valid_port = false 139 | } 140 | 141 | let portFromString = Int(port_ntfc) 142 | if ((portFromString) != nil) { 143 | if ((portFromString < 1000) || (portFromString > 65535)) { 144 | valid_port = false 145 | } 146 | } 147 | else { 148 | valid_port = false 149 | } 150 | 151 | if (!valid_port) { 152 | self.genericError("You entered an invalid port number.") 153 | return 154 | } 155 | 156 | self.syncthingBar!.updateSettings(settings_ntfc) 157 | 158 | } 159 | 160 | //start stop app monitor from here ..? 161 | self.monitor!.set_apps(self.syncthingBar!.settings?.monitor_apps) 162 | 163 | if self.syncthingBar!.settings!.monitoring { 164 | self.monitor?.startMonitor() 165 | } else { 166 | self.monitor?.stopMonitor() 167 | } 168 | 169 | if self.syncthingBar!.settings!.pause_on_battery { 170 | self.batteryMonitor?.startMonitor() 171 | } else { 172 | self.batteryMonitor?.stopMonitor() 173 | } 174 | } 175 | 176 | func startStop(notification: NSNotification) { 177 | // ctp: pausing execution made possible :D 178 | 179 | if let pause_ntfc = notification.userInfo!["pause"] as? Bool { 180 | if pause_ntfc { 181 | self.runner?.pause() 182 | } 183 | else { 184 | self.runner?.play() 185 | } 186 | } 187 | } 188 | 189 | func quit() { 190 | NSApplication.sharedApplication().terminate(self) 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /syncthing-bar/SyncthingBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingBar.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 14.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | let FolderTag = 1 12 | 13 | public class SyncthingBar: NSObject { 14 | var statusBar: NSStatusBar = NSStatusBar.systemStatusBar() 15 | var statusBarItem : NSStatusItem = NSStatusItem() 16 | var menu : NSMenu = NSMenu() 17 | var openUIItem: NSMenuItem 18 | var startStopSyncthingItem: NSMenuItem 19 | var url: NSString? 20 | var controller: LogWindowController? 21 | var settings: SyncthingSettings? 22 | var setter: SettingsWindowController? 23 | var log : SyncthingLog 24 | public var workspace : NSWorkspace = NSWorkspace.sharedWorkspace() 25 | 26 | public init(log : SyncthingLog) { 27 | self.log = log 28 | //Add statusBarItem 29 | statusBarItem = statusBar.statusItemWithLength(-1) 30 | statusBarItem.menu = menu 31 | 32 | let size = NSSize(width: 18, height: 18) 33 | let icon = NSImage(named: "syncthing-bar") 34 | // mop: that is the preferred way but the image is currently not drawn as it has to be and i am not an artist :( 35 | icon?.template = true 36 | icon?.size = size 37 | statusBarItem.image = icon 38 | 39 | menu.autoenablesItems = false 40 | 41 | openUIItem = NSMenuItem() 42 | openUIItem.title = "Open UI" 43 | openUIItem.action = #selector(SyncthingBar.openUIAction(_:)) 44 | openUIItem.enabled = false 45 | menu.addItem(openUIItem) 46 | 47 | startStopSyncthingItem = NSMenuItem() 48 | startStopSyncthingItem.title = "Stop Syncthing" 49 | startStopSyncthingItem.action = #selector(SyncthingBar.startStopSyncthingAction(_:)) 50 | startStopSyncthingItem.enabled = false 51 | menu.addItem(startStopSyncthingItem) 52 | 53 | menu.addItem(NSMenuItem.separatorItem()) 54 | 55 | let openLogItem : NSMenuItem = NSMenuItem() 56 | openLogItem.title = "Show Log" 57 | openLogItem.action = #selector(SyncthingBar.openLogAction(_:)) 58 | openLogItem.enabled = true 59 | menu.addItem(openLogItem) 60 | 61 | // this will automagically check, if there are already settings stored and load them ... 62 | settings = SyncthingSettings() 63 | 64 | let openSettingsItem : NSMenuItem = NSMenuItem() 65 | openSettingsItem.title = "Settings" 66 | openSettingsItem.action = #selector(SyncthingBar.openSettingsAction(_:)) 67 | openSettingsItem.enabled = true 68 | menu.addItem(openSettingsItem) 69 | 70 | let quitItem : NSMenuItem = NSMenuItem() 71 | quitItem.title = "Quit" 72 | quitItem.action = Selector("quitAction:") 73 | quitItem.enabled = true 74 | menu.addItem(quitItem) 75 | 76 | super.init() 77 | // mop: todo: move the remaining actions as well 78 | openUIItem.target = self 79 | startStopSyncthingItem.target = self 80 | openLogItem.target = self 81 | openSettingsItem.target = self 82 | 83 | self.updateSettings(self.settings!) 84 | } 85 | 86 | func enableUIOpener(uiUrl: NSString) { 87 | url = uiUrl 88 | openUIItem.enabled = true 89 | startStopSyncthingItem.enabled = true 90 | } 91 | 92 | func disableUIOpener() { 93 | openUIItem.enabled = false 94 | } 95 | 96 | func setFolders(folders: Array) { 97 | // mop: should probably check if anything changed ... but first simple stupid :S 98 | var item = menu.itemWithTag(FolderTag) 99 | while (item != nil) { 100 | menu.removeItem(item!) 101 | item = menu.itemWithTag(FolderTag) 102 | } 103 | 104 | // mop: maybe findByTag instead of hardcoded number? 105 | let startInsertIndex = 3 106 | var folderCount = 0 107 | for folder in folders { 108 | let folderItem : NSMenuItem = NSMenuItem() 109 | folderItem.title = "Open \(folder.label.length > 0 ? folder.label : folder.id) in Finder" 110 | folderItem.representedObject = folder 111 | folderItem.action = #selector(SyncthingBar.openFolderAction(_:)) 112 | folderItem.enabled = true 113 | folderItem.tag = FolderTag 114 | folderItem.target = self 115 | menu.insertItem(folderItem, atIndex: startInsertIndex + folderCount) 116 | folderCount = folderCount + 1 117 | } 118 | 119 | // mop: only add if there were folders (we already have a separator after "Open UI") 120 | if (folderCount > 0) { 121 | let lowerSeparator = NSMenuItem.separatorItem() 122 | // mop: well a bit hacky but we need to clear this one as well ;) 123 | lowerSeparator.tag = FolderTag 124 | menu.insertItem(lowerSeparator, atIndex: startInsertIndex + folderCount) 125 | } 126 | } 127 | 128 | func openUIAction(sender: AnyObject) { 129 | if (url != nil) { 130 | workspace.openURL(NSURL(string: url! as String)!) 131 | } 132 | } 133 | 134 | func startStopSyncthingAction(sender: AnyObject) { 135 | let notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter() 136 | let title: String = (sender as! NSMenuItem).title 137 | if title.rangeOfString("Stop") != nil { 138 | (sender as! NSMenuItem).title = "Start Syncthing" 139 | let startStopData = ["pause": true] 140 | notificationCenter.postNotificationName(StartStop, object: self, userInfo: startStopData) 141 | } 142 | else { 143 | (sender as! NSMenuItem).title = "Stop Syncthing" 144 | let startStopData = ["pause": false] 145 | notificationCenter.postNotificationName(StartStop, object: self, userInfo: startStopData) 146 | } 147 | } 148 | 149 | public func openFolderAction(sender: AnyObject) { 150 | let folder = (sender as! NSMenuItem).representedObject as! SyncthingFolder 151 | workspace.openURL(NSURL(fileURLWithPath: folder.path as String)) 152 | } 153 | 154 | func openLogAction(sender: AnyObject) { 155 | // mop: recreate even if it exists (not sure if i manually need to close and cleanup :S) 156 | // seems wrong to me but works (i want to view current log output :S) 157 | controller = LogWindowController(log: log) 158 | controller?.showWindow(self) 159 | //controller?.window?.makeMainWindow() 160 | controller?.window?.makeKeyAndOrderFront(self) 161 | } 162 | 163 | func openSettingsAction(sender: AnyObject) { 164 | // ctp: settins window only used for syncthing-bar, not syncthing itself, although we could also configure port here ... 165 | 166 | setter = SettingsWindowController(settings: self.settings!) 167 | setter?.showWindow(self) 168 | //setter?.window?.makeMainWindow() 169 | setter?.window?.makeKeyAndOrderFront(self) 170 | } 171 | 172 | func updateSettings(settings: SyncthingSettings) { 173 | // ctp: somewhat redundany to storing this in the settings controller already? 174 | // maybe we shouldn't create the settings window over and over ? 175 | 176 | // TODO: we are not storing these settings anywhere useful, yet 177 | // TODO: maybe create an app-settings-dir in the appropriate ~/Library location and write the settings into there? 178 | 179 | self.settings = settings 180 | 181 | let icon: NSImage?; 182 | let size = NSSize(width: 18, height: 18) 183 | 184 | if (self.settings!.bw_icon) { 185 | if (self.settings!.invert_icon) { 186 | icon = NSImage(named: "syncthing-bar-invert") 187 | icon?.template = true 188 | icon?.size = size 189 | statusBarItem.image = icon 190 | } else { 191 | icon = NSImage(named: "syncthing-bar") 192 | icon?.template = true 193 | icon?.size = size 194 | statusBarItem.image = icon 195 | } 196 | } else { 197 | icon = NSImage(named: "AppIcon") 198 | //icon?.setTemplate(true) 199 | icon?.size = size 200 | statusBarItem.image = icon 201 | } 202 | 203 | self.settings?.saveSettings() 204 | 205 | } 206 | 207 | } -------------------------------------------------------------------------------- /syncthing-bar/SettingsWindow.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 | Info: These are syncthing-bar settings to configure the menu-bar application. If you are looking for the syncthing settings, please go through the web-ui (click on "Open-UI"). 34 | 35 | 36 | 37 | 38 | 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 | 83 | 93 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 132 | 133 | 134 | 135 | 136 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /syncthing-bar/SyncthingRunner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyncthingRunner.swift 3 | // syncthing-bar 4 | // 5 | // Created by Andreas Streichardt on 13.12.14. 6 | // Copyright (c) 2014 mop. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let TooManyErrorsNotification = "koeln.mop.too-many-errors" 12 | let HttpChanged = "koeln.mop.http-changed" 13 | let FoldersDetermined = "koeln.mop.folders-determined" 14 | let SettingsSet = "koeln.mop.settings-set" 15 | let StartStop = "koeln.mop.start-stop" 16 | 17 | class SyncthingRunner: NSObject { 18 | var portFinder : PortFinder = PortFinder(startPort: 8084) 19 | var path : NSString 20 | //var path : NSString = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"binaryname"]"/Users/mop/Downloads/syncthing-macosx-amd64-v0.10.8/syncthing" 21 | var task: NSTask? 22 | var port: NSInteger? 23 | var lastFail : NSDate? 24 | var failCount : NSInteger = 0 25 | var notificationCenter: NSNotificationCenter = NSNotificationCenter.defaultCenter() 26 | var portOpenTimer : NSTimer? 27 | var repositoryCollectorTimer : NSTimer? 28 | var log : SyncthingLog 29 | var buf : NSString = NSString() 30 | var apiKey: NSString? 31 | var version: [Int]? 32 | var paused: Bool 33 | 34 | init(log: SyncthingLog) { 35 | self.paused = false 36 | self.log = log 37 | path = NSBundle.mainBundle().pathForResource("syncthing/syncthing", ofType: "")! 38 | 39 | super.init() 40 | 41 | notificationCenter.addObserver(self, selector: #selector(SyncthingRunner.taskStopped(_:)), name: NSTaskDidTerminateNotification, object: task) 42 | notificationCenter.addObserver(self, selector: #selector(SyncthingRunner.receivedOut(_:)), name: NSFileHandleDataAvailableNotification, object: nil) 43 | } 44 | 45 | func registerVersion() -> Bool { 46 | let pipe : NSPipe = NSPipe() 47 | let versionTask = NSTask() 48 | versionTask.launchPath = path as String 49 | versionTask.arguments = ["--version"] 50 | versionTask.standardOutput = pipe 51 | versionTask.launch() 52 | versionTask.waitUntilExit() 53 | 54 | let versionOut = pipe.fileHandleForReading.readDataToEndOfFile() 55 | let versionString = NSString(data: versionOut, encoding: NSUTF8StringEncoding) 56 | 57 | let regex = try? NSRegularExpression(pattern: "^syncthing v(\\d+)\\.(\\d+)\\.(\\d+)", 58 | options: []) 59 | var results = regex!.matchesInString(versionString! as String, options: [], range: NSMakeRange(0, versionString!.length)) 60 | if results.count == 1 { 61 | let major = Int((versionString?.substringWithRange(results[0].rangeAtIndex(1)))!) as Int! 62 | let minor = Int((versionString?.substringWithRange(results[0].rangeAtIndex(2)))!) as Int! 63 | let patch = Int((versionString?.substringWithRange(results[0].rangeAtIndex(3)))!) as Int! 64 | 65 | version = [ major, minor, patch ] 66 | print("Syncthing version \(version![0]) \(version![1]) \(version![2])") 67 | return true 68 | } else { 69 | return false 70 | } 71 | } 72 | 73 | func run() -> (String?) { 74 | let pipe : NSPipe = NSPipe() 75 | let readHandle = pipe.fileHandleForReading 76 | 77 | task = NSTask() 78 | task!.launchPath = path as String 79 | var environment = NSProcessInfo.processInfo().environment 80 | environment["STNORESTART"] = "1" 81 | task!.environment = environment 82 | 83 | let port = self.port! 84 | let httpData : [String: String] = ["host": "127.0.0.1", "port": String(port)]; 85 | 86 | self.apiKey = randomStringWithLength(32); 87 | 88 | task!.arguments = ["-no-browser", "-gui-address=127.0.0.1:\(port)", "-gui-apikey=\(self.apiKey!)"] 89 | task!.standardOutput = pipe 90 | readHandle.waitForDataInBackgroundAndNotify() 91 | task!.launch() 92 | 93 | 94 | // mop: wait until port is open :O 95 | portOpenTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(SyncthingRunner.checkPortOpen(_:)), userInfo: httpData, repeats: true) 96 | return nil 97 | } 98 | 99 | func receivedOut(notif : NSNotification) { 100 | // Unpack the FileHandle from the notification 101 | let fh:NSFileHandle = notif.object as! NSFileHandle 102 | // Get the data from the FileHandle 103 | let data = fh.availableData 104 | // Only deal with the data if it actually exists 105 | if data.length > 1 { 106 | // Since we just got the notification from fh, we must tell it to notify us again when it gets more data 107 | fh.waitForDataInBackgroundAndNotify() 108 | // Convert the data into a string 109 | let string = (buf as String) + (NSString(data: data, encoding: NSUTF8StringEncoding)! as String) 110 | var lines = string.componentsSeparatedByString("\n") 111 | buf = lines.removeLast() 112 | for line in lines { 113 | log.log("OUT: \(line)") 114 | } 115 | } 116 | } 117 | 118 | func ensureRunning() -> (String?) { 119 | if !registerVersion() { 120 | return "Could not determine syncthing version" 121 | } 122 | let result = portFinder.findPort() 123 | // mop: ITS GO :O ZOMG!!111 124 | if (result.err != nil) { 125 | return "Could not find a port!" 126 | } 127 | self.port = result.port 128 | let err = run() 129 | return err 130 | } 131 | 132 | // mop: copy paste :D http://stackoverflow.com/questions/26845307/generate-random-alphanumeric-string-in-swift looks good to me 133 | func randomStringWithLength (len : Int) -> NSString { 134 | let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 135 | 136 | let randomString : NSMutableString = NSMutableString(capacity: len) 137 | 138 | for _ in 1...len { 139 | let length = UInt32 (letters.length) 140 | let rand = arc4random_uniform(length) 141 | randomString.appendFormat("%C", letters.characterAtIndex(Int(rand))) 142 | } 143 | 144 | return randomString 145 | } 146 | 147 | func createRequest(path: NSString) -> NSMutableURLRequest { 148 | let url = NSURL(string: "http://localhost:\(self.port!)\(path)") 149 | let request = NSMutableURLRequest(URL: url!) 150 | request.addValue(self.apiKey! as String, forHTTPHeaderField: "X-API-Key") 151 | return request 152 | } 153 | 154 | func collectRepositories(timer: NSTimer) { 155 | // mop: jaja copy paste...must fix somewhen 156 | if (timer.userInfo as? Dictionary) != nil { 157 | let request: NSMutableURLRequest = createRequest("/rest/system/config") 158 | let idElement: NSString = "id" 159 | let labelElement: NSString = "label" 160 | let pathElement: NSString = "path" 161 | let foldersElement: NSString = "folders" 162 | 163 | NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in 164 | if (error != nil) { 165 | print("Got error collecting repositories \(error)") 166 | return; 167 | } 168 | let httpResponse = response as? NSHTTPURLResponse; 169 | if httpResponse == nil { 170 | print("Unexpected response"); 171 | return; 172 | } 173 | 174 | if httpResponse!.statusCode != 200 { 175 | print("Got non 200 HTTP Response \(httpResponse!.statusCode)"); 176 | return; 177 | } 178 | if (error == nil) { 179 | let jsonResult: NSDictionary = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary 180 | 181 | // mop: WTF am i typing :S 182 | let folders = jsonResult[foldersElement] as? Array 183 | if folders != nil { 184 | let folderStructArr = folders!.filter({(object: AnyObject) -> (Bool) in 185 | let id = object[idElement] as? String 186 | let path = object[pathElement] as? String 187 | 188 | return id != nil && path != nil 189 | }).map({(object: AnyObject) -> (SyncthingFolder) in 190 | let id = object[idElement] as? String 191 | let pathTemp = object[pathElement] as? String 192 | let path = ((pathTemp)! as NSString).stringByExpandingTildeInPath 193 | let label = object[labelElement] as? String 194 | 195 | return SyncthingFolder(id: id!, path: path, label: label!) 196 | }) 197 | 198 | let folderData = ["folders": folderStructArr] 199 | self.notificationCenter.postNotificationName(FoldersDetermined, object: self, userInfo: folderData) 200 | } else { 201 | print("Failed to parse folders :(") 202 | } 203 | } else { 204 | print("Got error collecting repositories \(error)") 205 | } 206 | } 207 | } 208 | } 209 | 210 | func checkPortOpen(timer: NSTimer) { 211 | if (timer.valid) { 212 | if let info = timer.userInfo as? Dictionary { 213 | let host = info["host"] 214 | let port = info["port"] 215 | 216 | let request = createRequest("/rest/version") 217 | 218 | NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in 219 | if (error == nil) { 220 | let httpData = ["host": host!, "port": port!] 221 | self.notificationCenter.postNotificationName(HttpChanged, object: self, userInfo: httpData) 222 | if (self.portOpenTimer!.valid) { 223 | self.portOpenTimer!.invalidate() 224 | } 225 | self.repositoryCollectorTimer = NSTimer.scheduledTimerWithTimeInterval(10.0, target: self, selector: #selector(SyncthingRunner.collectRepositories(_:)), userInfo: info, repeats: true) 226 | self.repositoryCollectorTimer!.fire() 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | func taskStopped(sender: AnyObject) { 234 | let task = sender.object as! NSTask 235 | if (task != self.task) { 236 | return 237 | } 238 | 239 | self.notificationCenter.postNotificationName(HttpChanged, object: self) 240 | 241 | if (self.paused) { 242 | // ctp: DO NOT attempt restart when paused ... 243 | return 244 | } 245 | 246 | stopTimers() 247 | 248 | let current = NSDate() 249 | // mop: retry 5 times :S 250 | if (lastFail != nil) { 251 | let timeDiff = current.timeIntervalSinceDate(lastFail!) 252 | if (timeDiff > 5) { 253 | failCount = 0 254 | } else if (failCount <= 5) { 255 | failCount += 1 256 | } else { 257 | notificationCenter.postNotificationName(TooManyErrorsNotification, object: self) 258 | print("Too many errors. Stopping") 259 | return 260 | } 261 | } 262 | lastFail = current 263 | run() 264 | } 265 | 266 | func stopTimers() { 267 | if (portOpenTimer != nil && portOpenTimer!.valid) { 268 | portOpenTimer!.invalidate() 269 | } 270 | 271 | if (repositoryCollectorTimer != nil) { 272 | if (repositoryCollectorTimer!.valid) { 273 | repositoryCollectorTimer!.invalidate() 274 | } 275 | } 276 | } 277 | 278 | func pause() { 279 | if (self.paused) { 280 | return 281 | } 282 | 283 | self.paused = true 284 | self.stop() 285 | } 286 | 287 | func play() { 288 | if (!self.paused) { 289 | return 290 | } 291 | 292 | self.paused = false 293 | self.run() 294 | } 295 | 296 | func stop() { 297 | if (task != nil) { 298 | task!.terminate(); 299 | } 300 | stopTimers() 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /syncthing-bar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2B403DDA1D09C1F900A4AB93 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B403DD91D09C1F900A4AB93 /* IOKit.framework */; }; 11 | 2B403DDF1D09C28E00A4AB93 /* BatteryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B403DDE1D09C28E00A4AB93 /* BatteryMonitor.swift */; }; 12 | 830680AC1B1C43D100B0B7A7 /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830680AB1B1C43D100B0B7A7 /* SettingsWindowController.swift */; }; 13 | 830680AE1B1C448700B0B7A7 /* SettingsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 830680AD1B1C448700B0B7A7 /* SettingsWindow.xib */; }; 14 | 830680B01B1C801700B0B7A7 /* SyncthingSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 830680AF1B1C801700B0B7A7 /* SyncthingSettings.swift */; }; 15 | 8310FB891B1F3DB500225E47 /* MonitorRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8310FB881B1F3DB500225E47 /* MonitorRunner.swift */; }; 16 | F11BC9B11A3E20CA00360C2E /* syncthing in Resources */ = {isa = PBXBuildFile; fileRef = F11BC9B01A3E20CA00360C2E /* syncthing */; }; 17 | F11BC9B31A3E45E100360C2E /* SyncthingFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11BC9B21A3E45E100360C2E /* SyncthingFolder.swift */; }; 18 | F14B62FC1B39E2B90022C08F /* syncthing-bar.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F1A2BD421A6178620014473E /* syncthing-bar.pdf */; }; 19 | F14B62FD1B39E2F60022C08F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F1A4E42B1A4587C300602986 /* Main.storyboard */; }; 20 | F1B0205F1B39D057006BC805 /* SyncthingBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B0205E1B39D057006BC805 /* SyncthingBarTests.swift */; }; 21 | F1B020621B39E269006BC805 /* SyncthingBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1EE75F91A3D76B70028E6DD /* SyncthingBar.swift */; }; 22 | F1B099071A3C40AD00B79D31 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B099061A3C40AD00B79D31 /* AppDelegate.swift */; }; 23 | F1B0990B1A3C40AD00B79D31 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F1B0990A1A3C40AD00B79D31 /* Images.xcassets */; }; 24 | F1B099281A3C4FA800B79D31 /* LogWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B099271A3C4FA700B79D31 /* LogWindowController.swift */; }; 25 | F1B0992A1A3C764400B79D31 /* SyncthingRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B099291A3C764400B79D31 /* SyncthingRunner.swift */; }; 26 | F1B0992C1A3C77FC00B79D31 /* PortFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B0992B1A3C77FC00B79D31 /* PortFinder.swift */; }; 27 | F1BEE1461B185310001B5F8E /* LogWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = F1F8A6371A4016180049AFC1 /* LogWindow.xib */; }; 28 | F1C594E51A3ED8830048CF9F /* SyncthingLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C594E41A3ED8830048CF9F /* SyncthingLog.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | F1B099141A3C40AE00B79D31 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = F1B098F91A3C40AD00B79D31 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = F1B099001A3C40AD00B79D31; 37 | remoteInfo = "syncthing-bar"; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 2B403DD91D09C1F900A4AB93 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 43 | 2B403DDB1D09C25B00A4AB93 /* syncthing-bar-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "syncthing-bar-Bridging-Header.h"; sourceTree = ""; }; 44 | 2B403DDE1D09C28E00A4AB93 /* BatteryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryMonitor.swift; sourceTree = ""; }; 45 | 830680AB1B1C43D100B0B7A7 /* SettingsWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsWindowController.swift; sourceTree = ""; }; 46 | 830680AD1B1C448700B0B7A7 /* SettingsWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsWindow.xib; sourceTree = ""; }; 47 | 830680AF1B1C801700B0B7A7 /* SyncthingSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingSettings.swift; sourceTree = ""; }; 48 | 8310FB881B1F3DB500225E47 /* MonitorRunner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonitorRunner.swift; sourceTree = ""; }; 49 | F11BC9B01A3E20CA00360C2E /* syncthing */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = syncthing; sourceTree = ""; }; 50 | F11BC9B21A3E45E100360C2E /* SyncthingFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingFolder.swift; sourceTree = ""; }; 51 | F1A2BD421A6178620014473E /* syncthing-bar.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "syncthing-bar.pdf"; sourceTree = ""; }; 52 | F1A4E42C1A4587C300602986 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "syncthing-bar/Base.lproj/Main.storyboard"; sourceTree = ""; }; 53 | F1B0205E1B39D057006BC805 /* SyncthingBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingBarTests.swift; sourceTree = ""; }; 54 | F1B099011A3C40AD00B79D31 /* syncthing-bar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "syncthing-bar.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | F1B099051A3C40AD00B79D31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | F1B099061A3C40AD00B79D31 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | F1B0990A1A3C40AD00B79D31 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 58 | F1B099131A3C40AE00B79D31 /* syncthing-barTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "syncthing-barTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | F1B099181A3C40AE00B79D31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | F1B099271A3C4FA700B79D31 /* LogWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogWindowController.swift; sourceTree = ""; }; 61 | F1B099291A3C764400B79D31 /* SyncthingRunner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingRunner.swift; sourceTree = ""; }; 62 | F1B0992B1A3C77FC00B79D31 /* PortFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PortFinder.swift; sourceTree = ""; }; 63 | F1C594E41A3ED8830048CF9F /* SyncthingLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingLog.swift; sourceTree = ""; }; 64 | F1EE75F91A3D76B70028E6DD /* SyncthingBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncthingBar.swift; sourceTree = ""; }; 65 | F1F8A6371A4016180049AFC1 /* LogWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LogWindow.xib; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | F1B098FE1A3C40AD00B79D31 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | 2B403DDA1D09C1F900A4AB93 /* IOKit.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | F1B099101A3C40AE00B79D31 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | F11BC9AF1A3E202700360C2E /* binary */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | F11BC9B01A3E20CA00360C2E /* syncthing */, 91 | ); 92 | name = binary; 93 | sourceTree = ""; 94 | }; 95 | F1B098F81A3C40AD00B79D31 = { 96 | isa = PBXGroup; 97 | children = ( 98 | 2B403DD91D09C1F900A4AB93 /* IOKit.framework */, 99 | F1F6D6371A41E58400E972D0 /* Resources */, 100 | F11BC9AF1A3E202700360C2E /* binary */, 101 | F1B099031A3C40AD00B79D31 /* syncthing-bar */, 102 | F1B099161A3C40AE00B79D31 /* syncthing-barTests */, 103 | F1B099021A3C40AD00B79D31 /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | F1B099021A3C40AD00B79D31 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | F1B099011A3C40AD00B79D31 /* syncthing-bar.app */, 111 | F1B099131A3C40AE00B79D31 /* syncthing-barTests.xctest */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | F1B099031A3C40AD00B79D31 /* syncthing-bar */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | F1A4E42B1A4587C300602986 /* Main.storyboard */, 120 | F1B099061A3C40AD00B79D31 /* AppDelegate.swift */, 121 | F1B0990A1A3C40AD00B79D31 /* Images.xcassets */, 122 | F1B099041A3C40AD00B79D31 /* Supporting Files */, 123 | F1B099271A3C4FA700B79D31 /* LogWindowController.swift */, 124 | F1B099291A3C764400B79D31 /* SyncthingRunner.swift */, 125 | F1B0992B1A3C77FC00B79D31 /* PortFinder.swift */, 126 | F1EE75F91A3D76B70028E6DD /* SyncthingBar.swift */, 127 | F11BC9B21A3E45E100360C2E /* SyncthingFolder.swift */, 128 | F1C594E41A3ED8830048CF9F /* SyncthingLog.swift */, 129 | F1F8A6371A4016180049AFC1 /* LogWindow.xib */, 130 | 830680AD1B1C448700B0B7A7 /* SettingsWindow.xib */, 131 | 830680AB1B1C43D100B0B7A7 /* SettingsWindowController.swift */, 132 | 830680AF1B1C801700B0B7A7 /* SyncthingSettings.swift */, 133 | 8310FB881B1F3DB500225E47 /* MonitorRunner.swift */, 134 | 2B403DDB1D09C25B00A4AB93 /* syncthing-bar-Bridging-Header.h */, 135 | 2B403DDE1D09C28E00A4AB93 /* BatteryMonitor.swift */, 136 | ); 137 | path = "syncthing-bar"; 138 | sourceTree = ""; 139 | }; 140 | F1B099041A3C40AD00B79D31 /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | F1B099051A3C40AD00B79D31 /* Info.plist */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | F1B099161A3C40AE00B79D31 /* syncthing-barTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | F1B099171A3C40AE00B79D31 /* Supporting Files */, 152 | F1B0205E1B39D057006BC805 /* SyncthingBarTests.swift */, 153 | ); 154 | path = "syncthing-barTests"; 155 | sourceTree = ""; 156 | }; 157 | F1B099171A3C40AE00B79D31 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | F1B099181A3C40AE00B79D31 /* Info.plist */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | F1F6D6371A41E58400E972D0 /* Resources */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | F1A2BD421A6178620014473E /* syncthing-bar.pdf */, 169 | ); 170 | path = Resources; 171 | sourceTree = ""; 172 | }; 173 | /* End PBXGroup section */ 174 | 175 | /* Begin PBXNativeTarget section */ 176 | F1B099001A3C40AD00B79D31 /* syncthing-bar */ = { 177 | isa = PBXNativeTarget; 178 | buildConfigurationList = F1B0991D1A3C40AE00B79D31 /* Build configuration list for PBXNativeTarget "syncthing-bar" */; 179 | buildPhases = ( 180 | F1B098FD1A3C40AD00B79D31 /* Sources */, 181 | F1B098FE1A3C40AD00B79D31 /* Frameworks */, 182 | F1B098FF1A3C40AD00B79D31 /* Resources */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | ); 188 | name = "syncthing-bar"; 189 | productName = "syncthing-bar"; 190 | productReference = F1B099011A3C40AD00B79D31 /* syncthing-bar.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | F1B099121A3C40AE00B79D31 /* syncthing-barTests */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = F1B099201A3C40AE00B79D31 /* Build configuration list for PBXNativeTarget "syncthing-barTests" */; 196 | buildPhases = ( 197 | F1B0990F1A3C40AE00B79D31 /* Sources */, 198 | F1B099101A3C40AE00B79D31 /* Frameworks */, 199 | F1B099111A3C40AE00B79D31 /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | F1B099151A3C40AE00B79D31 /* PBXTargetDependency */, 205 | ); 206 | name = "syncthing-barTests"; 207 | productName = "syncthing-barTests"; 208 | productReference = F1B099131A3C40AE00B79D31 /* syncthing-barTests.xctest */; 209 | productType = "com.apple.product-type.bundle.unit-test"; 210 | }; 211 | /* End PBXNativeTarget section */ 212 | 213 | /* Begin PBXProject section */ 214 | F1B098F91A3C40AD00B79D31 /* Project object */ = { 215 | isa = PBXProject; 216 | attributes = { 217 | LastSwiftMigration = 0710; 218 | LastSwiftUpdateCheck = 0730; 219 | LastUpgradeCheck = 0710; 220 | ORGANIZATIONNAME = mop; 221 | TargetAttributes = { 222 | F1B099001A3C40AD00B79D31 = { 223 | CreatedOnToolsVersion = 6.1; 224 | }; 225 | F1B099121A3C40AE00B79D31 = { 226 | CreatedOnToolsVersion = 6.1; 227 | TestTargetID = F1B099001A3C40AD00B79D31; 228 | }; 229 | }; 230 | }; 231 | buildConfigurationList = F1B098FC1A3C40AD00B79D31 /* Build configuration list for PBXProject "syncthing-bar" */; 232 | compatibilityVersion = "Xcode 3.2"; 233 | developmentRegion = English; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | en, 237 | Base, 238 | ); 239 | mainGroup = F1B098F81A3C40AD00B79D31; 240 | productRefGroup = F1B099021A3C40AD00B79D31 /* Products */; 241 | projectDirPath = ""; 242 | projectRoot = ""; 243 | targets = ( 244 | F1B099001A3C40AD00B79D31 /* syncthing-bar */, 245 | F1B099121A3C40AE00B79D31 /* syncthing-barTests */, 246 | ); 247 | }; 248 | /* End PBXProject section */ 249 | 250 | /* Begin PBXResourcesBuildPhase section */ 251 | F1B098FF1A3C40AD00B79D31 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | F11BC9B11A3E20CA00360C2E /* syncthing in Resources */, 256 | 830680AE1B1C448700B0B7A7 /* SettingsWindow.xib in Resources */, 257 | F14B62FC1B39E2B90022C08F /* syncthing-bar.pdf in Resources */, 258 | F14B62FD1B39E2F60022C08F /* Main.storyboard in Resources */, 259 | F1BEE1461B185310001B5F8E /* LogWindow.xib in Resources */, 260 | F1B0990B1A3C40AD00B79D31 /* Images.xcassets in Resources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | F1B099111A3C40AE00B79D31 /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | /* End PBXResourcesBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | F1B098FD1A3C40AD00B79D31 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 8310FB891B1F3DB500225E47 /* MonitorRunner.swift in Sources */, 279 | F1B0992C1A3C77FC00B79D31 /* PortFinder.swift in Sources */, 280 | 2B403DDF1D09C28E00A4AB93 /* BatteryMonitor.swift in Sources */, 281 | F1B0992A1A3C764400B79D31 /* SyncthingRunner.swift in Sources */, 282 | F1B099281A3C4FA800B79D31 /* LogWindowController.swift in Sources */, 283 | F1B099071A3C40AD00B79D31 /* AppDelegate.swift in Sources */, 284 | F1C594E51A3ED8830048CF9F /* SyncthingLog.swift in Sources */, 285 | 830680B01B1C801700B0B7A7 /* SyncthingSettings.swift in Sources */, 286 | 830680AC1B1C43D100B0B7A7 /* SettingsWindowController.swift in Sources */, 287 | F11BC9B31A3E45E100360C2E /* SyncthingFolder.swift in Sources */, 288 | F1B020621B39E269006BC805 /* SyncthingBar.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | F1B0990F1A3C40AE00B79D31 /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | F1B0205F1B39D057006BC805 /* SyncthingBarTests.swift in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXTargetDependency section */ 303 | F1B099151A3C40AE00B79D31 /* PBXTargetDependency */ = { 304 | isa = PBXTargetDependency; 305 | target = F1B099001A3C40AD00B79D31 /* syncthing-bar */; 306 | targetProxy = F1B099141A3C40AE00B79D31 /* PBXContainerItemProxy */; 307 | }; 308 | /* End PBXTargetDependency section */ 309 | 310 | /* Begin PBXVariantGroup section */ 311 | F1A4E42B1A4587C300602986 /* Main.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | F1A4E42C1A4587C300602986 /* Base */, 315 | ); 316 | name = Main.storyboard; 317 | path = ..; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | F1B0991B1A3C40AE00B79D31 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | CODE_SIGN_IDENTITY = ""; 341 | COPY_PHASE_STRIP = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | ENABLE_TESTABILITY = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_DYNAMIC_NO_PIC = NO; 346 | GCC_OPTIMIZATION_LEVEL = 0; 347 | GCC_PREPROCESSOR_DEFINITIONS = ( 348 | "DEBUG=1", 349 | "$(inherited)", 350 | ); 351 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | MACOSX_DEPLOYMENT_TARGET = 10.10; 359 | MTL_ENABLE_DEBUG_INFO = YES; 360 | ONLY_ACTIVE_ARCH = YES; 361 | SDKROOT = macosx; 362 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 363 | }; 364 | name = Debug; 365 | }; 366 | F1B0991C1A3C40AE00B79D31 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 371 | CLANG_CXX_LIBRARY = "libc++"; 372 | CLANG_ENABLE_MODULES = YES; 373 | CLANG_ENABLE_OBJC_ARC = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 377 | CLANG_WARN_EMPTY_BODY = YES; 378 | CLANG_WARN_ENUM_CONVERSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 381 | CLANG_WARN_UNREACHABLE_CODE = YES; 382 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 383 | CODE_SIGN_IDENTITY = ""; 384 | COPY_PHASE_STRIP = YES; 385 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 386 | ENABLE_NS_ASSERTIONS = NO; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | ENABLE_TESTABILITY = NO; 389 | GCC_C_LANGUAGE_STANDARD = gnu99; 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | MACOSX_DEPLOYMENT_TARGET = 10.10; 397 | MTL_ENABLE_DEBUG_INFO = NO; 398 | SDKROOT = macosx; 399 | }; 400 | name = Release; 401 | }; 402 | F1B0991E1A3C40AE00B79D31 /* Debug */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 406 | CLANG_ENABLE_MODULES = YES; 407 | COMBINE_HIDPI_IMAGES = YES; 408 | INFOPLIST_FILE = "syncthing-bar/Info.plist"; 409 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 410 | MACOSX_DEPLOYMENT_TARGET = 10.10; 411 | PRODUCT_BUNDLE_IDENTIFIER = "koeln.mop.$(PRODUCT_NAME:rfc1034identifier)"; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | SWIFT_OBJC_BRIDGING_HEADER = "syncthing-bar/syncthing-bar-Bridging-Header.h"; 414 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 415 | }; 416 | name = Debug; 417 | }; 418 | F1B0991F1A3C40AE00B79D31 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 422 | CLANG_ENABLE_MODULES = YES; 423 | COMBINE_HIDPI_IMAGES = YES; 424 | INFOPLIST_FILE = "syncthing-bar/Info.plist"; 425 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 426 | MACOSX_DEPLOYMENT_TARGET = 10.10; 427 | PRODUCT_BUNDLE_IDENTIFIER = "koeln.mop.$(PRODUCT_NAME:rfc1034identifier)"; 428 | PRODUCT_NAME = "$(TARGET_NAME)"; 429 | SWIFT_OBJC_BRIDGING_HEADER = "syncthing-bar/syncthing-bar-Bridging-Header.h"; 430 | }; 431 | name = Release; 432 | }; 433 | F1B099211A3C40AE00B79D31 /* Debug */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | BUNDLE_LOADER = "$(TEST_HOST)"; 437 | COMBINE_HIDPI_IMAGES = YES; 438 | FRAMEWORK_SEARCH_PATHS = ( 439 | "$(DEVELOPER_FRAMEWORKS_DIR)", 440 | "$(inherited)", 441 | ); 442 | GCC_PREPROCESSOR_DEFINITIONS = ( 443 | "DEBUG=1", 444 | "$(inherited)", 445 | ); 446 | INFOPLIST_FILE = "syncthing-barTests/Info.plist"; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 448 | PRODUCT_BUNDLE_IDENTIFIER = "koeln.mop.$(PRODUCT_NAME:rfc1034identifier)"; 449 | PRODUCT_NAME = "$(TARGET_NAME)"; 450 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/syncthing-bar.app/Contents/MacOS/syncthing-bar"; 451 | }; 452 | name = Debug; 453 | }; 454 | F1B099221A3C40AE00B79D31 /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | BUNDLE_LOADER = "$(TEST_HOST)"; 458 | COMBINE_HIDPI_IMAGES = YES; 459 | FRAMEWORK_SEARCH_PATHS = ( 460 | "$(DEVELOPER_FRAMEWORKS_DIR)", 461 | "$(inherited)", 462 | ); 463 | INFOPLIST_FILE = "syncthing-barTests/Info.plist"; 464 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 465 | PRODUCT_BUNDLE_IDENTIFIER = "koeln.mop.$(PRODUCT_NAME:rfc1034identifier)"; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/syncthing-bar.app/Contents/MacOS/syncthing-bar"; 468 | }; 469 | name = Release; 470 | }; 471 | /* End XCBuildConfiguration section */ 472 | 473 | /* Begin XCConfigurationList section */ 474 | F1B098FC1A3C40AD00B79D31 /* Build configuration list for PBXProject "syncthing-bar" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | F1B0991B1A3C40AE00B79D31 /* Debug */, 478 | F1B0991C1A3C40AE00B79D31 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | F1B0991D1A3C40AE00B79D31 /* Build configuration list for PBXNativeTarget "syncthing-bar" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | F1B0991E1A3C40AE00B79D31 /* Debug */, 487 | F1B0991F1A3C40AE00B79D31 /* Release */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | F1B099201A3C40AE00B79D31 /* Build configuration list for PBXNativeTarget "syncthing-barTests" */ = { 493 | isa = XCConfigurationList; 494 | buildConfigurations = ( 495 | F1B099211A3C40AE00B79D31 /* Debug */, 496 | F1B099221A3C40AE00B79D31 /* Release */, 497 | ); 498 | defaultConfigurationIsVisible = 0; 499 | defaultConfigurationName = Release; 500 | }; 501 | /* End XCConfigurationList section */ 502 | }; 503 | rootObject = F1B098F91A3C40AD00B79D31 /* Project object */; 504 | } 505 | -------------------------------------------------------------------------------- /syncthing-bar/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 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | Default 511 | 512 | 513 | 514 | 515 | 516 | 517 | Left to Right 518 | 519 | 520 | 521 | 522 | 523 | 524 | Right to Left 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | Default 536 | 537 | 538 | 539 | 540 | 541 | 542 | Left to Right 543 | 544 | 545 | 546 | 547 | 548 | 549 | Right to Left 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 | 647 | 648 | 649 | 650 | 651 | 652 | --------------------------------------------------------------------------------