Improve sharing server profiles.

* New share server profiles window.
* Import server profile urls from pasteboard.
This commit is contained in:
Qiu Yuzhou
2018-09-16 16:46:48 +08:00
parent 33b36928bc
commit 02d9ac3031
14 changed files with 605 additions and 205 deletions

View File

@ -6,7 +6,7 @@ Current version is 1.8.0
Next Generation of [ShadowsocksX](https://github.com/shadowsocks/shadowsocks-iOS) Next Generation of [ShadowsocksX](https://github.com/shadowsocks/shadowsocks-iOS)
## Why? ## Why a new implementation?
It's hard to maintain the original implementation as there is too much unused code in it. It's hard to maintain the original implementation as there is too much unused code in it.
It also embeds the `ss-local` source. It's crazy to maintain dependencies of `ss-local`. It also embeds the `ss-local` source. It's crazy to maintain dependencies of `ss-local`.
@ -36,21 +36,12 @@ From [here](https://github.com/shadowsocks/ShadowsocksX-NG/releases/)
- `ss-local` from shadowsocks-libev 3.2.0 - `ss-local` from shadowsocks-libev 3.2.0
- Support SIP003 plugins. Embed `kcptun` and `simple-obfs`. - Support SIP003 plugins. Embed `kcptun` and `simple-obfs`.
- Could update PAC by download GFW List from GitHub. - Could update PAC by download GFW List from GitHub.
- Shows QRCode for current server profile in legacy and SIP002 format. - Share your server profiles by qrcode or url.
- Scans QRCode from screen. - Import server profile urls from pasteboard.
- Auto launch at login. - Import server profile by scan QRCode on screen.
- User rules for PAC. - Custom rules for PAC.
- Support for [AEAD Ciphers](https://shadowsocks.org/en/spec/AEAD-Ciphers.html) - Support for [AEAD Ciphers](https://shadowsocks.org/en/spec/AEAD-Ciphers.html)
- HTTP Proxy by [privoxy](http://www.privoxy.org/) - HTTP Proxy by [privoxy](http://www.privoxy.org/)
- An advanced preferences panel for configuring:
- Local SOCKS5 listen address.
- Local SOCKS5 listen port.
- Local SOCKS5 timeout.
- If enable UDP relay.
- GFW List URL.
- Manually specify network service profiles which would be used to configure the proxy.
- Could reorder shadowsocks profiles by drag-&-dropping in servers preferences panel.
- Configurable global shortcuts for toggle running and switch proxy mode.
## Difference from original ShadowsocksX ## Difference from original ShadowsocksX

View File

@ -46,10 +46,11 @@
9B5832031E741F8D009D5B7D /* command-512.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B5832021E741F8D009D5B7D /* command-512.png */; }; 9B5832031E741F8D009D5B7D /* command-512.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B5832021E741F8D009D5B7D /* command-512.png */; };
9B5832071E7421B2009D5B7D /* virtual-server-icon-3.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */; }; 9B5832071E7421B2009D5B7D /* virtual-server-icon-3.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */; };
9B58320B1E7422DB009D5B7D /* http.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B58320A1E7422DB009D5B7D /* http.png */; }; 9B58320B1E7422DB009D5B7D /* http.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B58320A1E7422DB009D5B7D /* http.png */; };
9B5832111E742632009D5B7D /* kcptun_1.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B5832101E742632009D5B7D /* kcptun_1.png */; };
9B5AA0AC209C43C200E8B659 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 9B5AA0AB209C43C200E8B659 /* Credits.rtf */; }; 9B5AA0AC209C43C200E8B659 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 9B5AA0AB209C43C200E8B659 /* Credits.rtf */; };
9B6BF9541E27B2570061B9A7 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */; }; 9B6BF9541E27B2570061B9A7 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */; };
9B7297E7214D69C300FD24AA /* libmbedcrypto.2.12.0.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9B7297E5214D68F800FD24AA /* libmbedcrypto.2.12.0.dylib */; }; 9B7297E7214D69C300FD24AA /* libmbedcrypto.2.12.0.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 9B7297E5214D68F800FD24AA /* libmbedcrypto.2.12.0.dylib */; };
9B7297EA214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */; };
9B7297EC214DA88A00FD24AA /* ShareServerProfilesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B7297EE214DA88A00FD24AA /* ShareServerProfilesWindowController.xib */; };
9B86459D1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */; }; 9B86459D1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */; };
9B938D991E864B38005F5636 /* menu_g_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B938D931E864B38005F5636 /* menu_g_icon.png */; }; 9B938D991E864B38005F5636 /* menu_g_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B938D931E864B38005F5636 /* menu_g_icon.png */; };
9B938D9A1E864B38005F5636 /* menu_g_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B938D941E864B38005F5636 /* menu_g_icon@2x.png */; }; 9B938D9A1E864B38005F5636 /* menu_g_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9B938D941E864B38005F5636 /* menu_g_icon@2x.png */; };
@ -195,12 +196,14 @@
9B5832021E741F8D009D5B7D /* command-512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "command-512.png"; sourceTree = "<group>"; }; 9B5832021E741F8D009D5B7D /* command-512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "command-512.png"; sourceTree = "<group>"; };
9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "virtual-server-icon-3.png"; sourceTree = "<group>"; }; 9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "virtual-server-icon-3.png"; sourceTree = "<group>"; };
9B58320A1E7422DB009D5B7D /* http.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = http.png; sourceTree = "<group>"; }; 9B58320A1E7422DB009D5B7D /* http.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = http.png; sourceTree = "<group>"; };
9B5832101E742632009D5B7D /* kcptun_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = kcptun_1.png; sourceTree = "<group>"; };
9B5AA09F209C100C00E8B659 /* libsodium.23.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.23.dylib; sourceTree = "<group>"; }; 9B5AA09F209C100C00E8B659 /* libsodium.23.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.23.dylib; sourceTree = "<group>"; };
9B5AA0A2209C103900E8B659 /* libcares.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcares.2.dylib; sourceTree = "<group>"; }; 9B5AA0A2209C103900E8B659 /* libcares.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libcares.2.dylib; sourceTree = "<group>"; };
9B5AA0AB209C43C200E8B659 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; }; 9B5AA0AB209C43C200E8B659 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; 9B6BF9531E27B2570061B9A7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
9B7297E5214D68F800FD24AA /* libmbedcrypto.2.12.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libmbedcrypto.2.12.0.dylib; sourceTree = "<group>"; }; 9B7297E5214D68F800FD24AA /* libmbedcrypto.2.12.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libmbedcrypto.2.12.0.dylib; sourceTree = "<group>"; };
9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareServerProfilesWindowController.swift; sourceTree = "<group>"; };
9B7297ED214DA88A00FD24AA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ShareServerProfilesWindowController.xib; sourceTree = "<group>"; };
9B7297F0214DA89000FD24AA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/ShareServerProfilesWindowController.strings"; sourceTree = "<group>"; };
9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyInterfacesViewCtrl.swift; sourceTree = "<group>"; }; 9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxyInterfacesViewCtrl.swift; sourceTree = "<group>"; };
9B938D931E864B38005F5636 /* menu_g_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_g_icon.png; sourceTree = "<group>"; }; 9B938D931E864B38005F5636 /* menu_g_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_g_icon.png; sourceTree = "<group>"; };
9B938D941E864B38005F5636 /* menu_g_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_g_icon@2x.png"; sourceTree = "<group>"; }; 9B938D941E864B38005F5636 /* menu_g_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_g_icon@2x.png"; sourceTree = "<group>"; };
@ -308,7 +311,6 @@
9B938D961E864B38005F5636 /* menu_m_icon@2x.png */, 9B938D961E864B38005F5636 /* menu_m_icon@2x.png */,
9B938D971E864B38005F5636 /* menu_p_icon.png */, 9B938D971E864B38005F5636 /* menu_p_icon.png */,
9B938D981E864B38005F5636 /* menu_p_icon@2x.png */, 9B938D981E864B38005F5636 /* menu_p_icon@2x.png */,
9B5832101E742632009D5B7D /* kcptun_1.png */,
9B58320A1E7422DB009D5B7D /* http.png */, 9B58320A1E7422DB009D5B7D /* http.png */,
9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */, 9B5832061E7421B2009D5B7D /* virtual-server-icon-3.png */,
9B5832021E741F8D009D5B7D /* command-512.png */, 9B5832021E741F8D009D5B7D /* command-512.png */,
@ -411,6 +413,8 @@
9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */, 9B86459C1E7C2CAD00A84029 /* ProxyInterfacesViewCtrl.swift */,
9B3546701E802B1200B510B4 /* ToastWindowController.swift */, 9B3546701E802B1200B510B4 /* ToastWindowController.swift */,
9B3546711E802B1200B510B4 /* ToastWindowController.xib */, 9B3546711E802B1200B510B4 /* ToastWindowController.xib */,
9B7297E8214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift */,
9B7297EE214DA88A00FD24AA /* ShareServerProfilesWindowController.xib */,
); );
name = UI; name = UI;
sourceTree = "<group>"; sourceTree = "<group>";
@ -622,7 +626,6 @@
C6D429971DA75988002A5711 /* stop_privoxy.sh in Resources */, C6D429971DA75988002A5711 /* stop_privoxy.sh in Resources */,
C8E42A6E1D4F2CAF0074C7EA /* UserRulesController.xib in Resources */, C8E42A6E1D4F2CAF0074C7EA /* UserRulesController.xib in Resources */,
9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */, 9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */,
9B5832111E742632009D5B7D /* kcptun_1.png in Resources */,
9B16E59A1F99FD0700E54DC5 /* icons8-Eye Filled-50.png in Resources */, 9B16E59A1F99FD0700E54DC5 /* icons8-Eye Filled-50.png in Resources */,
9B938D9C1E864B38005F5636 /* menu_m_icon@2x.png in Resources */, 9B938D9C1E864B38005F5636 /* menu_m_icon@2x.png in Resources */,
9B3546731E802B1200B510B4 /* ToastWindowController.xib in Resources */, 9B3546731E802B1200B510B4 /* ToastWindowController.xib in Resources */,
@ -638,6 +641,7 @@
1C82DBA81FA96C7500B32551 /* obfs-local in Resources */, 1C82DBA81FA96C7500B32551 /* obfs-local in Resources */,
9B938D9D1E864B38005F5636 /* menu_p_icon.png in Resources */, 9B938D9D1E864B38005F5636 /* menu_p_icon.png in Resources */,
9B938D9B1E864B38005F5636 /* menu_m_icon.png in Resources */, 9B938D9B1E864B38005F5636 /* menu_m_icon.png in Resources */,
9B7297EC214DA88A00FD24AA /* ShareServerProfilesWindowController.xib in Resources */,
9B3FFF271D0898EB0019A709 /* gfwlist.txt in Resources */, 9B3FFF271D0898EB0019A709 /* gfwlist.txt in Resources */,
C6D429931DA75988002A5711 /* install_privoxy.sh in Resources */, C6D429931DA75988002A5711 /* install_privoxy.sh in Resources */,
9B58320B1E7422DB009D5B7D /* http.png in Resources */, 9B58320B1E7422DB009D5B7D /* http.png in Resources */,
@ -818,6 +822,7 @@
9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */, 9B5831F61E7302F8009D5B7D /* ShortcutsController.m in Sources */,
9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */, 9BB706A71D1B982300551F0E /* SWBApplication.m in Sources */,
9B3FFF1E1D0732660019A709 /* Utils.m in Sources */, 9B3FFF1E1D0732660019A709 /* Utils.m in Sources */,
9B7297EA214D7C6B00FD24AA /* ShareServerProfilesWindowController.swift in Sources */,
9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */, 9B3FFF321D08CEE40019A709 /* SWBQRCodeWindowController.m in Sources */,
9B3FFF211D08826E0019A709 /* PACUtils.swift in Sources */, 9B3FFF211D08826E0019A709 /* PACUtils.swift in Sources */,
9B3FFF141D0705810019A709 /* Notifications.swift in Sources */, 9B3FFF141D0705810019A709 /* Notifications.swift in Sources */,
@ -894,6 +899,15 @@
name = PreferencesWindowController.xib; name = PreferencesWindowController.xib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9B7297EE214DA88A00FD24AA /* ShareServerProfilesWindowController.xib */ = {
isa = PBXVariantGroup;
children = (
9B7297ED214DA88A00FD24AA /* Base */,
9B7297F0214DA89000FD24AA /* zh-Hans */,
);
name = ShareServerProfilesWindowController.xib;
sourceTree = "<group>";
};
9BAFE2E41E83ED7F00F71CCE /* PreferencesWinController.xib */ = { 9BAFE2E41E83ED7F00F71CCE /* PreferencesWinController.xib */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (

View File

@ -14,6 +14,7 @@ import RxSwift
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate { class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
var shareWinCtrl: ShareServerProfilesWindowController!
var qrcodeWinCtrl: SWBQRCodeWindowController! var qrcodeWinCtrl: SWBQRCodeWindowController!
var preferencesWinCtrl: PreferencesWindowController! var preferencesWinCtrl: PreferencesWindowController!
var editUserRulesWinCtrl: UserRulesController! var editUserRulesWinCtrl: UserRulesController!
@ -279,9 +280,50 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
} }
} }
@IBAction func showShareServerProfiles(_ sender: NSMenuItem) {
if shareWinCtrl != nil {
shareWinCtrl.close()
}
shareWinCtrl = ShareServerProfilesWindowController(windowNibName: NSNib.Name(rawValue: "ShareServerProfilesWindowController"))
shareWinCtrl.showWindow(self)
NSApp.activate(ignoringOtherApps: true)
shareWinCtrl.window?.makeKeyAndOrderFront(nil)
}
@IBAction func scanQRCodeFromScreen(_ sender: NSMenuItem) { @IBAction func scanQRCodeFromScreen(_ sender: NSMenuItem) {
ScanQRCodeOnScreen() ScanQRCodeOnScreen()
} }
@IBAction func importProfileURLFromPasteboard(_ sender: NSMenuItem) {
let pb = NSPasteboard.general
if #available(OSX 10.13, *) {
if let text = pb.string(forType: NSPasteboard.PasteboardType.URL) {
if let url = URL(string: text) {
NotificationCenter.default.post(
name: Notification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil
, userInfo: [
"urls": [url],
"source": "pasteboard",
])
}
}
}
if let text = pb.string(forType: NSPasteboard.PasteboardType.string) {
var urls = text.split(separator: "\n")
.map { String($0).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
urls = urls.filter { $0.scheme == "ss" }
NotificationCenter.default.post(
name: Notification.Name(rawValue: "NOTIFY_FOUND_SS_URL"), object: nil
, userInfo: [
"urls": urls,
"source": "pasteboard",
])
}
}
@IBAction func selectPACMode(_ sender: NSMenuItem) { @IBAction func selectPACMode(_ sender: NSMenuItem) {
let defaults = UserDefaults.standard let defaults = UserDefaults.standard
@ -528,29 +570,30 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
let urls: [URL] = userInfo["urls"] as! [URL] let urls: [URL] = userInfo["urls"] as! [URL]
let mgr = ServerProfileManager.instance let mgr = ServerProfileManager.instance
var isChanged = false var addCount = 0
var subtitle: String = ""
if userInfo["source"] as! String == "qrcode" {
subtitle = "By scan QR Code".localized
} else if userInfo["source"] as! String == "url" {
subtitle = "By handle SS URL".localized
} else if userInfo["source"] as! String == "pasteboard" {
subtitle = "By import from pasteboard".localized
}
for url in urls { for url in urls {
if let profile = ServerProfile(url: url) { if let profile = ServerProfile(url: url) {
mgr.profiles.append(profile) mgr.profiles.append(profile)
isChanged = true addCount = addCount + 1
var subtitle: String = ""
if userInfo["source"] as! String == "qrcode" {
subtitle = "By scan QR Code".localized
} else if userInfo["source"] as! String == "url" {
subtitle = "By Handle SS URL".localized
}
sendNotify("Add Shadowsocks Server Profile".localized, subtitle, "Host: \(profile.serverHost)")
} }
} }
if isChanged { if addCount > 0 {
sendNotify("Add \(addCount) Shadowsocks Server Profile".localized, subtitle, "")
mgr.save() mgr.save()
self.updateServersMenu() self.updateServersMenu()
} else { } else {
sendNotify("Not found valid qrcode of shadowsocks profile.", "", "") sendNotify("", "", "Not found valid qrcode or url of shadowsocks profile".localized)
} }
} }
} }

