<commit_before>//
//  DateRangePickerView.swift
//  DateRangePicker
//
//  Created by <NAME> on 07.11.15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import Foundation

@IBDesignable
public class DateRangePickerView : NSControl, ExpandedDateRangePickerControllerDelegate, NSPopoverDelegate {
	private let segmentedControl: NSSegmentedControl
	private let dateFormatter = NSDateFormatter()
	private var dateRangePickerController: ExpandedDateRangePickerController?
	
	// MARK: - Date properties
	private var _dateRange: DateRange  // Should almost never be accessed directly
	public var dateRange: DateRange {
		get {
			return _dateRange
		}

		set {
			let restrictedValue = newValue.restrictToDates(minDate, maxDate)
			if _dateRange != restrictedValue {
				self.willChangeValueForKey("endDate")
				self.willChangeValueForKey("startDate")
				_dateRange = restrictedValue
				self.didChangeValueForKey("endDate")
				self.didChangeValueForKey("startDate")
				
				if dateRangePickerController?.dateRange != dateRange {
					dateRangePickerController?.dateRange = dateRange
				}
				updateSegmentedControl()
				
				sendAction(action, to: target)
			}
		}
	}
	
	// Can be used for restricting the selectable dates to a specific range.
	public dynamic var minDate: NSDate? {
		didSet {
			dateRangePickerController?.minDate = minDate
			// Enforces the new date range restriction
			dateRange = _dateRange
			updateSegmentedControl()
		}
	}
	public dynamic var maxDate: NSDate? {
		didSet {
			dateRangePickerController?.maxDate = maxDate
			// Enforces the new date range restriction
			dateRange = _dateRange
			updateSegmentedControl()
		}
	}
	
	public var dateStyle: NSDateFormatterStyle {
		get {
			return dateFormatter.dateStyle
		}
		
		set {
			dateFormatter.dateStyle = newValue
			updateSegmentedControl()
		}
	}
	
	public var dateRangeString: String {
		return dateRange.dateRangeDescription(dateFormatter)
	}
	
	// MARK: - Objective-C interoperability
	public dynamic var startDate: NSDate {
		get {
			return dateRange.startDate!
		}
		
		set {
			dateRange = DateRange.Custom(newValue, endDate)
		}
	}
	public dynamic var endDate: NSDate {
		get {
			return dateRange.endDate!
		}
		
		set {
			dateRange = DateRange.Custom(startDate, newValue)
		}
	}
	
	public func setStartDate(startDate: NSDate, endDate: NSDate) {
		dateRange = .Custom(startDate, endDate)
	}
	
	// In Objective-C, the DateRange type isn't available. In order to still persist the picker's
	// date range (e.g. between launches), you can use these functions instead.
	public func dateRangeAsData() -> NSData {
		return dateRange.toData()
	}
	public func loadDateRangeFromData(data: NSData) {
		guard let newRange = DateRange.fromData(data) else { return }
		dateRange = newRange
	}
	
	// MARK: - Other properties
	public var segmentStyle: NSSegmentStyle {
		get {
			return segmentedControl.segmentStyle
		}
		
		set {
			segmentedControl.segmentStyle = newValue
		}
	}
	
	// MARK: - Methods
	public func displayExpandedDatePicker() {
		if dateRangePickerController != nil { return }
		
		let popover = NSPopover()
		popover.behavior = .Semitransient
		dateRangePickerController = ExpandedDateRangePickerController(dateRange: dateRange)
		dateRangePickerController?.minDate = minDate
		dateRangePickerController?.maxDate = maxDate
		dateRangePickerController?.delegate = self
		popover.contentViewController = dateRangePickerController
		popover.delegate = self
		popover.showRelativeToRect(self.bounds, ofView: self, preferredEdge: .MinY)
		updateSegmentedControl()
	}
	
