2017-05-27 19:43:27 +02:00
|
|
|
//
|
|
|
|
// MainWindowController.swift
|
|
|
|
// Evergreen
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 8/1/15.
|
|
|
|
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Cocoa
|
2017-09-17 21:22:15 +02:00
|
|
|
import Data
|
2017-10-07 23:40:14 +02:00
|
|
|
import Account
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
private let kWindowFrameKey = "MainWindow"
|
|
|
|
|
|
|
|
class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
|
|
|
|
|
|
|
// MARK: NSWindowController
|
|
|
|
|
2017-10-06 03:17:07 +02:00
|
|
|
private let windowAutosaveName = NSWindow.FrameAutosaveName(rawValue: kWindowFrameKey)
|
2017-10-19 06:53:45 +02:00
|
|
|
private var unreadCount: Int = 0 {
|
|
|
|
didSet {
|
|
|
|
if unreadCount != oldValue {
|
|
|
|
updateWindowTitle()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-06 03:17:07 +02:00
|
|
|
|
2017-12-21 23:47:12 +01:00
|
|
|
static var didPositionWindowOnFirstRun = false
|
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
override func windowDidLoad() {
|
|
|
|
|
|
|
|
super.windowDidLoad()
|
2017-11-18 21:13:28 +01:00
|
|
|
|
2017-12-02 23:54:12 +01:00
|
|
|
if !AppDefaults.shared.showTitleOnMainWindow {
|
|
|
|
window?.titleVisibility = .hidden
|
|
|
|
}
|
2017-12-21 23:43:29 +01:00
|
|
|
|
2017-10-06 03:17:07 +02:00
|
|
|
window?.setFrameUsingName(windowAutosaveName, force: true)
|
2017-12-21 23:47:12 +01:00
|
|
|
if AppDefaults.shared.isFirstRun && !MainWindowController.didPositionWindowOnFirstRun {
|
2017-12-21 23:43:29 +01:00
|
|
|
|
2017-12-24 19:28:34 +01:00
|
|
|
if let window = window {
|
|
|
|
let point = NSPoint(x: 128, y: 64)
|
|
|
|
let size = NSSize(width: 1000, height: 700)
|
|
|
|
let minSize = NSSize(width: 600, height: 600)
|
|
|
|
window.setPointAndSizeAdjustingForScreen(point: point, size: size, minimumSize: minSize)
|
2017-12-21 23:47:12 +01:00
|
|
|
MainWindowController.didPositionWindowOnFirstRun = true
|
2017-12-21 23:43:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
detailSplitViewItem?.minimumThickness = 384
|
|
|
|
|
2017-09-18 01:30:45 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillTerminate(_:)), name: NSApplication.willTerminateNotification, object: nil)
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(appNavigationKeyPressed(_:)), name: .AppNavigationKeyPressed, object: nil)
|
|
|
|
|
2017-10-07 23:40:14 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(refreshProgressDidChange(_:)), name: .AccountRefreshDidBegin, object: nil)
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(refreshProgressDidChange(_:)), name: .AccountRefreshDidFinish, object: nil)
|
2017-10-08 02:43:10 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(refreshProgressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
2017-10-19 06:53:45 +02:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
|
|
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
self.updateWindowTitle()
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
2017-10-19 06:53:45 +02:00
|
|
|
}
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
// MARK: Notifications
|
|
|
|
|
2017-09-17 21:54:08 +02:00
|
|
|
@objc func applicationWillTerminate(_ note: Notification) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
2017-10-06 03:17:07 +02:00
|
|
|
window?.saveFrame(usingName: windowAutosaveName)
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
|
2017-09-17 21:54:08 +02:00
|
|
|
@objc func appNavigationKeyPressed(_ note: Notification) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
2017-12-20 00:24:38 +01:00
|
|
|
guard let navigationKey = note.userInfo?[UserInfoKey.navigationKeyPressed] as? Int else {
|
2017-05-27 19:43:27 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
guard let contentView = window?.contentView, let view = note.object as? NSView, view.isDescendant(of: contentView) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-12-20 00:24:38 +01:00
|
|
|
if navigationKey == NSRightArrowFunctionKey {
|
|
|
|
handleRightArrowFunctionKey(in: view)
|
|
|
|
}
|
|
|
|
if navigationKey == NSLeftArrowFunctionKey {
|
|
|
|
handleLeftArrowFunctionKey(in: view)
|
|
|
|
}
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
|
2017-09-17 21:54:08 +02:00
|
|
|
@objc func refreshProgressDidChange(_ note: Notification) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
2017-11-19 20:42:22 +01:00
|
|
|
performSelectorCoalesced(#selector(MainWindowController.makeToolbarValidate(_:)), with: nil, delay: 0.1)
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
2017-10-19 06:53:45 +02:00
|
|
|
|
|
|
|
@objc func unreadCountDidChange(_ note: Notification) {
|
|
|
|
|
|
|
|
if note.object is AccountManager {
|
|
|
|
unreadCount = AccountManager.shared.unreadCount
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
// MARK: Toolbar
|
|
|
|
|
2017-10-06 05:03:35 +02:00
|
|
|
@objc func makeToolbarValidate(_ sender: Any) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
window?.toolbar?.validateVisibleItems()
|
|
|
|
}
|
2017-12-20 00:24:38 +01:00
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
// MARK: NSUserInterfaceValidations
|
|
|
|
|
|
|
|
public func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
|
|
|
|
|
|
|
if item.action == #selector(openArticleInBrowser(_:)) {
|
|
|
|
return currentLink != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if item.action == #selector(nextUnread(_:)) {
|
|
|
|
return canGoToNextUnread()
|
|
|
|
}
|
|
|
|
|
|
|
|
if item.action == #selector(markAllAsRead(_:)) {
|
|
|
|
return canMarkAllAsRead()
|
|
|
|
}
|
2017-11-06 06:27:34 +01:00
|
|
|
|
|
|
|
if item.action == #selector(markRead(_:)) {
|
|
|
|
return canMarkRead()
|
|
|
|
}
|
|
|
|
|
2017-12-25 21:21:44 +01:00
|
|
|
if item.action == #selector(markOlderArticlesAsRead(_:)) {
|
|
|
|
return canMarkOlderArticlesAsRead()
|
|
|
|
}
|
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
// MARK: - Actions
|
|
|
|
|
|
|
|
@IBAction func scrollOrGoToNextUnread(_ sender: Any?) {
|
|
|
|
|
2017-12-20 22:39:31 +01:00
|
|
|
guard let detailViewController = detailViewController else {
|
|
|
|
return
|
|
|
|
}
|
2017-12-20 21:59:31 +01:00
|
|
|
|
2017-12-20 22:39:31 +01:00
|
|
|
detailViewController.canScrollDown { (canScroll) in
|
|
|
|
|
|
|
|
canScroll ? detailViewController.scrollPageDown(sender) : self.nextUnread(sender)
|
|
|
|
}
|
2017-12-20 21:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@IBAction func showAddFolderWindow(_ sender: Any) {
|
2017-11-15 22:13:40 +01:00
|
|
|
|
|
|
|
appDelegate.showAddFolderSheetOnWindow(window!)
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func openArticleInBrowser(_ sender: Any?) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
if let link = currentLink {
|
2017-10-06 03:12:58 +02:00
|
|
|
Browser.open(link)
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
}
|
2017-12-20 21:59:31 +01:00
|
|
|
|
|
|
|
@IBAction func openInBrowser(_ sender: Any?) {
|
|
|
|
|
|
|
|
openArticleInBrowser(sender)
|
|
|
|
}
|
|
|
|
|
2017-12-24 07:18:42 +01:00
|
|
|
func makeTimelineViewFirstResponder() {
|
|
|
|
|
|
|
|
guard let window = window, let timelineViewController = timelineViewController else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
window.makeFirstResponderUnlessDescendantIsFirstResponder(timelineViewController.tableView)
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func nextUnread(_ sender: Any?) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
guard let timelineViewController = timelineViewController, let sidebarViewController = sidebarViewController else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if timelineViewController.canGoToNextUnread() {
|
2017-12-24 07:18:42 +01:00
|
|
|
goToNextUnreadInTimeline()
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
else if sidebarViewController.canGoToNextUnread() {
|
|
|
|
sidebarViewController.goToNextUnread()
|
2017-12-24 07:18:42 +01:00
|
|
|
if timelineViewController.canGoToNextUnread() {
|
|
|
|
goToNextUnreadInTimeline()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func goToNextUnreadInTimeline() {
|
|
|
|
|
|
|
|
guard let timelineViewController = timelineViewController else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if timelineViewController.canGoToNextUnread() {
|
|
|
|
timelineViewController.goToNextUnread()
|
2017-05-27 19:43:27 +02:00
|
|
|
makeTimelineViewFirstResponder()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func markAllAsRead(_ sender: Any?) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
timelineViewController?.markAllAsRead()
|
|
|
|
}
|
2017-11-06 06:27:34 +01:00
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func markRead(_ sender: Any?) {
|
2017-11-06 06:27:34 +01:00
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
timelineViewController?.markSelectedArticlesAsRead(sender)
|
2017-11-06 06:27:34 +01:00
|
|
|
}
|
2017-12-20 21:59:31 +01:00
|
|
|
|
|
|
|
@IBAction func markUnread(_ sender: Any?) {
|
|
|
|
|
|
|
|
timelineViewController?.markSelectedArticlesAsUnread(sender)
|
|
|
|
}
|
|
|
|
|
2017-12-21 06:23:48 +01:00
|
|
|
@IBAction func markAllAsReadAndGoToNextUnread(_ sender: Any?) {
|
|
|
|
|
|
|
|
markAllAsRead(sender)
|
|
|
|
nextUnread(sender)
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func markUnreadAndGoToNextUnread(_ sender: Any?) {
|
|
|
|
|
|
|
|
markUnread(sender)
|
|
|
|
nextUnread(sender)
|
|
|
|
}
|
|
|
|
|
2017-12-21 05:51:17 +01:00
|
|
|
@IBAction func markReadAndGoToNextUnread(_ sender: Any?) {
|
|
|
|
|
|
|
|
markUnread(sender)
|
|
|
|
nextUnread(sender)
|
|
|
|
}
|
|
|
|
|
2017-12-20 21:59:31 +01:00
|
|
|
@IBAction func toggleSidebar(_ sender: Any?) {
|
2017-05-27 19:43:27 +02:00
|
|
|
|
|
|
|
splitViewController!.toggleSidebar(sender)
|
|
|
|
}
|
2017-11-17 03:23:07 +01:00
|
|
|
|
2017-11-20 00:45:20 +01:00
|
|
|
@IBAction func markOlderArticlesAsRead(_ sender: Any?) {
|
2017-11-17 03:23:07 +01:00
|
|
|
|
2017-12-25 21:21:44 +01:00
|
|
|
timelineViewController?.markOlderArticlesAsRead()
|
2017-11-20 01:28:26 +01:00
|
|
|
}
|
2017-12-21 06:23:48 +01:00
|
|
|
|
|
|
|
@IBAction func navigateToTimeline(_ sender: Any?) {
|
|
|
|
|
|
|
|
timelineViewController?.focus()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func navigateToSidebar(_ sender: Any?) {
|
|
|
|
|
|
|
|
sidebarViewController?.focus()
|
|
|
|
}
|
2017-12-21 23:43:29 +01:00
|
|
|
|
|
|
|
@IBAction func goToPreviousSubscription(_ sender: Any?) {
|
|
|
|
|
|
|
|
sidebarViewController?.outlineView.selectPreviousRow(sender)
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func goToNextSubscription(_ sender: Any?) {
|
|
|
|
|
|
|
|
sidebarViewController?.outlineView.selectNextRow(sender)
|
|
|
|
}
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
|
2017-10-06 05:03:35 +02:00
|
|
|
// MARK: - Private
|
|
|
|
|
2017-05-27 19:43:27 +02:00
|
|
|
private extension MainWindowController {
|
|
|
|
|
|
|
|
var splitViewController: NSSplitViewController? {
|
|
|
|
get {
|
|
|
|
guard let viewController = contentViewController else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return viewController.childViewControllers.first as? NSSplitViewController
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var sidebarViewController: SidebarViewController? {
|
|
|
|
get {
|
|
|
|
return splitViewController?.splitViewItems[0].viewController as? SidebarViewController
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var timelineViewController: TimelineViewController? {
|
|
|
|
get {
|
|
|
|
return splitViewController?.splitViewItems[1].viewController as? TimelineViewController
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var detailSplitViewItem: NSSplitViewItem? {
|
2017-10-06 05:03:35 +02:00
|
|
|
get {
|
|
|
|
return splitViewController?.splitViewItems[2]
|
|
|
|
}
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var detailViewController: DetailViewController? {
|
|
|
|
get {
|
|
|
|
return splitViewController?.splitViewItems[2].viewController as? DetailViewController
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var selectedArticles: [Article]? {
|
|
|
|
get {
|
|
|
|
return timelineViewController?.selectedArticles
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var oneSelectedArticle: Article? {
|
|
|
|
get {
|
|
|
|
if let articles = selectedArticles {
|
|
|
|
return articles.count == 1 ? articles[0] : nil
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var currentLink: String? {
|
|
|
|
get {
|
2017-10-06 05:03:35 +02:00
|
|
|
return oneSelectedArticle?.preferredLink
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func canGoToNextUnread() -> Bool {
|
|
|
|
|
|
|
|
guard let timelineViewController = timelineViewController, let sidebarViewController = sidebarViewController else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return timelineViewController.canGoToNextUnread() || sidebarViewController.canGoToNextUnread()
|
|
|
|
}
|
|
|
|
|
|
|
|
func canMarkAllAsRead() -> Bool {
|
|
|
|
|
|
|
|
return timelineViewController?.canMarkAllAsRead() ?? false
|
|
|
|
}
|
2017-10-19 06:53:45 +02:00
|
|
|
|
2017-11-06 06:27:34 +01:00
|
|
|
func canMarkRead() -> Bool {
|
|
|
|
|
|
|
|
return timelineViewController?.canMarkSelectedArticlesAsRead() ?? false
|
|
|
|
}
|
2017-12-25 21:21:44 +01:00
|
|
|
|
|
|
|
func canMarkOlderArticlesAsRead() -> Bool {
|
|
|
|
|
|
|
|
return timelineViewController?.canMarkOlderArticlesAsRead() ?? false
|
|
|
|
}
|
|
|
|
|
2017-10-19 06:53:45 +02:00
|
|
|
func updateWindowTitle() {
|
|
|
|
|
|
|
|
if unreadCount < 1 {
|
2017-11-25 07:05:10 +01:00
|
|
|
window?.title = appDelegate.appName!
|
2017-10-19 06:53:45 +02:00
|
|
|
}
|
|
|
|
else if unreadCount > 0 {
|
2017-11-25 07:05:10 +01:00
|
|
|
window?.title = "\(appDelegate.appName!) (\(unreadCount))"
|
2017-10-19 06:53:45 +02:00
|
|
|
}
|
|
|
|
}
|
2017-12-20 00:24:38 +01:00
|
|
|
|
|
|
|
// MARK: - Navigation
|
|
|
|
|
|
|
|
func handleRightArrowFunctionKey(in view: NSView) {
|
|
|
|
|
|
|
|
guard let outlineView = sidebarViewController?.outlineView, view === outlineView, let timelineViewController = timelineViewController else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
timelineViewController.focus()
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleLeftArrowFunctionKey(in view: NSView) {
|
|
|
|
|
|
|
|
guard let timelineView = timelineViewController?.tableView, view === timelineView, let sidebarViewController = sidebarViewController else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sidebarViewController.focus()
|
|
|
|
}
|
2017-05-27 19:43:27 +02:00
|
|
|
}
|
|
|
|
|