View File

@ -20,7 +20,9 @@
* ./AppDelegate.swift * ./AppDelegate.swift
*/ */
"Add Shadowsocks Server Profile" = "Add Shadowsocks Server Profile"; "Add \(addCount) Shadowsocks Server Profile" = "Add \(addCount) Shadowsocks Server Profile";
"Not found valid qrcode or url of shadowsocks profile" = "Not found valid qrcode or url of shadowsocks profile";
"By scan QR Code" = "By scan QR Code"; "By scan QR Code" = "By scan QR Code";

View File

@ -23,7 +23,6 @@
<outlet property="serverProfilesBeginSeparatorMenuItem" destination="4iN-w2-but" id="Jyu-48-AzD"/> <outlet property="serverProfilesBeginSeparatorMenuItem" destination="4iN-w2-but" id="Jyu-48-AzD"/>
<outlet property="serverProfilesEndSeparatorMenuItem" destination="3cf-dF-7dx" id="eyc-6W-nWV"/> <outlet property="serverProfilesEndSeparatorMenuItem" destination="3cf-dF-7dx" id="eyc-6W-nWV"/>
<outlet property="serversMenuItem" destination="u5M-hQ-VSc" id="8gp-SY-Y4U"/> <outlet property="serversMenuItem" destination="u5M-hQ-VSc" id="8gp-SY-Y4U"/>
<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"/>
</connections> </connections>
@ -72,21 +71,27 @@
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="4iN-w2-but"/> <menuItem isSeparatorItem="YES" id="4iN-w2-but"/>
<menuItem isSeparatorItem="YES" id="3cf-dF-7dx"/> <menuItem isSeparatorItem="YES" id="3cf-dF-7dx"/>
<menuItem title="Scan QR Code From Screen ..." id="Qe6-bF-paT">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="scanQRCodeFromScreen:" target="Voe-Tx-rLC" id="zQZ-IT-H4a"/>
</connections>
</menuItem>
<menuItem title="Show QR Code For Current Server..." id="R6A-96-Zcb">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showQRCodeForCurrentServer:" target="Voe-Tx-rLC" id="t45-Zk-cai"/>
</connections>
</menuItem>
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem title="Scan QR Code From Screen" id="Qe6-bF-paT">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="scanQRCodeFromScreen:" target="Voe-Tx-rLC" id="zQZ-IT-H4a"/>
</connections>
</menuItem>
<menuItem title="Import Server URLs From Pasteboard" id="7Eq-XD-K5c">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="importProfileURLFromPasteboard:" target="Voe-Tx-rLC" id="RwL-Ms-BaC"/>
</connections>
</menuItem>
<menuItem title="Share Server Profiles..." id="r5z-RB-LIZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showShareServerProfiles:" target="Voe-Tx-rLC" id="3Qv-n1-5h1"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="vwY-hQ-ZIz"/> <menuItem isSeparatorItem="YES" id="vwY-hQ-ZIz"/>
<menuItem title="Preferences..." keyEquivalent="," id="4CS-qD-zW5"> <menuItem title="Preferences..." keyEquivalent="," id="4CS-qD-zW5">
<connections> <connections>