	// MARK: - Initializers
	private func sharedInit() {
		segmentedControl.segmentCount = 3
		segmentedControl.setLabel("◀", forSegment: 0)
		segmentedControl.setLabel("▶", forSegment: 2)
		segmentedControl.action = "segmentDidChange:"
		segmentedControl.autoresizingMask = [.ViewNotSizable]
		segmentedControl.target = self
		self.addSubview(segmentedControl)
		
		self.dateStyle = .MediumStyle
	}
	
	override public init(frame frameRect: NSRect) {
		segmentedControl = NSSegmentedControl()
		_dateRange = .PastDays(7)
		super.init(frame: frameRect)
		sharedInit()
	}
	
	required public init?(coder: NSCoder) {
		segmentedControl = NSSegmentedControl()
		_dateRange = .PastDays(7)
		super.init(coder: coder)
		sharedInit()
	}
	
	// MARK: - Internal
	override public func resizeSubviewsWithOldSize(size: CGSize) {
		let sideButtonWidth: CGFloat = 22
		// Magic number to avoid the segmented control overflowing out of its bounds.
		let unusedControlWidth: CGFloat = 8
		segmentedControl.setWidth(sideButtonWidth, forSegment:0)
		segmentedControl.setWidth(self.bounds.size.width - 2 * sideButtonWidth - unusedControlWidth, forSegment:1)
		segmentedControl.setWidth(sideButtonWidth, forSegment:2)
		segmentedControl.frame = self.bounds
		super.resizeSubviewsWithOldSize(size)
	}
	
	func segmentDidChange(sender: NSSegmentedControl) {
		switch sender.selectedSegment {
		case 0:
			dateRange = dateRange.previous()
		case 1:
			displayExpandedDatePicker()
		case 2:
			dateRange = dateRange.next()
		default:
			break
		}
	}
	
