Merge branch 'pr/159' into develop

# Conflicts:
#	ShadowsocksX-NG/Base.lproj/MainMenu.xib
This commit is contained in:
Charlie Qiu
2017-01-10 15:23:13 +08:00
45 changed files with 1428 additions and 787 deletions

View File

@ -7,7 +7,7 @@
//
import Cocoa
import Carbon
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
@ -18,7 +18,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
var proxyPreferencesWinCtrl: ProxyPreferencesController!
var editUserRulesWinCtrl: UserRulesController!
var httpPreferencesWinCtrl : HTTPPreferencesWindowController!
let keyCode = kVK_ANSI_P
let modifierKeys = cmdKey+controlKey
var hotKeyRef: EventHotKeyRef?
var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController()
@IBOutlet weak var window: NSWindow!
@ -41,7 +45,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
@IBOutlet var serversPreferencesMenuItem: NSMenuItem!
@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!
@IBOutlet weak var hudWindow: NSPanel!
@IBOutlet weak var panelView: NSView!
@IBOutlet weak var isNameTextField: NSTextField!
let kHudFadeInDuration: Double = 0.25
let kHudFadeOutDuration: Double = 0.5
let kHudDisplayDuration: Double = 2.0
let kHudAlphaValue: CGFloat = 0.75
let kHudCornerRadius: CGFloat = 18.0
let kHudHorizontalMargin: CGFloat = 30
let kHudHeight: CGFloat = 90.0
var timerToFadeOut: Timer? = nil
var fadingOut: Bool = false
var statusItem: NSStatusItem!
static let StatusItemIconWidth:CGFloat = 20
@ -137,8 +157,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
userNote.subtitle = "By Handle SS URL".localized
}
userNote.informativeText = "Host: \(profile.serverHost)"
" Port: \(profile.serverPort)"
" Encription Method: \(profile.method)".localized
//" Port: \(profile.serverPort)"
//" Encription Method: \(profile.method)".localized
userNote.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default
@ -167,6 +187,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.startMonitorPAC()
applyConfig()
SyncSSLocal()
// Register global hotkey
registerHotkey()
}
func applicationWillTerminate(_ aNotification: Notification) {
@ -174,8 +197,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
StopSSLocal()
StopPrivoxy()
ProxyConfHelper.disableProxy()
if let ref = hotKeyRef { UnregisterEventHotKey(ref) }
}
func applyConfig() {
let defaults = UserDefaults.standard
let isOn = defaults.bool(forKey: "ShadowsocksOn")
@ -197,7 +221,64 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.disableProxy()
}
}
// MARK: - Hotkey Methods
func registerHotkey() -> Void {
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.signature = OSType(fourCharCodeFrom(string: "sxng"))
gMyHotKeyID.id = UInt32(keyCode)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyPressed)
// Void pointer to `self`:
let context = Unmanaged.passUnretained(self).toOpaque()
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userContext) -> OSStatus in
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<AppDelegate>.fromOpaque(userContext!).takeUnretainedValue()
switch Globals.proxyType {
case .pac:
Globals.proxyType = .global
UserDefaults.standard.setValue("global", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Gobal Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
case .global:
Globals.proxyType = .pac
UserDefaults.standard.setValue("auto", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Auto Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
}
mySelf.fadeInHud()
return noErr
}, 1, &eventType, context, nil)
// Register hotkey.
RegisterEventHotKey(UInt32(keyCode),
UInt32(modifierKeys),
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
}
func fourCharCodeFrom(string: String) -> FourCharCode {
assert(string.characters.count == 4, "String length must be 4")
var result: FourCharCode = 0
for char in string.utf16 {
result = (result << 8) + FourCharCode(char)
}
return result
}
// MARK: - UI Methods
@IBAction func toggleRunning(_ sender: NSMenuItem) {
let defaults = UserDefaults.standard
var isOn = defaults.bool(forKey: "ShadowsocksOn")
@ -531,3 +612,93 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
}
}
extension AppDelegate {
func fadeInHud() -> Void {
if timerToFadeOut != nil {
timerToFadeOut?.invalidate()
timerToFadeOut = nil
}
fadingOut = false
hudWindow.orderFrontRegardless()
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeInDuration)
CATransaction.setCompletionBlock { self.didFadeIn() }
panelView.layer?.opacity = 1.0
CATransaction.commit()
}
func didFadeIn() -> Void {
timerToFadeOut = Timer.scheduledTimer(
timeInterval: kHudDisplayDuration,
target: self,
selector: #selector(fadeOutHud),
userInfo: nil,
repeats: false)
}
func fadeOutHud() -> Void {
fadingOut = true
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeOutDuration)
CATransaction.setCompletionBlock { self.didFadeOut() }
panelView.layer?.opacity = 0.0
CATransaction.commit()
}
func didFadeOut() -> Void {
if fadingOut {
self.hudWindow.orderOut(nil)
}
fadingOut = false
}
func setupHud() -> Void {
isNameTextField.stringValue = "Global Mode"
isNameTextField.sizeToFit()
var labelFrame: CGRect = isNameTextField.frame
var hudWindowFrame: CGRect = hudWindow.frame
hudWindowFrame.size.width = labelFrame.size.width + kHudHorizontalMargin * 2
hudWindowFrame.size.height = kHudHeight
let screenRect: NSRect = NSScreen.screens()![0].visibleFrame
hudWindowFrame.origin.x = (screenRect.size.width - hudWindowFrame.size.width) / 2
hudWindowFrame.origin.y = (screenRect.size.height - hudWindowFrame.size.height) / 2
hudWindow.setFrame(hudWindowFrame, display: true)
var viewFrame: NSRect = hudWindowFrame;
viewFrame.origin.x = 0
viewFrame.origin.y = 0
panelView.frame = viewFrame
labelFrame.origin.x = kHudHorizontalMargin
labelFrame.origin.y = (hudWindowFrame.size.height - labelFrame.size.height) / 2
isNameTextField.frame = labelFrame
}
func initUIComponent() -> Void {
hudWindow.isOpaque = false
hudWindow.backgroundColor = .clear
hudWindow.level = Int(CGWindowLevelForKey(.utilityWindow)) + 1000
hudWindow.styleMask = .borderless
hudWindow.hidesOnDeactivate = false
hudWindow.collectionBehavior = .canJoinAllSpaces
let viewLayer: CALayer = CALayer()
viewLayer.backgroundColor = CGColor.init(red: 0.05, green: 0.05, blue: 0.05, alpha: kHudAlphaValue)
viewLayer.cornerRadius = kHudCornerRadius
panelView.wantsLayer = true
panelView.layer = viewLayer
panelView.layer?.opacity = 0.0
setupHud()
}
override func awakeFromNib() {
initUIComponent()
}
}

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="16B2548a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -14,13 +15,15 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ShadowsocksX_NG" customModuleProvider="target">
<connections>
<outlet property="RunningStatusMenuItem" destination="fzk-mE-CEV" id="R4x-gK-Qcw"/>
<outlet property="autoModeMenuItem" destination="r07-Gu-aEz" id="9aH-pQ-Rgi"/>
<outlet property="exportAllServerProfileItem" destination="6k0-gn-DQv" id="W2x-96-ISj"/>
<outlet property="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/>
<outlet property="importBunchJsonFileItem" destination="T9g-gy-gvv" id="vua-jg-YWe"/>
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="q3D-7x-0db"/>
<outlet property="hudWindow" destination="QWV-F6-ac1" id="K6D-a4-oqE"/>
<outlet property="isNameTextField" destination="rUN-Nq-HDb" id="ayK-aM-rSa"/>
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
<outlet property="panelView" destination="rai-SH-9tZ" id="5aU-ld-rWb"/>
<outlet property="proxyMenuItem" destination="diI-fB-Rss" id="Qjk-9U-3Qy"/>
<outlet property="runningStatusMenuItem" destination="fzk-mE-CEV" id="Vwm-Rg-Ykn"/>
<outlet property="scanQRCodeMenuItem" destination="Qe6-bF-paT" id="XHa-pa-nCa"/>
@ -185,7 +188,7 @@
</connections>
</menuItem>
</items>
<point key="canvasLocation" x="22" y="89"/>
<point key="canvasLocation" x="-2367" y="-139"/>
</menu>
<menu id="2oY-e9-q1Z">
<items>
@ -199,6 +202,30 @@
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
<point key="canvasLocation" x="-2113" y="-132"/>
</menu>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="QWV-F6-ac1" customClass="NSPanel">
<windowStyleMask key="styleMask" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<rect key="contentRect" x="139" y="81" width="200" height="100"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<value key="minSize" type="size" width="200" height="100"/>
<value key="maxSize" type="size" width="200" height="100"/>
<view key="contentView" id="rai-SH-9tZ" userLabel="Panel View">
<rect key="frame" x="0.0" y="0.0" width="200" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="162" translatesAutoresizingMaskIntoConstraints="NO" id="rUN-Nq-HDb" userLabel="Is Name">
<rect key="frame" x="17" y="33" width="166" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="Label" usesSingleLineMode="YES" id="jcH-j9-Xl3">
<font key="font" metaFont="system" size="24"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="alternateSelectedControlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<point key="canvasLocation" x="-2357" y="172"/>
</window>
</objects>
</document>

View File

@ -51,7 +51,7 @@ class ServerProfileManager: NSObject {
if activeProfileId != nil {
defaults.set(activeProfileId, forKey: "ActiveServerProfileId")
writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!)
_ = writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!)
} else {
defaults.removeObject(forKey: "ActiveServerProfileId")
removeSSLocalConfFile()

View File

@ -23,3 +23,12 @@ extension Data {
return hexBytes.joined(separator: "")
}
}
enum ProxyType {
case pac
case global
}
struct Globals {
static var proxyType = ProxyType.pac
}

View File

@ -44,9 +44,9 @@
"Turn Shadowsocks On" = "打开 Shadowsocks";
"Proxy - Auto By PAC" = "代理 - PAC自动";
"Proxy - Auto By PAC" = "代理 - PAC自动⌃⌘P";
"Proxy - Global" = "代理 - 全局";
"Proxy - Global" = "代理 - 全局⌃⌘P";
"Proxy - Manual" = "代理 - 手动";