blob: 2f46e58dda5940362396e540456bb55ad86a88df [file] [log] [blame]
// Copyright 2016 The Tulsi Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Cocoa
protocol Selectable: class {
var selected: Bool { get set }
}
/// Models a UIRuleEntry as a node suitable for an outline view controller.
class UISelectableOutlineViewNode: NSObject {
/// The display name for this node.
let name: String
/// The object contained by this node (only valid for leaf nodes).
var entry: Selectable? {
didSet {
if let entry = entry {
state = entry.selected ? NSOnState : NSOffState
}
}
}
/// This node's children.
var children: [UISelectableOutlineViewNode] {
return _children
}
private var _children = [UISelectableOutlineViewNode]()
/// This node's parent.
weak var parent: UISelectableOutlineViewNode?
/// This node's checkbox state in the UI (NSOnState/NSOffState/NSMixedState)
dynamic var state: Int {
get {
if children.isEmpty {
return (entry?.selected ?? false) ? NSOnState : NSOffState
}
var stateIsValid = false
var state = NSOffState
for node in children {
if !stateIsValid {
state = node.state
stateIsValid = true
continue
}
if state != node.state {
return NSMixedState
}
}
return state
}
set {
let newSelectionState = (newValue == NSOnState)
let selected = entry?.selected
if selected == newSelectionState {
return
}
willChangeValue(forKey: "state")
if let entry = entry {
entry.selected = newSelectionState
}
for node in children {
node.state = newValue
}
didChangeValue(forKey: "state")
// Notify KVO that this node's ancestors have also changed state.
var ancestor = parent
while ancestor != nil {
ancestor!.willChangeValue(forKey: "state")
ancestor!.didChangeValue(forKey: "state")
ancestor = ancestor!.parent
}
}
}
init(name: String) {
self.name = name
super.init()
}
func addChild(_ child: UISelectableOutlineViewNode) {
_children.append(child)
child.parent = self
}
// TODO(abaire): Use a custom control to override nextState: such that it's never set to mixed via user interaction.
func validateState(_ ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
if let value = ioValue.pointee as? NSNumber {
if value.intValue == NSMixedState {
ioValue.pointee = NSNumber(value: NSOnState as Int)
}
}
}
}