mirror of
https://github.com/qinyuhang/ShadowsocksX-NG-R.git
synced 2026-10-02 08:34:01 +00:00
281 lines
10 KiB
Swift
281 lines
10 KiB
Swift
//
|
|
// SubscribePreferenceWindowController.swift
|
|
// ShadowsocksX-NG
|
|
//
|
|
// Created by 秦宇航 on 2017/6/15.
|
|
// Copyright © 2017年 qiuyuzhou. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
|
|
class SubscribePreferenceWindowController: NSWindowController
|
|
, NSTableViewDataSource, NSTableViewDelegate {
|
|
|
|
@IBOutlet weak var FeedLabel: NSTextField!
|
|
@IBOutlet weak var OKButton: NSButton!
|
|
|
|
@IBOutlet weak var FeedTextField: NSTextField!
|
|
@IBOutlet weak var TokenTextField: NSTextField!
|
|
@IBOutlet weak var GroupTextField: NSTextField!
|
|
@IBOutlet weak var MaxCountTextField: NSTextField!
|
|
@IBOutlet weak var SubscribeTableView: NSTableView!
|
|
|
|
@IBOutlet weak var AddSubscribeBtn: NSButton!
|
|
@IBOutlet weak var DeleteSubscribeBtn: NSButton!
|
|
|
|
var sbMgr: SubscribeManager!
|
|
var defaults: UserDefaults!
|
|
let tableViewDragType: String = "subscribe.host"
|
|
var editingSubscribe: Subscribe!
|
|
|
|
override func windowDidLoad() {
|
|
super.windowDidLoad()
|
|
|
|
sbMgr = SubscribeManager.instance
|
|
defaults = UserDefaults.standard
|
|
SubscribeTableView.reloadData()
|
|
updateSubscribeBoxVisible()
|
|
}
|
|
|
|
override func awakeFromNib() {
|
|
SubscribeTableView.register(forDraggedTypes: [tableViewDragType])
|
|
SubscribeTableView.allowsMultipleSelection = true
|
|
}
|
|
|
|
@IBAction func onOk(_ sender: NSButton) {
|
|
if editingSubscribe != nil {
|
|
if !editingSubscribe.feedValidator() {
|
|
// Done Shake window
|
|
shakeWindows()
|
|
return
|
|
}
|
|
}
|
|
sbMgr.save()
|
|
window?.performClose(self)
|
|
}
|
|
|
|
@IBAction func onAdd(_ sender: NSButton) {
|
|
if editingSubscribe != nil && !editingSubscribe.feedValidator(){
|
|
shakeWindows()
|
|
return
|
|
}
|
|
SubscribeTableView.beginUpdates()
|
|
let subscribe = Subscribe(initUrlString: "", initGroupName: "", initToken: "", initMaxCount: -1)
|
|
sbMgr.subscribes.append(subscribe)
|
|
|
|
let index = IndexSet(integer: sbMgr.subscribes.count-1)
|
|
SubscribeTableView.insertRows(at: index, withAnimation: .effectFade)
|
|
|
|
self.SubscribeTableView.scrollRowToVisible(self.sbMgr.subscribes.count-1)
|
|
self.SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false)
|
|
SubscribeTableView.endUpdates()
|
|
updateSubscribeBoxVisible()
|
|
}
|
|
|
|
@IBAction func onDelete(_ sender: NSButton) {
|
|
let index = Int(SubscribeTableView.selectedRowIndexes.first!)
|
|
var deleteCount = 0
|
|
if index >= 0 {
|
|
SubscribeTableView.beginUpdates()
|
|
for (_, toDeleteIndex) in SubscribeTableView.selectedRowIndexes.enumerated() {
|
|
_ = sbMgr.deleteSubscribe(atIndex: toDeleteIndex - deleteCount)
|
|
SubscribeTableView.removeRows(at: IndexSet(integer: toDeleteIndex - deleteCount), withAnimation: .effectFade)
|
|
deleteCount += 1
|
|
if sbMgr.subscribes.count == 0 {
|
|
cleanField()
|
|
}
|
|
}
|
|
SubscribeTableView.endUpdates()
|
|
}
|
|
self.SubscribeTableView.scrollRowToVisible(index - 1)
|
|
self.SubscribeTableView.selectRowIndexes(IndexSet(integer: index - 1), byExtendingSelection: false)
|
|
updateSubscribeBoxVisible()
|
|
}
|
|
|
|
func updateSubscribeBoxVisible() {
|
|
if sbMgr.subscribes.count <= 0 {
|
|
DeleteSubscribeBtn.isEnabled = false
|
|
FeedTextField.isEnabled = false
|
|
TokenTextField.isEnabled = false
|
|
GroupTextField.isEnabled = false
|
|
MaxCountTextField.isEnabled = false
|
|
}else{
|
|
DeleteSubscribeBtn.isEnabled = true
|
|
FeedTextField.isEnabled = true
|
|
TokenTextField.isEnabled = true
|
|
GroupTextField.isEnabled = true
|
|
MaxCountTextField.isEnabled = true
|
|
}
|
|
}
|
|
|
|
func bindSubscribe(_ index:Int) {
|
|
if index >= 0 && index < sbMgr.subscribes.count {
|
|
editingSubscribe = sbMgr.subscribes[index]
|
|
|
|
FeedTextField.bind("value", to: editingSubscribe, withKeyPath: "subscribeFeed", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
|
TokenTextField.bind("value", to: editingSubscribe, withKeyPath: "token", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
|
GroupTextField.bind("value", to: editingSubscribe, withKeyPath: "groupName", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
|
MaxCountTextField.bind("value", to: editingSubscribe, withKeyPath: "maxCount", options: [NSContinuouslyUpdatesValueBindingOption: true])
|
|
|
|
} else {
|
|
editingSubscribe = nil
|
|
FeedTextField.unbind("value")
|
|
TokenTextField.unbind("value")
|
|
GroupTextField.unbind("value")
|
|
MaxCountTextField.unbind("value")
|
|
}
|
|
}
|
|
|
|
func getDataAtRow(_ index:Int) -> String {
|
|
if sbMgr.subscribes[index].groupName != "" {
|
|
return sbMgr.subscribes[index].groupName
|
|
}
|
|
return sbMgr.subscribes[index].subscribeFeed
|
|
}
|
|
|
|
// MARK: For NSTableViewDataSource
|
|
|
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
|
if let mgr = sbMgr {
|
|
return mgr.subscribes.count
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func tableView(_ tableView: NSTableView
|
|
, objectValueFor tableColumn: NSTableColumn?
|
|
, row: Int) -> Any? {
|
|
|
|
let title = getDataAtRow(row)
|
|
|
|
if tableColumn?.identifier == "main" {
|
|
if title != "" {return title}
|
|
else {return "S"}
|
|
} else if tableColumn?.identifier == "status" {
|
|
return NSImage(named: "menu_icon")
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// MARK: Drag & Drop reorder rows
|
|
|
|
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
|
|
let item = NSPasteboardItem()
|
|
item.setString(String(row), forType: tableViewDragType)
|
|
return item
|
|
}
|
|
|
|
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int
|
|
, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
|
|
if dropOperation == .above {
|
|
return .move
|
|
}
|
|
return NSDragOperation()
|
|
}
|
|
|
|
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo
|
|
, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
|
|
if let mgr = sbMgr {
|
|
var oldIndexes = [Int]()
|
|
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
|
|
if let str = ($0.0.item as! NSPasteboardItem).string(forType: self.tableViewDragType), let index = Int(str) {
|
|
oldIndexes.append(index)
|
|
}
|
|
}
|
|
|
|
var oldIndexOffset = 0
|
|
var newIndexOffset = 0
|
|
|
|
// For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
|
|
// You may want to move rows in your content array and then call `tableView.reloadData()` instead.
|
|
tableView.beginUpdates()
|
|
for oldIndex in oldIndexes {
|
|
if oldIndex < row {
|
|
let o = mgr.subscribes.remove(at: oldIndex + oldIndexOffset)
|
|
mgr.subscribes.insert(o, at:row - 1)
|
|
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
|
|
oldIndexOffset -= 1
|
|
} else {
|
|
let o = mgr.subscribes.remove(at: oldIndex)
|
|
mgr.subscribes.insert(o, at:row + newIndexOffset)
|
|
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
|
|
newIndexOffset += 1
|
|
}
|
|
}
|
|
tableView.endUpdates()
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
//--------------------------------------------------
|
|
// For NSTableViewDelegate
|
|
|
|
func tableView(_ tableView: NSTableView
|
|
, shouldEdit tableColumn: NSTableColumn?, row: Int) -> Bool {
|
|
return false
|
|
}
|
|
|
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
|
if row < 0 {
|
|
editingSubscribe = nil
|
|
return true
|
|
}
|
|
// if editingSubscribe != nil {
|
|
// if !editingSubscribe.isValid() {
|
|
// return false
|
|
// }
|
|
// }
|
|
|
|
return true
|
|
}
|
|
|
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
|
if SubscribeTableView.selectedRow >= 0 {
|
|
bindSubscribe(SubscribeTableView.selectedRow)
|
|
if (SubscribeTableView.selectedRowIndexes.count > 1){
|
|
// duplicateProfileButton.isEnabled = false
|
|
} else {
|
|
// duplicateProfileButton.isEnabled = true
|
|
}
|
|
} else {
|
|
if !sbMgr.subscribes.isEmpty {
|
|
let index = IndexSet(integer: sbMgr.subscribes.count - 1)
|
|
SubscribeTableView.selectRowIndexes(index, byExtendingSelection: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
func cleanField(){
|
|
FeedTextField.stringValue = ""
|
|
TokenTextField.stringValue = ""
|
|
GroupTextField.stringValue = ""
|
|
MaxCountTextField.stringValue = ""
|
|
}
|
|
|
|
func shakeWindows(){
|
|
let numberOfShakes:Int = 8
|
|
let durationOfShake:Float = 0.5
|
|
let vigourOfShake:Float = 0.05
|
|
|
|
let frame:CGRect = (window?.frame)!
|
|
let shakeAnimation = CAKeyframeAnimation()
|
|
|
|
let shakePath = CGMutablePath()
|
|
|
|
shakePath.move(to: CGPoint(x:NSMinX(frame), y:NSMinY(frame)))
|
|
|
|
for _ in 1...numberOfShakes{
|
|
shakePath.addLine(to: CGPoint(x: NSMinX(frame) - frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame)))
|
|
shakePath.addLine(to: CGPoint(x: NSMinX(frame) + frame.size.width * CGFloat(vigourOfShake), y: NSMinY(frame)))
|
|
}
|
|
|
|
shakePath.closeSubpath()
|
|
shakeAnimation.path = shakePath
|
|
shakeAnimation.duration = CFTimeInterval(durationOfShake)
|
|
window?.animations = ["frameOrigin":shakeAnimation]
|
|
window?.animator().setFrameOrigin(window!.frame.origin)
|
|
}
|
|
}
|