View File

@ -29,20 +29,21 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Server Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" animationBehavior="default" id="F0z-JX-Cv5"> <window title="Server Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES"/>
<rect key="contentRect" x="423" y="365" width="590" height="564"/> <rect key="contentRect" x="423" y="365" width="557" height="301"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/> <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="se5-gp-TjO"> <view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="590" height="564"/> <rect key="frame" x="0.0" y="0.0" width="557" height="301"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<scrollView focusRingType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u4U-Ju-FJV"> <scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="u4U-Ju-FJV">
<rect key="frame" x="20" y="80" width="233" height="464"/> <rect key="frame" x="20" y="80" width="200" height="201"/>
<clipView key="contentView" focusRingType="none" id="1oC-7s-16v"> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="1" y="1" width="231" height="462"/> <clipView key="contentView" focusRingType="none" ambiguous="YES" id="1oC-7s-16v">
<rect key="frame" x="1" y="1" width="198" height="199"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" id="r91-ho-Lum"> <tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" id="r91-ho-Lum">
<rect key="frame" x="0.0" y="0.0" width="231" height="462"/> <rect key="frame" x="0.0" y="0.0" width="198" height="199"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -88,97 +89,106 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
</scrollView> </scrollView>
<box boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="oZ5-85-pwY"> <box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="oZ5-85-pwY">
<rect key="frame" x="273" y="61" width="297" height="483"/> <rect key="frame" x="240" y="61" width="297" height="220"/>
<view key="contentView" id="a3z-VL-3xV"> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="0.0" y="0.0" width="297" height="483"/> <view key="contentView" ambiguous="YES" id="a3z-VL-3xV">
<rect key="frame" x="0.0" y="0.0" width="297" height="220"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HPg-NP-kjP"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HPg-NP-kjP">
<rect key="frame" x="46" y="444" width="58" height="17"/> <rect key="frame" x="46" y="181" width="58" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Address:" id="yHs-XE-XEB"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Address:" id="yHs-XE-XEB">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Otb-CO-KUH"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Otb-CO-KUH">
<rect key="frame" x="212" y="446" width="9" height="17"/> <rect key="frame" x="212" y="183" width="9" height="17"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="5" id="JAU-I8-jVc"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title=":" id="Ibr-Gs-5Js"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title=":" id="Ibr-Gs-5Js">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<comboBox verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ef4-8J-eFn"> <comboBox verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ef4-8J-eFn">
<rect key="frame" x="110" y="409" width="166" height="26"/> <rect key="frame" x="110" y="146" width="166" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="11" id="5Qb-ey-uS5"> <comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="11" id="5Qb-ey-uS5">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</comboBoxCell> </comboBoxCell>
</comboBox> </comboBox>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kft-TG-O5S"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kft-TG-O5S">
<rect key="frame" x="30" y="414" width="74" height="17"/> <rect key="frame" x="30" y="151" width="74" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Encryption:" id="mOh-nD-7EX"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Encryption:" id="mOh-nD-7EX">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q3C-S0-iNn"> <textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q3C-S0-iNn">
<rect key="frame" x="110" y="287" width="170" height="22"/> <rect key="frame" x="110" y="24" width="170" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="oxu-qq-MGC"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="oxu-qq-MGC">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5eh-s9-YH8"> <textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5eh-s9-YH8">
<rect key="frame" x="110" y="319" width="170" height="22"/> <rect key="frame" x="110" y="56" width="170" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="dtO-2M-4yX"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="dtO-2M-4yX">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0PP-Ng-z0v"> <textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0PP-Ng-z0v">
<rect key="frame" x="110" y="351" width="135" height="22"/> <rect key="frame" x="110" y="88" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="IGs-KE-ySP"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="IGs-KE-ySP">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Baj-Yh-kmj"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Baj-Yh-kmj">
<rect key="frame" x="43" y="290" width="61" height="17"/> <rect key="frame" x="43" y="27" width="61" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Remarks:" id="MiN-VE-FtC"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Remarks:" id="MiN-VE-FtC">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DGz-Sq-t0c"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DGz-Sq-t0c">
<rect key="frame" x="58" y="354" width="46" height="17"/> <rect key="frame" x="58" y="91" width="46" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Plugin:" id="gDt-yZ-Mqs"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Plugin:" id="gDt-yZ-Mqs">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" ambiguous="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R4F-j7-GkK"> <textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R4F-j7-GkK">
<rect key="frame" x="110" y="441" width="96" height="22"/> <rect key="frame" x="110" y="178" width="96" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="IP" drawsBackground="YES" id="MWO-hQ-nMt"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="IP" drawsBackground="YES" id="MWO-hQ-nMt">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField verticalHuggingPriority="750" ambiguous="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lng-AE-g0J"> <textField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lng-AE-g0J">
<rect key="frame" x="223" y="441" width="50" height="22"/> <rect key="frame" x="223" y="178" width="50" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Port" drawsBackground="YES" id="e1F-jd-e0J"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Port" drawsBackground="YES" id="e1F-jd-e0J">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -191,17 +201,19 @@
<outlet property="formatter" destination="QZc-kt-D7Z" id="ZB9-Of-Xi9"/> <outlet property="formatter" destination="QZc-kt-D7Z" id="ZB9-Of-Xi9"/>
</connections> </connections>
</textField> </textField>
<tabView focusRingType="none" drawsBackground="NO" type="noTabsNoBorder" initialItem="aRD-sh-uEl" translatesAutoresizingMaskIntoConstraints="NO" id="DHz-Hx-kZ5"> <tabView focusRingType="none" fixedFrame="YES" drawsBackground="NO" type="noTabsNoBorder" initialItem="aRD-sh-uEl" translatesAutoresizingMaskIntoConstraints="NO" id="DHz-Hx-kZ5">
<rect key="frame" x="110" y="383" width="135" height="22"/> <rect key="frame" x="110" y="120" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<tabViewItems> <tabViewItems>
<tabViewItem label="Secure" identifier="secure" id="aRD-sh-uEl"> <tabViewItem label="Secure" identifier="secure" id="aRD-sh-uEl">
<view key="view" id="rXs-ye-nly"> <view key="view" ambiguous="YES" id="rXs-ye-nly">
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/> <rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<secureTextField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZXA-oi-Zv3"> <secureTextField verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZXA-oi-Zv3">
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/> <rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="zuN-Jd-LeS"> <secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="zuN-Jd-LeS">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -212,11 +224,6 @@
</secureTextFieldCell> </secureTextFieldCell>
</secureTextField> </secureTextField>
</subviews> </subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="ZXA-oi-Zv3" secondAttribute="trailing" id="N5U-Eb-r6N"/>
<constraint firstItem="ZXA-oi-Zv3" firstAttribute="leading" secondItem="rXs-ye-nly" secondAttribute="leading" id="kHp-7I-hYq"/>
<constraint firstItem="ZXA-oi-Zv3" firstAttribute="top" secondItem="rXs-ye-nly" secondAttribute="top" id="oRQ-oR-A5o"/>
</constraints>
</view> </view>
</tabViewItem> </tabViewItem>
<tabViewItem label="Insecure" identifier="insecure" id="SYv-Tn-tur"> <tabViewItem label="Insecure" identifier="insecure" id="SYv-Tn-tur">
@ -224,8 +231,9 @@
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/> <rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3Ad-X4-DBi"> <textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Ad-X4-DBi">
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/> <rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="DNt-0X-3Z0"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="DNt-0X-3Z0">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -233,20 +241,13 @@
</textFieldCell> </textFieldCell>
</textField> </textField>
</subviews> </subviews>
<constraints>
<constraint firstItem="3Ad-X4-DBi" firstAttribute="top" secondItem="Z6k-mP-BgX" secondAttribute="top" id="GRU-7C-6XV"/>
<constraint firstAttribute="trailing" secondItem="3Ad-X4-DBi" secondAttribute="trailing" id="cRR-i4-0ty"/>
<constraint firstItem="3Ad-X4-DBi" firstAttribute="leading" secondItem="Z6k-mP-BgX" secondAttribute="leading" id="s6R-BF-mOG"/>
</constraints>
</view> </view>
</tabViewItem> </tabViewItem>
</tabViewItems> </tabViewItems>
</tabView> </tabView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jDI-cn-wYa"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jDI-cn-wYa">
<rect key="frame" x="254" y="381" width="26" height="25"/> <rect key="frame" x="254" y="118" width="26" height="25"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="26" id="vGB-iL-nWP"/>
</constraints>
<buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="icons8-Blind Filled-50" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="axesIndependently" inset="2" id="8Bw-DM-zYg"> <buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="icons8-Blind Filled-50" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="axesIndependently" inset="2" id="8Bw-DM-zYg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -255,24 +256,27 @@
<action selector="togglePasswordVisible:" target="-2" id="3kh-Pz-mr0"/> <action selector="togglePasswordVisible:" target="-2" id="3kh-Pz-mr0"/>
</connections> </connections>
</button> </button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qfe-Ql-iCO"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qfe-Ql-iCO">
<rect key="frame" x="38" y="385" width="66" height="17"/> <rect key="frame" x="38" y="122" width="66" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Password:" id="kil-xC-HPD"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Password:" id="kil-xC-HPD">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4LD-CD-u7b"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4LD-CD-u7b">
<rect key="frame" x="26" y="321" width="78" height="17"/> <rect key="frame" x="26" y="58" width="78" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Plugin Opts:" id="orT-7j-dxE"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Plugin Opts:" id="orT-7j-dxE">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
</textField> </textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jch-rI-Swc"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jch-rI-Swc">
<rect key="frame" x="254" y="349" width="25" height="25"/> <rect key="frame" x="254" y="86" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" imagePosition="left" borderStyle="border" inset="2" id="IF1-Fy-5la"> <buttonCell key="cell" type="help" bezelStyle="helpButton" imagePosition="left" borderStyle="border" inset="2" id="IF1-Fy-5la">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -282,62 +286,19 @@
</connections> </connections>
</button> </button>
</subviews> </subviews>
<constraints>
<constraint firstItem="Baj-Yh-kmj" firstAttribute="baseline" secondItem="q3C-S0-iNn" secondAttribute="baseline" id="0Fs-Yi-WBo"/>
<constraint firstItem="lng-AE-g0J" firstAttribute="leading" secondItem="Otb-CO-KUH" secondAttribute="trailing" constant="4" id="0bx-FE-Nqz"/>
<constraint firstItem="R4F-j7-GkK" firstAttribute="baseline" secondItem="lng-AE-g0J" secondAttribute="baseline" id="0dG-a6-exG"/>
<constraint firstItem="5eh-s9-YH8" firstAttribute="top" secondItem="0PP-Ng-z0v" secondAttribute="bottom" constant="10" symbolic="YES" id="1aa-nq-sFq"/>
<constraint firstItem="DHz-Hx-kZ5" firstAttribute="leading" secondItem="Qfe-Ql-iCO" secondAttribute="trailing" constant="8" symbolic="YES" id="1bR-S3-Tdq"/>
<constraint firstItem="ef4-8J-eFn" firstAttribute="top" secondItem="R4F-j7-GkK" secondAttribute="bottom" constant="8" symbolic="YES" id="41P-Ix-it2"/>
<constraint firstItem="jDI-cn-wYa" firstAttribute="trailing" secondItem="0PP-Ng-z0v" secondAttribute="trailing" constant="35" id="5DS-4j-Cr9"/>
<constraint firstAttribute="trailing" secondItem="lng-AE-g0J" secondAttribute="trailing" constant="24" id="6W3-BA-gYo"/>
<constraint firstItem="0PP-Ng-z0v" firstAttribute="centerY" secondItem="DGz-Sq-t0c" secondAttribute="centerY" id="99Q-Zk-mdj"/>
<constraint firstItem="R4F-j7-GkK" firstAttribute="top" secondItem="a3z-VL-3xV" secondAttribute="top" constant="20" symbolic="YES" id="Dkz-UA-ymg"/>
<constraint firstItem="0PP-Ng-z0v" firstAttribute="top" secondItem="DHz-Hx-kZ5" secondAttribute="bottom" constant="10" id="Fwl-Mu-bKH"/>
<constraint firstItem="DHz-Hx-kZ5" firstAttribute="top" secondItem="ef4-8J-eFn" secondAttribute="bottom" constant="8" symbolic="YES" id="I7k-9o-SLi"/>
<constraint firstItem="jDI-cn-wYa" firstAttribute="leading" secondItem="DHz-Hx-kZ5" secondAttribute="trailing" constant="9" id="IMV-t5-Set"/>
<constraint firstItem="jDI-cn-wYa" firstAttribute="trailing" secondItem="q3C-S0-iNn" secondAttribute="trailing" id="Idl-zd-MSU"/>
<constraint firstItem="5eh-s9-YH8" firstAttribute="centerY" secondItem="4LD-CD-u7b" secondAttribute="centerY" id="Ik2-zM-uly"/>
<constraint firstItem="DHz-Hx-kZ5" firstAttribute="leading" secondItem="q3C-S0-iNn" secondAttribute="leading" id="JBi-Dy-gPS"/>
<constraint firstAttribute="trailing" secondItem="jDI-cn-wYa" secondAttribute="trailing" constant="17" id="JSl-uN-iMh"/>
<constraint firstItem="ef4-8J-eFn" firstAttribute="leading" secondItem="DHz-Hx-kZ5" secondAttribute="leading" id="L85-np-Xpn"/>
<constraint firstItem="q3C-S0-iNn" firstAttribute="leading" secondItem="Baj-Yh-kmj" secondAttribute="trailing" constant="8" symbolic="YES" id="LTy-e7-h27"/>
<constraint firstItem="q3C-S0-iNn" firstAttribute="top" secondItem="5eh-s9-YH8" secondAttribute="bottom" constant="10" symbolic="YES" id="ORc-LV-dNZ"/>
<constraint firstItem="DHz-Hx-kZ5" firstAttribute="bottom" secondItem="jDI-cn-wYa" secondAttribute="bottom" id="RM1-vq-zAm"/>
<constraint firstItem="Kft-TG-O5S" firstAttribute="centerY" secondItem="ef4-8J-eFn" secondAttribute="centerY" id="RYD-o2-vjB"/>
<constraint firstItem="Kft-TG-O5S" firstAttribute="leading" secondItem="a3z-VL-3xV" secondAttribute="leading" constant="32" id="TsS-51-KLG"/>
<constraint firstItem="ef4-8J-eFn" firstAttribute="leading" secondItem="Kft-TG-O5S" secondAttribute="trailing" constant="8" symbolic="YES" id="VGj-2V-kmi"/>
<constraint firstItem="Otb-CO-KUH" firstAttribute="leading" secondItem="R4F-j7-GkK" secondAttribute="trailing" constant="8" symbolic="YES" id="dL8-po-Zhh"/>
<constraint firstItem="jch-rI-Swc" firstAttribute="centerX" secondItem="jDI-cn-wYa" secondAttribute="centerX" id="eTA-qv-ID2"/>
<constraint firstItem="HPg-NP-kjP" firstAttribute="baseline" secondItem="R4F-j7-GkK" secondAttribute="baseline" id="fb7-sB-eHL"/>
<constraint firstItem="DHz-Hx-kZ5" firstAttribute="top" secondItem="jDI-cn-wYa" secondAttribute="top" id="fbb-RF-Wvf"/>
<constraint firstItem="Qfe-Ql-iCO" firstAttribute="leading" secondItem="a3z-VL-3xV" secondAttribute="leading" constant="40" id="fw1-Zg-LaZ"/>
<constraint firstItem="lng-AE-g0J" firstAttribute="trailing" secondItem="ef4-8J-eFn" secondAttribute="trailing" id="gxe-uL-y5w"/>
<constraint firstItem="R4F-j7-GkK" firstAttribute="leading" secondItem="HPg-NP-kjP" secondAttribute="trailing" constant="8" symbolic="YES" id="hKJ-ZW-uLY"/>
<constraint firstItem="jDI-cn-wYa" firstAttribute="trailing" secondItem="5eh-s9-YH8" secondAttribute="trailing" id="jLS-U1-IJa"/>
<constraint firstItem="0PP-Ng-z0v" firstAttribute="leading" secondItem="DHz-Hx-kZ5" secondAttribute="leading" id="kEJ-EJ-v0T"/>
<constraint firstItem="Otb-CO-KUH" firstAttribute="top" secondItem="lng-AE-g0J" secondAttribute="top" id="kLj-6Z-ZDw"/>
<constraint firstItem="5eh-s9-YH8" firstAttribute="leading" secondItem="DHz-Hx-kZ5" secondAttribute="leading" id="kXe-DX-Fsi"/>
<constraint firstItem="0PP-Ng-z0v" firstAttribute="leading" secondItem="DGz-Sq-t0c" secondAttribute="trailing" constant="8" symbolic="YES" id="mcp-te-11z"/>
<constraint firstItem="R4F-j7-GkK" firstAttribute="leading" secondItem="ef4-8J-eFn" secondAttribute="leading" id="opP-Lo-SO0"/>
<constraint firstItem="5eh-s9-YH8" firstAttribute="leading" secondItem="4LD-CD-u7b" secondAttribute="trailing" constant="8" symbolic="YES" id="uFv-U2-g2d"/>
<constraint firstItem="Qfe-Ql-iCO" firstAttribute="centerY" secondItem="DHz-Hx-kZ5" secondAttribute="centerY" id="wKU-B5-A3m"/>
<constraint firstItem="jch-rI-Swc" firstAttribute="top" secondItem="0PP-Ng-z0v" secondAttribute="top" id="zzw-ge-6mH"/>
</constraints>
</view> </view>
<color key="fillColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="fillColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</box> </box>
<box boxType="custom" borderType="none" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="Gqv-5O-Wwf"> <box fixedFrame="YES" boxType="custom" borderType="none" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="Gqv-5O-Wwf">
<rect key="frame" x="20" y="61" width="233" height="19"/> <rect key="frame" x="20" y="61" width="200" height="19"/>
<view key="contentView" id="MbP-FS-YEs"> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="0.0" y="0.0" width="233" height="19"/> <view key="contentView" ambiguous="YES" id="MbP-FS-YEs">
<rect key="frame" x="0.0" y="0.0" width="200" height="19"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4ig-lF-m2R"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4ig-lF-m2R">
<rect key="frame" x="0.0" y="-1" width="22" height="21"/> <rect key="frame" x="0.0" y="-1" width="22" height="21"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="22" id="sgm-c9-nQI"/>
</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="VkE-bG-mJb"> <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="VkE-bG-mJb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -346,11 +307,9 @@
<action selector="addProfile:" target="-2" id="NEh-IP-KO8"/> <action selector="addProfile:" target="-2" id="NEh-IP-KO8"/>
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3ei-2f-kHB"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3ei-2f-kHB">
<rect key="frame" x="20" y="-1" width="22" height="21"/> <rect key="frame" x="20" y="-1" width="22" height="21"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="22" id="LYH-ae-nU6"/>
</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="Nu6-Aw-ihE"> <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Nu6-Aw-ihE">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -360,21 +319,12 @@
</connections> </connections>
</button> </button>
</subviews> </subviews>
<constraints>
<constraint firstItem="4ig-lF-m2R" firstAttribute="leading" secondItem="MbP-FS-YEs" secondAttribute="leading" id="25E-Gc-eBh"/>
<constraint firstItem="4ig-lF-m2R" firstAttribute="top" secondItem="MbP-FS-YEs" secondAttribute="top" id="3Ss-BU-JcV"/>
<constraint firstItem="4ig-lF-m2R" firstAttribute="baseline" secondItem="3ei-2f-kHB" secondAttribute="baseline" id="Pp6-ir-tQB"/>
<constraint firstAttribute="bottom" secondItem="4ig-lF-m2R" secondAttribute="bottom" id="dIB-U3-h90"/>
<constraint firstItem="3ei-2f-kHB" firstAttribute="leading" secondItem="MbP-FS-YEs" secondAttribute="leading" constant="20" symbolic="YES" id="fOJ-7v-G14"/>
</constraints>
</view> </view>
<color key="fillColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/> <color key="fillColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
</box> </box>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yyD-qq-aSk"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yyD-qq-aSk">
<rect key="frame" x="495" y="13" width="81" height="32"/> <rect key="frame" x="462" y="13" width="81" height="32"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="69" id="SDg-E4-Aza"/>
</constraints>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zPE-oD-PwK"> <buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zPE-oD-PwK">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -386,11 +336,9 @@ DQ
<action selector="ok:" target="-2" id="ifI-e4-Htf"/> <action selector="ok:" target="-2" id="ifI-e4-Htf"/>
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kGm-Zm-ipY"> <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kGm-Zm-ipY">
<rect key="frame" x="414" y="13" width="81" height="32"/> <rect key="frame" x="381" y="13" width="81" height="32"/>
<constraints> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraint firstAttribute="width" constant="69" id="P30-kh-sbD"/>
</constraints>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="j03-9E-uHW"> <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="j03-9E-uHW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
@ -403,28 +351,11 @@ Gw
</connections> </connections>
</button> </button>
</subviews> </subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="oZ5-85-pwY" secondAttribute="trailing" constant="20" symbolic="YES" id="6XB-Xo-ULh"/>
<constraint firstItem="oZ5-85-pwY" firstAttribute="trailing" secondItem="yyD-qq-aSk" secondAttribute="trailing" id="CEw-Ad-ctb"/>
<constraint firstItem="u4U-Ju-FJV" firstAttribute="top" secondItem="oZ5-85-pwY" secondAttribute="top" id="IMn-dq-QUj"/>
<constraint firstItem="Gqv-5O-Wwf" firstAttribute="top" secondItem="u4U-Ju-FJV" secondAttribute="bottom" id="IYg-B8-HlS"/>
<constraint firstItem="yyD-qq-aSk" firstAttribute="leading" secondItem="kGm-Zm-ipY" secondAttribute="trailing" constant="12" symbolic="YES" id="JBX-pg-6SB"/>
<constraint firstAttribute="bottom" secondItem="kGm-Zm-ipY" secondAttribute="bottom" constant="20" symbolic="YES" id="LQl-3C-29i"/>
<constraint firstItem="oZ5-85-pwY" firstAttribute="leading" secondItem="Gqv-5O-Wwf" secondAttribute="trailing" constant="20" symbolic="YES" id="bvQ-7l-IZO"/>
<constraint firstItem="u4U-Ju-FJV" firstAttribute="leading" secondItem="Gqv-5O-Wwf" secondAttribute="leading" id="jtv-6s-2wd"/>
<constraint firstItem="oZ5-85-pwY" firstAttribute="leading" secondItem="u4U-Ju-FJV" secondAttribute="trailing" constant="20" symbolic="YES" id="nkb-ca-YNA"/>
<constraint firstItem="kGm-Zm-ipY" firstAttribute="top" secondItem="oZ5-85-pwY" secondAttribute="bottom" constant="20" symbolic="YES" id="rEF-Az-fO2"/>
<constraint firstItem="Gqv-5O-Wwf" firstAttribute="bottom" secondItem="oZ5-85-pwY" secondAttribute="bottom" id="sb3-Xg-ZeX"/>
<constraint firstItem="kGm-Zm-ipY" firstAttribute="baseline" secondItem="yyD-qq-aSk" secondAttribute="baseline" id="tvk-rK-yV5"/>
<constraint firstItem="u4U-Ju-FJV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="vkk-Ys-and"/>
<constraint firstItem="u4U-Ju-FJV" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="yg3-ls-7P7"/>
<constraint firstItem="oZ5-85-pwY" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="273" id="zO0-Le-Yu1"/>
</constraints>
</view> </view>
<connections> <connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/> <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections> </connections>
<point key="canvasLocation" x="406" y="177"/> <point key="canvasLocation" x="389.5" y="45.5"/>
</window> </window>
<numberFormatter formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="QZc-kt-D7Z" userLabel="Port Number Formatter"> <numberFormatter formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="QZc-kt-D7Z" userLabel="Port Number Formatter">
<real key="minimum" value="1"/> <real key="minimum" value="1"/>

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ShareServerProfilesWindowController" customModule="ShadowsocksX_NG" customModuleProvider="target">
<connections>
<outlet property="copyAllServerURLsButton" destination="PkN-2i-ex7" id="5gY-Vs-cfn"/>
<outlet property="copyQRCodeButton" destination="GPl-Kb-AZg" id="pId-jX-erp"/>
<outlet property="copyURLButton" destination="Myo-Gh-Sba" id="3uJ-PP-nOS"/>
<outlet property="profilesTableView" destination="LWb-xh-GIK" id="qqJ-Qa-fWq"/>
<outlet property="qrCodeImageView" destination="atP-ge-P5H" id="Wc7-Ht-oJE"/>
<outlet property="saveAllServerURLsAsFileButton" destination="WOW-rs-fyP" id="A7T-IN-ub2"/>
<outlet property="saveQRCodeAsFileButton" destination="4WD-tD-JDA" id="Bfg-wE-8ls"/>
<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="Share Server Profiles" 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"/>
<rect key="contentRect" x="562" y="341" width="508" height="386"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="508" height="386"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d0q-qb-o6U">
<rect key="frame" x="20" y="86" width="200" height="280"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" id="G7B-Z5-glP">
<rect key="frame" x="1" y="1" width="198" height="278"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="LWb-xh-GIK">
<rect key="frame" x="0.0" y="0.0" width="198" height="278"/>
<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="" editable="NO" width="195" minWidth="40" maxWidth="1000" id="BVB-7v-GrU">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Server">
<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" title="Text Cell" id="Z7X-TN-APg">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="cellTitle" id="a8X-ox-vkA">
<rect key="frame" x="1" y="1" width="195" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kni-HI-MDc">
<rect key="frame" x="0.0" y="0.0" width="195" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="EIQ-Bg-vVe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="Kni-HI-MDc" id="wnh-Vn-cT5"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="4GA-ch-rOE"/>
<outlet property="delegate" destination="-2" id="IdR-br-h7j"/>
</connections>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="gkA-hM-aoX">
<rect key="frame" x="1" y="224" width="178" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="UQv-FS-DZa">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="atP-ge-P5H">
<rect key="frame" x="238" y="116" width="250" height="250"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="V51-A5-F2F"/>
</imageView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GPl-Kb-AZg">
<rect key="frame" x="232" y="46" width="262" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Copy QRCode" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="PrW-s6-Uab">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="copyQRCode:" target="-2" id="cLd-uU-Mqd"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4WD-tD-JDA">
<rect key="frame" x="232" y="13" width="262" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Save QRCode As File" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IaI-Rj-Kss">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="saveQRCodeAsFile:" target="-2" id="cHE-5h-cbg"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PkN-2i-ex7">
<rect key="frame" x="14" y="46" width="212" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Copy All Server URLs" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Yt2-p1-4w0">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="copyAllServerURLs:" target="-2" id="y34-hx-a7y"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WOW-rs-fyP">
<rect key="frame" x="14" y="13" width="212" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Save All Server URLs As File" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="9OS-xy-GB1">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="saveAllServerURLsAsFile:" target="-2" id="Urh-dz-B8d"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Myo-Gh-Sba">
<rect key="frame" x="232" y="79" width="262" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Copy URL" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="PHX-gY-lZe">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="copyURL:" target="-2" id="dqq-Ps-sEh"/>
</connections>
</button>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="143" y="207"/>
</window>
</objects>
</document>

