aboutsummaryrefslogtreecommitdiffstats
path: root/WireGuard/WireGuard/UI/macOS/View/OnDemandWiFiControls.swift
blob: 194075d430c47621b177e82c16bae7c4ee01110e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// SPDX-License-Identifier: MIT
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.

import Cocoa
import CoreWLAN

class OnDemandWiFiControls: NSStackView {

    let onDemandWiFiCheckbox: NSButton = {
        let checkbox = NSButton()
        checkbox.title = tr("tunnelOnDemandWiFi")
        checkbox.setButtonType(.switch)
        checkbox.state = .off
        return checkbox
    }()

    static let onDemandSSIDOptions: [ActivateOnDemandViewModel.OnDemandSSIDOption] = [
        .anySSID, .onlySpecificSSIDs, .exceptSpecificSSIDs
    ]

    let onDemandSSIDOptionsPopup = NSPopUpButton()

    let onDemandSSIDsField: NSTokenField = {
        let tokenField = NSTokenField()
        tokenField.tokenizingCharacterSet = CharacterSet([])
        NSLayoutConstraint.activate([
            tokenField.widthAnchor.constraint(greaterThanOrEqualToConstant: 150)
        ])
        return tokenField
    }()

    override var intrinsicContentSize: NSSize {
        let minHeight: CGFloat = 22
        let height = max(minHeight, onDemandWiFiCheckbox.intrinsicContentSize.height, onDemandSSIDOptionsPopup.intrinsicContentSize.height, onDemandSSIDsField.intrinsicContentSize.height)
        return NSSize(width: NSView.noIntrinsicMetric, height: height)
    }

    var onDemandViewModel: ActivateOnDemandViewModel? {
        didSet { updateSSIDControls() }
    }

    var currentSSIDs: [String]

    init() {
        currentSSIDs = getCurrentSSIDs()
        super.init(frame: CGRect.zero)
        onDemandSSIDOptionsPopup.addItems(withTitles: OnDemandWiFiControls.onDemandSSIDOptions.map { $0.localizedUIString })
        setViews([onDemandWiFiCheckbox, onDemandSSIDOptionsPopup, onDemandSSIDsField], in: .leading)
        orientation = .horizontal

        NSLayoutConstraint.activate([
            onDemandWiFiCheckbox.centerYAnchor.constraint(equalTo: centerYAnchor),
            onDemandSSIDOptionsPopup.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor),
            onDemandSSIDsField.lastBaselineAnchor.constraint(equalTo: onDemandWiFiCheckbox.lastBaselineAnchor)
        ])

        onDemandWiFiCheckbox.target = self
        onDemandWiFiCheckbox.action = #selector(wiFiCheckboxToggled)

        onDemandSSIDOptionsPopup.target = self
        onDemandSSIDOptionsPopup.action = #selector(ssidOptionsPopupValueChanged)

        onDemandSSIDsField.delegate = self

        updateSSIDControls()
    }

    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func saveToViewModel() {
        guard let onDemandViewModel = onDemandViewModel else { return }
        onDemandViewModel.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on
        onDemandViewModel.ssidOption = OnDemandWiFiControls.onDemandSSIDOptions[onDemandSSIDOptionsPopup.indexOfSelectedItem]
        onDemandViewModel.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? []
    }

    func updateSSIDControls() {
        guard let onDemandViewModel = onDemandViewModel else { return }
        onDemandWiFiCheckbox.state = onDemandViewModel.isWiFiInterfaceEnabled ? .on : .off
        let optionIndex = OnDemandWiFiControls.onDemandSSIDOptions.firstIndex(of: onDemandViewModel.ssidOption)
        onDemandSSIDOptionsPopup.selectItem(at: optionIndex ?? 0)
        onDemandSSIDsField.objectValue = onDemandViewModel.selectedSSIDs
        onDemandSSIDOptionsPopup.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled
        onDemandSSIDsField.isHidden = !onDemandViewModel.isWiFiInterfaceEnabled || onDemandViewModel.ssidOption == .anySSID
    }

    @objc func wiFiCheckboxToggled() {
        onDemandViewModel?.isWiFiInterfaceEnabled = onDemandWiFiCheckbox.state == .on
        updateSSIDControls()
    }

    @objc func ssidOptionsPopupValueChanged() {
        let selectedIndex = onDemandSSIDOptionsPopup.indexOfSelectedItem
        onDemandViewModel?.ssidOption = OnDemandWiFiControls.onDemandSSIDOptions[selectedIndex]
        onDemandViewModel?.selectedSSIDs = (onDemandSSIDsField.objectValue as? [String]) ?? []
        updateSSIDControls()
        if !onDemandSSIDsField.isHidden {
            onDemandSSIDsField.becomeFirstResponder()
        }
    }
}

extension OnDemandWiFiControls: NSTokenFieldDelegate {
    func tokenField(_ tokenField: NSTokenField, completionsForSubstring substring: String, indexOfToken tokenIndex: Int, indexOfSelectedItem selectedIndex: UnsafeMutablePointer<Int>?) -> [Any]? {
        return currentSSIDs.filter { $0.hasPrefix(substring) }
    }
}

private func getCurrentSSIDs() -> [String] {
    return CWWiFiClient.shared().interfaces()?.compactMap { $0.ssid() } ?? []
}