|
|
|
//
|
|
|
|
// AppDelegate.swift
|
|
|
|
// LunaMac
|
|
|
|
//
|
|
|
|
// Created by Claire Davis on 9/21/22.
|
|
|
|
// Copyright © 2022 A Better Geek. All rights reserved.
|
|
|
|
//
|
|
|
|
/*
|
|
|
|
====== TO DO =======
|
|
|
|
* auto-update at a set interval
|
|
|
|
* make help menu item work (show the window)
|
|
|
|
* fix alignment of status bar icon (it's too high)
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import Foundation
|
|
|
|
import CoreFoundation
|
|
|
|
|
|
|
|
@NSApplicationMain
|
|
|
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
|
|
|
|
|
// static array of possible lunar values
|
|
|
|
let phaseArray:[String:[String:NSString]] = [
|
|
|
|
"new" : [
|
|
|
|
"nice" : "New Moon",
|
|
|
|
"start" : "0",
|
|
|
|
"end" : "1",
|
|
|
|
"icon" : "🌑"
|
|
|
|
],
|
|
|
|
"waxc" : [
|
|
|
|
"nice" : "Waxing Crescent",
|
|
|
|
"start" : "1",
|
|
|
|
"end" : "6.38264692644",
|
|
|
|
"icon" : "🌒"
|
|
|
|
],
|
|
|
|
"firstq" : [
|
|
|
|
"nice" : "First Quarter",
|
|
|
|
"start" : "6.38264692644",
|
|
|
|
"end" : "8.38264692644",
|
|
|
|
"icon" : "🌓"
|
|
|
|
],
|
|
|
|
"waxg" : [
|
|
|
|
"nice" : "Waxing Gibbous",
|
|
|
|
"start" : "8.38264692644",
|
|
|
|
"end" : "13.76529385288",
|
|
|
|
"icon" : "🌔"
|
|
|
|
],
|
|
|
|
"full" : [
|
|
|
|
"nice" : "Full Moon",
|
|
|
|
"start" : "13.76529385288",
|
|
|
|
"end" : "15.76529385288",
|
|
|
|
"icon" : "🌕"
|
|
|
|
],
|
|
|
|
"wang" : [
|
|
|
|
"nice" : "Waning Gibbous",
|
|
|
|
"start" : "15.76529385288",
|
|
|
|
"end" : "21.14794077932",
|
|
|
|
"icon" : "🌖"
|
|
|
|
],
|
|
|
|
"lastq" : [
|
|
|
|
"nice" : "Last Quarter",
|
|
|
|
"start" : "21.14794077932",
|
|
|
|
"end" : "23.14794077932",
|
|
|
|
"icon" : "🌗"
|
|
|
|
],
|
|
|
|
"wanc" : [
|
|
|
|
"nice" : "Waning Crescent",
|
|
|
|
"start" : "23.14794077932",
|
|
|
|
"end" : "28.53058770576",
|
|
|
|
"icon" : "🌘"
|
|
|
|
],
|
|
|
|
"new2" : [
|
|
|
|
"nice" : "New Moon",
|
|
|
|
"start" : "28.53058770576",
|
|
|
|
"end" : "29.53058770576",
|
|
|
|
"icon" : "🌑"
|
|
|
|
],
|
|
|
|
"default" : [
|
|
|
|
"nice" : "Failed to calculate.",
|
|
|
|
"icon" : "🌙"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
@IBAction func clickClick(_ sender: NSButtonCell) {
|
|
|
|
// show changelog
|
|
|
|
showLog()
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure applet stays in memory
|
|
|
|
var statusBarItem: NSStatusItem?
|
|
|
|
|
|
|
|
// create empty timer for event loop
|
|
|
|
var lunaTimer = Timer()
|
|
|
|
|
|
|
|
// is the info box open?
|
|
|
|
var infoOpen = false
|
|
|
|
|
|
|
|
// is the changelog open?
|
|
|
|
var logOpen = false
|
|
|
|
|
|
|
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
|
|
|
// build the icon and menu
|
|
|
|
updateIcon()
|
|
|
|
|
|
|
|
// listen for theme changes (dark and light)
|
|
|
|
themeListener()
|
|
|
|
|
|
|
|
// start the the timer for keeping the icon updated automatically
|
|
|
|
startTimer()
|
|
|
|
}
|
|
|
|
|
|
|
|
// start the timer for auto-updating the icon
|
|
|
|
func startTimer() {
|
|
|
|
lunaTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(updateIcon), userInfo: nil, repeats: true)
|
|
|
|
RunLoop.current.add(lunaTimer, forMode: RunLoop.Mode.common)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func buildMenu(key: String = "default") {
|
|
|
|
// get system-wide menu bar
|
|
|
|
let statusBar = NSStatusBar.system
|
|
|
|
|
|
|
|
// set icon size to square
|
|
|
|
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
|
|
|
|
|
|
|
|
// set icon using emoji
|
|
|
|
//statusBarItem?.button?.title = phaseArray[key]?["icon"] as String? ?? "🌙"
|
|
|
|
|
|
|
|
var suffix = ""
|
|
|
|
|
|
|
|
// check for dark mode and set the right image file
|
|
|
|
if #available(OSX 10.14, *) {
|
|
|
|
let darkMode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
|
|
|
|
|
|
|
|
if darkMode == "Dark" {
|
|
|
|
suffix += "-dark"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set image filename
|
|
|
|
let imgName = key + suffix + ".png"
|
|
|
|
|
|
|
|
statusBarItem?.button?.image = NSImage(named: imgName)
|
|
|
|
|
|
|
|
// create a menu for the icon
|
|
|
|
let statusBarMenu = NSMenu(title: "LunaMac Menu")
|
|
|
|
|
|
|
|
// add the menu to the menu bar icon
|
|
|
|
statusBarItem?.menu = statusBarMenu
|
|
|
|
|
|
|
|
let topTxt = phaseArray["default"]?["nice"] as String? ?? ""
|
|
|
|
|
|
|
|
// add menu items to the menu
|
|
|
|
if (!topTxt.isEmpty) {
|
|
|
|
statusBarMenu.addItem(
|
|
|
|
withTitle: phaseArray[key]?["nice"] as String? ?? topTxt,
|
|
|
|
action: nil,
|
|
|
|
keyEquivalent: ""
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
statusBarMenu.addItem(NSMenuItem.separator())
|
|
|
|
|
|
|
|
statusBarMenu.addItem(
|
|
|
|
withTitle: "Update moon phase",
|
|
|
|
action: #selector(AppDelegate.updateIcon),
|
|
|
|
keyEquivalent: ""
|
|
|
|
)
|
|
|
|
|
|
|
|
statusBarMenu.addItem(NSMenuItem.separator())
|
|
|
|
|
|
|
|
statusBarMenu.addItem(
|
|
|
|
withTitle: "About LunaMac...",
|
|
|
|
action: #selector(AppDelegate.showAbout),
|
|
|
|
keyEquivalent: ""
|
|
|
|
)
|
|
|
|
|
|
|
|
statusBarMenu.addItem(
|
|
|
|
withTitle: "Quit LunaMac",
|
|
|
|
action: #selector(AppDelegate.quitApp),
|
|
|
|
keyEquivalent: ""
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func showAbout() {
|
|
|
|
if (!infoOpen) {
|
|
|
|
// create infobox window object
|
|
|
|
let aboutWin = NSWindowController(windowNibName: "InfoBox")
|
|
|
|
|
|
|
|
aboutWin.loadWindow()
|
|
|
|
}
|
|
|
|
NSApp.activate(ignoringOtherApps: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func showLog() {
|
|
|
|
if (!logOpen) {
|
|
|
|
// create changelog window object
|
|
|
|
// this creates duplicate windows
|
|
|
|
let logWin = NSWindowController(windowNibName: "Changelog")
|
|
|
|
|
|
|
|
logWin.loadWindow()
|
|
|
|
}
|
|
|
|
NSApp.activate(ignoringOtherApps: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// update menu bar icon
|
|
|
|
@objc func updateIcon() {
|
|
|
|
// current system time in UTC
|
|
|
|
let sysDate = Date()
|
|
|
|
// get baseline date
|
|
|
|
// let baseDateStr = "2000-01-06 18:14:00 +0000"
|
|
|
|
|
|
|
|
// create date object from baseDate
|
|
|
|
// date components
|
|
|
|
var bdc = DateComponents()
|
|
|
|
bdc.year = 2000
|
|
|
|
bdc.month = 1
|
|
|
|
bdc.day = 6
|
|
|
|
bdc.hour = 18
|
|
|
|
bdc.minute = 14
|
|
|
|
// create date object
|
|
|
|
let ucal = Calendar(identifier: .gregorian)
|
|
|
|
let baseDate = ucal.date(from: bdc)
|
|
|
|
|
|
|
|
// days in a lunar cycle
|
|
|
|
let lunarDays = 29.53058770576
|
|
|
|
// seconds in a lunar cycle
|
|
|
|
let lunarSecs = lunarDays * (24 * 60 * 60)
|
|
|
|
|
|
|
|
// calculate seconds between sysDate and baseDate
|
|
|
|
// if baseDate is nil, default to current system time
|
|
|
|
let totalSecs = sysDate.timeIntervalSince(baseDate ?? Date())
|
|
|
|
|
|
|
|
if (totalSecs.sign != .minus) {
|
|
|
|
// a positive number is valid
|
|
|
|
// % calculates seconds of current cycle
|
|
|
|
let currSecs = totalSecs.truncatingRemainder(dividingBy: lunarSecs)
|
|
|
|
let currFrac = currSecs / lunarSecs
|
|
|
|
let currDays = currFrac * lunarDays
|
|
|
|
|
|
|
|
var newArr = [String:[String:NSString]]()
|
|
|
|
|
|
|
|
newArr = phaseArray.filter{findPhase(key: $0.key, val: $0.value, start: $0.value["start"]?.doubleValue ?? 0, end: $0.value["end"]?.doubleValue ?? 0, curr: currDays)}
|
|
|
|
|
|
|
|
// we want newArr[0].value which is [String:NSString]
|
|
|
|
let theKey = newArr.first!.key
|
|
|
|
buildMenu(key: theKey)
|
|
|
|
} else {
|
|
|
|
// if dateDiff is negative, something went wrong.
|
|
|
|
buildMenu()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc func findPhase(key: String, val: [String:NSString], start: Double, end: Double, curr: Double) -> Bool {
|
|
|
|
|
|
|
|
if ((curr >= start) && (curr <= end)) {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// quit application
|
|
|
|
@objc func quitApp() {
|
|
|
|
NSApp.terminate(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
func applicationWillTerminate(_ aNotification: Notification) {
|
|
|
|
// Insert code here to tear down your application
|
|
|
|
}
|
|
|
|
|
|
|
|
func themeListener() {
|
|
|
|
DistributedNotificationCenter.default.addObserver(
|
|
|
|
self,
|
|
|
|
selector: #selector(updateIcon),
|
|
|
|
name: .AppleInterfaceThemeChangedNotification,
|
|
|
|
object: nil
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension Notification.Name {
|
|
|
|
static let AppleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
|
|
|
|
}
|