Feature: Add multi server proxy editor from ss-ng
This commit is contained in:
parent
7d6745e2f0
commit
3f407a2c29
|
@ -13,6 +13,9 @@
|
|||
492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C4868210EE6B9004554A0 /* ApiRequest.swift */; };
|
||||
492C4871210EF62E004554A0 /* ClashConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492C4870210EF62E004554A0 /* ClashConfig.swift */; };
|
||||
4952C3BF2115C7CA004A4FA8 /* ProxyMenuItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4952C3BE2115C7CA004A4FA8 /* ProxyMenuItemFactory.swift */; };
|
||||
4952C3CB2116E952004A4FA8 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4952C3C82116E952004A4FA8 /* PreferencesWindowController.swift */; };
|
||||
4952C3CC2116E952004A4FA8 /* PreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4952C3CA2116E952004A4FA8 /* PreferencesWindowController.xib */; };
|
||||
4952C3CE2116EA2E004A4FA8 /* ProxyServerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4952C3CD2116EA2E004A4FA8 /* ProxyServerModel.swift */; };
|
||||
495340B020DE5F7200B0D3FF /* StatusItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 495340AF20DE5F7200B0D3FF /* StatusItemView.xib */; };
|
||||
495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495340B220DE68C300B0D3FF /* StatusItemView.swift */; };
|
||||
495A44BF20D2660A00888A0A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495A44BE20D2660A00888A0A /* AppDelegate.swift */; };
|
||||
|
@ -75,6 +78,9 @@
|
|||
492C4868210EE6B9004554A0 /* ApiRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiRequest.swift; sourceTree = "<group>"; };
|
||||
492C4870210EF62E004554A0 /* ClashConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashConfig.swift; sourceTree = "<group>"; };
|
||||
4952C3BE2115C7CA004A4FA8 /* ProxyMenuItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyMenuItemFactory.swift; sourceTree = "<group>"; };
|
||||
4952C3C82116E952004A4FA8 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
4952C3CA2116E952004A4FA8 /* PreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesWindowController.xib; sourceTree = "<group>"; };
|
||||
4952C3CD2116EA2E004A4FA8 /* ProxyServerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyServerModel.swift; sourceTree = "<group>"; };
|
||||
495340AF20DE5F7200B0D3FF /* StatusItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatusItemView.xib; sourceTree = "<group>"; };
|
||||
495340B220DE68C300B0D3FF /* StatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = "<group>"; };
|
||||
495A44BC20D2660A00888A0A /* ClashXLaunchHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ClashXLaunchHelper.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -152,6 +158,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
492C4870210EF62E004554A0 /* ClashConfig.swift */,
|
||||
4952C3CD2116EA2E004A4FA8 /* ProxyServerModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -170,6 +177,8 @@
|
|||
4989F98520D0AA300001E564 /* ViewControllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4952C3C82116E952004A4FA8 /* PreferencesWindowController.swift */,
|
||||
4952C3CA2116E952004A4FA8 /* PreferencesWindowController.xib */,
|
||||
4989F98920D0AD800001E564 /* SampleConfigViewController.swift */,
|
||||
495340AF20DE5F7200B0D3FF /* StatusItemView.xib */,
|
||||
495340B220DE68C300B0D3FF /* StatusItemView.swift */,
|
||||
|
@ -432,6 +441,7 @@
|
|||
49CF3B5F20CE80D2001EBF94 /* install_proxy_helper.sh in Resources */,
|
||||
49CF3B4020CD7845001EBF94 /* menu_icon_disabled@2x.png in Resources */,
|
||||
49CF3B3F20CD7845001EBF94 /* menu_icon.png in Resources */,
|
||||
4952C3CC2116E952004A4FA8 /* PreferencesWindowController.xib in Resources */,
|
||||
4989F98420D02D200001E564 /* Country.mmdb in Resources */,
|
||||
497F0DF320DE2FE50077AD41 /* Icon.icns in Resources */,
|
||||
492C4865210EC9E4004554A0 /* ProxyConfig in Resources */,
|
||||
|
@ -525,11 +535,13 @@
|
|||
495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */,
|
||||
495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */,
|
||||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */,
|
||||
4952C3CB2116E952004A4FA8 /* PreferencesWindowController.swift in Sources */,
|
||||
4913C82321157D0200F6B87C /* Notification.swift in Sources */,
|
||||
492C4871210EF62E004554A0 /* ClashConfig.swift in Sources */,
|
||||
492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */,
|
||||
49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */,
|
||||
4952C3BF2115C7CA004A4FA8 /* ProxyMenuItemFactory.swift in Sources */,
|
||||
4952C3CE2116EA2E004A4FA8 /* ProxyServerModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -189,8 +189,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
var genConfigWindow:NSWindowController?=nil
|
||||
@IBAction func actionGenConfig(_ sender: Any) {
|
||||
let ctrl = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
|
||||
.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "sampleConfigGenerator")) as! NSWindowController
|
||||
// let ctrl = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
|
||||
// .instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "sampleConfigGenerator")) as! NSWindowController
|
||||
let ctrl = PreferencesWindowController(windowNibName: NSNib.Name(rawValue: "PreferencesWindowController"))
|
||||
|
||||
|
||||
genConfigWindow?.close()
|
||||
genConfigWindow=ctrl
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// ProxyServerModel.swift
|
||||
// ClashX
|
||||
//
|
||||
// Created by CYC on 2018/8/5.
|
||||
// Copyright © 2018年 west2online. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class ProxyServerModel: NSObject, Codable {
|
||||
@objc dynamic var serverHost:String = ""
|
||||
@objc dynamic var serverPort:String = ""
|
||||
@objc dynamic var password:String = ""
|
||||
@objc dynamic var method:String = "RC4-MD5"
|
||||
@objc dynamic var remark:String = "Proxy"
|
||||
|
||||
func isValid() -> Bool {
|
||||
func validateIpAddress(_ ipToValidate: String) -> Bool {
|
||||
|
||||
var sin = sockaddr_in()
|
||||
var sin6 = sockaddr_in6()
|
||||
|
||||
if ipToValidate.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 {
|
||||
// IPv6 peer.
|
||||
return true
|
||||
}
|
||||
else if ipToValidate.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1 {
|
||||
// IPv4 peer.
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
func validateDomainName(_ value: String) -> Bool {
|
||||
let validHostnameRegex = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"
|
||||
|
||||
if (value.range(of: validHostnameRegex, options: .regularExpression) != nil) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validatePort(_ value: String) -> Bool {
|
||||
if let port = Int(value) {
|
||||
return port > 0 && port <= 65535
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if !(validateIpAddress(serverHost) ||
|
||||
validateDomainName(serverHost) ||
|
||||
validatePort(serverPort)){
|
||||
return false
|
||||
}
|
||||
|
||||
if password.isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,359 @@
|
|||
//
|
||||
// PreferencesWindowController.swift
|
||||
// ShadowsocksX-NG
|
||||
//
|
||||
// Created by 邱宇舟 on 16/6/6.
|
||||
// Copyright © 2016年 qiuyuzhou. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import RxCocoa
|
||||
import RxSwift
|
||||
|
||||
class PreferencesWindowController: NSWindowController
|
||||
, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
@IBOutlet weak var profilesTableView: NSTableView!
|
||||
|
||||
@IBOutlet weak var profileBox: NSBox!
|
||||
|
||||
@IBOutlet weak var hostTextField: NSTextField!
|
||||
@IBOutlet weak var portTextField: NSTextField!
|
||||
@IBOutlet weak var methodTextField: NSComboBox!
|
||||
|
||||
@IBOutlet weak var passwordTabView: NSTabView!
|
||||
@IBOutlet weak var passwordTextField: NSTextField!
|
||||
@IBOutlet weak var passwordSecureTextField: NSSecureTextField!
|
||||
@IBOutlet weak var togglePasswordVisibleButton: NSButton!
|
||||
|
||||
|
||||
@IBOutlet weak var remarkTextField: NSTextField!
|
||||
|
||||
|
||||
|
||||
@IBOutlet weak var removeButton: NSButton!
|
||||
|
||||
let tableViewDragType: String = "ss.server.profile.data"
|
||||
|
||||
|
||||
var serverConfigs = [ProxyServerModel]()
|
||||
var editingConfig:ProxyServerModel?
|
||||
|
||||
|
||||
var enabledKcptunSubDisosable: Disposable?
|
||||
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
|
||||
if serverConfigs.count == 0 {
|
||||
serverConfigs.append(ProxyServerModel())
|
||||
}
|
||||
|
||||
|
||||
methodTextField.addItems(withObjectValues: [
|
||||
"aes-128-gcm",
|
||||
"aes-192-gcm",
|
||||
"aes-256-gcm",
|
||||
"aes-128-cfb",
|
||||
"aes-192-cfb",
|
||||
"aes-256-cfb",
|
||||
"aes-128-ctr",
|
||||
"aes-192-ctr",
|
||||
"aes-256-ctr",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",
|
||||
"bf-cfb",
|
||||
"chacha20-ietf-poly1305",
|
||||
"salsa20",
|
||||
"chacha20",
|
||||
"chacha20-ietf",
|
||||
"rc4-md5",
|
||||
])
|
||||
|
||||
|
||||
|
||||
profilesTableView.reloadData()
|
||||
updateProfileBoxVisible()
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
profilesTableView.registerForDraggedTypes([NSPasteboard.PasteboardType(rawValue: tableViewDragType)])
|
||||
profilesTableView.allowsMultipleSelection = true
|
||||
}
|
||||
|
||||
@IBAction func addProfile(_ sender: NSButton) {
|
||||
if !(editingConfig?.isValid() ?? true){
|
||||
shakeWindows()
|
||||
return
|
||||
}
|
||||
profilesTableView.beginUpdates()
|
||||
let profile = ProxyServerModel()
|
||||
profile.remark = "New Server"
|
||||
serverConfigs.append(profile)
|
||||
|
||||
let index = IndexSet(integer: serverConfigs.count-1)
|
||||
profilesTableView.insertRows(at: index, withAnimation: NSTableView.AnimationOptions.effectFade)
|
||||
|
||||
self.profilesTableView.scrollRowToVisible(self.serverConfigs.count-1)
|
||||
self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false)
|
||||
profilesTableView.endUpdates()
|
||||
updateProfileBoxVisible()
|
||||
}
|
||||
|
||||
@IBAction func removeProfile(_ sender: NSButton) {
|
||||
let index = Int(profilesTableView.selectedRowIndexes.first!)
|
||||
var deleteCount = 0
|
||||
if index >= 0 {
|
||||
profilesTableView.beginUpdates()
|
||||
for (_, toDeleteIndex) in profilesTableView.selectedRowIndexes.enumerated() {
|
||||
print(serverConfigs.count)
|
||||
serverConfigs.remove(at: toDeleteIndex - deleteCount)
|
||||
profilesTableView.removeRows(at: IndexSet(integer: toDeleteIndex - deleteCount), withAnimation: NSTableView.AnimationOptions.effectFade)
|
||||
deleteCount += 1
|
||||
}
|
||||
profilesTableView.endUpdates()
|
||||
}
|
||||
self.profilesTableView.scrollRowToVisible(index-1)
|
||||
self.profilesTableView.selectRowIndexes(IndexSet(integer: index-1), byExtendingSelection: false)
|
||||
updateProfileBoxVisible()
|
||||
}
|
||||
|
||||
@IBAction func ok(_ sender: NSButton) {
|
||||
if !(editingConfig?.isValid() ?? false) {
|
||||
shakeWindows()
|
||||
return
|
||||
}
|
||||
window?.performClose(nil)
|
||||
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: NSButton) {
|
||||
window?.performClose(self)
|
||||
}
|
||||
|
||||
@IBAction func duplicate(_ sender: Any) {
|
||||
var copyCount = 0
|
||||
for (_, toDuplicateIndex) in profilesTableView.selectedRowIndexes.enumerated() {
|
||||
print(serverConfigs.count)
|
||||
let profile = serverConfigs[toDuplicateIndex + copyCount]
|
||||
let duplicateProfile = profile.copy() as! ProxyServerModel
|
||||
serverConfigs.insert(duplicateProfile, at:toDuplicateIndex + copyCount)
|
||||
|
||||
profilesTableView.beginUpdates()
|
||||
let index = IndexSet(integer: toDuplicateIndex + copyCount)
|
||||
profilesTableView.insertRows(at: index, withAnimation: NSTableView.AnimationOptions.effectFade)
|
||||
self.profilesTableView.scrollRowToVisible(toDuplicateIndex + copyCount)
|
||||
self.profilesTableView.selectRowIndexes(index, byExtendingSelection: false)
|
||||
profilesTableView.endUpdates()
|
||||
|
||||
copyCount += 1
|
||||
}
|
||||
updateProfileBoxVisible()
|
||||
}
|
||||
|
||||
@IBAction func togglePasswordVisible(_ sender: Any) {
|
||||
if passwordTabView.selectedTabViewItem?.identifier as! String == "secure" {
|
||||
passwordTabView.selectTabViewItem(withIdentifier: "insecure")
|
||||
togglePasswordVisibleButton.image = NSImage(named: NSImage.Name(rawValue: "icons8-Eye Filled-50"))
|
||||
} else {
|
||||
passwordTabView.selectTabViewItem(withIdentifier: "secure")
|
||||
togglePasswordVisibleButton.image = NSImage(named: NSImage.Name(rawValue: "icons8-Blind Filled-50"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func updateProfileBoxVisible() {
|
||||
if serverConfigs.count <= 0 {
|
||||
removeButton.isEnabled = false
|
||||
}else{
|
||||
removeButton.isEnabled = true
|
||||
}
|
||||
|
||||
if serverConfigs.isEmpty {
|
||||
profileBox.isHidden = true
|
||||
} else {
|
||||
profileBox.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func bindProfile(_ index:Int) {
|
||||
NSLog("bind profile \(index)")
|
||||
if let dis = enabledKcptunSubDisosable {
|
||||
dis.dispose()
|
||||
enabledKcptunSubDisosable = Optional.none
|
||||
}
|
||||
if index >= 0 && index < serverConfigs.count {
|
||||
editingConfig = serverConfigs[index]
|
||||
|
||||
hostTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "serverHost"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
portTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "serverPort"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
|
||||
methodTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "method"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
passwordTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "password"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
passwordSecureTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "password"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
|
||||
remarkTextField.bind(NSBindingName(rawValue: "value"), to: editingConfig!, withKeyPath: "remark"
|
||||
, options: [NSBindingOption.continuouslyUpdatesValue: true])
|
||||
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
editingConfig = nil
|
||||
hostTextField.unbind(NSBindingName(rawValue: "value"))
|
||||
portTextField.unbind(NSBindingName(rawValue: "value"))
|
||||
|
||||
methodTextField.unbind(NSBindingName(rawValue: "value"))
|
||||
passwordTextField.unbind(NSBindingName(rawValue: "value"))
|
||||
|
||||
remarkTextField.unbind(NSBindingName(rawValue: "value"))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func getDataAtRow(_ index:Int) -> (String) {
|
||||
let profile = serverConfigs[index]
|
||||
if !profile.remark.isEmpty {
|
||||
return profile.remark
|
||||
} else {
|
||||
return profile.serverHost
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// For NSTableViewDataSource
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return serverConfigs.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView
|
||||
, objectValueFor tableColumn: NSTableColumn?
|
||||
, row: Int) -> Any? {
|
||||
|
||||
let title = getDataAtRow(row)
|
||||
|
||||
if tableColumn?.identifier == NSUserInterfaceItemIdentifier("main") {
|
||||
return title
|
||||
} else if tableColumn?.identifier == NSUserInterfaceItemIdentifier("status") {
|
||||
return nil
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Drag & Drop reorder rows
|
||||
|
||||
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
|
||||
let item = NSPasteboardItem()
|
||||
item.setString(String(row), forType: NSPasteboard.PasteboardType(rawValue: tableViewDragType))
|
||||
return item
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int
|
||||
, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
|
||||
if dropOperation == .above {
|
||||
return .move
|
||||
}
|
||||
return NSDragOperation()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo
|
||||
, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
|
||||
var oldIndexes = [Int]()
|
||||
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:], using: {
|
||||
(draggingItem: NSDraggingItem, idx: Int, stop: UnsafeMutablePointer<ObjCBool>) in
|
||||
if let str = (draggingItem.item as! NSPasteboardItem).string(forType: NSPasteboard.PasteboardType(rawValue: 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 = serverConfigs.remove(at: oldIndex + oldIndexOffset)
|
||||
serverConfigs.insert(o, at:row - 1)
|
||||
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
|
||||
oldIndexOffset -= 1
|
||||
} else {
|
||||
let o = serverConfigs.remove(at: oldIndex)
|
||||
serverConfigs.insert(o, at:row + newIndexOffset)
|
||||
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
|
||||
newIndexOffset += 1
|
||||
}
|
||||
}
|
||||
tableView.endUpdates()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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 {
|
||||
editingConfig = nil
|
||||
return true
|
||||
}
|
||||
if editingConfig != nil {
|
||||
if !(editingConfig?.isValid() ?? false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
if profilesTableView.selectedRow >= 0 {
|
||||
bindProfile(profilesTableView.selectedRow)
|
||||
} else {
|
||||
if !serverConfigs.isEmpty {
|
||||
let index = IndexSet(integer: serverConfigs.count - 1)
|
||||
profilesTableView.selectRowIndexes(index, byExtendingSelection: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = [NSAnimatablePropertyKey(rawValue: "frameOrigin"):shakeAnimation]
|
||||
window?.animator().setFrameOrigin(window!.frame.origin)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,395 @@
|
|||
<?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="box content view" minToolsVersion="7.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="PreferencesWindowController" customModule="ClashX" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="hostTextField" destination="R4F-j7-GkK" id="a1I-WO-Qdj"/>
|
||||
<outlet property="methodTextField" destination="ef4-8J-eFn" id="OXd-fC-ICR"/>
|
||||
<outlet property="passwordSecureTextField" destination="ZXA-oi-Zv3" id="ZMF-s4-w53"/>
|
||||
<outlet property="passwordTabView" destination="DHz-Hx-kZ5" id="8W3-H3-Ezq"/>
|
||||
<outlet property="passwordTextField" destination="3Ad-X4-DBi" id="FHf-t3-D8t"/>
|
||||
<outlet property="portTextField" destination="lng-AE-g0J" id="7b1-ox-WSL"/>
|
||||
<outlet property="profileBox" destination="oZ5-85-pwY" id="D19-PQ-X7E"/>
|
||||
<outlet property="profilesTableView" destination="r91-ho-Lum" id="TNk-f6-Vgo"/>
|
||||
<outlet property="remarkTextField" destination="q3C-S0-iNn" id="DSv-lV-PX7"/>
|
||||
<outlet property="removeButton" destination="3ei-2f-kHB" id="DdP-dA-coT"/>
|
||||
<outlet property="togglePasswordVisibleButton" destination="jDI-cn-wYa" id="wc1-ra-kya"/>
|
||||
<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="Server Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<rect key="contentRect" x="423" y="365" width="613" height="332"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="613" height="332"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView focusRingType="none" 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="232"/>
|
||||
<clipView key="contentView" focusRingType="none" id="1oC-7s-16v">
|
||||
<rect key="frame" x="1" y="1" width="231" height="230"/>
|
||||
<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="r91-ho-Lum">
|
||||
<rect key="frame" x="0.0" y="0.0" width="231" height="230"/>
|
||||
<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="wR0-vc-Gtr">
|
||||
<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="aBb-XB-tD7"/>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="main" width="135" minWidth="40" maxWidth="1000" id="6gc-9b-Qnw">
|
||||
<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="COZ-Qr-bmU">
|
||||
<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="fz3-X6-PbB"/>
|
||||
<outlet property="delegate" destination="-2" id="6zR-v1-wZj"/>
|
||||
<outlet property="menu" destination="QmU-th-vjP" id="J0d-c9-pYs"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="jTp-ja-h3t">
|
||||
<rect key="frame" x="1" y="206" width="163" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="dLS-Vz-L6A">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<box boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="oZ5-85-pwY">
|
||||
<rect key="frame" x="273" y="61" width="320" height="251"/>
|
||||
<view key="contentView" id="a3z-VL-3xV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="251"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HPg-NP-kjP">
|
||||
<rect key="frame" x="46" y="212" width="58" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Address:" id="yHs-XE-XEB">
|
||||
<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>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Otb-CO-KUH">
|
||||
<rect key="frame" x="235" y="214" width="9" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="5" id="JAU-I8-jVc"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title=":" id="Ibr-Gs-5Js">
|
||||
<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>
|
||||
<comboBox verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ef4-8J-eFn">
|
||||
<rect key="frame" x="110" y="177" width="189" height="26"/>
|
||||
<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"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</comboBoxCell>
|
||||
</comboBox>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kft-TG-O5S">
|
||||
<rect key="frame" x="30" y="182" width="74" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Encryption:" id="mOh-nD-7EX">
|
||||
<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>
|
||||
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="q3C-S0-iNn">
|
||||
<rect key="frame" x="110" y="119" width="193" height="22"/>
|
||||
<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"/>
|
||||
<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="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Baj-Yh-kmj">
|
||||
<rect key="frame" x="43" y="122" width="61" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Remarks:" id="MiN-VE-FtC">
|
||||
<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>
|
||||
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R4F-j7-GkK">
|
||||
<rect key="frame" x="110" y="209" width="119" height="22"/>
|
||||
<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"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lng-AE-g0J">
|
||||
<rect key="frame" x="246" y="209" width="50" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="50" id="Ahb-NM-Zyb"/>
|
||||
</constraints>
|
||||
<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"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<allowedInputSourceLocales>
|
||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||
</allowedInputSourceLocales>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<tabView focusRingType="none" drawsBackground="NO" type="noTabsNoBorder" initialItem="aRD-sh-uEl" translatesAutoresizingMaskIntoConstraints="NO" id="DHz-Hx-kZ5">
|
||||
<rect key="frame" x="110" y="151" width="158" height="22"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<tabViewItems>
|
||||
<tabViewItem label="Secure" identifier="secure" id="aRD-sh-uEl">
|
||||
<view key="view" id="rXs-ye-nly">
|
||||
<rect key="frame" x="0.0" y="0.0" width="158" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<secureTextField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZXA-oi-Zv3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="158" height="22"/>
|
||||
<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"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<allowedInputSourceLocales>
|
||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||
</allowedInputSourceLocales>
|
||||
</secureTextFieldCell>
|
||||
</secureTextField>
|
||||
</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>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Insecure" identifier="insecure" id="SYv-Tn-tur">
|
||||
<view key="view" id="Z6k-mP-BgX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3Ad-X4-DBi">
|
||||
<rect key="frame" x="0.0" y="0.0" width="135" height="22"/>
|
||||
<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"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</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>
|
||||
</tabViewItem>
|
||||
</tabViewItems>
|
||||
</tabView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jDI-cn-wYa">
|
||||
<rect key="frame" x="277" y="149" width="26" height="25"/>
|
||||
<constraints>
|
||||
<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">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="togglePasswordVisible:" target="-2" id="3kh-Pz-mr0"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qfe-Ql-iCO">
|
||||
<rect key="frame" x="38" y="153" width="66" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Password:" id="kil-xC-HPD">
|
||||
<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>
|
||||
<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="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 firstAttribute="trailing" secondItem="lng-AE-g0J" secondAttribute="trailing" constant="24" id="6W3-BA-gYo"/>
|
||||
<constraint firstItem="R4F-j7-GkK" firstAttribute="top" secondItem="a3z-VL-3xV" secondAttribute="top" constant="20" symbolic="YES" id="Dkz-UA-ymg"/>
|
||||
<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="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="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="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="Otb-CO-KUH" firstAttribute="top" secondItem="lng-AE-g0J" secondAttribute="top" id="kLj-6Z-ZDw"/>
|
||||
<constraint firstItem="R4F-j7-GkK" firstAttribute="leading" secondItem="ef4-8J-eFn" secondAttribute="leading" id="opP-Lo-SO0"/>
|
||||
<constraint firstItem="Qfe-Ql-iCO" firstAttribute="centerY" secondItem="DHz-Hx-kZ5" secondAttribute="centerY" id="wKU-B5-A3m"/>
|
||||
<constraint firstItem="q3C-S0-iNn" firstAttribute="top" secondItem="DHz-Hx-kZ5" secondAttribute="bottom" constant="10" id="xus-im-ZK7"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<color key="fillColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</box>
|
||||
<box 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"/>
|
||||
<view key="contentView" id="MbP-FS-YEs">
|
||||
<rect key="frame" x="0.0" y="0.0" width="233" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4ig-lF-m2R">
|
||||
<rect key="frame" x="0.0" y="-1" width="22" height="21"/>
|
||||
<constraints>
|
||||
<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">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="addProfile:" target="-2" id="NEh-IP-KO8"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3ei-2f-kHB">
|
||||
<rect key="frame" x="20" y="-1" width="22" height="21"/>
|
||||
<constraints>
|
||||
<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">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="removeProfile:" target="-2" id="CJC-GL-qeS"/>
|
||||
</connections>
|
||||
</button>
|
||||
</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>
|
||||
<color key="fillColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
|
||||
</box>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yyD-qq-aSk">
|
||||
<rect key="frame" x="518" y="13" width="81" height="32"/>
|
||||
<constraints>
|
||||
<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">
|
||||
<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="ok:" target="-2" id="ifI-e4-Htf"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kGm-Zm-ipY">
|
||||
<rect key="frame" x="437" y="13" width="81" height="32"/>
|
||||
<constraints>
|
||||
<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">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="cancel:" target="-2" id="514-4D-a6S"/>
|
||||
</connections>
|
||||
</button>
|
||||
</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>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="417.5" y="61"/>
|
||||
</window>
|
||||
<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="maximum" value="65535"/>
|
||||
</numberFormatter>
|
||||
<menu showsStateColumn="NO" id="QmU-th-vjP">
|
||||
<items>
|
||||
<menuItem title="Clone" id="bl9-lq-u9V">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="duplicate:" target="-2" id="dEb-Dt-yBW"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="-25.5" y="171.5"/>
|
||||
</menu>
|
||||
<userDefaultsController representsSharedInstance="YES" id="UV9-ar-oJ3"/>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSAddTemplate" width="11" height="11"/>
|
||||
<image name="NSRemoveTemplate" width="11" height="11"/>
|
||||
<image name="icons8-Blind Filled-50" width="50" height="50"/>
|
||||
</resources>
|
||||
</document>
|
Loading…
Reference in New Issue