	private func updateSegmentedControl() {
		segmentedControl.setLabel(dateRangeString, forSegment: 1)
		<commit_msg>Minor comment on why we're not using Auto Layout.<commit_after>//
//  DateRangePickerView.swift
//  DateRangePicker
//
//  Created by <NAME> on 07.11.15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import Foundation

@IBDesignable
public class DateRangePickerView : NSControl, ExpandedDateRangePickerControllerDelegate, NSPopoverDelegate {
	private let segmentedControl: NSSegmentedControl
	private let dateFormatter = NSDateFormatter()
	private var dateRangePickerController: ExpandedDateRangePickerController?
	
	// MARK: - Date properties
	private var _dateRange: DateRange  // Should almost never be accessed directly
	public var dateRange: DateRange {
		get {
			return _dateRange
		}

		set {
			let restrictedValue = newValue.restrictToDates(minDate, maxDate)
			if _dateRange != restrictedValue {
				self.willChangeValueForKey("endDate")
				self.willChangeValueForKey("startDate")
				_dateRange = restrictedValue
				self.didChangeValueForKey("endDate")
				self.didChangeValueForKey("startDate")
				
				if dateRangePickerController?.dateRange != dateRange {
					dateRangePickerController?.dateRange = dateRange
				}
				updateSegmentedControl()
				
				sendAction(action, to: target)
			}
		}
	}
	
	// Can be used for restricting the selectable dates to a specific range.
	public dynamic var minDate: NSDate? {
		didSet {
			dateRangePickerController?.minDate = minDate
			// Enforces the new date range restriction
			dateRange = _dateRange
			updateSegmentedControl()
		}
	}
	public dynamic var maxDate: NSDate? {
		didSet {
			dateRangePickerController?.maxDate = maxDate
			// Enforces the new date range restriction
			dateRange = _dateRange
			updateSegmentedControl()
		}
	}
	
	public var dateStyle: NSDateFormatterStyle {
		get {
			return dateFormatter.dateStyle
		}
		
		set {
			dateFormatter.dateStyle = newValue
			updateSegmentedControl()
		}
	}
	
	public var dateRangeString: String {
		return dateRange.dateRangeDescription(dateFormatter)
	}
	
	// MARK: - Objective-C interoperability
	public dynamic var startDate: NSDate {
		get {
			return dateRange.startDate!
		}
		
		set {
			dateRange = DateRange.Custom(newValue, endDate)
		}
	}
	public dynamic var endDate: NSDate {
		get {
			return dateRange.endDate!
		}
		
		set {
			dateRange = DateRange.Custom(startDate, newValue)
		}
	}
	
	public func setStartDate(startDate: NSDate, endDate: NSDate) {
		dateRange = .Custom(startDate, endDate)
	}
	
	// In Objective-C, the DateRange type isn't available. In order to still persist the picker's
	// date range (e.g. between launches), you can use these functions instead.
	public func dateRangeAsData() -> NSData {
		return dateRange.toData()
	}
	public func loadDateRangeFromData(data: NSData) {
		guard let newRange = DateRange.fromData(data) else { return }
		dateRange = newRange
	}
	
	// MARK: - Other properties
	public var segmentStyle: NSSegmentStyle {
		get {
			return segmentedControl.segmentStyle
		}
		
		set {
			segmentedControl.segmentStyle = newValue
		}
	}
	
	// MARK: - Methods
	public func displayExpandedDatePicker() {
		if dateRangePickerController != nil { return }
		
		let popover = NSPopover()
		popover.behavior = .Semitransient
		dateRangePickerController = ExpandedDateRangePickerController(dateRange: dateRange)
		dateRangePickerController?.minDate = minDate
		dateRangePickerController?.maxDate = maxDate
		dateRangePickerController?.delegate = self
		popover.contentViewController = dateRangePickerController
		popover.delegate = self
		popover.showRelativeToRect(self.bounds, ofView: self, preferredEdge: .MinY)
		updateSegmentedControl()
	}
	
	// MARK: - Initializers
	private func sharedInit() {
		segmentedControl.segmentCount = 3
		segmentedControl.setLabel("◀", forSegment: 0)
		segmentedControl.setLabel("▶", forSegment: 2)
		segmentedControl.action = "segmentDidChange:"
		segmentedControl.autoresizingMask = [.ViewNotSizable]
		segmentedControl.target = self
		self.addSubview(segmentedControl)
		
		self.dateStyle = .MediumStyle
	}
	
	override public init(frame frameRect: NSRect) {
		segmentedControl = NSSegmentedControl()
		_dateRange = .PastDays(7)
		super.init(frame: frameRect)
		sharedInit()
	}
	
	required public init?(coder: NSCoder) {
		segmentedControl = NSSegmentedControl()
		_dateRange = .PastDays(7)
		super.init(coder: coder)
		sharedInit()
	}
	
	// MARK: - Internal
	override public func resizeSubviewsWithOldSize(size: CGSize) {
		// It would be nice to use Auto Layout instead, but that doesn't play nicely with views in a toolbar.
		let sideButtonWidth: CGFloat = 22
		// Magic number to avoid the segmented control overflowing out of its bounds.
		let unusedControlWidth: CGFloat = 8
		segmentedControl.setWidth(sideButtonWidth, forSegment:0)
		segmentedControl.setWidth(self.bounds.size.width - 2 * sideButtonWidth - unusedControlWidth, forSegment:1)
		segmentedControl.setWidth(sideButtonWidth, forSegment:2)
		segmentedControl.frame = self.bounds
		super.resizeSubviewsWithOldSize(size)
	}
	
	func segmentDidChange(sender: NSSegmentedControl) {
		switch sender.selectedSegment {
		case 0:
			dateRange = dateRange.previous()
		case 1:
			displayExpandedDatePicker()
		case 2:
			dateRange = dateRange.next()
		default:
			break
		}
	}
	
	private func updateSegmentedControl() {
		segmentedControl.setLabel(dateRangeString, forSegment: 1)
		
{'fraction_non_alphanumeric': 0.08439098314074635, 'fraction_numerical': 0.004925175222580034, 'mean_word_length': 4.138199513381995, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2259964', 'n_tokens_mistral': 3668, 'n_tokens_neox': 3154, 'n_words': 777}