Compare commits

..
1 Commits
Author SHA1 Message Date
toxblh 59a8bb6430 patch. work on macos version >=10.12.2 2018-04-09 11:35:39 +01:00
312 changed files with 650 additions and 9833 deletions
-6
View File
@@ -1,6 +0,0 @@
# These are supported funding model platforms
issuehunt: Toxblh
patreon: toxblh
ko_fi: toxblh
custom: https://www.paypal.me/toxblh
-22
View File
@@ -1,22 +0,0 @@
name: Build-and-test
on: [push, pull_request]
jobs:
test:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Run tests
run: xcodebuild test -project MTMR.xcodeproj -scheme 'UnitTests' | xcpretty -c && exit ${PIPESTATUS[0]}
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: xcodebuild archive -project "MTMR.xcodeproj" -scheme "MTMR" -archivePath Release/App.xcarchive DEVELOPMENT_TEAM="" CODE_SIGN_IDENTITY="" | xcpretty -c && exit ${PIPESTATUS[0]}
-41
View File
@@ -1,41 +0,0 @@
name: Publish unsign version
on:
push:
branches:
- master
tags:
- "v*"
jobs:
Build-and-release:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install create-dmg
run: npm i -g create-dmg
- name: Build Archive
run: xcodebuild archive -project "MTMR.xcodeproj" -scheme "MTMR" -archivePath Release/App.xcarchive DEVELOPMENT_TEAM="" CODE_SIGN_IDENTITY="" | xcpretty -c && exit ${PIPESTATUS[0]}
- name: Build App
run: xcodebuild -project "MTMR.xcodeproj" -exportArchive -archivePath Release/App.xcarchive -exportOptionsPlist export-options.plist -exportPath Release | xcpretty -c && exit ${PIPESTATUS[0]}
- name: Create DMG
run: |
cd Release
create-dmg MTMR.app || true
- name: GitHub Release
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: Release/MTMR*.dmg
-5
View File
@@ -1,5 +1,3 @@
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
@@ -7,7 +5,6 @@
## Build generated
build/
DerivedData/
Release/
## Various settings
*.pbxuser
@@ -69,5 +66,3 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
.vscode
+175 -311
View File
@@ -7,162 +7,63 @@
objects = {
/* Begin PBXBuildFile section */
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */; };
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */; };
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 368EDDE620812A1D00E10953 /* ScrollViewItem.swift */; };
36A778BE20A6C27100B38714 /* GeneralExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36A778BD20A6C27100B38714 /* GeneralExtensions.swift */; };
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; };
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; };
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; };
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; };
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; };
36FEF872235A1CFC00A0ABCE /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FEF871235A1CFC00A0ABCE /* AppSettings.swift */; };
4CC9FEDC22FDEA65001512EB /* AMR_ANSIEscapeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC9FEDB22FDEA65001512EB /* AMR_ANSIEscapeHelper.m */; };
4CDC6E5022FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */; };
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */; };
5DC6CA02241F92CB005CD5E8 /* Music.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 5DC6CA00241F92CB005CD5E8 /* Music.nowPlaying.scpt */; };
5DC6CA03241F92CB005CD5E8 /* Music.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 5DC6CA01241F92CB005CD5E8 /* Music.next.scpt */; };
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60173D3D20C0031B002C305F /* LaunchAtLoginController.m */; };
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; };
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; };
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */; };
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */ = {isa = PBXBuildFile; fileRef = 6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */; };
60669B4320AD8FA80074E817 /* GroupBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60669B4220AD8FA80074E817 /* GroupBarItem.swift */; };
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */; };
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */; };
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */; };
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */; };
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0008E542080286C003AD4DD /* SupportHelpers.swift */; };
B002E642216C0E38002774BA /* CoreDisplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B002E641216C0E38002774BA /* CoreDisplay.framework */; };
B00D181D2152F4A5000806F4 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B00D181C2152F4A5000806F4 /* Sparkle.framework */; };
B00D181F2152F521000806F4 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = B00D181C2152F4A5000806F4 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */; };
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05600D22083E9BB00EB218D /* CustomSlider.swift */; };
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; };
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; };
B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* TouchBarItems.swift */; };
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; };
B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */; };
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */; };
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08126F0217BE19000A98970 /* WidgetProtocol.swift */; };
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B08173262135F02B005D4908 /* NightShiftBarItem.swift */; };
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B08173292135F354005D4908 /* CoreBrightness.framework */; };
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B081732B213739FE005D4908 /* DnDBarItem.swift */; };
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B252205C7D8000BC04DC /* AppDelegate.swift */; };
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B254205C7D8000BC04DC /* ViewController.swift */; };
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; };
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; };
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0846A742220C968000288A7 /* NetworkBarItem.swift */; };
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; };
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; };
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B265205C7D8000BC04DC /* MTMRTests.swift */; };
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B270205C7D8000BC04DC /* MTMRUITests.swift */; };
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */; };
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17427207D6B580004B740 /* PlaySmart.scpt */; };
B0B17432207D6B590004B740 /* Weather.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17428207D6B580004B740 /* Weather.scpt */; };
B0B17433207D6B590004B740 /* Finder.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17429207D6B580004B740 /* Finder.scpt */; };
B0B17434207D6B590004B740 /* Battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742A207D6B580004B740 /* Battery.scpt */; };
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742B207D6B590004B740 /* Spotify.next.scpt */; };
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742C207D6B590004B740 /* iTunes.next.scpt */; };
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */; };
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742E207D6B590004B740 /* Vox.next.scpt */; };
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */; };
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */; };
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */; };
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */; };
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C1CFC9205C97D30021C862 /* WindowController.swift */; };
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */; };
BAF5AB5724317B4300B04904 /* BasicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAF5AB5624317B4300B04904 /* BasicView.swift */; };
BAF5AB5924317CAF00B04904 /* SwipeItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAF5AB5824317CAF00B04904 /* SwipeItem.swift */; };
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */ = {isa = PBXBuildFile; fileRef = B0F8771C207AD35400D6E430 /* battery.scpt */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
B00D181E2152F507000806F4 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 12;
dstPath = "";
dstSubfolderSpec = 10;
files = (
B00D181F2152F521000806F4 /* Sparkle.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
B082B262205C7D8000BC04DC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
proxyType = 1;
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
remoteInfo = MTMR;
};
/* End PBXCopyFilesBuildPhase section */
B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B082B247205C7D8000BC04DC /* Project object */;
proxyType = 1;
remoteGlobalIDString = B082B24E205C7D8000BC04DC;
remoteInfo = MTMR;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
36300E85209FD16700B31C71 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = SOURCE_ROOT; };
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptDefinitionTests.swift; sourceTree = "<group>"; };
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundColorTests.swift; sourceTree = "<group>"; };
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewItem.swift; sourceTree = "<group>"; };
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralExtensions.swift; sourceTree = "<group>"; };
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TECHNICAL_DEBT.md; sourceTree = "<group>"; };
36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = "<group>"; };
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptTouchBarItem.swift; sourceTree = "<group>"; };
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = "<group>"; };
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = "<group>"; };
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = "<group>"; };
36FEF871235A1CFC00A0ABCE /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
4CC9FEDA22FDEA65001512EB /* AMR_ANSIEscapeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMR_ANSIEscapeHelper.h; sourceTree = "<group>"; };
4CC9FEDB22FDEA65001512EB /* AMR_ANSIEscapeHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMR_ANSIEscapeHelper.m; sourceTree = "<group>"; };
4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellScriptTouchBarItem.swift; sourceTree = "<group>"; };
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YandexWeatherBarItem.swift; sourceTree = "<group>"; };
5DC6CA00241F92CB005CD5E8 /* Music.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Music.nowPlaying.scpt; sourceTree = "<group>"; };
5DC6CA01241F92CB005CD5E8 /* Music.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Music.next.scpt; sourceTree = "<group>"; };
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = "<group>"; };
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = "<group>"; };
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScrubberTouchBarItem.swift; sourceTree = "<group>"; };
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeprecatedCarbonAPI.h; sourceTree = "<group>"; };
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DeprecatedCarbonAPI.c; sourceTree = "<group>"; };
60669B4220AD8FA80074E817 /* GroupBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBarItem.swift; sourceTree = "<group>"; };
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBarItem.swift; sourceTree = "<group>"; };
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyBarItem.swift; sourceTree = "<group>"; };
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicBarItem.swift; sourceTree = "<group>"; };
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceBarItem.swift; sourceTree = "<group>"; };
B0008E542080286C003AD4DD /* SupportHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportHelpers.swift; sourceTree = "<group>"; };
B002E641216C0E38002774BA /* CoreDisplay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreDisplay.framework; path = ../../../../../System/Library/Frameworks/CoreDisplay.framework; sourceTree = "<group>"; };
B00D181C2152F4A5000806F4 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = "<group>"; };
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryBarItem.swift; sourceTree = "<group>"; };
B05600D22083E9BB00EB218D /* CustomSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomSlider.swift; sourceTree = "<group>"; };
B059D621205E03F5006E6B86 /* TouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarController.swift; sourceTree = "<group>"; };
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButtonTouchBarItem.swift; sourceTree = "<group>"; };
B059D623205E04F3006E6B86 /* TouchBarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarItems.swift; sourceTree = "<group>"; };
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = "<group>"; };
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TouchBarPrivateApi-Bridging.h"; sourceTree = "<group>"; };
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DFRFoundation.framework; path = ../../../../../System/Library/PrivateFrameworks/DFRFoundation.framework; sourceTree = "<group>"; };
B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; };
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PomodoroBarItem.swift; sourceTree = "<group>"; };
B08126F0217BE19000A98970 /* WidgetProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetProtocol.swift; sourceTree = "<group>"; };
B08173262135F02B005D4908 /* NightShiftBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightShiftBarItem.swift; sourceTree = "<group>"; };
B08173282135F128005D4908 /* CBBlueLightClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CBBlueLightClient.h; sourceTree = "<group>"; };
B08173292135F354005D4908 /* CoreBrightness.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBrightness.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreBrightness.framework; sourceTree = "<group>"; };
B081732B213739FE005D4908 /* DnDBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DnDBarItem.swift; sourceTree = "<group>"; };
B082B24F205C7D8000BC04DC /* MTMR.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MTMR.app; sourceTree = BUILT_PRODUCTS_DIR; };
B082B252205C7D8000BC04DC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B082B254205C7D8000BC04DC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
B082B256205C7D8000BC04DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B082B259205C7D8000BC04DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
B082B25B205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = "<group>"; };
B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B082B265205C7D8000BC04DC /* MTMRTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRTests.swift; sourceTree = "<group>"; };
B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B0846A742220C968000288A7 /* NetworkBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBarItem.swift; sourceTree = "<group>"; };
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticFeedback.swift; sourceTree = "<group>"; };
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MultitouchSupport.framework; path = ../../../../../System/Library/PrivateFrameworks/MultitouchSupport.framework; sourceTree = "<group>"; };
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B082B270205C7D8000BC04DC /* MTMRUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRUITests.swift; sourceTree = "<group>"; };
B082B272205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPress.swift; sourceTree = "<group>"; };
B0B17427207D6B580004B740 /* PlaySmart.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PlaySmart.scpt; sourceTree = "<group>"; };
B0B17428207D6B580004B740 /* Weather.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Weather.scpt; sourceTree = "<group>"; };
B0B17429207D6B580004B740 /* Finder.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Finder.scpt; sourceTree = "<group>"; };
B0B1742A207D6B580004B740 /* Battery.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Battery.scpt; sourceTree = "<group>"; };
B0B1742B207D6B590004B740 /* Spotify.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.next.scpt; sourceTree = "<group>"; };
B0B1742C207D6B590004B740 /* iTunes.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.next.scpt; sourceTree = "<group>"; };
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Spotify.nowPlaying.scpt; sourceTree = "<group>"; };
B0B1742E207D6B590004B740 /* Vox.next.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.next.scpt; sourceTree = "<group>"; };
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Vox.nowPlaying.scpt; sourceTree = "<group>"; };
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = iTunes.nowPlaying.scpt; sourceTree = "<group>"; };
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportNSTouchBar.swift; sourceTree = "<group>"; };
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarkModeBarItem.swift; sourceTree = "<group>"; };
B0C1CFC9205C97D30021C862 /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchBarSupport.m; sourceTree = "<group>"; };
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarSupport.h; sourceTree = "<group>"; };
BAF5AB5624317B4300B04904 /* BasicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicView.swift; sourceTree = "<group>"; };
BAF5AB5824317CAF00B04904 /* SwipeItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeItem.swift; sourceTree = "<group>"; };
B0F8771C207AD35400D6E430 /* battery.scpt */ = {isa = PBXFileReference; lastKnownFileType = text; path = battery.scpt; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -170,11 +71,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B002E642216C0E38002774BA /* CoreDisplay.framework in Frameworks */,
B081732A2135F354005D4908 /* CoreBrightness.framework in Frameworks */,
B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */,
B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */,
B00D181D2152F4A5000806F4 /* Sparkle.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -185,16 +82,19 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B082B269205C7D8000BC04DC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B059D62B205F11E8006E6B86 /* Frameworks */ = {
isa = PBXGroup;
children = (
B002E641216C0E38002774BA /* CoreDisplay.framework */,
B00D181C2152F4A5000806F4 /* Sparkle.framework */,
B08173292135F354005D4908 /* CoreBrightness.framework */,
B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */,
B059D62C205F11E8006E6B86 /* DFRFoundation.framework */,
);
name = Frameworks;
@@ -204,9 +104,9 @@
isa = PBXGroup;
children = (
36C2ECD2207B3B1D003CDA33 /* README.md */,
36BDC22F207CDA8600FCFEBE /* TECHNICAL_DEBT.md */,
B082B251205C7D8000BC04DC /* MTMR */,
B082B264205C7D8000BC04DC /* MTMRTests */,
B082B26F205C7D8000BC04DC /* MTMRUITests */,
B082B250205C7D8000BC04DC /* Products */,
B059D62B205F11E8006E6B86 /* Frameworks */,
);
@@ -217,6 +117,7 @@
children = (
B082B24F205C7D8000BC04DC /* MTMR.app */,
B082B261205C7D8000BC04DC /* MTMRTests.xctest */,
B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -224,31 +125,21 @@
B082B251205C7D8000BC04DC /* MTMR */ = {
isa = PBXGroup;
children = (
B0B88A07208CD12000A2C160 /* Widgets */,
B0B1743B207D6ED40004B740 /* CBridge */,
B0B17426207D6AFE0004B740 /* AppleScripts */,
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
B082B254205C7D8000BC04DC /* ViewController.swift */,
B082B256205C7D8000BC04DC /* Assets.xcassets */,
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
B059D623205E04F3006E6B86 /* TouchBarItems.swift */,
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
B0C1CFC9205C97D30021C862 /* WindowController.swift */,
B082B258205C7D8000BC04DC /* Main.storyboard */,
B082B25B205C7D8000BC04DC /* Info.plist */,
B0679BC0215AE73F000FC6B4 /* dsa_pub.pem */,
B082B25C205C7D8000BC04DC /* MTMR.entitlements */,
36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */,
B082B252205C7D8000BC04DC /* AppDelegate.swift */,
B0A7E9A9205D6AA400EEF070 /* KeyPress.swift */,
36FEF871235A1CFC00A0ABCE /* AppSettings.swift */,
B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */,
36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */,
BAF5AB5824317CAF00B04904 /* SwipeItem.swift */,
4CDC6E4F22FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift */,
B059D621205E03F5006E6B86 /* TouchBarController.swift */,
BAF5AB5624317B4300B04904 /* BasicView.swift */,
36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */,
B0008E542080286C003AD4DD /* SupportHelpers.swift */,
B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */,
368EDDE620812A1D00E10953 /* ScrollViewItem.swift */,
B05600D22083E9BB00EB218D /* CustomSlider.swift */,
36A778BD20A6C27100B38714 /* GeneralExtensions.swift */,
B0F3112420C9E35F0076BB88 /* SupportNSTouchBar.swift */,
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
B0F8771C207AD35400D6E430 /* battery.scpt */,
);
path = MTMR;
sourceTree = "<group>";
@@ -256,74 +147,19 @@
B082B264205C7D8000BC04DC /* MTMRTests */ = {
isa = PBXGroup;
children = (
36300E85209FD16700B31C71 /* .travis.yml */,
36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */,
B082B265205C7D8000BC04DC /* MTMRTests.swift */,
B082B267205C7D8000BC04DC /* Info.plist */,
36300E8E20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift */,
36300E9020A2D2B200B31C71 /* BackgroundColorTests.swift */,
);
path = MTMRTests;
sourceTree = "<group>";
};
B0B17426207D6AFE0004B740 /* AppleScripts */ = {
B082B26F205C7D8000BC04DC /* MTMRUITests */ = {
isa = PBXGroup;
children = (
5DC6CA01241F92CB005CD5E8 /* Music.next.scpt */,
5DC6CA00241F92CB005CD5E8 /* Music.nowPlaying.scpt */,
B0B1742A207D6B580004B740 /* Battery.scpt */,
B0B17429207D6B580004B740 /* Finder.scpt */,
B0B1742C207D6B590004B740 /* iTunes.next.scpt */,
B0B17430207D6B590004B740 /* iTunes.nowPlaying.scpt */,
B0B17427207D6B580004B740 /* PlaySmart.scpt */,
B0B1742B207D6B590004B740 /* Spotify.next.scpt */,
B0B1742D207D6B590004B740 /* Spotify.nowPlaying.scpt */,
B0B1742E207D6B590004B740 /* Vox.next.scpt */,
B0B1742F207D6B590004B740 /* Vox.nowPlaying.scpt */,
B0B17428207D6B580004B740 /* Weather.scpt */,
B082B270205C7D8000BC04DC /* MTMRUITests.swift */,
B082B272205C7D8000BC04DC /* Info.plist */,
);
path = AppleScripts;
sourceTree = "<group>";
};
B0B1743B207D6ED40004B740 /* CBridge */ = {
isa = PBXGroup;
children = (
4CC9FEDA22FDEA65001512EB /* AMR_ANSIEscapeHelper.h */,
4CC9FEDB22FDEA65001512EB /* AMR_ANSIEscapeHelper.m */,
B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */,
B059D62A205F0E7D006E6B86 /* TouchBarPrivateApi-Bridging.h */,
B0F87719207AC1EA00D6E430 /* TouchBarSupport.m */,
B0F8771B207AC92700D6E430 /* TouchBarSupport.h */,
6042B6A82083E1F500C525C8 /* DeprecatedCarbonAPI.h */,
6042B6A92083E27000C525C8 /* DeprecatedCarbonAPI.c */,
60173D3C20C0031B002C305F /* LaunchAtLoginController.h */,
60173D3D20C0031B002C305F /* LaunchAtLoginController.m */,
B08173282135F128005D4908 /* CBBlueLightClient.h */,
);
path = CBridge;
sourceTree = "<group>";
};
B0B88A07208CD12000A2C160 /* Widgets */ = {
isa = PBXGroup;
children = (
6042B6A62083E03A00C525C8 /* AppScrubberTouchBarItem.swift */,
B04B7BB62087398C00C835D0 /* BatteryBarItem.swift */,
6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */,
607EEA4C2087A8DA009DA5F0 /* CurrencyBarItem.swift */,
B081732B213739FE005D4908 /* DnDBarItem.swift */,
60669B4220AD8FA80074E817 /* GroupBarItem.swift */,
60F7D453208CC31400ABF5D2 /* InputSourceBarItem.swift */,
60C44AFC20A373A100C0EC91 /* MusicBarItem.swift */,
B0846A742220C968000288A7 /* NetworkBarItem.swift */,
B08173262135F02B005D4908 /* NightShiftBarItem.swift */,
B08126EE217BD0B900A98970 /* PomodoroBarItem.swift */,
36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */,
6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */,
607EEA4A2087835F009DA5F0 /* WeatherBarItem.swift */,
4CFF5E5B22E623DD00BFB1EE /* YandexWeatherBarItem.swift */,
B08126F0217BE19000A98970 /* WidgetProtocol.swift */,
B0F54A792295AC7D00B4C509 /* DarkModeBarItem.swift */,
);
path = Widgets;
path = MTMRUITests;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -336,8 +172,6 @@
B082B24B205C7D8000BC04DC /* Sources */,
B082B24C205C7D8000BC04DC /* Frameworks */,
B082B24D205C7D8000BC04DC /* Resources */,
B00D181E2152F507000806F4 /* CopyFiles */,
B0679BBF215AE085000FC6B4 /* ShellScript */,
);
buildRules = (
);
@@ -359,12 +193,31 @@
buildRules = (
);
dependencies = (
B082B263205C7D8000BC04DC /* PBXTargetDependency */,
);
name = MTMRTests;
productName = MTMRTests;
productReference = B082B261205C7D8000BC04DC /* MTMRTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B082B26B205C7D8000BC04DC /* MTMRUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */;
buildPhases = (
B082B268205C7D8000BC04DC /* Sources */,
B082B269205C7D8000BC04DC /* Frameworks */,
B082B26A205C7D8000BC04DC /* Resources */,
);
buildRules = (
);
dependencies = (
B082B26E205C7D8000BC04DC /* PBXTargetDependency */,
);
name = MTMRUITests;
productName = MTMRUITests;
productReference = B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -372,13 +225,12 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1010;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = "Anton Palgunov";
TargetAttributes = {
B082B24E205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1020;
ProvisioningStyle = Manual;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
@@ -387,7 +239,13 @@
};
B082B260205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1020;
ProvisioningStyle = Automatic;
TestTargetID = B082B24E205C7D8000BC04DC;
};
B082B26B205C7D8000BC04DC = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
TestTargetID = B082B24E205C7D8000BC04DC;
};
};
};
@@ -406,6 +264,7 @@
targets = (
B082B24E205C7D8000BC04DC /* MTMR */,
B082B260205C7D8000BC04DC /* MTMRTests */,
B082B26B205C7D8000BC04DC /* MTMRUITests */,
);
};
/* End PBXProject section */
@@ -415,22 +274,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B0B17434207D6B590004B740 /* Battery.scpt in Resources */,
B0B1743A207D6B590004B740 /* iTunes.nowPlaying.scpt in Resources */,
B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */,
B0B17437207D6B590004B740 /* Spotify.nowPlaying.scpt in Resources */,
5DC6CA02241F92CB005CD5E8 /* Music.nowPlaying.scpt in Resources */,
5DC6CA03241F92CB005CD5E8 /* Music.next.scpt in Resources */,
B0B17436207D6B590004B740 /* iTunes.next.scpt in Resources */,
B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */,
B0B17435207D6B590004B740 /* Spotify.next.scpt in Resources */,
B0679BC1215AE73F000FC6B4 /* dsa_pub.pem in Resources */,
B0B17433207D6B590004B740 /* Finder.scpt in Resources */,
B0B17439207D6B590004B740 /* Vox.nowPlaying.scpt in Resources */,
B0B17438207D6B590004B740 /* Vox.next.scpt in Resources */,
36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */,
B0B17432207D6B590004B740 /* Weather.scpt in Resources */,
B0B17431207D6B590004B740 /* PlaySmart.scpt in Resources */,
B0F8771D207AD35400D6E430 /* battery.scpt in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -441,27 +287,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
B0679BBF215AE085000FC6B4 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
B082B26A205C7D8000BC04DC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nbuildNumber=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\")\nbuildNumber=$(($buildNumber + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B082B24B205C7D8000BC04DC /* Sources */ = {
@@ -469,42 +302,12 @@
buildActionMask = 2147483647;
files = (
B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */,
B05600D32083E9BB00EB218D /* CustomSlider.swift in Sources */,
36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */,
BAF5AB5724317B4300B04904 /* BasicView.swift in Sources */,
B0846A752220C968000288A7 /* NetworkBarItem.swift in Sources */,
B082B255205C7D8000BC04DC /* ViewController.swift in Sources */,
B0C1CFCA205C97D30021C862 /* WindowController.swift in Sources */,
B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */,
B08126F1217BE19000A98970 /* WidgetProtocol.swift in Sources */,
B0008E552080286C003AD4DD /* SupportHelpers.swift in Sources */,
6042B6A72083E03A00C525C8 /* AppScrubberTouchBarItem.swift in Sources */,
BAF5AB5924317CAF00B04904 /* SwipeItem.swift in Sources */,
B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */,
B0F54A7A2295AC7D00B4C509 /* DarkModeBarItem.swift in Sources */,
B08126EF217BD0B900A98970 /* PomodoroBarItem.swift in Sources */,
B081732C213739FE005D4908 /* DnDBarItem.swift in Sources */,
60C44AFD20A373A100C0EC91 /* MusicBarItem.swift in Sources */,
B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */,
60173D3E20C0031B002C305F /* LaunchAtLoginController.m in Sources */,
6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */,
4CDC6E5022FCA93F0069ADD4 /* ShellScriptTouchBarItem.swift in Sources */,
607EEA4B2087835F009DA5F0 /* WeatherBarItem.swift in Sources */,
B0F3112520C9E35F0076BB88 /* SupportNSTouchBar.swift in Sources */,
4CFF5E5C22E623DD00BFB1EE /* YandexWeatherBarItem.swift in Sources */,
6042B6AA2083E27000C525C8 /* DeprecatedCarbonAPI.c in Sources */,
B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */,
B08173272135F02B005D4908 /* NightShiftBarItem.swift in Sources */,
36FEF872235A1CFC00A0ABCE /* AppSettings.swift in Sources */,
60F7D454208CC31400ABF5D2 /* InputSourceBarItem.swift in Sources */,
36A778BE20A6C27100B38714 /* GeneralExtensions.swift in Sources */,
60669B4320AD8FA80074E817 /* GroupBarItem.swift in Sources */,
36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */,
B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */,
B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */,
36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */,
607EEA4D2087A8DA009DA5F0 /* CurrencyBarItem.swift in Sources */,
4CC9FEDC22FDEA65001512EB /* AMR_ANSIEscapeHelper.m in Sources */,
6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */,
368EDDE720812A1D00E10953 /* ScrollViewItem.swift in Sources */,
B04B7BB72087398C00C835D0 /* BatteryBarItem.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -512,16 +315,33 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
36300E9120A2D2B200B31C71 /* BackgroundColorTests.swift in Sources */,
36300E8F20A2CF3400B31C71 /* AppleScriptDefinitionTests.swift in Sources */,
36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */,
36300E83209F040900B31C71 /* SupportHelpers.swift in Sources */,
36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */,
B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B082B268205C7D8000BC04DC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B082B263205C7D8000BC04DC /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B082B24E205C7D8000BC04DC /* MTMR */;
targetProxy = B082B262205C7D8000BC04DC /* PBXContainerItemProxy */;
};
B082B26E205C7D8000BC04DC /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B082B24E205C7D8000BC04DC /* MTMR */;
targetProxy = B082B26D205C7D8000BC04DC /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
B082B258205C7D8000BC04DC /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -565,6 +385,7 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -583,7 +404,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@@ -636,7 +457,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@@ -647,20 +468,19 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = MTMR/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 5.0;
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -668,22 +488,19 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
"$(PROJECT_DIR)",
);
INFOPLIST_FILE = MTMR/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMR;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/CBridge/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 5.0;
SWIFT_OBJC_BRIDGING_HEADER = "MTMR/TouchBarPrivateApi-Bridging.h";
SWIFT_VERSION = 4.0;
};
name = Release;
};
@@ -691,12 +508,16 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
};
name = Debug;
};
@@ -704,14 +525,48 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Manual;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MTMR.app/Contents/MacOS/MTMR";
};
name = Release;
};
B082B27C205C7D8000BC04DC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_TARGET_NAME = MTMR;
};
name = Debug;
};
B082B27D205C7D8000BC04DC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = D6D8BR2QNB;
INFOPLIST_FILE = MTMRUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = Toxblh.MTMRUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TEST_TARGET_NAME = MTMR;
};
name = Release;
};
@@ -745,6 +600,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B082B27B205C7D8000BC04DC /* Build configuration list for PBXNativeTarget "MTMRUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B082B27C205C7D8000BC04DC /* Debug */,
B082B27D205C7D8000BC04DC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B082B247205C7D8000BC04DC /* Project object */;
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
BuildableName = "MTMRTests.xctest"
BlueprintName = "MTMRTests"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B24E205C7D8000BC04DC"
BuildableName = "MTMR.app"
BlueprintName = "MTMR"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B082B260205C7D8000BC04DC"
BuildableName = "MTMRTests.xctest"
BlueprintName = "MTMRTests"
ReferencedContainer = "container:MTMR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+8 -154
View File
@@ -7,171 +7,25 @@
//
import Cocoa
import Sparkle
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
var isBlockedApp: Bool = false
private var fileSystemSource: DispatchSourceFileSystemObject?
func applicationDidFinishLaunching(_: Notification) {
// Configure Sparkle
SUUpdater.shared().automaticallyDownloadsUpdates = false
SUUpdater.shared().automaticallyChecksForUpdates = true
SUUpdater.shared().checkForUpdatesInBackground()
AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true] as NSDictionary)
TouchBarController.shared.setupControlStripPresence()
HapticFeedbackUpdate()
if let button = statusItem.button {
button.image = #imageLiteral(resourceName: "StatusImage")
}
createMenu()
reloadOnDefaultConfigChanged()
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(updateIsBlockedApp), name: NSWorkspace.didActivateApplicationNotification, object: nil)
}
func applicationWillTerminate(_: Notification) {}
func HapticFeedbackUpdate() {
HapticFeedback.shared = AppSettings.hapticFeedbackState ? HapticFeedback() : nil
}
@objc func updateIsBlockedApp() {
if let frontmostAppId = TouchBarController.shared.frontmostApplicationIdentifier {
isBlockedApp = AppSettings.blacklistedAppIds.firstIndex(of: frontmostAppId) != nil
func applicationDidFinishLaunching(_ aNotification: Notification) {
if #available(OSX 10.12.2, *) {
TouchBarController.shared.setupControlStripPresence()
} else {
isBlockedApp = false
// Fallback on earlier versions
}
createMenu()
// Insert code here to initialize your application
}
@objc func openPreferences(_: Any?) {
let task = Process()
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let presetPath = appSupportDirectory.appending("/items.json")
task.launchPath = "/usr/bin/open"
task.arguments = [presetPath]
task.launch()
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
@objc func toggleControlStrip(_ item: NSMenuItem) {
item.state = item.state == .on ? .off : .on
AppSettings.showControlStripState = item.state == .off
TouchBarController.shared.resetControlStrip()
}
@objc func toggleBlackListedApp(_: Any?) {
if let appIdentifier = TouchBarController.shared.frontmostApplicationIdentifier {
if let index = TouchBarController.shared.blacklistAppIdentifiers.firstIndex(of: appIdentifier) {
TouchBarController.shared.blacklistAppIdentifiers.remove(at: index)
} else {
TouchBarController.shared.blacklistAppIdentifiers.append(appIdentifier)
}
AppSettings.blacklistedAppIds = TouchBarController.shared.blacklistAppIdentifiers
TouchBarController.shared.updateActiveApp()
updateIsBlockedApp()
}
}
@objc func toggleHapticFeedback(_ item: NSMenuItem) {
item.state = item.state == .on ? .off : .on
AppSettings.hapticFeedbackState = item.state == .on
HapticFeedbackUpdate()
}
@objc func toggleMultitouch(_ item: NSMenuItem) {
item.state = item.state == .on ? .off : .on
AppSettings.multitouchGestures = item.state == .on
TouchBarController.shared.basicView?.legacyGesturesEnabled = item.state == .on
}
@objc func openPreset(_: Any?) {
let dialog = NSOpenPanel()
dialog.title = "Choose a items.json file"
dialog.showsResizeIndicator = true
dialog.showsHiddenFiles = true
dialog.canChooseDirectories = false
dialog.canCreateDirectories = false
dialog.allowsMultipleSelection = false
dialog.allowedFileTypes = ["json"]
dialog.directoryURL = NSURL.fileURL(withPath: NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR"), isDirectory: true)
if dialog.runModal() == .OK, let path = dialog.url?.path {
TouchBarController.shared.reloadPreset(path: path)
}
}
@objc func toggleStartAtLogin(_: Any?) {
LaunchAtLoginController().setLaunchAtLogin(!LaunchAtLoginController().launchAtLogin, for: NSURL.fileURL(withPath: Bundle.main.bundlePath))
createMenu()
}
func createMenu() {
let menu = NSMenu()
let startAtLogin = NSMenuItem(title: "Start at login", action: #selector(toggleStartAtLogin(_:)), keyEquivalent: "L")
startAtLogin.state = LaunchAtLoginController().launchAtLogin ? .on : .off
let toggleBlackList = NSMenuItem(title: "Toggle current app in blacklist", action: #selector(toggleBlackListedApp(_:)), keyEquivalent: "B")
toggleBlackList.state = isBlockedApp ? .on : .off
let hideControlStrip = NSMenuItem(title: "Hide Control Strip", action: #selector(toggleControlStrip(_:)), keyEquivalent: "T")
hideControlStrip.state = AppSettings.showControlStripState ? .off : .on
let hapticFeedback = NSMenuItem(title: "Haptic Feedback", action: #selector(toggleHapticFeedback(_:)), keyEquivalent: "H")
hapticFeedback.state = AppSettings.hapticFeedbackState ? .on : .off
let multitouchGestures = NSMenuItem(title: "Volume/Brightness gestures", action: #selector(toggleMultitouch(_:)), keyEquivalent: "")
multitouchGestures.state = AppSettings.multitouchGestures ? .on : .off
let settingSeparator = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
settingSeparator.isEnabled = false
menu.addItem(withTitle: "Preferences", action: #selector(openPreferences(_:)), keyEquivalent: ",")
menu.addItem(withTitle: "Open preset", action: #selector(openPreset(_:)), keyEquivalent: "O")
menu.addItem(withTitle: "Check for Updates...", action: #selector(SUUpdater.checkForUpdates(_:)), keyEquivalent: "").target = SUUpdater.shared()
menu.addItem(NSMenuItem.separator())
menu.addItem(settingSeparator)
menu.addItem(hapticFeedback)
menu.addItem(hideControlStrip)
menu.addItem(toggleBlackList)
menu.addItem(startAtLogin)
menu.addItem(multitouchGestures)
menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
statusItem.menu = menu
}
func reloadOnDefaultConfigChanged() {
let file = NSURL.fileURL(withPath: standardConfigPath)
let fd = open(file.path, O_EVTONLY)
fileSystemSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .write, queue: DispatchQueue(label: "DefaultConfigChanged"))
fileSystemSource?.setEventHandler(handler: {
print("Config changed, reloading...")
DispatchQueue.main.async {
TouchBarController.shared.reloadPreset(path: file.path)
}
})
fileSystemSource?.setCancelHandler(handler: {
close(fd)
})
fileSystemSource?.resume()
}
}
-33
View File
@@ -1,33 +0,0 @@
import Foundation
struct AppSettings {
@UserDefault(key: "com.toxblh.mtmr.settings.showControlStrip", defaultValue: false)
static var showControlStripState: Bool
@UserDefault(key: "com.toxblh.mtmr.settings.hapticFeedback", defaultValue: true)
static var hapticFeedbackState: Bool
@UserDefault(key: "com.toxblh.mtmr.settings.multitouchGestures", defaultValue: true)
static var multitouchGestures: Bool
@UserDefault(key: "com.toxblh.mtmr.blackListedApps", defaultValue: [])
static var blacklistedAppIds: [String]
@UserDefault(key: "com.toxblh.mtmr.dock.persistent", defaultValue: [])
static var dockPersistentAppIds: [String]
}
@propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
UserDefaults.standard.synchronize()
}
}
}
-97
View File
@@ -1,97 +0,0 @@
import Foundation
class AppleScriptTouchBarItem: CustomButtonTouchBarItem {
private var script: NSAppleScript!
private let interval: TimeInterval
private var forceHideConstraint: NSLayoutConstraint!
private let alternativeImages: [String: SourceProtocol]
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval, alternativeImages: [String: SourceProtocol]) {
self.interval = interval
self.alternativeImages = alternativeImages
super.init(identifier: identifier, title: "⏳")
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
title = "scheduled"
DispatchQueue.appleScriptQueue.async {
guard let script = source.appleScript else {
DispatchQueue.main.async {
self.title = "no script"
}
return
}
self.script = script
DispatchQueue.main.async {
self.isBordered = false
}
var error: NSDictionary?
guard script.compileAndReturnError(&error) else {
#if DEBUG
print(error?.description ?? "unknown error")
#endif
DispatchQueue.main.async {
self.title = "error"
}
return
}
self.refreshAndSchedule()
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refreshAndSchedule() {
#if DEBUG
print("refresh happened (interval \(interval)), self \(identifier.rawValue))")
#endif
let scriptResult = execute()
DispatchQueue.main.async {
self.title = scriptResult
self.forceHideConstraint.isActive = scriptResult == ""
#if DEBUG
print("did set new script result title \(scriptResult)")
#endif
}
DispatchQueue.appleScriptQueue.asyncAfter(deadline: .now() + interval) { [weak self] in
self?.refreshAndSchedule()
}
}
func updateIcon(iconLabel: String) {
if alternativeImages[iconLabel] != nil {
DispatchQueue.main.async {
self.image = self.alternativeImages[iconLabel]!.image
}
} else {
print("Cannot find icon with label \"\(iconLabel)\"")
}
}
func execute() -> String {
var error: NSDictionary?
let output = script.executeAndReturnError(&error)
if let error = error {
print(error)
return "error"
}
if output.descriptorType == typeAEList {
let arr = Array(1...output.numberOfItems).compactMap({ output.atIndex($0)!.stringValue ?? "" })
if arr.count <= 0 {
return ""
} else if arr.count == 1 {
return arr[0]
} else {
updateIcon(iconLabel: arr[1])
return arr[0]
}
}
return output.stringValue ?? ""
}
}
extension DispatchQueue {
static let appleScriptQueue = DispatchQueue(label: "mtmr.applescript")
}
-7
View File
@@ -1,7 +0,0 @@
tell application "Finder"
if not (exists window 1) then
make new Finder window
set target of front window to path to home folder as string
end if
activate
end tell
-7
View File
@@ -1,7 +0,0 @@
if application "Music" is running then
tell application "Music"
if player state is playing then
next track
end if
end tell
end if
-10
View File
@@ -1,10 +0,0 @@
if application "Music" is running then
tell application "Music"
if player state is playing then
return (get artist of current track) & " – " & (get name of current track)
else
return ""
end if
end tell
end if
return ""
-15
View File
@@ -1,15 +0,0 @@
if application "iTunes" is running then
tell application "iTunes" to playpause
end if
if application "Music" is running then
tell application "Music" to playpause
end if
if application "Spotify" is running then
tell application "Spotify" to playpause
end if
if application "VOX" is running then
tell application "VOX" to playpause
end if
-7
View File
@@ -1,7 +0,0 @@
if application "Spotify" is running then
tell application "Spotify"
if player state is playing then
next track
end if
end tell
end if
-10
View File
@@ -1,10 +0,0 @@
if application "Spotify" is running then
tell application "Spotify"
if player state is playing then
return (get artist of current track) & " – " & (get name of current track)
else
return ""
end if
end tell
end if
return ""
-7
View File
@@ -1,7 +0,0 @@
if application "VOX" is running then
tell application "VOX"
if player state is 1 then
next
end if
end tell
end if
-10
View File
@@ -1,10 +0,0 @@
if application "VOX" is running then
tell application "VOX"
if player state is 1 then
return (get artist) & " – " & (get track)
else
return ""
end if
end tell
end if
return ""
-32
View File
@@ -1,32 +0,0 @@
# This script requires two libs. Download them:
# https://itunes.apple.com/ru/app/json-helper-for-applescript/id453114608?l=en&mt=12
# https://itunes.apple.com/ru/app/location-helper-for-applescript/id488536386?mt=12
tell application "Location Helper"
set clocation_coords to get location coordinates
tell application "JSON Helper"
set weather to fetch JSON from "http://api.openweathermap.org/data/2.5/weather?lat=" & item 1 of clocation_coords & "&lon=" & item 2 of clocation_coords & "&units=metric&appid=32c4256d09a4c52b38aecddba7a078f6"
set temp to temp of main of weather as string
set cond_icon to icon of item 1 of weather of weather as string
if cond_icon is in ["01d", "01n"] then
set cond to "☀️"
else if cond_icon is in ["02d", "02n"] then
set cond to "⛅️"
else if cond_icon is in ["03d", "03n", "04d", "04n"] then
set cond to "☁️"
else if cond_icon is in ["09d", "09n"] then
set cond to "🌧"
else if cond_icon is in ["10d", "10n"] then
set cond to "🌦"
else if cond_icon is in ["11d", "11n"] then
set cond to "🌩"
else if cond_icon is in ["13d", "13n"] then
set cond to "❄️"
else if cond_icon is in ["50d", "50n"] then
set cond to "🌫"
else
set cond to ""
end if
set temp_round to round (temp * 1.0)
return cond & " " & temp_round & "°C"
end tell
end tell
-7
View File
@@ -1,7 +0,0 @@
if application "iTunes" is running then
tell application "iTunes"
if player state is playing then
next track
end if
end tell
end if
-10
View File
@@ -1,10 +0,0 @@
if application "iTunes" is running then
tell application "iTunes"
if player state is playing then
return (get artist of current track) & " – " & (get name of current track)
else
return ""
end if
end tell
end if
return ""
@@ -1,63 +1,53 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"idiom" : "mac",
"filename" : "logo-16.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"idiom" : "mac",
"filename" : "logo-32.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"idiom" : "mac",
"filename" : "logo-32.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"idiom" : "mac",
"filename" : "logo-64.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"idiom" : "mac",
"filename" : "logo-128.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"idiom" : "mac",
"filename" : "logo-256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "logo-256.png",
"size" : "256x256",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "logo-512.png",
"size" : "256x256",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "logo-512.png",
"size" : "512x512",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "logo-1024.png",
"size" : "512x512",
"scale" : "2x"
}
],
Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

-24
View File
@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "StatusImage.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "brightnessDown.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "brightnessUp.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "sun-icon-256.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "39857.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
-24
View File
@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "dnd-off.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

-21
View File
@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "dnd-on.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

-24
View File
@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "KeyboardBrightDown.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

-24
View File
@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "KeyboardBrightUp.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,24 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "NightShift.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

@@ -1,21 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "NightShiftEnabled.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

+49 -5
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
@@ -619,7 +619,7 @@
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
<action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
@@ -676,10 +676,54 @@
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="MTMR" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="VW7-73-dHf" customClass="SUUpdater"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController id="B8D-0N-5wS" customClass="WindowController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1028"/>
<connections>
<outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
</connections>
</window>
<connections>
<segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
</connections>
</windowController>
<customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="250"/>
</scene>
<!--View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="MTMR" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="178" height="57"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bso-ZY-Qqn">
<rect key="frame" x="18" y="20" width="142" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="My TouchBar. My rules" id="cmP-Ef-Jrj">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="74" y="613"/>
</scene>
</scenes>
</document>
-107
View File
@@ -1,107 +0,0 @@
//
// BasicView.swift
// MTMR
//
// Created by Fedor Zaitsev on 3/29/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
class BasicView: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
var twofingers: NSPanGestureRecognizer!
var threefingers: NSPanGestureRecognizer!
var fourfingers: NSPanGestureRecognizer!
var swipeItems: [SwipeItem] = []
var prevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0]
// legacy gesture positions
// by legacy I mean gestures to increse/decrease volume/brigtness which can be checked from app menu
var legacyPrevPositions: [Int: CGFloat] = [2:0, 3:0, 4:0]
var legacyGesturesEnabled = false
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem], swipeItems: [SwipeItem]) {
super.init(identifier: identifier)
self.swipeItems = swipeItems
let views = items.compactMap { $0.view }
let stackView = NSStackView(views: views)
stackView.spacing = 1
stackView.orientation = .horizontal
view = stackView
twofingers = NSPanGestureRecognizer(target: self, action: #selector(twofingersHandler(_:)))
twofingers.numberOfTouchesRequired = 2
twofingers.allowedTouchTypes = .direct
view.addGestureRecognizer(twofingers)
threefingers = NSPanGestureRecognizer(target: self, action: #selector(threefingersHandler(_:)))
threefingers.numberOfTouchesRequired = 3
threefingers.allowedTouchTypes = .direct
view.addGestureRecognizer(threefingers)
fourfingers = NSPanGestureRecognizer(target: self, action: #selector(fourfingersHandler(_:)))
fourfingers.numberOfTouchesRequired = 4
fourfingers.allowedTouchTypes = .direct
view.addGestureRecognizer(fourfingers)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func gestureHandler(position: CGFloat, fingers: Int, state: NSGestureRecognizer.State) {
switch state {
case .began:
prevPositions[fingers] = position
legacyPrevPositions[fingers] = position
case .changed:
if self.legacyGesturesEnabled {
if fingers == 2 {
let prevPos = legacyPrevPositions[fingers]!
if ((position - prevPos) > 10) || ((prevPos - position) > 10) {
if position > prevPos {
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP)
} else if position < prevPos {
HIDPostAuxKey(NX_KEYTYPE_SOUND_DOWN)
}
legacyPrevPositions[fingers] = position
}
}
if fingers == 3 {
let prevPos = legacyPrevPositions[fingers]!
if ((position - prevPos) > 15) || ((prevPos - position) > 15) {
if position > prevPos {
GenericKeyPress(keyCode: CGKeyCode(144)).send()
} else if position < prevPos {
GenericKeyPress(keyCode: CGKeyCode(145)).send()
}
legacyPrevPositions[fingers] = position
}
}
}
case .ended:
print("gesture ended \(position - prevPositions[fingers]!) \(fingers)")
for item in swipeItems {
item.processEvent(offset: position - prevPositions[fingers]!, fingers: fingers)
}
default:
break
}
}
@objc func twofingersHandler(_ sender: NSGestureRecognizer?) {
let position = (sender?.location(in: sender?.view).x)!
self.gestureHandler(position: position, fingers: 2, state: sender!.state)
}
@objc func threefingersHandler(_ sender: NSGestureRecognizer?) {
let position = (sender?.location(in: sender?.view).x)!
self.gestureHandler(position: position, fingers: 3, state: sender!.state)
}
@objc func fourfingersHandler(_ sender: NSGestureRecognizer?) {
let position = (sender?.location(in: sender?.view).x)!
self.gestureHandler(position: position, fingers: 4, state: sender!.state)
}
}
-326
View File
@@ -1,326 +0,0 @@
//
// ANSIEscapeHelper.h
// AnsiColorsTest
//
// Created by Ali Rantakari on 18.3.09.
//
// Version 0.9.6
//
/*
The MIT License
Copyright (c) 2008-2009,2013 Ali Rantakari
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#import <Cocoa/Cocoa.h>
#if !__has_feature(objc_arc)
#warning "This code requires ARC to be enabled."
#endif
// dictionary keys for the SGR code dictionaries that the array
// escapeCodesForString:cleanString: returns contains
#define kAMRCodeDictKey_code @"code"
#define kAMRCodeDictKey_location @"location"
// dictionary keys for the string formatting attribute
// dictionaries that the array attributesForString:cleanString:
// returns contains
#define kAMRAttrDictKey_range @"range"
#define kAMRAttrDictKey_attrName @"attributeName"
#define kAMRAttrDictKey_attrValue @"attributeValue"
/*!
@enum AMR_SGRCode
@abstract SGR (Select Graphic Rendition) ANSI control codes.
*/
typedef enum
{
AMR_SGRCodeNoneOrInvalid = -1,
AMR_SGRCodeAllReset = 0,
AMR_SGRCodeIntensityBold = 1,
AMR_SGRCodeIntensityFaint = 2,
AMR_SGRCodeIntensityNormal = 22,
AMR_SGRCodeItalicOn = 3,
AMR_SGRCodeUnderlineSingle = 4,
AMR_SGRCodeUnderlineDouble = 21,
AMR_SGRCodeUnderlineNone = 24,
AMR_SGRCodeFgBlack = 30,
AMR_SGRCodeFgRed = 31,
AMR_SGRCodeFgGreen = 32,
AMR_SGRCodeFgYellow = 33,
AMR_SGRCodeFgBlue = 34,
AMR_SGRCodeFgMagenta = 35,
AMR_SGRCodeFgCyan = 36,
AMR_SGRCodeFgWhite = 37,
AMR_SGRCodeFgReset = 39,
AMR_SGRCodeBgBlack = 40,
AMR_SGRCodeBgRed = 41,
AMR_SGRCodeBgGreen = 42,
AMR_SGRCodeBgYellow = 43,
AMR_SGRCodeBgBlue = 44,
AMR_SGRCodeBgMagenta = 45,
AMR_SGRCodeBgCyan = 46,
AMR_SGRCodeBgWhite = 47,
AMR_SGRCodeBgReset = 49,
AMR_SGRCodeFgBrightBlack = 90,
AMR_SGRCodeFgBrightRed = 91,
AMR_SGRCodeFgBrightGreen = 92,
AMR_SGRCodeFgBrightYellow = 93,
AMR_SGRCodeFgBrightBlue = 94,
AMR_SGRCodeFgBrightMagenta = 95,
AMR_SGRCodeFgBrightCyan = 96,
AMR_SGRCodeFgBrightWhite = 97,
AMR_SGRCodeBgBrightBlack = 100,
AMR_SGRCodeBgBrightRed = 101,
AMR_SGRCodeBgBrightGreen = 102,
AMR_SGRCodeBgBrightYellow = 103,
AMR_SGRCodeBgBrightBlue = 104,
AMR_SGRCodeBgBrightMagenta = 105,
AMR_SGRCodeBgBrightCyan = 106,
AMR_SGRCodeBgBrightWhite = 107
} AMR_SGRCode;
/*!
@class AMR_ANSIEscapeHelper
@abstract Contains helper methods for dealing with strings
that contain ANSI escape sequences for formatting (colors,
underlining, bold etc.)
*/
@interface AMR_ANSIEscapeHelper : NSObject
/*!
@property defaultStringColor
@abstract The default color used when creating an attributed string (default is black).
*/
@property(copy) NSColor *defaultStringColor;
/*!
@property font
@abstract The font to use when creating string formatting attribute values.
*/
@property(copy) NSFont *font;
/*!
@property ansiColors
@abstract The colors to use for displaying ANSI colors.
@discussion Keys in this dictionary should be NSNumber objects containing SGR code
values from the AMR_SGRCode enum. The corresponding values for these keys
should be NSColor objects. If this property is nil or if it doesn't
contain a key for a specific SGR code, the default color will be used
instead.
*/
@property(retain) NSMutableDictionary *ansiColors;
/*!
@method attributedStringWithANSIEscapedString:
@abstract Returns an attributed string that corresponds both in contents
and formatting to a given string that contains ANSI escape
sequences.
@param aString A String containing ANSI escape sequences
@result An attributed string that mimics as closely as possible
the formatting of the given ANSI-escaped string.
*/
- (NSAttributedString*) attributedStringWithANSIEscapedString:(NSString*)aString;
/*!
@method ansiEscapedStringWithAttributedString:
@abstract Returns a string containing ANSI escape sequences that corresponds
both in contents and formatting to a given attributed string.
@param aAttributedString An attributed string
@result A string that mimics as closely as possible
the formatting of the given attributed string with
ANSI escape sequences.
*/
- (NSString*) ansiEscapedStringWithAttributedString:(NSAttributedString*)aAttributedString;
/*!
@method escapeCodesForString:cleanString:
@abstract Returns an array of SGR codes and their locations from a
string containing ANSI escape sequences as well as a "clean"
version of the string (i.e. one without the ANSI escape
sequences.)
@param aString A String containing ANSI escape sequences
@param aCleanString Upon return, contains a "clean" version of aString (i.e. aString
without the ANSI escape sequences)
@result An array of NSDictionary objects, each of which has
an NSNumber value for the key "code" (specifying an SGR code) and
another NSNumber value for the key "location" (specifying the
location of the code within aCleanString.)
*/
- (NSArray*) escapeCodesForString:(NSString*)aString cleanString:(NSString**)aCleanString;
/*!
@method ansiEscapedStringWithCodesAndLocations:cleanString:
@abstract Returns a string containing ANSI escape codes for formatting based
on a string and an array of SGR codes and their locations within
the given string.
@param aCodesArray An array of NSDictionary objects, each of which should have
an NSNumber value for the key "code" (specifying an SGR
code) and another NSNumber value for the key "location"
(specifying the location of this SGR code in aCleanString.)
@param aCleanString The string to which to insert the ANSI escape codes
described in aCodesArray.
@result A string containing ANSI escape sequences.
*/
- (NSString*) ansiEscapedStringWithCodesAndLocations:(NSArray*)aCodesArray cleanString:(NSString*)aCleanString;
/*!
@method attributesForString:cleanString:
@abstract Convert ANSI escape sequences in a string to string formatting attributes.
@discussion Given a string with some ANSI escape sequences in it, this method returns
attributes for formatting the specified string according to those ANSI
escape sequences as well as a "clean" (i.e. free of the escape sequences)
version of this string.
@param aString A String containing ANSI escape sequences
@param aCleanString Upon return, contains a "clean" version of aString (i.e. aString
without the ANSI escape sequences.) Pass in NULL if you're not
interested in this.
@result An array containing NSDictionary objects, each of which has keys "range"
(an NSValue containing an NSRange, specifying the range for the
attribute within the "clean" version of aString), "attributeName" (an
NSString) and "attributeValue" (an NSObject). You may use these as
arguments for NSMutableAttributedString's methods for setting the
visual formatting.
*/
- (NSArray*) attributesForString:(NSString*)aString cleanString:(NSString**)aCleanString;
/*!
@method AMR_SGRCode:endsFormattingIntroducedByCode:
@abstract Whether the occurrence of a given SGR code would end the formatting run
introduced by another SGR code.
@discussion For example, AMR_SGRCodeFgReset, AMR_SGRCodeAllReset or any SGR code
specifying a foreground color would end the formatting run
introduced by a foreground color -specifying SGR code.
@param endCode The SGR code to test as a candidate for ending the formatting run
introduced by startCode
@param startCode The SGR code that has introduced a formatting run
@result YES if the occurrence of endCode would end the formatting run
introduced by startCode, NO otherwise.
*/
- (BOOL) AMR_SGRCode:(AMR_SGRCode)endCode endsFormattingIntroducedByCode:(AMR_SGRCode)startCode;
/*!
@method colorForSGRCode:
@abstract Returns the color to use for displaying a specific ANSI color.
@discussion This method first considers the values set in the ansiColors
property and only then the standard basic colors (NSColor's
redColor, blueColor etc.)
@param code An SGR code that specifies an ANSI color.
@result The color to use for displaying the ANSI color specified by code.
*/
- (NSColor*) colorForSGRCode:(AMR_SGRCode)code;
/*!
@method AMR_SGRCodeForColor:isForegroundColor:
@abstract Returns a color SGR code that corresponds to a given color.
@discussion This method matches colors to their equivalent SGR codes
by going through the colors specified in the ansiColors
dictionary, and if ansiColors is null or if a match is
not found there, by comparing the given color to the
standard basic colors (NSColor's redColor, blueColor
etc.) The comparison is done simply by checking for
equality.
@param aColor The color to get a corresponding SGR code for
@param aForeground Whether you want a foreground or background color code
@result SGR code that corresponds with aColor.
*/
- (AMR_SGRCode) AMR_SGRCodeForColor:(NSColor*)aColor isForegroundColor:(BOOL)aForeground;
/*!
@method closestSGRCodeForColor:isForegroundColor:
@abstract Returns a color SGR code that represents the closest ANSI
color to a given color.
@discussion This method attempts to find the closest ANSI color to
aColor and return its SGR code.
@param color The color to get a closest color SGR code match for
@param foreground Whether you want a foreground or background color code
@result SGR code for the ANSI color that is closest to aColor.
*/
- (AMR_SGRCode) closestSGRCodeForColor:(NSColor *)color isForegroundColor:(BOOL)foreground;
@end
-996
View File
@@ -1,996 +0,0 @@
//
// ANSIEscapeHelper.m
//
// Created by Ali Rantakari on 18.3.09.
/*
The MIT License
Copyright (c) 2008-2009,2013 Ali Rantakari
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/*
todo:
- don't add useless "reset" escape codes to the string in
-ansiEscapedStringWithAttributedString:
*/
#import "AMR_ANSIEscapeHelper.h"
// the CSI (Control Sequence Initiator) -- i.e. "escape sequence prefix".
// (add your own CSI:Miami joke here)
#define kANSIEscapeCSI @"\033["
// the end byte of an SGR (Select Graphic Rendition)
// ANSI Escape Sequence
#define kANSIEscapeSGREnd @"m"
// color definition helper macros
#define kBrightColorBrightness 1.0
#define kBrightColorSaturation 0.4
#define kBrightColorAlpha 1.0
#define kBrightColorWithHue(h) [NSColor colorWithCalibratedHue:(h) saturation:kBrightColorSaturation brightness:kBrightColorBrightness alpha:kBrightColorAlpha]
// default colors
#define kDefaultANSIColorFgBlack NSColor.blackColor
#define kDefaultANSIColorFgRed NSColor.redColor
#define kDefaultANSIColorFgGreen NSColor.greenColor
#define kDefaultANSIColorFgYellow NSColor.yellowColor
#define kDefaultANSIColorFgBlue NSColor.blueColor
#define kDefaultANSIColorFgMagenta NSColor.magentaColor
#define kDefaultANSIColorFgCyan NSColor.cyanColor
#define kDefaultANSIColorFgWhite NSColor.whiteColor
#define kDefaultANSIColorFgBrightBlack [NSColor colorWithCalibratedWhite:0.337 alpha:1.0]
#define kDefaultANSIColorFgBrightRed kBrightColorWithHue(1.0)
#define kDefaultANSIColorFgBrightGreen kBrightColorWithHue(1.0/3.0)
#define kDefaultANSIColorFgBrightYellow kBrightColorWithHue(1.0/6.0)
#define kDefaultANSIColorFgBrightBlue kBrightColorWithHue(2.0/3.0)
#define kDefaultANSIColorFgBrightMagenta kBrightColorWithHue(5.0/6.0)
#define kDefaultANSIColorFgBrightCyan kBrightColorWithHue(0.5)
#define kDefaultANSIColorFgBrightWhite NSColor.whiteColor
#define kDefaultANSIColorBgBlack NSColor.blackColor
#define kDefaultANSIColorBgRed NSColor.redColor
#define kDefaultANSIColorBgGreen NSColor.greenColor
#define kDefaultANSIColorBgYellow NSColor.yellowColor
#define kDefaultANSIColorBgBlue NSColor.blueColor
#define kDefaultANSIColorBgMagenta NSColor.magentaColor
#define kDefaultANSIColorBgCyan NSColor.cyanColor
#define kDefaultANSIColorBgWhite NSColor.whiteColor
#define kDefaultANSIColorBgBrightBlack kDefaultANSIColorFgBrightBlack
#define kDefaultANSIColorBgBrightRed kDefaultANSIColorFgBrightRed
#define kDefaultANSIColorBgBrightGreen kDefaultANSIColorFgBrightGreen
#define kDefaultANSIColorBgBrightYellow kDefaultANSIColorFgBrightYellow
#define kDefaultANSIColorBgBrightBlue kDefaultANSIColorFgBrightBlue
#define kDefaultANSIColorBgBrightMagenta kDefaultANSIColorFgBrightMagenta
#define kDefaultANSIColorBgBrightCyan kDefaultANSIColorFgBrightCyan
#define kDefaultANSIColorBgBrightWhite kDefaultANSIColorFgBrightWhite
#define kDefaultFontSize [NSFont systemFontOfSize:NSFont.systemFontSize]
#define kDefaultForegroundColor NSColor.blackColor
// minimum weight for an NSFont for it to be considered bold
#define kBoldFontMinWeight 9
@implementation AMR_ANSIEscapeHelper
- (id) init
{
if (!(self = [super init]))
return nil;
self.ansiColors = [NSMutableDictionary dictionary];
return self;
}
- (NSAttributedString*) attributedStringWithANSIEscapedString:(NSString*)aString
{
if (aString == nil)
return nil;
NSString *cleanString;
NSArray *attributesAndRanges = [self attributesForString:aString cleanString:&cleanString];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
initWithString:cleanString
attributes:@{
NSFontAttributeName: self.font ?: kDefaultFontSize,
NSForegroundColorAttributeName: self.defaultStringColor ?: kDefaultForegroundColor
}];
for (NSDictionary *thisAttributeDict in attributesAndRanges)
{
[attributedString
addAttribute:thisAttributeDict[kAMRAttrDictKey_attrName]
value:thisAttributeDict[kAMRAttrDictKey_attrValue]
range:[thisAttributeDict[kAMRAttrDictKey_range] rangeValue]
];
}
return attributedString;
}
- (NSString*) ansiEscapedStringWithAttributedString:(NSAttributedString*)aAttributedString
{
NSMutableArray *codesAndLocations = [NSMutableArray array];
NSArray *attrNames = @[
NSFontAttributeName, NSForegroundColorAttributeName,
NSBackgroundColorAttributeName, NSUnderlineStyleAttributeName,
];
for (NSString *thisAttrName in attrNames)
{
NSRange limitRange = NSMakeRange(0, aAttributedString.length);
id attributeValue;
NSRange effectiveRange;
while (limitRange.length > 0)
{
attributeValue = [aAttributedString
attribute:thisAttrName
atIndex:limitRange.location
longestEffectiveRange:&effectiveRange
inRange:limitRange
];
AMR_SGRCode thisSGRCode = AMR_SGRCodeNoneOrInvalid;
if ([thisAttrName isEqualToString:NSForegroundColorAttributeName])
{
if (attributeValue != nil)
thisSGRCode = [self closestSGRCodeForColor:attributeValue isForegroundColor:YES];
else
thisSGRCode = AMR_SGRCodeFgReset;
}
else if ([thisAttrName isEqualToString:NSBackgroundColorAttributeName])
{
if (attributeValue != nil)
thisSGRCode = [self closestSGRCodeForColor:attributeValue isForegroundColor:NO];
else
thisSGRCode = AMR_SGRCodeBgReset;
}
else if ([thisAttrName isEqualToString:NSFontAttributeName])
{
// we currently only use NSFontAttributeName for bolding so
// here we assume that the formatting "type" in ANSI SGR
// terms is indeed intensity
if (attributeValue != nil)
thisSGRCode = ([NSFontManager.sharedFontManager weightOfFont:attributeValue] >= kBoldFontMinWeight)
? AMR_SGRCodeIntensityBold : AMR_SGRCodeIntensityNormal;
else
thisSGRCode = AMR_SGRCodeIntensityNormal;
}
else if ([thisAttrName isEqualToString:NSUnderlineStyleAttributeName])
{
if (attributeValue != nil)
{
if ([attributeValue intValue] == NSUnderlineStyleSingle)
thisSGRCode = AMR_SGRCodeUnderlineSingle;
else if ([attributeValue intValue] == NSUnderlineStyleDouble)
thisSGRCode = AMR_SGRCodeUnderlineDouble;
else
thisSGRCode = AMR_SGRCodeUnderlineNone;
}
else
thisSGRCode = AMR_SGRCodeUnderlineNone;
}
if (thisSGRCode != AMR_SGRCodeNoneOrInvalid)
{
[codesAndLocations addObject: @{
kAMRCodeDictKey_code: @(thisSGRCode),
kAMRCodeDictKey_location: @(effectiveRange.location),
}];
}
limitRange = NSMakeRange(NSMaxRange(effectiveRange),
NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
}
}
return [self ansiEscapedStringWithCodesAndLocations:codesAndLocations cleanString:aAttributedString.string];
}
- (NSArray*) escapeCodesForString:(NSString*)aString cleanString:(NSString**)aCleanString
{
if (aString == nil)
return nil;
if (aString.length <= kANSIEscapeCSI.length)
{
if (aCleanString)
*aCleanString = aString.copy;
return @[];
}
NSString *cleanString = @"";
// find all escape sequence codes from aString and put them in this array
// along with their start locations within the "clean" version of aString
NSMutableArray *formatCodes = [NSMutableArray array];
NSUInteger aStringLength = aString.length;
NSUInteger coveredLength = 0;
NSRange searchRange = NSMakeRange(0,aStringLength);
NSRange thisEscapeSequenceRange;
do
{
thisEscapeSequenceRange = [aString rangeOfString:kANSIEscapeCSI options:NSLiteralSearch range:searchRange];
if (thisEscapeSequenceRange.location != NSNotFound)
{
// adjust range's length so that it encompasses the whole ANSI escape sequence
// and not just the Control Sequence Initiator (the "prefix") by finding the
// final byte of the control sequence (one that has an ASCII decimal value
// between 64 and 126.) at the same time, read all formatting codes from inside
// this escape sequence (there may be several, separated by semicolons.)
NSMutableArray *codes = [NSMutableArray array];
unsigned int code = 0;
unsigned int lengthAddition = 1;
NSUInteger thisIndex;
for (;;)
{
thisIndex = (NSMaxRange(thisEscapeSequenceRange)+lengthAddition-1);
if (thisIndex >= aStringLength)
break;
unichar c = [aString characterAtIndex:thisIndex];
if (('0' <= c) && (c <= '9'))
{
int digit = c - '0';
code = (code == 0) ? digit : code*10+digit;
}
// ASCII decimal 109 is the SGR (Select Graphic Rendition) final byte
// ("m"). this means that the code value we've just read specifies formatting
// for the output; exactly what we're interested in.
if (c == 'm')
{
[codes addObject:@(code)];
break;
}
else if ((64 <= c) && (c <= 126)) // any other valid final byte
{
[codes removeAllObjects];
break;
}
else if (c == ';') // separates codes within the same sequence
{
[codes addObject:@(code)];
code = 0;
}
lengthAddition++;
}
thisEscapeSequenceRange.length += lengthAddition;
NSUInteger locationInCleanString = coveredLength+thisEscapeSequenceRange.location-searchRange.location;
for (NSNumber *codeToAdd in codes)
{
[formatCodes addObject: @{
kAMRCodeDictKey_code: codeToAdd,
kAMRCodeDictKey_location: @(locationInCleanString)
}];
}
NSUInteger thisCoveredLength = thisEscapeSequenceRange.location-searchRange.location;
if (thisCoveredLength > 0)
cleanString = [cleanString stringByAppendingString:[aString substringWithRange:NSMakeRange(searchRange.location, thisCoveredLength)]];
coveredLength += thisCoveredLength;
searchRange.location = NSMaxRange(thisEscapeSequenceRange);
searchRange.length = aStringLength-searchRange.location;
}
}
while(thisEscapeSequenceRange.location != NSNotFound);
if (searchRange.length > 0)
cleanString = [cleanString stringByAppendingString:[aString substringWithRange:searchRange]];
if (aCleanString)
*aCleanString = cleanString;
return formatCodes;
}
- (NSString*) ansiEscapedStringWithCodesAndLocations:(NSArray*)aCodesArray cleanString:(NSString*)aCleanString
{
NSMutableString* retStr = [NSMutableString stringWithCapacity:aCleanString.length];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:kAMRCodeDictKey_location ascending:YES];
NSArray *codesArray = [aCodesArray sortedArrayUsingDescriptors:@[sortDescriptor]];
NSUInteger aCleanStringIndex = 0;
NSUInteger aCleanStringLength = aCleanString.length;
for (NSDictionary *thisCodeDict in codesArray)
{
if (!( thisCodeDict[kAMRCodeDictKey_code] &&
thisCodeDict[kAMRCodeDictKey_location]
))
continue;
AMR_SGRCode thisCode = [thisCodeDict[kAMRCodeDictKey_code] unsignedIntValue];
NSUInteger formattingRunStartLocation = [thisCodeDict[kAMRCodeDictKey_location] unsignedIntegerValue];
if (formattingRunStartLocation > aCleanStringLength)
continue;
if (aCleanStringIndex < formattingRunStartLocation)
[retStr appendString:[aCleanString substringWithRange:NSMakeRange(aCleanStringIndex, formattingRunStartLocation-aCleanStringIndex)]];
[retStr appendFormat:@"%@%d%@", kANSIEscapeCSI, thisCode, kANSIEscapeSGREnd];
aCleanStringIndex = formattingRunStartLocation;
}
if (aCleanStringIndex < aCleanStringLength)
[retStr appendString:[aCleanString substringFromIndex:aCleanStringIndex]];
[retStr appendFormat:@"%@%d%@", kANSIEscapeCSI, AMR_SGRCodeAllReset, kANSIEscapeSGREnd];
return retStr;
}
- (NSArray*) attributesForString:(NSString*)aString cleanString:(NSString**)aCleanString
{
if (aString == nil)
return nil;
if (aString.length <= kANSIEscapeCSI.length)
{
if (aCleanString)
*aCleanString = aString.copy;
return @[];
}
NSMutableArray *attrsAndRanges = [NSMutableArray array];
NSString *cleanString;
NSArray *formatCodes = [self escapeCodesForString:aString cleanString:&cleanString];
// go through all the found escape sequence codes and for each one, create
// the string formatting attribute name and value, find the next escape
// sequence that specifies the end of the formatting run started by
// the currently handled code, and generate a range from the difference
// in those codes' locations within the clean aString.
for (NSUInteger iCode = 0; iCode < formatCodes.count; iCode++)
{
NSDictionary *thisCodeDict = formatCodes[iCode];
AMR_SGRCode thisCode = [thisCodeDict[kAMRCodeDictKey_code] unsignedIntValue];
NSUInteger formattingRunStartLocation = [thisCodeDict[kAMRCodeDictKey_location] unsignedIntegerValue];
// the attributed string attribute name for the formatting run introduced
// by this code
NSString *thisAttributeName = nil;
// the attributed string attribute value for this formatting run introduced
// by this code
NSObject *thisAttributeValue = nil;
// set attribute name
switch(thisCode)
{
case AMR_SGRCodeFgBlack:
case AMR_SGRCodeFgRed:
case AMR_SGRCodeFgGreen:
case AMR_SGRCodeFgYellow:
case AMR_SGRCodeFgBlue:
case AMR_SGRCodeFgMagenta:
case AMR_SGRCodeFgCyan:
case AMR_SGRCodeFgWhite:
case AMR_SGRCodeFgBrightBlack:
case AMR_SGRCodeFgBrightRed:
case AMR_SGRCodeFgBrightGreen:
case AMR_SGRCodeFgBrightYellow:
case AMR_SGRCodeFgBrightBlue:
case AMR_SGRCodeFgBrightMagenta:
case AMR_SGRCodeFgBrightCyan:
case AMR_SGRCodeFgBrightWhite:
thisAttributeName = NSForegroundColorAttributeName;
break;
case AMR_SGRCodeBgBlack:
case AMR_SGRCodeBgRed:
case AMR_SGRCodeBgGreen:
case AMR_SGRCodeBgYellow:
case AMR_SGRCodeBgBlue:
case AMR_SGRCodeBgMagenta:
case AMR_SGRCodeBgCyan:
case AMR_SGRCodeBgWhite:
case AMR_SGRCodeBgBrightBlack:
case AMR_SGRCodeBgBrightRed:
case AMR_SGRCodeBgBrightGreen:
case AMR_SGRCodeBgBrightYellow:
case AMR_SGRCodeBgBrightBlue:
case AMR_SGRCodeBgBrightMagenta:
case AMR_SGRCodeBgBrightCyan:
case AMR_SGRCodeBgBrightWhite:
thisAttributeName = NSBackgroundColorAttributeName;
break;
case AMR_SGRCodeIntensityBold:
case AMR_SGRCodeIntensityNormal:
case AMR_SGRCodeIntensityFaint:
thisAttributeName = NSFontAttributeName;
break;
case AMR_SGRCodeUnderlineSingle:
case AMR_SGRCodeUnderlineDouble:
case AMR_SGRCodeUnderlineNone:
thisAttributeName = NSUnderlineStyleAttributeName;
break;
case AMR_SGRCodeAllReset:
case AMR_SGRCodeFgReset:
case AMR_SGRCodeBgReset:
case AMR_SGRCodeNoneOrInvalid:
case AMR_SGRCodeItalicOn:
continue;
}
// set attribute value
switch(thisCode)
{
case AMR_SGRCodeBgBlack:
case AMR_SGRCodeFgBlack:
case AMR_SGRCodeBgRed:
case AMR_SGRCodeFgRed:
case AMR_SGRCodeBgGreen:
case AMR_SGRCodeFgGreen:
case AMR_SGRCodeBgYellow:
case AMR_SGRCodeFgYellow:
case AMR_SGRCodeBgBlue:
case AMR_SGRCodeFgBlue:
case AMR_SGRCodeBgMagenta:
case AMR_SGRCodeFgMagenta:
case AMR_SGRCodeBgCyan:
case AMR_SGRCodeFgCyan:
case AMR_SGRCodeBgWhite:
case AMR_SGRCodeFgWhite:
case AMR_SGRCodeBgBrightBlack:
case AMR_SGRCodeFgBrightBlack:
case AMR_SGRCodeBgBrightRed:
case AMR_SGRCodeFgBrightRed:
case AMR_SGRCodeBgBrightGreen:
case AMR_SGRCodeFgBrightGreen:
case AMR_SGRCodeBgBrightYellow:
case AMR_SGRCodeFgBrightYellow:
case AMR_SGRCodeBgBrightBlue:
case AMR_SGRCodeFgBrightBlue:
case AMR_SGRCodeBgBrightMagenta:
case AMR_SGRCodeFgBrightMagenta:
case AMR_SGRCodeBgBrightCyan:
case AMR_SGRCodeFgBrightCyan:
case AMR_SGRCodeBgBrightWhite:
case AMR_SGRCodeFgBrightWhite:
thisAttributeValue = [self colorForSGRCode:thisCode];
break;
case AMR_SGRCodeIntensityBold:
{
NSFont *boldFont = [NSFontManager.sharedFontManager convertFont:self.font toHaveTrait:NSBoldFontMask];
thisAttributeValue = boldFont;
}
break;
case AMR_SGRCodeIntensityNormal:
case AMR_SGRCodeIntensityFaint:
{
NSFont *unboldFont = [NSFontManager.sharedFontManager convertFont:self.font toHaveTrait:NSUnboldFontMask];
thisAttributeValue = unboldFont;
}
break;
case AMR_SGRCodeUnderlineSingle:
thisAttributeValue = @(NSUnderlineStyleSingle);
break;
case AMR_SGRCodeUnderlineDouble:
thisAttributeValue = @(NSUnderlineStyleDouble);
break;
case AMR_SGRCodeUnderlineNone:
thisAttributeValue = @(NSUnderlineStyleNone);
break;
case AMR_SGRCodeAllReset:
case AMR_SGRCodeFgReset:
case AMR_SGRCodeBgReset:
case AMR_SGRCodeNoneOrInvalid:
case AMR_SGRCodeItalicOn:
break;
}
// find the next sequence that specifies the end of this formatting run
NSInteger formattingRunEndLocation = -1;
if (iCode < (formatCodes.count - 1))
{
NSDictionary *thisEndCodeCandidateDict;
unichar thisEndCodeCandidate;
for (NSUInteger iEndCode = iCode+1; iEndCode < formatCodes.count; iEndCode++)
{
thisEndCodeCandidateDict = formatCodes[iEndCode];
thisEndCodeCandidate = [thisEndCodeCandidateDict[kAMRCodeDictKey_code] unsignedIntValue];
if ([self AMR_SGRCode:thisEndCodeCandidate endsFormattingIntroducedByCode:thisCode])
{
formattingRunEndLocation = [thisEndCodeCandidateDict[kAMRCodeDictKey_location] unsignedIntegerValue];
break;
}
}
}
if (formattingRunEndLocation == -1)
formattingRunEndLocation = cleanString.length;
if (thisAttributeName && thisAttributeValue)
{
[attrsAndRanges addObject:@{
kAMRAttrDictKey_range: [NSValue valueWithRange:NSMakeRange(formattingRunStartLocation, (formattingRunEndLocation-formattingRunStartLocation))],
kAMRAttrDictKey_attrName: thisAttributeName,
kAMRAttrDictKey_attrValue: thisAttributeValue,
}];
}
}
if (aCleanString)
*aCleanString = cleanString;
return attrsAndRanges;
}
- (BOOL) AMR_SGRCode:(AMR_SGRCode)endCode endsFormattingIntroducedByCode:(AMR_SGRCode)startCode
{
switch(startCode)
{
case AMR_SGRCodeFgBlack:
case AMR_SGRCodeFgRed:
case AMR_SGRCodeFgGreen:
case AMR_SGRCodeFgYellow:
case AMR_SGRCodeFgBlue:
case AMR_SGRCodeFgMagenta:
case AMR_SGRCodeFgCyan:
case AMR_SGRCodeFgWhite:
case AMR_SGRCodeFgBrightBlack:
case AMR_SGRCodeFgBrightRed:
case AMR_SGRCodeFgBrightGreen:
case AMR_SGRCodeFgBrightYellow:
case AMR_SGRCodeFgBrightBlue:
case AMR_SGRCodeFgBrightMagenta:
case AMR_SGRCodeFgBrightCyan:
case AMR_SGRCodeFgBrightWhite:
return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeFgReset ||
endCode == AMR_SGRCodeFgBlack || endCode == AMR_SGRCodeFgRed ||
endCode == AMR_SGRCodeFgGreen || endCode == AMR_SGRCodeFgYellow ||
endCode == AMR_SGRCodeFgBlue || endCode == AMR_SGRCodeFgMagenta ||
endCode == AMR_SGRCodeFgCyan || endCode == AMR_SGRCodeFgWhite ||
endCode == AMR_SGRCodeFgBrightBlack || endCode == AMR_SGRCodeFgBrightRed ||
endCode == AMR_SGRCodeFgBrightGreen || endCode == AMR_SGRCodeFgBrightYellow ||
endCode == AMR_SGRCodeFgBrightBlue || endCode == AMR_SGRCodeFgBrightMagenta ||
endCode == AMR_SGRCodeFgBrightCyan || endCode == AMR_SGRCodeFgBrightWhite);
case AMR_SGRCodeBgBlack:
case AMR_SGRCodeBgRed:
case AMR_SGRCodeBgGreen:
case AMR_SGRCodeBgYellow:
case AMR_SGRCodeBgBlue:
case AMR_SGRCodeBgMagenta:
case AMR_SGRCodeBgCyan:
case AMR_SGRCodeBgWhite:
case AMR_SGRCodeBgBrightBlack:
case AMR_SGRCodeBgBrightRed:
case AMR_SGRCodeBgBrightGreen:
case AMR_SGRCodeBgBrightYellow:
case AMR_SGRCodeBgBrightBlue:
case AMR_SGRCodeBgBrightMagenta:
case AMR_SGRCodeBgBrightCyan:
case AMR_SGRCodeBgBrightWhite:
return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeBgReset ||
endCode == AMR_SGRCodeBgBlack || endCode == AMR_SGRCodeBgRed ||
endCode == AMR_SGRCodeBgGreen || endCode == AMR_SGRCodeBgYellow ||
endCode == AMR_SGRCodeBgBlue || endCode == AMR_SGRCodeBgMagenta ||
endCode == AMR_SGRCodeBgCyan || endCode == AMR_SGRCodeBgWhite ||
endCode == AMR_SGRCodeBgBrightBlack || endCode == AMR_SGRCodeBgBrightRed ||
endCode == AMR_SGRCodeBgBrightGreen || endCode == AMR_SGRCodeBgBrightYellow ||
endCode == AMR_SGRCodeBgBrightBlue || endCode == AMR_SGRCodeBgBrightMagenta ||
endCode == AMR_SGRCodeBgBrightCyan || endCode == AMR_SGRCodeBgBrightWhite);
case AMR_SGRCodeIntensityBold:
case AMR_SGRCodeIntensityNormal:
return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeIntensityNormal ||
endCode == AMR_SGRCodeIntensityBold || endCode == AMR_SGRCodeIntensityFaint);
case AMR_SGRCodeUnderlineSingle:
case AMR_SGRCodeUnderlineDouble:
return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeUnderlineNone ||
endCode == AMR_SGRCodeUnderlineSingle || endCode == AMR_SGRCodeUnderlineDouble);
case AMR_SGRCodeNoneOrInvalid:
case AMR_SGRCodeItalicOn:
case AMR_SGRCodeUnderlineNone:
case AMR_SGRCodeIntensityFaint:
case AMR_SGRCodeAllReset:
case AMR_SGRCodeBgReset:
case AMR_SGRCodeFgReset:
return NO;
}
return NO;
}
- (NSColor*) colorForSGRCode:(AMR_SGRCode)code
{
if (self.ansiColors)
{
NSColor *preferredColor = self.ansiColors[@(code)];
if (preferredColor)
return preferredColor;
}
switch(code)
{
case AMR_SGRCodeFgBlack:
return kDefaultANSIColorFgBlack;
case AMR_SGRCodeFgRed:
return kDefaultANSIColorFgRed;
case AMR_SGRCodeFgGreen:
return kDefaultANSIColorFgGreen;
case AMR_SGRCodeFgYellow:
return kDefaultANSIColorFgYellow;
case AMR_SGRCodeFgBlue:
return kDefaultANSIColorFgBlue;
case AMR_SGRCodeFgMagenta:
return kDefaultANSIColorFgMagenta;
case AMR_SGRCodeFgCyan:
return kDefaultANSIColorFgCyan;
case AMR_SGRCodeFgWhite:
return kDefaultANSIColorFgWhite;
case AMR_SGRCodeFgBrightBlack:
return kDefaultANSIColorFgBrightBlack;
case AMR_SGRCodeFgBrightRed:
return kDefaultANSIColorFgBrightRed;
case AMR_SGRCodeFgBrightGreen:
return kDefaultANSIColorFgBrightGreen;
case AMR_SGRCodeFgBrightYellow:
return kDefaultANSIColorFgBrightYellow;
case AMR_SGRCodeFgBrightBlue:
return kDefaultANSIColorFgBrightBlue;
case AMR_SGRCodeFgBrightMagenta:
return kDefaultANSIColorFgBrightMagenta;
case AMR_SGRCodeFgBrightCyan:
return kDefaultANSIColorFgBrightCyan;
case AMR_SGRCodeFgBrightWhite:
return kDefaultANSIColorFgBrightWhite;
case AMR_SGRCodeBgBlack:
return kDefaultANSIColorBgBlack;
case AMR_SGRCodeBgRed:
return kDefaultANSIColorBgRed;
case AMR_SGRCodeBgGreen:
return kDefaultANSIColorBgGreen;
case AMR_SGRCodeBgYellow:
return kDefaultANSIColorBgYellow;
case AMR_SGRCodeBgBlue:
return kDefaultANSIColorBgBlue;
case AMR_SGRCodeBgMagenta:
return kDefaultANSIColorBgMagenta;
case AMR_SGRCodeBgCyan:
return kDefaultANSIColorBgCyan;
case AMR_SGRCodeBgWhite:
return kDefaultANSIColorBgWhite;
case AMR_SGRCodeBgBrightBlack:
return kDefaultANSIColorBgBrightBlack;
case AMR_SGRCodeBgBrightRed:
return kDefaultANSIColorBgBrightRed;
case AMR_SGRCodeBgBrightGreen:
return kDefaultANSIColorBgBrightGreen;
case AMR_SGRCodeBgBrightYellow:
return kDefaultANSIColorBgBrightYellow;
case AMR_SGRCodeBgBrightBlue:
return kDefaultANSIColorBgBrightBlue;
case AMR_SGRCodeBgBrightMagenta:
return kDefaultANSIColorBgBrightMagenta;
case AMR_SGRCodeBgBrightCyan:
return kDefaultANSIColorBgBrightCyan;
case AMR_SGRCodeBgBrightWhite:
return kDefaultANSIColorBgBrightWhite;
case AMR_SGRCodeNoneOrInvalid:
case AMR_SGRCodeItalicOn:
case AMR_SGRCodeUnderlineNone:
case AMR_SGRCodeIntensityFaint:
case AMR_SGRCodeAllReset:
case AMR_SGRCodeBgReset:
case AMR_SGRCodeFgReset:
case AMR_SGRCodeIntensityBold:
case AMR_SGRCodeIntensityNormal:
case AMR_SGRCodeUnderlineSingle:
case AMR_SGRCodeUnderlineDouble:
break;
}
return kDefaultANSIColorFgBlack;
}
- (AMR_SGRCode) AMR_SGRCodeForColor:(NSColor*)aColor isForegroundColor:(BOOL)aForeground
{
if (self.ansiColors)
{
NSArray *codesForGivenColor = [self.ansiColors allKeysForObject:aColor];
if (codesForGivenColor != nil && 0 < codesForGivenColor.count)
{
for (NSNumber *thisCode in codesForGivenColor)
{
BOOL thisIsForegroundColor = (thisCode.intValue < 40);
if (aForeground == thisIsForegroundColor)
return thisCode.intValue;
}
}
}
if (aForeground)
{
if ([aColor isEqual:kDefaultANSIColorFgBlack])
return AMR_SGRCodeFgBlack;
else if ([aColor isEqual:kDefaultANSIColorFgRed])
return AMR_SGRCodeFgRed;
else if ([aColor isEqual:kDefaultANSIColorFgGreen])
return AMR_SGRCodeFgGreen;
else if ([aColor isEqual:kDefaultANSIColorFgYellow])
return AMR_SGRCodeFgYellow;
else if ([aColor isEqual:kDefaultANSIColorFgBlue])
return AMR_SGRCodeFgBlue;
else if ([aColor isEqual:kDefaultANSIColorFgMagenta])
return AMR_SGRCodeFgMagenta;
else if ([aColor isEqual:kDefaultANSIColorFgCyan])
return AMR_SGRCodeFgCyan;
else if ([aColor isEqual:kDefaultANSIColorFgWhite])
return AMR_SGRCodeFgWhite;
else if ([aColor isEqual:kDefaultANSIColorFgBrightBlack])
return AMR_SGRCodeFgBrightBlack;
else if ([aColor isEqual:kDefaultANSIColorFgBrightRed])
return AMR_SGRCodeFgBrightRed;
else if ([aColor isEqual:kDefaultANSIColorFgBrightGreen])
return AMR_SGRCodeFgBrightGreen;
else if ([aColor isEqual:kDefaultANSIColorFgBrightYellow])
return AMR_SGRCodeFgBrightYellow;
else if ([aColor isEqual:kDefaultANSIColorFgBrightBlue])
return AMR_SGRCodeFgBrightBlue;
else if ([aColor isEqual:kDefaultANSIColorFgBrightMagenta])
return AMR_SGRCodeFgBrightMagenta;
else if ([aColor isEqual:kDefaultANSIColorFgBrightCyan])
return AMR_SGRCodeFgBrightCyan;
else if ([aColor isEqual:kDefaultANSIColorFgBrightWhite])
return AMR_SGRCodeFgBrightWhite;
}
else
{
if ([aColor isEqual:kDefaultANSIColorBgBlack])
return AMR_SGRCodeBgBlack;
else if ([aColor isEqual:kDefaultANSIColorBgRed])
return AMR_SGRCodeBgRed;
else if ([aColor isEqual:kDefaultANSIColorBgGreen])
return AMR_SGRCodeBgGreen;
else if ([aColor isEqual:kDefaultANSIColorBgYellow])
return AMR_SGRCodeBgYellow;
else if ([aColor isEqual:kDefaultANSIColorBgBlue])
return AMR_SGRCodeBgBlue;
else if ([aColor isEqual:kDefaultANSIColorBgMagenta])
return AMR_SGRCodeBgMagenta;
else if ([aColor isEqual:kDefaultANSIColorBgCyan])
return AMR_SGRCodeBgCyan;
else if ([aColor isEqual:kDefaultANSIColorBgWhite])
return AMR_SGRCodeBgWhite;
else if ([aColor isEqual:kDefaultANSIColorBgBrightBlack])
return AMR_SGRCodeBgBrightBlack;
else if ([aColor isEqual:kDefaultANSIColorBgBrightRed])
return AMR_SGRCodeBgBrightRed;
else if ([aColor isEqual:kDefaultANSIColorBgBrightGreen])
return AMR_SGRCodeBgBrightGreen;
else if ([aColor isEqual:kDefaultANSIColorBgBrightYellow])
return AMR_SGRCodeBgBrightYellow;
else if ([aColor isEqual:kDefaultANSIColorBgBrightBlue])
return AMR_SGRCodeBgBrightBlue;
else if ([aColor isEqual:kDefaultANSIColorBgBrightMagenta])
return AMR_SGRCodeBgBrightMagenta;
else if ([aColor isEqual:kDefaultANSIColorBgBrightCyan])
return AMR_SGRCodeBgBrightCyan;
else if ([aColor isEqual:kDefaultANSIColorBgBrightWhite])
return AMR_SGRCodeBgBrightWhite;
}
return AMR_SGRCodeNoneOrInvalid;
}
// helper struct typedef and a few functions for
// -closestSGRCodeForColor:isForegroundColor:
typedef struct {
CGFloat hue;
CGFloat saturation;
CGFloat brightness;
} AMR_HSB;
AMR_HSB makeHSB(CGFloat hue, CGFloat saturation, CGFloat brightness)
{
AMR_HSB outHSB;
outHSB.hue = hue;
outHSB.saturation = saturation;
outHSB.brightness = brightness;
return outHSB;
}
AMR_HSB getHSBFromColor(NSColor *color)
{
CGFloat hue = 0.0;
CGFloat saturation = 0.0;
CGFloat brightness = 0.0;
[[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]
getHue:&hue
saturation:&saturation
brightness:&brightness
alpha:NULL
];
return makeHSB(hue, saturation, brightness);
}
BOOL floatsEqual(CGFloat first, CGFloat second, CGFloat maxAbsError)
{
return (fabs(first-second)) < maxAbsError;
}
#define MAX_HUE_FLOAT_EQUALITY_ABS_ERROR 0.000001
- (AMR_SGRCode) closestSGRCodeForColor:(NSColor *)color isForegroundColor:(BOOL)foreground
{
if (color == nil)
return AMR_SGRCodeNoneOrInvalid;
AMR_SGRCode closestColorSGRCode = [self AMR_SGRCodeForColor:color isForegroundColor:foreground];
if (closestColorSGRCode != AMR_SGRCodeNoneOrInvalid)
return closestColorSGRCode;
AMR_HSB givenColorHSB = getHSBFromColor(color);
CGFloat closestColorHueDiff = FLT_MAX;
CGFloat closestColorSaturationDiff = FLT_MAX;
CGFloat closestColorBrightnessDiff = FLT_MAX;
// (background SGR codes are +10 from foreground ones:)
NSUInteger AMR_SGRCodeShift = (foreground)?0:10;
NSArray *ansiFgColorCodes = @[
@(AMR_SGRCodeFgBlack+AMR_SGRCodeShift),
@(AMR_SGRCodeFgRed+AMR_SGRCodeShift),
@(AMR_SGRCodeFgGreen+AMR_SGRCodeShift),
@(AMR_SGRCodeFgYellow+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBlue+AMR_SGRCodeShift),
@(AMR_SGRCodeFgMagenta+AMR_SGRCodeShift),
@(AMR_SGRCodeFgCyan+AMR_SGRCodeShift),
@(AMR_SGRCodeFgWhite+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightBlack+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightRed+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightGreen+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightYellow+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightBlue+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightMagenta+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightCyan+AMR_SGRCodeShift),
@(AMR_SGRCodeFgBrightWhite+AMR_SGRCodeShift),
];
for (NSNumber *thisSGRCodeNumber in ansiFgColorCodes)
{
AMR_SGRCode thisSGRCode = thisSGRCodeNumber.intValue;
NSColor *thisColor = [self colorForSGRCode:thisSGRCode];
AMR_HSB thisColorHSB = getHSBFromColor(thisColor);
CGFloat hueDiff = fabs(givenColorHSB.hue - thisColorHSB.hue);
CGFloat saturationDiff = fabs(givenColorHSB.saturation - thisColorHSB.saturation);
CGFloat brightnessDiff = fabs(givenColorHSB.brightness - thisColorHSB.brightness);
// comparison depends on hue, saturation and brightness
// (strictly in that order):
if (!floatsEqual(hueDiff, closestColorHueDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR))
{
if (hueDiff > closestColorHueDiff)
continue;
closestColorSGRCode = thisSGRCode;
closestColorHueDiff = hueDiff;
closestColorSaturationDiff = saturationDiff;
closestColorBrightnessDiff = brightnessDiff;
continue;
}
if (!floatsEqual(saturationDiff, closestColorSaturationDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR))
{
if (saturationDiff > closestColorSaturationDiff)
continue;
closestColorSGRCode = thisSGRCode;
closestColorHueDiff = hueDiff;
closestColorSaturationDiff = saturationDiff;
closestColorBrightnessDiff = brightnessDiff;
continue;
}
if (!floatsEqual(brightnessDiff, closestColorBrightnessDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR))
{
if (brightnessDiff > closestColorBrightnessDiff)
continue;
closestColorSGRCode = thisSGRCode;
closestColorHueDiff = hueDiff;
closestColorSaturationDiff = saturationDiff;
closestColorBrightnessDiff = brightnessDiff;
continue;
}
// If hue (especially hue!), saturation and brightness diffs all
// are equal to some other color, we need to prefer one or the
// other so we'll select the more 'distinctive' color of the
// two (this is *very* subjective, obviously). I basically just
// looked at the hue chart, went through all the points between
// our main ANSI colors and decided which side the middle point
// would lean on. (e.g. the purple color that is exactly between
// the blue and magenta ANSI colors looks more magenta than
// blue to me so I put magenta higher than blue in the list
// below.)
//
// subjective ordering of colors from most to least 'distinctive':
long colorDistinctivenessOrder[6] = {
AMR_SGRCodeFgRed+AMR_SGRCodeShift,
AMR_SGRCodeFgMagenta+AMR_SGRCodeShift,
AMR_SGRCodeFgBlue+AMR_SGRCodeShift,
AMR_SGRCodeFgGreen+AMR_SGRCodeShift,
AMR_SGRCodeFgCyan+AMR_SGRCodeShift,
AMR_SGRCodeFgYellow+AMR_SGRCodeShift
};
for (int i = 0; i < 6; i++)
{
if (colorDistinctivenessOrder[i] == closestColorSGRCode)
break;
else if (colorDistinctivenessOrder[i] == thisSGRCode)
{
closestColorSGRCode = thisSGRCode;
closestColorHueDiff = hueDiff;
closestColorSaturationDiff = saturationDiff;
closestColorBrightnessDiff = brightnessDiff;
}
}
}
return closestColorSGRCode;
}
@end
-34
View File
@@ -1,34 +0,0 @@
//
// CBBlueLightClient.h
// MTMR
//
// Created by Anton Palgunov on 28/08/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef struct {
int hour;
int minute;
} Time;
typedef struct {
Time fromTime;
Time toTime;
} Schedule;
typedef struct {
BOOL active;
BOOL enabled;
BOOL sunSchedulePermitted;
int mode;
Schedule schedule;
unsigned long long disableFlags;
} Status;
@interface CBBlueLightClient: NSObject
- (BOOL) setEnabled: (BOOL)enabled;
- (BOOL) setMode: (int)mode;
- (void) getBlueLightStatus: (Status *)status;
@end
-45
View File
@@ -1,45 +0,0 @@
//
// DeprecatedCarbonAPI.c
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "DeprecatedCarbonAPI.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
CFStringRef kPidKey = CFSTR("pid");
pid_t pidFromASN(void const *asn) {
pid_t pid = -1;
ProcessSerialNumber psn = {kNoProcess, kNoProcess};
if (CFGetTypeID(asn) == _LSASNGetTypeID()) {
_LSASNExtractHighAndLowParts(asn, &psn.highLongOfPSN, &psn.lowLongOfPSN);
CFDictionaryRef processInfo = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
if (processInfo) {
CFNumberRef pidNumber = CFDictionaryGetValue(processInfo, kPidKey);
if (pidNumber) {
CFNumberGetValue(pidNumber, kCFNumberSInt32Type, &pid);
}
CFRelease(processInfo);
}
}
return pid;
}
#pragma GCC diagnostic pop
-27
View File
@@ -1,27 +0,0 @@
//
// DeprecatedCarbonAPI.h
//
// This file is part of TouchDock
// Copyright (C) 2017 Xander Deng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#import <Carbon/Carbon.h>
extern CFArrayRef _LSCopyApplicationArrayInFrontToBackOrder(uint32_t sessionID);
extern void _LSASNExtractHighAndLowParts(void const* asn, UInt32* psnHigh, UInt32* psnLow);
extern CFTypeID _LSASNGetTypeID(void);
pid_t pidFromASN(void const *asn);
-35
View File
@@ -1,35 +0,0 @@
//
// LaunchAtLoginController.h
//
// Copyright 2011 Tomáš Znamenáček
// Copyright 2010 Ben Clark-Robinson
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the ‘Software’),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@import Foundation;
@import CoreServices;
@interface LaunchAtLoginController : NSObject {}
@property(assign) BOOL launchAtLogin;
- (BOOL) willLaunchAtLogin: (NSURL*) itemURL;
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL;
@end
-128
View File
@@ -1,128 +0,0 @@
//
// LaunchAtLoginController.m
//
// Copyright 2011 Tomáš Znamenáček
// Copyright 2010 Ben Clark-Robinson
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the ‘Software’),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "LaunchAtLoginController.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
static NSString *const StartAtLoginKey = @"launchAtLogin";
@interface LaunchAtLoginController ()
@property(assign) LSSharedFileListRef loginItems;
@end
@implementation LaunchAtLoginController
@synthesize loginItems;
#pragma mark Change Observing
void sharedFileListDidChange(LSSharedFileListRef inList, void *context)
{
LaunchAtLoginController *self = (__bridge id) context;
[self willChangeValueForKey:StartAtLoginKey];
[self didChangeValueForKey:StartAtLoginKey];
}
#pragma mark Initialization
- (id) init
{
self = [super init];
loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(),
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (voidPtr)CFBridgingRetain(self));
return self;
}
- (void) dealloc
{
LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(),
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (__bridge void *)(self));
CFRelease(loginItems);
}
#pragma mark Launch List Control
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
LSSharedFileListItemRef copyItemWithURLinFileList(NSURL* wantedURL, LSSharedFileListRef fileList) {
if (wantedURL == NULL || fileList == NULL)
return NULL;
NSArray *listSnapshot = (__bridge_transfer NSArray *)LSSharedFileListCopySnapshot(fileList, NULL);
for(NSUInteger i = 0; i< [listSnapshot count]; i++) {
LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)[listSnapshot objectAtIndex:i];
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
CFURLRef currentItemURL = NULL;
LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
if (currentItemURL && [(__bridge_transfer NSURL*)currentItemURL isEqual:wantedURL]) {
CFRetain(item);
return item;
}
}
return NULL;
}
#pragma clang diagnostic pop
- (BOOL) willLaunchAtLogin: (NSURL*) itemURL
{
return !!copyItemWithURLinFileList(itemURL, loginItems);
}
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL
{
LSSharedFileListItemRef appItem = copyItemWithURLinFileList(itemURL, loginItems);
if (enabled && !appItem) {
LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst,
NULL, NULL, (__bridge CFURLRef)itemURL, NULL, NULL);
} else if (!enabled && appItem) {
LSSharedFileListItemRemove(loginItems, appItem);
}
if (appItem) {
CFRelease(appItem);
}
}
#pragma mark Basic Interface
- (NSURL*) appURL
{
return [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
}
- (void) setLaunchAtLogin: (BOOL) enabled
{
[self willChangeValueForKey:StartAtLoginKey];
[self setLaunchAtLogin:enabled forURL:[self appURL]];
[self didChangeValueForKey:StartAtLoginKey];
}
- (BOOL) launchAtLogin
{
return [self willLaunchAtLogin:[self appURL]];
}
@end
#pragma clang diagnostic pop
@@ -1,27 +0,0 @@
//
// TouchBarPrivateApi-Bridging.h
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
#import "AMR_ANSIEscapeHelper.h"
#import "TouchBarPrivateApi.h"
#import "TouchBarSupport.h"
#import "DeprecatedCarbonAPI.h"
#import "CBBlueLightClient.h"
#import "LaunchAtLoginController.h"
NS_ASSUME_NONNULL_BEGIN
CF_EXPORT CFTypeRef MTActuatorCreateFromDeviceID(UInt64 deviceID);
CF_EXPORT IOReturn MTActuatorOpen(CFTypeRef actuatorRef);
CF_EXPORT IOReturn MTActuatorClose(CFTypeRef actuatorRef);
CF_EXPORT IOReturn MTActuatorActuate(CFTypeRef actuatorRef, SInt32 actuationID, UInt32 arg1, Float32 arg2, Float32 arg3);
CF_EXPORT bool MTActuatorIsOpen(CFTypeRef actuatorRef);
CF_EXPORT void CoreDisplay_Display_SetUserBrightness(int CGDirectDisplayID, double level);
CF_EXPORT double CoreDisplay_Display_GetUserBrightness(int CGDirectDisplayID);
NS_ASSUME_NONNULL_END
-246
View File
@@ -1,246 +0,0 @@
//
// TouchBarItems.swift
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegate {
var tapClosure: (() -> Void)?
var longTapClosure: (() -> Void)? {
didSet {
longClick.isEnabled = longTapClosure != nil
}
}
var finishViewConfiguration: ()->() = {}
private var button: NSButton!
private var singleClick: HapticClickGestureRecognizer!
private var longClick: LongPressGestureRecognizer!
init(identifier: NSTouchBarItem.Identifier, title: String) {
attributedTitle = title.defaultTouchbarAttributedString
super.init(identifier: identifier)
button = CustomHeightButton(title: title, target: nil, action: nil)
longClick = LongPressGestureRecognizer(target: self, action: #selector(handleGestureLong))
longClick.isEnabled = false
longClick.allowedTouchTypes = .direct
longClick.delegate = self
singleClick = HapticClickGestureRecognizer(target: self, action: #selector(handleGestureSingle))
singleClick.allowedTouchTypes = .direct
singleClick.delegate = self
reinstallButton()
button.attributedTitle = attributedTitle
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var isBordered: Bool = true {
didSet {
reinstallButton()
}
}
var backgroundColor: NSColor? {
didSet {
reinstallButton()
}
}
var title: String {
get {
return attributedTitle.string
}
set {
attributedTitle = newValue.defaultTouchbarAttributedString
}
}
var attributedTitle: NSAttributedString {
didSet {
button?.imagePosition = attributedTitle.length > 0 ? .imageLeading : .imageOnly
button?.attributedTitle = attributedTitle
}
}
var image: NSImage? {
didSet {
button.image = image
}
}
private func reinstallButton() {
let title = button.attributedTitle
let image = button.image
let cell = CustomButtonCell(parentItem: self)
button.cell = cell
if let color = backgroundColor {
cell.isBordered = true
button.bezelColor = color
button.bezelStyle = .rounded
cell.backgroundColor = color
} else {
button.isBordered = isBordered
button.bezelStyle = isBordered ? .rounded : .inline
}
button.imageScaling = .scaleProportionallyDown
button.imageHugsTitle = true
button.attributedTitle = title
button?.imagePosition = title.length > 0 ? .imageLeading : .imageOnly
button.image = image
view = button
view.addGestureRecognizer(longClick)
view.addGestureRecognizer(singleClick)
finishViewConfiguration()
}
func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
if gestureRecognizer == singleClick && otherGestureRecognizer == longClick
|| gestureRecognizer == longClick && otherGestureRecognizer == singleClick // need it
{
return false
}
return true
}
@objc func handleGestureSingle(gr: NSClickGestureRecognizer) {
switch gr.state {
case .ended:
tapClosure?()
break
default:
break
}
}
@objc func handleGestureLong(gr: NSPressGestureRecognizer) {
switch gr.state {
case .possible: // tiny hack because we're calling action manually
(self.longTapClosure ?? self.tapClosure)?()
break
default:
break
}
}
}
class CustomHeightButton: NSButton {
override var intrinsicContentSize: NSSize {
var size = super.intrinsicContentSize
size.height = 30
return size
}
}
class CustomButtonCell: NSButtonCell {
weak var parentItem: CustomButtonTouchBarItem?
init(parentItem: CustomButtonTouchBarItem) {
super.init(textCell: "")
self.parentItem = parentItem
}
override func highlight(_ flag: Bool, withFrame cellFrame: NSRect, in controlView: NSView) {
super.highlight(flag, withFrame: cellFrame, in: controlView)
if !isBordered {
if flag {
setAttributedTitle(attributedTitle, withColor: .lightGray)
} else if let parentItem = self.parentItem {
attributedTitle = parentItem.attributedTitle
}
}
}
override func drawingRect(forBounds rect: NSRect) -> NSRect {
return rect // need that so content may better fit in button with very limited width
}
required init(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setAttributedTitle(_ title: NSAttributedString, withColor color: NSColor) {
let attrTitle = NSMutableAttributedString(attributedString: title)
attrTitle.addAttributes([.foregroundColor: color], range: NSRange(location: 0, length: attrTitle.length))
attributedTitle = attrTitle
}
}
class HapticClickGestureRecognizer: NSClickGestureRecognizer {
override func touchesBegan(with event: NSEvent) {
HapticFeedback.shared?.tap(strong: 2)
super.touchesBegan(with: event)
}
override func touchesEnded(with event: NSEvent) {
HapticFeedback.shared?.tap(strong: 1)
super.touchesEnded(with: event)
}
}
class LongPressGestureRecognizer: NSPressGestureRecognizer {
var recognizeTimeout = 0.4
private var timer: Timer?
override func touchesBegan(with event: NSEvent) {
timerInvalidate()
let touches = event.touches(for: self.view!)
if touches.count == 1 { // to prevent it for built-in two/three-finger gestures
timer = Timer.scheduledTimer(timeInterval: recognizeTimeout, target: self, selector: #selector(self.onTimer), userInfo: nil, repeats: false)
}
super.touchesBegan(with: event)
}
override func touchesMoved(with event: NSEvent) {
timerInvalidate() // to prevent it for built-in two/three-finger gestures
super.touchesMoved(with: event)
}
override func touchesCancelled(with event: NSEvent) {
timerInvalidate()
super.touchesCancelled(with: event)
}
override func touchesEnded(with event: NSEvent) {
timerInvalidate()
super.touchesEnded(with: event)
}
private func timerInvalidate() {
if let timer = timer {
timer.invalidate()
self.timer = nil
}
}
@objc private func onTimer() {
if let target = self.target, let action = self.action {
target.performSelector(onMainThread: action, with: self, waitUntilDone: false)
HapticFeedback.shared?.tap(strong: 6)
}
}
deinit {
timerInvalidate()
}
}
extension String {
var defaultTouchbarAttributedString: NSAttributedString {
let attrTitle = NSMutableAttributedString(string: self, attributes: [.foregroundColor: NSColor.white, .font: NSFont.systemFont(ofSize: 15, weight: .regular), .baselineOffset: 1])
attrTitle.setAlignment(.center, range: NSRange(location: 0, length: count))
return attrTitle
}
}
-107
View File
@@ -1,107 +0,0 @@
//
// CustomSlider.swift
// MTMR
//
// Created by Anton Palgunov on 15/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
class CustomSliderCell: NSSliderCell {
var knobImage: NSImage!
private var _currentKnobRect: NSRect!
private var _barRect: NSRect!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
init(knob: NSImage?) {
knobImage = knob
super.init()
}
override func drawKnob(_ knobRect: NSRect) {
if knobImage == nil {
super.drawKnob(knobRect)
return
}
_currentKnobRect = knobRect
drawBar(inside: _barRect, flipped: true)
let x = (knobRect.origin.x * (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width) + 1
let y = knobRect.origin.y + 3
knobImage.draw(
at: NSPoint(x: x, y: y),
from: NSZeroRect,
operation: NSCompositingOperation.sourceOver,
fraction: 1
)
}
override func drawBar(inside aRect: NSRect, flipped _: Bool) {
_barRect = aRect
let barRadius = CGFloat(2)
var bgRect = aRect
bgRect.size.height = CGFloat(4)
let bg = NSBezierPath(roundedRect: bgRect, xRadius: barRadius, yRadius: barRadius)
NSColor.lightGray.setFill()
bg.fill()
var activeRect = bgRect
activeRect.size.width = CGFloat((Double(bgRect.size.width) / (maxValue - minValue)) * doubleValue)
let active = NSBezierPath(roundedRect: activeRect, xRadius: barRadius, yRadius: barRadius)
NSColor.darkGray.setFill()
active.fill()
}
}
class CustomSlider: NSSlider {
var currentValue: CGFloat = 0
override func setNeedsDisplay(_ invalidRect: NSRect) {
super.setNeedsDisplay(invalidRect)
}
override func awakeFromNib() {
super.awakeFromNib()
if (cell?.isKind(of: CustomSliderCell.self)) == false {
let cell: CustomSliderCell = CustomSliderCell()
self.cell = cell
}
}
convenience init(knob: NSImage) {
self.init()
cell = CustomSliderCell(knob: knob)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
func knobImage() -> NSImage {
let cell = self.cell as! CustomSliderCell
return cell.knobImage
}
func setKnobImage(image: NSImage) {
let cell = self.cell as! CustomSliderCell
cell.knobImage = image
}
}
-17
View File
@@ -1,17 +0,0 @@
import Foundation
#if swift(>=4.1)
// compactMap supported
#else
extension Sequence {
func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
return try flatMap(transform)
}
}
#endif
extension String {
var ifNotEmpty: String? {
return count > 0 ? self : nil
}
}
-89
View File
@@ -1,89 +0,0 @@
//
// HapticFeedback.swift
// MTMR
//
// Created by Anton Palgunov on 09/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import IOKit
class HapticFeedback {
static var shared: HapticFeedback?
// Here we have list of possible IDs for Haptic Generator Device. They are not constant
// To find deviceID, you will need IORegistryExplorer app from Additional Tools for Xcode dmg
// which you can download from https://developer.apple.com/download/more/?=Additional%20Tools
// Open IORegistryExplorer app, search for AppleMultitouchDevice and get "Multitouch ID"
// There should be programmatic way to get it but I can't find, no docs for macOS :(
private let possibleDeviceIDs: [UInt64] = [
0x200_0000_0100_0000, // MacBook Pro 2016/2017
0x300000080500000 // MacBook Pro 2019 (possibly 2018 as well)
]
private var correctDeviceID: UInt64?
private var actuatorRef: CFTypeRef?
init() {
recreateDevice()
}
// Don't know how to do strong is enum one of
// 1 like back Click
// 2 like Click
// 3 week
// 4 medium
// 5 week medium
// 6 strong
// 15 nothing
// 16 nothing
// you can get a plist `otool -s __TEXT __tpad_act_plist /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/Current/MultitouchSupport|tail -n +3|awk -F'\t' '{print $2}'|xxd -r -p`
func tap(strong: Int32) {
guard correctDeviceID != nil, actuatorRef != nil else {
print("guard actuatorRef == nil (no haptic device found?)")
return
}
var result: IOReturn
result = MTActuatorOpen(actuatorRef!)
guard result == kIOReturnSuccess else {
print("guard MTActuatorOpen")
recreateDevice()
return
}
result = MTActuatorActuate(actuatorRef!, strong, 0, 0, 0)
guard result == kIOReturnSuccess else {
print("guard MTActuatorActuate")
return
}
result = MTActuatorClose(actuatorRef!)
guard result == kIOReturnSuccess else {
print("guard MTActuatorClose")
return
}
}
private func recreateDevice() {
if let actuatorRef = actuatorRef {
MTActuatorClose(actuatorRef)
self.actuatorRef = nil // just in case %)
}
if let correctDeviceID = correctDeviceID {
actuatorRef = MTActuatorCreateFromDeviceID(correctDeviceID).takeRetainedValue()
} else {
// Let's find our Haptic device
possibleDeviceIDs.forEach {(deviceID) in
guard correctDeviceID == nil else {return}
actuatorRef = MTActuatorCreateFromDeviceID(deviceID).takeRetainedValue()
if actuatorRef != nil {
correctDeviceID = deviceID
}
}
}
}
}
+3 -39
View File
@@ -17,52 +17,16 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.26</string>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>401</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<true/>
<key>NSAppleEventsUsageDescription</key>
<string>AppleEvents needed for correct work AppleScript</string>
<key>NSAppleMusicUsageDescription</key>
<string>MTMR needs access to Media for work</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>MTMR needs access to Bluetooth for work</string>
<key>NSCalendarsUsageDescription</key>
<string>MTMR needs access to Calendar for work</string>
<key>NSCameraUsageDescription</key>
<string>MTMR needs access to Camera for work</string>
<key>NSHomeKitUsageDescription</key>
<string>MTMR needs access to HomeKit for work</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 - 2020 Anton Palgunov. All rights reserved.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Weather widget need your location for correct work</string>
<string>Copyright © 2018 Anton Palgunov. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>MTMR needs access to Photo for work</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSRemindersUsageDescription</key>
<string>MTMR needs access to Reminders for work</string>
<key>NSSystemAdministrationUsageDescription</key>
<string>MTMR needs access to Administation for work</string>
<key>SUFeedURL</key>
<string>https://mtmr.app/appcast.xml</string>
<key>SUPublicDSAKeyFile</key>
<string>dsa_pub.pem</string>
<key>kTCCServiceMediaLibrary</key>
<string>MTMR needs access to Music for work</string>
</dict>
</plist>
-660
View File
@@ -1,660 +0,0 @@
import AppKit
import Foundation
extension Data {
func barItemDefinitions() -> [BarItemDefinition]? {
return try? JSONDecoder().decode([BarItemDefinition].self, from: utf8string!.stripComments().data(using: .utf8)!)
}
}
struct BarItemDefinition: Decodable {
let type: ItemType
let action: ActionType
let longAction: LongActionType
let additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]
private enum CodingKeys: String, CodingKey {
case type
}
init(type: ItemType, action: ActionType, longAction: LongActionType, additionalParameters: [GeneralParameters.CodingKeys: GeneralParameter]) {
self.type = type
self.action = action
self.longAction = longAction
self.additionalParameters = additionalParameters
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type)
var additionalParameters = try GeneralParameters(from: decoder).parameters
if let result = try? parametersDecoder(decoder),
case let (itemType, action, longAction, parameters) = result {
parameters.forEach { additionalParameters[$0] = $1 }
self.init(type: itemType, action: action, longAction: longAction, additionalParameters: additionalParameters)
} else {
self.init(type: .staticButton(title: "unknown"), action: .none, longAction: .none, additionalParameters: additionalParameters)
}
}
}
typealias ParametersDecoder = (Decoder) throws -> (
item: ItemType,
action: ActionType,
longAction: LongActionType,
parameters: [GeneralParameters.CodingKeys: GeneralParameter]
)
class SupportedTypesHolder {
private var supportedTypes: [String: ParametersDecoder] = [
"escape": { _ in (
item: .staticButton(title: "esc"),
action: .keyPress(keycode: 53),
longAction: .none,
parameters: [.align: .align(.left)]
) },
"delete": { _ in (
item: .staticButton(title: "del"),
action: .keyPress(keycode: 117),
longAction: .none,
parameters: [:]
) },
"brightnessUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessUp"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 144),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"brightnessDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "brightnessDown"))
return (
item: .staticButton(title: ""),
action: .keyPress(keycode: 145),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationUp": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_up"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"illuminationDown": { _ in
let imageParameter = GeneralParameter.image(source: #imageLiteral(resourceName: "ill_down"))
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_ILLUMINATION_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeDown": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeDownTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_DOWN),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"volumeUp": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarVolumeUpTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_SOUND_UP),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"mute": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarAudioOutputMuteTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_MUTE),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"previous": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarRewindTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PREVIOUS),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"play": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarPlayPauseTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_PLAY),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"next": { _ in
let imageParameter = GeneralParameter.image(source: NSImage(named: NSImage.touchBarFastForwardTemplateName)!)
return (
item: .staticButton(title: ""),
action: .hidKey(keycode: NX_KEYTYPE_NEXT),
longAction: .none,
parameters: [.image: imageParameter]
)
},
"sleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["sleepnow"]),
longAction: .none,
parameters: [:]
) },
"displaySleep": { _ in (
item: .staticButton(title: "☕️"),
action: .shellScript(executable: "/usr/bin/pmset", parameters: ["displaysleepnow"]),
longAction: .none,
parameters: [:]
) },
]
static let sharedInstance = SupportedTypesHolder()
func lookup(by type: String) -> ParametersDecoder {
return supportedTypes[type] ?? { decoder in (
item: try ItemType(from: decoder),
action: try ActionType(from: decoder),
longAction: try LongActionType(from: decoder),
parameters: [:]
) }
}
func register(typename: String, decoder: @escaping ParametersDecoder) {
supportedTypes[typename] = decoder
}
func register(typename: String, item: ItemType, action: ActionType, longAction: LongActionType) {
register(typename: typename) { _ in
(
item: item,
action,
longAction,
parameters: [:]
)
}
}
}
enum ItemType: Decodable {
case staticButton(title: String)
case appleScriptTitledButton(source: SourceProtocol, refreshInterval: Double, alternativeImages: [String: SourceProtocol])
case shellScriptTitledButton(source: SourceProtocol, refreshInterval: Double)
case timeButton(formatTemplate: String, timeZone: String?, locale: String?)
case battery
case dock(autoResize: Bool, filter: String?)
case volume
case brightness(refreshInterval: Double)
case weather(interval: Double, units: String, api_key: String, icon_type: String)
case yandexWeather(interval: Double)
case currency(interval: Double, from: String, to: String, full: Bool)
case inputsource
case music(interval: Double, disableMarquee: Bool)
case group(items: [BarItemDefinition])
case nightShift
case dnd
case pomodoro(workTime: Double, restTime: Double)
case network(flip: Bool)
case darkMode
case swipe(direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?)
private enum CodingKeys: String, CodingKey {
case type
case title
case source
case refreshInterval
case from
case to
case full
case timeZone
case units
case api_key
case icon_type
case formatTemplate
case locale
case image
case url
case longUrl
case items
case workTime
case restTime
case flip
case autoResize
case filter
case disableMarquee
case alternativeImages
case sourceApple
case sourceBash
case direction
case fingers
case minOffset
}
enum ItemTypeRaw: String, Decodable {
case staticButton
case appleScriptTitledButton
case shellScriptTitledButton
case timeButton
case battery
case dock
case volume
case brightness
case weather
case yandexWeather
case currency
case inputsource
case music
case group
case nightShift
case dnd
case pomodoro
case network
case darkMode
case swipe
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ItemTypeRaw.self, forKey: .type)
switch type {
case .appleScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
let alternativeImages = try container.decodeIfPresent([String: Source].self, forKey: .alternativeImages) ?? [:]
self = .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages)
case .shellScriptTitledButton:
let source = try container.decode(Source.self, forKey: .source)
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .shellScriptTitledButton(source: source, refreshInterval: interval)
case .staticButton:
let title = try container.decode(String.self, forKey: .title)
self = .staticButton(title: title)
case .timeButton:
let template = try container.decodeIfPresent(String.self, forKey: .formatTemplate) ?? "HH:mm"
let timeZone = try container.decodeIfPresent(String.self, forKey: .timeZone) ?? nil
let locale = try container.decodeIfPresent(String.self, forKey: .locale) ?? nil
self = .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale)
case .battery:
self = .battery
case .dock:
let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false
let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter)
self = .dock(autoResize: autoResize, filter: filterRegexString)
case .volume:
self = .volume
case .brightness:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 0.5
self = .brightness(refreshInterval: interval)
case .weather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
let units = try container.decodeIfPresent(String.self, forKey: .units) ?? "metric"
let api_key = try container.decodeIfPresent(String.self, forKey: .api_key) ?? "32c4256d09a4c52b38aecddba7a078f6"
let icon_type = try container.decodeIfPresent(String.self, forKey: .icon_type) ?? "text"
self = .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case .yandexWeather:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 1800.0
self = .yandexWeather(interval: interval)
case .currency:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 600.0
let from = try container.decodeIfPresent(String.self, forKey: .from) ?? "RUB"
let to = try container.decodeIfPresent(String.self, forKey: .to) ?? "USD"
let full = try container.decodeIfPresent(Bool.self, forKey: .full) ?? false
self = .currency(interval: interval, from: from, to: to, full: full)
case .inputsource:
self = .inputsource
case .music:
let interval = try container.decodeIfPresent(Double.self, forKey: .refreshInterval) ?? 5.0
let disableMarquee = try container.decodeIfPresent(Bool.self, forKey: .disableMarquee) ?? false
self = .music(interval: interval, disableMarquee: disableMarquee)
case .group:
let items = try container.decode([BarItemDefinition].self, forKey: .items)
self = .group(items: items)
case .nightShift:
self = .nightShift
case .dnd:
self = .dnd
case .pomodoro:
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime) ?? 1500.0
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime) ?? 600.0
self = .pomodoro(workTime: workTime, restTime: restTime)
case .network:
let flip = try container.decodeIfPresent(Bool.self, forKey: .flip) ?? false
self = .network(flip: flip)
case .darkMode:
self = .darkMode
case .swipe:
let sourceApple = try container.decodeIfPresent(Source.self, forKey: .sourceApple)
let sourceBash = try container.decodeIfPresent(Source.self, forKey: .sourceBash)
let direction = try container.decode(String.self, forKey: .direction)
let fingers = try container.decode(Int.self, forKey: .fingers)
let minOffset = try container.decodeIfPresent(Float.self, forKey: .minOffset) ?? 0.0
self = .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
}
}
}
enum ActionType: Decodable {
case none
case hidKey(keycode: Int32)
case keyPress(keycode: Int)
case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: () -> Void)
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case action
case keycode
case actionAppleScript
case executablePath
case shellArguments
case url
}
private enum ActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action)
switch type {
case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .keycode)
self = .hidKey(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .keycode)
self = .keyPress(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .actionAppleScript)
self = .appleScript(source: source)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .executablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .shellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters)
case .some(.openUrl):
let url = try container.decode(String.self, forKey: .url)
self = .openUrl(url: url)
case .none:
self = .none
}
}
}
enum LongActionType: Decodable {
case none
case hidKey(keycode: Int32)
case keyPress(keycode: Int)
case appleScript(source: SourceProtocol)
case shellScript(executable: String, parameters: [String])
case custom(closure: () -> Void)
case openUrl(url: String)
private enum CodingKeys: String, CodingKey {
case longAction
case longKeycode
case longActionAppleScript
case longExecutablePath
case longShellArguments
case longUrl
}
private enum LongActionTypeRaw: String, Decodable {
case hidKey
case keyPress
case appleScript
case shellScript
case openUrl
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let longType = try container.decodeIfPresent(LongActionTypeRaw.self, forKey: .longAction)
switch longType {
case .some(.hidKey):
let keycode = try container.decode(Int32.self, forKey: .longKeycode)
self = .hidKey(keycode: keycode)
case .some(.keyPress):
let keycode = try container.decode(Int.self, forKey: .longKeycode)
self = .keyPress(keycode: keycode)
case .some(.appleScript):
let source = try container.decode(Source.self, forKey: .longActionAppleScript)
self = .appleScript(source: source)
case .some(.shellScript):
let executable = try container.decode(String.self, forKey: .longExecutablePath)
let parameters = try container.decodeIfPresent([String].self, forKey: .longShellArguments) ?? []
self = .shellScript(executable: executable, parameters: parameters)
case .some(.openUrl):
let longUrl = try container.decode(String.self, forKey: .longUrl)
self = .openUrl(url: longUrl)
case .none:
self = .none
}
}
}
enum GeneralParameter {
case width(_: CGFloat)
case image(source: SourceProtocol)
case align(_: Align)
case bordered(_: Bool)
case background(_: NSColor)
case title(_: String)
}
struct GeneralParameters: Decodable {
let parameters: [GeneralParameters.CodingKeys: GeneralParameter]
enum CodingKeys: String, CodingKey {
case width
case image
case align
case bordered
case background
case title
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var result: [GeneralParameters.CodingKeys: GeneralParameter] = [:]
if let value = try container.decodeIfPresent(CGFloat.self, forKey: .width) {
result[.width] = .width(value)
}
if let imageSource = try container.decodeIfPresent(Source.self, forKey: .image) {
result[.image] = .image(source: imageSource)
}
let align = try container.decodeIfPresent(Align.self, forKey: .align) ?? .center
result[.align] = .align(align)
if let borderedFlag = try container.decodeIfPresent(Bool.self, forKey: .bordered) {
result[.bordered] = .bordered(borderedFlag)
}
if let backgroundColor = try container.decodeIfPresent(String.self, forKey: .background)?.hexColor {
result[.background] = .background(backgroundColor)
}
if let title = try container.decodeIfPresent(String.self, forKey: .title) {
result[.title] = .title(title)
}
parameters = result
}
}
protocol SourceProtocol {
var data: Data? { get }
var string: String? { get }
var image: NSImage? { get }
var appleScript: NSAppleScript? { get }
}
struct Source: Decodable, SourceProtocol {
let filePath: String?
let base64: String?
let inline: String?
private enum CodingKeys: String, CodingKey {
case filePath
case base64
case inline
}
var data: Data? {
return base64?.base64Data ?? inline?.data(using: .utf8) ?? filePath?.fileData
}
var string: String? {
return inline ?? filePath?.fileString
}
var image: NSImage? {
return data?.image
}
var appleScript: NSAppleScript? {
return filePath?.fileURL.appleScript ?? string?.appleScript
}
private init(filePath: String?, base64: String?, inline: String?) {
self.filePath = filePath
self.base64 = base64
self.inline = inline
}
init(filePath: String) {
self.init(filePath: filePath, base64: nil, inline: nil)
}
}
extension NSImage: SourceProtocol {
var data: Data? {
return nil
}
var string: String? {
return nil
}
var image: NSImage? {
return self
}
var appleScript: NSAppleScript? {
return nil
}
}
extension String {
var base64Data: Data? {
return Data(base64Encoded: self)
}
var fileData: Data? {
return try? Data(contentsOf: URL(fileURLWithPath: (self as NSString).expandingTildeInPath))
}
var fileString: String? {
var encoding: String.Encoding = .utf8
return try? String(contentsOf: URL(fileURLWithPath: (self as NSString).expandingTildeInPath), usedEncoding: &encoding)
}
var fileURL: URL {
return URL(fileURLWithPath: (self as NSString).expandingTildeInPath)
}
var appleScript: NSAppleScript? {
return NSAppleScript(source: self)
}
}
extension Data {
var utf8string: String? {
return String(data: self, encoding: .utf8)
}
var image: NSImage? {
return NSImage(data: self)?.resize(maxSize: NSSize(width: 24, height: 24))
}
}
enum Align: String, Decodable {
case left
case center
case right
}
extension URL {
var appleScript: NSAppleScript? {
guard FileManager.default.fileExists(atPath: path) else { return nil }
return NSAppleScript(contentsOf: self, error: nil)
}
}
+53 -8
View File
@@ -13,12 +13,8 @@ protocol KeyPress {
func send()
}
struct GenericKeyPress: KeyPress {
var keyCode: CGKeyCode
}
extension KeyPress {
func send() {
func send () {
let src = CGEventSource(stateID: .hidSystemState)
let keyDown = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: true)
let keyUp = CGEvent(keyboardEventSource: src, virtualKey: keyCode, keyDown: false)
@@ -29,7 +25,56 @@ extension KeyPress {
}
}
func HIDPostAuxKey(_ key: Int32) {
let key = UInt8(key)
MediaKeys.hidPostAuxKey(key)
struct ESCKeyPress: KeyPress {
let keyCode: CGKeyCode = 53
}
struct BrightnessUpPress: KeyPress {
let keyCode: CGKeyCode = 107
}
struct BrightnessDownPress: KeyPress {
let keyCode: CGKeyCode = 113
}
func doKey(_ key: Int, down: Bool) {
let flags = NSEvent.ModifierFlags(rawValue: down ? 0xa00 : 0xb00)
let data1 = (key << 16) | ((down ? 0xa : 0xb) << 8)
let ev = NSEvent.otherEvent(
with: NSEvent.EventType.systemDefined,
location: NSPoint(x:0.0, y:0.0),
modifierFlags: flags,
timestamp: TimeInterval(0),
windowNumber: 0,
context: nil,
// context: 0,
subtype: 8,
data1: data1,
data2: -1
)
let cev = ev!.cgEvent!
cev.post(tap: CGEventTapLocation(rawValue: 0)!)
}
func HIDPostAuxKey(_ key: Int) {
doKey(key, down: true)
doKey(key, down: false)
}
// hidsystem/ev_keymap.h
let NX_KEYTYPE_SOUND_UP = 0
let NX_KEYTYPE_SOUND_DOWN = 1
let NX_KEYTYPE_BRIGHTNESS_UP = 2
let NX_KEYTYPE_BRIGHTNESS_DOWN = 3
let NX_KEYTYPE_PLAY = 16
let NX_KEYTYPE_NEXT = 17
let NX_KEYTYPE_PREVIOUS = 18
-20
View File
@@ -1,20 +0,0 @@
import Foundation
class ScrollViewItem: NSCustomTouchBarItem/*, NSGestureRecognizerDelegate*/ {
init(identifier: NSTouchBarItem.Identifier, items: [NSTouchBarItem]) {
super.init(identifier: identifier)
let views = items.compactMap { $0.view }
let stackView = NSStackView(views: views)
stackView.spacing = 1
stackView.orientation = .horizontal
let scrollView = NSScrollView(frame: CGRect(origin: .zero, size: stackView.fittingSize))
scrollView.documentView = stackView
view = scrollView
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
-88
View File
@@ -1,88 +0,0 @@
//
// ShellScriptTouchBarItem.swift
// MTMR
//
// Created by bobr on 08/08/2019.
// Copyright © 2019 Anton Palgunov. All rights reserved.
//
import Foundation
class ShellScriptTouchBarItem: CustomButtonTouchBarItem {
private let interval: TimeInterval
private let source: String
private var forceHideConstraint: NSLayoutConstraint!
init?(identifier: NSTouchBarItem.Identifier, source: SourceProtocol, interval: TimeInterval) {
self.interval = interval
self.source = source.string ?? "echo No \"source\""
super.init(identifier: identifier, title: "⏳")
forceHideConstraint = view.widthAnchor.constraint(equalToConstant: 0)
DispatchQueue.shellScriptQueue.async {
self.refreshAndSchedule()
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refreshAndSchedule() {
// Execute script and get result
let scriptResult = execute(source)
// Apply returned text attributes (if they were returned) to our result string
let helper = AMR_ANSIEscapeHelper.init()
helper.defaultStringColor = NSColor.white
helper.font = "1".defaultTouchbarAttributedString.attribute(.font, at: 0, effectiveRange: nil) as? NSFont
let title = NSMutableAttributedString.init(attributedString: helper.attributedString(withANSIEscapedString: scriptResult) ?? NSAttributedString(string: ""))
title.addAttributes([.baselineOffset: 1], range: NSRange(location: 0, length: title.length))
let newBackgoundColor: NSColor? = title.length != 0 ? title.attribute(.backgroundColor, at: 0, effectiveRange: nil) as? NSColor : nil
// Update UI
DispatchQueue.main.async { [weak self, newBackgoundColor] in
if (newBackgoundColor != self?.backgroundColor) { // performance optimization because of reinstallButton
self?.backgroundColor = newBackgoundColor
}
self?.attributedTitle = title
self?.forceHideConstraint.isActive = scriptResult == ""
}
// Schedule next update
DispatchQueue.shellScriptQueue.asyncAfter(deadline: .now() + interval) { [weak self] in
self?.refreshAndSchedule()
}
}
func execute(_ command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
// kill process if it is over update interval
DispatchQueue.main.asyncAfter(deadline: .now() + interval) { [weak task] in
task?.terminate()
}
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
var output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as String? ?? ""
//always wait until task end or you can catch "task still running" error while accessing task.terminationStatus variable
task.waitUntilExit()
if (output == "" && task.terminationStatus != 0) {
output = "error"
}
return output.replacingOccurrences(of: "\\n+$", with: "", options: .regularExpression)
}
}
extension DispatchQueue {
static let shellScriptQueue = DispatchQueue(label: "mtmr.shellscript")
}
-104
View File
@@ -1,104 +0,0 @@
//
// SupportHelpers.swift
// MTMR
//
// Created by Anton Palgunov on 13/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import AppKit
import Foundation
extension String {
func trim() -> String {
return trimmingCharacters(in: NSCharacterSet.whitespaces)
}
func stripComments() -> String {
// ((\s|,)\/\*[\s\S]*?\*\/)|(( |, ")\/\/.*)
return replacingOccurrences(of: "((\\s|,)\\/\\*[\\s\\S]*?\\*\\/)|(( |, \\\")\\/\\/.*)", with: "", options: .regularExpression)
}
var hexColor: NSColor? {
let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.count {
case 3: // RGB (12-bit)
(r, g, b, a) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17, 255)
case 6: // RGB (24-bit)
(r, g, b, a) = (int >> 16, int >> 8 & 0xFF, int & 0xFF, 255)
case 8: // ARGB (32-bit)
(r, g, b, a) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
return nil
}
return NSColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
}
}
extension NSImage {
func resize(maxSize: NSSize) -> NSImage {
var ratio: Float = 0.0
let imageWidth = Float(size.width)
let imageHeight = Float(size.height)
let maxWidth = Float(maxSize.width)
let maxHeight = Float(maxSize.height)
// Get ratio (landscape or portrait)
if imageWidth > imageHeight {
// Landscape
ratio = maxWidth / imageWidth
} else {
// Portrait
ratio = maxHeight / imageHeight
}
// Calculate new size based on the ratio
let newWidth = imageWidth * ratio
let newHeight = imageHeight * ratio
// Create a new NSSize object with the newly calculated size
let newSize: NSSize = NSSize(width: Int(newWidth), height: Int(newHeight))
// Cast the NSImage to a CGImage
var imageRect: NSRect = NSMakeRect(0, 0, size.width, size.height)
let imageRef = cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
// Create NSImage from the CGImage using the new size
let imageWithNewSize = NSImage(cgImage: imageRef!, size: newSize)
// Return the new image
return imageWithNewSize
}
func rotateByDegreess(degrees: CGFloat) -> NSImage {
var imageBounds = NSZeroRect; imageBounds.size = size
let pathBounds = NSBezierPath(rect: imageBounds)
var transform = NSAffineTransform()
transform.rotate(byDegrees: degrees)
pathBounds.transform(using: transform as AffineTransform)
let rotatedBounds: NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y, size.width, size.height)
let rotatedImage = NSImage(size: rotatedBounds.size)
// Center the image within the rotated bounds
imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2)
imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2)
// Start a new transform
transform = NSAffineTransform()
// Move coordinate system to the center (since we want to rotate around the center)
transform.translateX(by: +(NSWidth(rotatedBounds) / 2), yBy: +(NSHeight(rotatedBounds) / 2))
transform.rotate(byDegrees: degrees)
// Move the coordinate system bak to normal
transform.translateX(by: -(NSWidth(rotatedBounds) / 2), yBy: -(NSHeight(rotatedBounds) / 2))
// Draw the original image, rotated, into the new image
rotatedImage.lockFocus()
transform.concat()
draw(in: imageBounds, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
rotatedImage.unlockFocus()
return rotatedImage
}
}
-31
View File
@@ -1,31 +0,0 @@
//
// ExtendNSTouchBar.swift
// MTMR
//
// Created by Anton Palgunov on 07/06/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
func presentSystemModal(_ touchBar: NSTouchBar!, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
if #available(OSX 10.14, *) {
NSTouchBar.presentSystemModalTouchBar(touchBar, systemTrayItemIdentifier: identifier)
} else {
NSTouchBar.presentSystemModalFunctionBar(touchBar, systemTrayItemIdentifier: identifier)
}
}
func presentSystemModal(_ touchBar: NSTouchBar!, placement: Int64, systemTrayItemIdentifier identifier: NSTouchBarItem.Identifier!) {
if #available(OSX 10.14, *) {
NSTouchBar.presentSystemModalTouchBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
} else {
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: placement, systemTrayItemIdentifier: identifier)
}
}
func minimizeSystemModal(_ touchBar: NSTouchBar!) {
if #available(OSX 10.14, *) {
NSTouchBar.minimizeSystemModalTouchBar(touchBar)
} else {
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
}
}
-66
View File
@@ -1,66 +0,0 @@
//
// SwipeItem.swift
// MTMR
//
// Created by Fedor Zaitsev on 3/29/20.
// Copyright © 2020 Anton Palgunov. All rights reserved.
//
import Foundation
import Foundation
class SwipeItem: NSCustomTouchBarItem {
private var scriptApple: NSAppleScript?
private var scriptBash: String?
private var direction: String
private var fingers: Int
private var minOffset: Float
init?(identifier: NSTouchBarItem.Identifier, direction: String, fingers: Int, minOffset: Float, sourceApple: SourceProtocol?, sourceBash: SourceProtocol?) {
self.direction = direction
self.fingers = fingers
self.scriptBash = sourceBash?.string
self.scriptApple = sourceApple?.appleScript
self.minOffset = minOffset
super.init(identifier: identifier)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func processEvent(offset: CGFloat, fingers: Int) {
if direction == "right" && Float(offset) > self.minOffset && self.fingers == fingers {
self.execute()
}
if direction == "left" && Float(offset) < -self.minOffset && self.fingers == fingers {
self.execute()
}
}
func execute() {
if scriptApple != nil {
DispatchQueue.appleScriptQueue.async {
var error: NSDictionary?
self.scriptApple?.executeAndReturnError(&error)
if let error = error {
print("SwipeItem apple script error: \(error)")
return
}
}
}
if scriptBash != nil {
DispatchQueue.shellScriptQueue.async {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["-c", self.scriptBash!]
task.launch()
task.waitUntilExit()
if (task.terminationStatus != 0) {
print("SwipeItem bash script error. Status: \(task.terminationStatus)")
}
}
}
}
}
+132 -387
View File
@@ -8,207 +8,48 @@
import Cocoa
struct ExactItem {
let identifier: NSTouchBarItem.Identifier
let presetItem: BarItemDefinition
}
let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!.appending("/MTMR")
let standardConfigPath = appSupportDirectory.appending("/items.json")
extension ItemType {
var identifierBase: String {
switch self {
case .staticButton(title: _):
return "com.toxblh.mtmr.staticButton."
case .appleScriptTitledButton(source: _):
return "com.toxblh.mtmr.appleScriptButton."
case .shellScriptTitledButton(source: _):
return "com.toxblh.mtmr.shellScriptButton."
case .timeButton(formatTemplate: _, timeZone: _, locale: _):
return "com.toxblh.mtmr.timeButton."
case .battery:
return "com.toxblh.mtmr.battery."
case .dock(autoResize: _, filter: _):
return "com.toxblh.mtmr.dock"
case .volume:
return "com.toxblh.mtmr.volume"
case .brightness(refreshInterval: _):
return "com.toxblh.mtmr.brightness"
case .weather(interval: _, units: _, api_key: _, icon_type: _):
return "com.toxblh.mtmr.weather"
case .yandexWeather(interval: _):
return "com.toxblh.mtmr.yandexWeather"
case .currency(interval: _, from: _, to: _, full: _):
return "com.toxblh.mtmr.currency"
case .inputsource:
return "com.toxblh.mtmr.inputsource."
case .music(interval: _):
return "com.toxblh.mtmr.music."
case .group(items: _):
return "com.toxblh.mtmr.groupBar."
case .nightShift:
return "com.toxblh.mtmr.nightShift."
case .dnd:
return "com.toxblh.mtmr.dnd."
case .pomodoro(interval: _):
return PomodoroBarItem.identifier
case .network(flip: _):
return NetworkBarItem.identifier
case .darkMode:
return DarkModeBarItem.identifier
case .swipe(direction: _, fingers: _, minOffset: _, sourceApple: _, sourceBash: _):
return "com.toxblh.mtmr.swipe."
}
}
}
extension NSTouchBarItem.Identifier {
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
}
@available(OSX 10.12.2, *)
class TouchBarController: NSObject, NSTouchBarDelegate {
static let shared = TouchBarController()
var touchBar: NSTouchBar!
let touchBar = NSTouchBar()
fileprivate var lastPresetPath = ""
var jsonItems: [BarItemDefinition] = []
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
var basicViewIdentifier = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollView.".appending(UUID().uuidString))
var basicView: BasicView?
var swipeItems: [SwipeItem] = []
var blacklistAppIdentifiers: [String] = []
var frontmostApplicationIdentifier: String? {
return NSWorkspace.shared.frontmostApplication?.bundleIdentifier
}
var timer = Timer()
var timeButton: NSButton = NSButton()
private override init() {
super.init()
SupportedTypesHolder.sharedInstance.register(typename: "exitTouchbar", item: .staticButton(title: "exit"), action: .custom(closure: { [weak self] in self?.dismissTouchBar() }), longAction: .none)
SupportedTypesHolder.sharedInstance.register(typename: "close") { _ in
(item: .staticButton(title: ""), action: .custom(closure: { [weak self] in
guard let `self` = self else { return }
self.reloadPreset(path: self.lastPresetPath)
}), longAction: .none, parameters: [.width: .width(30), .image: .image(source: (NSImage(named: NSImage.stopProgressFreestandingTemplateName))!)])
}
blacklistAppIdentifiers = AppSettings.blacklistedAppIds
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil)
reloadStandardConfig()
}
func createAndUpdatePreset(newJsonItems: [BarItemDefinition]) {
if let oldBar = self.touchBar {
minimizeSystemModal(oldBar)
}
touchBar = NSTouchBar()
jsonItems = newJsonItems
itemDefinitions = [:]
items = [:]
loadItemDefinitions(jsonItems: jsonItems)
createItems()
let centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
})
let centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
let scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
touchBar.delegate = self
touchBar.defaultItemIdentifiers = [basicViewIdentifier]
let leftItems = leftIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
})
let rightItems = rightIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
})
basicView = BasicView(identifier: basicViewIdentifier, items:leftItems + [scrollArea] + rightItems, swipeItems: swipeItems)
basicView?.legacyGesturesEnabled = AppSettings.multitouchGestures
touchBar.defaultItemIdentifiers = [
.escButton,
.dismissButton,
updateActiveApp()
.brightDown,
.brightUp,
.prev,
.play,
.next,
.sleep,
.weather,
.volumeDown,
.volumeUp,
.battery,
.time,
]
self.presentTouchBar()
}
@objc func activeApplicationChanged(_: Notification) {
updateActiveApp()
}
func updateActiveApp() {
if frontmostApplicationIdentifier != nil && blacklistAppIdentifiers.firstIndex(of: frontmostApplicationIdentifier!) != nil {
dismissTouchBar()
} else {
presentTouchBar()
}
}
func reloadStandardConfig() {
let presetPath = standardConfigPath
if !FileManager.default.fileExists(atPath: presetPath),
let defaultPreset = Bundle.main.path(forResource: "defaultPreset", ofType: "json") {
try? FileManager.default.createDirectory(atPath: appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
try? FileManager.default.copyItem(atPath: defaultPreset, toPath: presetPath)
}
reloadPreset(path: presetPath)
}
func reloadPreset(path: String) {
lastPresetPath = path
let items = path.fileData?.barItemDefinitions() ?? [BarItemDefinition(type: .staticButton(title: "bad preset"), action: .none, longAction: .none, additionalParameters: [:])]
createAndUpdatePreset(newJsonItems: items)
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems {
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
}
}
func createItems() {
for (identifier, definition) in itemDefinitions {
let item = createItem(forIdentifier: identifier, definition: definition)
if item is SwipeItem {
swipeItems.append(item as! SwipeItem)
} else {
items[identifier] = item
}
}
}
@objc func setupControlStripPresence() {
func setupControlStripPresence() {
DFRSystemModalShowsCloseBoxWhenFrontMost(false)
let item = NSCustomTouchBarItem(identifier: .controlStripItem)
item.view = NSButton(image: #imageLiteral(resourceName: "StatusImage"), target: self, action: #selector(presentTouchBar))
item.view = NSButton(image: #imageLiteral(resourceName: "Strip"), target: self, action: #selector(presentTouchBar))
NSTouchBarItem.addSystemTrayItem(item)
updateControlStripPresence()
DFRElementSetControlStripPresenceForIdentifier(.controlStripItem, true)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
}
func updateControlStripPresence() {
@@ -216,223 +57,127 @@ class TouchBarController: NSObject, NSTouchBarDelegate {
}
@objc private func presentTouchBar() {
if AppSettings.showControlStripState {
updateControlStripPresence()
presentSystemModal(touchBar, systemTrayItemIdentifier: .controlStripItem)
} else {
presentSystemModal(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
NSTouchBar.presentSystemModalFunctionBar(touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
@objc private func dismissTouchBar() {
minimizeSystemModal(touchBar)
updateControlStripPresence()
NSTouchBar.minimizeSystemModalFunctionBar(touchBar)
}
@objc func resetControlStrip() {
dismissTouchBar()
presentTouchBar()
}
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
switch identifier {
case .escButton:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "esc", target: self, action: #selector(handleEsc))
return item
case .dismissButton:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "exit", target: self, action: #selector(dismissTouchBar))
return item
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == basicViewIdentifier {
return basicView
}
case .brightUp:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "🔆", target: self, action: #selector(handleBrightUp))
return item
case .brightDown:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "🔅", target: self, action: #selector(handleBrightDown))
return item
return nil
}
case .volumeDown:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "🔉", target: self, action: #selector(handleVolumeDown))
return item
case .volumeUp:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "🔊", target: self, action: #selector(handleVolumeUp))
return item
func createItem(forIdentifier identifier: NSTouchBarItem.Identifier, definition item: BarItemDefinition) -> NSTouchBarItem? {
var barItem: NSTouchBarItem!
switch item.type {
case let .staticButton(title: title):
barItem = CustomButtonTouchBarItem(identifier: identifier, title: title)
case let .appleScriptTitledButton(source: source, refreshInterval: interval, alternativeImages: alternativeImages):
barItem = AppleScriptTouchBarItem(identifier: identifier, source: source, interval: interval, alternativeImages: alternativeImages)
case let .shellScriptTitledButton(source: source, refreshInterval: interval):
barItem = ShellScriptTouchBarItem(identifier: identifier, source: source, interval: interval)
case let .timeButton(formatTemplate: template, timeZone: timeZone, locale: locale):
barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone, locale: locale)
case .battery:
barItem = BatteryBarItem(identifier: identifier)
case let .dock(autoResize: autoResize, filter: regexString):
if let regexString = regexString {
guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else {
barItem = CustomButtonTouchBarItem(identifier: identifier, title: "Bad regex")
break
}
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize, filter: regex)
} else {
barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize)
}
case .volume:
if case let .image(source)? = item.additionalParameters[.image] {
barItem = VolumeViewController(identifier: identifier, image: source.image)
} else {
barItem = VolumeViewController(identifier: identifier)
}
case let .brightness(refreshInterval: interval):
if case let .image(source)? = item.additionalParameters[.image] {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval, image: source.image)
} else {
barItem = BrightnessViewController(identifier: identifier, refreshInterval: interval)
}
case let .weather(interval: interval, units: units, api_key: api_key, icon_type: icon_type):
barItem = WeatherBarItem(identifier: identifier, interval: interval, units: units, api_key: api_key, icon_type: icon_type)
case let .yandexWeather(interval: interval):
barItem = YandexWeatherBarItem(identifier: identifier, interval: interval)
case let .currency(interval: interval, from: from, to: to, full: full):
barItem = CurrencyBarItem(identifier: identifier, interval: interval, from: from, to: to, full: full)
case .inputsource:
barItem = InputSourceBarItem(identifier: identifier)
case let .music(interval: interval, disableMarquee: disableMarquee):
barItem = MusicBarItem(identifier: identifier, interval: interval, disableMarquee: disableMarquee)
case let .group(items: items):
barItem = GroupBarItem(identifier: identifier, items: items)
case .nightShift:
barItem = NightShiftBarItem(identifier: identifier)
case .dnd:
barItem = DnDBarItem(identifier: identifier)
case let .pomodoro(workTime: workTime, restTime: restTime):
barItem = PomodoroBarItem(identifier: identifier, workTime: workTime, restTime: restTime)
case let .network(flip: flip):
barItem = NetworkBarItem(identifier: identifier, flip: flip)
case .darkMode:
barItem = DarkModeBarItem(identifier: identifier)
case let .swipe(direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash):
barItem = SwipeItem(identifier: identifier, direction: direction, fingers: fingers, minOffset: minOffset, sourceApple: sourceApple, sourceBash: sourceBash)
}
case .prev:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "⏪", target: self, action: #selector(handlePrev))
return item
case .play:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "⏯", target: self, action: #selector(handlePlay))
return item
case .next:
let item = NSCustomTouchBarItem(identifier: identifier)
item.view = NSButton(title: "⏩", target: self, action: #selector(handleNext))
return item
if let action = self.action(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.tapClosure = action
}
if let longAction = self.longAction(forItem: item), let item = barItem as? CustomButtonTouchBarItem {
item.longTapClosure = longAction
}
if case let .bordered(bordered)? = item.additionalParameters[.bordered], let item = barItem as? CustomButtonTouchBarItem {
item.isBordered = bordered
}
if case let .background(color)? = item.additionalParameters[.background], let item = barItem as? CustomButtonTouchBarItem {
item.backgroundColor = color
}
if case let .width(value)? = item.additionalParameters[.width], let widthBarItem = barItem as? CanSetWidth {
widthBarItem.setWidth(value: value)
}
if case let .image(source)? = item.additionalParameters[.image], let item = barItem as? CustomButtonTouchBarItem {
item.image = source.image
}
if case let .title(value)? = item.additionalParameters[.title] {
if let item = barItem as? GroupBarItem {
item.collapsedRepresentationLabel = value
} else if let item = barItem as? CustomButtonTouchBarItem {
item.title = value
}
}
return barItem
}
case .time:
let item = NSCustomTouchBarItem(identifier: identifier)
timeButton = NSButton(title: self.getCurrentTime(), target: self, action: nil)
item.view = timeButton
return item
func action(forItem item: BarItemDefinition) -> (() -> Void)? {
switch item.action {
case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) }
case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case let .appleScript(source: source):
guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)")
return {}
}
return {
DispatchQueue.appleScriptQueue.async {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling \(item) ")
}
}
}
case let .shellScript(executable: executable, parameters: parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case let .openUrl(url: url):
return {
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
case let .custom(closure: closure):
return closure
case .none:
default:
return nil
}
}
func longAction(forItem item: BarItemDefinition) -> (() -> Void)? {
switch item.longAction {
case let .hidKey(keycode: keycode):
return { HIDPostAuxKey(keycode) }
case let .keyPress(keycode: keycode):
return { GenericKeyPress(keyCode: CGKeyCode(keycode)).send() }
case let .appleScript(source: source):
guard let appleScript = source.appleScript else {
print("cannot create apple script for item \(item)")
return {}
}
return {
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print("error \(error) when handling \(item) ")
}
}
case let .shellScript(executable: executable, parameters: parameters):
return {
let task = Process()
task.launchPath = executable
task.arguments = parameters
task.launch()
}
case let .openUrl(url: url):
return {
if let url = URL(string: url), NSWorkspace.shared.open(url) {
#if DEBUG
print("URL was successfully opened")
#endif
} else {
print("error", url)
}
}
case let .custom(closure: closure):
return closure
case .none:
return nil
}
func getCurrentTime() -> String {
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate("HH:mm")
let timestamp = dateFormatter.string(from: date)
return timestamp
}
}
protocol CanSetWidth {
func setWidth(value: CGFloat)
}
extension NSCustomTouchBarItem: CanSetWidth {
func setWidth(value: CGFloat) {
view.widthAnchor.constraint(equalToConstant: value).isActive = true
@objc func updateTime() {
timeButton.title = getCurrentTime()
}
}
extension BarItemDefinition {
var align: Align {
if case let .align(result)? = additionalParameters[.align] {
return result
}
return .center
@objc func handleEsc() {
let sender = ESCKeyPress()
sender.send()
}
@objc func handleVolumeUp() {
HIDPostAuxKey(Int(NX_KEYTYPE_SOUND_UP))
}
@objc func handleVolumeDown() {
HIDPostAuxKey(Int(NX_KEYTYPE_SOUND_DOWN))
}
@objc func handleBrightDown() {
// HIDPostAuxKey(Int(NX_KEYTYPE_BRIGHTNESS_DOWN))
let sender = BrightnessUpPress()
sender.send()
}
@objc func handleBrightUp() {
// HIDPostAuxKey(Int(NX_KEYTYPE_BRIGHTNESS_UP))
let sender = BrightnessDownPress()
sender.send()
}
@objc func handlePrev() {
HIDPostAuxKey(Int(NX_KEYTYPE_PREVIOUS))
}
@objc func handlePlay() {
HIDPostAuxKey(Int(NX_KEYTYPE_PLAY))
}
@objc func handleNext() {
HIDPostAuxKey(Int(NX_KEYTYPE_NEXT))
}
// func getBattery() {
// var error: NSDictionary?
// if let scriptObject = NSAppleScript(source: <#T##String#>) {
// if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(
// &error) {
// print(output.stringValue)
// } else if (error != nil) {
// print("error: \(error)")
// }
// }
// }
}
+37
View File
@@ -0,0 +1,37 @@
//
// TouchBarItems.swift
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
@available(OSX 10.12.2, *)
extension NSTouchBarItem.Identifier {
static let escButton = NSTouchBarItem.Identifier("com.toxblh.mtmr.escButton")
static let dismissButton = NSTouchBarItem.Identifier("com.toxblh.mtmr.dismissButton")
// Volume
static let volumeUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeUp")
static let volumeDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.volumeDown")
// Brightness
static let brightUp = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightUp")
static let brightDown = NSTouchBarItem.Identifier("com.toxblh.mtmr.brightDown")
// Music
static let prev = NSTouchBarItem.Identifier("com.toxblh.mtmr.prev")
static let next = NSTouchBarItem.Identifier("com.toxblh.mtmr.next")
static let play = NSTouchBarItem.Identifier("com.toxblh.mtmr.play")
// Plugins
static let sleep = NSTouchBarItem.Identifier("com.toxblh.mtmr.sleep")
static let weather = NSTouchBarItem.Identifier("com.toxblh.mtmr.weather")
static let time = NSTouchBarItem.Identifier("com.toxblh.mtmr.time")
static let battery = NSTouchBarItem.Identifier("com.toxblh.mtmr.battery")
static let nowPlaying = NSTouchBarItem.Identifier("com.toxblh.mtmr.nowPlaying")
static let controlStripItem = NSTouchBarItem.Identifier("com.toxblh.mtmr.controlStrip")
}
+10
View File
@@ -0,0 +1,10 @@
//
// TouchBarPrivateApi-Bridging.h
// MTMR
//
// Created by Anton Palgunov on 18/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
#import "TouchBarPrivateApi.h"
#import "TouchBarSupport.h"
@@ -17,16 +17,13 @@ extern void DFRSystemModalShowsCloseBoxWhenFrontMost(BOOL);
@interface NSTouchBar (PrivateMethods)
// macOS 10.14
+ (void)presentSystemModalTouchBar:(NSTouchBar *)touchBar placement:(long long)placement systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
+ (void)presentSystemModalTouchBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
+ (void)dismissSystemModalTouchBar:(NSTouchBar *)touchBar;
+ (void)minimizeSystemModalTouchBar:(NSTouchBar *)touchBar;
// macOS 10.13
// presentSystemModalFunctionBar:placement:systemTrayItemIdentifier:
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar placement:(long long)placement systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
+ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSTouchBarItemIdentifier)identifier;
+ (void)dismissSystemModalFunctionBar:(NSTouchBar *)touchBar;
+ (void)minimizeSystemModalFunctionBar:(NSTouchBar *)touchBar;
@end
@@ -10,6 +10,8 @@
@interface MediaKeys : NSObject
+ (void)HIDPostAuxKey:(UInt8)keyCode;
+ (void)decreaseVolume;
+ (void)increaseVolume;
+ (void)muteVolume;
@end
@@ -7,7 +7,6 @@
//
#import "TouchBarSupport.h"
#import <IOKit/hidsystem/ev_keymap.h>
@implementation MediaKeys
@@ -31,7 +30,7 @@ static io_connect_t get_event_driver(void)
}
static void HIDReleaseAuxKey( const UInt8 auxKeyCode )
static void HIDPostAuxKey( const UInt8 auxKeyCode )
{
NXEventData event;
kern_return_t kr;
@@ -52,8 +51,16 @@ static void HIDReleaseAuxKey( const UInt8 auxKeyCode )
kr = IOHIDPostEvent( get_event_driver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
}
+ (void)HIDPostAuxKey: (UInt8)keyCode {
HIDReleaseAuxKey(keyCode);
+ (void)decreaseVolume {
HIDPostAuxKey(NX_KEYTYPE_SOUND_DOWN);
}
+ (void)increaseVolume {
HIDPostAuxKey(NX_KEYTYPE_SOUND_UP);
}
+ (void)muteVolume {
HIDPostAuxKey(NX_KEYTYPE_MUTE);
}
@end
+27
View File
@@ -0,0 +1,27 @@
//
// ViewController.swift
// MTMR
//
// Created by Anton Palgunov on 16/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
-246
View File
@@ -1,246 +0,0 @@
//
// AppScrubberTouchBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
import Cocoa
class AppScrubberTouchBarItem: NSCustomTouchBarItem {
private var scrollView = NSScrollView()
private var autoResize: Bool = false
private var widthConstraint: NSLayoutConstraint?
private let filter: NSRegularExpression?
private var persistentAppIdentifiers: [String] = []
private var runningAppsIdentifiers: [String] = []
private var frontmostApplicationIdentifier: String? {
return NSWorkspace.shared.frontmostApplication?.bundleIdentifier
}
private var applications: [DockItem] = []
private var items: [DockBarItem] = []
init(identifier: NSTouchBarItem.Identifier, autoResize: Bool = false, filter: NSRegularExpression? = nil) {
self.filter = filter
super.init(identifier: identifier)
self.autoResize = autoResize
view = scrollView
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didTerminateApplicationNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil)
persistentAppIdentifiers = AppSettings.dockPersistentAppIds
hardReloadItems()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func hardReloadItems() {
applications = launchedApplications()
applications += getDockPersistentAppsList()
reloadData()
softReloadItems()
updateSize()
}
@objc func softReloadItems() {
let frontMostAppId = self.frontmostApplicationIdentifier
let runningAppsIds = NSWorkspace.shared.runningApplications.map { $0.bundleIdentifier }
for barItem in items {
let bundleId = barItem.dockItem.bundleIdentifier
barItem.isRunning = runningAppsIds.contains(bundleId)
barItem.isFrontmost = frontMostAppId == bundleId
}
}
func updateSize() {
if self.autoResize {
self.widthConstraint?.isActive = false
let width = self.scrollView.documentView?.fittingSize.width ?? 0
self.widthConstraint = self.scrollView.widthAnchor.constraint(equalToConstant: width)
self.widthConstraint!.isActive = true
}
}
func reloadData() {
items = applications.map { self.createAppButton(for: $0) }
let stackView = NSStackView(views: items.compactMap { $0.view })
stackView.spacing = 1
stackView.orientation = .horizontal
let visibleRect = self.scrollView.documentVisibleRect
scrollView.documentView = stackView
stackView.scroll(visibleRect.origin)
}
public func createAppButton(for app: DockItem) -> DockBarItem {
let item = DockBarItem(app)
item.isBordered = false
item.tapClosure = { [weak self] in
self?.switchToApp(app: app)
}
item.longTapClosure = { [weak self] in
self?.handleHalfLongPress(item: app)
}
item.killAppClosure = {[weak self] in
self?.handleLongPress(item: app)
}
return item
}
public func switchToApp(app: DockItem) {
let bundleIdentifier = app.bundleIdentifier
if bundleIdentifier!.contains("file://") {
NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: ""))
} else {
NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil)
}
softReloadItems()
// NB: if you can't open app which on another space, try to check mark
// "When switching to an application, switch to a Space with open windows for the application"
// in Mission control settings
}
//todo
private func handleLongPress(item: DockItem) {
if let pid = item.pid, let app = NSRunningApplication(processIdentifier: pid) {
if !app.terminate() {
app.forceTerminate()
}
hardReloadItems()
}
}
private func handleHalfLongPress(item: DockItem) {
if let index = self.persistentAppIdentifiers.firstIndex(of: item.bundleIdentifier) {
persistentAppIdentifiers.remove(at: index)
hardReloadItems()
} else {
persistentAppIdentifiers.append(item.bundleIdentifier)
}
AppSettings.dockPersistentAppIds = persistentAppIdentifiers
}
private func launchedApplications() -> [DockItem] {
runningAppsIdentifiers = []
var returnable: [DockItem] = []
for app in NSWorkspace.shared.runningApplications {
guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue }
guard let bundleIdentifier = app.bundleIdentifier else { continue }
if let filter = self.filter,
let name = app.localizedName,
filter.numberOfMatches(in: name, options: [], range: NSRange(location: 0, length: name.count)) == 0 {
continue
}
runningAppsIdentifiers.append(bundleIdentifier)
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: app.icon ?? getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier)
returnable.append(dockItem)
}
return returnable
}
public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil) -> NSImage {
if let bundleIdentifier = bundleIdentifier, let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier) {
return NSWorkspace.shared.icon(forFile: appPath)
}
if let path = path {
return NSWorkspace.shared.icon(forFile: path)
}
let genericIcon = NSImage(contentsOfFile: "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericDocumentIcon.icns")
return genericIcon ?? NSImage(size: .zero)
}
public func getDockPersistentAppsList() -> [DockItem] {
var returnable: [DockItem] = []
for bundleIdentifier in persistentAppIdentifiers {
if !runningAppsIdentifiers.contains(bundleIdentifier) {
let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier))
returnable.append(dockItem)
}
}
return returnable
}
}
public class DockItem: NSObject {
var bundleIdentifier: String!, icon: NSImage!, pid: Int32!
convenience init(bundleIdentifier: String, icon: NSImage, pid: Int32? = nil) {
self.init()
self.bundleIdentifier = bundleIdentifier
self.icon = icon
self.pid = pid
}
}
private let iconWidth = 32.0
class DockBarItem: CustomButtonTouchBarItem {
let dotView = NSView(frame: .zero)
let dockItem: DockItem
fileprivate var killGestureRecognizer: LongPressGestureRecognizer!
var killAppClosure: () -> Void = { }
var isRunning = false {
didSet {
redrawDotView()
}
}
var isFrontmost = false {
didSet {
redrawDotView()
}
}
init(_ app: DockItem) {
self.dockItem = app
super.init(identifier: .init(app.bundleIdentifier), title: "")
dotView.wantsLayer = true
image = app.icon
image?.size = NSSize(width: iconWidth, height: iconWidth)
killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer))
killGestureRecognizer.allowedTouchTypes = .direct
killGestureRecognizer.recognizeTimeout = 1.5
killGestureRecognizer.minimumPressDuration = 1.5
killGestureRecognizer.isEnabled = isRunning
self.finishViewConfiguration = { [weak self] in
guard let selfie = self else { return }
selfie.dotView.layer?.cornerRadius = 1.5
selfie.view.addSubview(selfie.dotView)
selfie.redrawDotView()
selfie.view.addGestureRecognizer(selfie.killGestureRecognizer)
}
}
func redrawDotView() {
dotView.layer?.backgroundColor = isRunning ? NSColor.white.cgColor : NSColor.clear.cgColor
dotView.frame.size = NSSize(width: isFrontmost ? iconWidth - 14 : 3, height: 3)
dotView.setFrameOrigin(NSPoint(x: 18.0 - Double(dotView.frame.size.width) / 2.0, y: iconWidth - 5))
}
@objc func firePanGestureRecognizer() {
self.killAppClosure()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
-140
View File
@@ -1,140 +0,0 @@
//
// BatteryBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 18/04/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
import IOKit.ps
class BatteryBarItem: CustomButtonTouchBarItem {
private let batteryInfo = BatteryInfo()
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: " ")
batteryInfo.start { [weak self] in
self?.refresh()
}
refresh()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func refresh() {
attributedTitle = batteryInfo.formattedInfo()
}
deinit {
batteryInfo.stop()
}
}
class BatteryInfo: NSObject {
var current: Int = 0
var timeToEmpty: Int = 0
var timeToFull: Int = 0
var isCharged: Bool = false
var isCharging: Bool = false
var ACPower: String = ""
var timeRemaining: String = ""
var notifyBlock: () -> Void = {}
var loop: CFRunLoopSource?
func start(notifyBlock: @escaping () -> Void) {
self.notifyBlock = notifyBlock
let opaque = Unmanaged.passRetained(self).toOpaque()
let context = UnsafeMutableRawPointer(opaque)
loop = IOPSNotificationCreateRunLoopSource({ context in
guard let ctx = context else {
return
}
let watcher = Unmanaged<BatteryInfo>.fromOpaque(ctx).takeUnretainedValue()
watcher.notifyBlock()
}, context).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
}
func stop() {
notifyBlock = {}
guard let loop = self.loop else {
return
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
self.loop = nil
}
func getPSInfo() {
let psInfo = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let psList = IOPSCopyPowerSourcesList(psInfo).takeRetainedValue() as [CFTypeRef]
for ps in psList {
if let psDesc = IOPSGetPowerSourceDescription(psInfo, ps).takeUnretainedValue() as? [String: Any] {
if let current = psDesc[kIOPSCurrentCapacityKey] as? Int {
self.current = current
}
if let timeToEmpty = psDesc[kIOPSTimeToEmptyKey] as? Int {
self.timeToEmpty = timeToEmpty
}
if let timeToFull = psDesc[kIOPSTimeToFullChargeKey] as? Int {
self.timeToFull = timeToFull
}
if let isCharged = psDesc[kIOPSIsChargedKey] as? Bool {
self.isCharged = isCharged
}
if let isCharging = psDesc[kIOPSIsChargingKey] as? Bool {
self.isCharging = isCharging
}
if let ACPower = psDesc[kIOPSPowerSourceStateKey] as? String {
self.ACPower = ACPower
}
}
}
}
func getFormattedTime(time: Int) -> String {
if time > 0 {
let timeFormatted = NSString(format: " %d:%02d", time / 60, time % 60) as String
return timeFormatted
}
return ""
}
public func formattedInfo() -> NSAttributedString {
var title = ""
getPSInfo()
if ACPower == "AC Power" {
if current < 100 {
title += "⚡️"
}
timeRemaining = getFormattedTime(time: timeToFull)
} else {
timeRemaining = getFormattedTime(time: timeToEmpty)
}
title += String(current) + "%"
var color = NSColor.white
if current <= 10 && ACPower != "AC Power" {
color = NSColor.red
}
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: NSFont.systemFont(ofSize: 15), .baselineOffset: 1])
let newTitleSecond = NSMutableAttributedString(string: timeRemaining as String, attributes: [NSAttributedString.Key.foregroundColor: color, NSAttributedString.Key.font: NSFont.systemFont(ofSize: 8, weight: .regular), NSAttributedString.Key.baselineOffset: 7])
newTitle.append(newTitleSecond)
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
return newTitle
}
}
@@ -1,71 +0,0 @@
import AppKit
import AVFoundation
import Cocoa
import CoreAudio
class BrightnessViewController: NSCustomTouchBarItem {
private(set) var sliderItem: CustomSlider!
init(identifier: NSTouchBarItem.Identifier, refreshInterval: Double, image: NSImage? = nil) {
super.init(identifier: identifier)
if image == nil {
sliderItem = CustomSlider()
} else {
sliderItem = CustomSlider(knob: image!)
}
sliderItem.target = self
sliderItem.action = #selector(BrightnessViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0
sliderItem.floatValue = getBrightness() * 100
view = sliderItem
let timer = Timer.scheduledTimer(timeInterval: refreshInterval, target: self, selector: #selector(BrightnessViewController.updateBrightnessSlider), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
sliderItem.unbind(NSBindingName.value)
}
@objc func updateBrightnessSlider() {
DispatchQueue.main.async {
self.sliderItem.floatValue = self.getBrightness() * 100
}
}
@objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider {
setBrightness(level: Float32(sliderItem.intValue) / 100.0)
}
}
private func getBrightness() -> Float32 {
if #available(OSX 10.13, *) {
return Float32(CoreDisplay_Display_GetUserBrightness(0))
} else {
var level: Float32 = 0.5
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &level)
return level
}
}
private func setBrightness(level: Float) {
if #available(OSX 10.13, *) {
CoreDisplay_Display_SetUserBrightness(0, Double(level))
} else {
let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect"))
IODisplaySetFloatParameter(service, 1, kIODisplayBrightnessKey as CFString, level)
IOObjectRelease(service)
}
}
}
-165
View File
@@ -1,165 +0,0 @@
//
// CurrencyBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class CurrencyBarItem: CustomButtonTouchBarItem {
private let activity: NSBackgroundActivityScheduler
private var prefix: String
private var postfix: String
private var from: String
private var to: String
private var decimal: Int
private var decimalValue: Float32!
private var decimalString: String!
private var oldValue: Float32!
private var full: Bool = false
private let currencies = [
"USD": "$",
"EUR": "€",
"RUB": "₽",
"JPY": "¥",
"GBP": "₤",
"CAD": "$",
"KRW": "₩",
"CNY": "¥",
"AUD": "$",
"BRL": "R$",
"IDR": "Rp",
"MXN": "$",
"SGD": "$",
"BTC": "฿",
"LTC": "Ł",
"ETH": "Ξ",
]
private let decimals = [
"USD": 4,
"EUR": 4,
"RUB": 2,
"JPY": 2,
"GBP": 4,
"CAD": 4,
"KRW": 4,
"CNY": 4,
"AUD": 4,
"BRL": 4,
"IDR": 1,
"MXN": 2,
"SGD": 4,
"CHF": 4,
"BTC": 2,
"LTC": 2,
"ETH": 2,
]
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, from: String, to: String, full: Bool) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
self.from = from
self.to = to
self.full = full
if let prefix = currencies[from] {
self.prefix = prefix
} else {
prefix = from
}
if let postfix = currencies[to] {
self.postfix = postfix
} else {
postfix = to
}
if let decimal = decimals[to] {
self.decimal = decimal
} else {
decimal = 2
}
super.init(identifier: identifier, title: "⏳")
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateCurrency()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateCurrency()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateCurrency() {
let urlRequest = URLRequest(url: URL(string: "https://api.coinbase.com/v2/exchange-rates?currency=\(from)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
var value: Float32!
if let data_array = json["data"] as? [String: AnyObject] {
if let rates = data_array["rates"] as? [String: AnyObject] {
if let item = rates["\(self.to)"] as? String {
value = Float32(item)
}
}
}
if value != nil {
DispatchQueue.main.async {
self.setCurrency(value: value!)
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
task.resume()
}
func setCurrency(value: Float32) {
var color = NSColor.white
if let oldValue = self.oldValue {
if oldValue < value {
color = NSColor.green
} else if oldValue > value {
color = NSColor.red
}
}
oldValue = value
decimalValue = (value * pow(10,Float(decimal))).rounded() / pow(10,Float(decimal))
decimalString = String(decimalValue)
var title = ""
if full {
title = String(format: "%@%@‣%@", prefix, postfix, decimalString)
} else {
title = String(format: "%@%.2f", prefix, value)
}
let regularFont = attributedTitle.attribute(.font, at: 0, effectiveRange: nil) as? NSFont ?? NSFont.systemFont(ofSize: 15)
let newTitle = NSMutableAttributedString(string: title as String, attributes: [.foregroundColor: color, .font: regularFont, .baselineOffset: 1])
newTitle.setAlignment(.center, range: NSRange(location: 0, length: title.count))
attributedTitle = newTitle
}
deinit {
activity.invalidate()
}
}
-57
View File
@@ -1,57 +0,0 @@
import Foundation
class DarkModeBarItem: CustomButtonTouchBarItem, Widget {
static var name: String = "darkmode"
static var identifier: String = "com.toxblh.mtmr.darkmode"
private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
isBordered = false
setWidth(value: 24)
tapClosure = { [weak self] in self?.DarkModeToggle() }
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func DarkModeToggle() {
DarkMode.isEnabled = !DarkMode.isEnabled
refresh()
}
@objc func refresh() {
image = DarkMode.isEnabled ? #imageLiteral(resourceName: "dark-mode-on") : #imageLiteral(resourceName: "dark-mode-off")
}
}
struct DarkMode {
private static let prefix = "tell application \"System Events\" to tell appearance preferences to"
static var isEnabled: Bool {
get {
return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark"
}
set {
toggle(force: newValue)
}
}
static func toggle(force: Bool? = nil) {
let value = force.map(String.init) ?? "not dark mode"
_ = runAppleScript("\(prefix) set dark mode to \(value)")
}
}
func runAppleScript(_ source: String) -> String? {
return NSAppleScript(source: source)?.executeAndReturnError(nil).stringValue
}
-78
View File
@@ -1,78 +0,0 @@
//
// DnDBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 29/08/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
class DnDBarItem: CustomButtonTouchBarItem {
private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
isBordered = false
setWidth(value: 32)
tapClosure = { [weak self] in self?.DnDToggle() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func DnDToggle() {
DoNotDisturb.isEnabled = !DoNotDisturb.isEnabled
refresh()
}
@objc func refresh() {
image = DoNotDisturb.isEnabled ? #imageLiteral(resourceName: "dnd-on") : #imageLiteral(resourceName: "dnd-off")
}
}
public struct DoNotDisturb {
private static let appId = "com.apple.notificationcenterui" as CFString
private static let dndPref = "com.apple.notificationcenterui.dndprefs_changed"
private static func set(_ key: String, value: CFPropertyList?) {
CFPreferencesSetValue(key as CFString, value, appId, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
}
private static func commitChanges() {
CFPreferencesSynchronize(appId, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
DistributedNotificationCenter.default().postNotificationName(NSNotification.Name(dndPref), object: nil, userInfo: nil, deliverImmediately: true)
NSRunningApplication.runningApplications(withBundleIdentifier: appId as String).first?.terminate()
}
private static func enable() {
set("dndStart", value: nil)
set("dndEnd", value: nil)
set("doNotDisturb", value: true as CFPropertyList)
set("doNotDisturbDate", value: Date() as CFPropertyList)
commitChanges()
}
private static func disable() {
set("dndStart", value: nil)
set("dndEnd", value: nil)
set("doNotDisturb", value: false as CFPropertyList)
set("doNotDisturbDate", value: nil)
commitChanges()
}
static var isEnabled: Bool {
get {
return CFPreferencesGetAppBooleanValue("doNotDisturb" as CFString, appId, nil)
}
set {
newValue ? enable() : disable()
}
}
}
-100
View File
@@ -1,100 +0,0 @@
//
// GroupBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 11.05.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class GroupBarItem: NSPopoverTouchBarItem, NSTouchBarDelegate {
var jsonItems: [BarItemDefinition]
var itemDefinitions: [NSTouchBarItem.Identifier: BarItemDefinition] = [:]
var items: [NSTouchBarItem.Identifier: NSTouchBarItem] = [:]
var leftIdentifiers: [NSTouchBarItem.Identifier] = []
var centerIdentifiers: [NSTouchBarItem.Identifier] = []
var centerItems: [NSTouchBarItem] = []
var rightIdentifiers: [NSTouchBarItem.Identifier] = []
var scrollArea: NSCustomTouchBarItem?
var centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
init(identifier: NSTouchBarItem.Identifier, items: [BarItemDefinition]) {
jsonItems = items
super.init(identifier: identifier)
popoverTouchBar.delegate = self
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {}
@objc override func showPopover(_: Any?) {
itemDefinitions = [:]
items = [:]
leftIdentifiers = []
centerItems = []
rightIdentifiers = []
loadItemDefinitions(jsonItems: jsonItems)
createItems()
centerItems = centerIdentifiers.compactMap({ (identifier) -> NSTouchBarItem? in
items[identifier]
})
centerScrollArea = NSTouchBarItem.Identifier("com.toxblh.mtmr.scrollArea.".appending(UUID().uuidString))
scrollArea = ScrollViewItem(identifier: centerScrollArea, items: centerItems)
TouchBarController.shared.touchBar.delegate = self
TouchBarController.shared.touchBar.defaultItemIdentifiers = []
TouchBarController.shared.touchBar.defaultItemIdentifiers = leftIdentifiers + [centerScrollArea] + rightIdentifiers
if AppSettings.showControlStripState {
presentSystemModal(TouchBarController.shared.touchBar, systemTrayItemIdentifier: .controlStripItem)
} else {
presentSystemModal(TouchBarController.shared.touchBar, placement: 1, systemTrayItemIdentifier: .controlStripItem)
}
}
func touchBar(_: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if identifier == centerScrollArea {
return scrollArea
}
guard let item = self.items[identifier],
let definition = self.itemDefinitions[identifier],
definition.align != .center else {
return nil
}
return item
}
func loadItemDefinitions(jsonItems: [BarItemDefinition]) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH-mm-ss"
let time = dateFormatter.string(from: Date())
for item in jsonItems {
let identifierString = item.type.identifierBase.appending(time + "--" + UUID().uuidString)
let identifier = NSTouchBarItem.Identifier(identifierString)
itemDefinitions[identifier] = item
if item.align == .left {
leftIdentifiers.append(identifier)
}
if item.align == .right {
rightIdentifiers.append(identifier)
}
if item.align == .center {
centerIdentifiers.append(identifier)
}
}
}
func createItems() {
for (identifier, definition) in itemDefinitions {
items[identifier] = TouchBarController.shared.createItem(forIdentifier: identifier, definition: definition)
}
}
}
-132
View File
@@ -1,132 +0,0 @@
//
// InputSourceBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 22.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class InputSourceBarItem: CustomButtonTouchBarItem {
fileprivate var notificationCenter: CFNotificationCenter
let buttonSize = NSSize(width: 21, height: 21)
init(identifier: NSTouchBarItem.Identifier) {
notificationCenter = CFNotificationCenterGetDistributedCenter()
super.init(identifier: identifier, title: "⏳")
observeIputSourceChangedNotification()
textInputSourceDidChange()
tapClosure = { [weak self] in
self?.switchInputSource()
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
CFNotificationCenterRemoveEveryObserver(notificationCenter, UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()))
}
@objc public func textInputSourceDidChange() {
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
var iconImage: NSImage?
if let imageURL = currentSource.iconImageURL,
let image = NSImage(contentsOf: imageURL) {
iconImage = image
} else if let iconRef = currentSource.iconRef {
iconImage = NSImage(iconRef: iconRef)
}
if let iconImage = iconImage {
iconImage.size = buttonSize
image = iconImage
title = ""
} else {
title = currentSource.name
}
}
@objc private func switchInputSource() {
var inputSources: [TISInputSource] = []
let currentSource = TISCopyCurrentKeyboardInputSource().takeUnretainedValue()
let inputSourceNSArray = TISCreateInputSourceList(nil, false).takeRetainedValue() as NSArray
let elements = inputSourceNSArray as! [TISInputSource]
inputSources = elements.filter({
$0.category == TISInputSource.Category.keyboardInputSource && $0.isSelectable
})
for item in inputSources {
if item.id != currentSource.id {
TISSelectInputSource(item)
break
}
}
}
@objc public func observeIputSourceChangedNotification() {
let callback: CFNotificationCallback = { _, observer, _, _, _ in
let mySelf = Unmanaged<InputSourceBarItem>.fromOpaque(observer!).takeUnretainedValue()
mySelf.textInputSourceDidChange()
}
CFNotificationCenterAddObserver(notificationCenter,
UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque()),
callback,
kTISNotifySelectedKeyboardInputSourceChanged,
nil,
.deliverImmediately)
}
}
extension TISInputSource {
enum Category {
static var keyboardInputSource: String {
return kTISCategoryKeyboardInputSource as String
}
}
private func getProperty(_ key: CFString) -> AnyObject? {
let cfType = TISGetInputSourceProperty(self, key)
if cfType != nil {
return Unmanaged<AnyObject>.fromOpaque(cfType!).takeUnretainedValue()
} else {
return nil
}
}
var id: String {
return getProperty(kTISPropertyInputSourceID) as! String
}
var name: String {
return getProperty(kTISPropertyLocalizedName) as! String
}
var category: String {
return getProperty(kTISPropertyInputSourceCategory) as! String
}
var isSelectable: Bool {
return getProperty(kTISPropertyInputSourceIsSelectCapable) as! Bool
}
var sourceLanguages: [String] {
return getProperty(kTISPropertyInputSourceLanguages) as! [String]
}
var iconImageURL: URL? {
return getProperty(kTISPropertyIconImageURL) as! URL?
}
var iconRef: IconRef? {
return OpaquePointer(TISGetInputSourceProperty(self, kTISPropertyIconRef)) as IconRef?
}
}
-433
View File
@@ -1,433 +0,0 @@
//
// MusicBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 05.05.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import ScriptingBridge
class MusicBarItem: CustomButtonTouchBarItem {
private enum Player: String {
case Music = "com.apple.Music"
case iTunes = "com.apple.iTunes"
case Spotify = "com.spotify.client"
case VOX = "com.coppertino.Vox"
case Chrome = "com.google.Chrome"
case Safari = "com.apple.Safari"
}
private let playerBundleIdentifiers = [
Player.Music,
Player.iTunes,
Player.Spotify,
Player.VOX,
Player.Chrome,
Player.Safari,
]
private let interval: TimeInterval
private let disableMarquee: Bool
private var songTitle: String?
private var timer: Timer?
private let iconSize = NSSize(width: 21, height: 21)
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, disableMarquee: Bool) {
self.interval = interval
self.disableMarquee = disableMarquee
super.init(identifier: identifier, title: "⏳")
isBordered = false
tapClosure = { [weak self] in self?.playPause() }
longTapClosure = { [weak self] in self?.nextTrack() }
refreshAndSchedule()
}
@objc func marquee() {
let str = title
if str.count > 10 {
let indexFirst = str.index(str.startIndex, offsetBy: 0)
let indexSecond = str.index(str.startIndex, offsetBy: 1)
title = String(str.suffix(from: indexSecond)) + String(str[indexFirst])
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func playPause() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
if ident == .Spotify {
let mp = (musicPlayer as SpotifyApplication)
mp.playpause!()
return
} else if ident == .iTunes {
let mp = (musicPlayer as iTunesApplication)
mp.playpause!()
return
} else if ident == .Music {
let mp = (musicPlayer as MusicApplication)
mp.playpause!()
return
} else if ident == .VOX {
let mp = (musicPlayer as VoxApplication)
mp.playpause!()
return
} else if ident == .Safari {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_play')[0].click()", in: tab)
return
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_play')[0].click()", in: tab)
return
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
_ = safariApplication.doJavaScript!("document.getElementById('movie_player').click()", in: tab)
return
}
}
}
}
// else if (ident == .Chrome) {
// let chromeApplication = musicPlayer as GoogleChromeApplication
// let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
// for window in chromeWindows! {
// for tab in window.tabs!() {
// let tab = tab as! GoogleChromeTab
// if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
// chromeApplication.executeJavaScript!(javascript: "document.getElementsByClassName('player-controls__btn_play')[0].click()")
// break
// } else if ((tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))!) {
// chromeApplication.executeJavaScript!(javascript: "document.getElementsByClassName('audio_page_player_ctrl')[0].click()")
// break
// } else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
// chromeApplication.executeJavaScript!(javascript: "alert(document.title)") // , id: tab
// break // document.getElementById('movie_player').click()
// }
// }
// }
// }
break
}
}
}
}
@objc func nextTrack() {
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
if ident == .Spotify {
let mp = (musicPlayer as SpotifyApplication)
mp.nextTrack!()
updatePlayer()
return
} else if ident == .iTunes {
let mp = (musicPlayer as iTunesApplication)
mp.nextTrack!()
updatePlayer()
return
} else if ident == .Music {
let mp = (musicPlayer as MusicApplication)
mp.nextTrack!()
updatePlayer()
return
} else if ident == .VOX {
let mp = (musicPlayer as VoxApplication)
mp.next!()
updatePlayer()
return
} else if ident == .Safari {
// You must enable the 'Allow JavaScript from Apple Events' option in Safari's Develop menu to use 'do JavaScript'.
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('player-controls__btn_next')[0].click()", in: tab)
updatePlayer()
return
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('audio_page_player_next')[0].click()", in: tab)
updatePlayer()
return
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
_ = safariApplication.doJavaScript!("document.getElementsByClassName('ytp-next-button')[0].click()", in: tab)
updatePlayer()
return
}
}
}
}
}
}
}
}
func refreshAndSchedule() {
DispatchQueue.main.async {
self.updatePlayer()
DispatchQueue.main.asyncAfter(deadline: .now() + self.interval) { [weak self] in
self?.refreshAndSchedule()
}
}
}
func updatePlayer() {
var iconUpdated = false
var titleUpdated = false
for ident in playerBundleIdentifiers {
if let musicPlayer = SBApplication(bundleIdentifier: ident.rawValue) {
if musicPlayer.isRunning {
var tempTitle = ""
if ident == .Spotify {
tempTitle = (musicPlayer as SpotifyApplication).title
} else if ident == .iTunes {
tempTitle = (musicPlayer as iTunesApplication).title
} else if ident == .Music {
tempTitle = (musicPlayer as MusicApplication).title
} else if ident == .VOX {
tempTitle = (musicPlayer as VoxApplication).title
} else if ident == .Safari {
let safariApplication = musicPlayer as SafariApplication
let safariWindows = safariApplication.windows?().compactMap({ $0 as? SafariWindow })
for window in safariWindows! {
for tab in window.tabs!() {
let tab = tab as! SafariTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
// if (!(tab.name?.hasSuffix("на Яндекс.Музыке"))!) {
tempTitle = (tab.name)!
break
// }
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
tempTitle = (tab.name)!
break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
tempTitle = (tab.name)!
break
}
}
}
} else if ident == .Chrome {
let chromeApplication = musicPlayer as GoogleChromeApplication
let chromeWindows = chromeApplication.windows?().compactMap({ $0 as? GoogleChromeWindow })
for window in chromeWindows! {
for tab in window.tabs!() {
let tab = tab as! GoogleChromeTab
if (tab.URL?.starts(with: "https://music.yandex.ru"))! {
if !(tab.title?.hasSuffix("на Яндекс.Музыке"))! {
tempTitle = tab.title!
break
}
} else if (tab.URL?.starts(with: "https://vk.com/audios"))! || (tab.URL?.starts(with: "https://vk.com/music"))! {
tempTitle = tab.title!
break
} else if (tab.URL?.starts(with: "https://www.youtube.com/watch"))! {
tempTitle = tab.title!
break
}
}
}
}
if tempTitle == self.songTitle {
return
} else {
self.songTitle = tempTitle
}
if let songTitle = self.songTitle?.ifNotEmpty {
self.timer?.invalidate()
self.timer = nil
if (disableMarquee) {
self.title = " " + songTitle
} else {
self.title = " " + songTitle + " "
self.timer = Timer.scheduledTimer(timeInterval: 0.25, target: self, selector: #selector(self.marquee), userInfo: nil, repeats: true)
}
titleUpdated = true
}
if let _ = tempTitle.ifNotEmpty,
let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: ident.rawValue) {
let image = NSWorkspace.shared.icon(forFile: appPath)
image.size = self.iconSize
self.image = image
iconUpdated = true
}
break
}
}
}
DispatchQueue.main.async {
if !iconUpdated {
self.image = nil
}
if !titleUpdated {
self.title = ""
}
}
}
}
@objc protocol SpotifyApplication {
@objc optional var currentTrack: SpotifyTrack { get }
@objc optional func nextTrack()
@objc optional func previousTrack()
@objc optional func playpause()
}
extension SBApplication: SpotifyApplication {}
@objc protocol SpotifyTrack {
@objc optional var artist: String { get }
@objc optional var name: String { get }
}
extension SBObject: SpotifyTrack {}
extension SpotifyApplication {
var title: String {
guard let t = currentTrack else { return "" }
return (t.artist ?? "") + " — " + (t.name ?? "")
}
}
@objc protocol iTunesApplication {
@objc optional var currentTrack: iTunesTrack { get }
@objc optional func playpause()
@objc optional func nextTrack()
@objc optional func previousTrack()
}
extension SBApplication: iTunesApplication {}
@objc protocol iTunesTrack {
@objc optional var artist: String { get }
@objc optional var name: String { get }
}
extension SBObject: iTunesTrack {}
extension iTunesApplication {
var title: String {
guard let t = currentTrack else { return "" }
return (t.artist ?? "") + " — " + (t.name ?? "")
}
}
@objc protocol MusicApplication {
@objc optional var currentTrack: MusicTrack { get }
@objc optional func playpause()
@objc optional func nextTrack()
@objc optional func previousTrack()
}
extension SBApplication: MusicApplication {}
@objc protocol MusicTrack {
@objc optional var artist: String { get }
@objc optional var name: String { get }
}
extension SBObject: MusicTrack {}
extension MusicApplication {
var title: String {
guard let t = currentTrack else { return "" }
return (t.artist ?? "") + " — " + (t.name ?? "")
}
}
@objc protocol VoxApplication {
@objc optional func playpause()
@objc optional func next()
@objc optional func previous()
@objc optional var track: String { get }
@objc optional var artist: String { get }
}
extension SBApplication: VoxApplication {}
extension VoxApplication {
var title: String {
return (artist ?? "") + " — " + (track ?? "")
}
}
@objc public protocol SBObjectProtocol: NSObjectProtocol {
func get() -> Any!
}
@objc public protocol SBApplicationProtocol: SBObjectProtocol {
func activate()
var delegate: SBApplicationDelegate! { get set }
}
@objc public protocol SafariApplication: SBApplicationProtocol {
@objc optional func windows() -> SBElementArray
@objc optional func doJavaScript(_ x: String!, in in_: Any!) -> Any // Applies a string of JavaScript code to a document.
}
extension SBApplication: SafariApplication {}
@objc public protocol SafariWindow: SBObjectProtocol {
@objc optional var name: String { get } // The title of the window.
@objc optional func tabs() -> SBElementArray
// @objc optional var document: SafariDocument { get } // The document whose contents are displayed in the window.
// @objc optional func setCurrentTab(_ currentTab: SafariTab!) // The current tab.
}
extension SBObject: SafariWindow {}
// @objc public protocol SafariDocument: SBObjectProtocol {
// @objc optional var name: String { get } // Its name.
// @objc optional var URL: String { get } // The current URL of the document.
// }
// extension SBObject: SafariDocument {}
@objc public protocol SafariTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab.
@objc optional var name: String { get } // The name of the tab.
}
extension SBObject: SafariTab {}
@objc public protocol GoogleChromeApplication: SBApplicationProtocol {
@objc optional func windows() -> SBElementArray
@objc optional func executeJavaScript(javascript: String!) -> Any // Applies a string of JavaScript code to a document. //, id: Any!
}
extension SBApplication: GoogleChromeApplication {}
@objc public protocol GoogleChromeWindow: SBObjectProtocol {
@objc optional var name: String { get } // The title of the window.
@objc optional func tabs() -> SBElementArray
}
extension SBObject: GoogleChromeWindow {}
@objc public protocol GoogleChromeTab: SBObjectProtocol {
@objc optional var URL: String { get } // The current URL of the tab.
@objc optional var title: String { get } // The name of the tab.
}
extension SBObject: GoogleChromeTab {}
-149
View File
@@ -1,149 +0,0 @@
//
// NetworkBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 23/02/2019.
// Copyright © 2019 Anton Palgunov. All rights reserved.
//
import Foundation
class NetworkBarItem: CustomButtonTouchBarItem, Widget {
static var name: String = "network"
static var identifier: String = "com.toxblh.mtmr.network"
private let flip: Bool
init(identifier: NSTouchBarItem.Identifier, flip: Bool = false) {
self.flip = flip
super.init(identifier: identifier, title: " ")
startMonitoringProcess()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func startMonitoringProcess() {
var pipe: Pipe
var outputHandle: FileHandle
var bandwidthProcess: Process?
var dSpeed: UInt64?
var uSpeed: UInt64?
var curr: Array<Substring>?
var dataAvailable: NSObjectProtocol?
pipe = Pipe()
bandwidthProcess = Process()
bandwidthProcess?.launchPath = "/usr/bin/env"
bandwidthProcess?.arguments = ["netstat", "-w1", "-l", "en0"]
bandwidthProcess?.standardOutput = pipe
outputHandle = pipe.fileHandleForReading
outputHandle.waitForDataInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
dataAvailable = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outputHandle,
queue: nil
) { _ -> Void in
let data = pipe.fileHandleForReading.availableData
if data.count > 0 {
if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
curr = [""]
curr = str
.replacingOccurrences(of: " ", with: " ")
.split(separator: " ")
if curr == nil || (curr?.count)! < 6 {} else {
if Int64(curr![2]) == nil {} else {
dSpeed = UInt64(curr![2])
uSpeed = UInt64(curr![5])
self.setTitle(up: self.getHumanizeSize(speed: uSpeed!), down: self.getHumanizeSize(speed: dSpeed!))
}
}
}
outputHandle.waitForDataInBackgroundAndNotify()
} else if let dataAvailable = dataAvailable {
NotificationCenter.default.removeObserver(dataAvailable)
}
}
var dataReady: NSObjectProtocol?
dataReady = NotificationCenter.default.addObserver(
forName: Process.didTerminateNotification,
object: outputHandle,
queue: nil
) { _ -> Void in
print("Task terminated!")
if let observer = dataReady {
NotificationCenter.default.removeObserver(observer)
}
}
bandwidthProcess?.launch()
}
func getHumanizeSize(speed: UInt64) -> String {
let humanText: String
if speed < 1024 {
humanText = String(format: "%.0f", Double(speed)) + " B/s"
} else if speed < (1024 * 1024) {
humanText = String(format: "%.1f", Double(speed) / 1024) + " KB/s"
} else if speed < (1024 * 1024 * 1024) {
humanText = String(format: "%.1f", Double(speed) / (1024 * 1024)) + " MB/s"
} else {
humanText = String(format: "%.2f", Double(speed) / (1024 * 1024 * 1024)) + " GB/s"
}
return humanText
}
func appendUpSpeed(appendString: NSMutableAttributedString, up: String, titleFont: NSFont, newStr: Bool = false) {
appendString.append(NSMutableAttributedString(
string: newStr ? "\n↑" : "↑",
attributes: [
NSAttributedString.Key.foregroundColor: NSColor.blue,
NSAttributedString.Key.font: titleFont,
]))
appendString.append(NSMutableAttributedString(
string: up,
attributes: [
NSAttributedString.Key.font: titleFont,
]))
}
func appendDownSpeed(appendString: NSMutableAttributedString, down: String, titleFont: NSFont, newStr: Bool = false) {
appendString.append(NSMutableAttributedString(
string: newStr ? "\n↓" : "↓",
attributes: [
NSAttributedString.Key.foregroundColor: NSColor.red,
NSAttributedString.Key.font: titleFont,
]))
appendString.append(NSMutableAttributedString(
string: down,
attributes: [
NSAttributedString.Key.font: titleFont
]))
}
func setTitle(up: String, down: String) {
let titleFont = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: NSFont.Weight.light)
let newTitle: NSMutableAttributedString = NSMutableAttributedString(string: "")
if (self.flip) {
appendUpSpeed(appendString: newTitle, up: up, titleFont: titleFont)
appendDownSpeed(appendString: newTitle, down: down, titleFont: titleFont, newStr: true)
} else {
appendDownSpeed(appendString: newTitle, down: down, titleFont: titleFont)
appendUpSpeed(appendString: newTitle, up: up, titleFont: titleFont, newStr: true)
}
self.attributedTitle = newTitle
}
}
-53
View File
@@ -1,53 +0,0 @@
//
// NightShiftBarItem.swift
// MTMR
//
// Created by Anton Palgunov on 28/08/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Foundation
class NightShiftBarItem: CustomButtonTouchBarItem {
private let nsclient = CBBlueLightClient()
private var timer: Timer!
private var blueLightStatus: Status {
var status: Status = Status()
nsclient.getBlueLightStatus(&status)
return status
}
private var isNightShiftEnabled: Bool {
return blueLightStatus.enabled.boolValue
}
private func setNightShift(state: Bool) {
nsclient.setEnabled(state)
}
init(identifier: NSTouchBarItem.Identifier) {
super.init(identifier: identifier, title: "")
isBordered = false
setWidth(value: 28)
tapClosure = { [weak self] in self?.nightShiftAction() }
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(refresh), userInfo: nil, repeats: true)
refresh()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func nightShiftAction() {
setNightShift(state: !isNightShiftEnabled)
refresh()
}
@objc func refresh() {
image = isNightShiftEnabled ? #imageLiteral(resourceName: "nightShiftOn") : #imageLiteral(resourceName: "nightShiftOff")
}
}
-124
View File
@@ -1,124 +0,0 @@
//
// PomodoroBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 10.05.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class PomodoroBarItem: CustomButtonTouchBarItem, Widget {
static let identifier = "com.toxblh.mtmr.pomodoro."
static let name = "pomodoro"
static let decoder: ParametersDecoder = { decoder in
enum CodingKeys: String, CodingKey {
case workTime
case restTime
}
let container = try decoder.container(keyedBy: CodingKeys.self)
let workTime = try container.decodeIfPresent(Double.self, forKey: .workTime)
let restTime = try container.decodeIfPresent(Double.self, forKey: .restTime)
return (
item: .pomodoro(workTime: workTime ?? 1500.00, restTime: restTime ?? 300),
action: .none,
longAction: .none,
parameters: [:]
)
}
private enum TimeTypes {
case work
case rest
case none
}
private let defaultTitle = "🍅 "
private let workTime: TimeInterval
private let restTime: TimeInterval
private var typeTime: TimeTypes = .none
private var timer: DispatchSourceTimer?
private var timeLeft: Int = 0
private var timeLeftString: String {
return String(format: "%.2i:%.2i", timeLeft / 60, timeLeft % 60)
}
init(identifier: NSTouchBarItem.Identifier, workTime: TimeInterval, restTime: TimeInterval) {
self.workTime = workTime
self.restTime = restTime
super.init(identifier: identifier, title: defaultTitle)
tapClosure = { [weak self] in self?.startStopWork() }
longTapClosure = { [weak self] in self?.startStopRest() }
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
timer?.cancel()
timer = nil
}
@objc func startStopWork() {
typeTime = .work
startStopTimer()
}
@objc func startStopRest() {
typeTime = .rest
startStopTimer()
}
func startStopTimer() {
timer == nil ? start() : reset()
}
private func start() {
timeLeft = Int(typeTime == .work ? workTime : restTime)
let queue: DispatchQueue = DispatchQueue(label: "Timer")
timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
timer?.schedule(deadline: .now(), repeating: .seconds(1), leeway: .never)
timer?.setEventHandler(handler: tick)
timer?.resume()
NSSound.beep()
}
private func finish() {
if typeTime != .none {
sendNotification()
}
reset()
}
private func reset() {
typeTime = .none
timer?.cancel()
timer = nil
title = defaultTitle
}
private func tick() {
timeLeft -= 1
DispatchQueue.main.async {
if self.timeLeft >= 0 {
self.title = self.defaultTitle + " " + self.timeLeftString
} else {
self.finish()
}
}
}
private func sendNotification() {
let notification: NSUserNotification = NSUserNotification()
notification.title = "Pomodoro"
notification.informativeText = typeTime == .work ? "it's time to rest your mind!" : "It's time to work!"
notification.soundName = "Submarine"
NSUserNotificationCenter.default.deliver(notification)
}
}
-28
View File
@@ -1,28 +0,0 @@
import Cocoa
class TimeTouchBarItem: CustomButtonTouchBarItem {
private let dateFormatter = DateFormatter()
private var timer: Timer!
init(identifier: NSTouchBarItem.Identifier, formatTemplate: String, timeZone: String? = nil, locale: String? = nil) {
dateFormatter.dateFormat = formatTemplate
if let locale = locale {
dateFormatter.locale = Locale(identifier: locale)
}
if let abbr = timeZone {
dateFormatter.timeZone = TimeZone(abbreviation: abbr)
}
super.init(identifier: identifier, title: " ")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
isBordered = false
updateTime()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateTime() {
title = dateFormatter.string(from: Date())
}
}
-102
View File
@@ -1,102 +0,0 @@
import AppKit
import AVFoundation
import Cocoa
import CoreAudio
class VolumeViewController: NSCustomTouchBarItem {
private(set) var sliderItem: CustomSlider!
init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) {
super.init(identifier: identifier)
var forPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster
)
AudioObjectAddPropertyListenerBlock(defaultDeviceID, &forPropertyAddress, nil, audioObjectPropertyListenerBlock)
if image == nil {
sliderItem = CustomSlider()
} else {
sliderItem = CustomSlider(knob: image!)
}
sliderItem.target = self
sliderItem.action = #selector(VolumeViewController.sliderValueChanged(_:))
sliderItem.minValue = 0.0
sliderItem.maxValue = 100.0
sliderItem.floatValue = getInputGain() * 100
view = sliderItem
}
func audioObjectPropertyListenerBlock(numberAddresses _: UInt32, addresses _: UnsafePointer<AudioObjectPropertyAddress>) {
DispatchQueue.main.async {
self.sliderItem.floatValue = self.getInputGain() * 100
}
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
sliderItem.unbind(NSBindingName.value)
}
@objc func sliderValueChanged(_ sender: Any) {
if let sliderItem = sender as? NSSlider {
_ = setInputGain(Float32(sliderItem.intValue) / 100.0)
}
}
private var defaultDeviceID: AudioObjectID {
var deviceID: AudioObjectID = AudioObjectID(0)
var size: UInt32 = UInt32(MemoryLayout<AudioObjectID>.size)
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice)
address.mScope = AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &size, &deviceID)
return deviceID
}
private func getInputGain() -> Float32 {
var volume: Float32 = 0.5
var size: UInt32 = UInt32(MemoryLayout.size(ofValue: volume))
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
AudioObjectGetPropertyData(defaultDeviceID, &address, 0, nil, &size, &volume)
return volume
}
private func setInputGain(_ volume: Float32) -> OSStatus {
var inputVolume: Float32 = volume
if inputVolume == 0.0 {
_ = setMute(mute: 1)
} else {
_ = setMute(mute: 0)
}
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume))
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume)
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume)
}
private func setMute(mute: Int) -> OSStatus {
var muteVal: Int = mute
var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress()
address.mSelector = AudioObjectPropertySelector(kAudioDevicePropertyMute)
let size: UInt32 = UInt32(MemoryLayout.size(ofValue: muteVal))
address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput)
address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster)
return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &muteVal)
}
}
-142
View File
@@ -1,142 +0,0 @@
//
// WeatherBarItem.swift
// MTMR
//
// Created by Daniel Apatin on 18.04.2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class WeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private let activity: NSBackgroundActivityScheduler
private var units: String
private var api_key: String
private var units_str = "°F"
private var prev_location: CLLocation!
private var location: CLLocation!
private let iconsImages = ["01d": "☀️", "01n": "☀️", "02d": "⛅️", "02n": "⛅️", "03d": "☁️", "03n": "☁️", "04d": "☁️", "04n": "☁️", "09d": "⛅️", "09n": "⛅️", "10d": "🌦", "10n": "🌦", "11d": "🌩", "11n": "🌩", "13d": "❄️", "13n": "❄️", "50d": "🌫", "50n": "🌫"]
private let iconsText = ["01d": "☀", "01n": "☀", "02d": "☁", "02n": "☁", "03d": "☁", "03n": "☁", "04d": "☁", "04n": "☁", "09d": "☂", "09n": "☂", "10d": "☂", "10n": "☂", "11d": "☈", "11n": "☈", "13d": "☃", "13n": "☃", "50d": "♨", "50n": "♨"]
private var iconsSource: Dictionary<String, String>
private var manager: CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval, units: String, api_key: String, icon_type: String? = "text") {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
self.units = units
self.api_key = api_key
if self.units == "metric" {
units_str = "°C"
}
if self.units == "imperial" {
units_str = "°F"
}
if icon_type == "images" {
iconsSource = iconsImages
} else {
iconsSource = iconsText
}
super.init(identifier: identifier, title: "⏳")
let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied {
print("User permission not given")
return
}
if !CLLocationManager.locationServicesEnabled() {
print("Location services not enabled")
return
}
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateWeather()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateWeather()
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation()
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateWeather() {
if location != nil {
let urlRequest = URLRequest(url: URL(string: "https://api.openweathermap.org/data/2.5/weather?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)&units=\(units)&appid=\(api_key)")!)
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]
// print(json)
var temperature: Int!
var condition_icon = ""
if let main = json["main"] as? [String: AnyObject] {
if let temp = main["temp"] as? Double {
temperature = Int(temp)
}
}
if let weather = json["weather"] as? NSArray, let item = weather[0] as? NSDictionary {
let icon = item["icon"] as! String
if let test = self.iconsSource[icon] {
condition_icon = test
}
}
if temperature != nil {
DispatchQueue.main.async {
self.setWeather(text: "\(condition_icon) \(temperature!)\(self.units_str)")
}
}
} catch let jsonError {
print(jsonError.localizedDescription)
}
}
}
task.resume()
}
}
func setWeather(text: String) {
title = text
}
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
location = lastLocation
if prev_location == nil {
updateWeather()
}
prev_location = lastLocation
}
func locationManager(_: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
// print("inside didChangeAuthorization ");
updateWeather()
}
deinit {
activity.invalidate()
}
}
-12
View File
@@ -1,12 +0,0 @@
//
// WidgetProtocol.swift
// MTMR
//
// Created by Anton Palgunov on 20/10/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
protocol Widget {
static var name: String { get }
static var identifier: String { get }
}
-152
View File
@@ -1,152 +0,0 @@
//
// YandexWeatherBarItem.swift
// MTMR
//
// Created by bobrosoft on 22/07/2019.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
import CoreLocation
class YandexWeatherBarItem: CustomButtonTouchBarItem, CLLocationManagerDelegate {
private let activity: NSBackgroundActivityScheduler
private let unitsStr = "°C"
private let iconsSource = [
"Ясно": "☀️", "Малооблачно": "🌤", "Облачно с прояснениями": "⛅️", "Пасмурно": "☁️", "Небольшой дождь": "🌦", "Дождь": "🌧", "Ливень": "⛈", "Гроза": "🌩", "Дождь со снегом": "☔️", "Небольшой снег": "❄️", "Снег": "🌨", "Туман": "🌫",
"Clear": "☀️", "Mostly clear": "🌤", "Partly cloudy": "⛅️", "Overcast": "☁️", "Light rain": "🌦", "Rain": "🌧", "Heavy rain": "⛈", "Storm": "🌩", "Sleet": "☔️", "Light snow": "❄️", "Snow": "🌨", "Fog": "🌫"
]
private var location: CLLocation!
private var prevLocation: CLLocation!
private var manager: CLLocationManager!
init(identifier: NSTouchBarItem.Identifier, interval: TimeInterval) {
activity = NSBackgroundActivityScheduler(identifier: "\(identifier.rawValue).updatecheck")
activity.interval = interval
super.init(identifier: identifier, title: "⏳")
let status = CLLocationManager.authorizationStatus()
if status == .restricted || status == .denied {
print("User permission not given")
return
}
if !CLLocationManager.locationServicesEnabled() {
print("Location services not enabled")
return
}
activity.repeats = true
activity.qualityOfService = .utility
activity.schedule { (completion: NSBackgroundActivityScheduler.CompletionHandler) in
self.updateWeather()
completion(NSBackgroundActivityScheduler.Result.finished)
}
updateWeather()
manager = CLLocationManager()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation()
tapClosure = tapClosure ?? defaultTapAction
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func updateWeather() {
var urlRequest = URLRequest(url: URL(string: getWeatherUrl())!)
urlRequest.addValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", forHTTPHeaderField: "user-agent") // important for the right format
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
guard error == nil, let response = data?.utf8string else {
return
}
// print(response)
var matches: [[String]]
var temperature: String?
matches = response.matchingStrings(regex: "fact__temp.*?temp__value.*?>(.*?)<")
temperature = matches.first?.item(at: 1)
var icon: String?
matches = response.matchingStrings(regex: "link__condition.*?>(.*?)<")
icon = matches.first?.item(at: 1)
if let _ = icon, let test = self.iconsSource[icon!] {
icon = test
}
if temperature != nil {
DispatchQueue.main.async {
self.setWeather(text: "\(icon ?? "?") \(temperature!)\(self.unitsStr)")
}
}
}
task.resume()
}
func getWeatherUrl() -> String {
if location != nil {
return "https://yandex.ru/pogoda/?lat=\(location.coordinate.latitude)&lon=\(location.coordinate.longitude)?lang=ru"
} else {
return "https://yandex.ru/pogoda/?lang=ru" // Yandex will try to determine your location by default
}
}
func setWeather(text: String) {
title = text
}
func defaultTapAction() {
print(getWeatherUrl())
if let url = URL(string: getWeatherUrl()) {
NSWorkspace.shared.open(url)
}
}
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let lastLocation = locations.last!
location = lastLocation
if prevLocation == nil {
updateWeather()
}
prevLocation = lastLocation
}
func locationManager(_: CLLocationManager, didFailWithError error: Error) {
print(error)
}
func locationManager(_: CLLocationManager, didChangeAuthorization _: CLAuthorizationStatus) {
updateWeather()
}
deinit {
activity.invalidate()
}
}
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0 ..< result.numberOfRanges).map {
result.range(at: $0).location != NSNotFound
? nsString.substring(with: result.range(at: $0))
: ""
}
}
}
}
extension Array {
func item(at index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
+17
View File
@@ -0,0 +1,17 @@
//
// WindowController.swift
// MTMR
//
// Created by Anton Palgunov on 17/03/2018.
// Copyright © 2018 Anton Palgunov. All rights reserved.
//
import Cocoa
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
}
}
@@ -3,27 +3,27 @@ set percent to word 6 of theList's item 2
set charge to word 7 of theList's item 2
if (charge = "charging") then
set iconC to "⚡️"
set iconC to "⚡️"
else
set iconC to ""
set iconC to ""
end if
set remainingRaw to my split(theList's item 2, " ")
set remainingTime to remainingRaw's item 5
if (remainingTime = "(no") then
set strTime to " (?)"
set strTime to " (?)"
else if (remainingTime = "0:00") then
set strTime to ""
set strTime to ""
else
set strTime to " (" & remainingTime & ")"
set strTime to " (" & remainingTime & ")"
end if
return iconC & percent & "%" & strTime
to split(someText, delimiter)
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""}
return someText
set AppleScript's text item delimiters to delimiter
set someText to someText's text items
set AppleScript's text item delimiters to {""}
return someText
end split

Some files were not shown because too many files have changed in this diff Show More