View File

@ -36,8 +36,6 @@ class PreferencesWindowController: NSWindowController
var profileMgr: ServerProfileManager! var profileMgr: ServerProfileManager!
var editingProfile: ServerProfile! var editingProfile: ServerProfile!
var enabledKcptunSubDisosable: Disposable?
override func windowDidLoad() { override func windowDidLoad() {
@ -207,10 +205,7 @@ class PreferencesWindowController: NSWindowController
func bindProfile(_ index:Int) { func bindProfile(_ index:Int) {
NSLog("bind profile \(index)") NSLog("bind profile \(index)")
if let dis = enabledKcptunSubDisosable {
dis.dispose()
enabledKcptunSubDisosable = Optional.none
}
if index >= 0 && index < profileMgr.profiles.count { if index >= 0 && index < profileMgr.profiles.count {
editingProfile = profileMgr.profiles[index] editingProfile = profileMgr.profiles[index]

View File

@ -0,0 +1,189 @@
//
// ShareServerProfilesWindowController.swift
// ShadowsocksX-NG
//
// Created by on 2018/9/16.
// Copyright © 2018 qiuyuzhou. All rights reserved.
//
import Cocoa
class ShareServerProfilesWindowController: NSWindowController
, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet weak var profilesTableView: NSTableView!
@IBOutlet weak var qrCodeImageView: NSImageView!
@IBOutlet weak var copyAllServerURLsButton: NSButton!
@IBOutlet weak var saveAllServerURLsAsFileButton: NSButton!
@IBOutlet weak var copyURLButton: NSButton!
@IBOutlet weak var copyQRCodeButton: NSButton!
@IBOutlet weak var saveQRCodeAsFileButton: NSButton!
var defaults: UserDefaults!
var profileMgr: ServerProfileManager!
override func windowDidLoad() {
super.windowDidLoad()
defaults = UserDefaults.standard
profileMgr = ServerProfileManager.instance
if !profileMgr.profiles.isEmpty {
let index = IndexSet(integer: 0)
profilesTableView.selectRowIndexes(index, byExtendingSelection: false)
} else {
copyAllServerURLsButton.isEnabled = false
saveAllServerURLsAsFileButton.isEnabled = false
copyURLButton.isEnabled = false
copyQRCodeButton.isEnabled = false
saveQRCodeAsFileButton.isEnabled = false
}
}
@IBAction func copyURL(_ sender: NSButton) {
let profile = getSelectedProfile()
if profile.isValid(), let url = profile.URL() {
let pb = NSPasteboard.general
pb.clearContents()
if pb.writeObjects([url as NSPasteboardWriting]) {
NSLog("Copy URL to clipboard")
} else {
NSLog("Failed to copy URL to clipboard")
}
}
}
@IBAction func copyQRCode(_ sender: NSButton) {
if let img = qrCodeImageView.image {
let pb = NSPasteboard.general
pb.clearContents()
if pb.writeObjects([img as NSPasteboardWriting]) {
NSLog("Copy QRCode to clipboard")
} else {
NSLog("Failed to copy QRCode to clipboard")
}
}
}
@IBAction func saveQRCodeAsFile(_ sender: NSButton) {
if let img = qrCodeImageView.image {
let savePanel = NSSavePanel()
savePanel.title = "Save All Server URLs To File".localized
savePanel.canCreateDirectories = true
savePanel.allowedFileTypes = ["gif"]
savePanel.isExtensionHidden = false
let profile = getSelectedProfile()
if profile.remark.isEmpty {
savePanel.nameFieldStringValue = "shadowsocks_qrcode.gif"
} else {
savePanel.nameFieldStringValue = "shadowsocks_qrcode_\(profile.remark).gif"
}
savePanel.becomeKey()
let result = savePanel.runModal()
if (result.rawValue == NSFileHandlingPanelOKButton && (savePanel.url) != nil) {
let imgRep = NSBitmapImageRep(data: img.tiffRepresentation!)
let data = imgRep?.representation(using: NSBitmapImageRep.FileType.gif, properties: [:])
try! data?.write(to: savePanel.url!)
}
}
}
@IBAction func copyAllServerURLs(_ sender: NSButton) {
let pb = NSPasteboard.general
pb.clearContents()
if pb.writeObjects([getAllServerURLs() as NSPasteboardWriting]) {
NSLog("Copy all server URLs to clipboard")
} else {
NSLog("Failed to all server URLs to clipboard")
}
}
@IBAction func saveAllServerURLsAsFile(_ sender: NSButton) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
let date_string = formatter.string(from: Date())
let savePanel = NSSavePanel()
savePanel.title = "Save All Server URLs To File".localized
savePanel.canCreateDirectories = true
savePanel.allowedFileTypes = ["txt"]
savePanel.isExtensionHidden = false
savePanel.nameFieldStringValue = "shadowsocks_profiles_\(date_string).txt"
savePanel.becomeKey()
let result = savePanel.runModal()
if (result.rawValue == NSFileHandlingPanelOKButton && (savePanel.url) != nil) {
let urls = getAllServerURLs()
try! urls.write(to: (savePanel.url)!, atomically: true, encoding: String.Encoding.utf8)
}
}
func getAllServerURLs() -> String {
let urls = profileMgr.profiles.filter({ (profile) -> Bool in
return profile.isValid()
}).map { (profile) -> String in
return profile.URL()!.absoluteString
}
return urls.joined(separator: "\n")
}
func getSelectedProfile() -> ServerProfile {
let i = profilesTableView.selectedRow
return profileMgr.profiles[i]
}
func getDataAtRow(_ index:Int) -> String {
let profile = profileMgr.profiles[index]
if !profile.remark.isEmpty {
return profile.remark
} else {
return profile.serverHost
}
}
//--------------------------------------------------
// For NSTableViewDataSource
func numberOfRows(in tableView: NSTableView) -> Int {
if let mgr = profileMgr {
return mgr.profiles.count
}
return 0
}
//--------------------------------------------------
// For NSTableViewDelegate
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let colId = NSUserInterfaceItemIdentifier(rawValue: "cellTitle")
if let cell = tableView.makeView(withIdentifier: colId, owner: self) as? NSTableCellView {
cell.textField?.stringValue = getDataAtRow(row)
return cell
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification) {
if profilesTableView.selectedRow >= 0 {
let profile = getSelectedProfile()
if profile.isValid(), let url = profile.URL() {
let img = createQRImage(url.absoluteString, NSMakeSize(250, 250))
qrCodeImageView.image = img
copyURLButton.isEnabled = true
copyQRCodeButton.isEnabled = true
saveQRCodeAsFileButton.isEnabled = true
return
}
}
qrCodeImageView.image = nil
copyURLButton.isEnabled = false
copyQRCodeButton.isEnabled = false
saveQRCodeAsFileButton.isEnabled = false
}
}

View File

@ -11,4 +11,6 @@
void ScanQRCodeOnScreen(); void ScanQRCodeOnScreen();
NSImage* createQRImage(NSString *string, NSSize size);
#endif /* QRCodeUtils_h */ #endif /* QRCodeUtils_h */

View File

@ -8,6 +8,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <CoreImage/CoreImage.h> #import <CoreImage/CoreImage.h>
#import <AppKit/AppKit.h>
void ScanQRCodeOnScreen() { void ScanQRCodeOnScreen() {
/* displays[] Quartz display ID's */ /* displays[] Quartz display ID's */
@ -75,3 +76,42 @@ void ScanQRCodeOnScreen() {
} }
]; ];
} }
NSImage* createQRImage(NSString *string, NSSize size) {
NSImage *outputImage = [[NSImage alloc]initWithSize:size];
[outputImage lockFocus];
// Setup the QR filter with our string
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[filter setValue:data forKey:@"inputMessage"];
/*
L: 7%
M: 15%
Q: 25%
H: 30%
*/
[filter setValue:@"Q" forKey:@"inputCorrectionLevel"];
CIImage *image = [filter valueForKey:@"outputImage"];
// Calculate the size of the generated image and the scale for the desired image size
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size.width / CGRectGetWidth(extent), size.height / CGRectGetHeight(extent));
CGImageRef bitmapImage = [NSGraphicsContext.currentContext.CIContext createCGImage:image fromRect:extent];
CGContextRef graphicsContext = NSGraphicsContext.currentContext.CGContext;
CGContextSetInterpolationQuality(graphicsContext, kCGInterpolationNone);
CGContextScaleCTM(graphicsContext, scale, scale);
CGContextDrawImage(graphicsContext, extent, bitmapImage);
// Cleanup
CGImageRelease(bitmapImage);
[outputImage unlockFocus];
return outputImage;
}

