Browse Source

code reorg

tags/v1.4.1
boB Rudis 5 years ago
parent
commit
dab5f16033
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 2
      Podfile
  2. 106
      RSwitch.xcodeproj/project.pbxproj
  3. 2
      RSwitch/Info.plist
  4. 0
      RSwitch/ObjC/ProcInfo/libprocInfo.a
  5. 0
      RSwitch/ObjC/ProcInfo/procHelper.h
  6. 0
      RSwitch/ObjC/ProcInfo/procHelper.m
  7. 0
      RSwitch/ObjC/ProcInfo/procInfo.h
  8. 0
      RSwitch/ObjC/RSwitch-Bridging-Header.h
  9. 25
      RSwitch/c/swift/Alerts.swift
  10. 157
      RSwitch/c/swift/Menu.swift
  11. 66
      RSwitch/c/swift/Utils.swift
  12. 7
      RSwitch/swift/AboutViewController.swift
  13. 27
      RSwitch/swift/AppAlerts/AppAlerts.swift
  14. 3
      RSwitch/swift/AppDelegate.swift
  15. 0
      RSwitch/swift/Downloaders/DownloadRStudio.swift
  16. 0
      RSwitch/swift/Downloaders/DownloadTarball.swift
  17. 6
      RSwitch/swift/HandleRSwitch.swift
  18. 0
      RSwitch/swift/HandleSwitchTo.swift
  19. 0
      RSwitch/swift/HandleUpdate.swift
  20. 64
      RSwitch/swift/Menu.swift
  21. 74
      RSwitch/swift/MenuActions/BrowseMenuAction.swift
  22. 2
      RSwitch/swift/Notify.swift
  23. 0
      RSwitch/swift/String+Version/Bundle.swift
  24. 0
      RSwitch/swift/String+Version/String+Version.swift
  25. 51
      RSwitch/swift/Utils/LaunchUtils.swift
  26. 22
      RSwitch/swift/Utils/Utils.swift
  27. 106
      RSwitch/swift/Utils/Versions+RunningUtils.swift
  28. 0
      RSwitch/swift/ViewController.swift
  29. 6
      guide/.Rhistory

2
Podfile

@ -1,5 +1,7 @@
platform :osx, '10.14'
source 'https://cdn.cocoapods.org/'
target 'RSwitch' do
use_frameworks!

