You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

211 lines
7.1 KiB

// Copyright © 2015 Abhishek Banthia
import Cocoa
import CoreLoggerKit
import CoreModelKit
struct PreferencesDataSourceConstants {
static let timezoneNameIdentifier = "formattedAddress"
static let customLabelIdentifier = "label"
static let availableTimezoneIdentifier = "availableTimezones"
static let favoriteTimezoneIdentifier = "favouriteTimezone"
}
protocol PreferenceSelectionUpdates: AnyObject {
func preferenceSelectionDataSourceMarkAsFavorite(_ dataObject: TimezoneData)
func preferenceSelectionDataSourceUnfavourite(_ dataObject: TimezoneData)
func preferenceSelectionDataSourceRefreshTimezoneTable()
func preferenceSelectionDataSourceRefreshMainTableView()
func preferenceSelectionDataSourceTableViewSelectionDidChange(_ status: Bool)
func preferenceSelectionDataSourceTable(didClick tableColumn: NSTableColumn)
}
class PreferencesDataSource: NSObject {
private weak var updateDelegate: PreferenceSelectionUpdates?
private let store: DataStore
var selectedTimezones: [Data] {
return store.timezones()
}
init(with store: DataStore, callbackDelegate delegate: PreferenceSelectionUpdates) {
self.store = store
updateDelegate = delegate
super.init()
}
}
extension PreferencesDataSource: NSTableViewDelegate {
func tableViewSelectionDidChange(_ notification: Notification) {
if let tableView = notification.object as? NSTableView {
updateDelegate?.preferenceSelectionDataSourceTableViewSelectionDidChange(tableView.selectedRow == -1)
}
}
func tableView(_: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
let data = NSKeyedArchiver.clocker_archive(with: rowIndexes)
pboard.declareTypes([.dragSession], owner: self)
pboard.setData(data, forType: .dragSession)
return true
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation _: NSTableView.DropOperation) -> Bool {
var newOrder = selectedTimezones
var destination = row
if row == newOrder.count {
destination -= 1
}
let pBoard = info.draggingPasteboard
guard let data = pBoard.data(forType: .dragSession) else {
assertionFailure("Data was unexpectedly nil")
return false
}
guard let rowIndexes = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSIndexSet.self, from: data) else {
assertionFailure("Row was unexpectedly nil")
return false
}
let first = rowIndexes.firstIndex
let currentObject = newOrder[first]
newOrder.remove(at: first)
newOrder.insert(currentObject, at: destination)
store.setTimezones(newOrder)
tableView.reloadData()
updateDelegate?.preferenceSelectionDataSourceRefreshMainTableView()
tableView.deselectRow(tableView.selectedRow)
return true
}
func tableView(_: NSTableView, validateDrop _: NSDraggingInfo, proposedRow _: Int, proposedDropOperation _: NSTableView.DropOperation) -> NSDragOperation {
return .every
}
func tableView(_: NSTableView, didClick tableColumn: NSTableColumn) {
updateDelegate?.preferenceSelectionDataSourceTable(didClick: tableColumn)
}
}
extension PreferencesDataSource: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int {
return selectedTimezones.count
}
func tableView(_: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
var selectedDataSource: TimezoneData?
if selectedTimezones.count > row,
let model = TimezoneData.customObject(from: selectedTimezones[row])
{
selectedDataSource = model
}
if tableColumn?.identifier.rawValue == PreferencesDataSourceConstants.timezoneNameIdentifier {
return handleTimezoneNameIdentifier(selectedDataSource)
}
if tableColumn?.identifier.rawValue == PreferencesDataSourceConstants.customLabelIdentifier {
return selectedDataSource?.customLabel ?? "Error"
}
if tableColumn?.identifier.rawValue == PreferencesDataSourceConstants.favoriteTimezoneIdentifier {
return selectedDataSource?.isFavourite ?? 0
}
return nil
}
private func handleTimezoneNameIdentifier(_ selectedDataSource: TimezoneData?) -> Any? {
guard let model = selectedDataSource else {
return nil
}
if let address = model.formattedAddress, address.isEmpty == false {
return model.formattedAddress
}
return model.timezone()
}
func tableView(_: NSTableView, setObjectValue object: Any?, for _: NSTableColumn?, row: Int) {
guard !selectedTimezones.isEmpty, let dataObject = TimezoneData.customObject(from: selectedTimezones[row]) else {
return
}
if let edit = object as? String {
setNewLabel(edit, for: dataObject, at: row)
} else if let isFavouriteValue = object as? NSNumber {
dataObject.isFavourite = isFavouriteValue.intValue
insert(timezone: dataObject, at: row)
dataObject.isFavourite == 1 ?
updateDelegate?.preferenceSelectionDataSourceMarkAsFavorite(dataObject) :
updateDelegate?.preferenceSelectionDataSourceUnfavourite(dataObject)
updateStatusItem()
updateDelegate?.preferenceSelectionDataSourceRefreshTimezoneTable()
}
updateDelegate?.preferenceSelectionDataSourceRefreshMainTableView()
}
private func setNewLabel(_ label: String, for dataObject: TimezoneData, at row: Int) {
let formattedValue = label.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
if selectedTimezones.count > row {
Logger.log(object: [
"Old Label": dataObject.customLabel ?? "Error",
"New Label": formattedValue,
],
for: "Custom Label Changed")
dataObject.setLabel(formattedValue)
insert(timezone: dataObject, at: row)
updateMenubarTitles()
} else {
Logger.log(object: [
"MethodName": "SetObjectValue",
"Selected Timezone Count": selectedTimezones.count,
"Current Row": row,
],
for: "Error in selected row count")
}
}
private func insert(timezone: TimezoneData, at index: Int) {
guard let encodedObject = NSKeyedArchiver.clocker_archive(with: timezone) else {
return
}
var newDefaults = selectedTimezones
newDefaults[index] = encodedObject
store.setTimezones(newDefaults)
}
private func updateMenubarTitles() {
if let appDelegate = NSApplication.shared.delegate as? AppDelegate {
appDelegate.setupMenubarTimer()
}
}
// TODO: This probably does not need to be used
private func updateStatusItem() {
guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else {
return
}
statusItem.refresh()
}
}