Files
ShadowsocksX-NG-R/ShadowsocksX-NG/SubscribePreferenceWindowController.swift
T

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)
}
}