Merge branch 'pr/95' into develop
This commit is contained in:
@ -35,6 +35,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
@IBOutlet weak var serversMenuItem: NSMenuItem!
|
@IBOutlet weak var serversMenuItem: NSMenuItem!
|
||||||
@IBOutlet var showQRCodeMenuItem: NSMenuItem!
|
@IBOutlet var showQRCodeMenuItem: NSMenuItem!
|
||||||
@IBOutlet var scanQRCodeMenuItem: NSMenuItem!
|
@IBOutlet var scanQRCodeMenuItem: NSMenuItem!
|
||||||
|
@IBOutlet var showBunchJsonExampleFileItem: NSMenuItem!
|
||||||
|
@IBOutlet var importBunchJsonFileItem: NSMenuItem!
|
||||||
|
@IBOutlet var exportAllServerProfileItem: NSMenuItem!
|
||||||
@IBOutlet var serversPreferencesMenuItem: NSMenuItem!
|
@IBOutlet var serversPreferencesMenuItem: NSMenuItem!
|
||||||
|
|
||||||
@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!
|
@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!
|
||||||
@ -255,7 +258,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
ScanQRCodeOnScreen()
|
ScanQRCodeOnScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleLaunghAtLogin(_ sender: NSMenuItem) {
|
@IBAction func showBunchJsonExampleFile(sender: NSMenuItem) {
|
||||||
|
ServerProfileManager.showExampleConfigFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func importBunchJsonFile(sender: NSMenuItem) {
|
||||||
|
ServerProfileManager.instance.importConfigFile()
|
||||||
|
//updateServersMenu()//not working
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func exportAllServerProfile(sender: NSMenuItem) {
|
||||||
|
ServerProfileManager.instance.exportConfigFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func toggleLaunghAtLogin(sender: NSMenuItem) {
|
||||||
launchAtLoginController.launchAtLogin = !launchAtLoginController.launchAtLogin;
|
launchAtLoginController.launchAtLogin = !launchAtLoginController.launchAtLogin;
|
||||||
updateLaunchAtLoginMenu()
|
updateLaunchAtLoginMenu()
|
||||||
}
|
}
|
||||||
@ -457,6 +473,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
let showQRItem = showQRCodeMenuItem
|
let showQRItem = showQRCodeMenuItem
|
||||||
let scanQRItem = scanQRCodeMenuItem
|
let scanQRItem = scanQRCodeMenuItem
|
||||||
let preferencesItem = serversPreferencesMenuItem
|
let preferencesItem = serversPreferencesMenuItem
|
||||||
|
let showBunch = showBunchJsonExampleFileItem
|
||||||
|
let importBuntch = importBunchJsonFileItem
|
||||||
|
let exportAllServer = exportAllServerProfileItem
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
for p in mgr.profiles {
|
for p in mgr.profiles {
|
||||||
@ -483,6 +502,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
}
|
}
|
||||||
serversMenuItem.submenu?.addItem(showQRItem!)
|
serversMenuItem.submenu?.addItem(showQRItem!)
|
||||||
serversMenuItem.submenu?.addItem(scanQRItem!)
|
serversMenuItem.submenu?.addItem(scanQRItem!)
|
||||||
|
serversMenuItem.submenu?.addItem(showBunch!)
|
||||||
|
serversMenuItem.submenu?.addItem(importBuntch!)
|
||||||
|
serversMenuItem.submenu?.addItem(exportAllServer!)
|
||||||
serversMenuItem.submenu?.addItem(NSMenuItem.separator())
|
serversMenuItem.submenu?.addItem(NSMenuItem.separator())
|
||||||
serversMenuItem.submenu?.addItem(preferencesItem!)
|
serversMenuItem.submenu?.addItem(preferencesItem!)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="16B2548a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
|
||||||
@ -16,14 +16,17 @@
|
|||||||
<connections>
|
<connections>
|
||||||
<outlet property="RunningStatusMenuItem" destination="fzk-mE-CEV" id="R4x-gK-Qcw"/>
|
<outlet property="RunningStatusMenuItem" destination="fzk-mE-CEV" id="R4x-gK-Qcw"/>
|
||||||
<outlet property="autoModeMenuItem" destination="r07-Gu-aEz" id="9aH-pQ-Rgi"/>
|
<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="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/>
|
||||||
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="w4p-0c-DZn"/>
|
<outlet property="importBunchJsonFileItem" destination="T9g-gy-gvv" id="vua-jg-YWe"/>
|
||||||
|
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="q3D-7x-0db"/>
|
||||||
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
|
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
|
||||||
<outlet property="proxyMenuItem" destination="diI-fB-Rss" id="Qjk-9U-3Qy"/>
|
<outlet property="proxyMenuItem" destination="diI-fB-Rss" id="Qjk-9U-3Qy"/>
|
||||||
<outlet property="runningStatusMenuItem" destination="fzk-mE-CEV" id="Vwm-Rg-Ykn"/>
|
<outlet property="runningStatusMenuItem" destination="fzk-mE-CEV" id="Vwm-Rg-Ykn"/>
|
||||||
<outlet property="scanQRCodeMenuItem" destination="Qe6-bF-paT" id="XHa-pa-nCa"/>
|
<outlet property="scanQRCodeMenuItem" destination="Qe6-bF-paT" id="XHa-pa-nCa"/>
|
||||||
<outlet property="serversMenuItem" destination="u5M-hQ-VSc" id="8gp-SY-Y4U"/>
|
<outlet property="serversMenuItem" destination="u5M-hQ-VSc" id="8gp-SY-Y4U"/>
|
||||||
<outlet property="serversPreferencesMenuItem" destination="M5r-E7-44f" id="voe-SX-k6a"/>
|
<outlet property="serversPreferencesMenuItem" destination="M5r-E7-44f" id="voe-SX-k6a"/>
|
||||||
|
<outlet property="showBunchJsonExampleFileItem" destination="pdy-JE-50Q" id="xcZ-ep-mON"/>
|
||||||
<outlet property="showQRCodeMenuItem" destination="R6A-96-Zcb" id="XHz-pz-nCz"/>
|
<outlet property="showQRCodeMenuItem" destination="R6A-96-Zcb" id="XHz-pz-nCz"/>
|
||||||
<outlet property="showRunningModeMenuItem" destination="CCV-hX-fVA" id="AU2-hH-w8F"/>
|
<outlet property="showRunningModeMenuItem" destination="CCV-hX-fVA" id="AU2-hH-w8F"/>
|
||||||
<outlet property="statusMenu" destination="Hob-KD-bx9" id="clA-ZW-0pT"/>
|
<outlet property="statusMenu" destination="Hob-KD-bx9" id="clA-ZW-0pT"/>
|
||||||
@ -110,6 +113,24 @@
|
|||||||
<action selector="scanQRCodeFromScreen:" target="Voe-Tx-rLC" id="zQZ-IT-H4a"/>
|
<action selector="scanQRCodeFromScreen:" target="Voe-Tx-rLC" id="zQZ-IT-H4a"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem title="Show Bunch Json Example File..." id="pdy-JE-50Q">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="showBunchJsonExampleFileWithSender:" target="Voe-Tx-rLC" id="lMy-Lo-OyB"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Import Bunch Json File..." id="T9g-gy-gvv">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="importBunchJsonFileWithSender:" target="Voe-Tx-rLC" id="bRk-cg-9yn"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Export All Server To Json..." id="6k0-gn-DQv">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="exportAllServerProfileWithSender:" target="Voe-Tx-rLC" id="0ip-dJ-juc"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="6sL-A4-S7N"/>
|
<menuItem isSeparatorItem="YES" id="6sL-A4-S7N"/>
|
||||||
<menuItem title="Server Preferences..." id="M5r-E7-44f">
|
<menuItem title="Server Preferences..." id="M5r-E7-44f">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
@ -135,7 +156,7 @@
|
|||||||
<menuItem title="Launch At Login" id="eUq-p7-ICK">
|
<menuItem title="Launch At Login" id="eUq-p7-ICK">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="toggleLaunghAtLogin:" target="Voe-Tx-rLC" id="erk-ce-mO3"/>
|
<action selector="toggleLaunghAtLoginWithSender:" target="Voe-Tx-rLC" id="eFB-1Q-AOT"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="DTJ-NF-Wxr"/>
|
<menuItem isSeparatorItem="YES" id="DTJ-NF-Wxr"/>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// ServerProfileManager.swift
|
// ServerProfileManager.swift
|
||||||
// ShadowsocksX-NG
|
// ShadowsocksX-NG
|
||||||
//
|
//
|
||||||
// Created by 邱宇舟 on 16/6/6.
|
// Created by 邱宇舟 on 16/6/6. Modified by 秦宇航 16/9/12
|
||||||
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -70,4 +70,114 @@ class ServerProfileManager: NSObject {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func importConfigFile() {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.title = "Choose Config Json File".localized
|
||||||
|
openPanel.allowsMultipleSelection = false
|
||||||
|
openPanel.canChooseDirectories = false
|
||||||
|
openPanel.canCreateDirectories = false
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.becomeKey()
|
||||||
|
openPanel.begin { (result) -> Void in
|
||||||
|
if (result == NSFileHandlingPanelOKButton && (openPanel.url) != nil) {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let filePath:String = (openPanel.url?.path)!
|
||||||
|
if (fileManager.fileExists(atPath: filePath) && filePath.hasSuffix("json")) {
|
||||||
|
let data = fileManager.contents(atPath: filePath)
|
||||||
|
let readString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
|
||||||
|
let readStringData = readString.data(using: String.Encoding.utf8.rawValue)
|
||||||
|
|
||||||
|
let jsonArr1 = try! JSONSerialization.jsonObject(with: readStringData!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
|
||||||
|
|
||||||
|
for item in jsonArr1.object(forKey: "configs") as! [[String: AnyObject]]{
|
||||||
|
let profile = ServerProfile()
|
||||||
|
profile.serverHost = item["server"] as! String
|
||||||
|
profile.serverPort = UInt16((item["server_port"]?.integerValue)!)
|
||||||
|
profile.method = item["method"] as! String
|
||||||
|
profile.password = item["password"] as! String
|
||||||
|
profile.remark = item["remarks"] as! String
|
||||||
|
self.profiles.append(profile)
|
||||||
|
self.save()
|
||||||
|
NotificationCenter.default.post(name: NSNotification.Name(rawValue: NOTIFY_SERVER_PROFILES_CHANGED), object: nil)
|
||||||
|
}
|
||||||
|
let configsCount = (jsonArr1.object(forKey: "configs") as! [[String: AnyObject]]).count
|
||||||
|
let notification = NSUserNotification()
|
||||||
|
notification.title = "Import Server Profile succeed!".localized
|
||||||
|
notification.informativeText = "Successful import \(configsCount) items".localized
|
||||||
|
NSUserNotificationCenter.default
|
||||||
|
.deliver(notification)
|
||||||
|
}else{
|
||||||
|
let notification = NSUserNotification()
|
||||||
|
notification.title = "Import Server Profile failed!".localized
|
||||||
|
notification.informativeText = "Invalid config file!".localized
|
||||||
|
NSUserNotificationCenter.default
|
||||||
|
.deliver(notification)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportConfigFile() {
|
||||||
|
//读取example文件,删掉configs里面的配置,再用NSDictionary填充到configs里面
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
|
||||||
|
let filePath:String = Bundle.main.bundlePath + "/Contents/Resources/example-gui-config.json"
|
||||||
|
let data = fileManager.contents(atPath: filePath)
|
||||||
|
let readString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
|
||||||
|
let readStringData = readString.data(using: String.Encoding.utf8.rawValue)
|
||||||
|
let jsonArr1 = try! JSONSerialization.jsonObject(with: readStringData!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
|
||||||
|
|
||||||
|
let configsArray:NSMutableArray = [] //not using var?
|
||||||
|
|
||||||
|
for profile in profiles{
|
||||||
|
let configProfile:NSMutableDictionary = [:] //not using var?
|
||||||
|
//standard ss profile
|
||||||
|
configProfile.setValue(true, forKey: "enable")
|
||||||
|
configProfile.setValue(profile.serverHost, forKey: "server")
|
||||||
|
configProfile.setValue(NSNumber(value:profile.serverPort), forKey: "server_port")//not work
|
||||||
|
configProfile.setValue(profile.password, forKey: "password")
|
||||||
|
configProfile.setValue(profile.method, forKey: "method")
|
||||||
|
configProfile.setValue(profile.remark, forKey: "remarks")
|
||||||
|
configProfile.setValue(profile.remark.data(using: String.Encoding.utf8)?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)), forKey: "remarks_base64")
|
||||||
|
configsArray.add(configProfile)
|
||||||
|
}
|
||||||
|
jsonArr1.setValue(configsArray, forKey: "configs")
|
||||||
|
let jsonData = try! JSONSerialization.data(withJSONObject: jsonArr1, options: JSONSerialization.WritingOptions.prettyPrinted)
|
||||||
|
let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String
|
||||||
|
let savePanel = NSSavePanel()
|
||||||
|
savePanel.title = "Export Config Json File".localized
|
||||||
|
savePanel.canCreateDirectories = true
|
||||||
|
savePanel.allowedFileTypes = ["json"]
|
||||||
|
savePanel.nameFieldStringValue = "export.json"
|
||||||
|
savePanel.becomeKey()
|
||||||
|
savePanel.begin { (result) -> Void in
|
||||||
|
if (result == NSFileHandlingPanelOKButton && (savePanel.url) != nil) {
|
||||||
|
//write jsonArr1 back to file
|
||||||
|
try! jsonString.write(toFile: (savePanel.url?.path)!, atomically: true, encoding: String.Encoding.utf8)
|
||||||
|
NSWorkspace.shared().selectFile((savePanel.url?.path)!, inFileViewerRootedAtPath: (savePanel.directoryURL?.path)!)
|
||||||
|
let notification = NSUserNotification()
|
||||||
|
notification.title = "Export Server Profile succeed!".localized
|
||||||
|
notification.informativeText = "Successful Export \(self.profiles.count) items".localized
|
||||||
|
NSUserNotificationCenter.default
|
||||||
|
.deliver(notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class func showExampleConfigFile() {
|
||||||
|
//copy file to ~/Downloads folder
|
||||||
|
let filePath:String = Bundle.main.bundlePath + "/Contents/Resources/example-gui-config.json"
|
||||||
|
let fileMgr = FileManager.default
|
||||||
|
let dataPath = NSHomeDirectory() + "/Downloads"
|
||||||
|
let destPath = dataPath + "/example-gui-config.json"
|
||||||
|
//检测文件是否已经存在,如果存在直接用sharedWorkspace显示
|
||||||
|
if fileMgr.fileExists(atPath: destPath) {
|
||||||
|
NSWorkspace.shared().selectFile(destPath, inFileViewerRootedAtPath: dataPath)
|
||||||
|
}else{
|
||||||
|
try! fileMgr.copyItem(atPath: filePath, toPath: destPath)
|
||||||
|
NSWorkspace.shared().selectFile(destPath, inFileViewerRootedAtPath: dataPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
44
ShadowsocksX-NG/example-gui-config.json
Normal file
44
ShadowsocksX-NG/example-gui-config.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"random": false,
|
||||||
|
"global": false,
|
||||||
|
"enabled": true,
|
||||||
|
"shareOverLan": false,
|
||||||
|
"isDefault": false,
|
||||||
|
"localPort": 1080,
|
||||||
|
"pacUrl": null,
|
||||||
|
"useOnlinePac": false,
|
||||||
|
"reconnectTimes": 3,
|
||||||
|
"randomAlgorithm": 0,
|
||||||
|
"TTL": 0,
|
||||||
|
"proxyEnable": false,
|
||||||
|
"proxyType": 0,
|
||||||
|
"proxyHost": null,
|
||||||
|
"proxyPort": 0,
|
||||||
|
"proxyAuthUser": null,
|
||||||
|
"proxyAuthPass": null,
|
||||||
|
"authUser": null,
|
||||||
|
"authPass": null,
|
||||||
|
"autoban": false,
|
||||||
|
"configs": [{
|
||||||
|
"remarks": "example",
|
||||||
|
"server": "abc.xyz",
|
||||||
|
"server_port": 1234,
|
||||||
|
"method": "rc4-md5",
|
||||||
|
"remarks_base64": "ZXhhbXBsZQ==",
|
||||||
|
"password": "passwd",
|
||||||
|
"tcp_over_udp": false,
|
||||||
|
"udp_over_tcp": false,
|
||||||
|
"enable": true
|
||||||
|
},{
|
||||||
|
"remarks": "example2",
|
||||||
|
"server": "xyz.xyz",
|
||||||
|
"server_port": 1234,
|
||||||
|
"method": "rc4-md5",
|
||||||
|
"remarks_base64": "ZXhhbXBsZTI=",
|
||||||
|
"password": "passwd",
|
||||||
|
"tcp_over_udp": false,
|
||||||
|
"udp_over_tcp": false,
|
||||||
|
"enable": true
|
||||||
|
}]
|
||||||
|
}
|
@ -80,3 +80,11 @@
|
|||||||
/* Class = "NSMenuItem"; title = "HTTP Proxy Preference ..."; ObjectID = "uEp-Gz-cu0"; */
|
/* Class = "NSMenuItem"; title = "HTTP Proxy Preference ..."; ObjectID = "uEp-Gz-cu0"; */
|
||||||
"uEp-Gz-cu0.title" = "HTTP代理设置...";
|
"uEp-Gz-cu0.title" = "HTTP代理设置...";
|
||||||
|
|
||||||
|
/* Class = "NSMenuItem"; title = "Show Bunch Json Example File..."; ObjectID = "pdy-JE-50Q"; */
|
||||||
|
"pdy-JE-50Q.title" = "显示示例服务器配置文件...";
|
||||||
|
|
||||||
|
/* Class = "NSMenuItem"; title = "Import Bunch Json File..."; ObjectID = "T9g-gy-gvv"; */
|
||||||
|
"T9g-gy-gvv.title" = "导入服务器配置文件...";
|
||||||
|
|
||||||
|
/* Class = "NSMenuItem"; title = "Export All Server To Json..."; ObjectID = "6k0-gn-DQv"; */
|
||||||
|
"6k0-gn-DQv.title" = "导出全部服务器配置...";
|
||||||
|
Reference in New Issue
Block a user