Files
i2p.i2p/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift
meeh e36a3b318a Mac OSX Launcher:
* Fixed startup option so the launcher can start at OSX login/bootup.
* Added I2P Browser to the list of "firefox" browsers to detect.
* Changed hardcoded path lookup to native "registry" lookup for firefox application.
* Made the advanced preferences table editable by the user.
* Cleanup of old and/or unused code.
* Bugfixes.
2019-03-10 11:16:56 +00:00

306 lines
9.6 KiB
Swift

//
// PreferencesViewController.swift
// I2PLauncher
//
// Created by Mikal Villa on 07/11/2018.
// Copyright © 2018 The I2P Project. All rights reserved.
//
// Table view programming guide from Apple:
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TableView/Introduction/Introduction.html
//
import Cocoa
import ServiceManagement
class PreferencesViewController: NSViewController {
enum ShowAsMode {
case bothIcon
case menubarIcon
case dockIcon
}
var changeDockMenubarIconTimer: Timer?
// MARK: - Advanced settings objects
@IBOutlet weak var advPrefTableView: NSTableView!
// MARK: - Launcher settings objects
@IBOutlet var radioDockIcon: NSButton?
@IBOutlet var radioMenubarIcon: NSButton?
@IBOutlet var radioBothIcon: NSButton?
@IBOutlet var checkboxStartWithOSX: NSButton?
@IBOutlet var checkboxStartFirefoxAlso: NSButton?
// MARK: - Router objects
@IBOutlet var checkboxStartWithLauncher: NSButton?
@IBOutlet var checkboxStopWithLauncher: NSButton?
@IBOutlet var buttonResetRouterConfig: NSButton?
@IBAction func onEnterInTextField(_ sender: NSTextField) {
let selectedRowNumber = advPrefTableView.selectedRow
print("Trying to store preferences")
let currentItem = Preferences.shared()[selectedRowNumber]
currentItem?.selectedValue = sender.stringValue
Preferences.shared()[selectedRowNumber] = currentItem
UserDefaults.standard.set(sender.stringValue, forKey: (currentItem?.name)!)
Preferences.shared().syncPref()
}
override func viewDidLoad() {
super.viewDidLoad()
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height)
if (advPrefTableView != nil) {
// For data feeding and view
advPrefTableView.delegate = self
advPrefTableView.dataSource = self
// Responding to Double-Click
advPrefTableView.target = self
advPrefTableView.doubleAction = #selector(tableViewDoubleClick(_:))
// Always redraw preference items which might have changed state since last draw.
Preferences.shared().redrawPrefTableItems()
// For sorting
advPrefTableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: "name", ascending: true)
advPrefTableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: "defaultValue", ascending: true)
advPrefTableView.tableColumns[2].sortDescriptorPrototype = NSSortDescriptor(key: "selectedValue", ascending: true)
self.advPrefTableView.isEnabled = Preferences.shared().allowAdvancedPreferenceEdit
}
// Update radio buttons to reflect runtime/stored preferences
self.updateRadioButtonEffect(mode: Preferences.shared().showAsIconMode, withSideEffect: false)
if (Preferences.shared().stopRouterOnLauncherShutdown) {
self.checkboxStopWithLauncher?.state = NSOnState;
} else {
self.checkboxStopWithLauncher?.state = NSOffState;
}
if (Preferences.shared().startRouterOnLauncherStart) {
self.checkboxStartWithLauncher?.state = NSOnState;
} else {
self.checkboxStartWithLauncher?.state = NSOffState;
}
}
override func viewDidAppear() {
super.viewDidAppear()
// Update window title
self.parent?.view.window?.title = self.title!
}
// MARK: - Router settings functions
@IBAction func checkboxStartRouterWithLauncherClicked(_ sender: NSButton) {
switch sender.state {
case NSOnState:
print("on")
Preferences.shared().startRouterOnLauncherStart = true
case NSOffState:
print("off")
Preferences.shared().startRouterOnLauncherStart = false
case NSMixedState:
print("mixed")
default: break
}
}
@IBAction func checkboxStopRouterWithLauncherClicked(_ sender: NSButton) {
switch sender.state {
case NSOnState:
print("on")
Preferences.shared().stopRouterOnLauncherShutdown = true
case NSOffState:
print("off")
Preferences.shared().stopRouterOnLauncherShutdown = false
case NSMixedState:
print("mixed")
default: break
}
}
@IBAction func buttonResetRouterConfigClicked(_ sender: Any) {
// TODO: Add a modal dialog asking user if they are **really** sure
}
// MARK: - Launcher settings functions
@IBAction func checkboxStartLauncherOnOSXStartupClicked(_ sender: NSButton) {
let launcherAppId = "net.i2p.bootstrap.macosx.StartupItemApp"
let startupMgr = Startup()
switch sender.state {
case NSOnState:
print("on")
Preferences.shared()["I2Pref_startLauncherAtLogin"] = true
if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
{
let success = SMLoginItemSetEnabled(launcherAppId as CFString, true)
print("SMLoginItemSetEnabled returned \(success)....")
} else {
startupMgr.addLoginItem(Startup.appPath())
print("Shared file for auto-startup added. (viewable via OSX Preferences -> Users -> Login Items)")
}
case NSOffState:
print("off")
Preferences.shared()["I2Pref_startLauncherAtLogin"] = false
if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
{
let success = SMLoginItemSetEnabled(launcherAppId as CFString, false)
print("SMLoginItemSetEnabled returned \(success)....")
} else {
startupMgr.removeLoginItem(Startup.appPath())
print("Shared file for auto-startup removed (if any). (viewable via OSX Preferences -> Users -> Login Items)")
}
case NSMixedState:
print("mixed")
default: break
}
}
@IBAction func checkboxStartFirefoxAlsoAtLaunchClicked(_ sender: NSButton) {
switch sender.state {
case NSOnState:
print("launch firefox: on")
Preferences.shared().alsoStartFirefoxOnLaunch = true
case NSOffState:
print("launch firefox: off")
Preferences.shared().alsoStartFirefoxOnLaunch = false
case NSMixedState:
print("mixed")
default: break
}
}
// MARK: - Radio buttons functions
func updateDockMenubarIcons(_ mode: ShowAsMode) -> Bool {
// Update preferences with latest choise
Preferences.shared().showAsIconMode = mode
// Update runtime
switch mode {
case .bothIcon, .dockIcon:
// Show dock icon
print("Preferences: Update Dock Icon -> Show")
if (!getDockIconStateIsShowing()) {
return triggerDockIconShowHide(showIcon: true)
}
case .menubarIcon:
// Hide dock icon
print("Preferences: Update Dock Icon -> Hide")
if (getDockIconStateIsShowing()) {
return triggerDockIconShowHide(showIcon: false)
}
}
// Note: In reality, this won't ever happen.
// The switch statement above would return before this.
return false
}
func updateRadioButtonEffect(mode: ShowAsMode, withSideEffect: Bool = true) {
changeDockMenubarIconTimer?.invalidate()
radioDockIcon?.state = NSOffState
radioMenubarIcon?.state = NSOffState
radioBothIcon?.state = NSOffState
switch mode {
case .bothIcon:
radioBothIcon?.state = NSOnState
case .dockIcon:
radioDockIcon?.state = NSOnState
case .menubarIcon:
radioMenubarIcon?.state = NSOnState
}
if (withSideEffect) {
if #available(OSX 10.12, *) {
changeDockMenubarIconTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in
// If we're on 10.12 or later
self.updateDockMenubarIcons(mode)
})
} else {
// Fallback on earlier versions
self.updateDockMenubarIcons(mode)
}
}
}
@IBAction func radioBothIconSelected(_ sender: Any) {
updateRadioButtonEffect(mode: ShowAsMode.bothIcon)
}
@IBAction func radioDockIconOnlySelected(_ sender: Any) {
updateRadioButtonEffect(mode: ShowAsMode.dockIcon)
}
@IBAction func radioMenubarOnlySelected(_ sender: Any) {
updateRadioButtonEffect(mode: ShowAsMode.menubarIcon)
}
// MARK: - Triggers
func triggerDockIconHideShow(showIcon state: Bool) -> Bool {
// Get transform state.
var transformState: ProcessApplicationTransformState
if state {
transformState = ProcessApplicationTransformState(kProcessTransformToForegroundApplication)
} else {
transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication)
}
// Show / hide dock icon.
var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))
let transformStatus: OSStatus = TransformProcessType(&psn, transformState)
return transformStatus == 0
}
func triggerDockIconShowHide(showIcon state: Bool) -> Bool {
var result: Bool
if state {
result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular)
} else {
result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.accessory)
}
return result
}
func getDockIconStateIsShowing() -> Bool {
if NSApp.activationPolicy() == NSApplicationActivationPolicy.regular {
return true
} else {
return false
}
}
// MARK: - Advanced
@IBAction func checkboxEnableAdvancedPreferencesClicked(_ sender: NSButton) {
switch sender.state {
case NSOnState:
print("on")
Preferences.shared().allowAdvancedPreferenceEdit = true
self.advPrefTableView.isEnabled = true
case NSOffState:
print("off")
Preferences.shared().allowAdvancedPreferenceEdit = false
self.advPrefTableView.isEnabled = false
case NSMixedState:
print("mixed")
default: break
}
}
// End of Class
}