2016-08-14 15:29:50 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import PureLayout
|
|
|
|
import RxSwift
|
|
|
|
|
2016-09-11 11:53:15 +03:00
|
|
|
struct GeneralPrefData: Equatable {
|
2016-08-14 15:29:50 +03:00
|
|
|
let openNewWindowWhenLaunching: Bool
|
|
|
|
let openNewWindowOnReactivation: Bool
|
2016-09-11 11:53:15 +03:00
|
|
|
|
|
|
|
let ignorePatterns: Set<FileItemIgnorePattern>
|
2016-08-14 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
2016-08-14 19:59:00 +03:00
|
|
|
func == (left: GeneralPrefData, right: GeneralPrefData) -> Bool {
|
|
|
|
return left.openNewWindowWhenLaunching == right.openNewWindowWhenLaunching
|
|
|
|
&& left.openNewWindowOnReactivation == right.openNewWindowOnReactivation
|
2016-09-11 15:28:56 +03:00
|
|
|
&& left.ignorePatterns == right.ignorePatterns
|
2016-08-14 19:59:00 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 09:32:58 +03:00
|
|
|
class GeneralPrefPane: PrefPane, NSTextFieldDelegate {
|
2016-08-14 15:29:50 +03:00
|
|
|
|
2016-09-24 17:35:27 +03:00
|
|
|
override var displayName: String {
|
|
|
|
return "General"
|
|
|
|
}
|
|
|
|
|
2016-09-10 22:46:43 +03:00
|
|
|
override var pinToContainer: Bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-09-11 15:28:56 +03:00
|
|
|
private var data: GeneralPrefData
|
2016-08-14 16:38:41 +03:00
|
|
|
|
|
|
|
private let openWhenLaunchingCheckbox = NSButton(forAutoLayout: ())
|
|
|
|
private let openOnReactivationCheckbox = NSButton(forAutoLayout: ())
|
2016-09-11 15:28:56 +03:00
|
|
|
private let ignoreField = NSTextField(forAutoLayout: ())
|
2016-08-14 15:29:50 +03:00
|
|
|
|
|
|
|
init(source: Observable<Any>, initialData: GeneralPrefData) {
|
2016-08-14 19:59:00 +03:00
|
|
|
self.data = initialData
|
2016-08-14 15:29:50 +03:00
|
|
|
super.init(source: source)
|
2016-09-11 15:28:56 +03:00
|
|
|
|
|
|
|
self.updateViews(newData: initialData)
|
2016-08-14 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func addViews() {
|
2016-08-25 10:27:59 +03:00
|
|
|
let paneTitle = self.paneTitleTextField(title: "General")
|
2016-08-14 15:29:50 +03:00
|
|
|
|
2016-08-25 10:27:59 +03:00
|
|
|
let openUntitledWindowTitle = self.titleTextField(title: "Open Untitled Window:")
|
2016-08-14 16:38:41 +03:00
|
|
|
self.configureCheckbox(button: self.openWhenLaunchingCheckbox,
|
2016-09-24 17:31:14 +03:00
|
|
|
title: "On launch",
|
2016-08-14 16:38:41 +03:00
|
|
|
action: #selector(GeneralPrefPane.openUntitledWindowWhenLaunchingAction(_:)))
|
|
|
|
self.configureCheckbox(button: self.openOnReactivationCheckbox,
|
2016-09-24 17:31:14 +03:00
|
|
|
title: "On re-activation",
|
2016-09-11 15:28:56 +03:00
|
|
|
action: #selector(GeneralPrefPane.openUntitledWindowOnReactivationAction(_:)))
|
2016-08-14 16:01:28 +03:00
|
|
|
|
2016-08-14 16:38:41 +03:00
|
|
|
let whenLaunching = self.openWhenLaunchingCheckbox
|
|
|
|
let onReactivation = self.openOnReactivationCheckbox
|
2016-09-10 22:46:43 +03:00
|
|
|
|
|
|
|
let ignoreListTitle = self.titleTextField(title: "Files To Ignore:")
|
2016-09-11 15:28:56 +03:00
|
|
|
let ignoreField = self.ignoreField
|
2016-09-13 09:32:58 +03:00
|
|
|
NSNotificationCenter.defaultCenter()
|
|
|
|
.addObserverForName(NSControlTextDidEndEditingNotification, object: ignoreField, queue: nil) { [unowned self] _ in
|
|
|
|
self.ignorePatternsAction()
|
|
|
|
}
|
2016-09-10 22:46:43 +03:00
|
|
|
let ignoreInfo = self.infoTextField(text: "")
|
|
|
|
ignoreInfo.attributedStringValue = self.ignoreInfoText()
|
|
|
|
|
2016-08-25 10:27:59 +03:00
|
|
|
let cliToolTitle = self.titleTextField(title: "CLI Tool:")
|
|
|
|
let cliToolButton = NSButton(forAutoLayout: ())
|
2016-09-24 17:31:14 +03:00
|
|
|
cliToolButton.title = "Copy 'vimr' CLI Tool..."
|
2016-08-25 10:27:59 +03:00
|
|
|
cliToolButton.bezelStyle = .RoundedBezelStyle
|
|
|
|
cliToolButton.bordered = true
|
|
|
|
cliToolButton.setButtonType(.MomentaryPushInButton)
|
|
|
|
cliToolButton.target = self
|
|
|
|
cliToolButton.action = #selector(GeneralPrefPane.copyCliTool(_:))
|
2016-09-10 22:46:43 +03:00
|
|
|
let cliToolInfo = self.infoTextField(
|
|
|
|
text: "Put the executable 'vimr' in your $PATH and execute 'vimr -h' for help."
|
|
|
|
)
|
2016-08-14 16:38:41 +03:00
|
|
|
|
2016-08-25 10:27:59 +03:00
|
|
|
self.addSubview(paneTitle)
|
2016-08-14 16:01:28 +03:00
|
|
|
self.addSubview(openUntitledWindowTitle)
|
2016-08-14 16:38:41 +03:00
|
|
|
self.addSubview(whenLaunching)
|
|
|
|
self.addSubview(onReactivation)
|
2016-09-10 22:46:43 +03:00
|
|
|
|
|
|
|
self.addSubview(ignoreListTitle)
|
|
|
|
self.addSubview(ignoreField)
|
|
|
|
self.addSubview(ignoreInfo)
|
|
|
|
|
2016-08-25 10:27:59 +03:00
|
|
|
self.addSubview(cliToolTitle)
|
|
|
|
self.addSubview(cliToolButton)
|
|
|
|
self.addSubview(cliToolInfo)
|
2016-08-14 16:01:28 +03:00
|
|
|
|
2016-08-14 15:29:50 +03:00
|
|
|
paneTitle.autoPinEdgeToSuperviewEdge(.Top, withInset: 18)
|
|
|
|
paneTitle.autoPinEdgeToSuperviewEdge(.Left, withInset: 18)
|
2016-08-25 10:27:59 +03:00
|
|
|
paneTitle.autoPinEdgeToSuperviewEdge(.Right, withInset: 18, relation: .GreaterThanOrEqual)
|
2016-08-14 16:01:28 +03:00
|
|
|
|
2016-08-14 16:38:41 +03:00
|
|
|
openUntitledWindowTitle.autoAlignAxis(.Baseline, toSameAxisOfView: whenLaunching, withOffset: 0)
|
2016-08-14 16:01:28 +03:00
|
|
|
openUntitledWindowTitle.autoPinEdgeToSuperviewEdge(.Left, withInset: 18)
|
|
|
|
|
2016-08-14 16:38:41 +03:00
|
|
|
whenLaunching.autoPinEdge(.Top, toEdge: .Bottom, ofView: paneTitle, withOffset: 18)
|
|
|
|
whenLaunching.autoPinEdge(.Left, toEdge: .Right, ofView: openUntitledWindowTitle, withOffset: 5)
|
2016-08-25 10:27:59 +03:00
|
|
|
whenLaunching.autoPinEdgeToSuperviewEdge(.Right, withInset: 18, relation: .GreaterThanOrEqual)
|
2016-08-14 16:38:41 +03:00
|
|
|
|
|
|
|
onReactivation.autoPinEdge(.Top, toEdge: .Bottom, ofView: whenLaunching, withOffset: 5)
|
|
|
|
onReactivation.autoPinEdge(.Left, toEdge: .Left, ofView: whenLaunching)
|
2016-08-25 10:27:59 +03:00
|
|
|
onReactivation.autoPinEdgeToSuperviewEdge(.Right, withInset: 18, relation: .GreaterThanOrEqual)
|
2016-09-10 22:46:43 +03:00
|
|
|
|
|
|
|
ignoreListTitle.autoAlignAxis(.Baseline, toSameAxisOfView: ignoreField)
|
|
|
|
ignoreListTitle.autoPinEdge(.Right, toEdge: .Right, ofView: openUntitledWindowTitle)
|
|
|
|
ignoreListTitle.autoPinEdgeToSuperviewEdge(.Left, withInset: 18, relation: .GreaterThanOrEqual)
|
|
|
|
|
|
|
|
ignoreField.autoPinEdge(.Top, toEdge: .Bottom, ofView: onReactivation, withOffset: 18)
|
|
|
|
ignoreField.autoPinEdgeToSuperviewEdge(.Right, withInset: 18)
|
|
|
|
ignoreField.autoPinEdge(.Left, toEdge: .Right, ofView: ignoreListTitle, withOffset: 5)
|
|
|
|
|
|
|
|
ignoreInfo.autoPinEdge(.Top, toEdge: .Bottom, ofView: ignoreField, withOffset: 5)
|
|
|
|
ignoreInfo.autoPinEdgeToSuperviewEdge(.Right, withInset: 18)
|
|
|
|
ignoreInfo.autoPinEdge(.Left, toEdge: .Right, ofView: ignoreListTitle, withOffset: 5)
|
2016-08-25 10:27:59 +03:00
|
|
|
|
2016-09-10 22:46:43 +03:00
|
|
|
cliToolTitle.autoAlignAxis(.Baseline, toSameAxisOfView: cliToolButton)
|
2016-08-25 10:27:59 +03:00
|
|
|
cliToolTitle.autoPinEdgeToSuperviewEdge(.Left, withInset: 18, relation: .GreaterThanOrEqual)
|
|
|
|
cliToolTitle.autoPinEdge(.Right, toEdge: .Right, ofView: openUntitledWindowTitle)
|
|
|
|
|
2016-09-10 22:46:43 +03:00
|
|
|
cliToolButton.autoPinEdge(.Top, toEdge: .Bottom, ofView: ignoreInfo, withOffset: 18)
|
2016-08-25 10:27:59 +03:00
|
|
|
cliToolButton.autoPinEdgeToSuperviewEdge(.Right, withInset: 18, relation: .GreaterThanOrEqual)
|
2016-09-10 22:46:43 +03:00
|
|
|
cliToolButton.autoPinEdge(.Left, toEdge: .Right, ofView: cliToolTitle, withOffset: 5)
|
2016-08-25 10:27:59 +03:00
|
|
|
|
|
|
|
cliToolInfo.autoPinEdge(.Top, toEdge: .Bottom, ofView: cliToolButton, withOffset: 5)
|
|
|
|
cliToolInfo.autoPinEdgeToSuperviewEdge(.Right, withInset: 18, relation: .GreaterThanOrEqual)
|
2016-09-10 22:46:43 +03:00
|
|
|
cliToolInfo.autoPinEdge(.Left, toEdge: .Right, ofView: cliToolTitle, withOffset: 5)
|
2016-08-25 10:27:59 +03:00
|
|
|
|
2016-08-14 22:46:03 +03:00
|
|
|
self.openWhenLaunchingCheckbox.boolState = self.data.openNewWindowWhenLaunching
|
|
|
|
self.openOnReactivationCheckbox.boolState = self.data.openNewWindowOnReactivation
|
2016-08-14 15:29:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
override func subscription(source source: Observable<Any>) -> Disposable {
|
|
|
|
return source
|
2016-08-14 22:46:03 +03:00
|
|
|
.filter { $0 is PrefData }
|
|
|
|
.map { ($0 as! PrefData).general }
|
2016-08-14 19:59:00 +03:00
|
|
|
.filter { [unowned self] data in data != self.data }
|
2016-09-11 15:28:56 +03:00
|
|
|
.subscribeNext { [unowned self] data in
|
|
|
|
self.updateViews(newData: data)
|
|
|
|
self.data = data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-13 09:32:58 +03:00
|
|
|
override func windowWillClose() {
|
|
|
|
self.ignorePatternsAction()
|
2016-09-11 15:28:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private func set(data data: GeneralPrefData) {
|
|
|
|
self.data = data
|
|
|
|
self.publish(event: data)
|
2016-08-14 15:29:50 +03:00
|
|
|
}
|
2016-08-14 16:01:28 +03:00
|
|
|
|
2016-09-10 22:46:43 +03:00
|
|
|
private func ignoreInfoText() -> NSAttributedString {
|
|
|
|
let font = NSFont.systemFontOfSize(NSFont.smallSystemFontSize())
|
2016-09-11 15:28:56 +03:00
|
|
|
let attrs = [
|
|
|
|
NSFontAttributeName: font,
|
|
|
|
NSForegroundColorAttributeName: NSColor.grayColor()
|
|
|
|
]
|
|
|
|
|
|
|
|
let wikiUrl = NSURL(string: "https://github.com/qvacua/vimr/wiki")!
|
2016-09-10 22:46:43 +03:00
|
|
|
let linkStr = NSAttributedString.link(withUrl: wikiUrl, text: "VimR Wiki", font: font)
|
2016-09-24 17:31:14 +03:00
|
|
|
let str = "Comma-separated list of ignore patterns\n"
|
2016-09-10 22:46:43 +03:00
|
|
|
+ "Matching files will be ignored in \"Open Quickly\".\n"
|
|
|
|
+ "Example: */.git, */node_modules\n"
|
2016-09-24 17:31:14 +03:00
|
|
|
+ "For detailed information see "
|
|
|
|
|
|
|
|
let ignoreInfoStr = NSMutableAttributedString(string:str, attributes:attrs)
|
2016-09-10 22:46:43 +03:00
|
|
|
ignoreInfoStr.appendAttributedString(linkStr)
|
2016-09-11 15:28:56 +03:00
|
|
|
ignoreInfoStr.appendAttributedString(NSAttributedString(string: ".", attributes: attrs))
|
2016-09-10 22:46:43 +03:00
|
|
|
|
|
|
|
return ignoreInfoStr
|
|
|
|
}
|
|
|
|
|
2016-08-14 22:46:03 +03:00
|
|
|
private func updateViews(newData newData: GeneralPrefData) {
|
2016-09-11 15:28:56 +03:00
|
|
|
self.openWhenLaunchingCheckbox.boolState = newData.openNewWindowWhenLaunching
|
|
|
|
self.openOnReactivationCheckbox.boolState = newData.openNewWindowOnReactivation
|
|
|
|
self.ignoreField.stringValue = PrefUtils.ignorePatternString(fromSet: newData.ignorePatterns)
|
2016-08-14 16:01:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Actions
|
|
|
|
extension GeneralPrefPane {
|
2016-08-25 10:27:59 +03:00
|
|
|
|
|
|
|
func copyCliTool(sender: NSButton) {
|
|
|
|
let panel = NSOpenPanel()
|
|
|
|
panel.canChooseFiles = false
|
|
|
|
panel.canChooseDirectories = true
|
|
|
|
|
|
|
|
panel.beginSheetModalForWindow(self.window!) { result in
|
|
|
|
guard result == NSFileHandlingPanelOKButton else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let vimrUrl = NSBundle.mainBundle().URLForResource("vimr", withExtension: nil) else {
|
|
|
|
self.alert(title: "Something Went Wrong.",
|
|
|
|
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let targetUrl = panel.URL?.URLByAppendingPathComponent("vimr") else {
|
|
|
|
self.alert(title: "Something Went Wrong.",
|
|
|
|
info: "The target directory could not be determined. Please try again with a different directory.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
try NSFileManager.defaultManager().copyItemAtURL(vimrUrl, toURL: targetUrl)
|
2016-08-25 10:53:28 +03:00
|
|
|
} catch let err as NSError {
|
|
|
|
self.alert(title: "Error copying 'vimr'", info: err.localizedDescription)
|
2016-08-25 10:27:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-14 16:01:28 +03:00
|
|
|
|
2016-08-14 16:38:41 +03:00
|
|
|
func openUntitledWindowWhenLaunchingAction(sender: NSButton) {
|
2016-09-11 15:28:56 +03:00
|
|
|
self.set(data: GeneralPrefData(
|
|
|
|
openNewWindowWhenLaunching: self.openWhenLaunchingCheckbox.boolState,
|
|
|
|
openNewWindowOnReactivation: self.data.openNewWindowOnReactivation,
|
|
|
|
ignorePatterns: self.data.ignorePatterns)
|
|
|
|
)
|
2016-08-14 16:01:28 +03:00
|
|
|
}
|
|
|
|
|
2016-09-11 15:28:56 +03:00
|
|
|
func openUntitledWindowOnReactivationAction(sender: NSButton) {
|
|
|
|
self.set(data: GeneralPrefData(
|
|
|
|
openNewWindowWhenLaunching: self.data.openNewWindowWhenLaunching,
|
|
|
|
openNewWindowOnReactivation: self.openOnReactivationCheckbox.boolState,
|
|
|
|
ignorePatterns: self.data.ignorePatterns)
|
|
|
|
)
|
2016-08-14 16:01:28 +03:00
|
|
|
}
|
2016-09-11 15:28:56 +03:00
|
|
|
|
2016-09-13 09:32:58 +03:00
|
|
|
private func ignorePatternsAction() {
|
|
|
|
let patterns = PrefUtils.ignorePatterns(fromString: self.ignoreField.stringValue)
|
|
|
|
if patterns == self.data.ignorePatterns {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-09-11 15:28:56 +03:00
|
|
|
self.set(data: GeneralPrefData(
|
|
|
|
openNewWindowWhenLaunching: self.data.openNewWindowWhenLaunching,
|
|
|
|
openNewWindowOnReactivation: self.data.openNewWindowOnReactivation,
|
|
|
|
ignorePatterns: patterns)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2016-08-25 10:27:59 +03:00
|
|
|
private func alert(title title: String, info: String) {
|
|
|
|
let alert = NSAlert()
|
|
|
|
alert.alertStyle = .WarningAlertStyle
|
|
|
|
alert.messageText = title
|
|
|
|
alert.informativeText = info
|
|
|
|
alert.runModal()
|
|
|
|
}
|
2016-08-14 15:29:50 +03:00
|
|
|
}
|
2016-09-13 09:32:58 +03:00
|
|
|
|