mirror of
https://github.com/qinyuhang/ShadowsocksX-NG-R.git
synced 2026-11-04 09:04:06 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e94ba06959 | ||
|
|
25d2dfdd4a | ||
|
|
294c2a94e1 | ||
|
|
8162d7346b | ||
|
|
c0f017fc82 | ||
|
|
eb3815defe | ||
|
|
5ce8e611e2 | ||
|
|
ac027b8588 | ||
|
|
d4305d4ab4 | ||
|
|
5dd66ea9a7 | ||
|
|
37050b64e2 | ||
|
|
93136411ee | ||
|
|
d529c29b20 | ||
|
|
b143d78ef0 | ||
|
|
5366345b79 | ||
|
|
290865ad18 | ||
|
|
1249a9f1a8 | ||
|
|
09437bdc61 | ||
|
|
08f130858c | ||
|
|
944cfb47da | ||
|
|
b4ce1eb460 | ||
|
|
010b093368 | ||
|
|
a984a62f7e | ||
|
|
55ef345611 | ||
|
|
1c2e395248 | ||
|
|
6f6b75b664 | ||
|
|
75d71ae3a2 | ||
|
|
5027ef621e | ||
|
|
2b592e9be6 | ||
|
|
17a3d3a59f |
@@ -11,6 +11,10 @@
|
|||||||
083BF8371D82742200831C68 /* PingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF8351D82742200831C68 /* PingClient.swift */; };
|
083BF8371D82742200831C68 /* PingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF8351D82742200831C68 /* PingClient.swift */; };
|
||||||
083BF8391D82759600831C68 /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF8381D82759600831C68 /* StatusItemView.swift */; };
|
083BF8391D82759600831C68 /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF8381D82759600831C68 /* StatusItemView.swift */; };
|
||||||
083BF83B1D8275A800831C68 /* SystemThemeChangeHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */; };
|
083BF83B1D8275A800831C68 /* SystemThemeChangeHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */; };
|
||||||
|
08635C771EF750FA00F1D25E /* SubscribeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08635C761EF750FA00F1D25E /* SubscribeManager.swift */; };
|
||||||
|
086A31451F1AF797004C7285 /* SubscribePreferenceWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 086A31471F1AF797004C7285 /* SubscribePreferenceWindowController.xib */; };
|
||||||
|
0877D6541EF1E65600126865 /* Subscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0877D6531EF1E65600126865 /* Subscribe.swift */; };
|
||||||
|
0877D6571EF1E82400126865 /* SubscribePreferenceWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0877D6551EF1E82400126865 /* SubscribePreferenceWindowController.swift */; };
|
||||||
0880CE401D6FE6D900BD39E2 /* example-gui-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 0880CE3F1D6FE6D900BD39E2 /* example-gui-config.json */; };
|
0880CE401D6FE6D900BD39E2 /* example-gui-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 0880CE3F1D6FE6D900BD39E2 /* example-gui-config.json */; };
|
||||||
088EC3971D5F5B8600E40791 /* whitelist.pac in Resources */ = {isa = PBXBuildFile; fileRef = 088EC3961D5F5B8600E40791 /* whitelist.pac */; };
|
088EC3971D5F5B8600E40791 /* whitelist.pac in Resources */ = {isa = PBXBuildFile; fileRef = 088EC3961D5F5B8600E40791 /* whitelist.pac */; };
|
||||||
088EC3991D5F5BA300E40791 /* whiteiplist.pac in Resources */ = {isa = PBXBuildFile; fileRef = 088EC3981D5F5BA300E40791 /* whiteiplist.pac */; };
|
088EC3991D5F5BA300E40791 /* whiteiplist.pac in Resources */ = {isa = PBXBuildFile; fileRef = 088EC3981D5F5BA300E40791 /* whiteiplist.pac */; };
|
||||||
@@ -110,6 +114,11 @@
|
|||||||
083BF8381D82759600831C68 /* StatusItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = "<group>"; };
|
083BF8381D82759600831C68 /* StatusItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = "<group>"; };
|
||||||
083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemThemeChangeHelper.swift; sourceTree = "<group>"; };
|
083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemThemeChangeHelper.swift; sourceTree = "<group>"; };
|
||||||
085641E91D7188C400116B27 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
085641E91D7188C400116B27 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
08635C761EF750FA00F1D25E /* SubscribeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeManager.swift; sourceTree = "<group>"; };
|
||||||
|
086A31461F1AF797004C7285 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SubscribePreferenceWindowController.xib; sourceTree = "<group>"; };
|
||||||
|
086A31491F1AF799004C7285 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SubscribePreferenceWindowController.strings"; sourceTree = "<group>"; };
|
||||||
|
0877D6531EF1E65600126865 /* Subscribe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subscribe.swift; sourceTree = "<group>"; };
|
||||||
|
0877D6551EF1E82400126865 /* SubscribePreferenceWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribePreferenceWindowController.swift; sourceTree = "<group>"; };
|
||||||
0880CE3F1D6FE6D900BD39E2 /* example-gui-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "example-gui-config.json"; sourceTree = "<group>"; };
|
0880CE3F1D6FE6D900BD39E2 /* example-gui-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "example-gui-config.json"; sourceTree = "<group>"; };
|
||||||
088EC3961D5F5B8600E40791 /* whitelist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whitelist.pac; sourceTree = "<group>"; };
|
088EC3961D5F5B8600E40791 /* whitelist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whitelist.pac; sourceTree = "<group>"; };
|
||||||
088EC3981D5F5BA300E40791 /* whiteiplist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whiteiplist.pac; sourceTree = "<group>"; };
|
088EC3981D5F5BA300E40791 /* whiteiplist.pac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = whiteiplist.pac; sourceTree = "<group>"; };
|
||||||
@@ -321,6 +330,8 @@
|
|||||||
9B0BFFEF1D0460A70040E62B /* Info.plist */,
|
9B0BFFEF1D0460A70040E62B /* Info.plist */,
|
||||||
9BEEF06D1D04DCE400FC52B3 /* ServerProfile.swift */,
|
9BEEF06D1D04DCE400FC52B3 /* ServerProfile.swift */,
|
||||||
9BEEF06F1D04DDB100FC52B3 /* ServerProfileManager.swift */,
|
9BEEF06F1D04DDB100FC52B3 /* ServerProfileManager.swift */,
|
||||||
|
0877D6531EF1E65600126865 /* Subscribe.swift */,
|
||||||
|
08635C761EF750FA00F1D25E /* SubscribeManager.swift */,
|
||||||
08BDBB471E2361BD00C4BBC2 /* VersionChecker.swift */,
|
08BDBB471E2361BD00C4BBC2 /* VersionChecker.swift */,
|
||||||
083BF8381D82759600831C68 /* StatusItemView.swift */,
|
083BF8381D82759600831C68 /* StatusItemView.swift */,
|
||||||
083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */,
|
083BF83A1D8275A800831C68 /* SystemThemeChangeHelper.swift */,
|
||||||
@@ -356,6 +367,8 @@
|
|||||||
9B3FFF331D08CEF70019A709 /* SWBQRCodeWindowController.xib */,
|
9B3FFF331D08CEF70019A709 /* SWBQRCodeWindowController.xib */,
|
||||||
9B3FFF301D08CEE40019A709 /* SWBQRCodeWindowController.h */,
|
9B3FFF301D08CEE40019A709 /* SWBQRCodeWindowController.h */,
|
||||||
9B3FFF311D08CEE40019A709 /* SWBQRCodeWindowController.m */,
|
9B3FFF311D08CEE40019A709 /* SWBQRCodeWindowController.m */,
|
||||||
|
0877D6551EF1E82400126865 /* SubscribePreferenceWindowController.swift */,
|
||||||
|
086A31471F1AF797004C7285 /* SubscribePreferenceWindowController.xib */,
|
||||||
9BEEF0731D04EF3E00FC52B3 /* PreferencesWindowController.swift */,
|
9BEEF0731D04EF3E00FC52B3 /* PreferencesWindowController.swift */,
|
||||||
9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */,
|
9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */,
|
||||||
9BEEF0791D05631500FC52B3 /* AdvPreferencesWindowController.swift */,
|
9BEEF0791D05631500FC52B3 /* AdvPreferencesWindowController.swift */,
|
||||||
@@ -513,6 +526,7 @@
|
|||||||
C6E28E951DA79705004F8330 /* HTTPPreferencesWindowController.xib in Resources */,
|
C6E28E951DA79705004F8330 /* HTTPPreferencesWindowController.xib in Resources */,
|
||||||
9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */,
|
9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */,
|
||||||
9B0D55461D2CC85400A4A8E2 /* ProxyPreferencesController.xib in Resources */,
|
9B0D55461D2CC85400A4A8E2 /* ProxyPreferencesController.xib in Resources */,
|
||||||
|
086A31451F1AF797004C7285 /* SubscribePreferenceWindowController.xib in Resources */,
|
||||||
9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */,
|
9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */,
|
||||||
9B3FFF291D08A1DF0019A709 /* user-rule.txt in Resources */,
|
9B3FFF291D08A1DF0019A709 /* user-rule.txt in Resources */,
|
||||||
C6D429971DA75988002A5711 /* stop_privoxy.sh in Resources */,
|
C6D429971DA75988002A5711 /* stop_privoxy.sh in Resources */,
|
||||||
@@ -697,6 +711,9 @@
|
|||||||
9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */,
|
9BA04B261D24044D005AAD7F /* ProxyPreferencesController.swift in Sources */,
|
||||||
9B3FFF141D0705810019A709 /* Notifications.swift in Sources */,
|
9B3FFF141D0705810019A709 /* Notifications.swift in Sources */,
|
||||||
9BEEF0701D04DDB100FC52B3 /* ServerProfileManager.swift in Sources */,
|
9BEEF0701D04DDB100FC52B3 /* ServerProfileManager.swift in Sources */,
|
||||||
|
08635C771EF750FA00F1D25E /* SubscribeManager.swift in Sources */,
|
||||||
|
0877D6571EF1E82400126865 /* SubscribePreferenceWindowController.swift in Sources */,
|
||||||
|
0877D6541EF1E65600126865 /* Subscribe.swift in Sources */,
|
||||||
9BEEF07B1D05631500FC52B3 /* AdvPreferencesWindowController.swift in Sources */,
|
9BEEF07B1D05631500FC52B3 /* AdvPreferencesWindowController.swift in Sources */,
|
||||||
083BF8391D82759600831C68 /* StatusItemView.swift in Sources */,
|
083BF8391D82759600831C68 /* StatusItemView.swift in Sources */,
|
||||||
9BEEF06E1D04DCE400FC52B3 /* ServerProfile.swift in Sources */,
|
9BEEF06E1D04DCE400FC52B3 /* ServerProfile.swift in Sources */,
|
||||||
@@ -743,6 +760,15 @@
|
|||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
086A31471F1AF797004C7285 /* SubscribePreferenceWindowController.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
086A31461F1AF797004C7285 /* Base */,
|
||||||
|
086A31491F1AF799004C7285 /* zh-Hans */,
|
||||||
|
);
|
||||||
|
name = SubscribePreferenceWindowController.xib;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9B0BFFEC1D0460A70040E62B /* MainMenu.xib */ = {
|
9B0BFFEC1D0460A70040E62B /* MainMenu.xib */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
var proxyPreferencesWinCtrl: ProxyPreferencesController!
|
var proxyPreferencesWinCtrl: ProxyPreferencesController!
|
||||||
var editUserRulesWinCtrl: UserRulesController!
|
var editUserRulesWinCtrl: UserRulesController!
|
||||||
var httpPreferencesWinCtrl : HTTPPreferencesWindowController!
|
var httpPreferencesWinCtrl : HTTPPreferencesWindowController!
|
||||||
|
var subscribePreferenceWinCtrl: SubscribePreferenceWindowController!
|
||||||
|
|
||||||
var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController()
|
var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController()
|
||||||
|
|
||||||
@@ -51,11 +52,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
@IBOutlet weak var ShowNetworkSpeedItem: NSMenuItem!
|
@IBOutlet weak var ShowNetworkSpeedItem: NSMenuItem!
|
||||||
@IBOutlet weak var checkUpdateMenuItem: NSMenuItem!
|
@IBOutlet weak var checkUpdateMenuItem: NSMenuItem!
|
||||||
@IBOutlet weak var checkUpdateAtLaunchMenuItem: NSMenuItem!
|
@IBOutlet weak var checkUpdateAtLaunchMenuItem: NSMenuItem!
|
||||||
|
@IBOutlet var updateSubscribeAtLaunchMenuItem: NSMenuItem!
|
||||||
|
@IBOutlet var manualUpdateSubscribeMenuItem: NSMenuItem!
|
||||||
|
@IBOutlet var editSubscribeMenuItem: NSMenuItem!
|
||||||
|
|
||||||
// MARK: Variables
|
// MARK: Variables
|
||||||
var statusItemView:StatusItemView!
|
var statusItemView:StatusItemView!
|
||||||
var statusItem: NSStatusItem?
|
var statusItem: NSStatusItem?
|
||||||
var speedMonitor:NetWorkMonitor?
|
var speedMonitor:NetWorkMonitor?
|
||||||
|
var globalSubscribeFeed: Subscribe!
|
||||||
|
|
||||||
// MARK: Application function
|
// MARK: Application function
|
||||||
|
|
||||||
@@ -89,7 +94,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
"LocalHTTPOn": true,
|
"LocalHTTPOn": true,
|
||||||
"LocalHTTP.FollowGlobal": true,
|
"LocalHTTP.FollowGlobal": true,
|
||||||
"AutoCheckUpdate": false,
|
"AutoCheckUpdate": false,
|
||||||
"ACLFileName": "chn.acl"
|
"ACLFileName": "chn.acl",
|
||||||
|
"Subscribes": [],
|
||||||
|
"AutoUpdateSubscribe":false,
|
||||||
])
|
])
|
||||||
|
|
||||||
setUpMenu(defaults.bool(forKey: "enable_showSpeed"))
|
setUpMenu(defaults.bool(forKey: "enable_showSpeed"))
|
||||||
@@ -111,7 +118,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
, using: {
|
, using: {
|
||||||
(note) in
|
(note) in
|
||||||
let profileMgr = ServerProfileManager.instance
|
let profileMgr = ServerProfileManager.instance
|
||||||
if profileMgr.activeProfileId == nil &&
|
if profileMgr.getActiveProfileId() == "" &&
|
||||||
profileMgr.profiles.count > 0{
|
profileMgr.profiles.count > 0{
|
||||||
if profileMgr.profiles[0].isValid(){
|
if profileMgr.profiles[0].isValid(){
|
||||||
profileMgr.setActiveProfiledId(profileMgr.profiles[0].uuid)
|
profileMgr.setActiveProfiledId(profileMgr.profiles[0].uuid)
|
||||||
@@ -192,14 +199,24 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
|
|
||||||
ProxyConfHelper.install()
|
ProxyConfHelper.install()
|
||||||
applyConfig()
|
applyConfig()
|
||||||
SyncSSLocal()
|
// SyncSSLocal()
|
||||||
|
|
||||||
if defaults.bool(forKey: "ConnectAtLaunch") {
|
if defaults.bool(forKey: "ConnectAtLaunch") && ServerProfileManager.instance.getActiveProfileId() != "" {
|
||||||
|
defaults.set(false, forKey: "ShadowsocksOn")
|
||||||
toggleRunning(toggleRunningMenuItem)
|
toggleRunning(toggleRunningMenuItem)
|
||||||
}
|
}
|
||||||
// Version Check!
|
|
||||||
if defaults.bool(forKey: "AutoCheckUpdate"){
|
DispatchQueue.global().async {
|
||||||
checkForUpdate(mustShowAlert: false)
|
// Version Check!
|
||||||
|
if defaults.bool(forKey: "AutoCheckUpdate") {
|
||||||
|
self.checkForUpdate(mustShowAlert: false)
|
||||||
|
}
|
||||||
|
if defaults.bool(forKey: "AutoUpdateSubscribe") {
|
||||||
|
SubscribeManager.instance.updateAllServerFromSubscribe()
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,13 +231,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyConfig() {
|
func applyConfig() {
|
||||||
let profileMgr = ServerProfileManager.instance
|
|
||||||
if profileMgr.profiles.count == 0{
|
|
||||||
let notice = NSUserNotification()
|
|
||||||
notice.title = "还没有服务器设定!"
|
|
||||||
notice.subtitle = "去设置里面填一下吧,填完记得选择呦~"
|
|
||||||
NSUserNotificationCenter.default.deliver(notice)
|
|
||||||
}
|
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
let isOn = defaults.bool(forKey: "ShadowsocksOn")
|
let isOn = defaults.bool(forKey: "ShadowsocksOn")
|
||||||
let mode = defaults.string(forKey: "ShadowsocksRunningMode")
|
let mode = defaults.string(forKey: "ShadowsocksRunningMode")
|
||||||
@@ -253,12 +263,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
|
|
||||||
@IBAction func toggleRunning(_ sender: NSMenuItem) {
|
@IBAction func toggleRunning(_ sender: NSMenuItem) {
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
var isOn = defaults.bool(forKey: "ShadowsocksOn")
|
defaults.set(!defaults.bool(forKey: "ShadowsocksOn"), forKey: "ShadowsocksOn")
|
||||||
isOn = !isOn
|
|
||||||
defaults.set(isOn, forKey: "ShadowsocksOn")
|
|
||||||
|
|
||||||
updateMainMenu()
|
updateMainMenu()
|
||||||
|
SyncSSLocal()
|
||||||
applyConfig()
|
applyConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +290,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
ctrl.window?.makeKeyAndOrderFront(self)
|
ctrl.window?.makeKeyAndOrderFront(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func editSubscribeFeed(_ sender: NSMenuItem) {
|
||||||
|
if subscribePreferenceWinCtrl != nil {
|
||||||
|
subscribePreferenceWinCtrl.close()
|
||||||
|
}
|
||||||
|
let ctrl = SubscribePreferenceWindowController(windowNibName: "SubscribePreferenceWindowController")
|
||||||
|
subscribePreferenceWinCtrl = ctrl
|
||||||
|
|
||||||
|
ctrl.showWindow(self)
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
ctrl.window?.makeKeyAndOrderFront(self)
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func toggleLaunghAtLogin(_ sender: NSMenuItem) {
|
@IBAction func toggleLaunghAtLogin(_ sender: NSMenuItem) {
|
||||||
launchAtLoginController.launchAtLogin = !launchAtLoginController.launchAtLogin;
|
launchAtLoginController.launchAtLogin = !launchAtLoginController.launchAtLogin;
|
||||||
updateLaunchAtLoginMenu()
|
updateLaunchAtLoginMenu()
|
||||||
@@ -345,6 +365,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
ServerProfileManager.instance.exportConfigFile()
|
ServerProfileManager.instance.exportConfigFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func updateSubscribe(_ sender: NSMenuItem) {
|
||||||
|
SubscribeManager.instance.updateAllServerFromSubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func updateSubscribeAtLaunch(_ sender: NSMenuItem) {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
defaults.set(!defaults.bool(forKey: "AutoUpdateSubscribe"), forKey: "AutoUpdateSubscribe")
|
||||||
|
updateSubscribeAtLaunchMenuItem.state = defaults.bool(forKey: "AutoUpdateSubscribe") ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Proxy submenu function
|
||||||
|
|
||||||
@IBAction func selectPACMode(_ sender: NSMenuItem) {
|
@IBAction func selectPACMode(_ sender: NSMenuItem) {
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
defaults.setValue("auto", forKey: "ShadowsocksRunningMode")
|
defaults.setValue("auto", forKey: "ShadowsocksRunningMode")
|
||||||
@@ -446,7 +479,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
let index = sender.tag
|
let index = sender.tag
|
||||||
let spMgr = ServerProfileManager.instance
|
let spMgr = ServerProfileManager.instance
|
||||||
let newProfile = spMgr.profiles[index]
|
let newProfile = spMgr.profiles[index]
|
||||||
if newProfile.uuid != spMgr.activeProfileId {
|
if newProfile.uuid != spMgr.getActiveProfileId() {
|
||||||
spMgr.setActiveProfiledId(newProfile.uuid)
|
spMgr.setActiveProfiledId(newProfile.uuid)
|
||||||
updateServersMenu()
|
updateServersMenu()
|
||||||
SyncSSLocal()
|
SyncSSLocal()
|
||||||
@@ -508,7 +541,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
|
|
||||||
let mgr = ServerProfileManager.instance
|
let mgr = ServerProfileManager.instance
|
||||||
for p in mgr.profiles {
|
for p in mgr.profiles {
|
||||||
if mgr.activeProfileId == p.uuid {
|
if mgr.getActiveProfileId() == p.uuid {
|
||||||
if !p.remark.isEmpty {
|
if !p.remark.isEmpty {
|
||||||
serverMenuText = p.remark
|
serverMenuText = p.remark
|
||||||
} else {
|
} else {
|
||||||
@@ -620,6 +653,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
let showBunch = showBunchJsonExampleFileItem
|
let showBunch = showBunchJsonExampleFileItem
|
||||||
let importBuntch = importBunchJsonFileItem
|
let importBuntch = importBunchJsonFileItem
|
||||||
let exportAllServer = exportAllServerProfileItem
|
let exportAllServer = exportAllServerProfileItem
|
||||||
|
let updateSubscribeItem = manualUpdateSubscribeMenuItem
|
||||||
|
let autoUpdateSubscribeItem = updateSubscribeAtLaunchMenuItem
|
||||||
|
let editSubscribeItem = editSubscribeMenuItem
|
||||||
// let pingItem = pingserverMenuItem
|
// let pingItem = pingserverMenuItem
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
@@ -634,7 +670,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
item.title += " - failed"
|
item.title += " - failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mgr.activeProfileId == p.uuid {
|
if mgr.getActiveProfileId() == p.uuid {
|
||||||
item.state = 1
|
item.state = 1
|
||||||
}
|
}
|
||||||
if !p.isValid() {
|
if !p.isValid() {
|
||||||
@@ -650,7 +686,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
groupSubmenuItem.title = p.ssrGroup
|
groupSubmenuItem.title = p.ssrGroup
|
||||||
serversMenuItem.submenu?.addItem(groupSubmenuItem)
|
serversMenuItem.submenu?.addItem(groupSubmenuItem)
|
||||||
serversMenuItem.submenu?.setSubmenu(groupSubmenu, for: groupSubmenuItem)
|
serversMenuItem.submenu?.setSubmenu(groupSubmenu, for: groupSubmenuItem)
|
||||||
if mgr.activeProfileId == p.uuid {
|
if mgr.getActiveProfileId() == p.uuid {
|
||||||
item.state = 1
|
item.state = 1
|
||||||
groupSubmenuItem.state = 1
|
groupSubmenuItem.state = 1
|
||||||
}
|
}
|
||||||
@@ -659,7 +695,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if mgr.activeProfileId == p.uuid {
|
if mgr.getActiveProfileId() == p.uuid {
|
||||||
item.state = 1
|
item.state = 1
|
||||||
serversMenuItem.submenu?.item(withTitle: p.ssrGroup)?.state = 1
|
serversMenuItem.submenu?.item(withTitle: p.ssrGroup)?.state = 1
|
||||||
}
|
}
|
||||||
@@ -675,6 +711,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
if !mgr.profiles.isEmpty {
|
if !mgr.profiles.isEmpty {
|
||||||
serversMenuItem.submenu?.addItem(NSMenuItem.separator())
|
serversMenuItem.submenu?.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
|
serversMenuItem.submenu?.addItem(editSubscribeItem!)
|
||||||
|
serversMenuItem.submenu?.addItem(autoUpdateSubscribeItem!)
|
||||||
|
autoUpdateSubscribeItem?.state = UserDefaults.standard.bool(forKey: "AutoUpdateSubscribe") ? 1 : 0
|
||||||
|
serversMenuItem.submenu?.addItem(updateSubscribeItem!)
|
||||||
serversMenuItem.submenu?.addItem(showQRItem!)
|
serversMenuItem.submenu?.addItem(showQRItem!)
|
||||||
serversMenuItem.submenu?.addItem(scanQRItem!)
|
serversMenuItem.submenu?.addItem(scanQRItem!)
|
||||||
serversMenuItem.submenu?.addItem(showBunch!)
|
serversMenuItem.submenu?.addItem(showBunch!)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?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="12120" systemVersion="16F71b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@@ -23,11 +23,13 @@
|
|||||||
<outlet property="checkUpdateAtLaunchMenuItem" destination="3nJ-zQ-Agj" id="bbR-eO-uCo"/>
|
<outlet property="checkUpdateAtLaunchMenuItem" destination="3nJ-zQ-Agj" id="bbR-eO-uCo"/>
|
||||||
<outlet property="checkUpdateMenuItem" destination="wyZ-4G-qg9" id="Sva-xA-VQY"/>
|
<outlet property="checkUpdateMenuItem" destination="wyZ-4G-qg9" id="Sva-xA-VQY"/>
|
||||||
<outlet property="connectAtLaunchMenuItem" destination="aB3-cf-5j0" id="kW6-UF-38P"/>
|
<outlet property="connectAtLaunchMenuItem" destination="aB3-cf-5j0" id="kW6-UF-38P"/>
|
||||||
|
<outlet property="editSubscribeMenuItem" destination="QJ6-eg-mzv" id="KC0-uH-8q0"/>
|
||||||
<outlet property="exportAllServerProfileItem" destination="6k0-gn-DQv" id="1xA-cd-5Ia"/>
|
<outlet property="exportAllServerProfileItem" destination="6k0-gn-DQv" id="1xA-cd-5Ia"/>
|
||||||
<outlet property="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/>
|
<outlet property="globalModeMenuItem" destination="Mw3-Jm-eXA" id="ar5-Yx-3ze"/>
|
||||||
<outlet property="importBunchJsonFileItem" destination="T9g-gy-gvv" id="GW7-ii-U1j"/>
|
<outlet property="importBunchJsonFileItem" destination="T9g-gy-gvv" id="GW7-ii-U1j"/>
|
||||||
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="w4p-0c-DZn"/>
|
<outlet property="lanchAtLoginMenuItem" destination="eUq-p7-ICK" id="w4p-0c-DZn"/>
|
||||||
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
|
<outlet property="manualModeMenuItem" destination="8PR-gs-c5N" id="9qz-mU-5kt"/>
|
||||||
|
<outlet property="manualUpdateSubscribeMenuItem" destination="NnE-Ad-1An" id="2Vf-Br-EnF"/>
|
||||||
<outlet property="pingserverMenuItem" destination="zfR-Jt-GmS" id="Gjs-bY-0tm"/>
|
<outlet property="pingserverMenuItem" destination="zfR-Jt-GmS" id="Gjs-bY-0tm"/>
|
||||||
<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"/>
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
<outlet property="showQRCodeMenuItem" destination="R6A-96-Zcb" id="XHz-pz-nCz"/>
|
<outlet property="showQRCodeMenuItem" destination="R6A-96-Zcb" id="XHz-pz-nCz"/>
|
||||||
<outlet property="statusMenu" destination="Hob-KD-bx9" id="clA-ZW-0pT"/>
|
<outlet property="statusMenu" destination="Hob-KD-bx9" id="clA-ZW-0pT"/>
|
||||||
<outlet property="toggleRunningMenuItem" destination="GSu-Tf-StS" id="XHw-pU-nCa"/>
|
<outlet property="toggleRunningMenuItem" destination="GSu-Tf-StS" id="XHw-pU-nCa"/>
|
||||||
|
<outlet property="updateSubscribeAtLaunchMenuItem" destination="nQ4-Nv-tox" id="dOD-xa-Rw4"/>
|
||||||
<outlet property="whiteListModeMenuItem" destination="RvZ-Zn-29U" id="2v5-Bd-EDj"/>
|
<outlet property="whiteListModeMenuItem" destination="RvZ-Zn-29U" id="2v5-Bd-EDj"/>
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
</customObject>
|
||||||
@@ -134,6 +137,24 @@
|
|||||||
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
|
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
|
||||||
<menu key="submenu" title="Servers" id="9Y1-db-3HK">
|
<menu key="submenu" title="Servers" id="9Y1-db-3HK">
|
||||||
<items>
|
<items>
|
||||||
|
<menuItem title="Edit Subscribe Feed" id="QJ6-eg-mzv">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="editSubscribeFeed:" target="Voe-Tx-rLC" id="V3v-T4-Rfn"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Update Subscribe on App Start" id="nQ4-Nv-tox">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="updateSubscribeAtLaunch:" target="Voe-Tx-rLC" id="mCZ-VF-av8"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Manual Update Subscribe" id="NnE-Ad-1An">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="updateSubscribe:" target="Voe-Tx-rLC" id="Vno-Oh-0oc"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Show QR Code For Current Server..." id="R6A-96-Zcb">
|
<menuItem title="Show QR Code For Current Server..." id="R6A-96-Zcb">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="SubscribePreferenceWindowController" customModule="ShadowsocksX_NG" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="AddSubscribeBtn" destination="nzj-fK-Vip" id="Tlt-DM-cZF"/>
|
||||||
|
<outlet property="DeleteSubscribeBtn" destination="jSv-em-0Gp" id="uDp-Bj-Mb8"/>
|
||||||
|
<outlet property="FeedTextField" destination="p1W-Md-Wfw" id="zh0-1b-0zw"/>
|
||||||
|
<outlet property="GroupTextField" destination="MrH-Ft-1tn" id="txD-SB-Kpr"/>
|
||||||
|
<outlet property="MaxCountTextField" destination="vQO-bq-Igs" id="feo-Jx-AXK"/>
|
||||||
|
<outlet property="OKButton" destination="rC3-mg-tMh" id="RK5-lm-9qc"/>
|
||||||
|
<outlet property="SubscribeTableView" destination="go8-dc-8a6" id="YOl-2J-3oX"/>
|
||||||
|
<outlet property="TokenTextField" destination="hnP-yo-KBb" id="aDw-vO-ZID"/>
|
||||||
|
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<window title="Subscribe Preference" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
|
||||||
|
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p1W-Md-Wfw">
|
||||||
|
<rect key="frame" x="236" y="228" width="224" height="22"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="AuN-e3-13P">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hnP-yo-KBb">
|
||||||
|
<rect key="frame" x="236" y="190" width="224" height="22"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Optional" drawsBackground="YES" id="ASh-mc-Uyt">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MrH-Ft-1tn">
|
||||||
|
<rect key="frame" x="236" y="149" width="224" height="22"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="3cf-F8-tBI">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nzj-fK-Vip">
|
||||||
|
<rect key="frame" x="20" y="20" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="CfY-uG-c0X"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="Lkb-PM-8fV"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Wxc-Ib-fdn">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="onAdd:" target="-2" id="giW-QT-LEJ"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jSv-em-0Gp">
|
||||||
|
<rect key="frame" x="40" y="20" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="aEO-oC-a3N"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IAV-Ta-HLA">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="onDelete:" target="-2" id="1OX-xh-AJb"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uNY-1k-LwL">
|
||||||
|
<rect key="frame" x="182" y="230" width="30" height="17"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="URL" id="yBp-oB-gbL">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="P7L-eo-l3V">
|
||||||
|
<rect key="frame" x="182" y="192" width="41" height="17"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Token" id="IrJ-D8-tlX">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fkq-Nl-5rY">
|
||||||
|
<rect key="frame" x="182" y="151" width="42" height="17"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Group" id="f2p-qb-uuE">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vQO-bq-Igs">
|
||||||
|
<rect key="frame" x="236" y="111" width="224" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="224" id="zre-fn-PCG"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Optional" drawsBackground="YES" id="3kt-n3-uTF">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XLx-TZ-soe">
|
||||||
|
<rect key="frame" x="182" y="113" width="43" height="17"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Count" id="wbG-HT-bdN">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<scrollView focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="shT-FD-AWs">
|
||||||
|
<rect key="frame" x="20" y="39" width="156" height="211"/>
|
||||||
|
<clipView key="contentView" focusRingType="none" id="zJf-T0-5ju">
|
||||||
|
<rect key="frame" x="1" y="1" width="154" height="209"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" id="go8-dc-8a6">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="154" height="209"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="status" width="20" minWidth="10" maxWidth="3.4028234663852886e+38" id="pQE-hc-jk2">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<imageCell key="dataCell" refusesFirstResponder="YES" alignment="left" identifier="status" imageScaling="proportionallyDown" id="GWO-Q7-6lA"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="main" width="128" minWidth="40" maxWidth="1000" id="3xd-Hy-qcx">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" identifier="main" title="Text" id="ik7-wS-gok">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="-2" id="jQk-FD-T8P"/>
|
||||||
|
<outlet property="delegate" destination="-2" id="8ZO-Kd-bCj"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="r2d-RN-LC6">
|
||||||
|
<rect key="frame" x="1" y="168" width="154" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="Kag-9R-Vsj">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rC3-mg-tMh">
|
||||||
|
<rect key="frame" x="407" y="13" width="59" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="CfK-8Y-YNP">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="onOk:" target="-2" id="dUz-Tf-zD1"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="vQO-bq-Igs" firstAttribute="top" secondItem="MrH-Ft-1tn" secondAttribute="bottom" constant="16" id="71r-dd-ykt"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="trailing" secondItem="rC3-mg-tMh" secondAttribute="trailing" id="7B7-nd-TLL"/>
|
||||||
|
<constraint firstItem="uNY-1k-LwL" firstAttribute="centerY" secondItem="p1W-Md-Wfw" secondAttribute="centerY" id="9fw-m5-qp1"/>
|
||||||
|
<constraint firstItem="shT-FD-AWs" firstAttribute="leading" secondItem="nzj-fK-Vip" secondAttribute="leading" id="ASi-b0-Bor"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="top" secondItem="hnP-yo-KBb" secondAttribute="bottom" constant="19" id="DsI-9d-P9b"/>
|
||||||
|
<constraint firstItem="XLx-TZ-soe" firstAttribute="leading" secondItem="shT-FD-AWs" secondAttribute="trailing" constant="8" id="EJz-xB-6RM"/>
|
||||||
|
<constraint firstItem="vQO-bq-Igs" firstAttribute="leading" secondItem="MrH-Ft-1tn" secondAttribute="leading" id="KHN-WD-iRw"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="MrH-Ft-1tn" secondAttribute="trailing" constant="20" symbolic="YES" id="L6b-S2-rXW"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="leading" secondItem="p1W-Md-Wfw" secondAttribute="leading" id="NbP-CB-QGM"/>
|
||||||
|
<constraint firstItem="rC3-mg-tMh" firstAttribute="top" secondItem="MrH-Ft-1tn" secondAttribute="bottom" constant="108" id="TKp-sn-xiU"/>
|
||||||
|
<constraint firstItem="vQO-bq-Igs" firstAttribute="leading" secondItem="XLx-TZ-soe" secondAttribute="trailing" constant="13" id="Urz-tT-Oyy"/>
|
||||||
|
<constraint firstItem="vQO-bq-Igs" firstAttribute="trailing" secondItem="MrH-Ft-1tn" secondAttribute="trailing" id="VKk-Df-fQc"/>
|
||||||
|
<constraint firstItem="jSv-em-0Gp" firstAttribute="top" secondItem="shT-FD-AWs" secondAttribute="bottom" id="VzA-No-qVy"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="nzj-fK-Vip" secondAttribute="bottom" constant="21" id="Y3H-aQ-YNb"/>
|
||||||
|
<constraint firstItem="XLx-TZ-soe" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="leading" id="Zeh-Nz-h3d"/>
|
||||||
|
<constraint firstItem="Fkq-Nl-5rY" firstAttribute="centerY" secondItem="MrH-Ft-1tn" secondAttribute="centerY" id="bK9-gp-UzV"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="XLx-TZ-soe" secondAttribute="bottom" constant="113" id="c8d-Bj-YQd"/>
|
||||||
|
<constraint firstItem="P7L-eo-l3V" firstAttribute="centerY" secondItem="hnP-yo-KBb" secondAttribute="centerY" id="dQh-T9-QP5"/>
|
||||||
|
<constraint firstItem="P7L-eo-l3V" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="leading" id="fDy-fs-ZnP"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="trailing" secondItem="p1W-Md-Wfw" secondAttribute="trailing" id="iTW-Og-aCE"/>
|
||||||
|
<constraint firstItem="vQO-bq-Igs" firstAttribute="centerY" secondItem="XLx-TZ-soe" secondAttribute="centerY" id="jkd-CC-vo0"/>
|
||||||
|
<constraint firstItem="P7L-eo-l3V" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="leading" id="lCH-5t-qVb"/>
|
||||||
|
<constraint firstItem="nzj-fK-Vip" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="mn4-pg-oMy"/>
|
||||||
|
<constraint firstItem="shT-FD-AWs" firstAttribute="top" secondItem="p1W-Md-Wfw" secondAttribute="top" id="onL-99-1hL"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="trailing" secondItem="hnP-yo-KBb" secondAttribute="trailing" id="pp9-gb-IcG"/>
|
||||||
|
<constraint firstItem="MrH-Ft-1tn" firstAttribute="leading" secondItem="hnP-yo-KBb" secondAttribute="leading" id="qIK-qK-NsQ"/>
|
||||||
|
<constraint firstItem="p1W-Md-Wfw" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="trailing" constant="26" id="tgx-dn-JvH"/>
|
||||||
|
<constraint firstItem="jSv-em-0Gp" firstAttribute="centerY" secondItem="nzj-fK-Vip" secondAttribute="centerY" id="w6C-hd-DIa"/>
|
||||||
|
<constraint firstItem="XLx-TZ-soe" firstAttribute="leading" secondItem="shT-FD-AWs" secondAttribute="trailing" constant="8" symbolic="YES" id="wLO-J3-cZU"/>
|
||||||
|
<constraint firstItem="P7L-eo-l3V" firstAttribute="top" secondItem="uNY-1k-LwL" secondAttribute="bottom" constant="21" id="wl6-VJ-tLB"/>
|
||||||
|
<constraint firstItem="Fkq-Nl-5rY" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="leading" id="xnv-Ha-oeD"/>
|
||||||
|
<constraint firstItem="jSv-em-0Gp" firstAttribute="leading" secondItem="nzj-fK-Vip" secondAttribute="trailing" id="y0j-ol-6WV"/>
|
||||||
|
<constraint firstItem="Fkq-Nl-5rY" firstAttribute="leading" secondItem="uNY-1k-LwL" secondAttribute="leading" id="ySw-mv-aeO"/>
|
||||||
|
<constraint firstItem="nzj-fK-Vip" firstAttribute="baseline" secondItem="jSv-em-0Gp" secondAttribute="baseline" id="zGB-7i-cIR"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="140" y="136"/>
|
||||||
|
</window>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="NSAddTemplate" width="11" height="11"/>
|
||||||
|
<image name="NSRemoveTemplate" width="11" height="11"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.4.1-R8</string>
|
<string>1.4.2-R8</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>4</string>
|
<string>1</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.utilities</string>
|
<string>public.app-category.utilities</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
let SS_LOCAL_VERSION = "2.5.6.9.static"
|
let SS_LOCAL_VERSION = "2.5.6.11.static"
|
||||||
let PRIVOXY_VERSION = "3.0.26.static"
|
let PRIVOXY_VERSION = "3.0.26.static"
|
||||||
let APP_SUPPORT_DIR = "/Library/Application Support/ShadowsocksX-NG/"
|
let APP_SUPPORT_DIR = "/Library/Application Support/ShadowsocksX-NG/"
|
||||||
let LAUNCH_AGENT_DIR = "/Library/LaunchAgents/"
|
let LAUNCH_AGENT_DIR = "/Library/LaunchAgents/"
|
||||||
@@ -168,7 +168,7 @@ func SyncSSLocal() {
|
|||||||
var changed: Bool = false
|
var changed: Bool = false
|
||||||
changed = changed || generateSSLocalLauchAgentPlist()
|
changed = changed || generateSSLocalLauchAgentPlist()
|
||||||
let mgr = ServerProfileManager.instance
|
let mgr = ServerProfileManager.instance
|
||||||
if mgr.activeProfileId != nil {
|
if mgr.getActiveProfileId() != "" {
|
||||||
if mgr.getActiveProfile() != nil {
|
if mgr.getActiveProfile() != nil {
|
||||||
changed = changed || writeSSLocalConfFile((mgr.getActiveProfile()?.toJsonConfig())!)
|
changed = changed || writeSSLocalConfFile((mgr.getActiveProfile()?.toJsonConfig())!)
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ func SyncPrivoxy() {
|
|||||||
var changed: Bool = false
|
var changed: Bool = false
|
||||||
changed = changed || generatePrivoxyLauchAgentPlist()
|
changed = changed || generatePrivoxyLauchAgentPlist()
|
||||||
let mgr = ServerProfileManager.instance
|
let mgr = ServerProfileManager.instance
|
||||||
if mgr.activeProfileId != nil {
|
if mgr.getActiveProfileId() != "" {
|
||||||
changed = changed || writePrivoxyConfFile()
|
changed = changed || writePrivoxyConfFile()
|
||||||
|
|
||||||
let on = UserDefaults.standard.bool(forKey: "LocalHTTPOn")
|
let on = UserDefaults.standard.bool(forKey: "LocalHTTPOn")
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class PreferencesWindowController: NSWindowController
|
|||||||
"auth_aes128_sha1",
|
"auth_aes128_sha1",
|
||||||
"auth_aes128_md5",
|
"auth_aes128_md5",
|
||||||
"auth_chain_a",
|
"auth_chain_a",
|
||||||
|
"auth_chain_b",
|
||||||
])
|
])
|
||||||
ObfsTextField.addItems(withObjectValues: [
|
ObfsTextField.addItems(withObjectValues: [
|
||||||
"plain",
|
"plain",
|
||||||
@@ -133,6 +134,10 @@ class PreferencesWindowController: NSWindowController
|
|||||||
self.profilesTableView.scrollRowToVisible(index-1)
|
self.profilesTableView.scrollRowToVisible(index-1)
|
||||||
self.profilesTableView.selectRowIndexes(IndexSet(integer: index-1), byExtendingSelection: false)
|
self.profilesTableView.selectRowIndexes(IndexSet(integer: index-1), byExtendingSelection: false)
|
||||||
updateProfileBoxVisible()
|
updateProfileBoxVisible()
|
||||||
|
if profileMgr.profiles.count == 0 {
|
||||||
|
defaults.set(true, forKey: "ShadowsocksOn")
|
||||||
|
(NSApplication.shared().delegate as! AppDelegate).toggleRunning((NSApplication.shared().delegate as! AppDelegate).toggleRunningMenuItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func ok(_ sender: NSButton) {
|
@IBAction func ok(_ sender: NSButton) {
|
||||||
@@ -260,7 +265,7 @@ class PreferencesWindowController: NSWindowController
|
|||||||
|
|
||||||
func getDataAtRow(_ index:Int) -> (String, Bool) {
|
func getDataAtRow(_ index:Int) -> (String, Bool) {
|
||||||
let profile = profileMgr.profiles[index]
|
let profile = profileMgr.profiles[index]
|
||||||
let isActive = (profileMgr.activeProfileId == profile.uuid)
|
let isActive = (profileMgr.getActiveProfileId() == profile.uuid)
|
||||||
if !profile.remark.isEmpty {
|
if !profile.remark.isEmpty {
|
||||||
return (profile.remark, isActive)
|
return (profile.remark, isActive)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -40,20 +40,20 @@ class ServerProfile: NSObject {
|
|||||||
(profile: ServerProfile) in
|
(profile: ServerProfile) in
|
||||||
profile.serverHost = data["ServerHost"] as! String
|
profile.serverHost = data["ServerHost"] as! String
|
||||||
profile.serverPort = (data["ServerPort"] as! NSNumber).uint16Value
|
profile.serverPort = (data["ServerPort"] as! NSNumber).uint16Value
|
||||||
profile.method = data["Method"] as! String
|
profile.method = (data["Method"] as! String).lowercased()
|
||||||
profile.password = data["Password"] as! String
|
profile.password = data["Password"] as! String
|
||||||
|
|
||||||
if let remark = data["Remark"] {
|
if let remark = data["Remark"] {
|
||||||
profile.remark = remark as! String
|
profile.remark = remark as! String
|
||||||
}
|
}
|
||||||
if let ssrObfs = data["ssrObfs"] {
|
if let ssrObfs = data["ssrObfs"] {
|
||||||
profile.ssrObfs = ssrObfs as! String
|
profile.ssrObfs = (ssrObfs as! String).lowercased()
|
||||||
}
|
}
|
||||||
if let ssrObfsParam = data["ssrObfsParam"] {
|
if let ssrObfsParam = data["ssrObfsParam"] {
|
||||||
profile.ssrObfsParam = ssrObfsParam as! String
|
profile.ssrObfsParam = ssrObfsParam as! String
|
||||||
}
|
}
|
||||||
if let ssrProtocol = data["ssrProtocol"] {
|
if let ssrProtocol = data["ssrProtocol"] {
|
||||||
profile.ssrProtocol = ssrProtocol as! String
|
profile.ssrProtocol = (ssrProtocol as! String).lowercased()
|
||||||
}
|
}
|
||||||
if let ssrProtocolParam = data["ssrProtocolParam"]{
|
if let ssrProtocolParam = data["ssrProtocolParam"]{
|
||||||
profile.ssrProtocolParam = ssrProtocolParam as! String
|
profile.ssrProtocolParam = ssrProtocolParam as! String
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// ServerProfileManager.swift
|
// ServerProfileManager.swift
|
||||||
// ShadowsocksX-NG
|
// ShadowsocksX-NG
|
||||||
//
|
//
|
||||||
// Created by 邱宇舟 on 16/6/6. Modified by 秦宇航 16/9/12
|
// Created by 邱宇舟 on 16/6/6. Modified by 秦宇航 17/7/22
|
||||||
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -19,13 +19,27 @@ class ServerProfileManager: NSObject {
|
|||||||
profiles = [ServerProfile]()
|
profiles = [ServerProfile]()
|
||||||
|
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
|
activeProfileId = defaults.string(forKey: "ActiveServerProfileId")
|
||||||
|
var didFindActiveProfileId = false
|
||||||
if let _profiles = defaults.array(forKey: "ServerProfiles") {
|
if let _profiles = defaults.array(forKey: "ServerProfiles") {
|
||||||
for _profile in _profiles {
|
for _profile in _profiles {
|
||||||
let profile = ServerProfile.fromDictionary(_profile as! [String : AnyObject])
|
let profile = ServerProfile.fromDictionary(_profile as! [String : AnyObject])
|
||||||
profiles.append(profile)
|
profiles.append(profile)
|
||||||
|
if profile.uuid == activeProfileId {
|
||||||
|
didFindActiveProfileId = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activeProfileId = defaults.string(forKey: "ActiveServerProfileId")
|
if profiles.count == 0{
|
||||||
|
let notice = NSUserNotification()
|
||||||
|
notice.title = "还没有服务器设定!"
|
||||||
|
notice.subtitle = "去设置里面填一下吧,填完记得选择呦~"
|
||||||
|
NSUserNotificationCenter.default.deliver(notice)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !didFindActiveProfileId {
|
||||||
|
activeProfileId = profiles[0].uuid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setActiveProfiledId(_ id: String) {
|
func setActiveProfiledId(_ id: String) {
|
||||||
@@ -34,6 +48,16 @@ class ServerProfileManager: NSObject {
|
|||||||
defaults.set(id, forKey: "ActiveServerProfileId")
|
defaults.set(id, forKey: "ActiveServerProfileId")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getActiveProfileId() -> String {
|
||||||
|
for p in profiles {
|
||||||
|
if p.uuid == activeProfileId {
|
||||||
|
return activeProfileId!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if profiles.count == 0 {return ""}
|
||||||
|
return profiles[0].uuid
|
||||||
|
}
|
||||||
|
|
||||||
func save() {
|
func save() {
|
||||||
let defaults = UserDefaults.standard
|
let defaults = UserDefaults.standard
|
||||||
var _profiles = [AnyObject]()
|
var _profiles = [AnyObject]()
|
||||||
@@ -46,11 +70,11 @@ class ServerProfileManager: NSObject {
|
|||||||
defaults.set(_profiles, forKey: "ServerProfiles")
|
defaults.set(_profiles, forKey: "ServerProfiles")
|
||||||
|
|
||||||
if getActiveProfile() == nil {
|
if getActiveProfile() == nil {
|
||||||
activeProfileId = nil
|
activeProfileId = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if activeProfileId != nil {
|
if getActiveProfileId() != "" {
|
||||||
defaults.set(activeProfileId, forKey: "ActiveServerProfileId")
|
defaults.set(getActiveProfileId(), forKey: "ActiveServerProfileId")
|
||||||
let _ = writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!)
|
let _ = writeSSLocalConfFile((getActiveProfile()?.toJsonConfig())!)
|
||||||
} else {
|
} else {
|
||||||
defaults.removeObject(forKey: "ActiveServerProfileId")
|
defaults.removeObject(forKey: "ActiveServerProfileId")
|
||||||
@@ -59,18 +83,42 @@ class ServerProfileManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getActiveProfile() -> ServerProfile? {
|
func getActiveProfile() -> ServerProfile? {
|
||||||
if let id = activeProfileId {
|
if getActiveProfileId() == "" { return nil }
|
||||||
for p in profiles {
|
for p in profiles {
|
||||||
if p.uuid == id {
|
if p.uuid == getActiveProfileId() {
|
||||||
return p
|
return p
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isExisted(profile: ServerProfile) -> (Bool, Int){
|
||||||
|
for (index, value) in profiles.enumerated() {
|
||||||
|
let ret = (value.serverHost == profile.serverHost && value.serverPort == profile.serverPort)
|
||||||
|
if ret {
|
||||||
|
return (ret, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (false, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDuplicated(profile: ServerProfile) -> (Bool, Int){
|
||||||
|
for (index, value) in profiles.enumerated() {
|
||||||
|
let ret = value.serverHost == profile.serverHost
|
||||||
|
&& value.password == profile.password
|
||||||
|
&& value.serverPort == profile.serverPort
|
||||||
|
&& value.ssrProtocol == profile.ssrProtocol
|
||||||
|
&& value.ssrObfs == profile.ssrObfs
|
||||||
|
&& value.ssrObfsParam == profile.ssrObfsParam
|
||||||
|
&& value.ssrProtocolParam == profile.ssrProtocolParam
|
||||||
|
&& value.remark == profile.remark
|
||||||
|
if ret {
|
||||||
|
return (ret, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (false, -1)
|
||||||
|
}
|
||||||
|
|
||||||
func importConfigFile() {
|
func importConfigFile() {
|
||||||
let openPanel = NSOpenPanel()
|
let openPanel = NSOpenPanel()
|
||||||
openPanel.title = "Choose Config Json File".localized
|
openPanel.title = "Choose Config Json File".localized
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// Subscribe.swift
|
||||||
|
// ShadowsocksX-NG
|
||||||
|
//
|
||||||
|
// Created by 秦宇航 on 2017/6/15.
|
||||||
|
// Copyright © 2017年 qiuyuzhou. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Alamofire
|
||||||
|
|
||||||
|
class Subscribe: NSObject{
|
||||||
|
|
||||||
|
var subscribeFeed = ""
|
||||||
|
var isActive = true
|
||||||
|
var maxCount = 0 // -1 is not limited
|
||||||
|
var groupName = ""
|
||||||
|
var token = ""
|
||||||
|
var cache = ""
|
||||||
|
|
||||||
|
var profileMgr: ServerProfileManager!
|
||||||
|
|
||||||
|
init(initUrlString:String, initGroupName: String, initToken: String, initMaxCount: Int){
|
||||||
|
super.init()
|
||||||
|
subscribeFeed = initUrlString
|
||||||
|
|
||||||
|
token = initToken
|
||||||
|
|
||||||
|
setMaxCount(initMaxCount: initMaxCount)
|
||||||
|
setGroupName(newGroupName: initGroupName)
|
||||||
|
profileMgr = ServerProfileManager.instance
|
||||||
|
}
|
||||||
|
func getFeed() -> String{
|
||||||
|
return subscribeFeed
|
||||||
|
}
|
||||||
|
func setFeed(newFeed: String){
|
||||||
|
subscribeFeed = newFeed
|
||||||
|
}
|
||||||
|
func diactivateSubscribe(){
|
||||||
|
isActive = false
|
||||||
|
}
|
||||||
|
func activateSubscribe(){
|
||||||
|
isActive = true
|
||||||
|
}
|
||||||
|
func setGroupName(newGroupName: String) {
|
||||||
|
func getGroupNameFromRes(resString: String) {
|
||||||
|
let decodeRes = decode64(resString)!
|
||||||
|
let ssrregexp = "ssr://([A-Za-z0-9_-]+)"
|
||||||
|
let urls = splitor(url: decodeRes, regexp: ssrregexp)
|
||||||
|
let profile = ServerProfile.fromDictionary(ParseAppURLSchemes(URL(string: urls[0])) as [String : AnyObject])
|
||||||
|
self.groupName = profile.ssrGroup
|
||||||
|
}
|
||||||
|
if newGroupName != "" { return groupName = newGroupName }
|
||||||
|
if self.cache != "" { return getGroupNameFromRes(resString: cache) }
|
||||||
|
sendRequest(url: self.subscribeFeed, options: "", callback: { resString in
|
||||||
|
if resString == "" { return self.groupName = "New Subscribe" }
|
||||||
|
getGroupNameFromRes(resString: resString)
|
||||||
|
self.cache = resString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func getGroupName() -> String {
|
||||||
|
return groupName
|
||||||
|
}
|
||||||
|
func getMaxCount() -> Int {
|
||||||
|
return maxCount
|
||||||
|
}
|
||||||
|
static func fromDictionary(_ data:[String:AnyObject]) -> Subscribe {
|
||||||
|
var feed:String = ""
|
||||||
|
var group:String = ""
|
||||||
|
var token:String = ""
|
||||||
|
var maxCount:Int = -1
|
||||||
|
for (key, value) in data {
|
||||||
|
switch key {
|
||||||
|
case "feed":
|
||||||
|
feed = value as! String
|
||||||
|
case "group":
|
||||||
|
group = value as! String
|
||||||
|
case "token":
|
||||||
|
token = value as! String
|
||||||
|
case "maxCount":
|
||||||
|
maxCount = value as! Int
|
||||||
|
default:
|
||||||
|
print("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Subscribe.init(initUrlString: feed, initGroupName: group, initToken: token, initMaxCount: maxCount)
|
||||||
|
}
|
||||||
|
static func toDictionary(_ data: Subscribe) -> [String: AnyObject] {
|
||||||
|
var ret : [String: AnyObject] = [:]
|
||||||
|
ret["feed"] = data.subscribeFeed as AnyObject
|
||||||
|
ret["group"] = data.groupName as AnyObject
|
||||||
|
ret["token"] = data.token as AnyObject
|
||||||
|
ret["maxCount"] = data.maxCount as AnyObject
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
fileprivate func sendRequest(url: String, options: Any, callback: @escaping (String) -> Void) {
|
||||||
|
let headers: HTTPHeaders = [
|
||||||
|
// "Authorization": "Basic U2hhZG93c29ja1gtTkctUg==",
|
||||||
|
// "Accept": "application/json",
|
||||||
|
"token": self.token,
|
||||||
|
"User-Agent": "ShadowsocksX-NG-R " + (getLocalInfo()["CFBundleShortVersionString"] as! String) + " Version " + (getLocalInfo()["CFBundleVersion"] as! String)
|
||||||
|
]
|
||||||
|
|
||||||
|
Alamofire.request(url, headers: headers)
|
||||||
|
.responseString{
|
||||||
|
response in
|
||||||
|
if response.result.isSuccess {
|
||||||
|
callback(response.result.value!)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
callback("")
|
||||||
|
self.pushNotification(title: "请求失败", subtitle: "", info: "发送到\(url)的请求失败,请检查您的网络")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func setMaxCount(initMaxCount:Int) {
|
||||||
|
func getMaxFromRes(resString: String) {
|
||||||
|
let maxCountReg = "MAX=[0-9]+"
|
||||||
|
let decodeRes = decode64(resString)!
|
||||||
|
let range = decodeRes.range(of: maxCountReg, options: .regularExpression)
|
||||||
|
if range != nil {
|
||||||
|
let result = decodeRes.substring(with:range!)
|
||||||
|
self.maxCount = Int(result.replacingOccurrences(of: "MAX=", with: ""))!
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
self.maxCount = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if initMaxCount != 0 { return self.maxCount = initMaxCount }
|
||||||
|
if cache != "" { return getMaxFromRes(resString: cache) }
|
||||||
|
sendRequest(url: self.subscribeFeed, options: "", callback: { resString in
|
||||||
|
if resString == "" { return }// Also should hold if token is wrong feedback
|
||||||
|
getMaxFromRes(resString: resString)
|
||||||
|
self.cache = resString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func updateServerFromFeed(){
|
||||||
|
func updateServerHandler(resString: String) {
|
||||||
|
let decodeRes = decode64(resString)!
|
||||||
|
let ssrregexp = "ssr://([A-Za-z0-9_-]+)"
|
||||||
|
let urls = splitor(url: decodeRes, regexp: ssrregexp)
|
||||||
|
// hold if user fill a maxCount larger then server return
|
||||||
|
// Should push a notification about it and correct the user filled maxCOunt?
|
||||||
|
let maxN = (self.maxCount > urls.count) ? urls.count : (self.maxCount == -1) ? urls.count: self.maxCount
|
||||||
|
// TODO change the loop into random pick
|
||||||
|
for index in 0..<maxN {
|
||||||
|
if let profileDict = ParseAppURLSchemes(URL(string: urls[index])) {
|
||||||
|
let profile = ServerProfile.fromDictionary(profileDict as [String : AnyObject])
|
||||||
|
let (dupResult, _) = self.profileMgr.isDuplicated(profile: profile)
|
||||||
|
let (existResult, existIndex) = self.profileMgr.isExisted(profile: profile)
|
||||||
|
if dupResult {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if existResult {
|
||||||
|
self.profileMgr.profiles.replaceSubrange(Range(existIndex..<existIndex + 1), with: [profile])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.profileMgr.profiles.append(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.profileMgr.save()
|
||||||
|
pushNotification(title: "成功更新订阅", subtitle: "", info: "更新来自\(subscribeFeed)的订阅")
|
||||||
|
(NSApplication.shared().delegate as! AppDelegate).updateServersMenu()
|
||||||
|
(NSApplication.shared().delegate as! AppDelegate).updateRunningModeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isActive){ return }
|
||||||
|
|
||||||
|
sendRequest(url: self.subscribeFeed, options: "", callback: { resString in
|
||||||
|
if resString == "" { return }
|
||||||
|
updateServerHandler(resString: resString)
|
||||||
|
self.cache = resString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func feedValidator() -> Bool{
|
||||||
|
// is the right format
|
||||||
|
// should be http or https reg
|
||||||
|
// but we should not support http only feed
|
||||||
|
// TODO refine the regular expression
|
||||||
|
let feedRegExp = "http[s]?://[A-Za-z0-9-_/.=?]*"
|
||||||
|
return subscribeFeed.range(of:feedRegExp, options: .regularExpression) != nil
|
||||||
|
}
|
||||||
|
fileprivate func pushNotification(title: String, subtitle: String, info: String){
|
||||||
|
let userNote = NSUserNotification()
|
||||||
|
userNote.title = title
|
||||||
|
userNote.subtitle = subtitle
|
||||||
|
userNote.informativeText = info
|
||||||
|
userNote.soundName = NSUserNotificationDefaultSoundName
|
||||||
|
|
||||||
|
NSUserNotificationCenter.default
|
||||||
|
.deliver(userNote);
|
||||||
|
}
|
||||||
|
class func isSame(source: Subscribe, target: Subscribe) -> Bool {
|
||||||
|
return source.subscribeFeed == target.subscribeFeed && source.token == target.token && source.maxCount == target.maxCount
|
||||||
|
}
|
||||||
|
func isExist(_ target: Subscribe) -> Bool {
|
||||||
|
return self.subscribeFeed == target.subscribeFeed
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// SubscribeManager.swift
|
||||||
|
// ShadowsocksX-NG
|
||||||
|
//
|
||||||
|
// Created by 秦宇航 on 2017/6/19.
|
||||||
|
// Copyright © 2017年 qiuyuzhou. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class SubscribeManager:NSObject{
|
||||||
|
static let instance:SubscribeManager = SubscribeManager()
|
||||||
|
|
||||||
|
var subscribes:[Subscribe]
|
||||||
|
var subscribesDefault : [[String: AnyObject]]
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
|
||||||
|
fileprivate override init() {
|
||||||
|
subscribes = []
|
||||||
|
subscribesDefault = [[:]]
|
||||||
|
if let subscribesDefault = defaults.array(forKey: "Subscribes") {
|
||||||
|
for value in subscribesDefault{
|
||||||
|
subscribes.append(Subscribe.fromDictionary(value as! [String : AnyObject]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func addSubscribe(oneSubscribe: Subscribe) -> Bool {
|
||||||
|
for (index, value) in subscribes.enumerated() {
|
||||||
|
if Subscribe.isSame(source: oneSubscribe, target: value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if value.isExist(oneSubscribe) {
|
||||||
|
subscribes.replaceSubrange(Range(index..<index + 1), with: [oneSubscribe])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscribes.append(oneSubscribe)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func deleteSubscribe(atIndex: Int) -> Bool {
|
||||||
|
subscribes.remove(at: atIndex)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func save() {
|
||||||
|
defaults.set(subscribesToDefaults(data: subscribes), forKey: "Subscribes")
|
||||||
|
}
|
||||||
|
fileprivate func subscribesToDefaults(data: [Subscribe]) -> [[String: AnyObject]]{
|
||||||
|
var ret : [[String: AnyObject]] = []
|
||||||
|
for value in data {
|
||||||
|
ret.append(Subscribe.toDictionary(value))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
fileprivate func DefaultsToSubscribes(data:[[String: AnyObject]]) -> [Subscribe] {
|
||||||
|
var ret : [Subscribe] = []
|
||||||
|
for value in data{
|
||||||
|
ret.append(Subscribe.fromDictionary(value))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
func updateAllServerFromSubscribe(){
|
||||||
|
subscribes.forEach{ value in
|
||||||
|
value.updateServerFromFeed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
//
|
||||||
|
// SubscribePreferenceWindowController.swift
|
||||||
|
// ShadowsocksX-NG
|
||||||
|
//
|
||||||
|
// Created by 秦宇航 on 2017/6/15.
|
||||||
|
// Copyright © 2017年 qiuyuzhou. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class SubscribePreferenceWindowController: NSWindowController
|
||||||
|
, NSTableViewDataSource, NSTableViewDelegate {
|
||||||
|
|
||||||
|
@IBOutlet weak var FeedLabel: NSTextField!
|
||||||
|
@IBOutlet weak var OKButton: NSButton!
|
||||||
|
|
||||||
|
@IBOutlet weak var FeedTextField: NSTextField!
|
||||||
|
@IBOutlet weak var TokenTextField: NSTextField!
|
||||||
|
@IBOutlet weak var GroupTextField: NSTextField!
|
||||||
|
@IBOutlet weak var MaxCountTextField: NSTextField!
|
||||||
|
@IBOutlet weak var SubscribeTableView: NSTableView!
|
||||||
|
|
||||||
|
@IBOutlet weak var AddSubscribeBtn: NSButton!
|
||||||
|
@IBOutlet weak var DeleteSubscribeBtn: NSButton!
|
||||||
|
|
||||||
|
var sbMgr: SubscribeManager!
|
||||||
|
var defaults: UserDefaults!
|
||||||
|
let tableViewDragType: String = "subscribe.host"
|
||||||
|
var editingSubscribe: Subscribe!
|
||||||
|
|
||||||
|
override func windowDidLoad() {
|
||||||
|
super.windowDidLoad()
|
||||||
|
|
||||||
|
sbMgr = SubscribeManager.instance
|
||||||
|
defaults = UserDefaults.standard
|
||||||
|
SubscribeTableView.reloadData()
|
||||||
|
updateSubscribeBoxVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
SubscribeTableView.register(forDraggedTypes: [tableViewDragType])
|
||||||
|
SubscribeTableView.allowsMultipleSelection = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func onOk(_ sender: NSButton) {
|
||||||
|
if editingSubscribe != nil {
|
||||||
|
if !editingSubscribe.feedValidator() {
|
||||||
|
// Done Shake window
|
||||||
|
shakeWindows()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sbMgr.save()
|
||||||
|
window?.performClose(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func onAdd(_ sender: NSButton) {
|
||||||
|
if editingSubscribe != nil && !editingSubscribe.feedValidator(){
|
||||||
|
shakeWindows()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SubscribeTableView.beginUpdates()
|
||||||
|
let subscribe = Subscribe(initUrlString: "", initGroupName: "", initToken: "", initMaxCount: -1)
|
||||||
|
sbMgr.subscribes.append(subscribe)
|
||||||
|
|
||||||
|
let index = IndexSet(integer: sbMgr.subscribes.count-1)
|
||||||
|
SubscribeTableView.insertRows(at: index, withAnimation: .effectFade)
|
||||||
|
|
||||||
|
self.SubscribeTableView.scrollRowToVisible(self.sbMgr.subscribes.count-1)
|
||||||
|
self.SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false)
|
||||||
|
SubscribeTableView.endUpdates()
|
||||||
|
updateSubscribeBoxVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func onDelete(_ sender: NSButton) {
|
||||||
|
let index = Int(SubscribeTableView.selectedRowIndexes.first!)
|
||||||
|
var deleteCount = 0
|
||||||
|
if index >= 0 {
|
||||||
|
SubscribeTableView.beginUpdates()
|
||||||
|
for (_, toDeleteIndex) in SubscribeTableView.selectedRowIndexes.enumerated() {
|
||||||
|
_ = sbMgr.deleteSubscribe(atIndex: toDeleteIndex - deleteCount)
|
||||||
|
SubscribeTableView.removeRows(at: IndexSet(integer: toDeleteIndex - deleteCount), withAnimation: .effectFade)
|
||||||
|
deleteCount += 1
|
||||||
|
if sbMgr.subscribes.count == 0 {
|
||||||
|
cleanField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubscribeTableView.endUpdates()
|
||||||
|
}
|
||||||
|
self.SubscribeTableView.scrollRowToVisible(index - 1)
|
||||||
|
self.SubscribeTableView.selectRowIndexes(IndexSet(integer: index - 1), byExtendingSelection: false)
|
||||||
|
updateSubscribeBoxVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSubscribeBoxVisible() {
|
||||||
|
if sbMgr.subscribes.count <= 0 {
|
||||||
|
DeleteSubscribeBtn.isEnabled = false
|
||||||
|
FeedTextField.isEnabled = false
|
||||||
|
TokenTextField.isEnabled = false
|
||||||
|
GroupTextField.isEnabled = false
|
||||||
|
MaxCountTextField.isEnabled = false
|
||||||
|
}else{
|
||||||
|
DeleteSubscribeBtn.isEnabled = true
|
||||||
|
FeedTextField.isEnabled = true
|
||||||
|
TokenTextField.isEnabled = true
|
||||||
|
GroupTextField.isEnabled = true
|
||||||
|
MaxCountTextField.isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindSubscribe(_ index:Int) {
|
||||||
|
if index >= 0 && index < sbMgr.subscribes.count {
|
||||||
|
editingSubscribe = sbMgr.subscribes[index]
|
||||||
|
|
||||||
|
FeedTextField.bind("value", to: editingSubscribe, withKeyPath: "subscribeFeed", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
||||||
|
TokenTextField.bind("value", to: editingSubscribe, withKeyPath: "token", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
||||||
|
GroupTextField.bind("value", to: editingSubscribe, withKeyPath: "groupName", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
||||||
|
MaxCountTextField.bind("value", to: editingSubscribe, withKeyPath: "maxCount", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
||||||
|
|
||||||
|
} else {
|
||||||
|
editingSubscribe = nil
|
||||||
|
FeedTextField.unbind("value")
|
||||||
|
TokenTextField.unbind("value")
|
||||||
|
GroupTextField.unbind("value")
|
||||||
|
MaxCountTextField.unbind("value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDataAtRow(_ index:Int) -> String {
|
||||||
|
if sbMgr.subscribes[index].groupName != "" {
|
||||||
|
return sbMgr.subscribes[index].groupName
|
||||||
|
}
|
||||||
|
return sbMgr.subscribes[index].subscribeFeed
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: For NSTableViewDataSource
|
||||||
|
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
if let mgr = sbMgr {
|
||||||
|
return mgr.subscribes.count
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView
|
||||||
|
, objectValueFor tableColumn: NSTableColumn?
|
||||||
|
, row: Int) -> Any? {
|
||||||
|
|
||||||
|
let title = getDataAtRow(row)
|
||||||
|
|
||||||
|
if tableColumn?.identifier == "main" {
|
||||||
|
if title != "" {return title}
|
||||||
|
else {return "S"}
|
||||||
|
} else if tableColumn?.identifier == "status" {
|
||||||
|
return NSImage(named: "menu_icon")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Drag & Drop reorder rows
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
|
||||||
|
let item = NSPasteboardItem()
|
||||||
|
item.setString(String(row), forType: tableViewDragType)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int
|
||||||
|
, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
|
||||||
|
if dropOperation == .above {
|
||||||
|
return .move
|
||||||
|
}
|
||||||
|
return NSDragOperation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo
|
||||||
|
, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
|
||||||
|
if let mgr = sbMgr {
|
||||||
|
var oldIndexes = [Int]()
|
||||||
|
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
|
||||||
|
if let str = ($0.0.item as! NSPasteboardItem).string(forType: self.tableViewDragType), let index = Int(str) {
|
||||||
|
oldIndexes.append(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldIndexOffset = 0
|
||||||
|
var newIndexOffset = 0
|
||||||
|
|
||||||
|
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
|
||||||
|
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
|
||||||
|
tableView.beginUpdates()
|
||||||
|
for oldIndex in oldIndexes {
|
||||||
|
if oldIndex < row {
|
||||||
|
let o = mgr.subscribes.remove(at: oldIndex + oldIndexOffset)
|
||||||
|
mgr.subscribes.insert(o, at:row - 1)
|
||||||
|
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
|
||||||
|
oldIndexOffset -= 1
|
||||||
|
} else {
|
||||||
|
let o = mgr.subscribes.remove(at: oldIndex)
|
||||||
|
mgr.subscribes.insert(o, at:row + newIndexOffset)
|
||||||
|
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
|
||||||
|
newIndexOffset += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableView.endUpdates()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// For NSTableViewDelegate
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView
|
||||||
|
, shouldEdit tableColumn: NSTableColumn?, row: Int) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||||
|
if row < 0 {
|
||||||
|
editingSubscribe = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// if editingSubscribe != nil {
|
||||||
|
// if !editingSubscribe.isValid() {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||||
|
if SubscribeTableView.selectedRow >= 0 {
|
||||||
|
bindSubscribe(SubscribeTableView.selectedRow)
|
||||||
|
if (SubscribeTableView.selectedRowIndexes.count > 1){
|
||||||
|
// duplicateProfileButton.isEnabled = false
|
||||||
|
} else {
|
||||||
|
// duplicateProfileButton.isEnabled = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !sbMgr.subscribes.isEmpty {
|
||||||
|
let index = IndexSet(integer: sbMgr.subscribes.count - 1)
|
||||||
|
SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanField(){
|
||||||
|
FeedTextField.stringValue = ""
|
||||||
|
TokenTextField.stringValue = ""
|
||||||
|
GroupTextField.stringValue = ""
|
||||||
|
MaxCountTextField.stringValue = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func shakeWindows(){
|
||||||
|
let numberOfShakes:Int = 8
|
||||||
|
let durationOfShake:Float = 0.5
|
||||||
|
let vigourOfShake:Float = 0.05
|
||||||
|
|
||||||
|
let frame:CGRect = (window?.frame)!
|
||||||
|
let shakeAnimation = CAKeyframeAnimation()
|
||||||
|
|
||||||
|
let shakePath = CGMutablePath()
|
||||||
|
|
||||||
|
shakePath.move(to: CGPoint(x:NSMinX(frame), y:NSMinY(frame)))
|
||||||
|
|
||||||
|
for _ in 1...numberOfShakes{
|
||||||
|
shakePath.addLine(to: CGPoint(x: NSMinX(frame) - frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame)))
|
||||||
|
shakePath.addLine(to: CGPoint(x: NSMinX(frame) + frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame)))
|
||||||
|
}
|
||||||
|
|
||||||
|
shakePath.closeSubpath()
|
||||||
|
shakeAnimation.path = shakePath
|
||||||
|
shakeAnimation.duration = CFTimeInterval(durationOfShake)
|
||||||
|
window?.animations = ["frameOrigin":shakeAnimation]
|
||||||
|
window?.animator().setFrameOrigin(window!.frame.origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,10 +39,10 @@ func splitProfile(url: String, max: Int) -> [String] {
|
|||||||
return [""]
|
return [""]
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func splitor(url: String, regexp: String) -> [String] {
|
func splitor(url: String, regexp: String) -> [String] {
|
||||||
var ret: [String] = []
|
var ret: [String] = []
|
||||||
var ssrUrl = url
|
var ssrUrl = url
|
||||||
while ssrUrl.range(of:regexp, options: .regularExpression) != nil{
|
while ssrUrl.range(of:regexp, options: .regularExpression) != nil {
|
||||||
let range = ssrUrl.range(of:regexp, options: .regularExpression)
|
let range = ssrUrl.range(of:regexp, options: .regularExpression)
|
||||||
let result = ssrUrl.substring(with:range!)
|
let result = ssrUrl.substring(with:range!)
|
||||||
ssrUrl.replaceSubrange(range!, with: "")
|
ssrUrl.replaceSubrange(range!, with: "")
|
||||||
@@ -50,3 +50,8 @@ fileprivate func splitor(url: String, regexp: String) -> [String] {
|
|||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLocalInfo() -> [String: Any] {
|
||||||
|
let InfoDict = Bundle.main.infoDictionary
|
||||||
|
return InfoDict!
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
cd `dirname "${BASH_SOURCE[0]}"`
|
cd `dirname "${BASH_SOURCE[0]}"`
|
||||||
ssLocalVersion=2.5.6.9.static
|
ssLocalVersion=2.5.6.11.static
|
||||||
mkdir -p "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion"
|
mkdir -p "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion"
|
||||||
cp -f ss-local "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion/"
|
cp -f ss-local "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local-$ssLocalVersion/"
|
||||||
rm -f "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local"
|
rm -f "$HOME/Library/Application Support/ShadowsocksX-NG/ss-local"
|
||||||
|
|||||||
Binary file not shown.
@@ -113,3 +113,11 @@
|
|||||||
|
|
||||||
"sDG-YV-yuQ.title" = "ACL自动";
|
"sDG-YV-yuQ.title" = "ACL自动";
|
||||||
|
|
||||||
|
"NnE-Ad-1An.title" = "手动更新订阅";
|
||||||
|
|
||||||
|
"nQ4-Nv-tox.title" = "打开时自动更新订阅";
|
||||||
|
|
||||||
|
"QJ6-eg-mzv.title" = "编辑订阅";
|
||||||
|
|
||||||
|
"QJ6-eg-mzv.title" = "编辑订阅";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "CfK-8Y-YNP"; */
|
||||||
|
"CfK-8Y-YNP.title" = "OK";
|
||||||
|
|
||||||
|
/* Class = "NSWindow"; title = "Window"; ObjectID = "F0z-JX-Cv5"; */
|
||||||
|
"F0z-JX-Cv5.title" = "订阅设置";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Token"; ObjectID = "IrJ-D8-tlX"; */
|
||||||
|
"IrJ-D8-tlX.title" = "口令";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Group"; ObjectID = "f2p-qb-uuE"; */
|
||||||
|
"f2p-qb-uuE.title" = "组名";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Text"; ObjectID = "ik7-wS-gok"; */
|
||||||
|
"ik7-wS-gok.title" = "Text";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Subscribe Feed URL"; ObjectID = "rMX-5B-Fvs"; */
|
||||||
|
"rMX-5B-Fvs.title" = "订阅设置";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "Count"; ObjectID = "wbG-HT-bdN"; */
|
||||||
|
"wbG-HT-bdN.title" = "最大数量";
|
||||||
|
|
||||||
|
/* Class = "NSTextFieldCell"; title = "URL"; ObjectID = "yBp-oB-gbL"; */
|
||||||
|
"yBp-oB-gbL.title" = "订阅地址";
|
||||||
Reference in New Issue
Block a user