Files
Archive/v2rayu/V2rayU/Preference/PreferenceRouting.swift
2024-07-21 20:29:05 +02:00

308 lines
12 KiB
Swift

//
// Preferences.swift
// V2rayU
//
// Created by yanue on 2018/10/19.
// Copyright © 2018 yanue. All rights reserved.
//
import Cocoa
import Preferences
final class PreferenceRoutingViewController: NSViewController, PreferencePane, NSTabViewDelegate {
let preferencePaneIdentifier = PreferencePane.Identifier.routingTab
let preferencePaneTitle = "Routing"
let toolbarItemIcon = NSImage(named: NSImage.networkName)!
let tableViewDragType: String = "v2ray.routing"
@IBOutlet weak var domainStrategy: NSPopUpButton!
@IBOutlet weak var proxyTextView: NSTextView!
@IBOutlet weak var directTextView: NSTextView!
@IBOutlet weak var blockTextView: NSTextView!
@IBOutlet weak var routingRuleContent: NSTextView!
@IBOutlet weak var routingRuleName: NSTextField!
@IBOutlet weak var defaulRoutingRuleName: NSTextField!
@IBOutlet weak var errTip: NSTextField!
@IBOutlet weak var routingsTableView: NSTableView!
@IBOutlet weak var addRemoveButton: NSSegmentedControl!
@IBOutlet weak var customView: NSView!
@IBOutlet weak var defaultView: NSView!
override var nibName: NSNib.Name? {
return "PreferenceRouting"
}
override func viewDidLoad() {
super.viewDidLoad()
// fix: https://github.com/sindresorhus/Preferences/issues/31
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height);
// set table drag style
self.routingsTableView.registerForDraggedTypes([NSPasteboard.PasteboardType(rawValue: tableViewDragType)])
self.routingsTableView.allowsMultipleSelection = true
// reload tableview
V2rayRoutings.loadConfig()
// table view
self.routingsTableView.delegate = self
self.routingsTableView.dataSource = self
self.routingsTableView.reloadData()
}
func set_tip(str: String) {
self.errTip.stringValue = str
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.errTip.stringValue = ""
}
}
func loadJsonData(rowIndex: Int) {
print("loadJsonData", rowIndex)
DispatchQueue.main.async {
self.defaultView.isHidden = true
self.customView.isHidden = true
if let item = V2rayRoutings.load(idx: rowIndex) {
let isDefaultRule = V2rayRoutings.isDefaultRule(name: item.name)
print("loadJsonData", item.domainStrategy,item.proxy,item.direct,item.block,item.json,isDefaultRule)
if isDefaultRule {
self.defaultView.isHidden = false
self.domainStrategy.selectItem(withTitle: item.domainStrategy)
self.proxyTextView.string = item.proxy
self.directTextView.string = item.direct
self.blockTextView.string = item.block
self.defaulRoutingRuleName.stringValue = item.remark
} else {
self.customView.isHidden = false
self.routingRuleName.stringValue = item.remark
self.routingRuleContent.string = item.json
}
}
}
}
@IBAction func addRemoveServer(_ sender: NSSegmentedCell) {
// 0 add,1 remove
let seg = addRemoveButton.indexOfSelectedItem
print("addRemoveServer",seg)
DispatchQueue.global().async {
switch seg {
// add server config
case 0:
// add
V2rayRoutings.add(remark: "new-rule", json: V2rayRoutings.default_rule_content)
DispatchQueue.main.async {
V2rayRoutings.loadConfig()
// reload data
self.routingsTableView.reloadData()
// selected current row
self.routingsTableView.selectRowIndexes(NSIndexSet(index: V2rayRoutings.count() - 1) as IndexSet, byExtendingSelection: false)
// refresh menu
menuController.showRouting()
}
break
// delete server config
case 1:
DispatchQueue.main.async {
// get seleted index
let idx = self.routingsTableView.selectedRow
// remove
V2rayRoutings.remove(idx: idx)
// reload
V2rayRoutings.loadConfig()
// selected prev row
let cnt: Int = V2rayRoutings.count()
var rowIndex: Int = idx - 1
if idx > 0 && idx < cnt {
rowIndex = idx
}
// reload
self.routingsTableView.reloadData()
// fix
if cnt > 1 {
// selected row
self.routingsTableView.selectRowIndexes(NSIndexSet(index: rowIndex) as IndexSet, byExtendingSelection: false)
}
if rowIndex >= 0 {
self.loadJsonData(rowIndex: rowIndex)
} else {
self.routingsTableView.becomeFirstResponder()
}
// refresh menu
menuController.showRouting()
}
break
// unknown action
default:
return
}
}
}
@IBAction func goHelp(_ sender: Any) {
guard let url = URL(string: "https://xtls.github.io/config/routing.html#routingobject") else {
return
}
NSWorkspace.shared.open(url)
}
@IBAction func goHelp2(_ sender: Any) {
guard let url = URL(string: "https://xtls.github.io/config/routing.html#routingobject") else {
return
}
NSWorkspace.shared.open(url)
}
@IBAction func saveRouting(_ sender: Any) {
let selectedRule = self.routingsTableView.selectedRow
if selectedRule == -1 {
return
}
guard let rule = V2rayRoutings.load(idx: selectedRule) else {
return
}
if V2rayRoutings.isDefaultRule(name: rule.name) {
let domainStrategy = self.domainStrategy.titleOfSelectedItem
if domainStrategy != nil {
rule.domainStrategy = domainStrategy!
}
rule.proxy = self.proxyTextView.string
rule.direct = self.directTextView.string
rule.block = self.blockTextView.string
} else {
rule.remark = self.routingRuleName.stringValue
let (res, err) = parseRoutingRuleJson(json: self.routingRuleContent.string)
if err != nil {
print("parseRoutingRuleJson err", err)
self.errTip.stringValue = "parse json err: \(err)"
// hide err
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.errTip.stringValue = ""
}
return
} else {
self.errTip.stringValue = ""
rule.json = self.routingRuleContent.string
}
}
// save update
V2rayRoutings.save(routing: rule)
// reload table
self.routingsTableView.reloadData()
// set selected
self.routingsTableView.selectRowIndexes(NSIndexSet(index: selectedRule) as IndexSet, byExtendingSelection: false)
//
menuController.showRouting();
// if is selected rule name == rule.name, restart v2ray
let selectedRuleName = UserDefaults.get(forKey: .routingSelectedRule)
if selectedRuleName == rule.name {
// restart v2ray
V2rayLaunch.restartV2ray()
}
}
}
// NSnameSource
extension PreferenceRoutingViewController: NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return V2rayRoutings.count()
}
// show cell
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let v2rayItemList = V2rayRoutings.list()
// set cell data
if v2rayItemList.count >= row {
return v2rayItemList[row].remark
}
return nil
}
}
// NSTableViewDelegate
extension PreferenceRoutingViewController: NSTableViewDelegate {
// For NSTableViewDelegate
func tableViewSelectionDidChange(_ notification: Notification) {
// Ensure there's a valid selected row before calling loadJsonData
if self.routingsTableView.selectedRow >= 0 {
self.loadJsonData(rowIndex: self.routingsTableView.selectedRow)
} else {
// Handle the case where no row is selected or add a default behavior
print("No row selected")
}
self.errTip.stringValue = ""
}
// 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
var oldIndexLast = 0
var newIndexLast = 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.
for oldIndex in oldIndexes {
if oldIndex < row {
oldIndexLast = oldIndex + oldIndexOffset
newIndexLast = row - 1
oldIndexOffset -= 1
} else {
oldIndexLast = oldIndex
newIndexLast = row + newIndexOffset
newIndexOffset += 1
}
}
DispatchQueue.global().async {
print("move",oldIndexLast,newIndexLast)
// move
V2rayRoutings.move(oldIndex: oldIndexLast, newIndex: newIndexLast)
DispatchQueue.main.async {
// set selected
self.routingsTableView.selectRowIndexes(NSIndexSet(index: newIndexLast) as IndexSet, byExtendingSelection: false)
// reload table
self.routingsTableView.reloadData()
}
// reload menu
menuController.showRouting()
}
return true
}
}