View File

@ -26,7 +26,9 @@
* ./AppDelegate.swift * ./AppDelegate.swift
*/ */
"Add Shadowsocks Server Profile" = "已添加Shadowsocks服务器配置"; "Add \(addCount) Shadowsocks Server Profile" = "已添加\(addCount)个Shadowsocks服务器配置";
"Not found valid qrcode or url of shadowsocks profile" = "没有找到有效的shadowsocks配置二维码或链接";
"By scan QR Code" = "通过扫描二维码"; "By scan QR Code" = "通过扫描二维码";

View File

@ -2,6 +2,9 @@
/* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "4CS-qD-zW5"; */ /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "4CS-qD-zW5"; */
"4CS-qD-zW5.title" = "偏好设置..."; "4CS-qD-zW5.title" = "偏好设置...";
/* Class = "NSMenuItem"; title = "Import Server URLs From Pasteboard"; ObjectID = "7Eq-XD-K5c"; */
"7Eq-XD-K5c.title" = "从剪贴板导入服务器配置链接";
/* Class = "NSMenuItem"; title = "应用用户自定规则到 PAC"; ObjectID = "6qf-cg-HXc"; */ /* Class = "NSMenuItem"; title = "应用用户自定规则到 PAC"; ObjectID = "6qf-cg-HXc"; */
"6qf-cg-HXc.title" = "应用用户自定规则到 PAC"; "6qf-cg-HXc.title" = "应用用户自定规则到 PAC";
@ -30,7 +33,7 @@
"Mw3-Jm-eXA.title" = "全局模式"; "Mw3-Jm-eXA.title" = "全局模式";
/* Class = "NSMenuItem"; title = "扫描屏幕上的二维码..."; ObjectID = "Qe6-bF-paT"; */ /* Class = "NSMenuItem"; title = "扫描屏幕上的二维码..."; ObjectID = "Qe6-bF-paT"; */
"Qe6-bF-paT.title" = "扫描屏幕上的二维码..."; "Qe6-bF-paT.title" = "扫描屏幕上的二维码";
/* Class = "NSMenuItem"; title = "显示当前服务器的二维码..."; ObjectID = "R6A-96-Zcb"; */ /* Class = "NSMenuItem"; title = "显示当前服务器的二维码..."; ObjectID = "R6A-96-Zcb"; */
"R6A-96-Zcb.title" = "显示当前服务器的二维码..."; "R6A-96-Zcb.title" = "显示当前服务器的二维码...";
@ -98,4 +101,5 @@
/* Class = "NSMenuItem"; title = "Preferences"; ObjectID = "iVn-LD-Ynd"; */ /* Class = "NSMenuItem"; title = "Preferences"; ObjectID = "iVn-LD-Ynd"; */
"iVn-LD-Ynd.title" = "偏好设置"; "iVn-LD-Ynd.title" = "偏好设置";
/* Class = "NSMenuItem"; title = "Share Server Profiles..."; ObjectID = "r5z-RB-LIZ"; */
"r5z-RB-LIZ.title" = "分享服务器配置...";

View File

@ -0,0 +1,19 @@
/* Class = "NSButtonCell"; title = "Save All Server URLs As File"; ObjectID = "9OS-xy-GB1"; */
"9OS-xy-GB1.title" = "保存所有服务器链接到文件";
/* Class = "NSWindow"; title = "Share Server Profiles"; ObjectID = "F0z-JX-Cv5"; */
"F0z-JX-Cv5.title" = "分享服务器配置";
/* Class = "NSButtonCell"; title = "Save QRCode As File"; ObjectID = "IaI-Rj-Kss"; */
"IaI-Rj-Kss.title" = "保存二维码到文件";
/* Class = "NSButtonCell"; title = "Copy URL"; ObjectID = "PHX-gY-lZe"; */
"PHX-gY-lZe.title" = "复制链接";
/* Class = "NSButtonCell"; title = "Copy QRCode"; ObjectID = "PrW-s6-Uab"; */
"PrW-s6-Uab.title" = "复制二维码";
/* Class = "NSButtonCell"; title = "Copy All Server URLs"; ObjectID = "Yt2-p1-4w0"; */
"Yt2-p1-4w0.title" = "复制所有服务器链接";