2017-11-02 04:45:04 +01:00
|
|
|
//
|
|
|
|
// FeedListViewController.swift
|
|
|
|
// Evergreen
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 11/1/17.
|
|
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2018-02-03 07:51:32 +01:00
|
|
|
import AppKit
|
2017-11-04 20:19:34 +01:00
|
|
|
import RSTree
|
2017-11-04 22:53:21 +01:00
|
|
|
import RSCore
|
2017-11-02 04:45:04 +01:00
|
|
|
|
2018-01-25 07:20:22 +01:00
|
|
|
extension Notification.Name {
|
|
|
|
|
|
|
|
static let FeedListSidebarSelectionDidChange = Notification.Name(rawValue: "FeedListSidebarSelectionDidChange")
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FeedListUserInfoKey {
|
|
|
|
|
|
|
|
static let selectedObject = "selectedObject"
|
|
|
|
}
|
|
|
|
|
2017-11-02 04:45:04 +01:00
|
|
|
final class FeedListViewController: NSViewController {
|
|
|
|
|
2017-11-04 20:19:34 +01:00
|
|
|
@IBOutlet var outlineView: NSOutlineView!
|
2018-02-12 01:20:46 +01:00
|
|
|
@IBOutlet var openHomePageButton: NSButton!
|
|
|
|
@IBOutlet var addToFeedsButton: NSButton!
|
|
|
|
|
2017-11-25 20:33:07 +01:00
|
|
|
private var sidebarCellAppearance: SidebarCellAppearance!
|
2017-11-04 20:19:34 +01:00
|
|
|
private let treeControllerDelegate = FeedListTreeControllerDelegate()
|
|
|
|
lazy var treeController: TreeController = {
|
|
|
|
TreeController(delegate: treeControllerDelegate)
|
|
|
|
}()
|
|
|
|
|
2018-02-12 01:20:46 +01:00
|
|
|
private var selectedNodes: [Node] {
|
|
|
|
if let nodes = outlineView.selectedItems as? [Node] {
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
return [Node]()
|
|
|
|
}
|
|
|
|
|
|
|
|
private var selectedObjects: [AnyObject] {
|
|
|
|
return selectedNodes.representedObjects()
|
|
|
|
}
|
|
|
|
|
2017-11-25 20:33:07 +01:00
|
|
|
// MARK: NSViewController
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
|
2017-12-29 01:37:30 +01:00
|
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
2017-11-25 20:33:07 +01:00
|
|
|
sidebarCellAppearance = SidebarCellAppearance(theme: appDelegate.currentTheme, fontSize: AppDefaults.shared.sidebarFontSize)
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
2017-12-28 22:37:52 +01:00
|
|
|
outlineView.needsLayout = true
|
2018-02-12 01:20:46 +01:00
|
|
|
updateUI()
|
2017-11-25 20:33:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Notifications
|
|
|
|
|
|
|
|
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
|
|
|
|
|
|
|
configureAvailableCells()
|
|
|
|
}
|
2017-11-04 20:19:34 +01:00
|
|
|
}
|
|
|
|
|
2018-02-12 01:20:46 +01:00
|
|
|
// MARK: Actions
|
|
|
|
|
|
|
|
extension FeedListViewController {
|
|
|
|
|
2018-02-12 02:14:09 +01:00
|
|
|
@IBAction func openHomePage(_ sender: Any?) {
|
2018-02-12 01:20:46 +01:00
|
|
|
|
2018-02-12 02:14:09 +01:00
|
|
|
guard let homePageURL = singleSelectedHomePageURL() else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
Browser.open(homePageURL, inBackground: false)
|
2018-02-12 01:20:46 +01:00
|
|
|
}
|
|
|
|
|
2018-02-12 02:14:09 +01:00
|
|
|
@IBAction func addToFeeds(_ sender: Any?) {
|
2018-02-12 01:20:46 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-04 20:19:34 +01:00
|
|
|
// MARK: - NSOutlineViewDataSource
|
|
|
|
|
|
|
|
extension FeedListViewController: NSOutlineViewDataSource {
|
|
|
|
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
|
|
|
|
|
|
|
|
return nodeForItem(item as AnyObject?).numberOfChildNodes
|
|
|
|
}
|
|
|
|
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
|
|
|
|
|
|
|
|
return nodeForItem(item as AnyObject?).childNodes![index]
|
|
|
|
}
|
|
|
|
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
|
|
|
|
|
|
|
|
return nodeForItem(item as AnyObject?).canHaveChildNodes
|
|
|
|
}
|
|
|
|
|
|
|
|
private func nodeForItem(_ item: AnyObject?) -> Node {
|
|
|
|
|
|
|
|
if item == nil {
|
|
|
|
return treeController.rootNode
|
|
|
|
}
|
|
|
|
return item as! Node
|
|
|
|
}
|
2017-11-02 04:45:04 +01:00
|
|
|
}
|
2017-11-04 22:53:21 +01:00
|
|
|
|
|
|
|
// MARK: - NSOutlineViewDelegate
|
|
|
|
|
|
|
|
extension FeedListViewController: NSOutlineViewDelegate {
|
|
|
|
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
|
|
|
|
|
|
|
let cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "FeedListCell"), owner: self) as! SidebarCell
|
2017-12-29 01:37:30 +01:00
|
|
|
cell.translatesAutoresizingMaskIntoConstraints = false
|
2017-11-04 22:53:21 +01:00
|
|
|
let node = item as! Node
|
|
|
|
configure(cell, node)
|
|
|
|
|
|
|
|
return cell
|
|
|
|
}
|
|
|
|
|
|
|
|
func outlineViewSelectionDidChange(_ notification: Notification) {
|
|
|
|
|
2018-02-12 01:20:46 +01:00
|
|
|
updateUI()
|
|
|
|
|
2018-01-25 07:20:22 +01:00
|
|
|
let selectedRow = self.outlineView.selectedRow
|
|
|
|
|
|
|
|
if selectedRow < 0 || selectedRow == NSNotFound {
|
|
|
|
postSidebarSelectionDidChangeNotification(nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if let selectedNode = self.outlineView.item(atRow: selectedRow) as? Node {
|
|
|
|
postSidebarSelectionDidChangeNotification(selectedNode.representedObject)
|
|
|
|
}
|
2017-11-04 22:53:21 +01:00
|
|
|
}
|
2018-02-12 02:14:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private extension FeedListViewController {
|
2017-11-04 22:53:21 +01:00
|
|
|
|
2018-02-12 02:14:09 +01:00
|
|
|
func configure(_ cell: SidebarCell, _ node: Node) {
|
2017-11-04 22:53:21 +01:00
|
|
|
|
2017-11-25 20:33:07 +01:00
|
|
|
cell.cellAppearance = sidebarCellAppearance
|
2017-11-04 22:53:21 +01:00
|
|
|
cell.objectValue = node
|
|
|
|
cell.name = nameFor(node)
|
|
|
|
cell.image = imageFor(node)
|
2017-12-28 03:01:05 +01:00
|
|
|
cell.shouldShowImage = true
|
2017-11-04 22:53:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func imageFor(_ node: Node) -> NSImage? {
|
|
|
|
|
2017-12-28 03:01:05 +01:00
|
|
|
if let _ = node.representedObject as? FeedListFolder {
|
|
|
|
return NSImage(named: NSImage.Name.folder)
|
|
|
|
}
|
|
|
|
else if let feed = node.representedObject as? FeedListFeed {
|
2017-12-29 03:11:12 +01:00
|
|
|
if let image = appDelegate.faviconDownloader.favicon(withHomePageURL: feed.homePageURL) {
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
return appDelegate.genericFeedImage
|
2017-11-25 20:33:07 +01:00
|
|
|
}
|
2017-12-28 03:01:05 +01:00
|
|
|
return nil
|
2017-11-04 22:53:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func nameFor(_ node: Node) -> String {
|
|
|
|
|
|
|
|
if let displayNameProvider = node.representedObject as? DisplayNameProvider {
|
|
|
|
return displayNameProvider.nameForDisplay
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2017-11-25 20:33:07 +01:00
|
|
|
|
|
|
|
func nodeForRow(_ row: Int) -> Node? {
|
|
|
|
|
|
|
|
if row < 0 || row >= outlineView.numberOfRows {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if let node = outlineView.item(atRow: row) as? Node {
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func cellForRowView(_ rowView: NSTableRowView) -> SidebarCell? {
|
|
|
|
|
|
|
|
return rowView.view(atColumn: 0) as? SidebarCell
|
|
|
|
}
|
|
|
|
|
|
|
|
func configureAvailableCells() {
|
|
|
|
|
|
|
|
outlineView.enumerateAvailableRowViews { (rowView: NSTableRowView, row: Int) -> Void in
|
|
|
|
|
|
|
|
guard let cell = cellForRowView(rowView), let node = nodeForRow(row) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
configure(cell, node)
|
|
|
|
}
|
|
|
|
}
|
2018-01-25 07:20:22 +01:00
|
|
|
|
|
|
|
func postSidebarSelectionDidChangeNotification(_ selectedObject: Any?) {
|
|
|
|
|
|
|
|
var userInfo = [AnyHashable: Any]()
|
|
|
|
|
|
|
|
if let selectedObject = selectedObject {
|
|
|
|
userInfo[FeedListUserInfoKey.selectedObject] = selectedObject
|
|
|
|
}
|
|
|
|
|
|
|
|
NotificationCenter.default.post(name: .FeedListSidebarSelectionDidChange, object: self, userInfo: userInfo)
|
|
|
|
}
|
2018-02-12 01:20:46 +01:00
|
|
|
|
|
|
|
func updateUI() {
|
|
|
|
|
|
|
|
updateButtons()
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateButtons() {
|
|
|
|
|
|
|
|
let objects = selectedObjects
|
|
|
|
|
|
|
|
if objects.isEmpty {
|
|
|
|
openHomePageButton.isEnabled = false
|
|
|
|
addToFeedsButton.isEnabled = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
addToFeedsButton.isEnabled = true
|
|
|
|
|
|
|
|
if let _ = singleSelectedHomePageURL() {
|
|
|
|
openHomePageButton.isEnabled = true
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
openHomePageButton.isEnabled = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func singleSelectedHomePageURL() -> String? {
|
|
|
|
|
|
|
|
guard selectedObjects.count == 1, let homePageURL = (selectedObjects.first! as? FeedListFeed)?.homePageURL, !homePageURL.isEmpty else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return homePageURL
|
|
|
|
}
|
2017-11-04 22:53:21 +01:00
|
|
|
}
|