<commit_before>//
//  JumpBarControl.swift
//  KPCJumpBarControl
//
//  Created by <NAME> on 08/05/16.
//  Licensed under the MIT License (see LICENSE file)
//

import AppKit

public enum JumpBarControlError: Error {
    case alreadyBound(String)
}

fileprivate struct JumpBarControlBinding {
    fileprivate static var treeObserverContext = 1
    fileprivate weak var treeController: NSTreeController?
    fileprivate var treeKeyPath: String    
}

open class JumpBarControl: NSControl, JumpBarSegmentControlDelegate {
    let KPCJumpBarControlTag: NSInteger = -9999999

    open weak var delegate: JumpBarControlDelegate? = nil
    public fileprivate(set) var selectedIndexPath: IndexPath?
    
    fileprivate var hasCompressedSegments: Bool = false
    fileprivate var isKey: Bool = false

    fileprivate var contentBinding: JumpBarControlBinding?
    fileprivate var selectionBinding: JumpBarControlBinding?
    public var isBound: Bool { get { return self.contentBinding != nil } }
    fileprivate var previousSelectionIndexPaths: [IndexPath] = []
    fileprivate var hasMultipleSelection: Bool = false

    // MARK: - Overrides
    
    override open var isFlipped: Bool {
        get { return true }
    }
    
    override open var isEnabled: Bool {
        didSet {
            self.segmentControls().forEach({ $0.isEnabled = self.isEnabled})
            self.setNeedsDisplay()
        }
    }
    
    override open var frame: NSRect {
        didSet { self.layoutSegmentsIfNeeded(withNewSize:super.frame.size) }
    }

    override open var bounds: NSRect {
        didSet { self.layoutSegmentsIfNeeded(withNewSize:super.bounds.size) }
    }
    