106
RSwitch.xcodeproj/project.pbxproj

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
01073F0F2311AE2E007162C9 /* String+Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F0E2311AE2E007162C9 /* String+Version.swift */; };
01073F112311E0F7007162C9 /* Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F102311E0F7007162C9 /* Alerts.swift */; };
01073F132311E1CF007162C9 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F122311E1CF007162C9 /* Utils.swift */; };
01073F152311E370007162C9 /* Notify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F142311E370007162C9 /* Notify.swift */; };
01073F172311E397007162C9 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F162311E397007162C9 /* Menu.swift */; };
@ -16,11 +15,15 @@
01073F1B2311E613007162C9 /* DownloadTarball.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F1A2311E613007162C9 /* DownloadTarball.swift */; };
01073F1D2311E64E007162C9 /* DownloadRStudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F1C2311E64E007162C9 /* DownloadRStudio.swift */; };
01073F1F2311E67D007162C9 /* HandleUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F1E2311E67D007162C9 /* HandleUpdate.swift */; };
01073F212311E6BD007162C9 /* HandleSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F202311E6BD007162C9 /* HandleSwitch.swift */; };
01073F212311E6BD007162C9 /* HandleRSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01073F202311E6BD007162C9 /* HandleRSwitch.swift */; };
0178970D230ED25100F8F5BC /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0178970C230ED25100F8F5BC /* AboutViewController.swift */; };
018A8C3B2312C7BC0006E87D /* libprocInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 018A8C3A2312C7BC0006E87D /* libprocInfo.a */; };
018A8C3F2312CB480006E87D /* procHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 018A8C3E2312CB480006E87D /* procHelper.m */; };
018A8C412312F4940006E87D /* HandleSwitchTo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018A8C402312F4940006E87D /* HandleSwitchTo.swift */; };
0198B99E23196689003F7578 /* BrowseMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0198B99D23196689003F7578 /* BrowseMenuAction.swift */; };
0198B9A0231970ED003F7578 /* AppAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0198B99F231970ED003F7578 /* AppAlerts.swift */; };
0198B9A823197CBA003F7578 /* LaunchUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0198B9A723197CBA003F7578 /* LaunchUtils.swift */; };
0198B9AA23197D3A003F7578 /* Versions+RunningUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0198B9A923197D3A003F7578 /* Versions+RunningUtils.swift */; };
01F3EF0C230E635300DF5DF9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F3EF0B230E635300DF5DF9 /* AppDelegate.swift */; };
01F3EF0E230E635300DF5DF9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F3EF0D230E635300DF5DF9 /* ViewController.swift */; };
01F3EF10230E635500DF5DF9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01F3EF0F230E635500DF5DF9 /* Assets.xcassets */; };
@ -30,7 +33,6 @@
/* Begin PBXFileReference section */
01073F0E2311AE2E007162C9 /* String+Version.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Version.swift"; sourceTree = "<group>"; };
01073F102311E0F7007162C9 /* Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alerts.swift; sourceTree = "<group>"; };
01073F122311E1CF007162C9 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
01073F142311E370007162C9 /* Notify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notify.swift; sourceTree = "<group>"; };
01073F162311E397007162C9 /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = "<group>"; };
@ -38,7 +40,7 @@
01073F1A2311E613007162C9 /* DownloadTarball.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadTarball.swift; sourceTree = "<group>"; };
01073F1C2311E64E007162C9 /* DownloadRStudio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadRStudio.swift; sourceTree = "<group>"; };
01073F1E2311E67D007162C9 /* HandleUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleUpdate.swift; sourceTree = "<group>"; };
01073F202311E6BD007162C9 /* HandleSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleSwitch.swift; sourceTree = "<group>"; };
01073F202311E6BD007162C9 /* HandleRSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleRSwitch.swift; sourceTree = "<group>"; };
0178970C230ED25100F8F5BC /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
018A8C392312C7BC0006E87D /* procInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = procInfo.h; sourceTree = "<group>"; };
018A8C3A2312C7BC0006E87D /* libprocInfo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libprocInfo.a; sourceTree = "<group>"; };
@ -46,6 +48,10 @@
018A8C3D2312CB480006E87D /* procHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = procHelper.h; sourceTree = "<group>"; };
018A8C3E2312CB480006E87D /* procHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = procHelper.m; sourceTree = "<group>"; };
018A8C402312F4940006E87D /* HandleSwitchTo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleSwitchTo.swift; sourceTree = "<group>"; };
0198B99D23196689003F7578 /* BrowseMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseMenuAction.swift; sourceTree = "<group>"; };
0198B99F231970ED003F7578 /* AppAlerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAlerts.swift; sourceTree = "<group>"; };
0198B9A723197CBA003F7578 /* LaunchUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchUtils.swift; sourceTree = "<group>"; };
0198B9A923197D3A003F7578 /* Versions+RunningUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Versions+RunningUtils.swift"; sourceTree = "<group>"; };
01F3EF08230E635300DF5DF9 /* RSwitch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RSwitch.app; sourceTree = BUILT_PRODUCTS_DIR; };
01F3EF0B230E635300DF5DF9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
01F3EF0D230E635300DF5DF9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -73,35 +79,85 @@
01073F232311E859007162C9 /* swift */ = {
isa = PBXGroup;
children = (
0198B9A1231970F5003F7578 /* AppAlerts */,
0198B9A323197301003F7578 /* Downloaders */,
0198B99C23196666003F7578 /* MenuActions */,
0198B9A2231971D1003F7578 /* String+Version */,
0198B9A623197C8E003F7578 /* Utils */,
0178970C230ED25100F8F5BC /* AboutViewController.swift */,
01F3EF0B230E635300DF5DF9 /* AppDelegate.swift */,
01073F102311E0F7007162C9 /* Alerts.swift */,
01073F202311E6BD007162C9 /* HandleSwitch.swift */,
01073F202311E6BD007162C9 /* HandleRSwitch.swift */,
01073F1E2311E67D007162C9 /* HandleUpdate.swift */,
01073F1C2311E64E007162C9 /* DownloadRStudio.swift */,
01073F1A2311E613007162C9 /* DownloadTarball.swift */,
018A8C402312F4940006E87D /* HandleSwitchTo.swift */,
01073F182311E3B8007162C9 /* Bundle.swift */,
01073F162311E397007162C9 /* Menu.swift */,
01073F142311E370007162C9 /* Notify.swift */,
01073F0E2311AE2E007162C9 /* String+Version.swift */,
01073F122311E1CF007162C9 /* Utils.swift */,
01F3EF0D230E635300DF5DF9 /* ViewController.swift */,
0178970C230ED25100F8F5BC /* AboutViewController.swift */,
);
path = swift;
sourceTree = "<group>";
};
018A8C342312C6510006E87D /* c */ = {
018A8C342312C6510006E87D /* ObjC */ = {
isa = PBXGroup;
children = (
0198B99B23196479003F7578 /* ProcInfo */,
018A8C3C2312C8380006E87D /* RSwitch-Bridging-Header.h */,
);
path = ObjC;
sourceTree = "<group>";
};
0198B99B23196479003F7578 /* ProcInfo */ = {
isa = PBXGroup;
children = (
018A8C3A2312C7BC0006E87D /* libprocInfo.a */,
018A8C392312C7BC0006E87D /* procInfo.h */,
018A8C3D2312CB480006E87D /* procHelper.h */,
018A8C3E2312CB480006E87D /* procHelper.m */,
01073F232311E859007162C9 /* swift */,
);
path = c;
path = ProcInfo;
sourceTree = "<group>";
};
0198B99C23196666003F7578 /* MenuActions */ = {
isa = PBXGroup;
children = (
0198B99D23196689003F7578 /* BrowseMenuAction.swift */,
);
path = MenuActions;
sourceTree = "<group>";
};
0198B9A1231970F5003F7578 /* AppAlerts */ = {
isa = PBXGroup;
children = (
0198B99F231970ED003F7578 /* AppAlerts.swift */,
);
path = AppAlerts;
sourceTree = "<group>";
};
0198B9A2231971D1003F7578 /* String+Version */ = {
isa = PBXGroup;
children = (
01073F182311E3B8007162C9 /* Bundle.swift */,
01073F0E2311AE2E007162C9 /* String+Version.swift */,
);
path = "String+Version";
sourceTree = "<group>";
};
0198B9A323197301003F7578 /* Downloaders */ = {
isa = PBXGroup;
children = (
01073F1A2311E613007162C9 /* DownloadTarball.swift */,
01073F1C2311E64E007162C9 /* DownloadRStudio.swift */,
);
path = Downloaders;
sourceTree = "<group>";
};
0198B9A623197C8E003F7578 /* Utils */ = {
isa = PBXGroup;
children = (
01073F122311E1CF007162C9 /* Utils.swift */,
0198B9A723197CBA003F7578 /* LaunchUtils.swift */,
0198B9A923197D3A003F7578 /* Versions+RunningUtils.swift */,
);
path = Utils;
sourceTree = "<group>";
};
01F3EEFF230E635300DF5DF9 = {
@ -125,7 +181,8 @@
01F3EF0A230E635300DF5DF9 /* RSwitch */ = {
isa = PBXGroup;
children = (
018A8C342312C6510006E87D /* c */,
018A8C342312C6510006E87D /* ObjC */,
01073F232311E859007162C9 /* swift */,
01F3EF0F230E635500DF5DF9 /* Assets.xcassets */,
01F3EF11230E635500DF5DF9 /* Main.storyboard */,
01F3EF14230E635500DF5DF9 /* Info.plist */,
@ -265,20 +322,23 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
01073F212311E6BD007162C9 /* HandleSwitch.swift in Sources */,
01073F212311E6BD007162C9 /* HandleRSwitch.swift in Sources */,
0198B9A0231970ED003F7578 /* AppAlerts.swift in Sources */,
018A8C3F2312CB480006E87D /* procHelper.m in Sources */,
01073F1F2311E67D007162C9 /* HandleUpdate.swift in Sources */,
01073F1B2311E613007162C9 /* DownloadTarball.swift in Sources */,
018A8C412312F4940006E87D /* HandleSwitchTo.swift in Sources */,
0198B9AA23197D3A003F7578 /* Versions+RunningUtils.swift in Sources */,
0178970D230ED25100F8F5BC /* AboutViewController.swift in Sources */,
01073F192311E3B8007162C9 /* Bundle.swift in Sources */,
01073F152311E370007162C9 /* Notify.swift in Sources */,
01073F1D2311E64E007162C9 /* DownloadRStudio.swift in Sources */,
01073F172311E397007162C9 /* Menu.swift in Sources */,
01073F112311E0F7007162C9 /* Alerts.swift in Sources */,
01F3EF0E230E635300DF5DF9 /* ViewController.swift in Sources */,
01F3EF0C230E635300DF5DF9 /* AppDelegate.swift in Sources */,
01073F0F2311AE2E007162C9 /* String+Version.swift in Sources */,
0198B9A823197CBA003F7578 /* LaunchUtils.swift in Sources */,
0198B99E23196689003F7578 /* BrowseMenuAction.swift in Sources */,
01073F132311E1CF007162C9 /* Utils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -431,14 +491,15 @@
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/RSwitch/c",
"$(PROJECT_DIR)/RSwitch/ObjC",
"$(PROJECT_DIR)/RSwitch/ObjC/ProcInfo",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.4.0;
PRODUCT_BUNDLE_IDENTIFIER = is.rud.bob.RSwitch;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "RSwitch/c/RSwitch-Bridging-Header.h";
SWIFT_OBJC_BRIDGING_HEADER = "RSwitch/ObjC/RSwitch-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
@ -462,14 +523,15 @@
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/RSwitch/c",
"$(PROJECT_DIR)/RSwitch/ObjC",
"$(PROJECT_DIR)/RSwitch/ObjC/ProcInfo",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 1.4.0;
PRODUCT_BUNDLE_IDENTIFIER = is.rud.bob.RSwitch;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "RSwitch/c/RSwitch-Bridging-Header.h";
SWIFT_OBJC_BRIDGING_HEADER = "RSwitch/ObjC/RSwitch-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;

2
RSwitch/Info.plist

@ -21,7 +21,7 @@
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>

0
RSwitch/c/libprocInfo.a → RSwitch/ObjC/ProcInfo/libprocInfo.a

0
RSwitch/c/procHelper.h → RSwitch/ObjC/ProcInfo/procHelper.h

0
RSwitch/c/procHelper.m → RSwitch/ObjC/ProcInfo/procHelper.m

0
RSwitch/c/procInfo.h → RSwitch/ObjC/ProcInfo/procInfo.h

0
RSwitch/c/RSwitch-Bridging-Header.h → RSwitch/ObjC/RSwitch-Bridging-Header.h

25
RSwitch/c/swift/Alerts.swift

@ -1,25 +0,0 @@
//
// Alerts.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
// Show an informational alert
public func infoAlert(_ message: String, _ extra: String? = nil, style: NSAlert.Style = NSAlert.Style.informational) {
let alert = NSAlert()
alert.messageText = message
if extra != nil { alert.informativeText = extra! }
alert.alertStyle = style
alert.runModal()
}
// Show an informational alert and then quit
public func quitAlert(_ message: String, _ extra: String? = nil) {
infoAlert(message, "The application will now quit.", style: NSAlert.Style.critical)
NSApp.terminate(nil)
}

157
RSwitch/c/swift/Menu.swift

@ -1,157 +0,0 @@
//
// Menu.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate: NSMenuDelegate {
func menuWillOpen(_ menu: NSMenu) {
if (menu != self.statusMenu) { return }
// clear the menu
menu.removeAllItems()
// add selection to open frameworks dir in Finder
menu.addItem(NSMenuItem(title: "Open R Frameworks Directory", action: #selector(openFrameworksDir), keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Current R Version:", action: nil, keyEquivalent: ""))
// populate installed versions
let fm = FileManager.default
var targetPath:String? = nil
do {
// gets a directory listing
let entries = try fm.contentsOfDirectory(atPath: app_dirs.macos_r_framework)
// retrieves all versions (excludes hidden files and the Current alias
let versions = entries.sorted().filter { !($0.hasPrefix(".")) && !($0 == "Current") }
let hasCurrent = entries.filter { $0 == "Current" }
// if there was a Current alias (prbly shld alert if not)
if (hasCurrent.count > 0) {
// get where Current points to
let furl = NSURL(fileURLWithPath: app_dirs.macos_r_framework + "/" + "Current")
if (furl.fileReferenceURL() != nil) {
do {
let fdat = try NSURL(resolvingAliasFileAt: furl as URL, options: [])
targetPath = fdat.lastPathComponent!
} catch {
targetPath = furl.path
}
}
// populate menu items with all installed R versions, ensuring we
// put a checkbox next to the one that is Current
var i = 1
for version in versions {
let keynum = (i < 10) ? String(i) : ""
let item = NSMenuItem(title: version, action: #selector(handleSwitch), keyEquivalent: keynum)
item.isEnabled = true
if (version == targetPath) { item.state = NSControl.StateValue.on }
item.representedObject = version
menu.addItem(item)
i = i + 1
}
}
} catch {
quitAlert("Failed to list contents of R framework directory. You either do not have R installed or have incorrect permissions set on " + app_dirs.macos_r_framework)
}
// Add items to download latest r-devel tarball and latest macOS daily
menu.addItem(NSMenuItem.separator())
let rdevelItem = NSMenuItem(title: NSLocalizedString("Download latest R-devel tarball", comment: "Download latest tarball item"), action: self.rdevel_enabled ? #selector(download_latest_tarball) : nil, keyEquivalent: "")
rdevelItem.isEnabled = self.rdevel_enabled
menu.addItem(rdevelItem)
let rstudioItem = NSMenuItem(title: NSLocalizedString("Download latest RStudio daily build", comment: "Download latest RStudio item"), action: self.rstudio_enabled ? #selector(download_latest_rstudio) : nil, keyEquivalent: "")
rstudioItem.isEnabled = self.rstudio_enabled
menu.addItem(rstudioItem)
// Add items to open variosu R for macOS pages
menu.addItem(NSMenuItem.separator())
let webDropdown = NSMenuItem(title: "Web resources", action: nil, keyEquivalent: "")
let webSub = NSMenu()
menu.addItem(webDropdown)
menu.setSubmenu(webSub, for: webDropdown)
webSub.addItem(NSMenuItem(title: NSLocalizedString("R for macOS Developers…", comment: "macOS Dev Page item"), action: #selector(browse_r_macos_dev_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("R for macOS CRAN…", comment: "macOS CRAN Page item"), action: #selector(browse_r_macos_cran_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("R-SIG-Mac Archives…", comment: "R-SIG-Mac Page item"), action: #selector(browse_r_sig_mac_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("R-devel News…", comment: "R-devel News Page item"), action: #selector(browse_r_devel_news_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("R Installation/Admin macOS Section…", comment: "R Install Page item"), action: #selector(browse_r_admin_macos_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem.separator())
webSub.addItem(NSMenuItem(title: NSLocalizedString("RStudio macOS Dailies…", comment: "RStudio macOS Dailies Page item"), action: #selector(browse_rstudio_mac_dailies_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("R StackOverflow…", comment: "R StackOverflow Page item"), action: #selector(browse_r_stackoverflow_page), keyEquivalent: ""))
webSub.addItem(NSMenuItem(title: NSLocalizedString("RStudio Community…", comment: "RStudio Community Page item"), action: #selector(browse_rstudio_community_page), keyEquivalent: ""))
let running_rstudios = NSWorkspace.shared.runningApplications.filter {
$0.bundleIdentifier == "org.rstudio.RStudio"
}
let running_rapps = NSWorkspace.shared.runningApplications.filter {
$0.bundleIdentifier == "org.R-project.R"
}
if ((running_rstudios.count) + (running_rapps.count) > 0) {
menu.addItem(NSMenuItem.separator())
let switchToDropdown = NSMenuItem(title: "Switch to", action: nil, keyEquivalent: "")
let switchToSub = NSMenu()
menu.addItem(switchToDropdown)
menu.setSubmenu(switchToSub, for: switchToDropdown)
for app in running_rstudios + running_rapps {
let args = getArgs(app.processIdentifier)!
let title = app.localizedName! + (args.count > 1 ? " : " + (args[1] as! NSString).lastPathComponent.replacingOccurrences(of: ".Rproj", with: "") : "")
let mi = NSMenuItem(title: title, action: #selector(switch_to), keyEquivalent: "")
mi.representedObject = app
switchToSub.addItem(mi)
}
}
// Add launchers
menu.addItem(NSMenuItem.separator())
let launch_r = "Launch R GUI" + (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control) ? " (new instance)" : "")
let launch_rstudio = "Launch RStudio" + (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control) ? " (new instance)" : "")
menu.addItem(NSMenuItem(title: NSLocalizedString(launch_r, comment: "Launch R GUI item"), action: #selector(launchRApp), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: NSLocalizedString(launch_rstudio, comment: "Launch RStudio item"), action: #selector(launchRStudio), keyEquivalent: ""))
// Add a About item
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: NSLocalizedString("Check for update…", comment: "Check for update item"), action: #selector(checkForUpdate), keyEquivalent: ""))
// Add a About item
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: NSLocalizedString("About RSwitch…", comment: "About menu item"), action: #selector(about), keyEquivalent: ""))
// Add a Quit item
menu.addItem(NSMenuItem.separator())
menu.addItem(quitItem)
}
}

66
RSwitch/c/swift/Utils.swift

@ -1,66 +0,0 @@
//
// Utils.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
public func browse(_ urlString : String) {
let url = URL(string: urlString)!
NSWorkspace.shared.open(url)
}
extension AppDelegate {
struct app_urls {
static let mac_r_project = "https://mac.r-project.org/"
static let macos_cran = "https://cran.rstudio.org/bin/macosx/"
static let r_sig_mac = "https://stat.ethz.ch/pipermail/r-sig-mac/"
static let rstudio_dailies = "https://dailies.rstudio.com/rstudio/oss/mac/"
static let latest_rstudio_dailies = "https://www.rstudio.org/download/latest/daily/desktop/mac/RStudio-latest.dmg"
static let r_admin_macos = "https://cran.rstudio.org/doc/manuals/R-admin.html#Installing-R-under-macOS"
static let r_devel_news = "https://developer.r-project.org/blosxom.cgi/R-devel/NEWS"
static let r_stackoverflow = "https://stackoverflow.com/questions/tagged/r"
static let rstudio_community = "https://community.rstudio.com/"
static let version_check = "https://rud.is/rswitch/releases/current-version.txt"
static let releases = "https://git.rud.is/hrbrmstr/RSwitch/releases"
}
@objc func browse_r_macos_dev_page(_ sender: NSMenuItem?) { browse(app_urls.mac_r_project) }
@objc func browse_r_macos_cran_page(_ sender: NSMenuItem?) { browse(app_urls.macos_cran) }
@objc func browse_r_sig_mac_page(_ sender: NSMenuItem?) { browse(app_urls.r_sig_mac) }
@objc func browse_rstudio_mac_dailies_page(_ sender: NSMenuItem?) { browse(app_urls.rstudio_dailies) }
@objc func browse_r_admin_macos_page(_ sender: NSMenuItem?) { browse(app_urls.r_admin_macos) }
@objc func browse_r_devel_news_page(_ sender: NSMenuItem?) { browse(app_urls.r_devel_news) }
@objc func browse_r_stackoverflow_page(_ sender: NSMenuItem?) { browse(app_urls.r_stackoverflow) }
@objc func browse_rstudio_community_page(_ sender: NSMenuItem?) { browse(app_urls.rstudio_community) }
// Show about dialog
@objc func about(_ sender: NSMenuItem?) { abtController.showWindow(self) }
// Show the framework dir in a new Finder window
@objc func openFrameworksDir(_ sender: NSMenuItem?) { NSWorkspace.shared.openFile(app_dirs.macos_r_framework, withApplication: "Finder") }
// Launch RStudio
@objc func launchRStudio(_ sender: NSMenuItem?) {
if (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control)) {
NSWorkspace.shared.launchApplication(withBundleIdentifier: "org.rstudio.RStudio", options: NSWorkspace.LaunchOptions.newInstance, additionalEventParamDescriptor: nil, launchIdentifier: nil)
} else {
NSWorkspace.shared.launchApplication("RStudio.app")
}
}
// Launch R.app
@objc func launchRApp(_ sender: NSMenuItem?) {
if (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control)) {
NSWorkspace.shared.launchApplication(withBundleIdentifier: "org.R-project.R", options: NSWorkspace.LaunchOptions.newInstance, additionalEventParamDescriptor: nil, launchIdentifier: nil)
} else {
NSWorkspace.shared.launchApplication("R.app")
}
}
}

7
RSwitch/c/swift/AboutViewController.swift → RSwitch/swift/AboutViewController.swift

@ -1,6 +1,13 @@
import Cocoa
extension AppDelegate {
// Show about dialog
@objc func about(_ sender: NSMenuItem?) { abtController.showWindow(self) }
}
class AboutViewController: NSViewController {
override func viewDidLoad() {

27
RSwitch/swift/AppAlerts/AppAlerts.swift

@ -0,0 +1,27 @@
//
// AppAlerts.swift
// RSwitch
//
// Created by hrbrmstr on 8/30/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
public class AppAlerts {
// Show an informational alert
public static func infoAlert(_ message: String, _ extra: String? = nil, style: NSAlert.Style = NSAlert.Style.informational) {
let alert = NSAlert()
alert.messageText = message
if extra != nil { alert.informativeText = extra! }
alert.alertStyle = style
alert.runModal()
}
// Show an informational alert and then quit
public static func quitAlert(_ message: String, _ extra: String? = nil) {
infoAlert(message, "The application will now quit.", style: NSAlert.Style.critical)
NSApp.terminate(nil)
}
}

3
RSwitch/c/swift/AppDelegate.swift → RSwitch/swift/AppDelegate.swift

@ -7,8 +7,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var abtController: NSWindowController!
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let statusMenu = NSMenu()
let quitItem = NSMenuItem(title: NSLocalizedString("Quit", comment: "Quit menu item"), action: #selector(NSApp.terminate), keyEquivalent: "q")
let statusMenu = NSMenu(title: "RSwitch")
var rdevel_enabled: Bool!
var rstudio_enabled: Bool!

0
RSwitch/c/swift/DownloadRStudio.swift → RSwitch/swift/Downloaders/DownloadRStudio.swift

0
RSwitch/c/swift/DownloadTarball.swift → RSwitch/swift/Downloaders/DownloadTarball.swift

6
RSwitch/c/swift/HandleSwitch.swift → RSwitch/swift/HandleRSwitch.swift

@ -1,5 +1,5 @@
//
// HandleSwitch.swift
// HandleRSwitch.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
@ -17,7 +17,7 @@ extension AppDelegate {
// The core worker function. Receives the basename of the selected directory
// then removes the current alias and creates the new one.
@objc func handleSwitch(_ sender: NSMenuItem?) {
@objc func handleRSwitch(_ sender: NSMenuItem?) {
let fm = FileManager.default;
let title = sender?.title
@ -35,7 +35,7 @@ extension AppDelegate {
)
self.notifyUser(title: "Success", text: "Current R version switched to " + title!)
} catch {
self.notifyUser(title: "Action failed", text: "Failed to create alias for " + app_dirs.macos_r_framework + "/" + title!)
self.notifyUser(title: "Action failed", text: "Failed to create alias for " + app_dirs.macos_r_framework + "/" + title! + " (\(error))")
}
}

0
RSwitch/c/swift/HandleSwitchTo.swift → RSwitch/swift/HandleSwitchTo.swift

0
RSwitch/c/swift/HandleUpdate.swift → RSwitch/swift/HandleUpdate.swift

64
RSwitch/swift/Menu.swift

@ -0,0 +1,64 @@
//
// Menu.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate: NSMenuDelegate {
func menuWillOpen(_ menu: NSMenu) {
if (menu != self.statusMenu) { return }
// clear the menu
menu.removeAllItems()
// add selection to open frameworks dir in Finder
menu.addItem(NSMenuItem(title: "Open R Frameworks Directory", action: #selector(openFrameworksDir), keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Current R Version:", action: nil, keyEquivalent: ""))
// populate installed versions
populateRVersionsMenu(menu: menu)
// Add items to download latest r-devel tarball and latest macOS daily
menu.addItem(NSMenuItem.separator())
let rdevelItem = NSMenuItem(title: NSLocalizedString("Download latest R-devel tarball", comment: "Download latest tarball item"), action: self.rdevel_enabled ? #selector(download_latest_tarball) : nil, keyEquivalent: "")
rdevelItem.isEnabled = self.rdevel_enabled
menu.addItem(rdevelItem)
let rstudioItem = NSMenuItem(title: NSLocalizedString("Download latest RStudio daily build", comment: "Download latest RStudio item"), action: self.rstudio_enabled ? #selector(download_latest_rstudio) : nil, keyEquivalent: "")
rstudioItem.isEnabled = self.rstudio_enabled
menu.addItem(rstudioItem)
// Add items to open variosu R for macOS pages
BrowseMenuAction.populateWebItems(menu: menu)
// Add running apps
populateRunningApps(menu: menu)
// Add launchers
populateLaunchers(menu: menu)
// Add a Check for update
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: NSLocalizedString("Check for update…", comment: "Check for update item"), action: #selector(checkForUpdate), keyEquivalent: ""))
// Add an About item
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: NSLocalizedString("About RSwitch…", comment: "About menu item"), action: #selector(about), keyEquivalent: ""))
// Add a Quit item
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApp.terminate), keyEquivalent: "q"))
}
}

74
RSwitch/swift/MenuActions/BrowseMenuAction.swift

@ -0,0 +1,74 @@
//
// MenuAction.swift
// RSwitch
//
// Created by hrbrmstr on 8/30/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate {
@objc func browseFromMenu(_ sender: NSMenuItem) {
let url = (sender.representedObject as! BrowseMenuAction).url
NSWorkspace.shared.open(url)
}
}
class BrowseMenuAction {
public var title : String
public var selector : Selector
public var keyEquivalent : String
public var url : URL
private static let webItemsR = [
BrowseMenuAction(title: "R for macOS Developer's…", url: "https://mac.r-project.org/"),
BrowseMenuAction(title: "R for macOS CRAN…", url: "https://cran.rstudio.org/bin/macosx/"),
BrowseMenuAction(title: "R-SIG-Mac Archives…", url: "https://stat.ethz.ch/pipermail/r-sig-mac/"),
BrowseMenuAction(title: "R-devel News…", url: "https://developer.r-project.org/blosxom.cgi/R-devel/NEWS"),
BrowseMenuAction(title: "R Installation/Admin macOS Section…", url: "https://cran.rstudio.org/doc/manuals/R-admin.html#Installing-R-under-macOS"),
]
private static let webItemsExt = [
BrowseMenuAction(title: "RStudio macOS Dailies…", url: "https://dailies.rstudio.com/rstudio/oss/mac/"),
BrowseMenuAction(title: "R StackOverflow…", url: "https://stackoverflow.com/questions/tagged/r"),
BrowseMenuAction(title: "RStudio Community…", url: "https://community.rstudio.com/")
]
init(title: String, url: String, selector: String = "browseFromMenu", keyEquivalent: String = "") {
self.title = title
self.url = URL(string: url)!
self.selector = Selector((selector+":"))
self.keyEquivalent = keyEquivalent
print(self.selector)
}
public func asMenuItem() -> NSMenuItem {
let mi = NSMenuItem(title: title, action: selector, keyEquivalent: keyEquivalent)
mi.representedObject = self
return(mi)
}
public static func populateWebItems(menu : NSMenu) {
menu.addItem(NSMenuItem.separator())
let webDropdown = NSMenuItem(title: "Web resources", action: nil, keyEquivalent: "")
let webSub = NSMenu()
menu.addItem(webDropdown)
menu.setSubmenu(webSub, for: webDropdown)
for item in webItemsR { webSub.addItem(item.asMenuItem()) }
webSub.addItem(NSMenuItem.separator())
for item in webItemsExt { webSub.addItem(item.asMenuItem()) }
}
}

2
RSwitch/c/swift/Notify.swift → RSwitch/swift/Notify.swift

@ -27,7 +27,7 @@ extension AppDelegate : NSUserNotificationCenterDelegate {
}
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
return true
return(true)
}
}

0
RSwitch/c/swift/Bundle.swift → RSwitch/swift/String+Version/Bundle.swift

0
RSwitch/c/swift/String+Version.swift → RSwitch/swift/String+Version/String+Version.swift

51
RSwitch/swift/Utils/LaunchUtils.swift

@ -0,0 +1,51 @@
//
// LaunchUtils.swift
// RSwitch
//
// Created by hrbrmstr on 8/30/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate {
struct bundleIds {
static let rstudio = "org.rstudio.RStudio"
static let r_base = "org.R-project.R"
}
struct appNames {
static let R = "R.app"
static let RStudio = "RStudio.app"
}
// Launch R.app
@objc func launchRApp(_ sender: NSMenuItem?) {
if (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control)) {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIds.r_base, options: NSWorkspace.LaunchOptions.newInstance, additionalEventParamDescriptor: nil, launchIdentifier: nil)
} else {
NSWorkspace.shared.launchApplication(appNames.R)
}
}
// Launch RStudio
@objc func launchRStudio(_ sender: NSMenuItem?) {
if (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control)) {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIds.rstudio, options: NSWorkspace.LaunchOptions.newInstance, additionalEventParamDescriptor: nil, launchIdentifier: nil)
} else {
NSWorkspace.shared.launchApplication(appNames.RStudio)
}
}
func populateLaunchers(menu : NSMenu) {
menu.addItem(NSMenuItem.separator())
let launch_r = "Launch R GUI" + (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control) ? " (new instance)" : "")
let launch_rstudio = "Launch RStudio" + (NSEvent.modifierFlags.contains(NSEvent.ModifierFlags.control) ? " (new instance)" : "")
menu.addItem(NSMenuItem(title: launch_r, action: #selector(launchRApp), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: launch_rstudio, action: #selector(launchRStudio), keyEquivalent: ""))
}
}

22
RSwitch/swift/Utils/Utils.swift

@ -0,0 +1,22 @@
//
// Utils.swift
// RSwitch
//
// Created by hrbrmstr on 8/24/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate {
struct app_urls {
static let rstudio_dailies = "https://dailies.rstudio.com/rstudio/oss/mac/"
static let latest_rstudio_dailies = "https://www.rstudio.org/download/latest/daily/desktop/mac/RStudio-latest.dmg"
static let version_check = "https://rud.is/rswitch/releases/current-version.txt"
static let releases = "https://git.rud.is/hrbrmstr/RSwitch/releases"
}
}

106
RSwitch/swift/Utils/Versions+RunningUtils.swift

@ -0,0 +1,106 @@
//
// VersionsUtils.swift
// RSwitch
//
// Created by hrbrmstr on 8/30/19.
// Copyright © 2019 Bob Rudis. All rights reserved.
//
import Foundation
import Cocoa
extension AppDelegate {
// Show the framework dir in a new Finder window
@objc func openFrameworksDir(_ sender: NSMenuItem?) {
NSWorkspace.shared.openFile(app_dirs.macos_r_framework, withApplication: "Finder")
}
func populateRVersionsMenu(menu : NSMenu) {
let fm = FileManager.default
var targetPath:String? = nil
do {
// gets a directory listing
let entries = try fm.contentsOfDirectory(atPath: app_dirs.macos_r_framework)
// retrieves all versions (excludes hidden files and the Current alias
let versions = entries.sorted().filter { !($0.hasPrefix(".")) && !($0 == "Current") }
let hasCurrent = entries.filter { $0 == "Current" }
// if there was a Current alias (prbly shld alert if not)
if (hasCurrent.count > 0) {
// get where Current points to
let furl = NSURL(fileURLWithPath: app_dirs.macos_r_framework + "/" + "Current")
if (furl.fileReferenceURL() != nil) {
do {
let fdat = try NSURL(resolvingAliasFileAt: furl as URL, options: [])
targetPath = fdat.lastPathComponent!
} catch {
targetPath = furl.path
}
}
// populate menu items with all installed R versions, ensuring we
// put a checkbox next to the one that is Current
var i = 1
for version in versions {
let keynum = (i < 10) ? String(i) : ""
let item = NSMenuItem(title: version, action: #selector(handleRSwitch), keyEquivalent: keynum)
item.isEnabled = true
if (version == targetPath) { item.state = NSControl.StateValue.on }
item.representedObject = version
menu.addItem(item)
i = i + 1
}
}
} catch {
AppAlerts.quitAlert("Failed to list contents of R framework directory. You either do not have R installed or have incorrect permissions set on " + app_dirs.macos_r_framework)
}
}
func populateRunningApps(menu : NSMenu) {
// gather running RStudio instances
let running_rstudios = NSWorkspace.shared.runningApplications.filter {
$0.bundleIdentifier == bundleIds.rstudio
}
// gather running R GUI instances
let running_rapps = NSWorkspace.shared.runningApplications.filter {
$0.bundleIdentifier == bundleIds.r_base
}
// if we have any running instances of anything
if ((running_rstudios.count) + (running_rapps.count) > 0) {
menu.addItem(NSMenuItem.separator())
let switchToDropdown = NSMenuItem(title: "Switch to", action: nil, keyEquivalent: "")
let switchToSub = NSMenu()
menu.addItem(switchToDropdown)
menu.setSubmenu(switchToSub, for: switchToDropdown)
// populate RStudio first (it'll be in launch order) then R GUI
for app in running_rstudios + running_rapps {
let args = getArgs(app.processIdentifier)!
let title = app.localizedName! + (args.count > 1 ? " : " + (args[1] as! NSString).lastPathComponent.replacingOccurrences(of: ".Rproj", with: "") : "")
let mi = NSMenuItem(title: title, action: #selector(switch_to), keyEquivalent: "")
mi.representedObject = app
switchToSub.addItem(mi)
}
}
}
}

0
RSwitch/c/swift/ViewController.swift → RSwitch/swift/ViewController.swift

6
guide/.Rhistory

@ -0,0 +1,6 @@
library(tree)
install.packages("tree")
library(tree)
tree
fs::dir_tree(path = "/Library/Frameworks/R.framework/Versions/3.5", recurse = 2)
fs::dir_tree(path = "/Library/Frameworks/R.framework/Versions/3.5", recurse = 1)
Loading…
Cancel
Save