From 68ad4eec14e88b0a96f9b0af98c1b96df4e2eb97 Mon Sep 17 00:00:00 2001
From: meeh <meeh@mail.i2p>
Date: Sat, 8 Dec 2018 09:05:55 +0000
Subject: [PATCH] Mac OS X Launcher: Adding user interface classes for the
 different Preferences views so far.

---
 .../preferences/AdvancedTableView.swift       |  17 ++
 .../PreferencesViewController+TableView.swift |  94 +++++++
 .../PreferencesViewController.swift           | 257 ++++++++++++++++++
 .../PreferencesWindowController.swift         |  24 ++
 4 files changed, 392 insertions(+)
 create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift
 create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift
 create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift
 create mode 100644 launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift

diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift
new file mode 100644
index 0000000000..76a4bb3d17
--- /dev/null
+++ b/launchers/macosx/I2PLauncher/userinterface/preferences/AdvancedTableView.swift
@@ -0,0 +1,17 @@
+//
+//  AdvancedTableView.swift
+//  I2PLauncher
+//
+//  Created by Mikal Villa on 08/12/2018.
+//  Copyright © 2018 The I2P Project. All rights reserved.
+//
+
+import Cocoa
+
+
+class AdvancedTableView: NSTableView {
+  override func viewWillDraw() {
+    super.viewWillDraw()
+  }
+}
+
diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift
new file mode 100644
index 0000000000..1b009ddd63
--- /dev/null
+++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController+TableView.swift
@@ -0,0 +1,94 @@
+//
+//  PreferencesViewController+TableView.swift
+//  I2PLauncher
+//
+//  Created by Mikal Villa on 08/12/2018.
+//  Copyright © 2018 The I2P Project. All rights reserved.
+//
+
+import Cocoa
+
+extension PreferencesViewController: NSTableViewDataSource {
+  
+  func numberOfRows(in tableView: NSTableView) -> Int {
+    return Preferences.shared().count
+  }
+  
+}
+
+extension PreferencesViewController: NSTableViewDelegate {
+  
+  fileprivate enum CellIdentifiers {
+    static let NameCell = "KeyColumnID"
+    static let DefaultCell = "DefaultColumnID"
+    static let ValueCell = "ValueColumnID"
+  }
+  
+  func tableViewDoubleClick(_ sender:AnyObject) {
+    
+    // 1
+    /*guard tableView.selectedRow >= 0,
+     let item = Preferences.shared()[tableView.selectedRow] else {
+     return
+     }
+     
+     if item.isFolder {
+     // 2
+     self.representedObject = item.url as Any
+     }
+     else {
+     // 3
+     NSWorkspace.shared().open(item.url as URL)
+     }
+     */
+  }
+  
+  func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
+    // 1
+    guard let sortDescriptor = tableView.sortDescriptors.first else {
+      return
+    }
+    /*if let order = Directory.FileOrder(rawValue: sortDescriptor.key!) {
+     // 2
+     sortOrder = order
+     sortAscending = sortDescriptor.ascending
+     reloadFileList()
+     }*/
+  }
+  
+  
+  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
+    
+    //var image: NSImage?
+    var text: String = ""
+    var cellIdentifier: String = ""
+    
+    
+    // 1
+    guard let item = Preferences.shared()[row] else {
+      return nil
+    }
+    
+    // 2
+    if tableColumn == tableView.tableColumns[0] {
+      text = item.name!
+      cellIdentifier = CellIdentifiers.NameCell
+    } else if tableColumn == tableView.tableColumns[1] {
+      text = "\(item.defaultValue!)"
+      cellIdentifier = CellIdentifiers.DefaultCell
+    } else if tableColumn == tableView.tableColumns[2] {
+      let thing = (item.selectedValue ?? "none")
+      text = "\(thing)"
+      cellIdentifier = CellIdentifiers.ValueCell
+    }
+    
+    // 3
+    if let cell = tableView.make(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView {
+      cell.textField?.stringValue = text
+      //cell.imageView?.image = image ?? nil
+      return cell
+    }
+    return nil
+  }
+  
+}
diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift
new file mode 100644
index 0000000000..823a1d70ba
--- /dev/null
+++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesViewController.swift
@@ -0,0 +1,257 @@
+//
+//  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
+
+
+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?
+  
+  
+  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)
+    }
+    
+    // Update radio buttons to reflect runtime/stored preferences
+    self.updateRadioButtonEffect(mode: Preferences.shared().showAsIconMode, withSideEffect: false)
+    
+  }
+  
+  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) {
+    switch sender.state {
+    case NSOnState:
+      print("on")
+    case NSOffState:
+      print("off")
+    case NSMixedState:
+      print("mixed")
+    default: break
+    }
+  }
+  @IBAction func checkboxStartFirefoxAlsoAtLaunchClicked(_ sender: NSButton) {
+    switch sender.state {
+    case NSOnState:
+      print("on")
+    case NSOffState:
+      print("off")
+    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
+    case NSOffState:
+      print("off")
+      Preferences.shared().allowAdvancedPreferenceEdit = false
+    case NSMixedState:
+      print("mixed")
+    default: break
+    }
+  }
+
+  
+  // End of Class
+}
+
+
diff --git a/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift
new file mode 100644
index 0000000000..845a1f7596
--- /dev/null
+++ b/launchers/macosx/I2PLauncher/userinterface/preferences/PreferencesWindowController.swift
@@ -0,0 +1,24 @@
+//
+//  PreferencesWindowController.swift
+//  I2PLauncher
+//
+//  Created by Mikal Villa on 07/11/2018.
+//  Copyright © 2018 The I2P Project. All rights reserved.
+//
+
+import Cocoa
+
+class PreferencesWindowController: NSWindowController, NSWindowDelegate {
+
+    override func windowDidLoad() {
+        super.windowDidLoad()
+    
+        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
+    }
+  
+  func windowShouldClose(_ sender: NSWindow) -> Bool {
+    self.window?.orderOut(sender)
+    return false
+  }
+
+}
-- 
GitLab