diff --git a/Podfile b/Podfile index 1cd04fe..f260a1c 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,7 @@ platform :osx, '10.14' +source 'https://cdn.cocoapods.org/' + target 'RSwitch' do use_frameworks! diff --git a/RSwitch.xcodeproj/project.pbxproj b/RSwitch.xcodeproj/project.pbxproj index 1cc08f5..77a7e6c 100644 --- a/RSwitch.xcodeproj/project.pbxproj +++ b/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 = ""; }; - 01073F102311E0F7007162C9 /* Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alerts.swift; sourceTree = ""; }; 01073F122311E1CF007162C9 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 01073F142311E370007162C9 /* Notify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notify.swift; sourceTree = ""; }; 01073F162311E397007162C9 /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = ""; }; @@ -38,7 +40,7 @@ 01073F1A2311E613007162C9 /* DownloadTarball.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadTarball.swift; sourceTree = ""; }; 01073F1C2311E64E007162C9 /* DownloadRStudio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadRStudio.swift; sourceTree = ""; }; 01073F1E2311E67D007162C9 /* HandleUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleUpdate.swift; sourceTree = ""; }; - 01073F202311E6BD007162C9 /* HandleSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleSwitch.swift; sourceTree = ""; }; + 01073F202311E6BD007162C9 /* HandleRSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleRSwitch.swift; sourceTree = ""; }; 0178970C230ED25100F8F5BC /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 018A8C392312C7BC0006E87D /* procInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = procInfo.h; sourceTree = ""; }; 018A8C3A2312C7BC0006E87D /* libprocInfo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libprocInfo.a; sourceTree = ""; }; @@ -46,6 +48,10 @@ 018A8C3D2312CB480006E87D /* procHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = procHelper.h; sourceTree = ""; }; 018A8C3E2312CB480006E87D /* procHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = procHelper.m; sourceTree = ""; }; 018A8C402312F4940006E87D /* HandleSwitchTo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleSwitchTo.swift; sourceTree = ""; }; + 0198B99D23196689003F7578 /* BrowseMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseMenuAction.swift; sourceTree = ""; }; + 0198B99F231970ED003F7578 /* AppAlerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAlerts.swift; sourceTree = ""; }; + 0198B9A723197CBA003F7578 /* LaunchUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchUtils.swift; sourceTree = ""; }; + 0198B9A923197D3A003F7578 /* Versions+RunningUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Versions+RunningUtils.swift"; sourceTree = ""; }; 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 = ""; }; 01F3EF0D230E635300DF5DF9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -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 = ""; }; - 018A8C342312C6510006E87D /* c */ = { + 018A8C342312C6510006E87D /* ObjC */ = { isa = PBXGroup; children = ( + 0198B99B23196479003F7578 /* ProcInfo */, 018A8C3C2312C8380006E87D /* RSwitch-Bridging-Header.h */, + ); + path = ObjC; + sourceTree = ""; + }; + 0198B99B23196479003F7578 /* ProcInfo */ = { + isa = PBXGroup; + children = ( 018A8C3A2312C7BC0006E87D /* libprocInfo.a */, 018A8C392312C7BC0006E87D /* procInfo.h */, 018A8C3D2312CB480006E87D /* procHelper.h */, 018A8C3E2312CB480006E87D /* procHelper.m */, - 01073F232311E859007162C9 /* swift */, ); - path = c; + path = ProcInfo; + sourceTree = ""; + }; + 0198B99C23196666003F7578 /* MenuActions */ = { + isa = PBXGroup; + children = ( + 0198B99D23196689003F7578 /* BrowseMenuAction.swift */, + ); + path = MenuActions; + sourceTree = ""; + }; + 0198B9A1231970F5003F7578 /* AppAlerts */ = { + isa = PBXGroup; + children = ( + 0198B99F231970ED003F7578 /* AppAlerts.swift */, + ); + path = AppAlerts; + sourceTree = ""; + }; + 0198B9A2231971D1003F7578 /* String+Version */ = { + isa = PBXGroup; + children = ( + 01073F182311E3B8007162C9 /* Bundle.swift */, + 01073F0E2311AE2E007162C9 /* String+Version.swift */, + ); + path = "String+Version"; + sourceTree = ""; + }; + 0198B9A323197301003F7578 /* Downloaders */ = { + isa = PBXGroup; + children = ( + 01073F1A2311E613007162C9 /* DownloadTarball.swift */, + 01073F1C2311E64E007162C9 /* DownloadRStudio.swift */, + ); + path = Downloaders; + sourceTree = ""; + }; + 0198B9A623197C8E003F7578 /* Utils */ = { + isa = PBXGroup; + children = ( + 01073F122311E1CF007162C9 /* Utils.swift */, + 0198B9A723197CBA003F7578 /* LaunchUtils.swift */, + 0198B9A923197D3A003F7578 /* Versions+RunningUtils.swift */, + ); + path = Utils; sourceTree = ""; }; 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; diff --git a/RSwitch/Info.plist b/RSwitch/Info.plist index a258524..628a752 100644 --- a/RSwitch/Info.plist +++ b/RSwitch/Info.plist @@ -21,7 +21,7 @@ CFBundleVersion 1 LSApplicationCategoryType - public.app-category.utilities + public.app-category.developer-tools LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright diff --git a/RSwitch/c/libprocInfo.a b/RSwitch/ObjC/ProcInfo/libprocInfo.a similarity index 100% rename from RSwitch/c/libprocInfo.a rename to RSwitch/ObjC/ProcInfo/libprocInfo.a diff --git a/RSwitch/c/procHelper.h b/RSwitch/ObjC/ProcInfo/procHelper.h similarity index 100% rename from RSwitch/c/procHelper.h rename to RSwitch/ObjC/ProcInfo/procHelper.h diff --git a/RSwitch/c/procHelper.m b/RSwitch/ObjC/ProcInfo/procHelper.m similarity index 100% rename from RSwitch/c/procHelper.m rename to RSwitch/ObjC/ProcInfo/procHelper.m diff --git a/RSwitch/c/procInfo.h b/RSwitch/ObjC/ProcInfo/procInfo.h similarity index 100% rename from RSwitch/c/procInfo.h rename to RSwitch/ObjC/ProcInfo/procInfo.h diff --git a/RSwitch/c/RSwitch-Bridging-Header.h b/RSwitch/ObjC/RSwitch-Bridging-Header.h similarity index 100% rename from RSwitch/c/RSwitch-Bridging-Header.h rename to RSwitch/ObjC/RSwitch-Bridging-Header.h diff --git a/RSwitch/c/swift/Alerts.swift b/RSwitch/c/swift/Alerts.swift deleted file mode 100644 index 48a8936..0000000 --- a/RSwitch/c/swift/Alerts.swift +++ /dev/null @@ -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) -} diff --git a/RSwitch/c/swift/Menu.swift b/RSwitch/c/swift/Menu.swift deleted file mode 100644 index 80ed839..0000000 --- a/RSwitch/c/swift/Menu.swift +++ /dev/null @@ -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) - - } - -} diff --git a/RSwitch/c/swift/Utils.swift b/RSwitch/c/swift/Utils.swift deleted file mode 100644 index 35291f6..0000000 --- a/RSwitch/c/swift/Utils.swift +++ /dev/null @@ -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") - } - } - -} diff --git a/RSwitch/c/swift/AboutViewController.swift b/RSwitch/swift/AboutViewController.swift similarity index 50% rename from RSwitch/c/swift/AboutViewController.swift rename to RSwitch/swift/AboutViewController.swift index d228d54..862cc82 100644 --- a/RSwitch/c/swift/AboutViewController.swift +++ b/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() { diff --git a/RSwitch/swift/AppAlerts/AppAlerts.swift b/RSwitch/swift/AppAlerts/AppAlerts.swift new file mode 100644 index 0000000..416e20a --- /dev/null +++ b/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) + } +} diff --git a/RSwitch/c/swift/AppDelegate.swift b/RSwitch/swift/AppDelegate.swift similarity index 85% rename from RSwitch/c/swift/AppDelegate.swift rename to RSwitch/swift/AppDelegate.swift index 49a2b09..6a5015c 100644 --- a/RSwitch/c/swift/AppDelegate.swift +++ b/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! diff --git a/RSwitch/c/swift/DownloadRStudio.swift b/RSwitch/swift/Downloaders/DownloadRStudio.swift similarity index 97% rename from RSwitch/c/swift/DownloadRStudio.swift rename to RSwitch/swift/Downloaders/DownloadRStudio.swift index ffa7c43..af912eb 100644 --- a/RSwitch/c/swift/DownloadRStudio.swift +++ b/RSwitch/swift/Downloaders/DownloadRStudio.swift @@ -78,7 +78,7 @@ extension AppDelegate { } } - DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true } + DispatchQueue.main.async { [weak self] in self?.rstudio_enabled = true } } diff --git a/RSwitch/c/swift/DownloadTarball.swift b/RSwitch/swift/Downloaders/DownloadTarball.swift similarity index 97% rename from RSwitch/c/swift/DownloadTarball.swift rename to RSwitch/swift/Downloaders/DownloadTarball.swift index 8cbda4b..9bc0c93 100644 --- a/RSwitch/c/swift/DownloadTarball.swift +++ b/RSwitch/swift/Downloaders/DownloadTarball.swift @@ -62,7 +62,7 @@ extension AppDelegate { } } - DispatchQueue.main.async { [weak self] in self?.rdevel_enabled = true } + DispatchQueue.main.async { [weak self] in self?.rdevel_enabled = true } } diff --git a/RSwitch/c/swift/HandleSwitch.swift b/RSwitch/swift/HandleRSwitch.swift similarity index 88% rename from RSwitch/c/swift/HandleSwitch.swift rename to RSwitch/swift/HandleRSwitch.swift index 63ac002..c1546d7 100644 --- a/RSwitch/c/swift/HandleSwitch.swift +++ b/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))") } } diff --git a/RSwitch/c/swift/HandleSwitchTo.swift b/RSwitch/swift/HandleSwitchTo.swift similarity index 100% rename from RSwitch/c/swift/HandleSwitchTo.swift rename to RSwitch/swift/HandleSwitchTo.swift diff --git a/RSwitch/c/swift/HandleUpdate.swift b/RSwitch/swift/HandleUpdate.swift similarity index 100% rename from RSwitch/c/swift/HandleUpdate.swift rename to RSwitch/swift/HandleUpdate.swift diff --git a/RSwitch/swift/Menu.swift b/RSwitch/swift/Menu.swift new file mode 100644 index 0000000..82b1f90 --- /dev/null +++ b/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")) + + } + +} diff --git a/RSwitch/swift/MenuActions/BrowseMenuAction.swift b/RSwitch/swift/MenuActions/BrowseMenuAction.swift new file mode 100644 index 0000000..2f97595 --- /dev/null +++ b/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()) } + + } + + +} + diff --git a/RSwitch/c/swift/Notify.swift b/RSwitch/swift/Notify.swift similarity index 97% rename from RSwitch/c/swift/Notify.swift rename to RSwitch/swift/Notify.swift index 08bc0a6..659d117 100644 --- a/RSwitch/c/swift/Notify.swift +++ b/RSwitch/swift/Notify.swift @@ -27,7 +27,7 @@ extension AppDelegate : NSUserNotificationCenterDelegate { } func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { - return true + return(true) } } diff --git a/RSwitch/c/swift/Bundle.swift b/RSwitch/swift/String+Version/Bundle.swift similarity index 100% rename from RSwitch/c/swift/Bundle.swift rename to RSwitch/swift/String+Version/Bundle.swift diff --git a/RSwitch/c/swift/String+Version.swift b/RSwitch/swift/String+Version/String+Version.swift similarity index 100% rename from RSwitch/c/swift/String+Version.swift rename to RSwitch/swift/String+Version/String+Version.swift diff --git a/RSwitch/swift/Utils/LaunchUtils.swift b/RSwitch/swift/Utils/LaunchUtils.swift new file mode 100644 index 0000000..3605fd2 --- /dev/null +++ b/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: "")) + } + +} diff --git a/RSwitch/swift/Utils/Utils.swift b/RSwitch/swift/Utils/Utils.swift new file mode 100644 index 0000000..19f895c --- /dev/null +++ b/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" + } + +} diff --git a/RSwitch/swift/Utils/Versions+RunningUtils.swift b/RSwitch/swift/Utils/Versions+RunningUtils.swift new file mode 100644 index 0000000..59e0844 --- /dev/null +++ b/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) + } + + } + + } + +} diff --git a/RSwitch/c/swift/ViewController.swift b/RSwitch/swift/ViewController.swift similarity index 100% rename from RSwitch/c/swift/ViewController.swift rename to RSwitch/swift/ViewController.swift diff --git a/guide/.Rhistory b/guide/.Rhistory index e69de29..ccbe5e9 100644 --- a/guide/.Rhistory +++ b/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)