source: launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift @ e36a3b3

Last change on this file since e36a3b3 was e36a3b3, checked in by meeh <meeh@…>, 15 months ago

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.
  • Property mode set to 100644
File size: 9.6 KB
Line 
1//
2//  PreferencesViewController.swift
3//  I2PLauncher
4//
5//  Created by Mikal Villa on 07/11/2018.
6//  Copyright © 2018 The I2P Project. All rights reserved.
7//
8// Table view programming guide from Apple:
9// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TableView/Introduction/Introduction.html
10//
11
12import Cocoa
13import ServiceManagement
14
15
16class PreferencesViewController: NSViewController {
17 
18  enum ShowAsMode {
19    case bothIcon
20    case menubarIcon
21    case dockIcon
22  }
23 
24  var changeDockMenubarIconTimer: Timer?
25 
26  // MARK: - Advanced settings objects
27  @IBOutlet weak var advPrefTableView: NSTableView!
28 
29  // MARK: - Launcher settings objects
30  @IBOutlet var radioDockIcon: NSButton?
31  @IBOutlet var radioMenubarIcon: NSButton?
32  @IBOutlet var radioBothIcon: NSButton?
33  @IBOutlet var checkboxStartWithOSX: NSButton?
34  @IBOutlet var checkboxStartFirefoxAlso: NSButton?
35 
36  // MARK: - Router objects
37  @IBOutlet var checkboxStartWithLauncher: NSButton?
38  @IBOutlet var checkboxStopWithLauncher: NSButton?
39  @IBOutlet var buttonResetRouterConfig: NSButton?
40 
41  @IBAction func onEnterInTextField(_ sender: NSTextField) {
42    let selectedRowNumber = advPrefTableView.selectedRow
43    print("Trying to store preferences")
44    let currentItem = Preferences.shared()[selectedRowNumber]
45    currentItem?.selectedValue = sender.stringValue
46    Preferences.shared()[selectedRowNumber] = currentItem
47    UserDefaults.standard.set(sender.stringValue, forKey: (currentItem?.name)!)
48    Preferences.shared().syncPref()
49  }
50 
51  override func viewDidLoad() {
52    super.viewDidLoad()
53   
54    self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height)
55   
56    if (advPrefTableView != nil) {
57      // For data feeding and view
58      advPrefTableView.delegate = self
59      advPrefTableView.dataSource = self
60     
61      // Responding to Double-Click
62      advPrefTableView.target = self
63      advPrefTableView.doubleAction = #selector(tableViewDoubleClick(_:))
64     
65      // Always redraw preference items which might have changed state since last draw.
66      Preferences.shared().redrawPrefTableItems()
67     
68      // For sorting
69      advPrefTableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: "name", ascending: true)
70      advPrefTableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: "defaultValue", ascending: true)
71      advPrefTableView.tableColumns[2].sortDescriptorPrototype = NSSortDescriptor(key: "selectedValue", ascending: true)
72     
73      self.advPrefTableView.isEnabled = Preferences.shared().allowAdvancedPreferenceEdit
74    }
75   
76    // Update radio buttons to reflect runtime/stored preferences
77    self.updateRadioButtonEffect(mode: Preferences.shared().showAsIconMode, withSideEffect: false)
78   
79    if (Preferences.shared().stopRouterOnLauncherShutdown) {
80      self.checkboxStopWithLauncher?.state = NSOnState;
81    } else {
82      self.checkboxStopWithLauncher?.state = NSOffState;
83    }
84    if (Preferences.shared().startRouterOnLauncherStart) {
85      self.checkboxStartWithLauncher?.state = NSOnState;
86    } else {
87      self.checkboxStartWithLauncher?.state = NSOffState;
88    }
89   
90   
91  }
92 
93  override func viewDidAppear() {
94    super.viewDidAppear()
95   
96    // Update window title
97    self.parent?.view.window?.title = self.title!
98  }
99 
100  // MARK: - Router settings functions
101 
102  @IBAction func checkboxStartRouterWithLauncherClicked(_ sender: NSButton) {
103    switch sender.state {
104    case NSOnState:
105      print("on")
106      Preferences.shared().startRouterOnLauncherStart = true
107    case NSOffState:
108      print("off")
109      Preferences.shared().startRouterOnLauncherStart = false
110    case NSMixedState:
111      print("mixed")
112    default: break
113    }
114  }
115 
116  @IBAction func checkboxStopRouterWithLauncherClicked(_ sender: NSButton) {
117    switch sender.state {
118    case NSOnState:
119      print("on")
120      Preferences.shared().stopRouterOnLauncherShutdown = true
121    case NSOffState:
122      print("off")
123      Preferences.shared().stopRouterOnLauncherShutdown = false
124    case NSMixedState:
125      print("mixed")
126    default: break
127    }
128  }
129 
130  @IBAction func buttonResetRouterConfigClicked(_ sender: Any) {
131    // TODO: Add a modal dialog asking user if they are **really** sure
132  }
133 
134  // MARK: - Launcher settings functions
135 
136  @IBAction func checkboxStartLauncherOnOSXStartupClicked(_ sender: NSButton) {
137    let launcherAppId = "net.i2p.bootstrap.macosx.StartupItemApp"
138    let startupMgr = Startup()
139    switch sender.state {
140    case NSOnState:
141      print("on")
142      Preferences.shared()["I2Pref_startLauncherAtLogin"] = true
143      if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
144      {
145        let success = SMLoginItemSetEnabled(launcherAppId as CFString, true)
146        print("SMLoginItemSetEnabled returned \(success)....")
147      } else {
148        startupMgr.addLoginItem(Startup.appPath())
149        print("Shared file for auto-startup added. (viewable via OSX Preferences -> Users -> Login Items)")
150      }
151    case NSOffState:
152      print("off")
153      Preferences.shared()["I2Pref_startLauncherAtLogin"] = false
154      if (Preferences.shared()["I2Pref_useServiceManagementAsStartupTool"] as! Bool)
155      {
156        let success = SMLoginItemSetEnabled(launcherAppId as CFString, false)
157        print("SMLoginItemSetEnabled returned \(success)....")
158      } else {
159        startupMgr.removeLoginItem(Startup.appPath())
160        print("Shared file for auto-startup removed (if any). (viewable via OSX Preferences -> Users -> Login Items)")
161      }
162    case NSMixedState:
163      print("mixed")
164    default: break
165    }
166  }
167  @IBAction func checkboxStartFirefoxAlsoAtLaunchClicked(_ sender: NSButton) {
168    switch sender.state {
169    case NSOnState:
170      print("launch firefox: on")
171      Preferences.shared().alsoStartFirefoxOnLaunch = true
172    case NSOffState:
173      print("launch firefox: off")
174      Preferences.shared().alsoStartFirefoxOnLaunch = false
175    case NSMixedState:
176      print("mixed")
177    default: break
178    }
179  }
180 
181  // MARK: - Radio buttons functions
182 
183  func updateDockMenubarIcons(_ mode: ShowAsMode) -> Bool {
184    // Update preferences with latest choise
185    Preferences.shared().showAsIconMode = mode
186    // Update runtime
187    switch mode {
188    case .bothIcon, .dockIcon:
189      // Show dock icon
190      print("Preferences: Update Dock Icon -> Show")
191      if (!getDockIconStateIsShowing()) {
192        return triggerDockIconShowHide(showIcon: true)
193      }
194    case .menubarIcon:
195      // Hide dock icon
196      print("Preferences: Update Dock Icon -> Hide")
197      if (getDockIconStateIsShowing()) {
198        return triggerDockIconShowHide(showIcon: false)
199      }
200    }
201    // Note: In reality, this won't ever happen.
202    // The switch statement above would return before this.
203    return false
204  }
205 
206  func updateRadioButtonEffect(mode: ShowAsMode, withSideEffect: Bool = true) {
207    changeDockMenubarIconTimer?.invalidate()
208   
209    radioDockIcon?.state = NSOffState
210    radioMenubarIcon?.state = NSOffState
211    radioBothIcon?.state = NSOffState
212   
213    switch mode {
214    case .bothIcon:
215      radioBothIcon?.state = NSOnState
216    case .dockIcon:
217      radioDockIcon?.state = NSOnState
218    case .menubarIcon:
219      radioMenubarIcon?.state = NSOnState
220    }
221   
222    if (withSideEffect) {
223      if #available(OSX 10.12, *) {
224        changeDockMenubarIconTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in
225          // If we're on 10.12 or later
226          self.updateDockMenubarIcons(mode)
227        })
228      } else {
229        // Fallback on earlier versions
230        self.updateDockMenubarIcons(mode)
231      }
232    }
233  }
234 
235  @IBAction func radioBothIconSelected(_ sender: Any) {
236    updateRadioButtonEffect(mode: ShowAsMode.bothIcon)
237  }
238 
239  @IBAction func radioDockIconOnlySelected(_ sender: Any) {
240    updateRadioButtonEffect(mode: ShowAsMode.dockIcon)
241  }
242 
243  @IBAction func radioMenubarOnlySelected(_ sender: Any) {
244    updateRadioButtonEffect(mode: ShowAsMode.menubarIcon)
245  }
246 
247  // MARK: - Triggers
248 
249  func triggerDockIconHideShow(showIcon state: Bool) -> Bool {
250    // Get transform state.
251    var transformState: ProcessApplicationTransformState
252    if state {
253      transformState = ProcessApplicationTransformState(kProcessTransformToForegroundApplication)
254    } else {
255      transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication)
256    }
257   
258    // Show / hide dock icon.
259    var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess))
260    let transformStatus: OSStatus = TransformProcessType(&psn, transformState)
261    return transformStatus == 0
262  }
263 
264  func triggerDockIconShowHide(showIcon state: Bool) -> Bool {
265    var result: Bool
266    if state {
267      result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular)
268    } else {
269      result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.accessory)
270    }
271    return result
272  }
273 
274  func getDockIconStateIsShowing() -> Bool {
275    if NSApp.activationPolicy() == NSApplicationActivationPolicy.regular {
276      return true
277    } else {
278      return false
279    }
280  }
281 
282 
283  // MARK: - Advanced
284 
285  @IBAction func checkboxEnableAdvancedPreferencesClicked(_ sender: NSButton) {
286    switch sender.state {
287    case NSOnState:
288      print("on")
289      Preferences.shared().allowAdvancedPreferenceEdit = true
290      self.advPrefTableView.isEnabled = true
291    case NSOffState:
292      print("off")
293      Preferences.shared().allowAdvancedPreferenceEdit = false
294      self.advPrefTableView.isEnabled = false
295    case NSMixedState:
296      print("mixed")
297    default: break
298    }
299  }
300
301 
302  // End of Class
303}
304
305
Note: See TracBrowser for help on using the repository browser.