    // MARK: - Constructors

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        self.setup()
    }
    
    required public init?(coder: NSCoder) {
        super.init(coder: coder)
        self.setup()
    }

    fileprivate func setup() {
        self.tag = KPCJumpBarControlTag
        self.isEnabled = true
    }

    override open func menu(for event: NSEvent) -> NSMenu? {
        return nil
    }
    
    // MARK: - Window

    override open func viewWillMove(toWindow newWindow: NSWindow?) {
        super.viewWillMove(toWindow: newWindow)
        
        NotificationCenter.default.removeObserver(self, name:NSWindow.didResignKeyNotification, object:self.window)
        NotificationCenter.default.removeObserver(self, name:NSWindow.didBecomeKeyNotification, object:self.window)
    }
    
    override open func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        
        NotificationCenter.default.addObserver(self,
                                               selector:#selector(JumpBarControl.setControlNeedsDisplay),
                                               name:NSWindow.didResignKeyNotification,
                                               object:self.window)
        
        NotificationCenter.default.addObserver(self,
                                               selector:#selector(JumpBarControl.setControlNeedsDisplay),
                                               name:NSWindow.didBecomeKeyNotification,
                                               object:self.window)
    }
    
    @objc open func setControlNeedsDisplay() {
        self.setNeedsDisplay()
    }
    
    // MARK: - Content
    
    open func bindItemsTree(to treeController: NSTreeController) throws
    {
        guard self.contentBinding == nil else {
            throw JumpBarControlError.alreadyBound("JumpBarControl \(self) is already bound to tree and selection controllers. Unbind first.")
        }
        
        self.contentBinding = JumpBarControlBinding(treeController: treeController, treeKeyPath: "arrangedObjects")
        self.selectionBinding = JumpBarControlBinding(treeController: treeController, treeKeyPath: "selectionIndexPaths")
        
        treeController.addObserver(self,
                                   forKeyPath: "arrangedObjects",
                                   options: [.new, .old],
                                   context: &JumpBarControlBinding.treeObserverContext)

        treeController.addObserver(self,
                                   forKeyPath: "selectionIndexPaths",
                                   options: [.new, .old],
                                   context: &JumpBarControlBinding.treeObserverContext)

        self.useItemsTree(treeController.arrangedRootObjects())
    }
    
    open func unbindItemsTreeAndSelection() {
        guard self.contentBinding != nil else {
            return
        }
        
        self.contentBinding!.treeController?.removeObserver(self, forKeyPath: self.contentBinding!.treeKeyPath)
        self.selectionBinding!.treeController?.removeObserver(self, forKeyPath: self.selectionBinding!.treeKeyPath)
        self.contentBinding = nil
        self.selectionBinding = nil
    }
    
    open override func observeValue(forKeyPath keyPath: String?,
                                    of object: Any?,
                                    change: [NSKeyValueChangeKey : Any]?,
                                    context: UnsafeMutableRawPointer?)
    {
        guard context == &JumpBarControlBinding.treeObserverContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }
        
        if (keyPath == "arrangedObjects") {
            self.hasMultipleSelection = false
            self.useItemsTree(self.contentBinding!.treeController!.arrangedRootObjects())
        } else if (keyPath == "selectionIndexPaths") {
            let treeController: NSTreeController = object as! NSTreeController
            if (treeController.selectionIndexPaths.count == 1) {
                self.hasMultipleSelection = false
                self.update(withIndexPath: treeController.selectionIndexPaths.first!)
            } else {
                self.hasMultipleSelection = true
                let commonAncestorIndexPath = IndexPath.commonAncestor(indexPaths: treeController.selectionIndexPaths)
                self.update(withIndexPath: commonAncestorIndexPath ?? IndexPath())
            }
            self.previousSelectionIndexPaths = treeController.selectionIndexPaths
        }
    }

    open func useItemsTree(_ itemsTree: [JumpBarItemProtocol]) {
        self.segmentControls().forEach { $0.removeFromSuperview() }

        // At that stage, we do items all in one shot. Might be optimized later.
        self.menu = NSMenu.menuWithTreeNodeObjects(itemsTree,
                                                   target:self,
                                                   action:#selector(JumpBarControl.select(itemFromMenuItem:)))
        
        if itemsTree.count > 0 {
            self.selectedIndexPath = IndexPath(index: 0)
        }
        
        self.layoutSegments()
    }
    
    // MARK: - Selection
    
    @objc fileprivate func select(itemFromMenuItem sender: NSMenuItem) {
        self.select(itemAtIndexPath: sender.indexPath())
    }
    
    open func select(itemAtIndexPath nextSelectedIndexPath: IndexPath) {
        guard nextSelectedIndexPath != self.selectedIndexPath else {
            return
        }
        if let nextSelectedItem = self.segmentItem(atIndexPath: nextSelectedIndexPath) {
            self.delegate?.jumpBarControl(self, willSelectItem: nextSelectedItem, atIndexPath: nextSelectedIndexPath)
            self.update(withIndexPath: nextSelectedIndexPath)
            self.delegate?.jumpBarControl(self, didSelectItem: nextSelectedItem, atIndexPath: nextSelectedIndexPath)
        }
    }
    
    private func update(withIndexPath indexPath: IndexPath) {
        self.removeSegments(fromLevel: 0)
        self.selectedIndexPath = indexPath
        self.layoutSegments()
    }
    
    open func selectedSegmentItem() -> JumpBarItemProtocol? {
        if let ip = self.selectedIndexPath {
            return self.segmentItem(atIndexPath: ip)
        }
        return nil
    }
    
    open func segmentItem(atIndexPath indexPath: IndexPath) -> JumpBarItemProtocol? {
        guard let item = self.menu?.menuItemAtIndexPath(indexPath) else {
            return nil
        }
        return item.representedObject as? JumpBarItemProtocol // Return representedObject, since NSMenuItem(s) are kept hidden
    }

    // MARK: - Activation

    open func makeKey(_ flag: Bool) {<commit_msg>Make the update func visible to allow the update of the jumpBar without triggering selection in delegate<commit_after>//
//  JumpBarControl.swift
//  KPCJumpBarControl
//
//  Created by <NAME> on 08/05/16.
//  Licensed under the MIT License (see LICENSE file)
//

import AppKit

public enum JumpBarControlError: Error {
    case alreadyBound(String)
}

fileprivate struct JumpBarControlBinding {
    fileprivate static var treeObserverContext = 1
    fileprivate weak var treeController: NSTreeController?
    fileprivate var treeKeyPath: String    
}

open class JumpBarControl: NSControl, JumpBarSegmentControlDelegate {
    let KPCJumpBarControlTag: NSInteger = -9999999

    open weak var delegate: JumpBarControlDelegate? = nil
    public fileprivate(set) var selectedIndexPath: IndexPath?
    
    fileprivate var hasCompressedSegments: Bool = false
    fileprivate var isKey: Bool = false

    fileprivate var contentBinding: JumpBarControlBinding?
    fileprivate var selectionBinding: JumpBarControlBinding?
    public var isBound: Bool { get { return self.contentBinding != nil } }
    fileprivate var previousSelectionIndexPaths: [IndexPath] = []
    fileprivate var hasMultipleSelection: Bool = false

    // MARK: - Overrides
    
    override open var isFlipped: Bool {
        get { return true }
    }
    
    override open var isEnabled: Bool {
        didSet {
            self.segmentControls().forEach({ $0.isEnabled = self.isEnabled})
            self.setNeedsDisplay()
        }
    }
    
    override open var frame: NSRect {
        didSet { self.layoutSegmentsIfNeeded(withNewSize:super.frame.size) }
    }

    override open var bounds: NSRect {
        didSet { self.layoutSegmentsIfNeeded(withNewSize:super.bounds.size) }
    }
    
    // MARK: - Constructors

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        self.setup()
    }
    
    required public init?(coder: NSCoder) {
        super.init(coder: coder)
        self.setup()
    }

    fileprivate func setup() {
        self.tag = KPCJumpBarControlTag
        self.isEnabled = true
    }

    override open func menu(for event: NSEvent) -> NSMenu? {
        return nil
    }
    
    // MARK: - Window

    override open func viewWillMove(toWindow newWindow: NSWindow?) {
        super.viewWillMove(toWindow: newWindow)
        
        NotificationCenter.default.removeObserver(self, name:NSWindow.didResignKeyNotification, object:self.window)
        NotificationCenter.default.removeObserver(self, name:NSWindow.didBecomeKeyNotification, object:self.window)
    }
    
    override open func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        
        NotificationCenter.default.addObserver(self,
                                               selector:#selector(JumpBarControl.setControlNeedsDisplay),
                                               name:NSWindow.didResignKeyNotification,
                                               object:self.window)
        
        NotificationCenter.default.addObserver(self,
                                               selector:#selector(JumpBarControl.setControlNeedsDisplay),
                                               name:NSWindow.didBecomeKeyNotification,
                                               object:self.window)
    }
    
    @objc open func setControlNeedsDisplay() {
        self.setNeedsDisplay()
    }
    
    // MARK: - Content
    
    open func bindItemsTree(to treeController: NSTreeController) throws
    {
        guard self.contentBinding == nil else {
            throw JumpBarControlError.alreadyBound("JumpBarControl \(self) is already bound to tree and selection controllers. Unbind first.")
        }
        
        self.contentBinding = JumpBarControlBinding(treeController: treeController, treeKeyPath: "arrangedObjects")
        self.selectionBinding = JumpBarControlBinding(treeController: treeController, treeKeyPath: "selectionIndexPaths")
        
        treeController.addObserver(self,
                                   forKeyPath: "arrangedObjects",
                                   options: [.new, .old],
                                   context: &JumpBarControlBinding.treeObserverContext)

        treeController.addObserver(self,
                                   forKeyPath: "selectionIndexPaths",
                                   options: [.new, .old],
                                   context: &JumpBarControlBinding.treeObserverContext)

        self.useItemsTree(treeController.arrangedRootObjects())
    }
    
    open func unbindItemsTreeAndSelection() {
        guard self.contentBinding != nil else {
            return
        }
        
        self.contentBinding!.treeController?.removeObserver(self, forKeyPath: self.contentBinding!.treeKeyPath)
        self.selectionBinding!.treeController?.removeObserver(self, forKeyPath: self.selectionBinding!.treeKeyPath)
        self.contentBinding = nil
        self.selectionBinding = nil
    }
    
    open override func observeValue(forKeyPath keyPath: String?,
                                    of object: Any?,
                                    change: [NSKeyValueChangeKey : Any]?,
                                    context: UnsafeMutableRawPointer?)
    {
        guard context == &JumpBarControlBinding.treeObserverContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }
        
        if (keyPath == "arrangedObjects") {
            self.hasMultipleSelection = false
            self.useItemsTree(self.contentBinding!.treeController!.arrangedRootObjects())
        } else if (keyPath == "selectionIndexPaths") {
            let treeController: NSTreeController = object as! NSTreeController
            if (treeController.selectionIndexPaths.count == 1) {
                self.hasMultipleSelection = false
                self.update(withIndexPath: treeController.selectionIndexPaths.first!)
            } else {
                self.hasMultipleSelection = true
                let commonAncestorIndexPath = IndexPath.commonAncestor(indexPaths: treeController.selectionIndexPaths)
                self.update(withIndexPath: commonAncestorIndexPath ?? IndexPath())
            }
            self.previousSelectionIndexPaths = treeController.selectionIndexPaths
        }
    }

    open func useItemsTree(_ itemsTree: [JumpBarItemProtocol]) {
        self.segmentControls().forEach { $0.removeFromSuperview() }

        // At that stage, we do items all in one shot. Might be optimized later.
        self.menu = NSMenu.menuWithTreeNodeObjects(itemsTree,
                                                   target:self,
                                                   action:#selector(JumpBarControl.select(itemFromMenuItem:)))
        
        if itemsTree.count > 0 {
            self.selectedIndexPath = IndexPath(index: 0)
        }
        
        self.layoutSegments()
    }
    
    // MARK: - Selection
    
    @objc fileprivate func select(itemFromMenuItem sender: NSMenuItem) {
        self.select(itemAtIndexPath: sender.indexPath())
    }
    
    open func select(itemAtIndexPath nextSelectedIndexPath: IndexPath) {
        guard nextSelectedIndexPath != self.selectedIndexPath else {
            return
        }
        if let nextSelectedItem = self.segmentItem(atIndexPath: nextSelectedIndexPath) {
            self.delegate?.jumpBarControl(self, willSelectItem: nextSelectedItem, atIndexPath: nextSelectedIndexPath)
            self.update(withIndexPath: nextSelectedIndexPath)
            self.delegate?.jumpBarControl(self, didSelectItem: nextSelectedItem, atIndexPath: nextSelectedIndexPath)
        }
    }
    
    open func update(withIndexPath indexPath: IndexPath) {
        self.removeSegments(fromLevel: 0)
        self.selectedIndexPath = indexPath
        self.layoutSegments()
    }
    
    open func selectedSegmentItem() -> JumpBarItemProtocol? {
        if let ip = self.selectedIndexPath {
            return self.segmentItem(atIndexPath: ip)
        }
        return nil
    }
    
    open func segmentItem(atIndexPath indexPath: IndexPath) -> JumpBarItemProtocol? {
        guard let item = self.menu?.menuItemAtIndexPath(indexPath) else {
            return nil
        }
        return item.representedObject as? JumpBarItemProtocol // Return representedObject, since NSMenuItem(s) are kept hidden
    }

    // MARK: - Activation

    open func makeKey(_ flag: Bool) {
{'fraction_non_alphanumeric': 0.07536504945831371, 'fraction_numerical': 0.0023551577955723034, 'mean_word_length': 2.1506214060471156, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 50, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3503793', 'n_tokens_mistral': 4349, 'n_tokens_neox': 4167, 'n_words': 918}