diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index ae0a21d9..00000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "Frameworks/Haxcessibility"] - path = Frameworks/Haxcessibility - url = git://github.com/numist/Haxcessibility.git -[submodule "Frameworks/ReactiveCocoa"] - path = Frameworks/ReactiveCocoa - url = git://github.com/numist/ReactiveCocoa.git diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 93045150..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-2.1.0 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 23354af4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: objective-c -xcode_project: Switch.xcodeproj -xcode_scheme: Switch -osx_image: xcode9 - -before_script: - - export LANG=en_US.UTF-8 - -install: - - gem install rake - - rake deps - -script: - - rake app - - rake test diff --git a/Dependencies/Defaults.gitcheckout b/Dependencies/Defaults.gitcheckout new file mode 100644 index 00000000..b377c8b7 --- /dev/null +++ b/Dependencies/Defaults.gitcheckout @@ -0,0 +1 @@ +master:93fbdf7f09a0887ca49d35752ebc31735f987c7a diff --git a/Dependencies/Defaults.giturl b/Dependencies/Defaults.giturl new file mode 100644 index 00000000..e7b808a9 --- /dev/null +++ b/Dependencies/Defaults.giturl @@ -0,0 +1 @@ +git@github.com:sindresorhus/Defaults.git \ No newline at end of file diff --git a/Dependencies/Defaults/.editorconfig b/Dependencies/Defaults/.editorconfig new file mode 100644 index 00000000..aaac3258 --- /dev/null +++ b/Dependencies/Defaults/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = tab +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/Dependencies/Defaults/.gitattributes b/Dependencies/Defaults/.gitattributes new file mode 100644 index 00000000..6313b56c --- /dev/null +++ b/Dependencies/Defaults/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/Dependencies/Defaults/.github/funding.yml b/Dependencies/Defaults/.github/funding.yml new file mode 100644 index 00000000..15edf6e2 --- /dev/null +++ b/Dependencies/Defaults/.github/funding.yml @@ -0,0 +1,4 @@ +github: sindresorhus +open_collective: sindresorhus +patreon: sindresorhus +custom: https://sindresorhus.com/donate diff --git a/Dependencies/Defaults/.gitignore b/Dependencies/Defaults/.gitignore new file mode 100644 index 00000000..0854237e --- /dev/null +++ b/Dependencies/Defaults/.gitignore @@ -0,0 +1,4 @@ +/.build +/Packages +xcuserdata +project.xcworkspace diff --git a/Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata similarity index 72% rename from Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata index 911cb93c..919434a6 100644 --- a/Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Dependencies/Defaults/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Dependencies/Defaults/.travis.yml b/Dependencies/Defaults/.travis.yml new file mode 100644 index 00000000..3087b646 --- /dev/null +++ b/Dependencies/Defaults/.travis.yml @@ -0,0 +1,3 @@ +language: swift +osx_image: xcode11.4 +script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS diff --git a/Dependencies/Defaults/Configs/Defaults.plist b/Dependencies/Defaults/Configs/Defaults.plist new file mode 100644 index 00000000..e49db7f5 --- /dev/null +++ b/Dependencies/Defaults/Configs/Defaults.plist @@ -0,0 +1,22 @@ + + + + + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + MIT License © Sindre Sorhus + + diff --git a/SwitchTests/SwitchTests-Info.plist b/Dependencies/Defaults/Configs/DefaultsTests.plist similarity index 66% rename from SwitchTests/SwitchTests-Info.plist rename to Dependencies/Defaults/Configs/DefaultsTests.plist index d6837796..5ad566df 100644 --- a/SwitchTests/SwitchTests-Info.plist +++ b/Dependencies/Defaults/Configs/DefaultsTests.plist @@ -2,20 +2,18 @@ - CFBundleDevelopmentRegion - en CFBundleExecutable - ${EXECUTABLE_NAME} + $(EXECUTABLE_NAME) CFBundleIdentifier - net.numist.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleName + $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? + 0.0.0 CFBundleVersion 1 diff --git a/Dependencies/Defaults/Defaults.podspec b/Dependencies/Defaults/Defaults.podspec new file mode 100644 index 00000000..82d8a4f4 --- /dev/null +++ b/Dependencies/Defaults/Defaults.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + s.name = 'Defaults' + s.version = '4.1.0' + s.summary = 'Swifty and modern UserDefaults' + s.license = 'MIT' + s.homepage = 'https://github.com/sindresorhus/Defaults' + s.social_media_url = 'https://twitter.com/sindresorhus' + s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' } + s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" } + s.source_files = 'Sources/**/*.swift' + s.swift_version = '5.3' + s.macos.deployment_target = '10.12' + s.ios.deployment_target = '10.0' + s.tvos.deployment_target = '10.0' + s.watchos.deployment_target = '3.0' + s.weak_framework = 'Combine' +end diff --git a/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj b/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj new file mode 100644 index 00000000..966867de --- /dev/null +++ b/Dependencies/Defaults/Defaults.xcodeproj/project.pbxproj @@ -0,0 +1,1168 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 52D6D9871BEFF229002C0205 /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Defaults.framework */; }; + 8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; }; + 8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; }; + 8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; }; + 8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7841EB5B820000D00A4 /* Defaults.swift */; }; + 8933C78E1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; }; + 8933C78F1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; }; + 8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; }; + DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */; }; + DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */; }; + E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; }; + E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; }; + E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; }; + E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; }; + E339B3B32449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; }; + E339B3B42449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; }; + E339B3B52449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; }; + E339B3B62449ED2000E7A40A /* Reset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B22449ED2000E7A40A /* Reset.swift */; }; + E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; }; + E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; }; + E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; }; + E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E339B3B72449F10D00E7A40A /* UserDefaults.swift */; }; + E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; }; + E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; }; + E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; }; + E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; }; + E3EB3E33216505920033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; }; + E3EB3E35216507AE0033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; }; + E3EB3E36216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; }; + E3EB3E37216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; }; + E3EB3E38216507B60033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; }; + E3EB3E39216507C30033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; }; + E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; }; + E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52D6D97B1BEFF229002C0205; + remoteInfo = Defaults; + }; + DD7502801C68FCFC006590AF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52D6DA0E1BF000BD002C0205; + remoteInfo = "Defaults-macOS"; + }; + DD7502931C690C7A006590AF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52D6D9EF1BEFFFBE002C0205; + remoteInfo = "Defaults-tvOS"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 52D6D97C1BEFF229002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 52D6DA0F1BF000BD002C0205 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6614F6E222FC6E1C00B0C9CE /* readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; lineEnding = 0; path = readme.md; sourceTree = ""; usesTabs = 1; }; + 8933C7841EB5B820000D00A4 /* Defaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Defaults.swift; sourceTree = ""; usesTabs = 1; }; + 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = DefaultsTests.swift; sourceTree = ""; usesTabs = 1; }; + AD2FAA261CD0B6D800659CF4 /* Defaults.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = ""; }; + AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DefaultsTests.plist; sourceTree = ""; }; + DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + E286D0C623B8D51100570D1E /* Observation+Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Observation+Combine.swift"; sourceTree = ""; usesTabs = 1; }; + E339B3B22449ED2000E7A40A /* Reset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Reset.swift; sourceTree = ""; usesTabs = 1; }; + E339B3B72449F10D00E7A40A /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = ""; usesTabs = 1; }; + E38C9F26244ADA2F00A6737A /* SwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SwiftUI.swift; sourceTree = ""; usesTabs = 1; }; + E3EB3E32216505920033B089 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Utilities.swift; sourceTree = ""; usesTabs = 1; }; + E3EB3E34216507AE0033B089 /* Observation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observation.swift; sourceTree = ""; usesTabs = 1; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 52D6D9781BEFF229002C0205 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9831BEFF229002C0205 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 52D6D9871BEFF229002C0205 /* Defaults.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9DE1BEFFF6E002C0205 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9EC1BEFFFBE002C0205 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6DA0B1BF000BD002C0205 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD7502771C68FCFC006590AF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD75028A1C690C7A006590AF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 52D6D9721BEFF229002C0205 = { + isa = PBXGroup; + children = ( + 6614F6E222FC6E1C00B0C9CE /* readme.md */, + 8933C7811EB5B7E0000D00A4 /* Sources */, + 8933C7831EB5B7EB000D00A4 /* Tests */, + 52D6D99C1BEFF38C002C0205 /* Configs */, + 52D6D97D1BEFF229002C0205 /* Products */, + ); + sourceTree = ""; + usesTabs = 1; + }; + 52D6D97D1BEFF229002C0205 /* Products */ = { + isa = PBXGroup; + children = ( + 52D6D97C1BEFF229002C0205 /* Defaults.framework */, + 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */, + 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */, + 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */, + 52D6DA0F1BF000BD002C0205 /* Defaults.framework */, + DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */, + DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 52D6D99C1BEFF38C002C0205 /* Configs */ = { + isa = PBXGroup; + children = ( + DD7502721C68FC1B006590AF /* Frameworks */, + DD7502731C68FC20006590AF /* Tests */, + ); + path = Configs; + sourceTree = ""; + }; + 8933C7811EB5B7E0000D00A4 /* Sources */ = { + isa = PBXGroup; + children = ( + E30E93D822E9425E00530C8F /* Defaults */, + ); + path = Sources; + sourceTree = ""; + }; + 8933C7831EB5B7EB000D00A4 /* Tests */ = { + isa = PBXGroup; + children = ( + 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */, + ); + name = Tests; + path = Tests/DefaultsTests; + sourceTree = ""; + }; + DD7502721C68FC1B006590AF /* Frameworks */ = { + isa = PBXGroup; + children = ( + AD2FAA261CD0B6D800659CF4 /* Defaults.plist */, + ); + name = Frameworks; + sourceTree = ""; + }; + DD7502731C68FC20006590AF /* Tests */ = { + isa = PBXGroup; + children = ( + AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */, + ); + name = Tests; + sourceTree = ""; + }; + E30E93D822E9425E00530C8F /* Defaults */ = { + isa = PBXGroup; + children = ( + 8933C7841EB5B820000D00A4 /* Defaults.swift */, + E339B3B72449F10D00E7A40A /* UserDefaults.swift */, + E339B3B22449ED2000E7A40A /* Reset.swift */, + E3EB3E34216507AE0033B089 /* Observation.swift */, + E286D0C623B8D51100570D1E /* Observation+Combine.swift */, + E38C9F26244ADA2F00A6737A /* SwiftUI.swift */, + E3EB3E32216505920033B089 /* Utilities.swift */, + ); + path = Defaults; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 52D6D9791BEFF229002C0205 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9DF1BEFFF6E002C0205 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9ED1BEFFFBE002C0205 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6DA0C1BF000BD002C0205 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 52D6D97B1BEFF229002C0205 /* Defaults-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS" */; + buildPhases = ( + 52D6D9771BEFF229002C0205 /* Sources */, + 52D6D9781BEFF229002C0205 /* Frameworks */, + 52D6D9791BEFF229002C0205 /* Headers */, + 52D6D97A1BEFF229002C0205 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Defaults-iOS"; + productName = Defaults; + productReference = 52D6D97C1BEFF229002C0205 /* Defaults.framework */; + productType = "com.apple.product-type.framework"; + }; + 52D6D9851BEFF229002C0205 /* Defaults-iOS Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS Tests" */; + buildPhases = ( + 52D6D9821BEFF229002C0205 /* Sources */, + 52D6D9831BEFF229002C0205 /* Frameworks */, + 52D6D9841BEFF229002C0205 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 52D6D9891BEFF229002C0205 /* PBXTargetDependency */, + ); + name = "Defaults-iOS Tests"; + productName = DefaultsTests; + productReference = 52D6D9861BEFF229002C0205 /* Defaults-iOS Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 52D6D9E11BEFFF6E002C0205 /* Defaults-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Defaults-watchOS" */; + buildPhases = ( + 52D6D9DD1BEFFF6E002C0205 /* Sources */, + 52D6D9DE1BEFFF6E002C0205 /* Frameworks */, + 52D6D9DF1BEFFF6E002C0205 /* Headers */, + 52D6D9E01BEFFF6E002C0205 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Defaults-watchOS"; + productName = "Defaults-watchOS"; + productReference = 52D6D9E21BEFFF6E002C0205 /* Defaults.framework */; + productType = "com.apple.product-type.framework"; + }; + 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Defaults-tvOS" */; + buildPhases = ( + 52D6D9EB1BEFFFBE002C0205 /* Sources */, + 52D6D9EC1BEFFFBE002C0205 /* Frameworks */, + 52D6D9ED1BEFFFBE002C0205 /* Headers */, + 52D6D9EE1BEFFFBE002C0205 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Defaults-tvOS"; + productName = "Defaults-tvOS"; + productReference = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */; + productType = "com.apple.product-type.framework"; + }; + 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Defaults-macOS" */; + buildPhases = ( + 52D6DA0A1BF000BD002C0205 /* Sources */, + 52D6DA0B1BF000BD002C0205 /* Frameworks */, + 52D6DA0C1BF000BD002C0205 /* Headers */, + 52D6DA0D1BF000BD002C0205 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Defaults-macOS"; + productName = "Defaults-macOS"; + productReference = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */; + productType = "com.apple.product-type.framework"; + }; + DD7502791C68FCFC006590AF /* Defaults-macOS Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Defaults-macOS Tests" */; + buildPhases = ( + DD7502761C68FCFC006590AF /* Sources */, + DD7502771C68FCFC006590AF /* Frameworks */, + DD7502781C68FCFC006590AF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DD7502811C68FCFC006590AF /* PBXTargetDependency */, + ); + name = "Defaults-macOS Tests"; + productName = "Defaults-OS Tests"; + productReference = DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DD75028C1C690C7A006590AF /* Defaults-tvOS Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Defaults-tvOS Tests" */; + buildPhases = ( + DD7502891C690C7A006590AF /* Sources */, + DD75028A1C690C7A006590AF /* Frameworks */, + DD75028B1C690C7A006590AF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DD7502941C690C7A006590AF /* PBXTargetDependency */, + ); + name = "Defaults-tvOS Tests"; + productName = "Defaults-tvOS Tests"; + productReference = DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 52D6D9731BEFF229002C0205 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 1200; + ORGANIZATIONNAME = Defaults; + TargetAttributes = { + 52D6D97B1BEFF229002C0205 = { + CreatedOnToolsVersion = 7.1; + LastSwiftMigration = 1020; + }; + 52D6D9851BEFF229002C0205 = { + CreatedOnToolsVersion = 7.1; + LastSwiftMigration = 1020; + }; + 52D6D9E11BEFFF6E002C0205 = { + CreatedOnToolsVersion = 7.1; + LastSwiftMigration = 1020; + }; + 52D6D9EF1BEFFFBE002C0205 = { + CreatedOnToolsVersion = 7.1; + LastSwiftMigration = 1020; + }; + 52D6DA0E1BF000BD002C0205 = { + CreatedOnToolsVersion = 7.1; + LastSwiftMigration = 1020; + }; + DD7502791C68FCFC006590AF = { + CreatedOnToolsVersion = 7.2.1; + LastSwiftMigration = 1020; + }; + DD75028C1C690C7A006590AF = { + CreatedOnToolsVersion = 7.2.1; + LastSwiftMigration = 1020; + }; + }; + }; + buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Defaults" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 52D6D9721BEFF229002C0205; + productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */, + 52D6D97B1BEFF229002C0205 /* Defaults-iOS */, + 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */, + 52D6D9E11BEFFF6E002C0205 /* Defaults-watchOS */, + 52D6D9851BEFF229002C0205 /* Defaults-iOS Tests */, + DD7502791C68FCFC006590AF /* Defaults-macOS Tests */, + DD75028C1C690C7A006590AF /* Defaults-tvOS Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 52D6D97A1BEFF229002C0205 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9841BEFF229002C0205 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9E01BEFFF6E002C0205 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9EE1BEFFFBE002C0205 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6DA0D1BF000BD002C0205 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD7502781C68FCFC006590AF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD75028B1C690C7A006590AF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 52D6D9771BEFF229002C0205 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */, + E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */, + 8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */, + E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */, + E3EB3E35216507AE0033B089 /* Observation.swift in Sources */, + E3EB3E33216505920033B089 /* Utilities.swift in Sources */, + E339B3B42449ED2000E7A40A /* Reset.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9821BEFF229002C0205 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9DD1BEFFF6E002C0205 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */, + E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */, + E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */, + E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */, + E3EB3E37216507B50033B089 /* Observation.swift in Sources */, + 8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */, + E339B3B62449ED2000E7A40A /* Reset.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6D9EB1BEFFFBE002C0205 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */, + E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */, + E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */, + E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */, + E3EB3E38216507B60033B089 /* Observation.swift in Sources */, + 8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */, + E339B3B52449ED2000E7A40A /* Reset.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52D6DA0A1BF000BD002C0205 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */, + E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */, + E3EB3E39216507C30033B089 /* Utilities.swift in Sources */, + E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */, + E3EB3E36216507B50033B089 /* Observation.swift in Sources */, + 8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */, + E339B3B32449ED2000E7A40A /* Reset.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD7502761C68FCFC006590AF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8933C78F1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DD7502891C690C7A006590AF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8933C78E1EB5B82C000D00A4 /* DefaultsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52D6D97B1BEFF229002C0205 /* Defaults-iOS */; + targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */; + }; + DD7502811C68FCFC006590AF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52D6DA0E1BF000BD002C0205 /* Defaults-macOS */; + targetProxy = DD7502801C68FCFC006590AF /* PBXContainerItemProxy */; + }; + DD7502941C690C7A006590AF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52D6D9EF1BEFFFBE002C0205 /* Defaults-tvOS */; + targetProxy = DD7502931C690C7A006590AF /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 52D6D98E1BEFF229002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "-weak_framework", + Combine, + ); + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 52D6D98F1BEFF229002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ( + "-weak_framework", + Combine, + ); + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 52D6D9911BEFF229002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS"; + PRODUCT_NAME = Defaults; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 52D6D9921BEFF229002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS"; + PRODUCT_NAME = Defaults; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 52D6D9941BEFF229002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_COMPILATION_MODE = singlefile; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 52D6D9951BEFF229002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-iOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 52D6D9E81BEFFF6E002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-watchOS"; + PRODUCT_NAME = Defaults; + SDKROOT = watchos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 3.0; + }; + name = Debug; + }; + 52D6D9E91BEFFF6E002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-watchOS"; + PRODUCT_NAME = Defaults; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 3.0; + }; + name = Release; + }; + 52D6DA021BEFFFBE002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS"; + PRODUCT_NAME = Defaults; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Debug; + }; + 52D6DA031BEFFFBE002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 4.0.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS"; + PRODUCT_NAME = Defaults; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Release; + }; + 52D6DA211BF000BD002C0205 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + MARKETING_VERSION = 4.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS"; + PRODUCT_NAME = Defaults; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 52D6DA221BF000BD002C0205 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Configs/Defaults.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + MARKETING_VERSION = 4.1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS"; + PRODUCT_NAME = Defaults; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + DD7502831C68FCFC006590AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + DD7502841C68FCFC006590AF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-macOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + DD7502961C690C7A006590AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Debug; + }; + DD7502971C690C7A006590AF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Configs/DefaultsTests.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.Defaults.Defaults-tvOS-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = appletvos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 12.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "Defaults" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6D98E1BEFF229002C0205 /* Debug */, + 52D6D98F1BEFF229002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6D9911BEFF229002C0205 /* Debug */, + 52D6D9921BEFF229002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "Defaults-iOS Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6D9941BEFF229002C0205 /* Debug */, + 52D6D9951BEFF229002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "Defaults-watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6D9E81BEFFF6E002C0205 /* Debug */, + 52D6D9E91BEFFF6E002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "Defaults-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6DA021BEFFFBE002C0205 /* Debug */, + 52D6DA031BEFFFBE002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "Defaults-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52D6DA211BF000BD002C0205 /* Debug */, + 52D6DA221BF000BD002C0205 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "Defaults-macOS Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DD7502831C68FCFC006590AF /* Debug */, + DD7502841C68FCFC006590AF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "Defaults-tvOS Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DD7502961C690C7A006590AF /* Debug */, + DD7502971C690C7A006590AF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 52D6D9731BEFF229002C0205 /* Project object */; +} diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme new file mode 100644 index 00000000..dfc0e457 --- /dev/null +++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-iOS.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme new file mode 100644 index 00000000..10c1e989 --- /dev/null +++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-macOS.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme new file mode 100644 index 00000000..1a5ea11f --- /dev/null +++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-tvOS.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme similarity index 65% rename from Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme rename to Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme index 20643589..09c9bf2a 100644 --- a/Frameworks/NNKit/NNKit.xcodeproj/xcshareddata/xcschemes/NNKit.xcscheme +++ b/Dependencies/Defaults/Defaults.xcodeproj/xcshareddata/xcschemes/Defaults-watchOS.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "52D6D9E11BEFFF6E002C0205" + BuildableName = "Defaults.framework" + BlueprintName = "Defaults-watchOS" + ReferencedContainer = "container:Defaults.xcodeproj"> + codeCoverageEnabled = "YES"> - - - - + BlueprintIdentifier = "52D6D9E11BEFFF6E002C0205" + BuildableName = "Defaults.framework" + BlueprintName = "Defaults-watchOS" + ReferencedContainer = "container:Defaults.xcodeproj"> - - + + + + diff --git a/Dependencies/Defaults/Package.swift b/Dependencies/Defaults/Package.swift new file mode 100644 index 00000000..cf586e5c --- /dev/null +++ b/Dependencies/Defaults/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version:5.2 +import PackageDescription + +let package = Package( + name: "Defaults", + platforms: [ + .macOS(.v10_12), + .iOS(.v10), + .tvOS(.v10), + .watchOS(.v3) + ], + products: [ + .library( + name: "Defaults", + targets: [ + "Defaults" + ] + ) + ], + targets: [ + .target( + name: "Defaults" + ), + .testTarget( + name: "DefaultsTests", + dependencies: [ + "Defaults" + ] + ) + ] +) diff --git a/Dependencies/Defaults/Sources/Defaults/Defaults.swift b/Dependencies/Defaults/Sources/Defaults/Defaults.swift new file mode 100644 index 00000000..227c9bb4 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/Defaults.swift @@ -0,0 +1,142 @@ +// MIT License © Sindre Sorhus +import Foundation + +public protocol DefaultsBaseKey: Defaults.AnyKey { + var name: String { get } + var suite: UserDefaults { get } +} + +extension DefaultsBaseKey { + /// Reset the item back to its default value. + public func reset() { + suite.removeObject(forKey: name) + } +} + +public enum Defaults { + public typealias BaseKey = DefaultsBaseKey + public typealias AnyKey = Keys + + public class Keys: BaseKey { + public typealias Key = Defaults.Key + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public typealias NSSecureCodingKey = Defaults.NSSecureCodingKey + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public typealias NSSecureCodingOptionalKey = Defaults.NSSecureCodingOptionalKey + + public let name: String + public let suite: UserDefaults + + fileprivate init(name: String, suite: UserDefaults) { + self.name = name + self.suite = suite + } + } + + public final class Key: AnyKey { + public let defaultValue: Value + + /// Create a defaults key. + /// The `default` parameter can be left out if the `Value` type is an optional. + public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) { + self.defaultValue = defaultValue + + super.init(name: key, suite: suite) + + if (defaultValue as? _DefaultsOptionalType)?.isNil == true { + return + } + + // Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding. + if UserDefaults.isNativelySupportedType(Value.self) { + suite.register(defaults: [key: defaultValue]) + } else if let value = suite._encode(defaultValue) { + suite.register(defaults: [key: value]) + } + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public final class NSSecureCodingKey: AnyKey { + public let defaultValue: Value + + /// Create a defaults key. + /// The `default` parameter can be left out if the `Value` type is an optional. + public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) { + self.defaultValue = defaultValue + + super.init(name: key, suite: suite) + + if (defaultValue as? _DefaultsOptionalType)?.isNil == true { + return + } + + // Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding. + if UserDefaults.isNativelySupportedType(Value.self) { + suite.register(defaults: [key: defaultValue]) + } else if let value = try? NSKeyedArchiver.archivedData(withRootObject: defaultValue, requiringSecureCoding: true) { + suite.register(defaults: [key: value]) + } + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public final class NSSecureCodingOptionalKey: AnyKey { + /// Create an optional defaults key. + public init(_ key: String, suite: UserDefaults = .standard) { + super.init(name: key, suite: suite) + } + } + + /// Access a defaults value using a `Defaults.Key`. + public static subscript(key: Key) -> Value { + get { key.suite[key] } + set { + key.suite[key] = newValue + } + } + + /// Access a defaults value using a `Defaults.NSSecureCodingKey`. + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public static subscript(key: NSSecureCodingKey) -> Value { + get { key.suite[key] } + set { + key.suite[key] = newValue + } + } + + /// Access a defaults value using a `Defaults.NSSecureCodingOptionalKey`. + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public static subscript(key: NSSecureCodingOptionalKey) -> Value? { + get { key.suite[key] } + set { + key.suite[key] = newValue + } + } +} + +extension Defaults { + /** + Remove all entries from the given `UserDefaults` suite. + + - Note: This only removes user-defined entries. System-defined entries will remain. + */ + public static func removeAll(suite: UserDefaults = .standard) { + suite.removeAll() + } +} + +extension Defaults.Key { + public convenience init(_ key: String, suite: UserDefaults = .standard) where Value == T? { + self.init(key, default: nil, suite: suite) + } +} + +@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) +extension Defaults.NSSecureCodingKey where Value: _DefaultsOptionalType { + public convenience init(_ key: String, suite: UserDefaults = .standard) { + self.init(key, default: nil, suite: suite) + } +} diff --git a/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift b/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift new file mode 100644 index 00000000..201c8705 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/Observation+Combine.swift @@ -0,0 +1,152 @@ +#if canImport(Combine) +import Foundation +import Combine + +extension Defaults { + /** + Custom `Subscription` for `UserDefaults` key observation. + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + final class DefaultsSubscription: Subscription where SubscriberType.Input == BaseChange { + private var subscriber: SubscriberType? + private var observation: UserDefaultsKeyObservation? + private let options: ObservationOptions + + init(subscriber: SubscriberType, suite: UserDefaults, key: String, options: ObservationOptions) { + self.subscriber = subscriber + self.options = options + self.observation = UserDefaultsKeyObservation( + object: suite, + key: key, + callback: observationCallback(_:) + ) + } + + func request(_ demand: Subscribers.Demand) { + // Nothing as we send events only when they occur. + } + + func cancel() { + observation?.invalidate() + observation = nil + subscriber = nil + } + + func start() { + observation?.start(options: options) + } + + private func observationCallback(_ change: BaseChange) { + _ = subscriber?.receive(change) + } + } + + /** + Custom Publisher, which is using DefaultsSubscription. + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + struct DefaultsPublisher: Publisher { + typealias Output = BaseChange + typealias Failure = Never + + private let suite: UserDefaults + private let key: String + private let options: ObservationOptions + + init(suite: UserDefaults, key: String, options: ObservationOptions) { + self.suite = suite + self.key = key + self.options = options + } + + func receive(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { + let subscription = DefaultsSubscription( + subscriber: subscriber, + suite: suite, + key: key, + options: options + ) + + subscriber.receive(subscription: subscription) + subscription.start() + } + } + + /** + Returns a type-erased `Publisher` that publishes changes related to the given key. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + let publisher = Defaults.publisher(.isUnicornMode).map(\.newValue) + + let cancellable = publisher.sink { value in + print(value) + //=> false + } + ``` + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + public static func publisher( + _ key: Key, + options: ObservationOptions = [.initial] + ) -> AnyPublisher, Never> { + let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) + .map { KeyChange(change: $0, defaultValue: key.defaultValue) } + + return AnyPublisher(publisher) + } + + /** + Returns a type-erased `Publisher` that publishes changes related to the given key. + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + public static func publisher( + _ key: NSSecureCodingKey, + options: ObservationOptions = [.initial] + ) -> AnyPublisher, Never> { + let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) + .map { NSSecureCodingKeyChange(change: $0, defaultValue: key.defaultValue) } + + return AnyPublisher(publisher) + } + + /** + Returns a type-erased `Publisher` that publishes changes related to the given optional key. + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + public static func publisher( + _ key: NSSecureCodingOptionalKey, + options: ObservationOptions = [.initial] + ) -> AnyPublisher, Never> { + let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) + .map { NSSecureCodingOptionalKeyChange(change: $0) } + + return AnyPublisher(publisher) + } + + /** + Publisher for multiple `Key` observation, but without specific information about changes. + */ + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + public static func publisher( + keys: AnyKey..., + options: ObservationOptions = [.initial] + ) -> AnyPublisher { + let initial = Empty(completeImmediately: false).eraseToAnyPublisher() + + let combinedPublisher = + keys.map { key in + DefaultsPublisher(suite: key.suite, key: key.name, options: options) + .map { _ in () } + .eraseToAnyPublisher() + }.reduce(initial) { combined, keyPublisher in + combined.merge(with: keyPublisher).eraseToAnyPublisher() + } + + return combinedPublisher + } +} +#endif diff --git a/Dependencies/Defaults/Sources/Defaults/Observation.swift b/Dependencies/Defaults/Sources/Defaults/Observation.swift new file mode 100644 index 00000000..f0ef82a9 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/Observation.swift @@ -0,0 +1,446 @@ +import Foundation + +public protocol DefaultsObservation: AnyObject { + func invalidate() + + /** + Keep this observation alive for as long as, and no longer than, another object exists. + + ``` + Defaults.observe(.xyz) { [unowned self] change in + self.xyz = change.newValue + }.tieToLifetime(of: self) + ``` + */ + @discardableResult + func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self + + /** + Break the lifetime tie created by `tieToLifetime(of:)`, if one exists. + + - Postcondition: The effects of any call to `tieToLifetime(of:)` are reversed. + - Note: If the tied-to object has already died, then self is considered to be invalidated, and this method has no logical effect. + */ + func removeLifetimeTie() +} + +extension Defaults { + public typealias Observation = DefaultsObservation + + public enum ObservationOption { + /// Whether a notification should be sent to the observer immediately, before the observer registration method even returns. + case initial + + /// Whether separate notifications should be sent to the observer before and after each change, instead of a single notification after the change. + case prior + } + + public typealias ObservationOptions = Set + + private static func deserialize(_ value: Any?, to type: Value.Type) -> Value? { + guard + let value = value, + !(value is NSNull) + else { + return nil + } + + // This handles the case where the value was a plist value using `isNativelySupportedType` + if let value = value as? Value { + return value + } + + // Using the array trick as done below in `UserDefaults#_set()` + return [Value].init(jsonString: "\([value])")?.first + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + private static func deserialize(_ value: Any?, to type: Value.Type) -> Value? { + guard + let value = value, + !(value is NSNull) + else { + return nil + } + + // This handles the case where the value was a plist value using `isNativelySupportedType` + if let value = value as? Value { + return value + } + + guard let dataValue = value as? Data else { + return nil + } + + return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? Value + } + + struct BaseChange { + let kind: NSKeyValueChange + let indexes: IndexSet? + let isPrior: Bool + let newValue: Any? + let oldValue: Any? + + init(change: [NSKeyValueChangeKey: Any]) { + self.kind = NSKeyValueChange(rawValue: change[.kindKey] as! UInt)! + self.indexes = change[.indexesKey] as? IndexSet + self.isPrior = change[.notificationIsPriorKey] as? Bool ?? false + self.oldValue = change[.oldKey] + self.newValue = change[.newKey] + } + } + + public struct KeyChange { + public let kind: NSKeyValueChange + public let indexes: IndexSet? + public let isPrior: Bool + public let newValue: Value + public let oldValue: Value + + init(change: BaseChange, defaultValue: Value) { + self.kind = change.kind + self.indexes = change.indexes + self.isPrior = change.isPrior + self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue + self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public struct NSSecureCodingKeyChange { + public let kind: NSKeyValueChange + public let indexes: IndexSet? + public let isPrior: Bool + public let newValue: Value + public let oldValue: Value + + init(change: BaseChange, defaultValue: Value) { + self.kind = change.kind + self.indexes = change.indexes + self.isPrior = change.isPrior + self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue + self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public struct NSSecureCodingOptionalKeyChange { + public let kind: NSKeyValueChange + public let indexes: IndexSet? + public let isPrior: Bool + public let newValue: Value? + public let oldValue: Value? + + init(change: BaseChange) { + self.kind = change.kind + self.indexes = change.indexes + self.isPrior = change.isPrior + self.oldValue = deserialize(change.oldValue, to: Value.self) + self.newValue = deserialize(change.newValue, to: Value.self) + } + } + + private static var preventPropagationThreadDictionaryKey: String { + "\(type(of: Observation.self))_threadUpdatingValuesFlag" + } + + /** + Execute the closure without triggering change events. + + Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key. + + - Note: This only works with `Defaults.observe()` and `Defaults.publisher()`. User-made KVO will not be affected. + + ``` + let observer = Defaults.observe(keys: .key1, .key2) { + // … + + Defaults.withoutPropagation { + // Update `.key1` without propagating the change to listeners. + Defaults[.key1] = 11 + } + + // This will be propagated. + Defaults[.someKey] = true + } + ``` + */ + public static func withoutPropagation(_ closure: () -> Void) { + // How does it work? + // KVO observation callbacks are executed right after a change is made, and run on the same thread as the caller. So it works by storing a flag in the current thread's dictionary, which is then evaluated in the callback. + + let key = preventPropagationThreadDictionaryKey + Thread.current.threadDictionary[key] = true + closure() + Thread.current.threadDictionary[key] = false + } + + final class UserDefaultsKeyObservation: NSObject, Observation { + typealias Callback = (BaseChange) -> Void + + private weak var object: UserDefaults? + private let key: String + private let callback: Callback + + init(object: UserDefaults, key: String, callback: @escaping Callback) { + self.object = object + self.key = key + self.callback = callback + } + + deinit { + invalidate() + } + + func start(options: ObservationOptions) { + object?.addObserver(self, forKeyPath: key, options: options.toNSKeyValueObservingOptions, context: nil) + } + + public func invalidate() { + object?.removeObserver(self, forKeyPath: key, context: nil) + object = nil + lifetimeAssociation?.cancel() + } + + private var lifetimeAssociation: LifetimeAssociation? + + public func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self { + lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in + self?.invalidate() + }) + + return self + } + + public func removeLifetimeTie() { + lifetimeAssociation?.cancel() + } + + // swiftlint:disable:next block_based_kvo + override func observeValue( + forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey: Any]?, // swiftlint:disable:this discouraged_optional_collection + context: UnsafeMutableRawPointer? + ) { + guard let selfObject = self.object else { + invalidate() + return + } + + guard + selfObject == object as? NSObject, + let change = change + else { + return + } + + let key = preventPropagationThreadDictionaryKey + let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false + guard !updatingValuesFlag else { + return + } + + callback(BaseChange(change: change)) + } + } + + private final class CompositeUserDefaultsKeyObservation: NSObject, Observation { + private static var observationContext = 0 + + private final class SuiteKeyPair { + weak var suite: UserDefaults? + let key: String + + init(suite: UserDefaults, key: String) { + self.suite = suite + self.key = key + } + } + + private var observables: [SuiteKeyPair] + private var lifetimeAssociation: LifetimeAssociation? + private let callback: UserDefaultsKeyObservation.Callback + + init(observables: [(suite: UserDefaults, key: String)], callback: @escaping UserDefaultsKeyObservation.Callback) { + self.observables = observables.map { SuiteKeyPair(suite: $0.suite, key: $0.key) } + self.callback = callback + super.init() + } + + deinit { + invalidate() + } + + public func start(options: ObservationOptions) { + for observable in observables { + observable.suite?.addObserver( + self, + forKeyPath: observable.key, + options: options.toNSKeyValueObservingOptions, + context: &Self.observationContext + ) + } + } + + public func invalidate() { + for observable in observables { + observable.suite?.removeObserver(self, forKeyPath: observable.key, context: &Self.observationContext) + observable.suite = nil + } + + lifetimeAssociation?.cancel() + } + + public func tieToLifetime(of weaklyHeldObject: AnyObject) -> Self { + lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in + self?.invalidate() + }) + + return self + } + + public func removeLifetimeTie() { + lifetimeAssociation?.cancel() + } + + // swiftlint:disable:next block_based_kvo + override func observeValue( + forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey: Any]?, // swiftlint:disable:this discouraged_optional_collection + context: UnsafeMutableRawPointer? + ) { + guard + context == &Self.observationContext + else { + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + return + } + + guard + object is UserDefaults, + let change = change + else { + return + } + + let key = preventPropagationThreadDictionaryKey + let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false + if updatingValuesFlag { + return + } + + callback(BaseChange(change: change)) + } + } + + /** + Observe a defaults key. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + let observer = Defaults.observe(.isUnicornMode) { change in + print(change.newValue) + //=> false + } + ``` + */ + public static func observe( + _ key: Key, + options: ObservationOptions = [.initial], + handler: @escaping (KeyChange) -> Void + ) -> Observation { + let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in + handler( + KeyChange(change: change, defaultValue: key.defaultValue) + ) + } + observation.start(options: options) + return observation + } + + /** + Observe a defaults key. + */ + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public static func observe( + _ key: NSSecureCodingKey, + options: ObservationOptions = [.initial], + handler: @escaping (NSSecureCodingKeyChange) -> Void + ) -> Observation { + let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in + handler( + NSSecureCodingKeyChange(change: change, defaultValue: key.defaultValue) + ) + } + observation.start(options: options) + return observation + } + + /** + Observe an optional defaults key. + */ + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public static func observe( + _ key: NSSecureCodingOptionalKey, + options: ObservationOptions = [.initial], + handler: @escaping (NSSecureCodingOptionalKeyChange) -> Void + ) -> Observation { + let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in + handler( + NSSecureCodingOptionalKeyChange(change: change) + ) + } + observation.start(options: options) + return observation + } + + /** + Observe multiple keys of any type, but without any information about the changes. + + ``` + extension Defaults.Keys { + static let setting1 = Key("setting1", default: false) + static let setting2 = Key("setting2", default: true) + } + + let observer = Defaults.observe(keys: .setting1, .setting2) { + // … + } + ``` + */ + public static func observe( + keys: AnyKey..., + options: ObservationOptions = [.initial], + handler: @escaping () -> Void + ) -> Observation { + let pairs = keys.map { + (suite: $0.suite, key: $0.name) + } + let compositeObservation = CompositeUserDefaultsKeyObservation(observables: pairs) { _ in + handler() + } + compositeObservation.start(options: options) + + return compositeObservation + } +} + +extension Defaults.ObservationOptions { + var toNSKeyValueObservingOptions: NSKeyValueObservingOptions { + var options: NSKeyValueObservingOptions = [.old, .new] + + if contains(.initial) { + options.insert(.initial) + } else if contains(.prior) { + options.insert(.prior) + } + + return options + } +} diff --git a/Dependencies/Defaults/Sources/Defaults/Reset.swift b/Dependencies/Defaults/Sources/Defaults/Reset.swift new file mode 100644 index 00000000..d86ddcd0 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/Reset.swift @@ -0,0 +1,105 @@ +import Foundation + +extension Defaults { + /** + Reset the given string keys back to their default values. + + Prefer using the strongly-typed keys instead whenever possible. This method can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic. + + - Parameter keys: String keys to reset. + - Parameter suite: `UserDefaults` suite. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + Defaults[.isUnicornMode] = true + //=> true + + Defaults.reset(Defaults.Keys.isUnicornMode.name) + // Or `Defaults.reset("isUnicornMode")` + + Defaults[.isUnicornMode] + //=> false + ``` + */ + public static func reset(_ keys: String..., suite: UserDefaults = .standard) { + reset(keys, suite: suite) + } + + /** + Reset the given string keys back to their default values. + + Prefer using the strongly-typed keys instead whenever possible. This method can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic. + + - Parameter keys: String keys to reset. + - Parameter suite: `UserDefaults` suite. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + Defaults[.isUnicornMode] = true + //=> true + + Defaults.reset([Defaults.Keys.isUnicornMode.name]) + // Or `Defaults.reset(["isUnicornMode"])` + + Defaults[.isUnicornMode] + //=> false + ``` + */ + public static func reset(_ keys: [String], suite: UserDefaults = .standard) { + for key in keys { + suite.removeObject(forKey: key) + } + } +} + +extension Defaults { + /** + Reset the given keys back to their default values. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + Defaults[.isUnicornMode] = true + //=> true + + Defaults.reset(.isUnicornMode) + + Defaults[.isUnicornMode] + //=> false + ``` + */ + public static func reset(_ keys: AnyKey...) { + reset(keys) + } + + /** + Reset the given keys back to their default values. + + ``` + extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) + } + + Defaults[.isUnicornMode] = true + //=> true + + Defaults.reset(.isUnicornMode) + + Defaults[.isUnicornMode] + //=> false + ``` + */ + public static func reset(_ keys: [AnyKey]) { + for key in keys { + key.reset() + } + } +} diff --git a/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift b/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift new file mode 100644 index 00000000..743d2dc8 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/SwiftUI.swift @@ -0,0 +1,113 @@ +#if canImport(Combine) +import SwiftUI +import Combine + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +extension Defaults { + final class Observable: ObservableObject { + let objectWillChange = ObservableObjectPublisher() + private var observation: DefaultsObservation? + private let key: Defaults.Key + + var value: Value { + get { Defaults[key] } + set { + objectWillChange.send() + Defaults[key] = newValue + } + } + + init(_ key: Key) { + self.key = key + + self.observation = Defaults.observe(key, options: [.prior]) { [weak self] change in + guard change.isPrior else { + return + } + + DispatchQueue.mainSafeAsync { + self?.objectWillChange.send() + } + } + } + + /// Reset the key back to its default value. + func reset() { + key.reset() + } + } +} + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +@propertyWrapper +public struct Default: DynamicProperty { + public typealias Publisher = AnyPublisher, Never> + + private let key: Defaults.Key + @ObservedObject private var observable: Defaults.Observable + + /** + Get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`. + + - Important: You cannot use this in an `ObservableObject`. It's meant to be used in a `View`. + + ``` + extension Defaults.Keys { + static let hasUnicorn = Key("hasUnicorn", default: false) + } + + struct ContentView: View { + @Default(.hasUnicorn) var hasUnicorn + + var body: some View { + Text("Has Unicorn: \(hasUnicorn)") + Toggle("Toggle Unicorn", isOn: $hasUnicorn) + } + } + ``` + */ + public init(_ key: Defaults.Key) { + self.key = key + self.observable = Defaults.Observable(key) + } + + public var wrappedValue: Value { + get { observable.value } + nonmutating set { + observable.value = newValue + } + } + + public var projectedValue: Binding { $observable.value } + + /// Combine publisher that publishes values when the `Defaults` item changes. + public var publisher: Publisher { Defaults.publisher(key) } + + public mutating func update() { + _observable.update() + } + + /** + Reset the key back to its default value. + + ``` + extension Defaults.Keys { + static let opacity = Key("opacity", default: 1) + } + + struct ContentView: View { + @Default(.opacity) var opacity + + var body: some View { + Button("Reset") { + _opacity.reset() + } + } + } + ``` + */ + public func reset() { + key.reset() + } +} +#endif diff --git a/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift b/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift new file mode 100644 index 00000000..147c9282 --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/UserDefaults.swift @@ -0,0 +1,147 @@ +import Foundation + +extension UserDefaults { + private func _get(_ key: String) -> Value? { + if UserDefaults.isNativelySupportedType(Value.self) { + return object(forKey: key) as? Value + } + + guard + let text = string(forKey: key), + let data = "[\(text)]".data(using: .utf8) + else { + return nil + } + + do { + return (try JSONDecoder().decode([Value].self, from: data)).first + } catch { + print(error) + } + + return nil + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + private func _get(_ key: String) -> Value? { + if UserDefaults.isNativelySupportedType(Value.self) { + return object(forKey: key) as? Value + } + + guard + let data = data(forKey: key) + else { + return nil + } + + do { + return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Value + } catch { + print(error) + } + + return nil + } + + func _encode(_ value: Value) -> String? { + do { + // Some codable values like URL and enum are encoded as a top-level + // string which JSON can't handle, so we need to wrap it in an array + // We need this: https://forums.swift.org/t/allowing-top-level-fragments-in-jsondecoder/11750 + let data = try JSONEncoder().encode([value]) + return String(String(data: data, encoding: .utf8)!.dropFirst().dropLast()) + } catch { + print(error) + return nil + } + } + + private func _set(_ key: String, to value: Value) { + if (value as? _DefaultsOptionalType)?.isNil == true { + removeObject(forKey: key) + return + } + + if UserDefaults.isNativelySupportedType(Value.self) { + set(value, forKey: key) + return + } + + set(_encode(value), forKey: key) + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + private func _set(_ key: String, to value: Value) { + // TODO: Handle nil here too. + if UserDefaults.isNativelySupportedType(Value.self) { + set(value, forKey: key) + return + } + + set(try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true), forKey: key) + } + + public subscript(key: Defaults.Key) -> Value { + get { _get(key.name) ?? key.defaultValue } + set { + _set(key.name, to: newValue) + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public subscript(key: Defaults.NSSecureCodingKey) -> Value { + get { _get(key.name) ?? key.defaultValue } + set { + _set(key.name, to: newValue) + } + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + public subscript(key: Defaults.NSSecureCodingOptionalKey) -> Value? { + get { _get(key.name) } + set { + guard let value = newValue else { + set(nil, forKey: key.name) + return + } + + _set(key.name, to: value) + } + } + + static func isNativelySupportedType(_ type: T.Type) -> Bool { + switch type { + case + is Bool.Type, + is Bool?.Type, // swiftlint:disable:this discouraged_optional_boolean + is String.Type, + is String?.Type, + is Int.Type, + is Int?.Type, + is Double.Type, + is Double?.Type, + is Float.Type, + is Float?.Type, + is Date.Type, + is Date?.Type, + is Data.Type, + is Data?.Type: + return true + default: + return false + } + } +} + +extension UserDefaults { + /** + Remove all entries. + + - Note: This only removes user-defined entries. System-defined entries will remain. + */ + public func removeAll() { + for key in dictionaryRepresentation().keys { + removeObject(forKey: key) + } + } +} diff --git a/Dependencies/Defaults/Sources/Defaults/Utilities.swift b/Dependencies/Defaults/Sources/Defaults/Utilities.swift new file mode 100644 index 00000000..0746b7ad --- /dev/null +++ b/Dependencies/Defaults/Sources/Defaults/Utilities.swift @@ -0,0 +1,148 @@ +import Foundation + +extension Decodable { + init?(jsonData: Data) { + guard let value = try? JSONDecoder().decode(Self.self, from: jsonData) else { + return nil + } + + self = value + } + + init?(jsonString: String) { + guard let data = jsonString.data(using: .utf8) else { + return nil + } + + self.init(jsonData: data) + } +} + + +final class ObjectAssociation { + subscript(index: AnyObject) -> T? { + get { + objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? + } + set { + objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} + + +/** +Causes a given target object to live at least as long as a given owner object. +*/ +final class LifetimeAssociation { + private class ObjectLifetimeTracker { + var object: AnyObject? + var deinitHandler: () -> Void + + init(for weaklyHeldObject: AnyObject, deinitHandler: @escaping () -> Void) { + self.object = weaklyHeldObject + self.deinitHandler = deinitHandler + } + + deinit { + deinitHandler() + } + } + + private static let associatedObjects = ObjectAssociation<[ObjectLifetimeTracker]>() + private weak var wrappedObject: ObjectLifetimeTracker? + private weak var owner: AnyObject? + + /** + Causes the given target object to live at least as long as either the given owner object or the resulting `LifetimeAssociation`, whichever is deallocated first. + + When either the owner or the new `LifetimeAssociation` is destroyed, the given deinit handler, if any, is called. + + ``` + class Ghost { + var association: LifetimeAssociation? + + func haunt(_ host: Furniture) { + association = LifetimeAssociation(of: self, with: host) { [weak self] in + // Host has been deinitialized + self?.haunt(seekHost()) + } + } + } + + let piano = Piano() + Ghost().haunt(piano) + // The Ghost will remain alive as long as `piano` remains alive. + ``` + + - Parameter target: The object whose lifetime will be extended. + - Parameter owner: The object whose lifetime extends the target object's lifetime. + - Parameter deinitHandler: An optional closure to call when either `owner` or the resulting `LifetimeAssociation` is deallocated. + */ + init(of target: AnyObject, with owner: AnyObject, deinitHandler: @escaping () -> Void = {}) { + let wrappedObject = ObjectLifetimeTracker(for: target, deinitHandler: deinitHandler) + + let associatedObjects = LifetimeAssociation.associatedObjects[owner] ?? [] + LifetimeAssociation.associatedObjects[owner] = associatedObjects + [wrappedObject] + + self.wrappedObject = wrappedObject + self.owner = owner + } + + /** + Invalidates the association, unlinking the target object's lifetime from that of the owner object. The provided deinit handler is not called. + */ + func cancel() { + wrappedObject?.deinitHandler = {} + invalidate() + } + + deinit { + invalidate() + } + + private func invalidate() { + guard + let owner = owner, + let wrappedObject = wrappedObject, + var associatedObjects = LifetimeAssociation.associatedObjects[owner], + let wrappedObjectAssociationIndex = associatedObjects.firstIndex(where: { $0 === wrappedObject }) + else { + return + } + + associatedObjects.remove(at: wrappedObjectAssociationIndex) + LifetimeAssociation.associatedObjects[owner] = associatedObjects + self.owner = nil + } +} + + +/// A protocol for making generic type constraints of optionals. +/// - Note: It's intentionally not including `associatedtype Wrapped` as that limits a lot of the use-cases. +public protocol _DefaultsOptionalType: ExpressibleByNilLiteral { + /// This is useful as you can't compare `_OptionalType` to `nil`. + var isNil: Bool { get } +} + +extension Optional: _DefaultsOptionalType { + public var isNil: Bool { self == nil } +} + +func isOptionalType(_ type: T.Type) -> Bool { + type is _DefaultsOptionalType.Type +} + + +extension DispatchQueue { + /** + Performs the `execute` closure immediately if we're on the main thread or asynchronously puts it on the main thread otherwise. + */ + static func mainSafeAsync(execute work: @escaping () -> Void) { + if Thread.isMainThread { + work() + } else { + main.async(execute: work) + } + } +} diff --git a/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift b/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift new file mode 100644 index 00000000..9d4d4423 --- /dev/null +++ b/Dependencies/Defaults/Tests/DefaultsTests/DefaultsTests.swift @@ -0,0 +1,813 @@ +import Foundation +import CoreData +import Combine +import XCTest +import Defaults + +let fixtureURL = URL(string: "https://sindresorhus.com")! +let fixtureURL2 = URL(string: "https://example.com")! + +enum FixtureEnum: String, Codable { + case tenMinutes = "10 Minutes" + case halfHour = "30 Minutes" + case oneHour = "1 Hour" +} + +let fixtureDate = Date() + +@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) +final class ExamplePersistentHistory: NSPersistentHistoryToken { + let value: String + + init(value: String) { + self.value = value + super.init() + } + + required init?(coder: NSCoder) { + self.value = coder.decodeObject(forKey: "value") as! String + super.init() + } + + override func encode(with coder: NSCoder) { + coder.encode(value, forKey: "value") + } + + override class var supportsSecureCoding: Bool { true } +} + +extension Defaults.Keys { + static let key = Key("key", default: false) + static let url = Key("url", default: fixtureURL) + static let `enum` = Key("enum", default: .oneHour) + static let data = Key("data", default: Data([])) + static let date = Key("date", default: fixtureDate) + + // NSSecureCoding + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + static let persistentHistoryValue = ExamplePersistentHistory(value: "ExampleToken") + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + static let persistentHistory = NSSecureCodingKey("persistentHistory", default: persistentHistoryValue) +} + +final class DefaultsTests: XCTestCase { + override func setUp() { + super.setUp() + Defaults.removeAll() + } + + override func tearDown() { + super.setUp() + Defaults.removeAll() + } + + func testKey() { + let key = Defaults.Key("independentKey", default: false) + XCTAssertFalse(Defaults[key]) + Defaults[key] = true + XCTAssertTrue(Defaults[key]) + } + + func testOptionalKey() { + let key = Defaults.Key("independentOptionalKey") + XCTAssertNil(Defaults[key]) + Defaults[key] = true + XCTAssertTrue(Defaults[key]!) + Defaults[key] = nil + XCTAssertNil(Defaults[key]) + Defaults[key] = false + XCTAssertFalse(Defaults[key]!) + } + + func testKeyRegistersDefault() { + let keyName = "registersDefault" + XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), false) + _ = Defaults.Key(keyName, default: true) + XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), true) + + // Test that it works with multiple keys with `Defaults`. + let keyName2 = "registersDefault2" + _ = Defaults.Key(keyName2, default: keyName2) + XCTAssertEqual(UserDefaults.standard.string(forKey: keyName2), keyName2) + } + + func testKeyWithUserDefaultSubscript() { + let key = Defaults.Key("keyWithUserDeaultSubscript", default: false) + XCTAssertFalse(UserDefaults.standard[key]) + UserDefaults.standard[key] = true + XCTAssertTrue(UserDefaults.standard[key]) + } + + func testKeys() { + XCTAssertFalse(Defaults[.key]) + Defaults[.key] = true + XCTAssertTrue(Defaults[.key]) + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + func testNSSecureCodingKeys() { + XCTAssertEqual(Defaults.Keys.persistentHistoryValue.value, Defaults[.persistentHistory].value) + let newPersistentHistory = ExamplePersistentHistory(value: "NewValue") + Defaults[.persistentHistory] = newPersistentHistory + XCTAssertEqual(newPersistentHistory.value, Defaults[.persistentHistory].value) + } + + func testUrlType() { + XCTAssertEqual(Defaults[.url], fixtureURL) + + let newUrl = URL(string: "https://twitter.com")! + Defaults[.url] = newUrl + XCTAssertEqual(Defaults[.url], newUrl) + } + + func testEnumType() { + XCTAssertEqual(Defaults[.enum], FixtureEnum.oneHour) + } + + func testDataType() { + XCTAssertEqual(Defaults[.data], Data([])) + + let newData = Data([0xFF]) + Defaults[.data] = newData + XCTAssertEqual(Defaults[.data], newData) + } + + func testDateType() { + XCTAssertEqual(Defaults[.date], fixtureDate) + + let newDate = Date() + Defaults[.date] = newDate + XCTAssertEqual(Defaults[.date], newDate) + } + + func testRemoveAll() { + let key = Defaults.Key("removeAll", default: false) + let key2 = Defaults.Key("removeAll2", default: false) + Defaults[key] = true + Defaults[key2] = true + XCTAssertTrue(Defaults[key]) + XCTAssertTrue(Defaults[key2]) + Defaults.removeAll() + XCTAssertFalse(Defaults[key]) + XCTAssertFalse(Defaults[key2]) + } + + func testCustomSuite() { + let customSuite = UserDefaults(suiteName: "com.sindresorhus.customSuite")! + let key = Defaults.Key("customSuite", default: false, suite: customSuite) + XCTAssertFalse(customSuite[key]) + XCTAssertFalse(Defaults[key]) + Defaults[key] = true + XCTAssertTrue(customSuite[key]) + XCTAssertTrue(Defaults[key]) + Defaults.removeAll(suite: customSuite) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveKeyCombine() { + let key = Defaults.Key("observeKey", default: false) + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults + .publisher(key, options: []) + .map { ($0.oldValue, $0.newValue) } + .collect(2) + + let cancellable = publisher.sink { tuples in + for (i, expected) in [(false, true), (true, false)].enumerated() { + XCTAssertEqual(expected.0, tuples[i].0) + XCTAssertEqual(expected.1, tuples[i].1) + } + + expect.fulfill() + } + + Defaults[key] = true + Defaults.reset(key) + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveNSSecureCodingKeyCombine() { + let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue")) + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults + .publisher(key, options: []) + .map { ($0.oldValue.value, $0.newValue.value) } + .collect(3) + + let expectedValues = [ + ("TestValue", "NewTestValue"), + ("NewTestValue", "NewTestValue2"), + ("NewTestValue2", "TestValue") + ] + + let cancellable = publisher.sink { actualValues in + for (expected, actual) in zip(expectedValues, actualValues) { + XCTAssertEqual(expected.0, actual.0) + XCTAssertEqual(expected.1, actual.1) + } + + expect.fulfill() + } + + Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") + Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2") + Defaults.reset(key) + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveOptionalKeyCombine() { + let key = Defaults.Key("observeOptionalKey") + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults + .publisher(key, options: []) + .map { ($0.oldValue, $0.newValue) } + .collect(3) + + let expectedValues: [(Bool?, Bool?)] = [(nil, true), (true, false), (false, nil)] + + let cancellable = publisher.sink { actualValues in + for (expected, actual) in zip(expectedValues, actualValues) { + XCTAssertEqual(expected.0, actual.0) + XCTAssertEqual(expected.1, actual.1) + } + + expect.fulfill() + } + + Defaults[key] = true + Defaults[key] = false + Defaults.reset(key) + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveNSSecureCodingOptionalKeyCombine() { + let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey") + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults + .publisher(key, options: []) + .map { ($0.oldValue?.value, $0.newValue?.value) } + .collect(3) + + let expectedValues: [(String?, String?)] = [ + (nil, "NewTestValue"), + ("NewTestValue", "NewTestValue2"), + ("NewTestValue2", nil) + ] + + let cancellable = publisher.sink { actualValues in + for (expected, actual) in zip(expectedValues, actualValues) { + XCTAssertEqual(expected.0, actual.0) + XCTAssertEqual(expected.1, actual.1) + } + + expect.fulfill() + } + + XCTAssertNil(Defaults[key]) + Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") + Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2") + Defaults.reset(key) + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveMultipleKeysCombine() { + let key1 = Defaults.Key("observeKey1", default: "x") + let key2 = Defaults.Key("observeKey2", default: true) + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) + + let cancellable = publisher.sink { _ in + expect.fulfill() + } + + Defaults[key1] = "y" + Defaults[key2] = false + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveMultipleNSSecureKeysCombine() { + let key1 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue")) + let key2 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue")) + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) + + let cancellable = publisher.sink { _ in + expect.fulfill() + } + + Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1") + Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2") + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveMultipleOptionalKeysCombine() { + let key1 = Defaults.Key("observeOptionalKey1") + let key2 = Defaults.Key("observeOptionalKey2") + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) + + let cancellable = publisher.sink { _ in + expect.fulfill() + } + + Defaults[key1] = "x" + Defaults[key2] = false + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObserveMultipleNSSecureOptionalKeysCombine() { + let key1 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey1") + let key2 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey2") + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) + + let cancellable = publisher.sink { _ in + expect.fulfill() + } + + Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1") + Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2") + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testReceiveValueBeforeSubscriptionCombine() { + let key = Defaults.Key("receiveValueBeforeSubscription", default: "hello") + let expect = expectation(description: "Observation closure being called") + + let publisher = Defaults + .publisher(key) + .map(\.newValue) + .eraseToAnyPublisher() + .collect(2) + + let cancellable = publisher.sink { values in + XCTAssertEqual(["hello", "world"], values) + expect.fulfill() + } + + Defaults[key] = "world" + cancellable.cancel() + waitForExpectations(timeout: 10) + } + + func testObserveKey() { + let key = Defaults.Key("observeKey", default: false) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertFalse(change.oldValue) + XCTAssertTrue(change.newValue) + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = true + + waitForExpectations(timeout: 10) + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + func testObserveNSSecureCodingKey() { + let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue")) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertEqual(change.oldValue.value, "TestValue") + XCTAssertEqual(change.newValue.value, "NewTestValue") + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") + + waitForExpectations(timeout: 10) + } + + func testObserveOptionalKey() { + let key = Defaults.Key("observeOptionalKey") + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertNil(change.oldValue) + XCTAssertTrue(change.newValue!) + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = true + + waitForExpectations(timeout: 10) + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + func testObserveNSSecureCodingOptionalKey() { + let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey") + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertNil(change.oldValue) + XCTAssertEqual(change.newValue?.value, "NewOptionalValue") + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = ExamplePersistentHistory(value: "NewOptionalValue") + + waitForExpectations(timeout: 10) + } + + func testObserveMultipleKeys() { + let key1 = Defaults.Key("observeKey1", default: "x") + let key2 = Defaults.Key("observeKey2", default: true) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + var counter = 0 + observation = Defaults.observe(keys: key1, key2, options: []) { + counter += 1 + if counter == 2 { + expect.fulfill() + } else if counter > 2 { + XCTFail() + } + } + + Defaults[key1] = "y" + Defaults[key2] = false + observation.invalidate() + + waitForExpectations(timeout: 10) + } + + @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) + func testObserveMultipleNSSecureKeys() { + let key1 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue")) + let key2 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue")) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + var counter = 0 + observation = Defaults.observe(keys: key1, key2, options: []) { + counter += 1 + if counter == 2 { + expect.fulfill() + } else if counter > 2 { + XCTFail() + } + } + + Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1") + Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2") + observation.invalidate() + + waitForExpectations(timeout: 10) + } + + func testObserveKeyURL() { + let fixtureURL = URL(string: "https://sindresorhus.com")! + let fixtureURL2 = URL(string: "https://example.com")! + let key = Defaults.Key("observeKeyURL", default: fixtureURL) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertEqual(change.oldValue, fixtureURL) + XCTAssertEqual(change.newValue, fixtureURL2) + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = fixtureURL2 + + waitForExpectations(timeout: 10) + } + + func testObserveKeyEnum() { + let key = Defaults.Key("observeKeyEnum", default: .oneHour) + let expect = expectation(description: "Observation closure being called") + + var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { change in + XCTAssertEqual(change.oldValue, .oneHour) + XCTAssertEqual(change.newValue, .tenMinutes) + observation.invalidate() + expect.fulfill() + } + + Defaults[key] = .tenMinutes + + waitForExpectations(timeout: 10) + } + + func testObservePreventPropagation() { + let key1 = Defaults.Key("preventPropagation0", default: nil) + let expect = expectation(description: "No infinite recursion") + + var observation: Defaults.Observation! + var wasInside = false + observation = Defaults.observe(key1, options: []) { _ in + XCTAssertFalse(wasInside) + wasInside = true + Defaults.withoutPropagation { + Defaults[key1] = true + } + expect.fulfill() + } + + Defaults[key1] = false + observation.invalidate() + + waitForExpectations(timeout: 10) + } + + func testObservePreventPropagationMultipleKeys() { + let key1 = Defaults.Key("preventPropagation1", default: nil) + let key2 = Defaults.Key("preventPropagation2", default: nil) + let expect = expectation(description: "No infinite recursion") + + var observation: Defaults.Observation! + var wasInside = false + observation = Defaults.observe(keys: key1, key2, options: []) { + XCTAssertFalse(wasInside) + wasInside = true + Defaults.withoutPropagation { + Defaults[key1] = true + } + expect.fulfill() + } + + Defaults[key1] = false + observation.invalidate() + + waitForExpectations(timeout: 10) + } + + // This checks if the callback is still being called if the value is changed on a second thread while the initial thread is doing some long running task. + func testObservePreventPropagationMultipleThreads() { + let key1 = Defaults.Key("preventPropagation3", default: nil) + let expect = expectation(description: "No infinite recursion") + + var observation: Defaults.Observation! + observation = Defaults.observe(key1, options: []) { _ in + Defaults.withoutPropagation { + Defaults[key1]! += 1 + } + print("--- Main Thread: \(Thread.isMainThread)") + if !Thread.isMainThread { + XCTAssert(Defaults[key1]! == 4) + expect.fulfill() + } else { + usleep(100000) + print("--- Release: \(Thread.isMainThread)") + } + } + DispatchQueue.global().asyncAfter(deadline: .now() + 0.05) { + Defaults[key1]! += 1 + } + Defaults[key1] = 1 + observation.invalidate() + + waitForExpectations(timeout: 10) + } + + // Check if propagation prevention works across multiple observations. + func testObservePreventPropagationMultipleObservations() { + let key1 = Defaults.Key("preventPropagation4", default: nil) + let key2 = Defaults.Key("preventPropagation5", default: nil) + let expect = expectation(description: "No infinite recursion") + + let observation1 = Defaults.observe(key2, options: []) { _ in + XCTFail() + } + let observation2 = Defaults.observe(keys: key1, key2, options: []) { + Defaults.withoutPropagation { + Defaults[key2] = true + } + expect.fulfill() + } + + Defaults[key1] = false + observation1.invalidate() + observation2.invalidate() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObservePreventPropagationCombine() { + let key1 = Defaults.Key("preventPropagation6", default: nil) + let expect = expectation(description: "No infinite recursion") + + var wasInside = false + let cancellable = Defaults.publisher(key1, options: []).sink { _ in + XCTAssertFalse(wasInside) + wasInside = true + Defaults.withoutPropagation { + Defaults[key1] = true + } + expect.fulfill() + } + + Defaults[key1] = false + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObservePreventPropagationMultipleKeysCombine() { + let key1 = Defaults.Key("preventPropagation7", default: nil) + let key2 = Defaults.Key("preventPropagation8", default: nil) + let expect = expectation(description: "No infinite recursion") + + var wasInside = false + let cancellable = Defaults.publisher(keys: key1, key2, options: []).sink { _ in + XCTAssertFalse(wasInside) + wasInside = true + Defaults.withoutPropagation { + Defaults[key1] = true + } + expect.fulfill() + } + + Defaults[key2] = false + cancellable.cancel() + + waitForExpectations(timeout: 10) + } + + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) + func testObservePreventPropagationModifiersCombine() { + let key1 = Defaults.Key("preventPropagation9", default: nil) + let expect = expectation(description: "No infinite recursion") + + var wasInside = false + var cancellable: AnyCancellable! + cancellable = Defaults.publisher(key1, options: []) + .receive(on: DispatchQueue.main) + .delay(for: 0.5, scheduler: DispatchQueue.global()) + .sink { _ in + XCTAssertFalse(wasInside) + wasInside = true + Defaults.withoutPropagation { + Defaults[key1] = true + } + expect.fulfill() + cancellable.cancel() + } + + Defaults[key1] = false + + waitForExpectations(timeout: 10) + } + + func testResetKey() { + let defaultFixture1 = "foo1" + let defaultFixture2 = 0 + let defaultFixture3 = "foo3" + let newFixture1 = "bar1" + let newFixture2 = 1 + let newFixture3 = "bar3" + let key1 = Defaults.Key("key1", default: defaultFixture1) + let key2 = Defaults.Key("key2", default: defaultFixture2) + Defaults[key1] = newFixture1 + Defaults[key2] = newFixture2 + Defaults.reset(key1) + XCTAssertEqual(Defaults[key1], defaultFixture1) + XCTAssertEqual(Defaults[key2], newFixture2) + + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) { + let key3 = Defaults.NSSecureCodingKey("key3", default: ExamplePersistentHistory(value: defaultFixture3)) + Defaults[key3] = ExamplePersistentHistory(value: newFixture3) + Defaults.reset(key3) + + XCTAssertEqual(Defaults[key3].value, defaultFixture3) + } + } + + func testResetMultipleKeys() { + let defaultFxiture1 = "foo1" + let defaultFixture2 = 0 + let defaultFixture3 = "foo3" + let newFixture1 = "bar1" + let newFixture2 = 1 + let newFixture3 = "bar3" + let key1 = Defaults.Key("akey1", default: defaultFxiture1) + let key2 = Defaults.Key("akey2", default: defaultFixture2) + let key3 = Defaults.Key("akey3", default: defaultFixture3) + Defaults[key1] = newFixture1 + Defaults[key2] = newFixture2 + Defaults[key3] = newFixture3 + Defaults.reset(key1, key2) + XCTAssertEqual(Defaults[key1], defaultFxiture1) + XCTAssertEqual(Defaults[key2], defaultFixture2) + XCTAssertEqual(Defaults[key3], newFixture3) + } + + func testResetOptionalKey() { + let newString1 = "bar1" + let newString2 = "bar2" + let newString3 = "bar3" + let key1 = Defaults.Key("optionalKey1") + let key2 = Defaults.Key("optionalKey2") + Defaults[key1] = newString1 + Defaults[key2] = newString2 + Defaults.reset(key1) + XCTAssertEqual(Defaults[key1], nil) + XCTAssertEqual(Defaults[key2], newString2) + + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) { + let key3 = Defaults.NSSecureCodingOptionalKey("optionalKey3") + Defaults[key3] = ExamplePersistentHistory(value: newString3) + Defaults.reset(key3) + XCTAssertEqual(Defaults[key3], nil) + } + } + + func testResetMultipleOptionalKeys() { + let newFixture1 = "bar1" + let newFixture2 = 1 + let newFixture3 = "bar3" + let key1 = Defaults.Key("aoptionalKey1") + let key2 = Defaults.Key("aoptionalKey2") + let key3 = Defaults.Key("aoptionalKey3") + Defaults[key1] = newFixture1 + Defaults[key2] = newFixture2 + Defaults[key3] = newFixture3 + Defaults.reset(key1, key2) + XCTAssertEqual(Defaults[key1], nil) + XCTAssertEqual(Defaults[key2], nil) + XCTAssertEqual(Defaults[key3], newFixture3) + } + + func testObserveWithLifetimeTie() { + let key = Defaults.Key("lifetimeTie", default: false) + let expect = expectation(description: "Observation closure being called") + + weak var observation: Defaults.Observation! + observation = Defaults.observe(key, options: []) { _ in + observation.invalidate() + expect.fulfill() + }.tieToLifetime(of: self) + + Defaults[key] = true + + waitForExpectations(timeout: 10) + } + + func testObserveWithLifetimeTieManualBreak() { + let key = Defaults.Key("lifetimeTieManualBreak", default: false) + + weak var observation: Defaults.Observation? = Defaults.observe(key, options: []) { _ in }.tieToLifetime(of: self) + observation!.removeLifetimeTie() + + for i in 1...10 { + if observation == nil { + break + } + + sleep(1) + + if i == 10 { + XCTFail() + } + } + } +} diff --git a/Dependencies/Defaults/license b/Dependencies/Defaults/license new file mode 100644 index 00000000..e7af2f77 --- /dev/null +++ b/Dependencies/Defaults/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +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. diff --git a/Dependencies/Defaults/readme.md b/Dependencies/Defaults/readme.md new file mode 100644 index 00000000..f24a6963 --- /dev/null +++ b/Dependencies/Defaults/readme.md @@ -0,0 +1,558 @@ +# Defaults [![Build Status](https://travis-ci.org/sindresorhus/Defaults.svg?branch=master)](https://travis-ci.org/sindresorhus/Defaults) + +> Swifty and modern [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults) + +Store key-value pairs persistently across launches of your app. + +It uses `NSUserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences. + +It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter). + +For a real-world example, see my [Plash app](https://github.com/sindresorhus/Plash/blob/533dbc888d8ba3bd9581e60320af282a22c53f85/Plash/Constants.swift#L9-L18). + +## Highlights + +- **Strongly typed:** You declare the type and default value upfront. +- **Codable support:** You can store any [Codable](https://developer.apple.com/documentation/swift/codable) value, like an enum. +- **NSSecureCoding support:** You can store any [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding) value. +- **SwiftUI:** Property wrapper that updates the view when the `UserDefaults` value changes. +- **Publishers:** Combine publishers built-in. +- **Observation:** Observe changes to keys. +- **Debuggable:** The data is stored as JSON-serialized values. + +## Compatibility + +- macOS 10.12+ +- iOS 10+ +- tvOS 10+ +- watchOS 3+ + +## Install + +#### Swift Package Manager + +Add `https://github.com/sindresorhus/Defaults` in the [“Swift Package Manager” tab in Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). + +You also need to set the build setting “Other Linker Flags” to `-weak_framework Combine` to work around [this Xcode bug](https://github.com/feedback-assistant/reports/issues/44). + +#### Carthage + +``` +github "sindresorhus/Defaults" +``` + +#### CocoaPods + +```ruby +pod 'Defaults' +``` + +## Usage + +You declare the defaults keys upfront with type and default value. + +```swift +import Cocoa +import Defaults + +extension Defaults.Keys { + static let quality = Key("quality", default: 0.8) + // ^ ^ ^ ^ + // Key Type UserDefaults name Default value +} +``` + +You can then access it as a subscript on the `Defaults` global: + +```swift +Defaults[.quality] +//=> 0.8 + +Defaults[.quality] = 0.5 +//=> 0.5 + +Defaults[.quality] += 0.1 +//=> 0.6 + +Defaults[.quality] = "🦄" +//=> [Cannot assign value of type 'String' to type 'Double'] +``` + +You can also declare optional keys for when you don't want to declare a default value upfront: + +```swift +extension Defaults.Keys { + static let name = Key("name") +} + +if let name = Defaults[.name] { + print(name) +} +``` + +The default value is then `nil`. + +--- + +If you have `NSSecureCoding` classes which you want to save, you can use them as follows: + +```swift +extension Defaults.Keys { + static let someSecureCoding = NSSecureCodingKey("someSecureCoding", default: SomeNSSecureCodingClass(string: "Default", int: 5, bool: true)) + static let someOptionalSecureCoding = NSSecureCodingOptionalKey("someOptionalSecureCoding") +} + +Defaults[.someSecureCoding].string +//=> "Default" + +Defaults[.someSecureCoding].int +//=> 5 + +Defaults[.someSecureCoding].bool +//=> true +``` + +You can use those keys just like in all the other examples. The return value will be your `NSSecureCoding` class. + +### Enum example + +```swift +enum DurationKeys: String, Codable { + case tenMinutes = "10 Minutes" + case halfHour = "30 Minutes" + case oneHour = "1 Hour" +} + +extension Defaults.Keys { + static let defaultDuration = Key("defaultDuration", default: .oneHour) +} + +Defaults[.defaultDuration].rawValue +//=> "1 Hour" +``` + +### Use keys directly + +You are not required to attach keys to `Defaults.Keys`. + +```swift +let isUnicorn = Defaults.Key("isUnicorn", default: true) + +Defaults[isUnicorn] +//=> true +``` + +### SwiftUI support + +You can use the `@Default` property wrapper to get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`. + +```swift +extension Defaults.Keys { + static let hasUnicorn = Key("hasUnicorn", default: false) +} + +struct ContentView: View { + @Default(.hasUnicorn) var hasUnicorn + + var body: some View { + Text("Has Unicorn: \(hasUnicorn)") + Toggle("Toggle Unicorn", isOn: $hasUnicorn) + } +} +``` + +Note that it's `@Default`, not `@Defaults`. + +You cannot use `@Default` in an `ObservableObject`. It's meant to be used in a `View`. + +This is only implemented for `Defaults.Key`. PR welcome for `Defaults.NSSecureCoding` if you need it. + +### Observe changes to a key + +```swift +extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) +} + +let observer = Defaults.observe(.isUnicornMode) { change in + // Initial event + print(change.oldValue) + //=> false + print(change.newValue) + //=> false + + // First actual event + print(change.oldValue) + //=> false + print(change.newValue) + //=> true +} + +Defaults[.isUnicornMode] = true +``` + +In contrast to the native `UserDefaults` key observation, here you receive a strongly-typed change object. + +There is also an observation API using the [Combine](https://developer.apple.com/documentation/combine) framework, exposing a [Publisher](https://developer.apple.com/documentation/combine/publisher) for key changes: + +```swift +let publisher = Defaults.publisher(.isUnicornMode) + +let cancellable = publisher.sink { change in + // Initial event + print(change.oldValue) + //=> false + print(change.newValue) + //=> false + + // First actual event + print(change.oldValue) + //=> false + print(change.newValue) + //=> true +} + +Defaults[.isUnicornMode] = true + +// To invalidate the observation. +cancellable.cancel() +``` + +### Invalidate observations automatically + +```swift +extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) +} + +final class Foo { + init() { + Defaults.observe(.isUnicornMode) { change in + print(change.oldValue) + print(change.newValue) + }.tieToLifetime(of: self) + } +} + +Defaults[.isUnicornMode] = true +``` + +The observation will be valid until `self` is deinitialized. + +### Reset keys to their default values + +```swift +extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: false) +} + +Defaults[.isUnicornMode] = true +//=> true + +Defaults.reset(.isUnicornMode) + +Defaults[.isUnicornMode] +//=> false +``` + +This works for a `Key` with an optional too, which will be reset back to `nil`. + +### Control propagation of change events + +Changes made within the `Defaults.withoutPropagation` closure will not be propagated to observation callbacks (`Defaults.observe()` or `Defaults.publisher()`), and therefore could prevent infinite recursion. + +```swift +let observer = Defaults.observe(keys: .key1, .key2) { + // … + + Defaults.withoutPropagation { + // Update `.key1` without propagating the change to listeners. + Defaults[.key1] = 11 + } + + // This will be propagated. + Defaults[.someKey] = true + } +``` + +### It's just `UserDefaults` with sugar + +This works too: + +```swift +extension Defaults.Keys { + static let isUnicorn = Key("isUnicorn", default: true) +} + +UserDefaults.standard[.isUnicorn] +//=> true +``` + +### Shared `UserDefaults` + +```swift +let extensionDefaults = UserDefaults(suiteName: "com.unicorn.app")! + +extension Defaults.Keys { + static let isUnicorn = Key("isUnicorn", default: true, suite: extensionDefaults) +} + +Defaults[.isUnicorn] +//=> true + +// Or + +extensionDefaults[.isUnicorn] +//=> true +``` + +### Default values are registered with `UserDefaults` + +When you create a `Defaults.Key`, it automatically registers the `default` value with normal `UserDefaults`. This means you can make use of the default value in, for example, bindings in Interface Builder. + +```swift +extension Defaults.Keys { + static let isUnicornMode = Key("isUnicornMode", default: true) +} + +print(UserDefaults.standard.bool(forKey: isUnicornMode.name)) +//=> true +``` + +## API + +### `Defaults` + +#### `Defaults.Keys` + +Type: `class` + +Stores the keys. + +#### `Defaults.Key` *(alias `Defaults.Keys.Key`)* + +```swift +Defaults.Key(_ key: String, default: T, suite: UserDefaults = .standard) +``` + +Type: `class` + +Create a key with a default value. + +The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding. + +#### `Defaults.NSSecureCodingKey` *(alias `Defaults.Keys.NSSecureCodingKey`)* + +```swift +Defaults.NSSecureCodingKey(_ key: String, default: T, suite: UserDefaults = .standard) +``` + +Type: `class` + +Create a NSSecureCoding key with a default value. + +The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding. + +#### `Defaults.NSSecureCodingOptionalKey` *(alias `Defaults.Keys.NSSecureCodingOptionalKey`)* + +```swift +Defaults.NSSecureCodingOptionalKey(_ key: String, suite: UserDefaults = .standard) +``` + +Type: `class` + +Create a NSSecureCoding key with an optional value. + +#### `Defaults.reset(keys…)` + +Type: `func` + +Reset the given keys back to their default values. + +You can specify up to 10 keys. If you need to specify more, call this method multiple times. + +You can also specify string keys, which can be useful if you need to store some keys in a collection, as it's not possible to store `Defaults.Key` in a collection because it's generic. + +#### `Defaults.observe` + +```swift +Defaults.observe( + _ key: Defaults.Key, + options: ObservationOptions = [.initial], + handler: @escaping (KeyChange) -> Void +) -> Defaults.Observation +``` + +```swift +Defaults.observe( + _ key: Defaults.NSSecureCodingKey, + options: ObservationOptions = [.initial], + handler: @escaping (NSSecureCodingKeyChange) -> Void +) -> Defaults.Observation +``` + +```swift +Defaults.observe( + _ key: Defaults.NSSecureCodingOptionalKey, + options: ObservationOptions = [.initial], + handler: @escaping (NSSecureCodingOptionalKeyChange) -> Void +) -> Defaults.Observation +``` + +Type: `func` + +Observe changes to a key or an optional key. + +By default, it will also trigger an initial event on creation. This can be useful for setting default values on controls. You can override this behavior with the `options` argument. + +#### `Defaults.observe(keys: keys..., options:)` + +Type: `func` + +Observe multiple keys of any type, but without any information about the changes. + +Options are the same as in `.observe(…)` for a single key. + +#### `Defaults.publisher(_ key:, options:)` + +```swift +Defaults.publisher( + _ key: Defaults.Key, + options: ObservationOptions = [.initial] +) -> AnyPublisher, Never> +``` + +```swift +Defaults.publisher( + _ key: Defaults.NSSecureCodingKey, + options: ObservationOptions = [.initial] +) -> AnyPublisher, Never> +``` + +```swift +Defaults.publisher( + _ key: Defaults.NSSecureCodingOptionalKey, + options: ObservationOptions = [.initial] +) -> AnyPublisher, Never> +``` + +Type: `func` + +Observation API using [Publisher](https://developer.apple.com/documentation/combine/publisher) from the [Combine](https://developer.apple.com/documentation/combine) framework. + +Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+. + +#### `Defaults.publisher(keys: keys…, options:)` + +Type: `func` + +[Combine](https://developer.apple.com/documentation/combine) observation API for multiple key observation, but without specific information about changes. + +Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+. + +#### `Defaults.removeAll` + +```swift +Defaults.removeAll(suite: UserDefaults = .standard) +``` + +Type: `func` + +Remove all entries from the given `UserDefaults` suite. + +### `Defaults.Observation` + +Type: `protocol` + +Represents an observation of a defaults key. + +#### `Defaults.Observation#invalidate` + +```swift +Defaults.Observation#invalidate() +``` + +Type: `func` + +Invalidate the observation. + +#### `Defaults.Observation#tieToLifetime` + +```swift +@discardableResult +Defaults.Observation#tieToLifetime(of weaklyHeldObject: AnyObject) -> Self +``` + +Type: `func` + +Keep the observation alive for as long as, and no longer than, another object exists. + +When `weaklyHeldObject` is deinitialized, the observation is invalidated automatically. + +#### `Defaults.Observation.removeLifetimeTie` + +```swift +Defaults.Observation#removeLifetimeTie() +``` + +Type: `func` + +Break the lifetime tie created by `tieToLifetime(of:)`, if one exists. + +The effects of any call to `tieToLifetime(of:)` are reversed. Note however that if the tied-to object has already died, then the observation is already invalid and this method has no logical effect. + +#### `Defaults.withoutPropagation(_ closure:)` + +Execute the closure without triggering change events. + +Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key. + +### `@Default(_ key:)` + +Get/set a `Defaults` item and also have the view be updated when the value changes. + +This is only implemented for `Defaults.Key`. PR welcome for `Defaults.NSSecureCoding` if you need it. + +## FAQ + +### How can I store a dictionary of arbitrary values? + +You cannot store `[String: Any]` directly as it cannot conform to `Codable`. However, you can use the [`AnyCodable`](https://github.com/Flight-School/AnyCodable) package to work around this `Codable` limitation: + +```swift +import AnyCodable + +extension Defaults.Keys { + static let magic = Key<[String: AnyCodable]>("magic", default: [:]) +} + +// … + +Defaults[.magic]["unicorn"] = "🦄" + +if let value = Defaults[.magic]["unicorn"]?.value { + print(value) + //=> "🦄" +} + +Defaults[.magic]["number"] = 3 +Defaults[.magic]["boolean"] = true +``` + +### How is this different from [`SwiftyUserDefaults`](https://github.com/radex/SwiftyUserDefaults)? + +It's inspired by that package and other solutions. The main difference is that this module doesn't hardcode the default values and comes with Codable support. + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Kacper Rączy](https://github.com/fredyshox) + +## Related + +- [Preferences](https://github.com/sindresorhus/Preferences) - Add a preferences window to your macOS app +- [KeyboardShortcuts](https://github.com/sindresorhus/KeyboardShortcuts) - Add user-customizable global keyboard shortcuts to your macOS app +- [LaunchAtLogin](https://github.com/sindresorhus/LaunchAtLogin) - Add "Launch at Login" functionality to your macOS app +- [DockProgress](https://github.com/sindresorhus/DockProgress) - Show progress in your app's Dock icon +- [Gifski](https://github.com/sindresorhus/Gifski) - Convert videos to high-quality GIFs on your Mac +- [More…](https://github.com/search?q=user%3Asindresorhus+language%3Aswift) diff --git a/Dependencies/Haxcessibility.gitcheckout b/Dependencies/Haxcessibility.gitcheckout new file mode 100644 index 00000000..f75045fd --- /dev/null +++ b/Dependencies/Haxcessibility.gitcheckout @@ -0,0 +1 @@ +trunk:e27e9c79fc4413d97a29b5eb72ed454aeace94c4 diff --git a/Dependencies/Haxcessibility.giturl b/Dependencies/Haxcessibility.giturl new file mode 100644 index 00000000..097046bb --- /dev/null +++ b/Dependencies/Haxcessibility.giturl @@ -0,0 +1 @@ +git@github.com:numist/Haxcessibility.git \ No newline at end of file diff --git a/Dependencies/Haxcessibility/.gitignore b/Dependencies/Haxcessibility/.gitignore new file mode 100644 index 00000000..8c171e4c --- /dev/null +++ b/Dependencies/Haxcessibility/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +xcuserdata +*.mode* +*.pbxuser +*.xcuserdatad +*.xccheckout \ No newline at end of file diff --git a/Dependencies/Haxcessibility/Classes/HAXApplication.h b/Dependencies/Haxcessibility/Classes/HAXApplication.h new file mode 100644 index 00000000..3441bbb1 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXApplication.h @@ -0,0 +1,21 @@ +// HAXApplication.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import + +#import + +@class HAXWindow; + +@interface HAXApplication : HAXElement + +@property (readonly, nullable) HAXWindow * focusedWindow; +@property (readonly, nonnull) NSArray *windows; +@property (readonly, nullable) NSString *localizedName; + ++(nullable HAXApplication *)applicationWithPID:(pid_t)pid; + +-(nullable HAXWindow *)windowWithID:(CGWindowID)cgWindowID; + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXApplication.m b/Dependencies/Haxcessibility/Classes/HAXApplication.m new file mode 100644 index 00000000..a57cff95 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXApplication.m @@ -0,0 +1,45 @@ +// HAXApplication.m +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import "HAXApplication.h" +#import "HAXElement+Protected.h" +#import "HAXWindow.h" + +@implementation HAXApplication + ++(HAXApplication *)applicationWithPID:(pid_t)pid { + AXUIElementRef app = AXUIElementCreateApplication(pid); + id result = nil; + if (app) { + result = [[HAXApplication alloc] initWithElementRef:app]; + CFRelease(app); + } + return result; +} + +-(HAXWindow *)focusedWindow { + return [self elementOfClass:[HAXWindow class] forKey:(NSString *)kAXFocusedWindowAttribute error:NULL]; +} + +-(NSArray *)windows { + NSArray *axWindowObjects = [self getAttributeValueForKey:(NSString *)kAXWindowsAttribute error:NULL]; + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[axWindowObjects count]]; + for (id axObject in axWindowObjects) { + [result addObject:[[HAXWindow alloc] initWithElementRef:(AXUIElementRef)axObject]]; + } + return result; +} + +-(NSString *)localizedName { + return [self getAttributeValueForKey:(NSString *)kAXTitleAttribute error:NULL]; +} + +-(HAXWindow *)windowWithID:(CGWindowID)cgWindowID { + for (HAXWindow *window in self.windows) { + if (window.cgWindowID == cgWindowID) { return window; } + } + return nil; +} + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXButton.h b/Dependencies/Haxcessibility/Classes/HAXButton.h new file mode 100644 index 00000000..37e0e2b1 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXButton.h @@ -0,0 +1,11 @@ +// HAXButton.h +// Created by Kocsis Olivér on 2014-05-21 +// Copyright 2014 Joinect Technologies + +#import + +@interface HAXButton : HAXView + +-(void)press; + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXButton.m b/Dependencies/Haxcessibility/Classes/HAXButton.m new file mode 100644 index 00000000..5155ef98 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXButton.m @@ -0,0 +1,14 @@ +// HAXButton.m +// Created by Kocsis Olivér on 2014-05-21 +// Copyright 2014 Joinect Technologies + +#import "HAXButton.h" +#import "HAXElement+Protected.h" + +@implementation HAXButton + +-(void)press { + [self performAction:(__bridge NSString *)kAXPressAction error:NULL]; +} + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h b/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h new file mode 100644 index 00000000..7197d7f4 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXElement+Protected.h @@ -0,0 +1,22 @@ +// HAXElement+Protected.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import + +#include + +@interface HAXElement () + +-(instancetype)initWithElementRef:(AXUIElementRef)elementRef __attribute__((nonnull(1))); + +@property (nonatomic, readonly) AXUIElementRef elementRef __attribute__((NSObject)); + +-(id)getAttributeValueForKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1))); +-(CFTypeRef)copyAttributeValueForKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1))); +-(BOOL)setAttributeValue:(CFTypeRef)value forKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1,2))); +-(BOOL)performAction:(NSString *)action error:(NSError **)error __attribute__((nonnull(1))); + +-(id)elementOfClass:(Class)klass forKey:(NSString *)key error:(NSError **)error __attribute__((nonnull(1,2))); + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXElement.h b/Dependencies/Haxcessibility/Classes/HAXElement.h new file mode 100644 index 00000000..6a5725e0 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXElement.h @@ -0,0 +1,27 @@ +// HAXElement.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import + +@protocol HAXElementDelegate; + +@class HAXButton; + +@interface HAXElement : NSObject + +@property (nonatomic, nullable, weak) id delegate; +@property (nonatomic, nullable, readonly) NSString *title; +@property (nonatomic, nullable, readonly) NSString *role; +@property (nonatomic, readonly) BOOL hasChildren; +@property (nonatomic, nullable, readonly) NSArray *children; +@property (nonatomic, nullable, readonly) NSArray *attributeNames; +@property (nonatomic, nullable, readonly) NSArray *buttons; +@property (nonatomic, readonly) pid_t processIdentifier; + +@end + +@protocol HAXElementDelegate +@optional +-(void)elementWasDestroyed:(nonnull HAXElement *)element; +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXElement.m b/Dependencies/Haxcessibility/Classes/HAXElement.m new file mode 100644 index 00000000..bff21a43 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXElement.m @@ -0,0 +1,230 @@ +// HAXElement.m +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import "HAXElement+Protected.h" +#import "HAXButton.h" + + +@interface HAXElement () + +@property (nonatomic, strong) AXObserverRef observer __attribute__((NSObject)); + +@end + + +@implementation HAXElement + +-(instancetype)initWithElementRef:(AXUIElementRef)elementRef { + if((self = [super init])) { + _elementRef = CFRetain(elementRef); + } + return self; +} + +-(void)dealloc { + if (_observer) { + [self removeAXObserver]; + } + if (_elementRef) { + CFRelease(_elementRef); + _elementRef = NULL; + } +} + +-(BOOL)isEqual:(id)object { + return + [object isKindOfClass:self.class] + && CFEqual(self.elementRef, [object elementRef]); +} + +-(NSUInteger)hash { + return CFHash(self.elementRef); +} + +-(void)setDelegate:(id)delegate { + if (delegate && !_observer) { + [self addAXObserver]; + } + _delegate = delegate; +} + +-(id)getAttributeValueForKey:(NSString *)key error:(NSError **)error { + CFTypeRef result = [self copyAttributeValueForKey:key error:error]; + return result ? CFBridgingRelease(result) : nil; +} + +-(CFTypeRef)copyAttributeValueForKey:(NSString *)key error:(NSError **)error { + NSParameterAssert(key != nil); + CFTypeRef attributeRef = NULL; + AXError result = AXUIElementCopyAttributeValue(self.elementRef, (__bridge CFStringRef)key, &attributeRef); + if((result != kAXErrorSuccess) && error) { + *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{ + @"key": key, + @"elementRef": (id)self.elementRef} + ]; + } + return attributeRef; +} + +-(BOOL)setAttributeValue:(CFTypeRef)value forKey:(NSString *)key error:(NSError **)error { + NSParameterAssert(value != nil); + NSParameterAssert(key != nil); + AXError result = AXUIElementSetAttributeValue(self.elementRef, (__bridge CFStringRef)key, value); + if((result != kAXErrorSuccess) && error) { + *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{ + @"key": key, + @"elementRef": (id)self.elementRef + }]; + } + return result == kAXErrorSuccess; +} + +-(BOOL)performAction:(NSString *)action error:(NSError **)error { + NSParameterAssert(action != nil); + AXError result = AXUIElementPerformAction(self.elementRef, (__bridge CFStringRef)action); + if ((result != kAXErrorSuccess) && error) { + *error = [NSError errorWithDomain:NSStringFromClass(self.class) code:result userInfo:@{ + @"action": action, + @"elementRef": (id)self.elementRef + }]; + } + + return result == kAXErrorSuccess; +} + + +-(id)elementOfClass:(Class)klass forKey:(NSString *)key error:(NSError **)error { + AXUIElementRef subelementRef = (AXUIElementRef)[self copyAttributeValueForKey:key error:error]; + id result = nil; + if (subelementRef) { + result = [[klass alloc] initWithElementRef:subelementRef]; + CFRelease(subelementRef); + subelementRef = NULL; + } + return result; +} + + +-(void)addAXObserver { + if (self.observer) { return; } + + AXObserverRef observer; + AXError err; + pid_t pid; + + err = AXUIElementGetPid(self.elementRef, &pid); + if (err != kAXErrorSuccess) { return; } + + err = AXObserverCreate(pid, axCallback, &observer); + if (err != kAXErrorSuccess) { return; } + + err = AXObserverAddNotification(observer, self.elementRef, kAXUIElementDestroyedNotification, (__bridge void *)(self)); + if (err != kAXErrorSuccess) { + CFRelease(observer); + observer = NULL; + return; + } + + CFRunLoopAddSource([[NSRunLoop mainRunLoop] getCFRunLoop], AXObserverGetRunLoopSource(observer), kCFRunLoopDefaultMode); + + self.observer = observer; + CFRelease(observer); +} + +static void axCallback(AXObserverRef observer, AXUIElementRef element, CFStringRef notification, void *refcon) { + [(__bridge HAXElement *)refcon didObserveNotification:(__bridge NSString *)notification]; +} + +-(void)didObserveNotification:(NSString *)notification { + id delegate = self.delegate; + + if ([notification isEqualToString:(__bridge NSString *)kAXUIElementDestroyedNotification] && [delegate respondsToSelector:@selector(elementWasDestroyed:)]) { + [delegate elementWasDestroyed:self]; + } +} + +-(void)removeAXObserver { + if (!self.observer) { return; } + + (void)AXObserverRemoveNotification(self.observer, self.elementRef, kAXUIElementDestroyedNotification); + + CFRunLoopSourceRef observerRunLoopSource = AXObserverGetRunLoopSource(self.observer); + if (observerRunLoopSource) { + CFRunLoopRemoveSource([[NSRunLoop mainRunLoop] getCFRunLoop], observerRunLoopSource, kCFRunLoopDefaultMode); + } + + self.observer = NULL; +} + +-(BOOL)hasChildren { + return (self.children.count > 0); +} + +-(NSArray *)children { + NSArray * axUIElements = nil; + NSMutableArray * result = nil; + + axUIElements = [self getAttributeValueForKey:(__bridge NSString *)kAXChildrenAttribute error:NULL]; + if (axUIElements != nil) { + result = [NSMutableArray arrayWithCapacity:[axUIElements count]]; + for (id elementI in axUIElements) { + [result addObject:[[HAXElement alloc] initWithElementRef:(AXUIElementRef)(elementI)]]; + } + } + + return result; +} + +-(NSString *)role { + NSString * result = [self getAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL]; + if ([result isKindOfClass:[NSString class]] == NO) { + result = nil; + } + return result; +} + +-(NSArray *) buttons { + NSArray *axChildren = self.children; + NSMutableArray *result = nil; + + if (axChildren) { + result = [NSMutableArray arrayWithCapacity:axChildren.count]; + + for (HAXElement *haxElementI in axChildren) { + NSString * axRole = CFBridgingRelease([haxElementI copyAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL]); + if (axRole == nil) { + result = nil; + break; + } + if ([axRole isEqualToString:(__bridge NSString *)kAXButtonRole]) { + HAXButton *button = [[HAXButton alloc] initWithElementRef:(AXUIElementRef)haxElementI.elementRef]; + [result addObject:button]; + } + } + } + + return result; +} + +-(NSString *)title { + NSString * result = [self getAttributeValueForKey:NSAccessibilityTitleAttribute error:NULL]; + if ([result isKindOfClass:[NSString class]] == NO) { + result = nil; + } + return result; +} + +-(NSArray *)attributeNames { + CFArrayRef attrNamesRef = NULL; + AXUIElementCopyAttributeNames(_elementRef, &attrNamesRef); + return attrNamesRef ? CFBridgingRelease(attrNamesRef) : nil; +} + +-(pid_t)processIdentifier { + pid_t result; + if (AXUIElementGetPid (self.elementRef, &result) != kAXErrorSuccess) { return 0; } + return result; +} + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXSystem.h b/Dependencies/Haxcessibility/Classes/HAXSystem.h new file mode 100644 index 00000000..67f9d5ec --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXSystem.h @@ -0,0 +1,15 @@ +// HAXSystem.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import + +@class HAXApplication; + +@interface HAXSystem : HAXElement + ++(nonnull instancetype)system; + +@property (readonly, nullable) HAXApplication *focusedApplication; + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXSystem.m b/Dependencies/Haxcessibility/Classes/HAXSystem.m new file mode 100644 index 00000000..ca59d2ed --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXSystem.m @@ -0,0 +1,23 @@ +// HAXSystem.m +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import "HAXApplication.h" +#import "HAXSystem.h" +#import "HAXElement+Protected.h" + +@implementation HAXSystem + ++(instancetype)system { + AXUIElementRef element = AXUIElementCreateSystemWide(); + HAXSystem *result = [[HAXSystem alloc] initWithElementRef:element]; + CFRelease(element); + return result; +} + + +-(HAXApplication *)focusedApplication { + return [self elementOfClass:[HAXApplication class] forKey:(NSString *)kAXFocusedApplicationAttribute error:NULL]; +} + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXView.h b/Dependencies/Haxcessibility/Classes/HAXView.h new file mode 100644 index 00000000..21eb9da3 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXView.h @@ -0,0 +1,20 @@ +// HAXView.h +// Created by Kocsis Olivér on 2014-05-12 +// Copyright 2014 Joinect Technologies + +#import +#import + +@interface HAXView : HAXElement + +@property (nonatomic) CGPoint carbonOrigin; +@property (nonatomic) NSPoint origin; +@property (nonatomic) CGSize size; +@property (nonatomic) CGRect carbonFrame; +@property (nonatomic) NSRect frame; +@property (nullable, readonly) NSString *title; +@property (nullable, readonly) NSScreen *screen; +@property (readonly, getter=isFullscreen) BOOL fullscreen; + +@end + diff --git a/Dependencies/Haxcessibility/Classes/HAXView.m b/Dependencies/Haxcessibility/Classes/HAXView.m new file mode 100644 index 00000000..03625157 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXView.m @@ -0,0 +1,107 @@ +// HAXView.m +// Created by Kocsis Olivér on 2014-05-12 +// Copyright 2014 Joinect Technologies + +#import "HAXView.h" +#import "HAXElement+Protected.h" +#import "NSScreen+Helpers.h" + +@implementation HAXView + +-(CGPoint)carbonOrigin { + CGPoint origin = {0}; + AXValueRef originRef = (AXValueRef)[self copyAttributeValueForKey:(__bridge NSString *)kAXPositionAttribute error:NULL]; + if(originRef) { + AXValueGetValue(originRef, kAXValueCGPointType, &origin); + CFRelease(originRef); + originRef = NULL; + } + return origin; +} + +-(void)setCarbonOrigin:(CGPoint)carbonOrigin { + AXValueRef originRef = AXValueCreate(kAXValueCGPointType, &carbonOrigin); + [self setAttributeValue:originRef forKey:(__bridge NSString *)kAXPositionAttribute error:NULL]; + CFRelease(originRef); +} + +-(NSPoint)origin { + return cocoaScreenFrameFromCarbonScreenFrame(self.carbonFrame).origin; +} + +-(void)setOrigin:(NSPoint)origin { + self.carbonOrigin = carbonScreenPointFromCocoaScreenPoint(origin); +} + +-(NSSize)size { + CGSize size = {0}; + AXValueRef sizeRef = (AXValueRef)[self copyAttributeValueForKey:(__bridge NSString *)kAXSizeAttribute error:NULL]; + if(sizeRef) { + AXValueGetValue(sizeRef, kAXValueCGSizeType, &size); + CFRelease(sizeRef); + sizeRef = NULL; + } + return size; +} + +-(void)setSize:(NSSize)size { + AXValueRef sizeRef = AXValueCreate(kAXValueCGSizeType, &size); + [self setAttributeValue:sizeRef forKey:(__bridge NSString *)kAXSizeAttribute error:NULL]; + CFRelease(sizeRef); +} + +-(CGRect)carbonFrame { + return (CGRect){ .origin = self.carbonOrigin, .size = self.size }; +} + +-(void)setCarbonFrame:(CGRect)carbonFrame { + self.carbonOrigin = carbonFrame.origin; + self.size = carbonFrame.size; +} + +-(NSRect)frame { + return cocoaScreenFrameFromCarbonScreenFrame(self.carbonFrame); +} + +-(void)setFrame:(NSRect)frame { + self.origin = frame.origin; + self.size = frame.size; +} + +-(NSString *)title { + return [self getAttributeValueForKey:(__bridge NSString *)kAXTitleAttribute error:NULL]; +} + +-(NSScreen *)screen { + NSScreen *matchingScreen = nil; + NSRect viewFrame = self.frame; + NSUInteger bestOverlap = 0; + for (NSScreen * screenI in [NSScreen screens]) { + NSRect intersection = NSIntersectionRect(screenI.frame, viewFrame); + NSUInteger intersectionOverlap = intersection.size.width * intersection.size.height; + if(intersectionOverlap > bestOverlap) { + matchingScreen = screenI; + bestOverlap = intersectionOverlap; + } + } + return matchingScreen; +} + +-(BOOL)isFullscreen { + BOOL isFullScreen = NO; + NSArray * sceenArray = [NSScreen screens]; + NSRect windowFrame = self.frame; + + for (NSScreen * screenI in sceenArray) { + NSRect screenFrame; + screenFrame = [screenI frame]; + if(NSEqualRects(screenFrame, windowFrame)) { + isFullScreen = YES; + break; + } + } + + return isFullScreen; +} + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXWindow.h b/Dependencies/Haxcessibility/Classes/HAXWindow.h new file mode 100644 index 00000000..15825d2d --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXWindow.h @@ -0,0 +1,17 @@ +// HAXWindow.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import +#import + +@interface HAXWindow : HAXView + +@property (nullable, readonly) NSArray *views; + +-(BOOL)raise; +-(BOOL)close; + +-(CGWindowID)cgWindowID; + +@end diff --git a/Dependencies/Haxcessibility/Classes/HAXWindow.m b/Dependencies/Haxcessibility/Classes/HAXWindow.m new file mode 100644 index 00000000..af0c3e13 --- /dev/null +++ b/Dependencies/Haxcessibility/Classes/HAXWindow.m @@ -0,0 +1,64 @@ +// HAXWindow.m +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import "HAXWindow.h" +#import "HAXElement+Protected.h" + +extern AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out); + +@implementation HAXWindow + +-(NSArray *)views { + NSArray *axChildren = self.children; + NSMutableArray *result = nil; + + if (axChildren) { + result = [NSMutableArray arrayWithCapacity:axChildren.count]; + + for (HAXElement * haxElementI in axChildren) { + NSString * axRole = [haxElementI getAttributeValueForKey:(__bridge NSString *)kAXRoleAttribute error:NULL]; + if ([axRole isEqualToString:@"AXView"]) { + HAXView * haxView = [[HAXView init] initWithElementRef:(AXUIElementRef)haxElementI.elementRef]; + [result addObject:haxView]; + } + } + } + return result; +} + +-(BOOL)raise { + __block BOOL success = NO; + pid_t pid = self.processIdentifier; + if (pid == [NSProcessInfo processInfo].processIdentifier && ![NSThread isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ success = [self raise]; }); + return success; + } + if ([self performAction:(__bridge NSString *)kAXRaiseAction error:NULL]) { + ProcessSerialNumber psn; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (pid && GetProcessForPID (pid, &psn) == 0) { + success = (noErr == SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly)); + } +#pragma clang diagnostic pop + } + return success; +} + +-(BOOL)close { + HAXElement *element = [self elementOfClass:[HAXElement class] forKey:(__bridge NSString *)kAXCloseButtonAttribute error:NULL]; + return [element performAction:(__bridge NSString *)kAXPressAction error:NULL]; +} + +// https://stackoverflow.com/a/9624565 +-(CGWindowID)cgWindowID { + CGWindowID result; + AXError err = _AXUIElementGetWindow(self.elementRef, &result); + if (err == kAXErrorSuccess) { + return result; + } + return kCGNullWindowID; +} + +@end diff --git a/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3d1ebf70 --- /dev/null +++ b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.pbxproj @@ -0,0 +1,384 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; + BC3B3B76175E9FD4002AD452 /* HAXElement+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = D44E051312D75B3D00541D6A /* HAXElement+Protected.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C3E21520194F381400C85776 /* HAXButton.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E2151C194F381400C85776 /* HAXButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C3E21521194F381400C85776 /* HAXButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E2151D194F381400C85776 /* HAXButton.m */; }; + C3E21526194F38BE00C85776 /* NSScreen+Helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */; }; + C3E21527194F38BE00C85776 /* NSScreen+Helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */; }; + C3E2152A194F38FB00C85776 /* HAXView.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E21528194F38FB00C85776 /* HAXView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C3E2152B194F38FB00C85776 /* HAXView.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E21529194F38FB00C85776 /* HAXView.m */; }; + D4D2103412D6957000509E57 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4D2103312D6957000509E57 /* Carbon.framework */; }; + D4D2103712D6959400509E57 /* HAXElement.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2103512D6959400509E57 /* HAXElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4D2103812D6959400509E57 /* HAXElement.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2103612D6959400509E57 /* HAXElement.m */; }; + D4D2105512D698F400509E57 /* HAXSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2105412D698F400509E57 /* HAXSystem.m */; }; + D4D2105912D6991900509E57 /* HAXSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2105312D698F200509E57 /* HAXSystem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4D2106D12D69C8B00509E57 /* HAXApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2106C12D69C8B00509E57 /* HAXApplication.m */; }; + D4D2108212D6A08800509E57 /* HAXWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = D4D2108112D6A08800509E57 /* HAXWindow.m */; }; + D4D2113D12D6AAF000509E57 /* Haxcessibility.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2113C12D6AAF000509E57 /* Haxcessibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4D2113E12D6AB0400509E57 /* HAXApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2106B12D69C8900509E57 /* HAXApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4D2113F12D6AB0400509E57 /* HAXWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = D4D2108012D6A08600509E57 /* HAXWindow.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 32DBCF5E0370ADEE00C91783 /* Haxcessibility.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Haxcessibility.pch; sourceTree = ""; }; + 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Haxcessibility.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C3E2151C194F381400C85776 /* HAXButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXButton.h; sourceTree = ""; }; + C3E2151D194F381400C85776 /* HAXButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAXButton.m; sourceTree = ""; }; + C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSScreen+Helpers.h"; sourceTree = ""; }; + C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSScreen+Helpers.m"; sourceTree = ""; }; + C3E21528194F38FB00C85776 /* HAXView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXView.h; sourceTree = ""; }; + C3E21529194F38FB00C85776 /* HAXView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HAXView.m; sourceTree = ""; }; + D44E051312D75B3D00541D6A /* HAXElement+Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HAXElement+Protected.h"; sourceTree = ""; }; + D4D2103312D6957000509E57 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + D4D2103512D6959400509E57 /* HAXElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HAXElement.h; sourceTree = ""; }; + D4D2103612D6959400509E57 /* HAXElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = HAXElement.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + D4D2105312D698F200509E57 /* HAXSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXSystem.h; sourceTree = ""; }; + D4D2105412D698F400509E57 /* HAXSystem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXSystem.m; sourceTree = ""; }; + D4D2106B12D69C8900509E57 /* HAXApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXApplication.h; sourceTree = ""; }; + D4D2106C12D69C8B00509E57 /* HAXApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXApplication.m; sourceTree = ""; }; + D4D2108012D6A08600509E57 /* HAXWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HAXWindow.h; sourceTree = ""; }; + D4D2108112D6A08800509E57 /* HAXWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HAXWindow.m; sourceTree = ""; }; + D4D2113C12D6AAF000509E57 /* Haxcessibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Haxcessibility.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, + D4D2103412D6957000509E57 /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Haxcessibility */ = { + isa = PBXGroup; + children = ( + D4D2102E12D6947D00509E57 /* Classes */, + 32C88DFF0371C24200C91783 /* Other Sources */, + 089C1665FE841158C02AAC07 /* Resources */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = Haxcessibility; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D4D2103312D6957000509E57 /* Carbon.framework */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 089C1665FE841158C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 8DC2EF5A0486A6940098B216 /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; + 32C88DFF0371C24200C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + D4D2113C12D6AAF000509E57 /* Haxcessibility.h */, + 32DBCF5E0370ADEE00C91783 /* Haxcessibility.pch */, + C3E21524194F38BD00C85776 /* NSScreen+Helpers.h */, + C3E21525194F38BD00C85776 /* NSScreen+Helpers.m */, + ); + path = "Other Sources"; + sourceTree = ""; + }; + D4D2102E12D6947D00509E57 /* Classes */ = { + isa = PBXGroup; + children = ( + D4D2106B12D69C8900509E57 /* HAXApplication.h */, + D4D2106C12D69C8B00509E57 /* HAXApplication.m */, + C3E2151C194F381400C85776 /* HAXButton.h */, + C3E2151D194F381400C85776 /* HAXButton.m */, + D4D2103512D6959400509E57 /* HAXElement.h */, + D44E051312D75B3D00541D6A /* HAXElement+Protected.h */, + D4D2103612D6959400509E57 /* HAXElement.m */, + D4D2105312D698F200509E57 /* HAXSystem.h */, + D4D2105412D698F400509E57 /* HAXSystem.m */, + C3E21528194F38FB00C85776 /* HAXView.h */, + C3E21529194F38FB00C85776 /* HAXView.m */, + D4D2108012D6A08600509E57 /* HAXWindow.h */, + D4D2108112D6A08800509E57 /* HAXWindow.m */, + ); + path = Classes; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D4D2105912D6991900509E57 /* HAXSystem.h in Headers */, + C3E2152A194F38FB00C85776 /* HAXView.h in Headers */, + D4D2103712D6959400509E57 /* HAXElement.h in Headers */, + D4D2113E12D6AB0400509E57 /* HAXApplication.h in Headers */, + C3E21520194F381400C85776 /* HAXButton.h in Headers */, + D4D2113F12D6AB0400509E57 /* HAXWindow.h in Headers */, + C3E21526194F38BE00C85776 /* NSScreen+Helpers.h in Headers */, + D4D2113D12D6AAF000509E57 /* Haxcessibility.h in Headers */, + BC3B3B76175E9FD4002AD452 /* HAXElement+Protected.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8DC2EF4F0486A6940098B216 /* Haxcessibility */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Haxcessibility" */; + buildPhases = ( + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + 8DC2EF520486A6940098B216 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Haxcessibility; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = Haxcessibility; + productReference = 8DC2EF5B0486A6940098B216 /* Haxcessibility.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + CLASSPREFIX = HAX; + LastUpgradeCheck = 1200; + ORGANIZATIONNAME = "Rob Rix"; + }; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Haxcessibility" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 0867D691FE84028FC02AAC07 /* Haxcessibility */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* Haxcessibility */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C3E2152B194F38FB00C85776 /* HAXView.m in Sources */, + D4D2103812D6959400509E57 /* HAXElement.m in Sources */, + C3E21521194F381400C85776 /* HAXButton.m in Sources */, + D4D2105512D698F400509E57 /* HAXSystem.m in Sources */, + D4D2106D12D69C8B00509E57 /* HAXApplication.m in Sources */, + D4D2108212D6A08800509E57 /* HAXWindow.m in Sources */, + C3E21527194F38BE00C85776 /* NSScreen+Helpers.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + INFOPLIST_FILE = Resources/Info.plist; + INSTALL_PATH = "@rpath"; + PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.${PRODUCT_NAME:rfc1034Identifier}"; + PRODUCT_NAME = Haxcessibility; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Resources/Info.plist; + INSTALL_PATH = "@rpath"; + PRODUCT_BUNDLE_IDENTIFIER = "com.antitypical.${PRODUCT_NAME:rfc1034Identifier}"; + PRODUCT_NAME = Haxcessibility; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Other Sources/Haxcessibility.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "Other Sources/Haxcessibility.pch"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + SDKROOT = macosx; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Haxcessibility" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Haxcessibility" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..356e8952 --- /dev/null +++ b/Dependencies/Haxcessibility/Haxcessibility.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Dependencies/Haxcessibility/LICENSE b/Dependencies/Haxcessibility/LICENSE new file mode 100644 index 00000000..9414dc1f --- /dev/null +++ b/Dependencies/Haxcessibility/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2007-2013, Rob Rix +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of Monochrome Industries nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h new file mode 100644 index 00000000..48eae4f0 --- /dev/null +++ b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.h @@ -0,0 +1,10 @@ +// Haxcessibility.h +// Created by Rob Rix on 2011-01-06 +// Copyright 2011 Rob Rix + +#import +#import +#import +#import +#import +#import diff --git a/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch new file mode 100644 index 00000000..c0125768 --- /dev/null +++ b/Dependencies/Haxcessibility/Other Sources/Haxcessibility.pch @@ -0,0 +1,3 @@ +#ifdef __OBJC__ + #import +#endif diff --git a/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h new file mode 100644 index 00000000..a67b2bf1 --- /dev/null +++ b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.h @@ -0,0 +1,8 @@ +// NSScreen+Helpers.h +// Created by Scott Perry on 2020-09-26 +// Copyright 2020 Scott Perry + +#import + +NSRect cocoaScreenFrameFromCarbonScreenFrame(CGRect carbonFrame); +CGPoint carbonScreenPointFromCocoaScreenPoint(NSPoint cocoaPoint); diff --git a/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m new file mode 100644 index 00000000..d4f2d5b1 --- /dev/null +++ b/Dependencies/Haxcessibility/Other Sources/NSScreen+Helpers.m @@ -0,0 +1,38 @@ +// NSScreen+Helpers.m +// Created by Scott Perry on 2020-09-26 +// Copyright 2020 Scott Perry + +#import "NSScreen+Helpers.h" + +static NSScreen *hax_screenWithPoint(NSPoint p) { + for (NSScreen *screen in [NSScreen screens]) { + if (NSPointInRect(p, [screen frame])) { + return screen; + } + } + return nil; +} + +NSRect cocoaScreenFrameFromCarbonScreenFrame(CGRect carbonFrame) { + // TODO(numist): is this actually correct with multimon? + NSRect originScreenFrame = ((NSScreen *)[NSScreen screens][0]).frame; + + return NSMakeRect( + carbonFrame.origin.x, + originScreenFrame.size.height - carbonFrame.origin.y - carbonFrame.size.height, + carbonFrame.size.width, + carbonFrame.size.height + ); +} + +CGPoint carbonScreenPointFromCocoaScreenPoint(NSPoint cocoaPoint) { + NSScreen *foundScreen = hax_screenWithPoint(cocoaPoint); + + if (foundScreen) { + return CGPointMake( + cocoaPoint.x, + foundScreen.frame.size.height - cocoaPoint.y - 1 + ); + } + return CGPointZero; +} diff --git a/Dependencies/Haxcessibility/README.mdown b/Dependencies/Haxcessibility/README.mdown new file mode 100644 index 00000000..2c6c776a --- /dev/null +++ b/Dependencies/Haxcessibility/README.mdown @@ -0,0 +1,32 @@ +# Haxcessibility + +Haxcessibility is, above all, a horrible pun on Mac OS X’s Accessibility framework and its AX prefix. I am shameless. + +Second to that, Haxcessibility is a use case–driven remote control for Mac apps by Mac apps. It enables hacks like moving and resizing another app’s windows, and there’s loads more that the AX APIs make possible that Haxcessibility could make convenient with a method or two. + + +# Use it + +Resize the focused app’s focused window to fullscreen: + + [HAXSystem system].focusedApplication.focusedWindow.frame = NSScreen.mainScreen.frame + +Close all windows in the focused app: + + [[HAXSystem system].focusedApplication.windows makeObjectsPerformSelector:@selector(close)]; + +# Improve it + +Don’t see the feature you want? Fortunately, it’s pretty easy to add your own convenience methods. [Fork Haxcessibility](https://github.com/numist/Haxcessibility/fork) and send me a pull request with your code. + +You’ll want to pay special attention to the HAXElement+Protected.h private header. `HAXElement` is the root of most functionality in Haxcessibility, and this header declares the conveniences defined for wrapping more of the Accessibility APIs’ functionality. + +# Thanks to + +This framework would not be what it is without the help of: + +- [Rob Rix](https://github.com/robrix) for creating and curating Haxcessibility for the first five years of its existence. + +- [Decimus Software](http://decimus.net) for DTerm, which showed us what you can do with the Accessibility APIs + +- [DEVONtechnologies, LLC](http://devontechnologies.com/) for their patronage of [Grid](https://github.com/robrix/Grid), the original _raison d’être_ of this framework diff --git a/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings b/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 00000000..88f65cf6 --- /dev/null +++ b/Dependencies/Haxcessibility/Resources/English.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Frameworks/NNKit/NNKit/NNKit-Info.plist b/Dependencies/Haxcessibility/Resources/Info.plist similarity index 80% rename from Frameworks/NNKit/NNKit/NNKit-Info.plist rename to Dependencies/Haxcessibility/Resources/Info.plist index 0729ca8e..f4f9da73 100644 --- a/Frameworks/NNKit/NNKit/NNKit-Info.plist +++ b/Dependencies/Haxcessibility/Resources/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - net.numist.${PRODUCT_NAME:rfc1034identifier} + ${PRODUCT_BUNDLE_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -21,9 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 1 - NSHumanReadableCopyright - Copyright © 2013 Scott Perry. All rights reserved. + 2 NSPrincipalClass diff --git a/Dependencies/Haxcessibility/TODO.mdown b/Dependencies/Haxcessibility/TODO.mdown new file mode 100644 index 00000000..3acc698f --- /dev/null +++ b/Dependencies/Haxcessibility/TODO.mdown @@ -0,0 +1,13 @@ +# To-do + +- HAXWindow + - window elements should subclass a general widget class of some description + - animate size/position/frame changes? + - Maybe something like the NSWindow -setFrame:animate: method for that. + +- Overall + - wrap AXAPIEnabled/AXIsProcessTrusted + - handle AX trust authorization + - getting auth + - setting the trust bit or whatever + - relaunching the app at the client’s request (so they can in turn ask the user, if they deem it necessary) diff --git a/Dependencies/LetsMove.gitcheckout b/Dependencies/LetsMove.gitcheckout new file mode 100644 index 00000000..1b58f0a3 --- /dev/null +++ b/Dependencies/LetsMove.gitcheckout @@ -0,0 +1 @@ +master:70c5772c2ce84613ba539cb122e4065a9e33db5b diff --git a/Dependencies/LetsMove.giturl b/Dependencies/LetsMove.giturl new file mode 100644 index 00000000..f54c5233 --- /dev/null +++ b/Dependencies/LetsMove.giturl @@ -0,0 +1 @@ +git@github.com:potionfactory/LetsMove.git \ No newline at end of file diff --git a/Dependencies/LetsMove/.gitignore b/Dependencies/LetsMove/.gitignore new file mode 100644 index 00000000..f157394b --- /dev/null +++ b/Dependencies/LetsMove/.gitignore @@ -0,0 +1,9 @@ +# Mac OS X +*.DS_Store + +# Xcode +build +*.mode* +*.pbxuser +xcuserdata/ +project.xcworkspace/ diff --git a/Dependencies/LetsMove/Base.lproj/MainMenu.xib b/Dependencies/LetsMove/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..91ffec69 --- /dev/null +++ b/Dependencies/LetsMove/Base.lproj/MainMenu.xib @@ -0,0 +1,4208 @@ + + + + 1060 + 10C540 + 740 + 1038.25 + 458.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 740 + + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + YES + + + YES + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + YES + + + LetsMove + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + LetsMove + + YES + + + About LetsMove + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide LetsMove + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit LetsMove + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + YES + + + New + n + 1048576 + 2147483647 + + + + + + Open… + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + Save As… + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + Print… + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + YES + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + YES + + + Show Spelling and Grammar + : + 1048576 + 2147483647 + + + + + + Check Document Now + ; + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + Correct Spelling Automatically + + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + YES + + + Show Substitutions + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Dashes + + 2147483647 + + + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + Text Replacement + + 2147483647 + + + + + + + + + Transformations + + 2147483647 + + + submenuAction: + + Transformations + + YES + + + Make Upper Case + + 2147483647 + + + + + + Make Lower Case + + 2147483647 + + + + + + Capitalize + + 2147483647 + + + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 2147483647 + + + submenuAction: + + Format + + YES + + + Font + + 2147483647 + + + submenuAction: + + Font + + YES + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Bold + b + 1048576 + 2147483647 + + + 2 + + + + Italic + i + 1048576 + 2147483647 + + + 1 + + + + Underline + u + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bigger + + + 1048576 + 2147483647 + + + 3 + + + + Smaller + - + 1048576 + 2147483647 + + + 4 + + + + YES + YES + + + 2147483647 + + + + + + Kern + + 2147483647 + + + submenuAction: + + Kern + + YES + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Tighten + + 2147483647 + + + + + + Loosen + + 2147483647 + + + + + + + + + Ligature + + 2147483647 + + + submenuAction: + + Ligature + + YES + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Use All + + 2147483647 + + + + + + + + + Baseline + + 2147483647 + + + submenuAction: + + Baseline + + YES + + + Use Default + + 2147483647 + + + + + + Superscript + + 2147483647 + + + + + + Subscript + + 2147483647 + + + + + + Raise + + 2147483647 + + + + + + Lower + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Colors + C + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Copy Style + c + 1572864 + 2147483647 + + + + + + Paste Style + v + 1572864 + 2147483647 + + + + + _NSFontMenu + + + + + Text + + 2147483647 + + + submenuAction: + + Text + + YES + + + Align Left + { + 1048576 + 2147483647 + + + + + + Center + | + 1048576 + 2147483647 + + + + + + Justify + + 2147483647 + + + + + + Align Right + } + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Writing Direction + + 2147483647 + + + submenuAction: + + Writing Direction + + YES + + + YES + Paragraph + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + YES + Selection + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Ruler + + 2147483647 + + + + + + Copy Ruler + c + 1310720 + 2147483647 + + + + + + Paste Ruler + v + 1310720 + 2147483647 + + + + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + YES + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Customize Toolbar… + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + YES + + + LetsMove Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{335, 390}, {480, 360}} + 1954021376 + LetsMove + NSWindow + + {3.40282e+38, 3.40282e+38} + + + 256 + + YES + + + 268 + {{105, 161}, {271, 57}} + + YES + + 68288064 + 138413056 + Let's move! + + LucidaGrande + 48 + 16 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2ODY1AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + {480, 360} + + + {{0, 0}, {1920, 1178}} + {3.40282e+38, 3.40282e+38} + + + LetsMoveAppDelegate + + + NSFontManager + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + addFontTrait: + + + + 421 + + + + addFontTrait: + + + + 422 + + + + modifyFont: + + + + 423 + + + + orderFrontFontPanel: + + + + 424 + + + + modifyFont: + + + + 425 + + + + raiseBaseline: + + + + 426 + + + + lowerBaseline: + + + + 427 + + + + copyFont: + + + + 428 + + + + subscript: + + + + 429 + + + + superscript: + + + + 430 + + + + tightenKerning: + + + + 431 + + + + underline: + + + + 432 + + + + orderFrontColorPanel: + + + + 433 + + + + useAllLigatures: + + + + 434 + + + + loosenKerning: + + + + 435 + + + + pasteFont: + + + + 436 + + + + unscript: + + + + 437 + + + + useStandardKerning: + + + + 438 + + + + useStandardLigatures: + + + + 439 + + + + turnOffLigatures: + + + + 440 + + + + turnOffKerning: + + + + 441 + + + + terminate: + + + + 449 + + + + toggleAutomaticSpellingCorrection: + + + + 456 + + + + orderFrontSubstitutionsPanel: + + + + 458 + + + + toggleAutomaticDashSubstitution: + + + + 461 + + + + toggleAutomaticTextReplacement: + + + + 463 + + + + uppercaseWord: + + + + 464 + + + + capitalizeWord: + + + + 467 + + + + lowercaseWord: + + + + 468 + + + + pasteAsPlainText: + + + + 486 + + + + performFindPanelAction: + + + + 487 + + + + performFindPanelAction: + + + + 488 + + + + performFindPanelAction: + + + + 489 + + + + showHelp: + + + + 493 + + + + delegate + + + + 495 + + + + alignCenter: + + + + 518 + + + + pasteRuler: + + + + 519 + + + + toggleRuler: + + + + 520 + + + + alignRight: + + + + 521 + + + + copyRuler: + + + + 522 + + + + alignJustified: + + + + 523 + + + + alignLeft: + + + + 524 + + + + makeBaseWritingDirectionNatural: + + + + 525 + + + + makeBaseWritingDirectionLeftToRight: + + + + 526 + + + + makeBaseWritingDirectionRightToLeft: + + + + 527 + + + + makeTextWritingDirectionNatural: + + + + 528 + + + + makeTextWritingDirectionLeftToRight: + + + + 529 + + + + makeTextWritingDirectionRightToLeft: + + + + 530 + + + + window + + + + 535 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + + + 80 + + + + + 78 + + + + + 72 + + + + + 82 + + + + + 124 + + + YES + + + + + + 77 + + + + + 73 + + + + + 79 + + + + + 112 + + + + + 74 + + + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + YES + + + + + + 216 + + + YES + + + + + + 200 + + + YES + + + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + YES + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + 297 + + + + + 298 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + YES + + + + + + 349 + + + YES + + + + + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + YES + + + + + + 372 + + + YES + + + + + + 375 + + + YES + + + + + + 376 + + + YES + + + + + + + 377 + + + YES + + + + + + 388 + + + YES + + + + + + + + + + + + + + + + + + + + + 389 + + + + + 390 + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 396 + + + + + 397 + + + YES + + + + + + 398 + + + YES + + + + + + 399 + + + YES + + + + + + 400 + + + + + 401 + + + + + 402 + + + + + 403 + + + + + 404 + + + + + 405 + + + YES + + + + + + + + + + 406 + + + + + 407 + + + + + 408 + + + + + 409 + + + + + 410 + + + + + 411 + + + YES + + + + + + + + 412 + + + + + 413 + + + + + 414 + + + + + 415 + + + YES + + + + + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 419 + + + + + 420 + + + + + 450 + + + YES + + + + + + 451 + + + YES + + + + + + + + 452 + + + + + 453 + + + + + 454 + + + + + 457 + + + + + 459 + + + + + 460 + + + + + 462 + + + + + 465 + + + + + 466 + + + + + 485 + + + + + 490 + + + YES + + + + + + 491 + + + YES + + + + + + 492 + + + + + 494 + + + + + 496 + + + YES + + + + + + 497 + + + YES + + + + + + + + + + + + + + + 498 + + + + + 499 + + + + + 500 + + + + + 501 + + + + + 502 + + + + + 503 + + + YES + + + + + + 504 + + + + + 505 + + + + + 506 + + + + + 507 + + + + + 508 + + + YES + + + + + + + + + + + + + + 509 + + + + + 510 + + + + + 511 + + + + + 512 + + + + + 513 + + + + + 514 + + + + + 515 + + + + + 516 + + + + + 517 + + + + + 533 + + + YES + + + + + + 534 + + + + + + + YES + + YES + -3.IBPluginDependency + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 200.IBEditorWindowLastContentRect + 200.IBPluginDependency + 200.ImportedFromIB2 + 200.editorWindowContentRectSynchronizationRect + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 205.IBEditorWindowLastContentRect + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 208.IBPluginDependency + 208.ImportedFromIB2 + 209.IBPluginDependency + 209.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 213.IBPluginDependency + 213.ImportedFromIB2 + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBEditorWindowLastContentRect + 220.IBPluginDependency + 220.ImportedFromIB2 + 220.editorWindowContentRectSynchronizationRect + 221.IBPluginDependency + 221.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBEditorWindowLastContentRect + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 346.IBPluginDependency + 346.ImportedFromIB2 + 348.IBPluginDependency + 348.ImportedFromIB2 + 349.IBEditorWindowLastContentRect + 349.IBPluginDependency + 349.ImportedFromIB2 + 349.editorWindowContentRectSynchronizationRect + 350.IBPluginDependency + 350.ImportedFromIB2 + 351.IBPluginDependency + 351.ImportedFromIB2 + 354.IBPluginDependency + 354.ImportedFromIB2 + 371.IBEditorWindowLastContentRect + 371.IBPluginDependency + 371.IBWindowTemplateEditedContentRect + 371.NSWindowTemplate.visibleAtLaunch + 371.editorWindowContentRectSynchronizationRect + 371.windowTemplate.maxSize + 372.IBPluginDependency + 375.IBPluginDependency + 376.IBEditorWindowLastContentRect + 376.IBPluginDependency + 377.IBPluginDependency + 388.IBEditorWindowLastContentRect + 388.IBPluginDependency + 389.IBPluginDependency + 390.IBPluginDependency + 391.IBPluginDependency + 392.IBPluginDependency + 393.IBPluginDependency + 394.IBPluginDependency + 395.IBPluginDependency + 396.IBPluginDependency + 397.IBPluginDependency + 398.IBPluginDependency + 399.IBPluginDependency + 400.IBPluginDependency + 401.IBPluginDependency + 402.IBPluginDependency + 403.IBPluginDependency + 404.IBPluginDependency + 405.IBPluginDependency + 406.IBPluginDependency + 407.IBPluginDependency + 408.IBPluginDependency + 409.IBPluginDependency + 410.IBPluginDependency + 411.IBPluginDependency + 412.IBPluginDependency + 413.IBPluginDependency + 414.IBPluginDependency + 415.IBPluginDependency + 416.IBPluginDependency + 417.IBPluginDependency + 418.IBPluginDependency + 419.IBPluginDependency + 450.IBPluginDependency + 451.IBEditorWindowLastContentRect + 451.IBPluginDependency + 452.IBPluginDependency + 453.IBPluginDependency + 454.IBPluginDependency + 457.IBPluginDependency + 459.IBPluginDependency + 460.IBPluginDependency + 462.IBPluginDependency + 465.IBPluginDependency + 466.IBPluginDependency + 485.IBPluginDependency + 490.IBPluginDependency + 491.IBEditorWindowLastContentRect + 491.IBPluginDependency + 492.IBPluginDependency + 496.IBPluginDependency + 497.IBEditorWindowLastContentRect + 497.IBPluginDependency + 498.IBPluginDependency + 499.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 500.IBPluginDependency + 501.IBPluginDependency + 502.IBPluginDependency + 503.IBPluginDependency + 504.IBPluginDependency + 505.IBPluginDependency + 506.IBPluginDependency + 507.IBPluginDependency + 508.IBEditorWindowLastContentRect + 508.IBPluginDependency + 509.IBPluginDependency + 510.IBPluginDependency + 511.IBPluginDependency + 512.IBPluginDependency + 513.IBPluginDependency + 514.IBPluginDependency + 515.IBPluginDependency + 516.IBPluginDependency + 517.IBPluginDependency + 533.IBPluginDependency + 534.IBPluginDependency + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBEditorWindowLastContentRect + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{522, 812}, {146, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{753, 187}, {275, 113}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {275, 83}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{547, 180}, {254, 283}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{187, 434}, {243, 243}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {167, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{753, 217}, {238, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {241, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{654, 239}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{380, 394}, {433, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{6, 978}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + {{604, 269}, {231, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + {{475, 832}, {234, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{746, 287}, {220, 133}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {215, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{1054, 339}, {480, 360}} + com.apple.InterfaceBuilder.CocoaPlugin + {{1054, 339}, {480, 360}} + + {{33, 99}, {480, 360}} + {3.40282e+38, 3.40282e+38} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{591, 420}, {83, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{523, 2}, {178, 283}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{753, 197}, {170, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{725, 289}, {246, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{674, 260}, {204, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{878, 180}, {164, 173}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + {{286, 129}, {275, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{452, 109}, {196, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{145, 474}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + + YES + + + + + YES + + + YES + + + + 535 + + + + YES + + LetsMoveAppDelegate + NSObject + + window + NSWindow + + + IBProjectSource + LetsMoveAppDelegate.h + + + + + YES + + NSActionCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSActionCell.h + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSBrowser + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSBrowser.h + + + + NSCell + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSCell.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSDocument + NSObject + + YES + + YES + printDocument: + revertDocumentToSaved: + runPageLayout: + saveDocument: + saveDocumentAs: + saveDocumentTo: + + + YES + id + id + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocument.h + + + + NSDocument + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentScripting.h + + + + NSDocumentController + NSObject + + YES + + YES + clearRecentDocuments: + newDocument: + openDocument: + saveAllDocuments: + + + YES + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentController.h + + + + NSFontManager + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSMatrix + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSMatrix.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMovieView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSMovieView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSTableView + NSControl + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSTextField + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSTextField.h + + + + NSTextFieldCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSTextFieldCell.h + + + + NSTextView + NSText + + IBFrameworkSource + AppKit.framework/Headers/NSTextView.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + + + 0 + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../LetsMove.xcodeproj + 3 + + diff --git a/Dependencies/LetsMove/LetsMove-Info.plist b/Dependencies/LetsMove/LetsMove-Info.plist new file mode 100644 index 00000000..a07afc7d --- /dev/null +++ b/Dependencies/LetsMove/LetsMove-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.25 + CFBundleVersion + 125 + NSHumanReadableCopyright + Copyright © 2009–2020 Karelia Software LLC + + diff --git a/Dependencies/LetsMove/LetsMove-Test-Info.plist b/Dependencies/LetsMove/LetsMove-Test-Info.plist new file mode 100644 index 00000000..ba0e84af --- /dev/null +++ b/Dependencies/LetsMove/LetsMove-Test-Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Dependencies/LetsMove/LetsMove.h b/Dependencies/LetsMove/LetsMove.h new file mode 100644 index 00000000..e4c6d772 --- /dev/null +++ b/Dependencies/LetsMove/LetsMove.h @@ -0,0 +1,10 @@ +// +// LetsMove.h +// LetsMove +// +// Created by Matt Prowse on 14/05/2016. +// +// The contents of this file are dedicated to the public domain. + +#import +#import diff --git a/Dependencies/LetsMove/LetsMove.modulemap b/Dependencies/LetsMove/LetsMove.modulemap new file mode 100644 index 00000000..c0837b12 --- /dev/null +++ b/Dependencies/LetsMove/LetsMove.modulemap @@ -0,0 +1,6 @@ +framework module LetsMove { + umbrella header "LetsMove.h" + + export * + module * { export * } +} diff --git a/Dependencies/LetsMove/LetsMove.podspec b/Dependencies/LetsMove/LetsMove.podspec new file mode 100644 index 00000000..9db006c0 --- /dev/null +++ b/Dependencies/LetsMove/LetsMove.podspec @@ -0,0 +1,23 @@ +Pod::Spec.new do |s| + s.name = "LetsMove" + s.version = "1.25" + s.summary = "Moves a running Mac application to the /Applications directory." + s.homepage = "https://github.com/potionfactory/LetsMove/" + s.license = 'Public Domain' + s.author = { "Andy Kim" => "andy@karelia.com" } + + s.platform = :osx, '10.6' + + s.source = { + :git => "https://github.com/potionfactory/LetsMove.git", + :tag => "v1.25" + } + + s.source_files = '*.{h,m}' + s.exclude_files = 'main.m', 'LetsMoveAppDelegate.{h,m}' + s.public_header_files = 'PFMoveApplication.h' + + s.resources = '*.lproj' + s.requires_arc = false +end + diff --git a/Dependencies/LetsMove/LetsMove.xcodeproj/project.pbxproj b/Dependencies/LetsMove/LetsMove.xcodeproj/project.pbxproj new file mode 100644 index 00000000..b595d9e0 --- /dev/null +++ b/Dependencies/LetsMove/LetsMove.xcodeproj/project.pbxproj @@ -0,0 +1,635 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 256AC3DA0F4B6AC300CF3369 /* LetsMoveAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* LetsMoveAppDelegate.m */; }; + 50223BEB1E1D90F800619DEA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F146F82F1CE7097500233A9B /* Cocoa.framework */; }; + 50223BEC1E1D90FA00619DEA /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F146F8311CE7097800233A9B /* Security.framework */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + A120E7F810630B160045BE3F /* PFMoveApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = A120E7F710630B160045BE3F /* PFMoveApplication.m */; }; + A17CADEB192F7E14008A209F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A17CADE5192F7E14008A209F /* InfoPlist.strings */; }; + A17CADEC192F7E14008A209F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A17CADE7192F7E14008A209F /* MainMenu.xib */; }; + A17CADED192F7E14008A209F /* MoveApplication.strings in Resources */ = {isa = PBXBuildFile; fileRef = A17CADE9192F7E14008A209F /* MoveApplication.strings */; }; + F146F8301CE7097500233A9B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F146F82F1CE7097500233A9B /* Cocoa.framework */; }; + F146F8321CE7097800233A9B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F146F8311CE7097800233A9B /* Security.framework */; }; + F146F8331CE7124100233A9B /* MoveApplication.strings in Resources */ = {isa = PBXBuildFile; fileRef = A17CADE9192F7E14008A209F /* MoveApplication.strings */; }; + F146F8341CE7125900233A9B /* PFMoveApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = A120E7F710630B160045BE3F /* PFMoveApplication.m */; }; + F146F8391CE7167F00233A9B /* PFMoveApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = A120E7F610630B160045BE3F /* PFMoveApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F146F83C1CE71DAF00233A9B /* LetsMove.h in Headers */ = {isa = PBXBuildFile; fileRef = F146F83B1CE71D9700233A9B /* LetsMove.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 01F3187C242381EF007E0E18 /* vi-VN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "vi-VN"; path = "vi-VN.lproj/MoveApplication.strings"; sourceTree = ""; }; + 256AC3D80F4B6AC300CF3369 /* LetsMoveAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LetsMoveAppDelegate.h; sourceTree = ""; }; + 256AC3D90F4B6AC300CF3369 /* LetsMoveAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LetsMoveAppDelegate.m; sourceTree = ""; }; + 256AC3F00F4B6AF500CF3369 /* LetsMove_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LetsMove_Prefix.pch; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* LetsMove-Test-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "LetsMove-Test-Info.plist"; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* LetsMove Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "LetsMove Test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + A120E7F610630B160045BE3F /* PFMoveApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFMoveApplication.h; sourceTree = ""; }; + A120E7F710630B160045BE3F /* PFMoveApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFMoveApplication.m; sourceTree = ""; }; + A1339024192F8183009ABCE2 /* cs */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/MoveApplication.strings; sourceTree = ""; }; + A178554A24B74D830036CB0A /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/MoveApplication.strings; sourceTree = ""; }; + A17CADE6192F7E14008A209F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A17CADE8192F7E14008A209F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; + A17CADEA192F7E14008A209F /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MoveApplication.strings; sourceTree = ""; }; + A17ED68F1A00BA8B0032BA5B /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C1192F81C200F1CCA3 /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C2192F81D400F1CCA3 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C3192F81DD00F1CCA3 /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C4192F81E200F1CCA3 /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C5192F81E700F1CCA3 /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C6192F81ED00F1CCA3 /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C7192F81F100F1CCA3 /* ko */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C8192F81F700F1CCA3 /* nb */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38C9192F81FC00F1CCA3 /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38CA192F820200F1CCA3 /* pt_BR */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = pt_BR; path = pt_BR.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38CB192F820900F1CCA3 /* pt */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38CC192F820E00F1CCA3 /* ru */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38CD192F821300F1CCA3 /* zh_CN */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_CN; path = zh_CN.lproj/MoveApplication.strings; sourceTree = ""; }; + A19B38CD192F821800F1CCA3 /* zh_TW */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_TW; path = zh_TW.lproj/MoveApplication.strings; sourceTree = ""; }; + A1A4C5A8106760B100AF3142 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.md; sourceTree = ""; }; + B2A84660197EAB71008A4ACA /* hu */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/MoveApplication.strings; sourceTree = ""; }; + B2A84661197EAB79008A4ACA /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/MoveApplication.strings; sourceTree = ""; }; + B2A84662197EAB85008A4ACA /* sr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/MoveApplication.strings; sourceTree = ""; }; + B5B5D108198940DF0074DBCE /* pl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MoveApplication.strings; sourceTree = ""; }; + C3A14200EF754221ACE32119 /* mk */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/MoveApplication.strings; sourceTree = ""; }; + D2CC8CA81B90F10700F763DF /* sk */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/MoveApplication.strings; sourceTree = ""; }; + F146F8271CE7094400233A9B /* LetsMove.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LetsMove.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F146F82F1CE7097500233A9B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + F146F8311CE7097800233A9B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + F146F8381CE7164D00233A9B /* LetsMove.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = LetsMove.modulemap; sourceTree = ""; }; + F146F83A1CE71D8A00233A9B /* LetsMove-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LetsMove-Info.plist"; sourceTree = ""; }; + F146F83B1CE71D9700233A9B /* LetsMove.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LetsMove.h; sourceTree = ""; }; + F7846CE41A9F83E100BFAF88 /* ca */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/MoveApplication.strings; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 50223BEC1E1D90FA00619DEA /* Security.framework in Frameworks */, + 50223BEB1E1D90F800619DEA /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F146F8231CE7094400233A9B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F146F8321CE7097800233A9B /* Security.framework in Frameworks */, + F146F8301CE7097500233A9B /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + A120E7F610630B160045BE3F /* PFMoveApplication.h */, + A120E7F710630B160045BE3F /* PFMoveApplication.m */, + 256AC3D80F4B6AC300CF3369 /* LetsMoveAppDelegate.h */, + 256AC3D90F4B6AC300CF3369 /* LetsMoveAppDelegate.m */, + ); + name = Classes; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* LetsMove Test.app */, + F146F8271CE7094400233A9B /* LetsMove.framework */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* LetsMove */ = { + isa = PBXGroup; + children = ( + A1A4C5A8106760B100AF3142 /* README.md */, + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = LetsMove; + sourceTree = ""; + usesTabs = 1; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + F146F83B1CE71D9700233A9B /* LetsMove.h */, + 256AC3F00F4B6AF500CF3369 /* LetsMove_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + F146F83A1CE71D8A00233A9B /* LetsMove-Info.plist */, + 8D1107310486CEB800E47090 /* LetsMove-Test-Info.plist */, + F146F8381CE7164D00233A9B /* LetsMove.modulemap */, + A17CADE5192F7E14008A209F /* InfoPlist.strings */, + A17CADE7192F7E14008A209F /* MainMenu.xib */, + A17CADE9192F7E14008A209F /* MoveApplication.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + F146F8311CE7097800233A9B /* Security.framework */, + F146F82F1CE7097500233A9B /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + F146F8241CE7094400233A9B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F146F83C1CE71DAF00233A9B /* LetsMove.h in Headers */, + F146F8391CE7167F00233A9B /* PFMoveApplication.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* LetsMove Test */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "LetsMove Test" */; + buildPhases = ( + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + 8D1107290486CEB800E47090 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "LetsMove Test"; + productInstallPath = "$(HOME)/Applications"; + productName = LetsMove; + productReference = 8D1107320486CEB800E47090 /* LetsMove Test.app */; + productType = "com.apple.product-type.application"; + }; + F146F8261CE7094400233A9B /* LetsMove */ = { + isa = PBXNativeTarget; + buildConfigurationList = F146F82C1CE7094400233A9B /* Build configuration list for PBXNativeTarget "LetsMove" */; + buildPhases = ( + F146F8221CE7094400233A9B /* Sources */, + F146F8231CE7094400233A9B /* Frameworks */, + F146F8241CE7094400233A9B /* Headers */, + F146F8251CE7094400233A9B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LetsMove; + productName = LetsMove; + productReference = F146F8271CE7094400233A9B /* LetsMove.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1150; + TargetAttributes = { + F146F8261CE7094400233A9B = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LetsMove" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 1; + knownRegions = ( + ko, + nb, + cs, + en, + da, + de, + es, + fr, + it, + ja, + nl, + pt_BR, + pt, + ru, + zh_CN, + zh_TW, + pl, + hu, + tr, + sr, + sv, + ca, + sk, + mk, + "vi-VN", + el, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* LetsMove */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* LetsMove Test */, + F146F8261CE7094400233A9B /* LetsMove */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A17CADED192F7E14008A209F /* MoveApplication.strings in Resources */, + A17CADEB192F7E14008A209F /* InfoPlist.strings in Resources */, + A17CADEC192F7E14008A209F /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F146F8251CE7094400233A9B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F146F8331CE7124100233A9B /* MoveApplication.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + 256AC3DA0F4B6AC300CF3369 /* LetsMoveAppDelegate.m in Sources */, + A120E7F810630B160045BE3F /* PFMoveApplication.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F146F8221CE7094400233A9B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F146F8341CE7125900233A9B /* PFMoveApplication.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + A17CADE5192F7E14008A209F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A17CADE6192F7E14008A209F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + A17CADE7192F7E14008A209F /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + A17CADE8192F7E14008A209F /* en */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; + A17CADE9192F7E14008A209F /* MoveApplication.strings */ = { + isa = PBXVariantGroup; + children = ( + A17CADEA192F7E14008A209F /* en */, + A19B38CD192F821300F1CCA3 /* zh_CN */, + A19B38CD192F821800F1CCA3 /* zh_TW */, + A1339024192F8183009ABCE2 /* cs */, + A19B38C1192F81C200F1CCA3 /* da */, + A19B38C9192F81FC00F1CCA3 /* nl */, + A19B38C4192F81E200F1CCA3 /* fr */, + A19B38C2192F81D400F1CCA3 /* de */, + A19B38C5192F81E700F1CCA3 /* it */, + A19B38C6192F81ED00F1CCA3 /* ja */, + A19B38C7192F81F100F1CCA3 /* ko */, + A19B38C8192F81F700F1CCA3 /* nb */, + A19B38CB192F820900F1CCA3 /* pt */, + A19B38CA192F820200F1CCA3 /* pt_BR */, + A19B38CC192F820E00F1CCA3 /* ru */, + A19B38C3192F81DD00F1CCA3 /* es */, + B5B5D108198940DF0074DBCE /* pl */, + B2A84660197EAB71008A4ACA /* hu */, + B2A84661197EAB79008A4ACA /* tr */, + B2A84662197EAB85008A4ACA /* sr */, + C3A14200EF754221ACE32119 /* mk */, + A17ED68F1A00BA8B0032BA5B /* sv */, + F7846CE41A9F83E100BFAF88 /* ca */, + D2CC8CA81B90F10700F763DF /* sk */, + 01F3187C242381EF007E0E18 /* vi-VN */, + A178554A24B74D830036CB0A /* el */, + ); + name = MoveApplication.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = LetsMove_Prefix.pch; + INFOPLIST_FILE = "LetsMove-Test-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_BUNDLE_IDENTIFIER = "com.potionfactory.LetsMove-Test"; + PRODUCT_NAME = "LetsMove Test"; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = LetsMove_Prefix.pch; + INFOPLIST_FILE = "LetsMove-Test-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_BUNDLE_IDENTIFIER = "com.potionfactory.LetsMove-Test"; + PRODUCT_NAME = "LetsMove Test"; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=macosx10.4]" = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; + CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; + CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + "GCC_VERSION[sdk=macosx10.4][arch=*]" = 4.0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.6; + "MACOSX_DEPLOYMENT_TARGET[arch=x86_64]" = 10.5; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + "ARCHS[sdk=macosx10.4]" = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; + CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; + CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + "GCC_VERSION[sdk=macosx10.4][arch=*]" = 4.0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_PEDANTIC = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.6; + "MACOSX_DEPLOYMENT_TARGET[arch=x86_64]" = 10.5; + }; + name = Release; + }; + F146F82D1CE7094400233A9B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.20; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1.20; + DYLIB_CURRENT_VERSION = 1.20; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + INFOPLIST_FILE = "LetsMove-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = LetsMove.modulemap; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.potionfactory.LetsMove; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + F146F82E1CE7094400233A9B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_MODULES_AUTOLINK = NO; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.20; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1.20; + DYLIB_CURRENT_VERSION = 1.20; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_VERSION = A; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + INFOPLIST_FILE = "LetsMove-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = LetsMove.modulemap; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.potionfactory.LetsMove; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "LetsMove Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LetsMove" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F146F82C1CE7094400233A9B /* Build configuration list for PBXNativeTarget "LetsMove" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F146F82D1CE7094400233A9B /* Debug */, + F146F82E1CE7094400233A9B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Dependencies/LetsMove/LetsMove.xcodeproj/xcshareddata/xcschemes/LetsMove.xcscheme b/Dependencies/LetsMove/LetsMove.xcodeproj/xcshareddata/xcschemes/LetsMove.xcscheme new file mode 100644 index 00000000..9bd82a6e --- /dev/null +++ b/Dependencies/LetsMove/LetsMove.xcodeproj/xcshareddata/xcschemes/LetsMove.xcscheme @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dependencies/LetsMove/LetsMoveAppDelegate.h b/Dependencies/LetsMove/LetsMoveAppDelegate.h new file mode 100644 index 00000000..a4c05345 --- /dev/null +++ b/Dependencies/LetsMove/LetsMoveAppDelegate.h @@ -0,0 +1,23 @@ +// +// LetsMoveAppDelegate.h +// LetsMove +// +// Created by Andy Kim on 9/17/09. +// Copyright 2009 Potion Factory LLC. All rights reserved. +// + +#import + +#ifdef MAC_OS_X_VERSION_10_6 +#define APPLICATION_DELEGATE +#else +#define APPLICATION_DELEGATE +#endif + +@interface LetsMoveAppDelegate : NSObject APPLICATION_DELEGATE { + IBOutlet NSWindow *window; +} + +- (NSWindow *)window; + +@end diff --git a/Dependencies/LetsMove/LetsMoveAppDelegate.m b/Dependencies/LetsMove/LetsMoveAppDelegate.m new file mode 100644 index 00000000..ece4eeb8 --- /dev/null +++ b/Dependencies/LetsMove/LetsMoveAppDelegate.m @@ -0,0 +1,31 @@ +// +// LetsMoveAppDelegate.m +// LetsMove +// +// Created by Andy Kim on 9/17/09. +// Copyright 2009 Potion Factory LLC. All rights reserved. +// + +#import "LetsMoveAppDelegate.h" +#import "PFMoveApplication.h" + +@implementation LetsMoveAppDelegate + +- (NSWindow *)window { + return window; +} + +- (void)applicationWillFinishLaunching:(NSNotification *)aNotification { + // Offer to the move the Application if necessary. + // Note that if the user chooses to move the application, + // this call will never return. Therefore you can suppress + // any first run UI by putting it after this call. + NSLog(@"applicationWillFinishlaunching: %@", aNotification); + + PFMoveToApplicationsFolderIfNecessary(); + + [window center]; + [window makeKeyAndOrderFront:self]; +} + +@end diff --git a/Dependencies/LetsMove/LetsMove_Prefix.pch b/Dependencies/LetsMove/LetsMove_Prefix.pch new file mode 100644 index 00000000..733682fe --- /dev/null +++ b/Dependencies/LetsMove/LetsMove_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'LetsMove' target in the 'LetsMove' project +// + +#ifdef __OBJC__ + #import +#endif diff --git a/Dependencies/LetsMove/PFMoveApplication.h b/Dependencies/LetsMove/PFMoveApplication.h new file mode 100644 index 00000000..f5d0b12d --- /dev/null +++ b/Dependencies/LetsMove/PFMoveApplication.h @@ -0,0 +1,32 @@ +// +// PFMoveApplication.h, version 1.25 +// LetsMove +// +// Created by Andy Kim at Potion Factory LLC on 9/17/09 +// +// The contents of this file are dedicated to the public domain. + +#ifdef __cplusplus +extern "C" { +#endif + +#import + +/** + Moves the running application to ~/Applications or /Applications if the former does not exist. + After the move, it relaunches app from the new location. + DOES NOT work for sandboxed applications. + + Call from \c NSApplication's delegate method \c -applicationWillFinishLaunching: method. */ +void PFMoveToApplicationsFolderIfNecessary(void); + +/** + Check whether an app move is currently in progress. + Returns YES if LetsMove is currently in-progress trying to move the app to the Applications folder, or NO otherwise. + This can be used to work around a crash with apps that terminate after last window is closed. + See https://github.com/potionfactory/LetsMove/issues/64 for details. */ +BOOL PFMoveIsInProgress(void); + +#ifdef __cplusplus +} +#endif diff --git a/Dependencies/LetsMove/PFMoveApplication.m b/Dependencies/LetsMove/PFMoveApplication.m new file mode 100644 index 00000000..07975244 --- /dev/null +++ b/Dependencies/LetsMove/PFMoveApplication.m @@ -0,0 +1,563 @@ +// +// PFMoveApplication.m, version 1.25 +// LetsMove +// +// Created by Andy Kim at Potion Factory LLC on 9/17/09 +// +// The contents of this file are dedicated to the public domain. + +#import "PFMoveApplication.h" + +#import +#import +#import +#import + +@interface LetsMove : NSObject +@end + +@implementation LetsMove ++ (NSBundle *)bundle { + return [NSBundle bundleForClass:self]; +} +@end + +// Strings +// These are macros to be able to use custom i18n tools +#define _I10NS(nsstr) NSLocalizedStringFromTableInBundle(nsstr, @"MoveApplication", [LetsMove bundle], nil) +#define kStrMoveApplicationCouldNotMove _I10NS(@"Could not move to Applications folder") +#define kStrMoveApplicationQuestionTitle _I10NS(@"Move to Applications folder?") +#define kStrMoveApplicationQuestionTitleHome _I10NS(@"Move to Applications folder in your Home folder?") +#define kStrMoveApplicationQuestionMessage _I10NS(@"I can move myself to the Applications folder if you'd like.") +#define kStrMoveApplicationButtonMove _I10NS(@"Move to Applications Folder") +#define kStrMoveApplicationButtonDoNotMove _I10NS(@"Do Not Move") +#define kStrMoveApplicationQuestionInfoWillRequirePasswd _I10NS(@"Note that this will require an administrator password.") +#define kStrMoveApplicationQuestionInfoInDownloadsFolder _I10NS(@"This will keep your Downloads folder uncluttered.") + +// Needs to be defined for compiling under 10.5 SDK +#ifndef NSAppKitVersionNumber10_5 + #define NSAppKitVersionNumber10_5 949 +#endif + +// By default, we use a small control/font for the suppression button. +// If you prefer to use the system default (to match your other alerts), +// set this to 0. +#define PFUseSmallAlertSuppressCheckbox 1 + + +static NSString *AlertSuppressKey = @"moveToApplicationsFolderAlertSuppress"; +static BOOL MoveInProgress = NO; + +// Helper functions +static NSString *PreferredInstallLocation(BOOL *isUserDirectory); +static BOOL IsInApplicationsFolder(NSString *path); +static BOOL IsInDownloadsFolder(NSString *path); +static BOOL IsApplicationAtPathRunning(NSString *path); +static BOOL IsApplicationAtPathNested(NSString *path); +static NSString *ContainingDiskImageDevice(NSString *path); +static BOOL Trash(NSString *path); +static BOOL DeleteOrTrash(NSString *path); +static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled); +static BOOL CopyBundle(NSString *srcPath, NSString *dstPath); +static NSString *ShellQuotedString(NSString *string); +static void Relaunch(NSString *destinationPath); + +// Main worker function +void PFMoveToApplicationsFolderIfNecessary(void) { + + // Make sure to do our work on the main thread. + // Apparently Electron apps need this for things to work properly. + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + PFMoveToApplicationsFolderIfNecessary(); + }); + return; + } + + // Skip if user suppressed the alert before + if ([[NSUserDefaults standardUserDefaults] boolForKey:AlertSuppressKey]) return; + + // Path of the bundle + NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; + + // Check if the bundle is embedded in another application + BOOL isNestedApplication = IsApplicationAtPathNested(bundlePath); + + // Skip if the application is already in some Applications folder, + // unless it's inside another app's bundle. + if (IsInApplicationsFolder(bundlePath) && !isNestedApplication) return; + + // OK, looks like we'll need to do a move - set the status variable appropriately + MoveInProgress = YES; + + // File Manager + NSFileManager *fm = [NSFileManager defaultManager]; + + // Are we on a disk image? + NSString *diskImageDevice = ContainingDiskImageDevice(bundlePath); + + // Since we are good to go, get the preferred installation directory. + BOOL installToUserApplications = NO; + NSString *applicationsDirectory = PreferredInstallLocation(&installToUserApplications); + NSString *bundleName = [bundlePath lastPathComponent]; + NSString *destinationPath = [applicationsDirectory stringByAppendingPathComponent:bundleName]; + + // Check if we need admin password to write to the Applications directory + BOOL needAuthorization = ([fm isWritableFileAtPath:applicationsDirectory] == NO); + + // Check if the destination bundle is already there but not writable + needAuthorization |= ([fm fileExistsAtPath:destinationPath] && ![fm isWritableFileAtPath:destinationPath]); + + // Setup the alert + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + { + NSString *informativeText = nil; + + [alert setMessageText:(installToUserApplications ? kStrMoveApplicationQuestionTitleHome : kStrMoveApplicationQuestionTitle)]; + + informativeText = kStrMoveApplicationQuestionMessage; + + if (needAuthorization) { + informativeText = [informativeText stringByAppendingString:@" "]; + informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoWillRequirePasswd]; + } + else if (IsInDownloadsFolder(bundlePath)) { + // Don't mention this stuff if we need authentication. The informative text is long enough as it is in that case. + informativeText = [informativeText stringByAppendingString:@" "]; + informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoInDownloadsFolder]; + } + + [alert setInformativeText:informativeText]; + + // Add accept button + [alert addButtonWithTitle:kStrMoveApplicationButtonMove]; + + // Add deny button + NSButton *cancelButton = [alert addButtonWithTitle:kStrMoveApplicationButtonDoNotMove]; + [cancelButton setKeyEquivalent:[NSString stringWithFormat:@"%C", 0x1b]]; // Escape key + + // Setup suppression button + [alert setShowsSuppressionButton:YES]; + + if (PFUseSmallAlertSuppressCheckbox) { + NSCell *cell = [[alert suppressionButton] cell]; + [cell setControlSize:NSSmallControlSize]; + [cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + } + } + + // Activate app -- work-around for focus issues related to "scary file from internet" OS dialog. + if (![NSApp isActive]) { + [NSApp activateIgnoringOtherApps:YES]; + } + + if ([alert runModal] == NSAlertFirstButtonReturn) { + NSLog(@"INFO -- Moving myself to the Applications folder"); + + // Move + if (needAuthorization) { + BOOL authorizationCanceled; + + if (!AuthorizedInstall(bundlePath, destinationPath, &authorizationCanceled)) { + if (authorizationCanceled) { + NSLog(@"INFO -- Not moving because user canceled authorization"); + MoveInProgress = NO; + return; + } + else { + NSLog(@"ERROR -- Could not copy myself to /Applications with authorization"); + goto fail; + } + } + } + else { + // If a copy already exists in the Applications folder, put it in the Trash + if ([fm fileExistsAtPath:destinationPath]) { + // But first, make sure that it's not running + if (IsApplicationAtPathRunning(destinationPath)) { + // Give the running app focus and terminate myself + NSLog(@"INFO -- Switching to an already running version"); + [[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:[NSArray arrayWithObject:destinationPath]] waitUntilExit]; + MoveInProgress = NO; + exit(0); + } + else { + if (!Trash([applicationsDirectory stringByAppendingPathComponent:bundleName])) + goto fail; + } + } + + if (!CopyBundle(bundlePath, destinationPath)) { + NSLog(@"ERROR -- Could not copy myself to %@", destinationPath); + goto fail; + } + } + + // Trash the original app. It's okay if this fails. + // NOTE: This final delete does not work if the source bundle is in a network mounted volume. + // Calling rm or file manager's delete method doesn't work either. It's unlikely to happen + // but it'd be great if someone could fix this. + if (!isNestedApplication && diskImageDevice == nil && !DeleteOrTrash(bundlePath)) { + NSLog(@"WARNING -- Could not delete application after moving it to Applications folder"); + } + + // Relaunch. + Relaunch(destinationPath); + + // Launched from within a disk image? -- unmount (if no files are open after 5 seconds, + // otherwise leave it mounted). + if (diskImageDevice && !isNestedApplication) { + NSString *script = [NSString stringWithFormat:@"(/bin/sleep 5 && /usr/bin/hdiutil detach %@) &", ShellQuotedString(diskImageDevice)]; + [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]]; + } + + MoveInProgress = NO; + exit(0); + } + // Save the alert suppress preference if checked + else if ([[alert suppressionButton] state] == NSOnState) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:AlertSuppressKey]; + } + + MoveInProgress = NO; + return; + +fail: + { + // Show failure message + alert = [[[NSAlert alloc] init] autorelease]; + [alert setMessageText:kStrMoveApplicationCouldNotMove]; + [alert runModal]; + MoveInProgress = NO; + } +} + +BOOL PFMoveIsInProgress() { + return MoveInProgress; +} + +#pragma mark - +#pragma mark Helper Functions + +static NSString *PreferredInstallLocation(BOOL *isUserDirectory) { + // Return the preferred install location. + // Assume that if the user has a ~/Applications folder, they'd prefer their + // applications to go there. + + NSFileManager *fm = [NSFileManager defaultManager]; + + NSArray *userApplicationsDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES); + + if ([userApplicationsDirs count] > 0) { + NSString *userApplicationsDir = [userApplicationsDirs objectAtIndex:0]; + BOOL isDirectory; + + if ([fm fileExistsAtPath:userApplicationsDir isDirectory:&isDirectory] && isDirectory) { + // User Applications directory exists. Get the directory contents. + NSArray *contents = [fm contentsOfDirectoryAtPath:userApplicationsDir error:NULL]; + + // Check if there is at least one ".app" inside the directory. + for (NSString *contentsPath in contents) { + if ([[contentsPath pathExtension] isEqualToString:@"app"]) { + if (isUserDirectory) *isUserDirectory = YES; + return [userApplicationsDir stringByResolvingSymlinksInPath]; + } + } + } + } + + // No user Applications directory in use. Return the machine local Applications directory + if (isUserDirectory) *isUserDirectory = NO; + + return [[NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSLocalDomainMask, YES) lastObject] stringByResolvingSymlinksInPath]; +} + +static BOOL IsInApplicationsFolder(NSString *path) { + // Check all the normal Application directories + NSArray *applicationDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSAllDomainsMask, YES); + for (NSString *appDir in applicationDirs) { + if ([path hasPrefix:appDir]) return YES; + } + + // Also, handle the case that the user has some other Application directory (perhaps on a separate data partition). + if ([[path pathComponents] containsObject:@"Applications"]) return YES; + + return NO; +} + +static BOOL IsInDownloadsFolder(NSString *path) { + NSArray *downloadDirs = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSAllDomainsMask, YES); + for (NSString *downloadsDirPath in downloadDirs) { + if ([path hasPrefix:downloadsDirPath]) return YES; + } + + return NO; +} + +static BOOL IsApplicationAtPathRunning(NSString *bundlePath) { + bundlePath = [bundlePath stringByStandardizingPath]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + // Use the new API on 10.6 or higher to determine if the app is already running + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) { + for (NSRunningApplication *runningApplication in [[NSWorkspace sharedWorkspace] runningApplications]) { + NSString *runningAppBundlePath = [[[runningApplication bundleURL] path] stringByStandardizingPath]; + if ([runningAppBundlePath isEqualToString:bundlePath]) { + return YES; + } + } + return NO; + } +#endif + // Use the shell to determine if the app is already running on systems 10.5 or lower + NSString *script = [NSString stringWithFormat:@"/bin/ps ax -o comm | /usr/bin/grep %@/ | /usr/bin/grep -v grep >/dev/null", ShellQuotedString(bundlePath)]; + NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]]; + [task waitUntilExit]; + + // If the task terminated with status 0, it means that the final grep produced 1 or more lines of output. + // Which means that the app is already running + return [task terminationStatus] == 0; +} + +static BOOL IsApplicationAtPathNested(NSString *path) { + NSString *containingPath = [path stringByDeletingLastPathComponent]; + + NSArray *components = [containingPath pathComponents]; + for (NSString *component in components) { + if ([[component pathExtension] isEqualToString:@"app"]) { + return YES; + } + } + + return NO; +} + +static NSString *ContainingDiskImageDevice(NSString *path) { + NSString *containingPath = [path stringByDeletingLastPathComponent]; + + struct statfs fs; + if (statfs([containingPath fileSystemRepresentation], &fs) || (fs.f_flags & MNT_ROOTFS)) + return nil; + + NSString *device = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:fs.f_mntfromname length:strlen(fs.f_mntfromname)]; + + NSTask *hdiutil = [[[NSTask alloc] init] autorelease]; + [hdiutil setLaunchPath:@"/usr/bin/hdiutil"]; + [hdiutil setArguments:[NSArray arrayWithObjects:@"info", @"-plist", nil]]; + [hdiutil setStandardOutput:[NSPipe pipe]]; + [hdiutil launch]; + [hdiutil waitUntilExit]; + + NSData *data = [[[hdiutil standardOutput] fileHandleForReading] readDataToEndOfFile]; + NSDictionary *info = nil; +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) { + info = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:NULL]; + } + else { +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 + info = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:NULL]; +#endif +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + } +#endif + + if (![info isKindOfClass:[NSDictionary class]]) return nil; + + NSArray *images = (NSArray *)[info objectForKey:@"images"]; + if (![images isKindOfClass:[NSArray class]]) return nil; + + for (NSDictionary *image in images) { + if (![image isKindOfClass:[NSDictionary class]]) return nil; + + id systemEntities = [image objectForKey:@"system-entities"]; + if (![systemEntities isKindOfClass:[NSArray class]]) return nil; + + for (NSDictionary *systemEntity in systemEntities) { + if (![systemEntity isKindOfClass:[NSDictionary class]]) return nil; + + NSString *devEntry = [systemEntity objectForKey:@"dev-entry"]; + if (![devEntry isKindOfClass:[NSString class]]) return nil; + + if ([devEntry isEqualToString:device]) + return device; + } + } + + return nil; +} + +static BOOL Trash(NSString *path) { + BOOL result = NO; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) { + result = [[NSFileManager defaultManager] trashItemAtURL:[NSURL fileURLWithPath:path] resultingItemURL:NULL error:NULL]; + } +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 + if (!result) { + result = [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation + source:[path stringByDeletingLastPathComponent] + destination:@"" + files:[NSArray arrayWithObject:[path lastPathComponent]] + tag:NULL]; + } +#endif + + // As a last resort try trashing with AppleScript. + // This allows us to trash the app in macOS Sierra even when the app is running inside + // an app translocation image. + if (!result) { + NSAppleScript *appleScript = [[[NSAppleScript alloc] initWithSource: + [NSString stringWithFormat:@"\ + set theFile to POSIX file \"%@\" \n\ + tell application \"Finder\" \n\ + move theFile to trash \n\ + end tell", path]] autorelease]; + NSDictionary *errorDict = nil; + NSAppleEventDescriptor *scriptResult = [appleScript executeAndReturnError:&errorDict]; + if (scriptResult == nil) { + NSLog(@"Trash AppleScript error: %@", errorDict); + } + result = (scriptResult != nil); + } + + if (!result) { + NSLog(@"ERROR -- Could not trash '%@'", path); + } + + return result; +} + +static BOOL DeleteOrTrash(NSString *path) { + NSError *error; + + if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) { + return YES; + } + else { + // Don't log warning if on Sierra and running inside App Translocation path + if ([path rangeOfString:@"/AppTranslocation/"].location == NSNotFound) + NSLog(@"WARNING -- Could not delete '%@': %@", path, [error localizedDescription]); + + return Trash(path); + } +} + +static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled) { + if (canceled) *canceled = NO; + + // Make sure that the destination path is an app bundle. We're essentially running 'sudo rm -rf' + // so we really don't want to fuck this up. + if (![[dstPath pathExtension] isEqualToString:@"app"]) return NO; + + // Do some more checks + if ([[dstPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO; + if ([[srcPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO; + + int pid, status; + AuthorizationRef myAuthorizationRef; + + // Get the authorization + OSStatus err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &myAuthorizationRef); + if (err != errAuthorizationSuccess) return NO; + + AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0}; + AuthorizationRights myRights = {1, &myItems}; + AuthorizationFlags myFlags = (AuthorizationFlags)(kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize); + + err = AuthorizationCopyRights(myAuthorizationRef, &myRights, NULL, myFlags, NULL); + if (err != errAuthorizationSuccess) { + if (err == errAuthorizationCanceled && canceled) + *canceled = YES; + goto fail; + } + + static OSStatus (*security_AuthorizationExecuteWithPrivileges)(AuthorizationRef authorization, const char *pathToTool, + AuthorizationFlags options, char * const *arguments, + FILE **communicationsPipe) = NULL; + if (!security_AuthorizationExecuteWithPrivileges) { + // On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want to still use it since there's no + // good alternative (without requiring code signing). We'll look up the function through dyld and fail + // if it is no longer accessible. If Apple removes the function entirely this will fail gracefully. If + // they keep the function and throw some sort of exception, this won't fail gracefully, but that's a + // risk we'll have to take for now. + security_AuthorizationExecuteWithPrivileges = (OSStatus (*)(AuthorizationRef, const char*, + AuthorizationFlags, char* const*, + FILE **)) dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges"); + } + if (!security_AuthorizationExecuteWithPrivileges) goto fail; + + // Delete the destination + { + char *args[] = {"-rf", (char *)[dstPath fileSystemRepresentation], NULL}; + err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/rm", kAuthorizationFlagDefaults, args, NULL); + if (err != errAuthorizationSuccess) goto fail; + + // Wait until it's done + pid = wait(&status); + if (pid == -1 || !WIFEXITED(status)) goto fail; // We don't care about exit status as the destination most likely does not exist + } + + // Copy + { + char *args[] = {"-pR", (char *)[srcPath fileSystemRepresentation], (char *)[dstPath fileSystemRepresentation], NULL}; + err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/cp", kAuthorizationFlagDefaults, args, NULL); + if (err != errAuthorizationSuccess) goto fail; + + // Wait until it's done + pid = wait(&status); + if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) goto fail; + } + + AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults); + return YES; + +fail: + AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults); + return NO; +} + +static BOOL CopyBundle(NSString *srcPath, NSString *dstPath) { + NSFileManager *fm = [NSFileManager defaultManager]; + NSError *error = nil; + + if ([fm copyItemAtPath:srcPath toPath:dstPath error:&error]) { + return YES; + } + else { + NSLog(@"ERROR -- Could not copy '%@' to '%@' (%@)", srcPath, dstPath, error); + return NO; + } +} + +static NSString *ShellQuotedString(NSString *string) { + return [NSString stringWithFormat:@"'%@'", [string stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]]; +} + +static void Relaunch(NSString *destinationPath) { + // The shell script waits until the original app process terminates. + // This is done so that the relaunched app opens as the front-most app. + int pid = [[NSProcessInfo processInfo] processIdentifier]; + + // Command run just before running open /final/path + NSString *preOpenCmd = @""; + + NSString *quotedDestinationPath = ShellQuotedString(destinationPath); + + // OS X >=10.5: + // Before we launch the new app, clear xattr:com.apple.quarantine to avoid + // duplicate "scary file from the internet" dialog. + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) { + // Add the -r flag on 10.6 + preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d -r com.apple.quarantine %@", quotedDestinationPath]; + } + else { + preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d com.apple.quarantine %@", quotedDestinationPath]; + } + + NSString *script = [NSString stringWithFormat:@"(while /bin/kill -0 %d >&/dev/null; do /bin/sleep 0.1; done; %@; /usr/bin/open %@) &", pid, preOpenCmd, quotedDestinationPath]; + + [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]]; +} diff --git a/Dependencies/LetsMove/README.md b/Dependencies/LetsMove/README.md new file mode 100644 index 00000000..b5a20b1f --- /dev/null +++ b/Dependencies/LetsMove/README.md @@ -0,0 +1,212 @@ +LetsMove +======== + +A sample project that demonstrates how to move a running Mac OS X application to the Applications folder. + +![Screenshot](http://i.imgur.com/euTRZiI.png) + + +Requirements +------------ +Builds and runs on Mac OS X 10.6 or higher. Does NOT support sandboxed applications. + + +Usage +----- + +Option 1: + +Build then embed LetsMove.framework into your app. + +Option 2: + +Copy the following files into your project: + +- PFMoveApplication.h +- PFMoveApplication.m + +If your project has ARC enabled, you'll want to disable ARC on the above files. You can do so by adding -fno-objc-arc compiler flag to your PFMoveApplication.m source file. See http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project/6658549#6658549 + +If your application is localized, also copy the 'MoveApplication.string' files into your project. + +Link your application against Security.framework. + +In your app delegate's "-[applicationWillFinishLaunching:]" method, call the PFMoveToApplicationsFolderIfNecessary function at the very top. + + +License +------- +Public domain + + + +Version History +--------------- + +* 1.25 + - Added Greek and Vietnamese localizations + - Update project for Xcode 11.5 + +* 1.24 + - Add PFMoveIsInProgress function + - Update project for Xcode 9.1 + +* 1.23 + - Fix localization not being used when built as a framework + - Fix backwards compatibility with OS X 10.9 and earlier being broken + - Make usable for Electron based apps or other apps that do not have access to the main thread dispatch queue + - Update Russian localization + +* 1.22 + - Fix not deleting or trashing itself after copying to /Applications in macOS Sierra + +* 1.21 + - Support for [Carthage](https://github.com/Carthage/Carthage) added + - Project now support OS X 10.6 and higher + +* 1.20 + - Support for applications bundled inside another application + - Brazilian Portuguese localization slightly updated + - Build warnings fixed + +* 1.19 + - Slovak localization added + +* 1.18 + - Catalan localization added + +* 1.17 + - Tranditional Chinese localization added. + +* 1.16 + - Deprecation warning that appears when minimum deployment target is set to OS X 10.10 taken care of + +* 1.15 + - Swedish localization added + +* 1.14 + - Hugarian, Serbian, and Turkish localizations added + - Macedonian localization added + +* 1.13 + - Polish localization added + +* 1.12 + - Use country code based .lproj directories + - Make it compile for projects that don't use precompiled headers to import AppKit.framework + - Minor adjustment to Dutch localization + - Warning fixes in example project + +* 1.11 + - Objective-C++ compatibility + +* 1.10 + - Fixed deprecation warnings that show up when building against the OS X 10.9 SDK. + +* 1.9 + - Removed OS X 10.4 support + - Properly detect if the running app is in a disk image + - Fixed a bug where if the app's name contained a quote, the app could not be moved + - After a successful move, delete the application instead of moving it to the Trash. + - Other fixes and improvements + +* 1.8 + - If the app is already there in the Applications folder but not writable, request authentication from user + - Added Korean localization + +* 1.7.2 + - Fixed an exception that could happen. + +* 1.7.1 + - Refactoring + +* 1.7 + - Only move to ~/Appilcations directory if an app is already in there. + +* 1.6.3 + - Function calls deprecated in 10.7 no longer cause compile time warnings. + - Added Simplified Chinese and European Portuguese localizations + +* 1.6.2 + - Garbage collection compatibility added + - Use a new method to check if an application is already running on Mac OS X 10.6 systems or higher + +* 1.6.1 + - Use exit(0) to terminate the app before relaunching instead of [NSApp terminate:]. We don't want applicationShouldTerminate or applicationWillTerminate NSApplication delegate methods to be called, possibly introducing side effects. + +* 1.6 + - Resolve any aliases when finding the Applications directory + +* 1.5.2 + - Cleaned up the code a bit. Almost functionally equivalent to 1.5.1. + +* 1.5.1 + - Fixed a bug with clearing the quarantine file attribute on Mac OS X 10.5 + +* 1.5 + - Don't prompt to move the application if it has "Applications" in its path somewhere + +* 1.4 + - Mac OS X 10.5 compatibility fixes + +* 1.3 + - Fixed a rare bug in the shell script that checks to see if the app is already running + - Clear quarantine flag after copying + - Compile time option to show normal sized alert supress checkbox button + - German, Danish, and Norwegian localizations added + +* 1.2 + - Copy application from disk image then unmount disk image + - Spanish, French, Dutch, and Russian localizations + +* 1.1 + - Prefers ~/Applications over /Applications if it exists + - Escape key pushes the "Do Not Move" button + +* 1.0 + - First release + + +Code Contributors: +------------- +* Andy Kim +* John Brayton +* Chad Sellers +* Kevin LaCoste +* Rasmus Andersson +* Timothy J. Wood +* Matt Gallagher +* Whitney Young +* Nick Moore +* Nicholas Riley +* Matt Prowse +* Maxim Ananov +* Charlie Stigler + + +Translators: +------------ +* Eita Hayashi (Japanese) +* Gleb M. Borisov, Maxim Ananov (Russian) +* Wouter Broekhof (Dutch) +* Rasmus Andersson / Spotify (French and Spanish) +* Markus Kirschner (German) +* Fredrik Nannestad (Danish) +* Georg Alexander Bøe (Norwegian) +* Marco Improda (Italian) +* Venj Chu (Simplified Chinese) +* Sérgio Miranda (European Portuguese) +* Victor Figueiredo and BR Lingo (Brazilian Portuguese) +* AppLingua (Korean) +* Czech X Team (Czech) +* Marek Telecki (Polish) +* Petar Vlahu (Macedonian) +* Václav Slavík (Hungarian, Serbian, and Turkish) +* Erik Vikström (Swedish) +* Inndy Lin (Traditional Chinese) +* aONe (Catalan) +* Marek Hrusovsky (Slovak) +* Sotirios Papathanasiou (Greek) +* Minh-Ton (Vietnamese) + +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) diff --git a/Dependencies/LetsMove/ca.lproj/MoveApplication.strings b/Dependencies/LetsMove/ca.lproj/MoveApplication.strings new file mode 100644 index 00000000..e0511209 Binary files /dev/null and b/Dependencies/LetsMove/ca.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/cs.lproj/MoveApplication.strings b/Dependencies/LetsMove/cs.lproj/MoveApplication.strings new file mode 100644 index 00000000..164e3816 Binary files /dev/null and b/Dependencies/LetsMove/cs.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/da.lproj/MoveApplication.strings b/Dependencies/LetsMove/da.lproj/MoveApplication.strings new file mode 100644 index 00000000..99aeb78e Binary files /dev/null and b/Dependencies/LetsMove/da.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/de.lproj/MoveApplication.strings b/Dependencies/LetsMove/de.lproj/MoveApplication.strings new file mode 100644 index 00000000..2163f8d7 Binary files /dev/null and b/Dependencies/LetsMove/de.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/el.lproj/MoveApplication.strings b/Dependencies/LetsMove/el.lproj/MoveApplication.strings new file mode 100644 index 00000000..f8e64d1c Binary files /dev/null and b/Dependencies/LetsMove/el.lproj/MoveApplication.strings differ diff --git a/Switch/en.lproj/InfoPlist.strings b/Dependencies/LetsMove/en.lproj/InfoPlist.strings similarity index 100% rename from Switch/en.lproj/InfoPlist.strings rename to Dependencies/LetsMove/en.lproj/InfoPlist.strings diff --git a/Dependencies/LetsMove/en.lproj/MainMenu.xib b/Dependencies/LetsMove/en.lproj/MainMenu.xib new file mode 100644 index 00000000..45b687e3 --- /dev/null +++ b/Dependencies/LetsMove/en.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dependencies/LetsMove/en.lproj/MoveApplication.strings b/Dependencies/LetsMove/en.lproj/MoveApplication.strings new file mode 100644 index 00000000..f5f7b1fc Binary files /dev/null and b/Dependencies/LetsMove/en.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/es.lproj/MoveApplication.strings b/Dependencies/LetsMove/es.lproj/MoveApplication.strings new file mode 100644 index 00000000..ffa298ed Binary files /dev/null and b/Dependencies/LetsMove/es.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/fr.lproj/MoveApplication.strings b/Dependencies/LetsMove/fr.lproj/MoveApplication.strings new file mode 100644 index 00000000..220bd6e6 Binary files /dev/null and b/Dependencies/LetsMove/fr.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/hu.lproj/MoveApplication.strings b/Dependencies/LetsMove/hu.lproj/MoveApplication.strings new file mode 100644 index 00000000..06512dc7 Binary files /dev/null and b/Dependencies/LetsMove/hu.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/it.lproj/MoveApplication.strings b/Dependencies/LetsMove/it.lproj/MoveApplication.strings new file mode 100644 index 00000000..5ca59b12 Binary files /dev/null and b/Dependencies/LetsMove/it.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/ja.lproj/MoveApplication.strings b/Dependencies/LetsMove/ja.lproj/MoveApplication.strings new file mode 100644 index 00000000..207ef907 Binary files /dev/null and b/Dependencies/LetsMove/ja.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/ko.lproj/MoveApplication.strings b/Dependencies/LetsMove/ko.lproj/MoveApplication.strings new file mode 100644 index 00000000..f38e08ec Binary files /dev/null and b/Dependencies/LetsMove/ko.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/main.m b/Dependencies/LetsMove/main.m new file mode 100644 index 00000000..2966a19c --- /dev/null +++ b/Dependencies/LetsMove/main.m @@ -0,0 +1,13 @@ +// +// main.m +// LetsMove +// +// Created by Andy Kim on 9/17/09. +// Copyright 2009 Potion Factory LLC. All rights reserved. +// + +#import + +int main(int argc, const char *argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Dependencies/LetsMove/mk.lproj/MoveApplication.strings b/Dependencies/LetsMove/mk.lproj/MoveApplication.strings new file mode 100644 index 00000000..54b9140c Binary files /dev/null and b/Dependencies/LetsMove/mk.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/nb.lproj/MoveApplication.strings b/Dependencies/LetsMove/nb.lproj/MoveApplication.strings new file mode 100644 index 00000000..035c4b46 Binary files /dev/null and b/Dependencies/LetsMove/nb.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/nl.lproj/MoveApplication.strings b/Dependencies/LetsMove/nl.lproj/MoveApplication.strings new file mode 100644 index 00000000..564b5638 Binary files /dev/null and b/Dependencies/LetsMove/nl.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/pl.lproj/MoveApplication.strings b/Dependencies/LetsMove/pl.lproj/MoveApplication.strings new file mode 100644 index 00000000..d47a9da1 Binary files /dev/null and b/Dependencies/LetsMove/pl.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/pt.lproj/MoveApplication.strings b/Dependencies/LetsMove/pt.lproj/MoveApplication.strings new file mode 100644 index 00000000..e16135f0 Binary files /dev/null and b/Dependencies/LetsMove/pt.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/pt_BR.lproj/MoveApplication.strings b/Dependencies/LetsMove/pt_BR.lproj/MoveApplication.strings new file mode 100644 index 00000000..0b37c2d9 Binary files /dev/null and b/Dependencies/LetsMove/pt_BR.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/ru.lproj/MoveApplication.strings b/Dependencies/LetsMove/ru.lproj/MoveApplication.strings new file mode 100644 index 00000000..daf245bd Binary files /dev/null and b/Dependencies/LetsMove/ru.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/sk.lproj/MoveApplication.strings b/Dependencies/LetsMove/sk.lproj/MoveApplication.strings new file mode 100644 index 00000000..b549b354 Binary files /dev/null and b/Dependencies/LetsMove/sk.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/sr.lproj/MoveApplication.strings b/Dependencies/LetsMove/sr.lproj/MoveApplication.strings new file mode 100644 index 00000000..91b01344 Binary files /dev/null and b/Dependencies/LetsMove/sr.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/sv.lproj/MoveApplication.strings b/Dependencies/LetsMove/sv.lproj/MoveApplication.strings new file mode 100644 index 00000000..466cdc5b Binary files /dev/null and b/Dependencies/LetsMove/sv.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/tr.lproj/MoveApplication.strings b/Dependencies/LetsMove/tr.lproj/MoveApplication.strings new file mode 100644 index 00000000..9cec22a3 Binary files /dev/null and b/Dependencies/LetsMove/tr.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/vi-VN.lproj/MoveApplication.strings b/Dependencies/LetsMove/vi-VN.lproj/MoveApplication.strings new file mode 100644 index 00000000..f9946577 Binary files /dev/null and b/Dependencies/LetsMove/vi-VN.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/zh_CN.lproj/MoveApplication.strings b/Dependencies/LetsMove/zh_CN.lproj/MoveApplication.strings new file mode 100644 index 00000000..99a1ec67 Binary files /dev/null and b/Dependencies/LetsMove/zh_CN.lproj/MoveApplication.strings differ diff --git a/Dependencies/LetsMove/zh_TW.lproj/MoveApplication.strings b/Dependencies/LetsMove/zh_TW.lproj/MoveApplication.strings new file mode 100644 index 00000000..1abfb4ab Binary files /dev/null and b/Dependencies/LetsMove/zh_TW.lproj/MoveApplication.strings differ diff --git a/Dependencies/README.md b/Dependencies/README.md new file mode 100644 index 00000000..bbc793f6 --- /dev/null +++ b/Dependencies/README.md @@ -0,0 +1,46 @@ +# Dependency Management + +After a string of bad experiences over the years with: + +* `git submodule` +* repositories going away +* sophisticated build tools + +I have settled on the dumbest solution I can think of to solve these problems: + +All of Switch's dependencies are checked into the repository. + +## The Goal + +People using this project should be able to get up and running with a `git clone`, `open *.xcworkspace`, and `⌘R`. + +## `git` Dependencies + +Switch includes source checkouts of: + +* [Defaults](https://github.com/sindresorhus/Defaults), Swifty and modern UserDefaults +* [Haxcessibility](https://github.com/numist/Haxcessibility), a use case–driven remote control framework for Mac apps by Mac apps +* [LetsMove](https://github.com/potionfactory/LetsMove/), encourages users to move the app to `/Applications` + +### Adding a new dependency + +1. Create a file with the name of the project directory suffixed with `.giturl` that contains a URI suitable for passing to `git clone` +1. Run `update_dependencies.sh` + +### Updating a dependency + +1. Run `update_dependencies.sh` + +### Determining dependency version + +Dependencies are stored without `.git` directories, so `update_dependencies.sh` records the cloned branch and sha to `$PROJECT.gitcheckout`. + +## Other dependencies + +Switch includes manually-managed releases of: + +* [Sparkle](https://github.com/sparkle-project/Sparkle), a software update framework for macOS + +### Convention + +Non-`git` dependencies are checked into the appropriate subdirectory of `Dependencies/` along with a `README.md`/`VERSION` and `LICENSE` to explain their provenance. diff --git a/Dependencies/Sparkle/CHANGELOG b/Dependencies/Sparkle/CHANGELOG new file mode 100644 index 00000000..1ed70192 --- /dev/null +++ b/Dependencies/Sparkle/CHANGELOG @@ -0,0 +1,624 @@ +# 1.24.0-alpha + + * Fixed error about "about:blank" release notes (Louis Pontoise) + * Use the SHA-256 hash of the archive as the cache path (Nate Weaver) + * Don't return an optional from the FileHandle method (Nate Weaver) + * Added URL+Hashing (Nate Weaver) + * Update SUUpdateAlert.xib (DanielFirlej) + * Added help command line option (Dominik H) + * Added function that parses all command line options (Dominik H) + * Command line option to provide a download url prefix is now parsed and set on each archive item (Dominik H) + +# 1.23.0 + + * Support generating appcast with localizations (#1499) (Alik Vovkotrub) + * Support versions with git commit SHA (#1504) (Alec Larson) + * Hide "Skip..." and "Remind..." buttons when they're not relevant (#1480) (Kenneth Johnsen) + * Preserve Finder tags while updating apps (#1512) (CoreCode) + * Read-only update alert dialog formatting improvements (#1515) (Quinn Taylor) + * Check if `SUBundleName` is set before normalizing (Jake Fenton) + * `NSInteger` cast warning on Xcode 11 (Marga Keuvelaar) + * Correct appcast file extension (Tom Vos) + * Update Sparkle.strings (Emir Sarı) + * Fix spelling (#1508) (Frank Chiarulli Jr) + +# 1.22.0 + + * Enabled "Hardened Runtime" build option for Apple's notarization requirement + * Add delegate methods to suppress update alerts (George Nachman) + * Improved error when running from translocated location (Michael Buckley) + * Add phased rollout feature (#1381) (Fabian Jäger) + * Ignore non-standard permissions in delta updates instead of failing the build (Kornel Lesiński) + * Notify user when installed version is newer than the latest in the appcast (CoreCode) + * Reset timers after computer sleep (CoreCode) + * Block-based alternatives to `NSInvocation`-based delegate methods (Fabian Jäger) + * Add delegate `userDidSkipThisVersion` (BobZombie, Leo Natan, bono yusuke) + * Pass item to updaterShouldShowUpdateAlertForScheduledUpdate delegate method (George Nachman) + * Support providing private key as argument in `generate_appcast` (Yakuhzi, marchyang) + * Separate the ed25519 sources into a new static library (Tony Arnold) + * Disambiguate signing error messages (Nate Weaver) + * Use `XMLNode.Options.nodePrettyPrint` in `generate_appcast` instead of trying to add whitespace manually (fumoboy007) + * Annotate SUHost for nullability (Michael Buckley) + * Use SUAVAILABLE macro (Christiaan Hofman) + * Fix warnings when using modules (nivekkagicom) + * Correction of Czech localization inconsistency (#1403) (vojtakonarik) + * BR locale fix (BR Lingo) + * Update Japanese localization (fujisoft) + * French Sparkle.strings (Jean-Baptiste) + +# 1.21.3 + + * Losslessly reduced the size of PNG (Barijaona Ramaholimihaso) + * Catch exceptions from subcommands (Julian Mayer) + * `generate_appcast` can sign any bundles instead of just apps (Nate Weaver) + * Check that effectiveAppearance is being observed before calling removeObserver (Pierluigi) + +# 1.21.2 + + * Allow EdDSA for delta updates, too (Kornel) + * Warning fixes (Brian Bergstrand) + * Improvements to release notes view context menu and dark mode (Bi11) + +# 1.21.0 + + * Added support EdDSA (ed25519) signatures (Kornel) + * DSA signatures are considered outdated, and Apple's `Security.framework` only supports weaker SHA-1-based DSA. + * Both old DSA and new EdDSA are still supported (and one app can use both), but new applications should use EdDSA only, and we recommend migrating away from DSA signatures. + * `generate_keys` is now a Swift tool that stores EdDSA private keys in the Keychain + * Existing apps can continue using their old DSA keys, but we've dropped support for generation of old DSA keys + * `sign_update` is now a Swift tool that signs using EdDSA from private keys in the Keychain + * The old DSA-based signing script has been moved to `bin/old_dsa_scripts` + * The old DSA-based signing script has been fixed to work on pre-10.13 systems (Thomas Tempelmann) + * `generate_appcast` has been updated to support EdDSA signatures + * It can sign both DSA (if `dsa_priv.pem` file is specified) and EdDSA (from Keychain) + * The tool now uses Caches directory and doesn't generate unnecessary delta files + * Fixed verification of delta updates on filesystems that change permissions of symlinks + * Fixed `NSURLSession` leak (Michael Ehrmann) + +# 1.20.0 + + * `generate_appcast` option to read private key directly from the keychain (Tamás Lustyik) + * Add delegate callbacks for fininshed download and extraction related events (Csaba Horony) + * Don't check for updates if Do Not Disturb is on (Kornel) + * Expose `CodesigningVerifier`, add codesign info API (sunuslee) + * Threading fixes: + * Fix potential hang with `dispatch_sync` to main thread (Brian Bergstrand) + * Fix closeCheckingWindow called from background thread (Alexey Martemyanov) + * Improve 'read-only' error message (#1192) (Adrian Thomas) + * New Spanish localisation (Ken Arroyo Ohori) + * Updated Finnish language resources (Jason Pollack) + * Hungarian localization (Csaba Horony) + * Log more information about authentication requests (Kornel) + * Explicitly specify types to silence "Messaging unqualified id" warning that's new in Xcode 10. Removed __has_feature(objc_generics) check and use generisc to help silence the warnings. (Kent Sutherland) + * Fix binary delta creation on network drives (sagecook) + * Fix compilation issues on Xcode 10 with new build system (Leo Natan) + +# 1.19.0 + + * Refactoring of downloader code to avoid deprecated methods (Deadpikle) + * Changes to which methods run on the main thread. + Note: some delegate methods may be called on non-main thread now. (Kornel) + * Update Japanese localization (1024jp) + * Update Sparkle.strings (Stefan Paychère) + * Fix Sparkle clients polling too frequently (Jonathan Bullard) + * Handle SecTransformExecute errors (Kornel) + * Silence Touch Bar availability warnings on Xcode 9 by using API_AVAILABLE. Disable gnu-zero-variadic-macro-arguments to prevent warnings from use of API_AVAILABLE. (Kent Sutherland) + * 10.11 SDK compatibility (David Fuhrmann) + +# 1.18.1 + + * Add optional updaterDidRelaunchApplication: method on SUUpdaterDelegate (#1115) (App Tyrant) + * Implemented sparkle:os attribute as documented (Memphiz) + * Additional termination detection in case kpoll fails. (fujisoft) + * Included bin files in CocoaPods installation (Keith Smiley) + * Updated Dutch localization (Eitot) + * Updated German localization (Eitot) + * Updated Japanese translation (1024jp) + * Updated Portuguese translation (Victor) + * Updated to Xcode 9/Swift 4 + + # 1.18.0 + + * Name of the host app is used in authorization prompt (the `SPARKLE_RELAUNCH_TOOL_NAME` setting is now obsolete) + * More detailed progress bar for package installers (Kornel Lesiński) + * Disabled the keyboard shortcut for the install button for scheduled updates to avoid accidental installs. (George Nachman) + * generate_appcast tool adds release notes if there's an .html file with the same base name as the archive (Brett Walker) + * Added `sparkle:shortVersionString` to the enclosure, #1032 (Brett Walker) + * Fixed Japanese localization (1024jp) + * Fixed escaping of system profile URLs + * Added more logging in various failure cases (Kornel Lesiński) + * Better error message for quarantined apps that can't be updated + +# 1.17.0 + + * Added Touch Bar support (Bi11) + * Upgraded SULog to use logging APIs that Apple provides built-in (Zorg) + * Skip buttons are disabled if the update is marked as critical (Kornel Lesiński) + * Background updates ask OS for lower-priority networking (Kornel Lesiński) + * Refactorings to sync with upcoming 2.0 + * Added kqueue based termination listener (Zorg) + * Added AppKit prevention guards to modules that shouldn't import it (Zorg) + * Added Obj-C generics where applicable (Zorg) + * Made SUBundleIcon & SUApplicationInfo take SUHost, not NSBundle (Zorg) + * Improved -[SUHost objectForInfoDictionaryKey:] (Zorg) + * Detect and fail if any two-way dependencies exist in the project (Zorg) + * generate_appcast: + * fixed handling of multiple directories in an archive + * percent encode the filename used in the delta url (Brett Walker) + * Update Sparkle.strings (BR Lingo) + * Improved handling of non-ASCII names in delta archives (Kornel Lesiński) + * Don't touch Info.plist unless git version changes (Václav Slavík) + +# 1.16.0 + + * Guided package installs are now the default for updating packages (Zorg) + - `pkg` installers won't show any UI. If you require the old behavior of showing a full installer window, rename the `*.pkg` file to `*.sparkle_interactive.pkg` + * Previous version of the app is now deleted instead of staying in the trash (Zorg) + * Added `generate_appcast` helper tool (Kornel Lesiński) + * Made manual check resume pending automatic updates instead of starting a new update (Kornel Lesiński) + * Started using `length` value from RSS if HTTP doesn't give one (Zorg) + * Hidden automatic updates checkbox for information only updates (Bi11) + * Added progressbar for DMG and binary delta extraction (Kornel Lesiński) + * Fixed showing of download status if we attempt a 2nd download (Zorg) + * Refactorings to sync with upcoming 2.0 + * Decoupled and simplified installation code using protocols (Zorg) + * Added nullability annotations (Zorg) + * Allowed delegate methods that return an object to return nil (Zorg) + * Decreased responsibility of SUHost and moved code into other components (Zorg) + * Removed Sparkle.pch and many file #includes (Zorg) + +# 1.15.0 + +* A new icon! Thanks to 1024jp +* Show alert when an update is sent over insecure HTTP with no DSA key (Zorg) + - If you can't use HTTPS, you must at least sign updates with a DSA key. +* Improved binary delta implementation (Zorg) +* Added improved -validateMenuItem: as a method in SUUpdater.h for public use (Zorg) +* Removed reachability preflight check (Zorg) +* Clear update caches directory before downloading new update (Zorg) +* Check the bundle's parent directory for writability too (Zorg) +* Don't follow symbolic links for file operations (Zorg) +* Don't bring up an authorized dialog during cleanup (Zorg) +* Made Sparkle look for the highest compatible version regardless of timestamps (Zorg) +* Fixed compatibility with 10.7 + * Fixed crash on 10.7 - subscript operator not available (kleuter) + * Fixed warnings caused by -Wpartial-availability (Zorg) +* Fixed german l10n. (Sebastian Volland) +* Error code for download errors (Kornel Lesiński) +* Update last update check date when the update driver finishes (Zorg) +* Scale app icon up if needed in Software Update window (Nicholas Riley) +* Don't register for termination notifications more than once (Zorg) +* Don't terminate the app if we're already terminating (Zorg) +* Removed SUEnableAutomaticChecksKeyOld and SUCheckAtStartup constants (Eitot) +* Updated Sparkle framework headers to use modules if modules are available (B. Kevin Hardman) +* Fixed warnings, fixed uses of SULocalizedString (Jerry Krinock) +* Improved signing verifier to take any host and s/application/bundle/ (Zorg) +* Improved Spotlight updates after delta extraction (Zorg) + +# 1.14.0 (Mar 11, 2016) + +* Disable javascript by default and make it opt-in (Zorg) +* URL-encoding of appcast URLs is preserved (Kornel Lesiński) +* Delegate is asked for fallback updates if delta update fails (Kornel Lesiński) +* Fixed crash on 10.7 - subscript operator not available (kleuter) +* Fixed check of feed URL before delegate had a chance to set it (Kornel Lesiński) +* Re-added support for password-protected dmg images (Andrew K. Boyd) +* Added warning about ATS blocking (Kornel Lesiński) +* Translation fixes for pt-BR. (vitu) +* Add some Japanese lozalized strings (1024jp) +* Made test app available in all languages #695 (LIU Dongyuan / 柳东原) +* Czech localizations update (Frantisek Erben) +* Removed a test resource from the framework bundle (Karl Moskowski) +* Test if the updated app is the frontmost one (Zorg) +* UI Tests for the Test Application (Zorg) + +# 1.13.1 (Jan 31, 2016) + +Important security fixes: + +* Prevent inclusion of local files via file:// XML entities +* Disable redirects to non-HTTP URLs in release notes webview + +# 1.13.0 (Dec 18, 2015) + +* Changed framework's bundle ID from `org.andymatuschak.Sparkle` to `org.sparkle-project.Sparkle`. + +# 1.12.0 (Dec 13, 2015) + +* Rewritten file operations for updating an app (Zorg) + - Ensuring atomic move operations, robust error handling. + - Faster. + - Using modern APIs where possible (no FSPathMakeRef, FSGetCatalogInfo, FSFindFolder, etc.) + - Strong documentation, easier to read code. +* Automatic updates won't be installed if the system is about to shut off (Zorg) +* Deprecated serving over HTTP without DSA (Zorg) + - Note that Apple has deprecated insecure HTTP in macOS 10.11 +* Improved Autoupdate application (Zorg) + * Do all the installation work after the runloop is set up + * TerminationListener only does termination listening now + * Handle cases where host path is not installation path and host path is not desired executable path + * Don't show Autoupdate dock icon if we shouldn't show UI + * Update modification & access time for new update +* Added installUpdatesIfAvailable (Ian Langworth) +* Removed extensions from shell scripts (Jake Petroules) +* Rewritten test app so it works again, and from a local web server (Zorg) +* Replaced use of Python with built-in web server (Kevin Wojniak) +* Set LD_RUNPATH_SEARCH_PATHS in Podspec (Jake Petroules) +* Don't install automatic updates if the system might shut off (Zorg) +* Don't show Autoupdate dock icon if we shouldn't show UI (Zorg) +* Updated layout constraints when removing release notes (Zorg) +* Improved BinaryDelta error handling & logging (Zorg) +* Refactored quarantine removal (Zorg) +* Fixed German localization (1024jp) +* Updated zh_CN translation (LIU Dongyuan / 柳东原) +* Updated Mac models list until July 2015 (Gabriel Ulici) +* Updated Polish translation (Kornel Lesiński) +* Updated Xcode project languages for which we have translations (Jake Petroules) +* Updated XIB files (Kornel Lesiński) +* Use NSByteCountFormatter if available (Jake Petroules) +* Declared protocols on SUUpdateAlert for the 10.11 SDK (Daniel Jalkut) +* Silenced warning about casting away const-ness and -Wassign-enum (Daniel Jalkut) +* Added script to generate a report comparing the Sparkle.strings files (Kevin Wojniak) +* Check for empty strings (as well as nil) in SUHost's -name method (Karl Moskowski) +* Don't follow symlinks for checking file existence (Zorg) +* Unit tests in Swift (Zorg, Jake Petroules) +* Fixed framework imports (Felix Schulze) +* Fixed issues with copying files from different mounted drives (Zorg) +* Disallowed automatic updates when user can't write to the bundle (Zorg) +* Set the task working directories instead of changing the process working directory (Kevin Wojniak) + +# 1.11.1 (Nov 9, 2015) + +* Don't install automatic updates when system is about to shut down + +# 1.11.0 (Aug 24, 2015) + +* Big improvements to code signing and DSA verification + - Sparkle now checks not only whether an update is correctly signed, but also whether the updated version will be able to verify future updates. Updates now must either use DSA keys correctly, or not try to use them at all. Same goes for Apple Code Signing. + - Rely on code signing and the DSA key in the new app instead of appcast. If the new app has a public DSA key, then the appcast item must have a DSA signature for the app, even if the app is code signed. (Zorg) +* More verbose error message when DSA keys don't match (Kornel Lesiński) +* Added delegate methods for pre-download and immediately post-failed-download (Isaac Greenspan) +* Fix Lucida Grande is always used for release notes (LIU Dongyuan / 柳东原) +* Only remove quarantine with setResourceValue: when it's present. Fixes "Unable to quarantine: 93" messages from showing up in the console. (Zorg) +* Fixed const and nullability warnings (Jake Petroules, Kornel Lesiński) +* Replaced deprecated NSRunAlertPanel/alertWithMessageText (Kevin Wojniak) +* Imported the Foundation umbrella header in all the public headers (C.W. Betts) +* pt-BR localization update (Victor Figueiredo) +* Reject unsupported code-signing xattrs in binary delta (Zorg) +* Fixed crash while applying delta update (antonc27) +* Added logging of appcast/download URL on error (Kornel Lesiński) +* More robust reading of Autoupdate.app path from Sparkle bundle + +# 1.10.0 (Apr 26, 2015) + +* Massive improvements to the BinaryDelta tool (Zorg) + - Ability to track file permissions (Zorg) + - Nicely formatted log output (Zorg) + - Numerous bug fixes in handling of symlinks, empty directories, case-insensitive names, etc. (Zorg) + - Refactored and modernized code (Zorg) + - libxar is no longer weak-linked (C.W. Betts) +* Double-check the code signature of the the app after installation (Isaac Wankerl) +* Added headless guided package installation (Graham Miln) +* Added ability to inject custom HTTP headers in appcast request (Mattias Gunneras) +* Changes to make unarching more reliable (Zorg, Kornel Lesiński) +* Have Sparkle build a framework module (C.W. Betts) +* Stdout used for non error outputs (JDuquennoy) +* French locale update (Kent Sutherland) + +# 1.9.0 (Jan 26, 2015) + +* Added SUUpdater delegate method for failures. (Benjamin Gordon) +* Make the error definitions public (C.W. Betts) +* Add support for lzma compressed tarballs (Kyle Fuller) +* Back to SKIP_INSTALL=YES by default (Tony Arnold) +* Properly set install names and rpaths for targets (Jake Petroules) +* Use Library/Caches rather than app support directory (Kornel Lesiński) +* Check for a modal window being onscreen before trying to put up the Sparkle prompt (Alf Watt) +* Fixed crashes on 10.7 (Chris Campbell, Ger Teunis) +* Fixed Sparkle tags parsing (Tamás Lustyik) +* SULog code cleanups (Kevin Wojniak) +* Make sure CFBundleVersion is a semantic version number. (Jake Petroules) +* Replace typedef enums with typedef NS_ENUM to make Swift happier (C.W. Betts) +* Fix warnings under Xcode 6.1 relating the SUUpdateAlert XIB (Tony Arnold) +* Prefer string constants to strings (Jake Petroules) +* Use Info.plist keys instead of macros (Jake Petroules) +* Only export public symbols. (Jake Petroules) +* BinaryDelta: avoid crash with bad paths (Jake Petroules) +* Fixing Swedish translations (Erik Vikström) +* Turkish localization fixes (Emir) +* Proofing of Ukrainian localization (Vera Tkachenko) + +# 1.8.0 (Jul 26, 2014) + +* New SUDSAVerifier based on up-to-date macOS APIs (Zachary Waldowski) +* Detailed error log for failed signature checks (Kornel Lesiński) +* Converted Sparkle to ARC (C.W. Betts) +* Converted ivars to properties. (Jake Petroules) +* Cocoapod support (Xhacker Liu) +* Quarantine removal on macOS 10.10 (C.W. Betts) +* Updated Japanese localization (1024jp) +* Added Greek localization + +# 1.7.1 (Jul 2, 2014) + +* Removed option to install unverified updates (Kornel Lesiński) +* Added detailed log when code signing verification fails (Sam Deane) +* Restored original Sparkle icon. (Jake Petroules) +* Switched SUUpdateAlert.xib to AutoLayout (Kornel Lesiński) +* Replace references to andymatuschak.org with sparkle-project.org. (Jake Petroules) +* Several code cleanups, modernizations, fixed warnings and improved code formatting (Jake Petroules) +* Make the repository significantly more organized. (Jake Petroules) +* Xcode project: set organization name and class prefix. (Jake Petroules) +* Link to Foundation and AppKit instead of Cocoa. (Jake Petroules) +* Use new operatingSystemVersion API when available. (Jake Petroules) +* Add .clang-format configuration file for source code formatting. (Jake Petroules) +* Add a target to build Sparkle API documentation using Doxygen. (Jake Petroules) + +# 1.7.0 + +* Dropped support for macOS 10.6. Sparkle now supports 10.7 and newer (including 10.10 Yosemite) on 64-bit Intel Macs (the last 32-bit Mac was released in 2006). +* Removed use of deprecated functions (Zachary Waldowski) +* Switched to modern Obj-C runtime and new literals syntax +* Removed pre-10.7 code. (C.W. Betts) +* Use more Blocks/libdispatch code. (C.W. Betts) +* Cleaned up and improved security of `generate_keys`/`sign_update` scripts + +# 1.6.1 + +* Removed archive password prompt (Kornel Lesiński) +* (Re)fixes bug where URLs are naively double escaped (Andrew Madsen) +* Fixed typo that caused crashes in BinaryDelta (Tamas Lustyik) +* SUStandardVersionComparator.h is public (Vincent CARLIER) +* Remove pre-10.6-specific code. (C.W. Betts) +* Objective C 2 getters and setters. (C.W. Betts) +* Define correct dependencies on locale scripts (Antonin Hildebrand) + +# 1.6.0 + +* Cleaned up and deleted redundant strings files (Kornel Lesiński) +* Modern Objective C syntax, properties where possible. (C.W. Betts) +* Make SUAppcastDelegate a formal protocol. (C.W. Betts) +* Fixed default font in release notes WebView (Kornel Lesiński) +* Configurable name for finish_installation.app (Kornel Lesiński) +* Removed code for 10.4 (Kornel Lesiński) +* Convert all strings files to UTF-8 (UTF-16 must die) (Kornel Lesiński) +* Removing GC target (Matt Thomas) +* finish_installation.app and pkg files will not removed when we use *.pkg installer and restart system in the installer (Takayama Fumihiko) +* Select Korean and Slovak for Sparkle.strings localization (Shon Frazier) +* Updated the Romanian translation (Gabe) +* pt-BR localization polishing (BR Lingo) +* update zh_CN (61) +* Shut up some warnings & make build with newer Xcode (Uli Kusterer) +* Less unsafety with format strings (Uli Kusterer) +* New icon (Rick Fillion) +* fixed a 'content rectangle not entirely onscreen' warning (Simone Manganelli) +* updated sends system profile to use info.plist if user defaults key isn't present (Jamie Pinkham) +* Support for notifications on some updater events (Doug Russell) +* Allow the delegate to trigger a silent install and relaunch (Matt Stevens) +* Support silent relaunches (Matt Stevens) +* Increment the sudden termination counter if installing on quit (Matt Stevens) +* Prompts the user to update after a week (rather than a day) if he doesn't quit the app (Andy Matuschak) +* Adding appcast item element, tag (Andy Matuschak) +* We have this check box that says "Automatically download and install updates in the future." But we only download them automatically. We still ask permission again before installing them. (Andy Matuschak) + +# 1.5.0-beta6 + +* Important Changes + * Sparkle now requires DSA signatures on your updates. Check the documentation for more information on how to set that up if you don't already sign your updates. You can bypass this requirement if you deliver both your appcast and your updates over SSL. +* Sparkle will no longer display release notes located at file:// URLs, since Javascript on such a page would be able to read files on your file system. +* For security reasons, Sparkle will refuse to install updates which appear to "downgrade" the app. +* SUUpdater now implements new keys: "automaticallyDownloadsUpdates", "lastUpdateCheckDate", and "sendsSystemProfile." +* Fixed a bug that could prevent SUProbingUpdateDriver from working. +* Fixed a bug that prevented the updaterWillRelaunchApplication: delegate method from getting called. +* Fixed displaying release notes transmitted "loose" in the key. +* Fixed Sparkle compilation on 10.4 systems. +* Fixed a bug that could cause window confusion if an app changed its LSUIElement at runtime. +* Added support for Sparkle 1.1's behavior of disabling updates when the check interval is 0. +* Sparkle can now handle appending parameters to URLs which already have parameters. +* If an update's sparkle:shortVersionString is the same as the host's CFBundleShortVersionString, the sparkle:version and CFBundleVersion will be presented in parentheticals. + +# 1.5.0-beta5 + +* Important Changes! + * Made every Sparkle class private except for SUUpdater, SUAppcast, SUAppcastItem, and the SUVersionComparisonProtocol. + * There is now a single SUUpdater singleton for every host bundle; instead of -[SUUpdater setHostBundle], you can use +[SUUpdater updaterForBundle]. + * Redefined the (entire) delegate protocol accordingly. + * Renamed -[SUUpdater updatePreferencesChanged] to -[SUUpdater resetUpdateCycle]. This provides better semantics for non-apps, which need to start the update cycle manually. + * -[SUUpdater checkForUpdatesWithDriver] is private. If you were using SUProbingUpdateDriver, you can now use -[SUUpdater checkForUpdateInformation] for a similar effect. + * All the user defaults keys are now private; instead, SUUpdater is KVC-compliant for automaticallyChecksForUpdates, updateCheckInterval, and feedURL. +* Reduced the size of the English-only framework by 25%. +* System profiling information is now only submitted to the server once per week; this will help normalize your statistics across users with different interval preferences. +* The feedParamatersForUpdater: delegate method now requires "displayKey" and "displayVersion" keys so that it can inform the user what's being sent. +* Added a delegate method called pathToRelaunchForUpdater: which can be used for plugins to provide the path which should be used when relaunching the client after installing an update. +* Added support for xml:lang to pick localized nodes in appcasts (for release notes, etc). +* Fixed a bug which would cause the "checking for updates" window to not disappear in certain extraordinary error conditions. +* Fixed a DSA signature checking bug for .tar.gz archives. +* Sparkle now refuses to update on any read-only volume, not just dmgs. +* Sparkle will clean up the host app's name and version before sending it as the user agent string; some non-ASCII characters were causing problems. +* Added an Italian localization courtesy Michele Longhi. +* Added a Swedish localization courtesy Daniel Bergman. +* Fixes to the French localization courtesy Ronald Leroux and Yann Ricqueberg. +* Fixes to the German localization courtesy Sven-S. Porst. +* Fixes to the Russian localization courtesy Alexander Bykov and Anton Sotkov. +* Fixed a number of issues related to archive format detection: I reverted back to extensions from UTIs. +* Focus behavior fixes for LSUIElement apps. +* The status window progress bar now animates even when indeterminate. +* Major refactorings to improve functionality for non-app bundles. + +# 1.5.0-beta4 + +* Fixed a critical bug which prevented non-.dmgs from unarchiving properly. +* Added reporting of 64-bit capability to the profiling system. + +# 1.5.0-beta3 + +* Added a new delegate method to SUUpdater.h to allow delegates to specify custom version comparators. +* Added a German localization, courtesy the Camino localizer team: Dominik Tobschall, Tobias Stohr, and Friedemann Bochow. +* Bug fixes: + * Fixed a serious bug which could cause a server to be DDoS'd (or the host app to crash!) if an appcast fails to be parsed. + * Fixed .tbz extraction if the archive was made with Stuffit. + * Fixed support for .tar.bz2 and .tar.gz; Sparkle has to assume the archive is a tar when it sees "bz2" and "gz"; don't use those without tarring. + * Fixed a typo which caused the shouldPromptForPermissionToCheckForUpdatesToHostBundle: method to not work in 1.5b2. + * Fixed .zip extraction on Tiger (Apple changed the UTI between releases) + * Fixed a crasher on Tiger. + * Fixed display of the default app icon when the host app doesn't have an icon. + * Sparkle now displays a sensible progress string and uses an indeterminate progress bar when the server doesn't report a file size. + * Fixed some memory leaks. + + # 1.5.0-beta2 + + * Compatibility Issues: + * Most of the delegate method selectors have changed to scale better. See SUUpdater.h for changes; you'll likely have to make changes if you implement any delegate methods. + * If you're using .tar.gz or .tar.bz2 archives, name them ".tbz" or ".tgz" instead; Sparkle now uses UTIs for archive detection, and it's not smart about double extensions. + * I'm no longer supporting 10.3. This may or may not work on Panther—probably not. + * Sparkle's no longer built for ppc64 by default. If you want to ship that, feel free to build your own, but this saves a few hundred k. +* Enhancements: + * Sparkle now detects if the preferences for automatic update checks or the time interval change mid-cycle. + * If your product is a non-.app, you need to clue Sparkle in on the change by calling [[SUUpdater sharedUpdater] updatePreferencesChanged]. + * Added a cancel to the "checking for updates..." dialog. + * Sparkle now cleans up all its litter in /tmp. + * Made SUUpdater's delegate an IBOutlet so you can hook it up in IB. +* Bug fixes: + * Sparkle no longer crashes on non-GC hosts when the user cancels an update's downloads. + * Sparkle no longer gets stuck in an inconsistent state or crashes when it can't parse the appcast on scheduled updates. + * Added the sharedUpdater method to SUUpdater, as it should have been. + * Fixed a bug where the "checking for updates..." window wouldn't go away if an error occurs while checking for updates. + * Made the dual-mode build configuration actually use the .xcconfig which builds it with GC support. (oops!) + * Fixed relaunching for prefpanes. + * Sparkle no longer fails to install updates on Snow Leopard (though there's still an issue with trashing the old version of the app, but it seems to be a 10.6 bug) + * Sparkle now handles redirects correctly under Tiger. + * Fixed the installation path for non-.app bundles. + * Fixed a bug which could crash Sparkle under non-English locales. + * Fixed a weird race condition which could cause the relaunch tool to never notice that its target relaunched. + * Fixed a bug where if the host app is inactive when an update occurs, the update alert sometimes doesn't become key. + * Minor textual fixes. +* Localizations: + * Dutch: Maarten Van Coile + * French: Yann Ricquebourg + * Spanish: Ernesto Gomez Cereijo + + # 1.5.0-beta1 + + * The most important things to know: + * The 10.3 support is untested at best; sketchy at worst. Test with it thoroughly before you use it. + * Sparkle now asks for permission to update on second launch; don't be surprised at that. You can change that behavior with a delegate method; read SUUpdater.h for more info. + * We no longer distinguish between "check on startup" and "scheduled updates"; everything is scheduled, with the default being every day. + * The test application is using the new profiling features, but that's only for demonstration: these are off by default. More on this later. + * There are no localizations yet. +* New features: + * Sparkle now supports .pkgs. Just name the .pkg the name of the app and put in the update archive. + * Sparkle now sends optional demographic profiling information; set SUEnableSystemProfiling to YES in your Info.plist and check out the GET data sent to your webserver when fetching the appcast. More on this in the documentation. The test application has this on so you can see the behavior. + * Sparkle now supports updating non-.apps. Just call -setHostBundle: on the global SUUpdater to let it know what you're trying to update. + * Sparkle now supports garbage collection in the host app. Use "Sparkle-with-GC.framework" for that, but be aware it's 10.5-only. + * Sparkle is now 64-bit compatible, compiling both ppc64 and x86_64. + * Sparkle now supports a sparkle:minimumSystemVersion key you can set on appcast items. It does what you think it does. + * Sparkle now checks to see if the host app is running from a disk image and refuses to update if it is. (10.4+ only) + * Added support for entities in enclosure paths. + * The file size output is now formatted prettily. + * Sparkle now gives visual indication that it's checking for updates when the update's user initiated. ie: it pops up a status controller saying "checking for updates..." + * Added support for an SUPublicDSAKeyFile, so people don't have to copy/paste their entire key into their Info.plist. Set this key in your Info.plist to the filename of the key in your Resources directory. + * Added an actually maintainable codebase. +* Changes: + * Sparkle version comparison is now dramatically less stupid and verified by a bunch of unit tests. If something doesn't work the way you think it should, add a test to SUVersionComparisonTest.m + * Added a minimum to the check interval so that developers don't accidentally release their apps into the wild with 60-second test check intervals and have DOS-attack-like results. It's an hour now for release mode; feel free to change it. + * The relaunching process now uses a separate helper app, which is a much more robust method. + * Changed CFBundleShortVersionString behavior: Sparkle no longer uses Apple's about box style of displaying ShortVersionString (CFBundleVersion) when the latter is available. + * No more MD5 checking. Use DSA: it's actually secure. + * The abomination that was SUStatusChecker is dead. Use SUProbingUpdateDriver instead. +* Bugfixes: + * Fixed a huge bug with fully-automatic updating: before, if the user chose to relaunch later, the app would be running from the trash for a while. Now the buttons are "install and relaunch" or "install later." + * Sparkle forces Spotlight to reindex the updated app so that it won't keep pointing to the one in the trash. + * Sparkle trims whitespace from around DSA signatures; this could cause crashes before. + * Fixed a bug where the user choosing to skip a version would inhibit future automatic updates until the next launch. + * Fixed a bug that could occur when the app has a localized CFBundleName. + * .dmgs now work on Leopard. + * The status controller's button now sizes appropriately to the localization. + * Sparkle now works correctly with LSUIElement apps: it focuses them before displaying the update alert. + * Sparkle now deletes failed partial downloads. + * The update alert no longer floats above everything in the app. + * Fixed varied and sundry memory leaks. +* A ton of other things that I've forgotten or were too small to mention! + +# 1.1 + +* Optimized framework size: now only 1.4mb with all localizations and 384kb with only English (an English-only version is in the Extras folder). +* Added a new SUStatusChecker class for programmatically determining if a new version is available (see the docs); thanks, Evan Schoenberg! +* Added support for apps using SIGCHLD; thanks, Augie Fackler! +* Added a zh_CN update from JT Lee +* Added a Polish update from Piotr Chylinski +* Fixed DMG support for images with /Applications symlinks. +* Fixed a really stupid interval-checking bug that could cause repeated hits to the appcast. +* Fixed a bug where the check interval would be inconsistent if a value of 0 was stored in the user defaults. + +# 1.0 + +* Additions: + * Added real version comparison courtesy Kevin Ballard: Sparkle now knows that 0.89 < 1.0a3 < 1.0. + * Added many localizations courtesy David Kocher's localization team. + * Added a much better installation mechanism courtesy Allan Odgaard. + * Added a user agent string to the RSS fetch request. + * Added support for CFBundleShortVersionString in addition to CFBundleVersion, and support for a sparkle:shortVersionString attribute on the enclosure. + * Added support for CFBundleDisplayName if available. +* Changes: + * Automatic updating is now allowed by default, but only if DSA signing is on. + * Pressing Escape or closing the update alert now reminds the user later. + * Now when there's a stored check interval, Sparkle doesn't check immediately on startup the first time the app is launched because the user hasn't consented to it yet. + * The update alert now remembers its size and floats. +* Bug Fixes: + * Fixed installation of DMGs with multiple files enclosed. + * Fixed a nasty memory leak. + * Fixed a bug wherein having no value for allowing automatic updates would display a checkbox for the updates but would not honor it. + * Fixed a bug in zip extraction that occurred in Panther. + * Fixed release notes caching. + * Fixed a bug wherein Sparkle refused to authenticate the installation if the user had cancelled authentication previously in that session. + * Fixed a weird bug that would cause a second help menu to appear on first launch. + * Fixed a bug that could occur when changing the scheduled check interval. + * Fixed a bug wherein the host app could crash if the user clicked Remind Me Later before the release notes finished loading. + * Fixed a bug wherein the behavior was undefined if the user manually initiated a check when an automatic one was already taking place. + * Fixed wrapping on the description field in the update alert. + +# 1.0-beta3 + +* Fixed a nasty crasher that occurred often when the user was not connected to the internet. + +# 1.0-beta2 + +* Major Improvements: + * Fully automatic updating! (see the Documentation: this is beta and off by default) + * Added support for DSA signatures (see the Documentation). + * Added support for MD5 sum verification. + * Added Security.framework-based authentication for installing to privileged directories. + * Huge refactoring of the codebase: there's now a Sparkle Xcode project, Sparkle is now a framework, and everything is modular / abstracted. And no more code-generated interface. +* Minor Improvements: + * A SUUpdaterWillRestartNotification is sent out before restarting now. + * Added key equivalents to alert panel buttons. + * Error handling is much prettier now: technical messages are not presented to the user anymore. + * There's now a test app for developers to see what Sparkle's like before using it. + * Wrote new, pretty, extremely thorough documentation. +* Bug Fixes: + * Relaunch behavior is much improved and shouldn't fail in huge apps anymore. + * Fixed a bug wherein a failing tar command could crash the host app. + * Sparkle now looks at InfoPlist.strings in addition to Info.plist. + * Fixed some stupid typos. + +# 1.0-beta1 + +* Major New Features: + * Sparkle now supports scheduled periodic updates—read the Readme for information on how to use it. + * Sparkle now supports WebKit-based release notes (for CSS and full HTML), which it displays in the main update alert, not a separate panel. The Readme has much more information. Sparkle will, of course, fall back on NSTextView if the host app does not include WebKit. +* Minor New Features: + * Added support for .zip update archives. + * Added support for .dmg update archives. + * Implemented Remind Me Later to replace simple update cancellation. + * Implemented Skip This Version functionality. + * Added support for multiple feeds via the user defaults SUFeedURL key taking precedent over the one in Info.plist. + * Added support for Sparkle's custom XML namespace, which is optional but may prove useful. See the Readme for more information. +* Bug Fixes: + * Sparkle will no longer enter an inconsistent state if the user tries to update again while one is already in progress. + * Sparkle now uses CFBundleName to determine the application's name instead of the app's filename. + * Sparkle no longer crashes if the user cancels during extraction. + * Lots of code refactoring. + +# 0.1 + +* Initial Release diff --git a/Dependencies/Sparkle/LICENSE b/Dependencies/Sparkle/LICENSE new file mode 100644 index 00000000..1e9b1c6e --- /dev/null +++ b/Dependencies/Sparkle/LICENSE @@ -0,0 +1,61 @@ +Copyright (c) 2006-2013 Andy Matuschak. +Copyright (c) 2009-2013 Elgato Systems GmbH. +Copyright (c) 2011-2014 Kornel Lesiński. +Copyright (c) 2015-2017 Mayur Pawashe. +Copyright (c) 2014 C.W. Betts. +Copyright (c) 2014 Petroules Corporation. +Copyright (c) 2014 Big Nerd Ranch. +All rights reserved. + +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. + +================= +EXTERNAL LICENSES +================= + +bspatch.c and bsdiff.c, from bsdiff 4.3 : + Copyright (c) 2003-2005 Colin Percival. + +sais.c and sais.c, from sais-lite (2010/08/07) : + Copyright (c) 2008-2010 Yuta Mori. + +SUDSAVerifier.m: + Copyright (c) 2011 Mark Hamlin. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted providing that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Dependencies/Sparkle/SampleAppcast.xml b/Dependencies/Sparkle/SampleAppcast.xml new file mode 100755 index 00000000..35ad5dad --- /dev/null +++ b/Dependencies/Sparkle/SampleAppcast.xml @@ -0,0 +1,41 @@ + + + + Your Great App's Changelog + Most recent changes with links to updates. + en + + Version 2.0 (2 bugs fixed; 3 new features) + + http://you.com/app/2.0.html + + Wed, 09 Jan 2006 19:20:11 +0000 + + + 10.7 + + + + Version 1.5 (8 bugs fixed; 2 new features) + + http://you.com/app/1.5.html + + Wed, 01 Jan 2006 12:20:11 +0000 + + + 10.7 + + + + + Version 1.4 (5 bugs fixed; 2 new features) + + http://you.com/app/1.4.html + + Wed, 25 Dec 2005 12:20:11 +0000 + + + 10.7 + + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Info.plist b/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Info.plist new file mode 100644 index 00000000..3d96af1d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.SparkleTestApp + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.5.1 + CFBundleVersion + 1.5.1 + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Resources/DWARF/Sparkle Test App b/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Resources/DWARF/Sparkle Test App new file mode 100644 index 00000000..fd5cceae Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app.dSYM/Contents/Resources/DWARF/Sparkle Test App differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Resources b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Resources new file mode 120000 index 00000000..953ee36f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Sparkle b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Sparkle new file mode 120000 index 00000000..b2c52731 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist new file mode 100644 index 00000000..676181f9 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -0,0 +1,56 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + English + CFBundleExecutable + Autoupdate + CFBundleIconFile + AppIcon.icns + CFBundleIdentifier + org.sparkle-project.Sparkle.Autoupdate + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.24.0 a-67-g0e162c98 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSBackgroundOnly + 1 + LSMinimumSystemVersion + 10.7 + LSUIElement + 1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate new file mode 100755 index 00000000..164511d4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop new file mode 100755 index 00000000..29dae8a1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns new file mode 100644 index 00000000..7f2a571c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..bb4125f0 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,860 @@ + + + + + files + + Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + MacOS/fileop + + cdhash + + JwkAFJqL9xY1mTI+1Kki3oSFsik= + + requirement + cdhash H"d5bc45cc18a448c02d5c4dd6859a64524a5b8a85" or cdhash H"270900149a8bf7163599323ed4a922de8485b229" or cdhash H"1b27242b81a5a51561703e2bb8a5e01acac436e9" or cdhash H"ead0c4c63eafc5d32327f0dbf958b7bd0993ec75" + + Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/DarkAqua.css b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/DarkAqua.css new file mode 100644 index 00000000..a41e0f28 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/DarkAqua.css @@ -0,0 +1,9 @@ +html { + color: #FFFFFFD8; +} +:link { + color: #419CFF; +} +:link:active { + color: #FF1919; +} diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist new file mode 100644 index 00000000..8786d487 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Sparkle + CFBundleIdentifier + org.sparkle-project.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.24.0 a-67-g0e162c98 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSMinimumSystemVersion + 10.7 + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist new file mode 100644 index 00000000..1f75b248 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -0,0 +1,314 @@ + + + + + ADP2,1 + Developer Transition Kit + iMac1,1 + iMac G3 (Rev A-D) + iMac4,1 + iMac (Core Duo) + iMac4,2 + iMac for Education (17 inch, Core Duo) + iMac5,1 + iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) + iMac5,2 + iMac (Core 2 Duo, 17 inch, Combo Drive) + iMac6,1 + iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac7,1 + iMac Intel Core 2 Duo (aluminum enclosure) + iMac8,1 + iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) + iMac9,1 + iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) + iMac10,1 + iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) + iMac11,1 + iMac (Core i5 or i7, 27 inch Late 2009) + iMac11,2 + 21.5" iMac (mid 2010) + iMac11,3 + iMac (Core i5 or i7, 27 inch Mid 2010) + iMac12,1 + iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) + iMac12,2 + iMac (Core i5 or i7, 27 inch Mid 2011) + iMac13,1 + iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) + iMac13,2 + iMac (Core i5 or i7, 27 inch Late 2012) + iMac14,1 + iMac (Core i5, 21.5 inch Late 2013) + iMac14,2 + iMac (Core i5 or i7, 27 inch Late 2013) + iMac14,3 + iMac (Core i5 or i7, 21.5 inch Late 2013) + iMac14,4 + iMac (Core i5, 21.5 inch Mid 2014) + iMac15,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) + iMac16,1 + iMac (Core i5, 21,5 inch Late 2015) + iMac16,2 + iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) + iMac17,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) + MacBook1,1 + MacBook (Core Duo) + MacBook2,1 + MacBook (Core 2 Duo) + MacBook4,1 + MacBook (Core 2 Duo Feb 2008) + MacBook5,1 + MacBook (Core 2 Duo, Late 2008, Unibody) + MacBook5,2 + MacBook (Core 2 Duo, Early 2009, White) + MacBook6,1 + MacBook (Core 2 Duo, Late 2009, Unibody) + MacBook7,1 + MacBook (Core 2 Duo, Mid 2010, White) + MacBook8,1 + MacBook (Core M, 12 inch, Early 2015) + MacBookAir1,1 + MacBook Air (Core 2 Duo, 13 inch, Early 2008) + MacBookAir2,1 + MacBook Air (Core 2 Duo, 13 inch, Mid 2009) + MacBookAir3,1 + MacBook Air (Core 2 Duo, 11 inch, Late 2010) + MacBookAir3,2 + MacBook Air (Core 2 Duo, 13 inch, Late 2010) + MacBookAir4,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2011) + MacBookAir4,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2011) + MacBookAir5,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2012) + MacBookAir5,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2012) + MacBookAir6,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) + MacBookAir6,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) + MacBookAir7,1 + MacBook Air (Core i5 or i7, 11 inch, Early 2015) + MacBookAir7,2 + MacBook Air (Core i5 or i7, 13 inch, Early 2015) + MacBookPro1,1 + MacBook Pro Core Duo (15-inch) + MacBookPro1,2 + MacBook Pro Core Duo (17-inch) + MacBookPro2,1 + MacBook Pro Core 2 Duo (17-inch) + MacBookPro2,2 + MacBook Pro Core 2 Duo (15-inch) + MacBookPro3,1 + MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) + MacBookPro3,2 + MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) + MacBookPro4,1 + MacBook Pro (Core 2 Duo Feb 2008) + MacBookPro5,1 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,2 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,3 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,4 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,5 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro6,1 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro6,2 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro7,1 + MacBook Pro Intel Core 2 Duo (mid 2010) + MacBookPro8,1 + MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) + MacBookPro8,2 + MacBook Pro Intel Core i7, 15" (early 2011) + MacBookPro8,3 + MacBook Pro Intel Core i7, 17" (early 2011) + MacBookPro9,1 + MacBook Pro (15-inch, Mid 2012) + MacBookPro9,2 + MacBook Pro (13-inch, Mid 2012) + MacBookPro10,1 + MacBook Pro (Retina, Mid 2012) + MacBookPro10,2 + MacBook Pro (Retina, 13-inch, Late 2012) + MacBookPro11,1 + MacBook Pro (Retina, 13-inch, Late 2013) + MacBookPro11,2 + MacBook Pro (Retina, 15-inch, Late 2013) + MacBookPro11,3 + MacBook Pro (Retina, 15-inch, Late 2013) + MacbookPro11,4 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro11,5 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro12,1  + MacBook Pro (Retina, 13-inch, Early 2015) + Macmini1,1 + Mac Mini (Core Solo/Duo) + Macmini2,1 + Mac mini Intel Core + Macmini3,1 + Mac mini Intel Core + Macmini4,1 + Mac mini Intel Core (Mid 2010) + Macmini5,1 + Mac mini (Core i5, Mid 2011) + Macmini5,2 + Mac mini (Core i5 or Core i7, Mid 2011) + Macmini5,3 + Mac mini (Core i7, Server, Mid 2011) + Macmini6,1 + Mac mini (Core i5, Late 2012) + Macmini6,2 + Mac mini (Core i7, Normal or Server, Late 2012) + Macmini7,1 + Mac mini (Core i5 or Core i7, Late 2014) + MacPro1,1,Quad + Mac Pro + MacPro1,1 + Mac Pro (four-core) + MacPro2,1 + Mac Pro (eight-core) + MacPro3,1 + Mac Pro (January 2008 4- or 8- core "Harpertown") + MacPro4,1 + Mac Pro (March 2009) + MacPro5,1 + Mac Pro (2010 or 2012) + MacPro6,1 + Mac Pro (Late 2013) + PowerBook1,1 + PowerBook G3 + PowerBook2,1 + iBook G3 + PowerBook2,2 + iBook G3 (FireWire) + PowerBook2,3 + iBook G3 + PowerBook2,4 + iBook G3 + PowerBook3,1 + PowerBook G3 (FireWire) + PowerBook3,2 + PowerBook G4 + PowerBook3,3 + PowerBook G4 (Gigabit Ethernet) + PowerBook3,4 + PowerBook G4 (DVI) + PowerBook3,5 + PowerBook G4 (1GHz / 867MHz) + PowerBook4,1 + iBook G3 (Dual USB, Late 2001) + PowerBook4,2 + iBook G3 (16MB VRAM) + PowerBook4,3 + iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) + PowerBook5,1 + PowerBook G4 (17 inch) + PowerBook5,2 + PowerBook G4 (15 inch FW 800) + PowerBook5,3 + PowerBook G4 (17-inch 1.33GHz) + PowerBook5,4 + PowerBook G4 (15 inch 1.5/1.33GHz) + PowerBook5,5 + PowerBook G4 (17-inch 1.5GHz) + PowerBook5,6 + PowerBook G4 (15 inch 1.67GHz/1.5GHz) + PowerBook5,7 + PowerBook G4 (17-inch 1.67GHz) + PowerBook5,8 + PowerBook G4 (Double layer SD, 15 inch) + PowerBook5,9 + PowerBook G4 (Double layer SD, 17 inch) + PowerBook6,1 + PowerBook G4 (12 inch) + PowerBook6,2 + PowerBook G4 (12 inch, DVI) + PowerBook6,3 + iBook G4 + PowerBook6,4 + PowerBook G4 (12 inch 1.33GHz) + PowerBook6,5 + iBook G4 (Early-Late 2004) + PowerBook6,7 + iBook G4 (Mid 2005) + PowerBook6,8 + PowerBook G4 (12 inch 1.5GHz) + PowerMac1,1 + Power Macintosh G3 (Blue & White) + PowerMac1,2 + Power Macintosh G4 (PCI Graphics) + PowerMac2,1 + iMac G3 (Slot-loading CD-ROM) + PowerMac2,2 + iMac G3 (Summer 2000) + PowerMac3,1 + Power Macintosh G4 (AGP Graphics) + PowerMac3,2 + Power Macintosh G4 (AGP Graphics) + PowerMac3,3 + Power Macintosh G4 (Gigabit Ethernet) + PowerMac3,4 + Power Macintosh G4 (Digital Audio) + PowerMac3,5 + Power Macintosh G4 (Quick Silver) + PowerMac3,6 + Power Macintosh G4 (Mirrored Drive Door) + PowerMac4,1 + iMac G3 (Early/Summer 2001) + PowerMac4,2 + iMac G4 (Flat Panel) + PowerMac4,4 + eMac + PowerMac4,5 + iMac G4 (17-inch Flat Panel) + PowerMac5,1 + Power Macintosh G4 Cube + PowerMac5,2 + Power Mac G4 Cube + PowerMac6,1 + iMac G4 (USB 2.0) + PowerMac6,3 + iMac G4 (20-inch Flat Panel) + PowerMac6,4 + eMac (USB 2.0, 2005) + PowerMac7,2 + Power Macintosh G5 + PowerMac7,3 + Power Macintosh G5 + PowerMac8,1 + iMac G5 + PowerMac8,2 + iMac G5 (Ambient Light Sensor) + PowerMac9,1 + Power Macintosh G5 (Late 2005) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) + RackMac1,1 + Xserve G4 + RackMac1,2 + Xserve G4 (slot-loading, cluster node) + RackMac3,1 + Xserve G5 + Xserve1,1 + Xserve (Intel Xeon) + Xserve2,1 + Xserve (January 2008 quad-core) + Xserve3,1 + Xserve (early 2009) + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..65f118ac Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..44b6741d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..4f215f09 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..103886fe Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..68e44511 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..465e87dd Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..d22bba56 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dc1aa3fa Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3515d02c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cf626429 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..25873443 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..37c88052 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ddcb7b3a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0085ef14 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..8668aa87 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5732e8f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..352a9a84 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..8ef47f95 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..01694b25 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9216cb74 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..49fce208 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..86011659 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..795d05d3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..5cb6f89c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74c1e5a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dea32c6d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..06a9205b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj new file mode 120000 index 00000000..f9834a39 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/fr_CA.lproj @@ -0,0 +1 @@ +fr.lproj \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..12476cc0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..60659fe7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3837f8af Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd46e650 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..b2400c73 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a4791c6b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..2e2c6025 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..43990e17 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..00c3b4cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..fb389d23 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..7eea1a8d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..966065f6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9200a22c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..abe1d0bf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..6ec0cbdf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..b2942676 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d5e01ade Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..db6622c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cbc7a9fd Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..3a875f2b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..9dc8dc71 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..96e672b3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..ccec5f8f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..2b64274f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..aa19d7e3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4fcb4eec Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..aced373c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt.lproj b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt.lproj new file mode 120000 index 00000000..3c1c9f6d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt.lproj @@ -0,0 +1 @@ +pt_BR.lproj \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0e8fdc60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0a899437 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..c9bd5783 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..7056ce7b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d77700f0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..554151eb Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5907ff1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9e45a85c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..56c26903 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9889fb2d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4743f8f9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..e0d48aa4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..912d1dd9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0aa042e4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a7119055 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74526023 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..96b5964a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..12fcd8c1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ae7ceb14 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..07518c34 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..6d16cc67 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0ee49a1f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..5241b1f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..d66c6ff0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..24605f17 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..e722b695 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..5c8a7d84 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd540a60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..8ebec67d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..dd31811c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..1cb9d707 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..2c066da9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..1e5b4eb2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..3c202810 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..27d22201 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..79433652 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 00000000..60b7f803 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources new file mode 100644 index 00000000..a0beaeea --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/A/_CodeSignature/CodeResources @@ -0,0 +1,3680 @@ + + + + + files + + Resources/Autoupdate.app/Contents/Info.plist + + vcMwRErKskmkeyJJVI3Z3sof/sE= + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + +dZZmoYTS+EoOzyj5DHipiL7vdA= + + Resources/Autoupdate.app/Contents/MacOS/fileop + + rsEt+sjfXAsfWD9h538L+27pg/s= + + Resources/Autoupdate.app/Contents/PkgInfo + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + VHoMteoopEbbYVG87rxkm0sjkA4= + + Resources/DarkAqua.css + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + Resources/Info.plist + + Vjt5kOIAFdWzUgFgF/aP2v3owEc= + + Resources/SUModelTranslation.plist + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + Heb65H1UseXl7rEaFwVxKauBWnI= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + oy0dHoyKmH2uV/KCHJzCagE+QIE= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + mYyXqqWSoYqVG1zNp1vopIw8r1k= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + uLKIwoprHw35+b4+/KP/j9X2zVg= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + tre2iSm68OK3ztgNotyXuz1MkzI= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + HB5ASms7UIZfv0WaGh6tCLBEDP8= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + S2YV0JmEwfPtYsMBBMuvddrPEis= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + xRBgLwOX0xZhrXGjHDHL6S+qCQc= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + o6d6uYDAajCHTJJOXT7zDECTmIM= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + b1mgRruuDPTLXfhBEjTV72kV1m0= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + ycrHyxLA68Lf8rq4IXBVK62UpGc= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + sGjRl91qI6175CwJYuqSYLYylJg= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + QHLHLZGOJJ4eN75aG1K1VUHNPz8= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + pZo0pXve6jqBertG5IixAzSpgV4= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2dtbduILRWtmfjobyd2yOVhQNH4= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5B8hYUrKag0Unyt6Uk0D2K5opL8= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + GY/ufItfyKYpgw54TfqJlPlymb0= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + AqJRrBMp2yA+umSXxQIQVmpnCN4= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + burLhfFkzbPjAIqMXw1qKn94xm8= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + lT25Wn73ZrIgcDJsgzSvIQ97TtY= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + W5vyz7ueX3DVKxQC82/3FnvJfeQ= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + JIh/Ueyxh8+us+26dxQRmrPiVAE= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5wxy4Op51XjVl1MvUlCnSUfvsj8= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + HEubU7VtIHZcWJ6RfdC038Os1gw= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + FT+kQgUNxKGrbheU8uSqkYFSHtI= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + qO2OAmNcqk2/bSzwAjGcXTD4+PY= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + Resources/Autoupdate.app/Contents/Info.plist + + hash + + vcMwRErKskmkeyJJVI3Z3sof/sE= + + hash2 + + KAQWAU9pYYHyY0ffDykJoZXUPjiewvVWowIwn5qsBNc= + + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + hash + + +dZZmoYTS+EoOzyj5DHipiL7vdA= + + hash2 + + /CLwW6G+QiK5ETp+clGZsGSksu5/2ppP1elEQVje7VY= + + + Resources/Autoupdate.app/Contents/MacOS/fileop + + hash + + rsEt+sjfXAsfWD9h538L+27pg/s= + + hash2 + + kI/01mUGzFnWLufOtr6ca07s6VBhqA7INTCKBS4xA+8= + + + Resources/Autoupdate.app/Contents/PkgInfo + + hash + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + hash2 + + glAhkclISwTWhTdPmHmgBmBpxJuKyuegSwHTjQfo7KA= + + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + hash + + VHoMteoopEbbYVG87rxkm0sjkA4= + + hash2 + + lxIEfRslquAFN4m0UvN8ZbULT6U6J3cPAYY9sqR1HPE= + + + Resources/DarkAqua.css + + hash + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + hash2 + + 1G4+GJId47E8UijYTy9xeqA8RpLdOaGZQZ8B85ydROg= + + + Resources/Info.plist + + hash + + Vjt5kOIAFdWzUgFgF/aP2v3owEc= + + hash2 + + F8ePDHN9JgUrW5JHoFYN8yT7d6AnZBL5ZflSVh3ri4Q= + + + Resources/SUModelTranslation.plist + + hash + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + hash2 + + bxnpRQhROJXTL2xuIeffR3p+hOuuji7eOoDeNqSYqTg= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + hash2 + + CL6tBm495f4yM6z7y8UHRhtooR3NLGfDUOMHspa3d6k= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + hash2 + + CE1qJ1jrwUiTKTlZajb/bhplzo/rdEH6pm5cABwD/rQ= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + Heb65H1UseXl7rEaFwVxKauBWnI= + + hash2 + + WUx1KM1Bz75vbTlcj3FvUEEJ3niP2QNBe7/lPioCMgY= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + hash2 + + YyE1WN1/ryPt2H0D9gYJv/r0SSv8VYTcxiiNeELiJIQ= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + hash2 + + LWRxgLZHNGYOe63gf0aOD8zoP03Z1s7ldxndzkWbzGw= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= + + hash2 + + dyM1bkEKAH1sW3J5pxDKHpNZ6ZJX7YH/x6jeICRqpkc= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + hash2 + + ivTLD912Rxy2BqIYFAQjsqh4PofwMLljqh6ncdYEdy8= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + hash2 + + BZL9bUc/f5RpZHoQGkA/XXKvykMh/LwkqI+1XW14Bxk= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= + + hash2 + + N3Os+6xHdP9Y/QLv2okENWzAaaY73ZZ1wAa+vhQKSWg= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + hash2 + + vZn/tXvSzWZPBBo0cVnIiPjRYfiMEtABs2gGlmJ3DKo= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + hash2 + + pOQG4CEenyMCs6E53Yf2+yYR99NwtjC9ESL6Hp719iM= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + oy0dHoyKmH2uV/KCHJzCagE+QIE= + + hash2 + + aXEwUfPGaGK1ndjF84VGCstTDsw+y3qn6bW9197R/wc= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + hash2 + + 8Dy4OJ7vlhUCXCV6cjyExPoQWOtUSRnuNLpDxfel5ss= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + hash2 + + oZ9SfHTeIGNZtJjH75VsT01y5Vo2tq2VCPVF8bDddeE= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + mYyXqqWSoYqVG1zNp1vopIw8r1k= + + hash2 + + j3xNys0dFAL/2iqvjfz2PopHNj9kPZSLHI5SyE8Pb5c= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + hash2 + + LK+XUVI/B5vkE00baFJQzgTVPcWQu2vfztwnjkmtAdg= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + hash2 + + c1eSzlRx9vqCBLiF84w+iiiGeii8RIOVaoC8Ds3gndI= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + uLKIwoprHw35+b4+/KP/j9X2zVg= + + hash2 + + kXFQNOUYJFVMleLIk/wvetRZoFi+Es/ChIGsKEkPdTs= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + hash2 + + GtD3UAnIT5BoshJo4areAKSruPfavkvTIyNd0gjejDM= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + hash2 + + 3KPglR1oBAj4L7IA3Y4fYKtWrk2kpbl7jPZwPCByWfo= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + tre2iSm68OK3ztgNotyXuz1MkzI= + + hash2 + + nPkeNlTVifGs1wwWJbh4cKzN6KGsoSoFWhhX8gcD+Zc= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + hash2 + + QVnq+Bn52L27jHSSWCd5PGIBzAeU4HwlTMuL0+M3JMI= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + hash2 + + R5U4ry1iLGtnxFs4Ex8GdV7tpXKbo1HoH0rsSQKIO1M= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + HB5ASms7UIZfv0WaGh6tCLBEDP8= + + hash2 + + QgdpF8+b+1E1FOKUQmo9gfa/0naxptxTvxgPAiBPnzc= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + hash2 + + iJPr/YNl1hGgBNcbjpEttX7EgNKwsu+R3oVtXyoxxV0= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + hash2 + + KtHl8n7bnlZ3Ir0ymG0RdRWNezTCdzyBh9HO0AB2TrA= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= + + hash2 + + LTWELwsYH8j9IGZy23C/qmUvJ0/E498TrPfWsXFOM8c= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/fr_CA.lproj + + symlink + fr.lproj + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + hash2 + + 2h4d04V7H95/KuIy1kBjzDQqtfxnZrRzKlxEjk3NYRo= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + hash2 + + ReDJwTVXlm8iJWToPp6haL3A35LkgyNtEYfEYQ38l+A= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + S2YV0JmEwfPtYsMBBMuvddrPEis= + + hash2 + + s4Q/66AafiJ25LzYFLwYJRMdqr1W2awMSkxlZjy9JtM= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + hash2 + + C9vH4mEYy0VzQEvjXYfCMPM4ggBQF1APABRkUOUQwPA= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + hash2 + + 8vAkRUe47lFmMm7zUZM55/XRK21KahmSbRy0Axp6gw0= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= + + hash2 + + GyEkgG0mW1s+T6Nz7aQ/eEvLYoysvr7BYots62oHX1w= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + hash2 + + QpgLimdJiwdp2DVF/DMQyJ2Zg8L2ihsreE7vcI8Uqh0= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + hash2 + + t7A1i/lrse5T6UZtyfTdB/7HRH5vPNuOj2I+QkIjAEI= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + xRBgLwOX0xZhrXGjHDHL6S+qCQc= + + hash2 + + VWBW48lOFIc7lprCjCV9s4BfRYheTgsJnhe5dnQbqOY= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + hash2 + + 5+DJlDvCzlPxtarex6vBp6GBNUjc//XUg1dP+YKgQvo= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + hash2 + + dMHKLXO9jQ/ephXEzJ8zaCiJ2TD94Xdtlfwqn0liUVo= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + o6d6uYDAajCHTJJOXT7zDECTmIM= + + hash2 + + abK36qjugFrOyJCWuehmLWtrUUaY2xV+kVIPmNJXKjA= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + hash2 + + 17Hm2P7maddnurefS4zzxjw/i66hgAqMfPCnw5etp8c= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + hash2 + + qzSS0s4cMsrK7155WvW8tp+ToVcFs5pmCwrBC9Lr/ec= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + b1mgRruuDPTLXfhBEjTV72kV1m0= + + hash2 + + 5nEPrrpTyzn6ealGjKbkHDtrZ2hvu6zXmQjlL7x8UGY= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + hash2 + + xhSLkCd/oWSClxzjFJOGmPOAS3u0od2to6thPF+1hi8= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + hash2 + + rU7gnpi3PsnD0n0noPJN7LKUwxXFHCjixcW7WHa1q2Y= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + ycrHyxLA68Lf8rq4IXBVK62UpGc= + + hash2 + + bvNquF3puRjnZvG2nQUQsz5WyXUsO3LC6BCtEjw2+9g= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + hash2 + + k7N16zCtXUz9SCO2rA2PSAb+B9zsqeF9kPAlIPeRQq0= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + hash2 + + 8g/ElSO4yIXKBPWT/sbST8vdzCsbEVIeHZSxJbCzJ4M= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + sGjRl91qI6175CwJYuqSYLYylJg= + + hash2 + + /oxFNXMvdoD2D7ykStspwXUrcEpRCaVbYebxNXyRuI4= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + hash2 + + zV9zmt6+b1fbS8Nzwh35PAkW5vdYcJ64kjUjQyde+Mo= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + hash2 + + 3I8r+QxaqIEqBcdZogXU5VuDg87Ls1S5Ss1nhmfM8Po= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + QHLHLZGOJJ4eN75aG1K1VUHNPz8= + + hash2 + + arRlqOWae64Pqn6yNw466S0RNtPGSxvbi7FEd00g1x8= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + hash2 + + PvUb+gVq9mfBJ4c4Rp3cyMOr6Fw7EqVwOSYCXdskwUA= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + hash2 + + KNo3HyQuwczwWYBLgpAwz1nk4Lo/IuCN4SZpFhn8diY= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + pZo0pXve6jqBertG5IixAzSpgV4= + + hash2 + + ILzDGh3jJc3hPKR/ADk0jiaDQaHZu7Zq8YLRXT+OXjc= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt.lproj + + symlink + pt_BR.lproj + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + hash2 + + 7Xu6H76LF/BEqK9o0LJHpt54NV3JbY/TPoinZv9GfSQ= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + hash2 + + fbgvSsPkysitkmLfP26OmYpP/r+044gpfMHqQoQp9Ns= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2dtbduILRWtmfjobyd2yOVhQNH4= + + hash2 + + 18VMio0CRtcvr8i0M+O3/t41QS15KVxSGxoVhQYapxE= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + hash2 + + L4KDh1UJEm3ta+qgzCe3s3RI7xXrAY8y5h5eqneS3Uo= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + hash2 + + NOQ75dz/Mq7PLhwssExcWXdtTFQzx8m/lLpLBDCaZCI= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5B8hYUrKag0Unyt6Uk0D2K5opL8= + + hash2 + + D42TQ5lV8E73WOOXTsUDSu2jqOFUt1+WMcLUTVab8W8= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + hash2 + + KUqcmkrRCh+XjXh9F7fudt94MreG4bKqDh1PLat/FpI= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + hash2 + + 2mIpfTwoRvVuhY/Aa3Bqw5VsjpV93xFNcBMQG83Q8DU= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + GY/ufItfyKYpgw54TfqJlPlymb0= + + hash2 + + hMvl0YhsMlkusdQxVcUiUDeQQqNn/KFwXfIqWaDSrG0= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + hash2 + + qwuyYXCO4H5IuXiRhZR3ucGl5S1CtG2e7kxxCkM8vtA= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + hash2 + + Kvykt9h+o+SUuINROlMCXornVL/uEH2Uz5Kd0bNSm6k= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + AqJRrBMp2yA+umSXxQIQVmpnCN4= + + hash2 + + 7Guwtd2PDpjTWg/qX1UODxgRfQDDxNUgN4qr4Siv/Cc= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + hash2 + + qZSxDMjtIXyNwOZYeUz2g9w6NFmKFStqy5UbKU9N3BA= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + hash2 + + fD0W6cMr/MZ0C0qrsMD3jEbXdK+eq8L7tl87ZPDspmY= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + burLhfFkzbPjAIqMXw1qKn94xm8= + + hash2 + + D5XPrSBItdufc2zVCFazX3SEmRVqQBdCZ0ADlLmHE4E= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + hash2 + + 9kzvdHcdVqdxsioITBt+Am06twXcJpKMreMKHDDj4RY= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + hash2 + + r48ahwyC8EFi+44X/EtUfWvh8QCu9klpjqwwwzQHiXE= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + lT25Wn73ZrIgcDJsgzSvIQ97TtY= + + hash2 + + L8TvfX3/5nLCXk5oNeUzyieunqgEUrKzk/t+6Ldxn/0= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + hash2 + + 2GEt92QWRuf/s8NnoWD/HidalRNoLUoeJoC7UM3Y2Ng= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + hash2 + + wIPOqj0JUePKauMIl6sT8YUjoxw1q36g+rrQvwkxfw4= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + W5vyz7ueX3DVKxQC82/3FnvJfeQ= + + hash2 + + C0r3bNAO7WAAZa4WiYUeBQMt+kmEndHyzPT1//HBsYo= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + hash2 + + gFeu5nmtM/aL1yTSsW9TPh9ZdN8r/lX5jhns53LlcfA= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + hash2 + + vmbxfC0cI7IAAQRNDQ43V6vocH93TiejQmBr23NDAlQ= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + JIh/Ueyxh8+us+26dxQRmrPiVAE= + + hash2 + + Hbc+V8H4hfQh3PucUY1S+6vXRBZErhTaZLYJ0jQooLo= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + hash2 + + 3T/cO/cEIAM8QBGVg53bYvcYseEMPOhn0C+yReVVJEU= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + hash2 + + qwss8xg3cxWWQXqCLxXd6z1ygf6MHYjMDKtVGzTpF0M= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5wxy4Op51XjVl1MvUlCnSUfvsj8= + + hash2 + + eXL2eqxEpbACqNQkixNqqVrLeqRbdnpPCndQcIXqdas= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + hash2 + + /lUZP3n163WFPUjg+ohhY7pnbmtrxuc164ew0tFmDd4= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + hash2 + + Pmyouw5QFzbN7VYg1RXFNm3IB4jOmZagTi9k8g2CyQE= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + HEubU7VtIHZcWJ6RfdC038Os1gw= + + hash2 + + 7ljWihx3qnfD/0BWdbNtzQirBF95hZ1sSXu5vTiVHe0= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + hash2 + + tu9oMdBCiHiyRSJMEmm5x3oGY3aCnmwXPsRf1hjj7fY= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + hash2 + + b6nNjM4vCBrXBbjH5GtbkPjZjJyMeSSBXO/tCRn+LFY= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + FT+kQgUNxKGrbheU8uSqkYFSHtI= + + hash2 + + 2J873kYqy/0EyE6QXT6tqYkydTGcrw3/Ncv9cnSRl7M= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + hash2 + + POFOdG9uGjcVXkx39sW2Rwl2FJ2zs6A007yF1UpDskE= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + hash2 + + AFd6Yy2xbmYtz1+r9u+r9qNka3oTzho3/n3DCwxWKoE= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + qO2OAmNcqk2/bSzwAjGcXTD4+PY= + + hash2 + + JNnNodE8g22fkHlnQzxC9vap/jefD5NkjUjmErvl940= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/Current b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/Current new file mode 120000 index 00000000..8c7e5a66 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Frameworks/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Info.plist b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Info.plist new file mode 100644 index 00000000..8a63f285 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Info.plist @@ -0,0 +1,67 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + English + CFBundleExecutable + Sparkle Test App + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon + CFBundleIdentifier + org.sparkle-project.SparkleTestApp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle Test App + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.5.1 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.5.1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSMinimumSystemVersion + 10.7 + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + SUEnableSystemProfiling + + SUFeedURL + http://localhost:1337/sparkletestcast.xml + SUPublicEDKey + eRFPLZuNM6m8bltmtpPX4fzKbufI1z6rKJHtgIIsllk= + + diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/MacOS/Sparkle Test App b/Dependencies/Sparkle/Sparkle Test App.app/Contents/MacOS/Sparkle Test App new file mode 100755 index 00000000..b7fb3be6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/MacOS/Sparkle Test App differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/PkgInfo b/Dependencies/Sparkle/Sparkle Test App.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/AppIcon.icns b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/AppIcon.icns new file mode 100644 index 00000000..359aa5b4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/AppIcon.icns differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/Assets.car b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/Assets.car new file mode 100644 index 00000000..9e6e2520 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/Assets.car differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/SUUpdateSettingsWindowController.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/SUUpdateSettingsWindowController.nib new file mode 100644 index 00000000..112cc4c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/SUUpdateSettingsWindowController.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ar.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ar.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ar.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ca.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ca.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ca.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cs.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cs.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cs.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cy.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cy.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/cy.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/da.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/da.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/da.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/de.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/de.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/de.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/el.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/el.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/el.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/MainMenu.nib b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/MainMenu.nib new file mode 100644 index 00000000..7798ccff Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/en.lproj/MainMenu.nib differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/es.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/es.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/es.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fi.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fi.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fi.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr-CA.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr-CA.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr-CA.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/fr.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/he.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/he.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/he.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hr.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hr.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hr.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hu.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hu.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/hu.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/id.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/id.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/id.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/is.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/is.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/is.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/it.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/it.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/it.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ja.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ja.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ja.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ko.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ko.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ko.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nb.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nb.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nb.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nl.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nl.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/nl.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pl.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pl.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pl.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-BR.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-BR.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-BR.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-PT.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-PT.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt-PT.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/pt.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ro.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ro.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ro.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ru.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ru.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/ru.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sk.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sk.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sk.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sl.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sl.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sl.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sparkletestcast.xml b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sparkletestcast.xml new file mode 100644 index 00000000..05010a0d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sparkletestcast.xml @@ -0,0 +1,23 @@ + + + + Sparkle Test App Changelog + Most recent changes with links to updates. + en + + Version 2.0 + + +
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • +
  • Suspendisse sed felis ac ante ultrices rhoncus. Etiam quis elit vel nibh placerat facilisis in id leo.
  • +
  • Vestibulum nec tortor odio, nec malesuada libero. Cras vel convallis nunc.
  • +
  • Suspendisse tristique massa eget velit consequat tincidunt. Praesent sodales hendrerit pretium.
  • + + ]]> +
    + Sat, 26 Jul 2014 15:20:11 +0000 + +
    +
    +
    diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sv.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sv.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/sv.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/th.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/th.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/th.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/tr.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/tr.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/tr.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/uk.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/uk.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/uk.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hant.lproj/InfoPlist.strings b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hant.lproj/InfoPlist.strings new file mode 100644 index 00000000..2aa973c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle Test App.app/Contents/Resources/zh-Hant.lproj/InfoPlist.strings differ diff --git a/Dependencies/Sparkle/Sparkle Test App.app/Contents/_CodeSignature/CodeResources b/Dependencies/Sparkle/Sparkle Test App.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..ef806bfc --- /dev/null +++ b/Dependencies/Sparkle/Sparkle Test App.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,1000 @@ + + + + + files + + Resources/AppIcon.icns + + jTBSN/DM6XbVu8Q5zA/teszX3Xc= + + Resources/Assets.car + + CqKRIzdSJsTqOaqq3Utza9cAcgQ= + + Resources/SUUpdateSettingsWindowController.nib + + AsgFbsA33n5f51idWjGdo4FpKAc= + + Resources/ar.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/ca.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/cs.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/cy.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/da.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/de.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/el.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/en.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/en.lproj/MainMenu.nib + + hash + + MFOca3mjNRm0TEBQD5MktMkVuck= + + optional + + + Resources/es.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/fi.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/fr-CA.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/fr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/he.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/hr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/hu.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/id.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/is.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/it.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/ja.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/ko.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/nb.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/nl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/pl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/pt-BR.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/pt-PT.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/pt.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/ro.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/ru.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/sk.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/sl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/sparkletestcast.xml + + nl4jSsZp7Tgu38npAXSHzoIFQc8= + + Resources/sv.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/th.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/tr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/uk.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/zh-Hans.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + Resources/zh-Hant.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + optional + + + + files2 + + Frameworks/Sparkle.framework + + cdhash + + pvnL86lTt4ubdNn5KMoA79N9DII= + + requirement + cdhash H"dbc99dfb29b01d1cfcae7d3db853b269e8e1df12" or cdhash H"a6f9cbf3a953b78b9b74d9f928ca00efd37d0c82" or cdhash H"eb212b0642030345f57d7d6f6b6c1ea095cfb6c4" or cdhash H"9c25b6327cb130977f43cbeb8f1dc88226efe4f5" + + Resources/AppIcon.icns + + hash + + jTBSN/DM6XbVu8Q5zA/teszX3Xc= + + hash2 + + m1KHUjhDToeHUHA54D1ZwVHAzFDRThD4DRzXvbqiSYA= + + + Resources/Assets.car + + hash + + CqKRIzdSJsTqOaqq3Utza9cAcgQ= + + hash2 + + g37uu5QJDD4PPlCEOo36l8dLPCFQJ2nS/llrmInNYGY= + + + Resources/SUUpdateSettingsWindowController.nib + + hash + + AsgFbsA33n5f51idWjGdo4FpKAc= + + hash2 + + q0YlT8rSpgw8FfZhikrVncoCyMSMhxR253yXo0+VnC0= + + + Resources/ar.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/ca.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/cs.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/cy.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/da.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/de.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/el.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/en.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/en.lproj/MainMenu.nib + + hash + + MFOca3mjNRm0TEBQD5MktMkVuck= + + hash2 + + 5M1Q9kfALnumtlbUv9oqUieZA8+7bN2EP7XbalKxaaU= + + optional + + + Resources/es.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/fi.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/fr-CA.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/fr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/he.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/hr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/hu.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/id.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/is.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/it.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/ja.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/ko.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/nb.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/nl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/pl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/pt-BR.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/pt-PT.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/pt.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/ro.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/ru.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/sk.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/sl.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/sparkletestcast.xml + + hash + + nl4jSsZp7Tgu38npAXSHzoIFQc8= + + hash2 + + 86UR4Kz0JzxOhClVZCf8AYbXUxJPMULCWc1ZC9qv1jM= + + + Resources/sv.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/th.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/tr.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/uk.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/zh-Hans.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + Resources/zh-Hant.lproj/InfoPlist.strings + + hash + + EXb50Tn5fxQUkxlhY3MtBSPwqvM= + + hash2 + + AniIlnyXWWnfWFDpYVp9p3hkK39jHX5OMrpmn+eoZR4= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Info.plist b/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Info.plist new file mode 100644 index 00000000..be06c406 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.org.sparkle-project.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.24.0 + CFBundleVersion + 1.24.0 + + diff --git a/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle b/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle new file mode 100644 index 00000000..ecb71b53 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework.dSYM/Contents/Resources/DWARF/Sparkle differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Headers b/Dependencies/Sparkle/Sparkle.framework/Headers new file mode 120000 index 00000000..a177d2a6 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Modules b/Dependencies/Sparkle/Sparkle.framework/Modules new file mode 120000 index 00000000..5736f318 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/PrivateHeaders b/Dependencies/Sparkle/Sparkle.framework/PrivateHeaders new file mode 120000 index 00000000..d8e56452 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/PrivateHeaders @@ -0,0 +1 @@ +Versions/Current/PrivateHeaders \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Resources b/Dependencies/Sparkle/Sparkle.framework/Resources new file mode 120000 index 00000000..953ee36f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Sparkle b/Dependencies/Sparkle/Sparkle.framework/Sparkle new file mode 120000 index 00000000..b2c52731 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h new file mode 100644 index 00000000..41cd5743 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloadData.h @@ -0,0 +1,43 @@ +// +// SPUDownloadData.h +// Sparkle +// +// Created by Mayur Pawashe on 8/10/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + * A class for containing downloaded data along with some information about it. + */ +SU_EXPORT @interface SPUDownloadData : NSObject + +- (instancetype)initWithData:(NSData *)data textEncodingName:(NSString * _Nullable)textEncodingName MIMEType:(NSString * _Nullable)MIMEType; + +/*! + * The raw data that was downloaded. + */ +@property (nonatomic, readonly) NSData *data; + +/*! + * The IANA charset encoding name if available. Eg: "utf-8" + */ +@property (nonatomic, readonly, nullable, copy) NSString *textEncodingName; + +/*! + * The MIME type if available. Eg: "text/plain" + */ +@property (nonatomic, readonly, nullable, copy) NSString *MIMEType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h new file mode 100644 index 00000000..5eee9bd5 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloader.h @@ -0,0 +1,25 @@ +// +// SPUDownloader.h +// Downloader +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SPUDownloaderProtocol.h" + +@protocol SPUDownloaderDelegate; + +// This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. +@interface SPUDownloader : NSObject + +// Due to XPC remote object reasons, this delegate is strongly referenced +// Invoke cleanup when done with this instance +- (instancetype)initWithDelegate:(id )delegate; + +@end diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h new file mode 100644 index 00000000..76e7e750 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDelegate.h @@ -0,0 +1,38 @@ +// +// SPUDownloaderDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUDownloadData; + +@protocol SPUDownloaderDelegate + +// This is only invoked for persistent downloads +- (void)downloaderDidSetDestinationName:(NSString *)destinationName temporaryDirectory:(NSString *)temporaryDirectory; + +// Under rare cases, this may be called more than once, in which case the current progress should be reset back to 0 +// This is only invoked for persistent downloads +- (void)downloaderDidReceiveExpectedContentLength:(int64_t)expectedContentLength; + +// This is only invoked for persistent downloads +- (void)downloaderDidReceiveDataOfLength:(uint64_t)length; + +// downloadData is nil if this is a persisent download, otherwise it's non-nil if it's a temporary download +- (void)downloaderDidFinishWithTemporaryDownloadData:(SPUDownloadData * _Nullable)downloadData; + +- (void)downloaderDidFailWithError:(NSError *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h new file mode 100644 index 00000000..36302df4 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderDeprecated.h @@ -0,0 +1,13 @@ +// +// SPUDownloaderDeprecated.h +// Sparkle +// +// Created by Deadpikle on 12/20/17. +// Copyright © 2017 Sparkle Project. All rights reserved. +// + +#import "SPUDownloader.h" + +@interface SPUDownloaderDeprecated : SPUDownloader + +@end diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h new file mode 100644 index 00000000..ebe477fe --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderProtocol.h @@ -0,0 +1,34 @@ +// +// SPUDownloaderProtocol.h +// PersistentDownloader +// +// Created by Mayur Pawashe on 4/1/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class SPUURLRequest; + +// The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service. +@protocol SPUDownloaderProtocol + +- (void)startPersistentDownloadWithRequest:(SPUURLRequest *)request bundleIdentifier:(NSString *)bundleIdentifier desiredFilename:(NSString *)desiredFilename; + +- (void)startTemporaryDownloadWithRequest:(SPUURLRequest *)request; + +- (void)downloadDidFinish; + +- (void)cleanup; + +- (void)cancel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h new file mode 100644 index 00000000..4bde75aa --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUDownloaderSession.h @@ -0,0 +1,20 @@ +// +// SPUDownloaderSession.h +// Sparkle +// +// Created by Deadpikle on 12/20/17. +// Copyright © 2017 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SPUDownloader.h" +#import "SPUDownloaderProtocol.h" + +NS_CLASS_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0) +@interface SPUDownloaderSession : SPUDownloader + +@end diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h new file mode 100644 index 00000000..69496147 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SPUURLRequest.h @@ -0,0 +1,35 @@ +// +// SPUURLRequest.h +// Sparkle +// +// Created by Mayur Pawashe on 5/19/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +// A class that wraps NSURLRequest and implements NSSecureCoding +// This class exists because NSURLRequest did not support NSSecureCoding in macOS 10.8 +// I have not verified if NSURLRequest in 10.9 implements NSSecureCoding or not +@interface SPUURLRequest : NSObject + +// Creates a new URL request +// Only these properties are currently tracked: +// * URL +// * Cache policy +// * Timeout interval +// * HTTP header fields +// * networkServiceType ++ (instancetype)URLRequestWithRequest:(NSURLRequest *)request; + +@property (nonatomic, readonly) NSURLRequest *request; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h new file mode 100644 index 00000000..34276b7d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcast.h @@ -0,0 +1,35 @@ +// +// SUAppcast.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCAST_H +#define SUAPPCAST_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +@class SUAppcastItem; +SU_EXPORT @interface SUAppcast : NSObject + +@property (copy, nullable) NSString *userAgentString; +@property (copy, nullable) NSDictionary *httpHeaders; + +- (void)fetchAppcastFromURL:(NSURL *)url inBackground:(BOOL)bg completionBlock:(void (^)(NSError *_Nullable))err; +- (SUAppcast *)copyWithoutDeltaUpdates; + +@property (readonly, copy, nullable) NSArray *items; +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h new file mode 100644 index 00000000..1d8b1d01 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h @@ -0,0 +1,54 @@ +// +// SUAppcastItem.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCASTITEM_H +#define SUAPPCASTITEM_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" +@class SUSignatures; + +SU_EXPORT @interface SUAppcastItem : NSObject +@property (copy, readonly) NSString *title; +@property (copy, readonly) NSString *dateString; +@property (copy, readonly) NSDate *date; +@property (copy, readonly) NSString *itemDescription; +@property (strong, readonly) NSURL *releaseNotesURL; +@property (strong, readonly) SUSignatures *signatures; +@property (copy, readonly) NSString *minimumSystemVersion; +@property (copy, readonly) NSString *maximumSystemVersion; +@property (strong, readonly) NSURL *fileURL; +@property (nonatomic, readonly) uint64_t contentLength; +@property (copy, readonly) NSString *versionString; +@property (copy, readonly) NSString *osString; +@property (copy, readonly) NSString *displayVersionString; +@property (copy, readonly) NSDictionary *deltaUpdates; +@property (strong, readonly) NSURL *infoURL; +@property (copy, readonly) NSNumber* phasedRolloutInterval; + +// Initializes with data from a dictionary provided by the RSS class. +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error; + +@property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; +@property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; +@property (getter=isMacOsUpdate, readonly) BOOL macOsUpdate; +@property (getter=isInformationOnlyUpdate, readonly) BOOL informationOnlyUpdate; + +// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. +@property (readonly, copy) NSDictionary *propertiesDictionary; + +- (NSURL *)infoURL; + +@end + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h new file mode 100644 index 00000000..3756a378 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUCodeSigningVerifier.h @@ -0,0 +1,26 @@ +// +// SUCodeSigningVerifier.h +// Sparkle +// +// Created by Andy Matuschak on 7/5/12. +// +// + +#ifndef SUCODESIGNINGVERIFIER_H +#define SUCODESIGNINGVERIFIER_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +SU_EXPORT @interface SUCodeSigningVerifier : NSObject ++ (BOOL)codeSignatureAtBundleURL:(NSURL *)oldBundlePath matchesSignatureAtBundleURL:(NSURL *)newBundlePath error:(NSError **)error; ++ (BOOL)codeSignatureIsValidAtBundleURL:(NSURL *)bundlePath error:(NSError **)error; ++ (BOOL)bundleAtURLIsCodeSigned:(NSURL *)bundlePath; ++ (NSDictionary *)codeSignatureInfoAtBundleURL:(NSURL *)bundlePath; +@end + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h new file mode 100644 index 00000000..4b160c4f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUErrors.h @@ -0,0 +1,57 @@ +// +// SUErrors.h +// Sparkle +// +// Created by C.W. Betts on 10/13/14. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUERRORS_H +#define SUERRORS_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +/** + * Error domain used by Sparkle + */ +SU_EXPORT extern NSString *const SUSparkleErrorDomain; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +typedef NS_ENUM(OSStatus, SUError) { + // Appcast phase errors. + SUAppcastParseError = 1000, + SUNoUpdateError = 1001, + SUAppcastError = 1002, + SURunningFromDiskImageError = 1003, + SURunningTranslocated = 1004, + + // Download phase errors. + SUTemporaryDirectoryError = 2000, + SUDownloadError = 2001, + + // Extraction phase errors. + SUUnarchivingError = 3000, + SUSignatureError = 3001, + + // Installation phase errors. + SUFileCopyFailure = 4000, + SUAuthenticationFailure = 4001, + SUMissingUpdateError = 4002, + SUMissingInstallerToolError = 4003, + SURelaunchError = 4004, + SUInstallationError = 4005, + SUDowngradeError = 4006, + SUInstallationCancelledError = 4007, + + // System phase errors + SUSystemPowerOffError = 5000 +}; +#pragma clang diagnostic pop + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h new file mode 100644 index 00000000..3e3f8a16 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUExport.h @@ -0,0 +1,18 @@ +// +// SUExport.h +// Sparkle +// +// Created by Jake Petroules on 2014-08-23. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUEXPORT_H +#define SUEXPORT_H + +#ifdef BUILDING_SPARKLE +#define SU_EXPORT __attribute__((visibility("default"))) +#else +#define SU_EXPORT +#endif + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h new file mode 100644 index 00000000..ed11921a --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h @@ -0,0 +1,52 @@ +// +// SUStandardVersionComparator.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUSTANDARDVERSIONCOMPARATOR_H +#define SUSTANDARDVERSIONCOMPARATOR_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + Sparkle's default version comparator. + + This comparator is adapted from MacPAD, by Kevin Ballard. + It's "dumb" in that it does essentially string comparison, + in components split by character type. +*/ +SU_EXPORT @interface SUStandardVersionComparator : NSObject + +/*! + Initializes a new instance of the standard version comparator. + */ +- (instancetype)init; + +/*! + Returns a singleton instance of the comparator. + + It is usually preferred to alloc/init new a comparator instead. +*/ ++ (SUStandardVersionComparator *)defaultComparator; + +/*! + Compares version strings through textual analysis. + + See the implementation for more details. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h new file mode 100644 index 00000000..d05270f2 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdater.h @@ -0,0 +1,233 @@ +// +// SUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUPDATER_H +#define SUUPDATER_H + +#if __has_feature(modules) +@import Cocoa; +#else +#import +#endif +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" + +@class SUAppcastItem, SUAppcast; + +@protocol SUUpdaterDelegate; + +/*! + The main API in Sparkle for controlling the update mechanism. + + This class is used to configure the update paramters as well as manually + and automatically schedule and control checks for updates. + */ +SU_EXPORT @interface SUUpdater : NSObject + +@property (unsafe_unretained) IBOutlet id delegate; + +/*! + The shared updater for the main bundle. + + This is equivalent to passing [NSBundle mainBundle] to SUUpdater::updaterForBundle: + */ ++ (SUUpdater *)sharedUpdater; + +/*! + The shared updater for a specified bundle. + + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ ++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle; + +/*! + Designated initializer for SUUpdater. + + If an updater has already been initialized for the provided bundle, that shared instance will be returned. + */ +- (instancetype)initForBundle:(NSBundle *)bundle; + +/*! + Explicitly checks for updates and displays a progress dialog while doing so. + + This method is meant for a main menu item. + Connect any menu item to this action in Interface Builder, + and Sparkle will check for updates and report back its findings verbosely + when it is invoked. + + This will find updates that the user has opted into skipping. + */ +- (IBAction)checkForUpdates:(id)sender; + +/*! + The menu item validation used for the -checkForUpdates: action + */ +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem; + +/*! + Checks for updates, but does not display any UI unless an update is found. + + This is meant for programmatically initating a check for updates. That is, + it will display no UI unless it actually finds an update, in which case it + proceeds as usual. + + If automatic downloading of updates it turned on and allowed, however, + this will invoke that behavior, and if an update is found, it will be downloaded + in the background silently and will be prepped for installation. + + This will not find updates that the user has opted into skipping. + */ +- (void)checkForUpdatesInBackground; + +/*! + A property indicating whether or not to check for updates automatically. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property BOOL automaticallyChecksForUpdates; + +/*! + A property indicating whether or not updates can be automatically downloaded in the background. + + Note that automatic downloading of updates can be disallowed by the developer + or by the user's system if silent updates cannot be done (eg: if they require authentication). + In this case, -automaticallyDownloadsUpdates will return NO regardless of how this property is set. + + Setting this property will persist in the host bundle's user defaults. + */ +@property BOOL automaticallyDownloadsUpdates; + +/*! + A property indicating the current automatic update check interval. + + Setting this property will persist in the host bundle's user defaults. + The update schedule cycle will be reset in a short delay after the property's new value is set. + This is to allow reverting this property without kicking off a schedule change immediately + */ +@property NSTimeInterval updateCheckInterval; + +/*! + Begins a "probing" check for updates which will not actually offer to + update to that version. + + However, the delegate methods + SUUpdaterDelegate::updater:didFindValidUpdate: and + SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, + so you can use that information in your UI. + + Updates that have been skipped by the user will not be found. + */ +- (void)checkForUpdateInformation; + +/*! + The URL of the appcast used to download update information. + + Setting this property will persist in the host bundle's user defaults. + If you don't want persistence, you may want to consider instead implementing + SUUpdaterDelegate::feedURLStringForUpdater: or SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + + This property must be called on the main thread. + */ +@property (copy) NSURL *feedURL; + +/*! + The host bundle that is being updated. + */ +@property (readonly, strong) NSBundle *hostBundle; + +/*! + The bundle this class (SUUpdater) is loaded into. + */ +@property (strong, readonly) NSBundle *sparkleBundle; + +/*! + The user agent used when checking for updates. + + The default implementation can be overrided. + */ +@property (nonatomic, copy) NSString *userAgentString; + +/*! + The HTTP headers used when checking for updates. + + The keys of this dictionary are HTTP header fields (NSString) and values are corresponding values (NSString) + */ +@property (copy) NSDictionary *httpHeaders; + +/*! + A property indicating whether or not the user's system profile information is sent when checking for updates. + + Setting this property will persist in the host bundle's user defaults. + */ +@property BOOL sendsSystemProfile; + +/*! + A property indicating the decryption password used for extracting updates shipped as Apple Disk Images (dmg) + */ +@property (nonatomic, copy) NSString *decryptionPassword; + +/*! + This function ignores normal update schedule, ignores user preferences, + and interrupts users with an unwanted immediate app update. + + WARNING: this function should not be used in regular apps. This function + is a user-unfriendly hack only for very special cases, like unstable + rapidly-changing beta builds that would not run correctly if they were + even one day out of date. + + Instead of this function you should set `SUAutomaticallyUpdate` to `YES`, + which will gracefully install updates when the app quits. + + For UI-less/daemon apps that aren't usually quit, instead of this function, + you can use the delegate method + SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: + or + SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationBlock: + to immediately start installation when an update was found. + + A progress dialog is shown but the user will never be prompted to read the + release notes. + + This function will cause update to be downloaded twice if automatic updates are + enabled. + + You may want to respond to the userDidCancelDownload delegate method in case + the user clicks the "Cancel" button while the update is downloading. + */ +- (void)installUpdatesIfAvailable; + +/*! + Returns the date of last update check. + + \returns \c nil if no check has been performed. + */ +@property (readonly, copy) NSDate *lastUpdateCheckDate; + +/*! + Appropriately schedules or cancels the update checking timer according to + the preferences for time interval and automatic checks. + + This call does not change the date of the next check, + but only the internal NSTimer. + */ +- (void)resetUpdateCycle; + +/*! + A property indicating whether or not an update is in progress. + + Note this property is not indicative of whether or not user initiated updates can be performed. + Use SUUpdater::validateMenuItem: for that instead. + */ +@property (readonly) BOOL updateInProgress; + +@end + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h new file mode 100644 index 00000000..ec844d04 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUUpdaterDelegate.h @@ -0,0 +1,352 @@ +// +// SUUpdaterDelegate.h +// Sparkle +// +// Created by Mayur Pawashe on 12/25/16. +// Copyright © 2016 Sparkle Project. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif + +#import "SUExport.h" + +@protocol SUVersionComparison, SUVersionDisplay; +@class SUUpdater, SUAppcast, SUAppcastItem; + +NS_ASSUME_NONNULL_BEGIN + +// ----------------------------------------------------------------------------- +// SUUpdater Notifications for events that might be interesting to more than just the delegate +// The updater will be the notification object +// ----------------------------------------------------------------------------- +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; +#define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; +#define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; + +// Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; +// Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; + +// ----------------------------------------------------------------------------- +// SUUpdater Delegate: +// ----------------------------------------------------------------------------- + +/*! + Provides methods to control the behavior of an SUUpdater object. + */ +@protocol SUUpdaterDelegate +@optional + +/*! + Returns whether to allow Sparkle to pop up. + + For example, this may be used to prevent Sparkle from interrupting a setup assistant. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; + +/*! + Returns additional parameters to append to the appcast URL's query string. + + This is potentially based on whether or not Sparkle will also be sending along the system profile. + + \param updater The SUUpdater instance. + \param sendingProfile Whether the system profile will also be sent. + + \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. + */ +- (NSArray *> *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +/*! + Returns a custom appcast URL. + + Override this to dynamically specify the entire URL. + + An alternative may be to use SUUpdaterDelegate::feedParametersForUpdater:sendingSystemProfile: + and let the server handle what kind of feed to provide. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)feedURLStringForUpdater:(SUUpdater *)updater; + +/*! + Returns whether Sparkle should prompt the user about automatic update checks. + + Use this to override the default behavior. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; + +/*! + Called after Sparkle has downloaded the appcast from the remote server. + + Implement this if you want to do some special handling with the appcast once it finishes loading. + + \param updater The SUUpdater instance. + \param appcast The appcast that was downloaded from the remote server. + */ +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +/*! + Returns the item in the appcast corresponding to the update that should be installed. + + If you're using special logic or extensions in your appcast, + implement this to use your own logic for finding a valid update, if any, + in the given appcast. + + \param appcast The appcast that was downloaded from the remote server. + \param updater The SUUpdater instance. + */ +- (nullable SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; + +/*! + Called when a valid update is found by the update driver. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + +/*! + Called just before the scheduled update driver prompts the user to install an update. + + \param updater The SUUpdater instance. + + \return YES to allow the update prompt to be shown (the default behavior), or NO to suppress it. + */ +- (BOOL)updaterShouldShowUpdateAlertForScheduledUpdate:(SUUpdater *)updater forItem:(SUAppcastItem *)item; + +/*! + Called after the user dismisses the update alert. + + \param updater The SUUpdater instance. + \param permanently YES if the alert will not appear again for this update; NO if it may reappear. + */ +- (void)updater:(SUUpdater *)updater didDismissUpdateAlertPermanently:(BOOL)permanently forItem:(SUAppcastItem *)item; + +/*! + Called when a valid update is not found. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidNotFindUpdate:(SUUpdater *)updater; + +/*! + Called when the user clicks the Skip This Version button. + + \param updater The SUUpdater instance. + */ +- (void)updater:(SUUpdater *)updater userDidSkipThisVersion:(SUAppcastItem *)item; + +/*! + Called immediately before downloading the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be downloaded. + \param request The mutable URL request that will be used to download the update. + */ +- (void)updater:(SUUpdater *)updater willDownloadUpdate:(SUAppcastItem *)item withRequest:(NSMutableURLRequest *)request; + +/*! + Called immediately after succesfull download of the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been downloaded. + */ +- (void)updater:(SUUpdater *)updater didDownloadUpdate:(SUAppcastItem *)item; + +/*! + Called after the specified update failed to download. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that failed to download. + \param error The error generated by the failed download. + */ +- (void)updater:(SUUpdater *)updater failedToDownloadUpdate:(SUAppcastItem *)item error:(NSError *)error; + +/*! + Called when the user clicks the cancel button while and update is being downloaded. + + \param updater The SUUpdater instance. + */ +- (void)userDidCancelDownload:(SUUpdater *)updater; + +/*! + Called immediately before extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be extracted. + */ +- (void)updater:(SUUpdater *)updater willExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately after extracting the specified downloaded update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that has been extracted. + */ +- (void)updater:(SUUpdater *)updater didExtractUpdate:(SUAppcastItem *)item; + +/*! + Called immediately before installing the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation The invocation that must be completed with `[invocation invoke]` before continuing with the relaunch. + + \return \c YES to delay the relaunch until \p invocation is invoked. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + This method acts as a simpler alternative to SUUpdaterDelegate::updater:shouldPostponeRelaunchForUpdate:untilInvoking: avoiding usage of NSInvocation, which is not available in Swift environments. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + + \return \c YES to delay the relaunch. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the application should be relaunched at all. + + Some apps \b cannot be relaunched under certain circumstances. + This method can be used to explicitly prevent a relaunch. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately before relaunching. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately after relaunching. SUUpdater delegate must be set before applicationDidFinishLaunching: to catch this event. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidRelaunchApplication:(SUUpdater *)updater; + +/*! + Returns an object that compares version numbers to determine their arithmetic relation to each other. + + This method allows you to provide a custom version comparator. + If you don't implement this method or return \c nil, + the standard version comparator will be used. + + \sa SUStandardVersionComparator + + \param updater The SUUpdater instance. + */ +- (nullable id)versionComparatorForUpdater:(SUUpdater *)updater; + +/*! + Returns an object that formats version numbers for display to the user. + + If you don't implement this method or return \c nil, + the standard version formatter will be used. + + \sa SUUpdateAlert + + \param updater The SUUpdater instance. + */ +- (nullable id)versionDisplayerForUpdater:(SUUpdater *)updater; + +/*! + Returns the path which is used to relaunch the client after the update is installed. + + The default is the path of the host bundle. + + \param updater The SUUpdater instance. + */ +- (nullable NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; + +/*! + Called before an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillShowModalAlert:(SUUpdater *)updater; + +/*! + Called after an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidShowModalAlert:(SUUpdater *)updater; + +/*! + Called when an update is scheduled to be silently installed on quit. + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; + +/*! + Called when an update is scheduled to be silently installed on quit. + This is after an update has been automatically downloaded in the background. + (i.e. SUUpdater::automaticallyDownloadsUpdates is YES) + This method acts as a more modern alternative to SUUpdaterDelegate::updater:willInstallUpdateOnQuit:immediateInstallationInvocation: using a block instead of NSInvocation, which is not available in Swift environments. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param installationBlock Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationBlock:(void (^)(void))installationBlock; + +/*! + Calls after an update that was scheduled to be silently installed on quit has been canceled. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that was proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; + +/*! + Called after an update is aborted due to an error. + + \param updater The SUUpdater instance. + \param error The error that caused the abort + */ +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h new file mode 100644 index 00000000..c654fc4d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h @@ -0,0 +1,37 @@ +// +// SUVersionComparisonProtocol.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUVERSIONCOMPARISONPROTOCOL_H +#define SUVERSIONCOMPARISONPROTOCOL_H + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +NS_ASSUME_NONNULL_BEGIN + +/*! + Provides version comparison facilities for Sparkle. +*/ +@protocol SUVersionComparison + +/*! + An abstract method to compare two version strings. + + Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, + and NSOrderedSame if they are equivalent. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! + +@end + +NS_ASSUME_NONNULL_END +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h new file mode 100644 index 00000000..980efb3f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h @@ -0,0 +1,29 @@ +// +// SUVersionDisplayProtocol.h +// EyeTV +// +// Created by Uli Kusterer on 08.12.09. +// Copyright 2009 Elgato Systems GmbH. All rights reserved. +// + +#if __has_feature(modules) +@import Foundation; +#else +#import +#endif +#import "SUExport.h" + +/*! + Applies special display formatting to version numbers. +*/ +@protocol SUVersionDisplay + +/*! + Formats two version strings. + + Both versions are provided so that important distinguishing information + can be displayed while also leaving out unnecessary/confusing parts. +*/ +- (void)formatVersion:(NSString *_Nonnull*_Nonnull)inOutVersionA andVersion:(NSString *_Nonnull*_Nonnull)inOutVersionB; + +@end diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h new file mode 100644 index 00000000..1085d419 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -0,0 +1,39 @@ +// +// Sparkle.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SPARKLE_H +#define SPARKLE_H + +// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless +// there are name-space collisions) so we can list all of them to start with: + +#pragma clang diagnostic push +// Do not use <> style includes since 2.x has two frameworks that need to work: Sparkle and SparkleCore +#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header" + +#import "SUAppcast.h" +#import "SUAppcastItem.h" +#import "SUStandardVersionComparator.h" +#import "SUUpdater.h" +#import "SUUpdaterDelegate.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" +#import "SUErrors.h" + +#import "SPUDownloader.h" +#import "SPUDownloaderDelegate.h" +#import "SPUDownloaderDeprecated.h" +#import "SPUDownloadData.h" +#import "SPUDownloaderProtocol.h" +#import "SPUDownloaderSession.h" +#import "SPUURLRequest.h" +#import "SUCodeSigningVerifier.h" + +#pragma clang diagnostic pop + +#endif diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 00000000..af3fe6d0 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Sparkle { + umbrella header "Sparkle.h" + + export * + module * { export * } +} diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h b/Dependencies/Sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h new file mode 100644 index 00000000..a52bf5a2 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/PrivateHeaders/SUUnarchiver.h @@ -0,0 +1,21 @@ +// +// SUUnarchiver.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol SUUnarchiverProtocol; + +@interface SUUnarchiver : NSObject + ++ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist new file mode 100644 index 00000000..676181f9 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -0,0 +1,56 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + English + CFBundleExecutable + Autoupdate + CFBundleIconFile + AppIcon.icns + CFBundleIdentifier + org.sparkle-project.Sparkle.Autoupdate + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.24.0 a-67-g0e162c98 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSBackgroundOnly + 1 + LSMinimumSystemVersion + 10.7 + LSUIElement + 1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate new file mode 100755 index 00000000..164511d4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop new file mode 100755 index 00000000..29dae8a1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/fileop differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns new file mode 100644 index 00000000..7f2a571c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..bb4125f0 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,860 @@ + + + + + files + + Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + MacOS/fileop + + cdhash + + JwkAFJqL9xY1mTI+1Kki3oSFsik= + + requirement + cdhash H"d5bc45cc18a448c02d5c4dd6859a64524a5b8a85" or cdhash H"270900149a8bf7163599323ed4a922de8485b229" or cdhash H"1b27242b81a5a51561703e2bb8a5e01acac436e9" or cdhash H"ead0c4c63eafc5d32327f0dbf958b7bd0993ec75" + + Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/DarkAqua.css b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/DarkAqua.css new file mode 100644 index 00000000..a41e0f28 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/DarkAqua.css @@ -0,0 +1,9 @@ +html { + color: #FFFFFFD8; +} +:link { + color: #419CFF; +} +:link:active { + color: #FF1919; +} diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist new file mode 100644 index 00000000..8786d487 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 20B28 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Sparkle + CFBundleIdentifier + org.sparkle-project.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.24.0 a-67-g0e162c98 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1.24.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 12C5020f + DTPlatformName + macosx + DTPlatformVersion + 11.1 + DTSDKBuild + 20C5048g + DTSDKName + macosx11.1 + DTXcode + 1230 + DTXcodeBuild + 12C5020f + LSMinimumSystemVersion + 10.7 + + diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist new file mode 100644 index 00000000..1f75b248 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -0,0 +1,314 @@ + + + + + ADP2,1 + Developer Transition Kit + iMac1,1 + iMac G3 (Rev A-D) + iMac4,1 + iMac (Core Duo) + iMac4,2 + iMac for Education (17 inch, Core Duo) + iMac5,1 + iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) + iMac5,2 + iMac (Core 2 Duo, 17 inch, Combo Drive) + iMac6,1 + iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac7,1 + iMac Intel Core 2 Duo (aluminum enclosure) + iMac8,1 + iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) + iMac9,1 + iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) + iMac10,1 + iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) + iMac11,1 + iMac (Core i5 or i7, 27 inch Late 2009) + iMac11,2 + 21.5" iMac (mid 2010) + iMac11,3 + iMac (Core i5 or i7, 27 inch Mid 2010) + iMac12,1 + iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) + iMac12,2 + iMac (Core i5 or i7, 27 inch Mid 2011) + iMac13,1 + iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) + iMac13,2 + iMac (Core i5 or i7, 27 inch Late 2012) + iMac14,1 + iMac (Core i5, 21.5 inch Late 2013) + iMac14,2 + iMac (Core i5 or i7, 27 inch Late 2013) + iMac14,3 + iMac (Core i5 or i7, 21.5 inch Late 2013) + iMac14,4 + iMac (Core i5, 21.5 inch Mid 2014) + iMac15,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2014 or Mid 2015) + iMac16,1 + iMac (Core i5, 21,5 inch Late 2015) + iMac16,2 + iMac (Retina 4K Core i5 or i7, 21.5 inch Late 2015) + iMac17,1 + iMac (Retina 5K Core i5 or i7, 27 inch Late 2015) + MacBook1,1 + MacBook (Core Duo) + MacBook2,1 + MacBook (Core 2 Duo) + MacBook4,1 + MacBook (Core 2 Duo Feb 2008) + MacBook5,1 + MacBook (Core 2 Duo, Late 2008, Unibody) + MacBook5,2 + MacBook (Core 2 Duo, Early 2009, White) + MacBook6,1 + MacBook (Core 2 Duo, Late 2009, Unibody) + MacBook7,1 + MacBook (Core 2 Duo, Mid 2010, White) + MacBook8,1 + MacBook (Core M, 12 inch, Early 2015) + MacBookAir1,1 + MacBook Air (Core 2 Duo, 13 inch, Early 2008) + MacBookAir2,1 + MacBook Air (Core 2 Duo, 13 inch, Mid 2009) + MacBookAir3,1 + MacBook Air (Core 2 Duo, 11 inch, Late 2010) + MacBookAir3,2 + MacBook Air (Core 2 Duo, 13 inch, Late 2010) + MacBookAir4,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2011) + MacBookAir4,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2011) + MacBookAir5,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2012) + MacBookAir5,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2012) + MacBookAir6,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) + MacBookAir6,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) + MacBookAir7,1 + MacBook Air (Core i5 or i7, 11 inch, Early 2015) + MacBookAir7,2 + MacBook Air (Core i5 or i7, 13 inch, Early 2015) + MacBookPro1,1 + MacBook Pro Core Duo (15-inch) + MacBookPro1,2 + MacBook Pro Core Duo (17-inch) + MacBookPro2,1 + MacBook Pro Core 2 Duo (17-inch) + MacBookPro2,2 + MacBook Pro Core 2 Duo (15-inch) + MacBookPro3,1 + MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) + MacBookPro3,2 + MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) + MacBookPro4,1 + MacBook Pro (Core 2 Duo Feb 2008) + MacBookPro5,1 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,2 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,3 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,4 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro5,5 + MacBook Pro Intel Core 2 Duo (aluminum unibody) + MacBookPro6,1 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro6,2 + MacBook Pro Intel Core i5, Intel Core i7 (mid 2010) + MacBookPro7,1 + MacBook Pro Intel Core 2 Duo (mid 2010) + MacBookPro8,1 + MacBook Pro Intel Core i5, Intel Core i7, 13" (early 2011) + MacBookPro8,2 + MacBook Pro Intel Core i7, 15" (early 2011) + MacBookPro8,3 + MacBook Pro Intel Core i7, 17" (early 2011) + MacBookPro9,1 + MacBook Pro (15-inch, Mid 2012) + MacBookPro9,2 + MacBook Pro (13-inch, Mid 2012) + MacBookPro10,1 + MacBook Pro (Retina, Mid 2012) + MacBookPro10,2 + MacBook Pro (Retina, 13-inch, Late 2012) + MacBookPro11,1 + MacBook Pro (Retina, 13-inch, Late 2013) + MacBookPro11,2 + MacBook Pro (Retina, 15-inch, Late 2013) + MacBookPro11,3 + MacBook Pro (Retina, 15-inch, Late 2013) + MacbookPro11,4 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro11,5 + MacBook Pro (Retina, 15-inch, Mid 2015) + MacbookPro12,1  + MacBook Pro (Retina, 13-inch, Early 2015) + Macmini1,1 + Mac Mini (Core Solo/Duo) + Macmini2,1 + Mac mini Intel Core + Macmini3,1 + Mac mini Intel Core + Macmini4,1 + Mac mini Intel Core (Mid 2010) + Macmini5,1 + Mac mini (Core i5, Mid 2011) + Macmini5,2 + Mac mini (Core i5 or Core i7, Mid 2011) + Macmini5,3 + Mac mini (Core i7, Server, Mid 2011) + Macmini6,1 + Mac mini (Core i5, Late 2012) + Macmini6,2 + Mac mini (Core i7, Normal or Server, Late 2012) + Macmini7,1 + Mac mini (Core i5 or Core i7, Late 2014) + MacPro1,1,Quad + Mac Pro + MacPro1,1 + Mac Pro (four-core) + MacPro2,1 + Mac Pro (eight-core) + MacPro3,1 + Mac Pro (January 2008 4- or 8- core "Harpertown") + MacPro4,1 + Mac Pro (March 2009) + MacPro5,1 + Mac Pro (2010 or 2012) + MacPro6,1 + Mac Pro (Late 2013) + PowerBook1,1 + PowerBook G3 + PowerBook2,1 + iBook G3 + PowerBook2,2 + iBook G3 (FireWire) + PowerBook2,3 + iBook G3 + PowerBook2,4 + iBook G3 + PowerBook3,1 + PowerBook G3 (FireWire) + PowerBook3,2 + PowerBook G4 + PowerBook3,3 + PowerBook G4 (Gigabit Ethernet) + PowerBook3,4 + PowerBook G4 (DVI) + PowerBook3,5 + PowerBook G4 (1GHz / 867MHz) + PowerBook4,1 + iBook G3 (Dual USB, Late 2001) + PowerBook4,2 + iBook G3 (16MB VRAM) + PowerBook4,3 + iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) + PowerBook5,1 + PowerBook G4 (17 inch) + PowerBook5,2 + PowerBook G4 (15 inch FW 800) + PowerBook5,3 + PowerBook G4 (17-inch 1.33GHz) + PowerBook5,4 + PowerBook G4 (15 inch 1.5/1.33GHz) + PowerBook5,5 + PowerBook G4 (17-inch 1.5GHz) + PowerBook5,6 + PowerBook G4 (15 inch 1.67GHz/1.5GHz) + PowerBook5,7 + PowerBook G4 (17-inch 1.67GHz) + PowerBook5,8 + PowerBook G4 (Double layer SD, 15 inch) + PowerBook5,9 + PowerBook G4 (Double layer SD, 17 inch) + PowerBook6,1 + PowerBook G4 (12 inch) + PowerBook6,2 + PowerBook G4 (12 inch, DVI) + PowerBook6,3 + iBook G4 + PowerBook6,4 + PowerBook G4 (12 inch 1.33GHz) + PowerBook6,5 + iBook G4 (Early-Late 2004) + PowerBook6,7 + iBook G4 (Mid 2005) + PowerBook6,8 + PowerBook G4 (12 inch 1.5GHz) + PowerMac1,1 + Power Macintosh G3 (Blue & White) + PowerMac1,2 + Power Macintosh G4 (PCI Graphics) + PowerMac2,1 + iMac G3 (Slot-loading CD-ROM) + PowerMac2,2 + iMac G3 (Summer 2000) + PowerMac3,1 + Power Macintosh G4 (AGP Graphics) + PowerMac3,2 + Power Macintosh G4 (AGP Graphics) + PowerMac3,3 + Power Macintosh G4 (Gigabit Ethernet) + PowerMac3,4 + Power Macintosh G4 (Digital Audio) + PowerMac3,5 + Power Macintosh G4 (Quick Silver) + PowerMac3,6 + Power Macintosh G4 (Mirrored Drive Door) + PowerMac4,1 + iMac G3 (Early/Summer 2001) + PowerMac4,2 + iMac G4 (Flat Panel) + PowerMac4,4 + eMac + PowerMac4,5 + iMac G4 (17-inch Flat Panel) + PowerMac5,1 + Power Macintosh G4 Cube + PowerMac5,2 + Power Mac G4 Cube + PowerMac6,1 + iMac G4 (USB 2.0) + PowerMac6,3 + iMac G4 (20-inch Flat Panel) + PowerMac6,4 + eMac (USB 2.0, 2005) + PowerMac7,2 + Power Macintosh G5 + PowerMac7,3 + Power Macintosh G5 + PowerMac8,1 + iMac G5 + PowerMac8,2 + iMac G5 (Ambient Light Sensor) + PowerMac9,1 + Power Macintosh G5 (Late 2005) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) + RackMac1,1 + Xserve G4 + RackMac1,2 + Xserve G4 (slot-loading, cluster node) + RackMac3,1 + Xserve G5 + Xserve1,1 + Xserve (Intel Xeon) + Xserve2,1 + Xserve (January 2008 quad-core) + Xserve3,1 + Xserve (early 2009) + + diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib new file mode 100644 index 00000000..f9c39a03 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..65f118ac Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..44b6741d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..4f215f09 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 00000000..e00af341 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings new file mode 100644 index 00000000..f2aea275 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ca.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..103886fe Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..68e44511 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..465e87dd Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 00000000..02e077cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..d22bba56 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dc1aa3fa Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3515d02c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings new file mode 100644 index 00000000..e0957c60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cf626429 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..25873443 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..37c88052 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings new file mode 100644 index 00000000..202e70b7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ddcb7b3a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0085ef14 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..8668aa87 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings new file mode 100644 index 00000000..6ef15d4a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5732e8f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..352a9a84 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..8ef47f95 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings new file mode 100644 index 00000000..1d70063c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..01694b25 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9216cb74 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..49fce208 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings new file mode 100644 index 00000000..8a083f86 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..86011659 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..795d05d3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..5cb6f89c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings new file mode 100644 index 00000000..773f7c99 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fi.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74c1e5a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..dea32c6d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..06a9205b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 00000000..954abee0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr_CA.lproj new file mode 120000 index 00000000..f9834a39 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/fr_CA.lproj @@ -0,0 +1 @@ +fr.lproj \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings new file mode 100644 index 00000000..ce63fdef Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/he.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..12476cc0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..60659fe7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..3837f8af Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings new file mode 100644 index 00000000..ab8fe1a3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd46e650 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..b2400c73 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a4791c6b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings new file mode 100644 index 00000000..d30ef64b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/hu.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..2e2c6025 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..43990e17 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..00c3b4cf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings new file mode 100644 index 00000000..5f6ace28 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..fb389d23 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..7eea1a8d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..966065f6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings new file mode 100644 index 00000000..5b4be9ea Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9200a22c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..abe1d0bf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..6ec0cbdf Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 00000000..f4685eda Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..b2942676 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d5e01ade Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..db6622c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 00000000..f008e1ee Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..cbc7a9fd Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..3a875f2b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..9dc8dc71 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 00000000..fa4cd97d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..96e672b3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..ccec5f8f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..2b64274f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 00000000..76f3556b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..aa19d7e3 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4fcb4eec Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..aced373c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 00000000..4444f338 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt.lproj b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt.lproj new file mode 120000 index 00000000..3c1c9f6d --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt.lproj @@ -0,0 +1 @@ +pt_BR.lproj \ No newline at end of file diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0e8fdc60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0a899437 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..c9bd5783 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 00000000..2a7ce299 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..7056ce7b Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..d77700f0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..554151eb Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 00000000..18a287e8 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..c5907ff1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..9e45a85c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..56c26903 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 00000000..967a4418 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..9889fb2d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..4743f8f9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..e0d48aa4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 00000000..8a11ecf1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..912d1dd9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..0aa042e4 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..a7119055 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 00000000..65aa28f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..74526023 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..96b5964a Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..12fcd8c1 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 00000000..caaf0603 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..ae7ceb14 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..07518c34 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..6d16cc67 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 00000000..e7c70db7 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..0ee49a1f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..5241b1f2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..d66c6ff0 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings new file mode 100644 index 00000000..058b4ba6 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..24605f17 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..e722b695 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..5c8a7d84 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 00000000..ffc57672 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..dd540a60 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..8ebec67d Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..dd31811c Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 00000000..263326c9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..1cb9d707 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..2c066da9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..1e5b4eb2 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 00000000..71cf325f Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 00000000..3c202810 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib new file mode 100644 index 00000000..27d22201 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 00000000..79433652 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 00000000..b9517885 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/Sparkle b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 00000000..a89d9ae9 Binary files /dev/null and b/Dependencies/Sparkle/Sparkle.framework/Versions/A/Sparkle differ diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources b/Dependencies/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources new file mode 100644 index 00000000..beb3429f --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/A/_CodeSignature/CodeResources @@ -0,0 +1,3900 @@ + + + + + files + + Resources/Autoupdate.app/Contents/Info.plist + + vcMwRErKskmkeyJJVI3Z3sof/sE= + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + +dZZmoYTS+EoOzyj5DHipiL7vdA= + + Resources/Autoupdate.app/Contents/MacOS/fileop + + rsEt+sjfXAsfWD9h538L+27pg/s= + + Resources/Autoupdate.app/Contents/PkgInfo + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + VHoMteoopEbbYVG87rxkm0sjkA4= + + Resources/DarkAqua.css + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + Resources/Info.plist + + Vjt5kOIAFdWzUgFgF/aP2v3owEc= + + Resources/SUModelTranslation.plist + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + Resources/SUStatus.nib + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + Heb65H1UseXl7rEaFwVxKauBWnI= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + oy0dHoyKmH2uV/KCHJzCagE+QIE= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + mYyXqqWSoYqVG1zNp1vopIw8r1k= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + uLKIwoprHw35+b4+/KP/j9X2zVg= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + tre2iSm68OK3ztgNotyXuz1MkzI= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + HB5ASms7UIZfv0WaGh6tCLBEDP8= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + optional + + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + S2YV0JmEwfPtYsMBBMuvddrPEis= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + xRBgLwOX0xZhrXGjHDHL6S+qCQc= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + o6d6uYDAajCHTJJOXT7zDECTmIM= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + b1mgRruuDPTLXfhBEjTV72kV1m0= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + ycrHyxLA68Lf8rq4IXBVK62UpGc= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + sGjRl91qI6175CwJYuqSYLYylJg= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + QHLHLZGOJJ4eN75aG1K1VUHNPz8= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + pZo0pXve6jqBertG5IixAzSpgV4= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + optional + + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2dtbduILRWtmfjobyd2yOVhQNH4= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5B8hYUrKag0Unyt6Uk0D2K5opL8= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + GY/ufItfyKYpgw54TfqJlPlymb0= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + AqJRrBMp2yA+umSXxQIQVmpnCN4= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + burLhfFkzbPjAIqMXw1qKn94xm8= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + lT25Wn73ZrIgcDJsgzSvIQ97TtY= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + W5vyz7ueX3DVKxQC82/3FnvJfeQ= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + JIh/Ueyxh8+us+26dxQRmrPiVAE= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5wxy4Op51XjVl1MvUlCnSUfvsj8= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + HEubU7VtIHZcWJ6RfdC038Os1gw= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + FT+kQgUNxKGrbheU8uSqkYFSHtI= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + qO2OAmNcqk2/bSzwAjGcXTD4+PY= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + optional + + + + files2 + + Headers/SPUDownloadData.h + + hash + + 0uirAEMDYuzqSh3dbtfncvlTpvo= + + hash2 + + DkOXqPnwmXR6QeDI4ja3DtFxDW5L3Dcjf21hEPWImoY= + + + Headers/SPUDownloader.h + + hash + + OAA6tKxz1g4V1SQZaj3RPNmNMzo= + + hash2 + + OEDz5kY4WgtWuZ8SApjyqvVDEf3iEj7Sg2uhl+3J+jk= + + + Headers/SPUDownloaderDelegate.h + + hash + + UkBvHxzNYA/YON0ImE8m/8g4vkg= + + hash2 + + 8D6RyR2aziRQIiRN3e7qxlEM9jfgkwBBTWmSjDhLqm8= + + + Headers/SPUDownloaderDeprecated.h + + hash + + l7/+P82TkgRp/YLnQtJtjMWd3/A= + + hash2 + + A7PBMhwuTH0X/JDeyLlh1Xrdc0krwjPqdBPCiePFrxQ= + + + Headers/SPUDownloaderProtocol.h + + hash + + VsD58zsMmnH4mVuK456ekiJcYFU= + + hash2 + + 02oqvyfRCQp5SMSBQNzYZaz1/0VTso0GKnOSbuDEAVo= + + + Headers/SPUDownloaderSession.h + + hash + + IeDSEpUGRwB9HcDVlqbxb6k19A8= + + hash2 + + ZFXLQz90P2h4rR2v1VqRAKzrSSlItkO5cW/0AFy7eYQ= + + + Headers/SPUURLRequest.h + + hash + + 588/W6GUNXYFVar9nygqHNYeTX8= + + hash2 + + 4EG4qYbJgM0wBJCoSPD1lbbDTLHlm85m4xNehyXyU74= + + + Headers/SUAppcast.h + + hash + + puyTMIscPdgGWGQM4TITePIKSdA= + + hash2 + + R3/VQvZCv8Ab34CC17vtBHR3dqkJXnidTR5n8aX4DD0= + + + Headers/SUAppcastItem.h + + hash + + YQvyEYAn6UWDwU8NT5ccPwhdRXE= + + hash2 + + ZB27X0FfNZ54ez8FZAFDEi6/o4BYtQnl4A9Y3tAbkUI= + + + Headers/SUCodeSigningVerifier.h + + hash + + Wj90pgADRte0DhyUmlOXWORp830= + + hash2 + + PPwqqlfaR8hxIfD2YEKdVzyDN4kBVdul55HDAELxQ7U= + + + Headers/SUErrors.h + + hash + + fXyYJmeBH5lNthmGD2GJOWkMmjg= + + hash2 + + JKSoOJCqip87NB39v1ip2Gki8x1rGVcXB7D6kTCeBTI= + + + Headers/SUExport.h + + hash + + G2w3uU/V7JzIupuOjQ2LyPVGdo4= + + hash2 + + XO8CQmbFThLbYg949NEGhg3g+iouIw3/3+BCCLtEdFE= + + + Headers/SUStandardVersionComparator.h + + hash + + 6L4GrA8i1OjMLIGc/sFvEUeSfso= + + hash2 + + 5HrjOlX+uJHOw1ma+1BtG5tlYx/75N/gVzt+BX/GOxg= + + + Headers/SUUpdater.h + + hash + + krK593R1jTMhVN/yl8nXWo1WIfg= + + hash2 + + IJc5cjKEoEbG6onu/Dy+1OXT5YJ7GV3R1U0u2FamUsY= + + + Headers/SUUpdaterDelegate.h + + hash + + MHw1NrGxR116E8+4fIwJlj7xhlU= + + hash2 + + I9h48457oU5WcQiRBj2su4zWt34+hDtHwnoeLWmZ7Ko= + + + Headers/SUVersionComparisonProtocol.h + + hash + + pin8CrEV9eYWF9sdDu0N1YixWOE= + + hash2 + + rsm3T+GsIhDgSqY8EtkBpIxYgSZCZxf4HE9a/FcTRCc= + + + Headers/SUVersionDisplayProtocol.h + + hash + + D3yVFnts0j1IacN9dSN7iZSVlZg= + + hash2 + + AQITUMp8aq1UAOrmksAKmGFpgI24u9rDSBBZrgDqdN4= + + + Headers/Sparkle.h + + hash + + wr5DylsSwG9e5D1UOhUWQxcyz/g= + + hash2 + + OWWeiknWNtWrmGQq0pZ+Hst0PnbN6WbPhGHqIhk4MOM= + + + Modules/module.modulemap + + hash + + /2jgDcgH5+EG9ojcVskajfVo4yg= + + hash2 + + 1TF+JZkzFr6n8oH4WItto+C5Vf3K12f0H9KjqD0A5QU= + + + PrivateHeaders/SUUnarchiver.h + + hash + + 7mTpmnTkJ97DZQQzK/hdl2aZmXU= + + hash2 + + SQYAanTtlyX15CJapj5tDbhBEMtgQ7ZNdmpSij0+tD4= + + + Resources/Autoupdate.app/Contents/Info.plist + + hash + + vcMwRErKskmkeyJJVI3Z3sof/sE= + + hash2 + + KAQWAU9pYYHyY0ffDykJoZXUPjiewvVWowIwn5qsBNc= + + + Resources/Autoupdate.app/Contents/MacOS/Autoupdate + + hash + + +dZZmoYTS+EoOzyj5DHipiL7vdA= + + hash2 + + /CLwW6G+QiK5ETp+clGZsGSksu5/2ppP1elEQVje7VY= + + + Resources/Autoupdate.app/Contents/MacOS/fileop + + hash + + rsEt+sjfXAsfWD9h538L+27pg/s= + + hash2 + + kI/01mUGzFnWLufOtr6ca07s6VBhqA7INTCKBS4xA+8= + + + Resources/Autoupdate.app/Contents/PkgInfo + + hash + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + hash2 + + glAhkclISwTWhTdPmHmgBmBpxJuKyuegSwHTjQfo7KA= + + + Resources/Autoupdate.app/Contents/Resources/AppIcon.icns + + hash + + 4McwRDEss5BzWwUMG2Xf93+ze08= + + hash2 + + nq7j0ugQwyNbJn/7zGFwxIR0njwU3i7hAYKEyZhvUfE= + + + Resources/Autoupdate.app/Contents/Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + Resources/Autoupdate.app/Contents/_CodeSignature/CodeResources + + hash + + VHoMteoopEbbYVG87rxkm0sjkA4= + + hash2 + + lxIEfRslquAFN4m0UvN8ZbULT6U6J3cPAYY9sqR1HPE= + + + Resources/DarkAqua.css + + hash + + SCihC2/GG/DhF4xcXD9MYaxhawM= + + hash2 + + 1G4+GJId47E8UijYTy9xeqA8RpLdOaGZQZ8B85ydROg= + + + Resources/Info.plist + + hash + + Vjt5kOIAFdWzUgFgF/aP2v3owEc= + + hash2 + + F8ePDHN9JgUrW5JHoFYN8yT7d6AnZBL5ZflSVh3ri4Q= + + + Resources/SUModelTranslation.plist + + hash + + iD2Ex40Usc4ZE6IAhRePqgwK/xw= + + hash2 + + bxnpRQhROJXTL2xuIeffR3p+hOuuji7eOoDeNqSYqTg= + + + Resources/SUStatus.nib + + hash + + ECVWRExfxyDt5uvKRD+70wc9J6s= + + hash2 + + AtY9YmPv7cUlbFWP2vCyVdi3/M+XQn98wOlrIES2Dgk= + + + Resources/ar.lproj/SUAutomaticUpdateAlert.nib + + hash + + LtkONVbhTzwCPtbjkr06qSniXCI= + + hash2 + + CL6tBm495f4yM6z7y8UHRhtooR3NLGfDUOMHspa3d6k= + + optional + + + Resources/ar.lproj/SUUpdateAlert.nib + + hash + + JeZDdP1OuZbqkm8UKYiyH00A7ss= + + hash2 + + CE1qJ1jrwUiTKTlZajb/bhplzo/rdEH6pm5cABwD/rQ= + + optional + + + Resources/ar.lproj/SUUpdatePermissionPrompt.nib + + hash + + Heb65H1UseXl7rEaFwVxKauBWnI= + + hash2 + + WUx1KM1Bz75vbTlcj3FvUEEJ3niP2QNBe7/lPioCMgY= + + optional + + + Resources/ar.lproj/Sparkle.strings + + hash + + Rf4jjdgTqvfw5JO/6f9jHMURv/U= + + hash2 + + 2cAJJ5NTxwpRgp24Ca3EuTXfaIIzsYdH3Y9cNCalZfc= + + optional + + + Resources/ca.lproj/Sparkle.strings + + hash + + wGGx+QzPg/20zZTq7jwCTgf/Ubc= + + hash2 + + om5I6jKleuRoCwjfrRRqKWQbs2l8lLj8QGKS47cxybA= + + optional + + + Resources/cs.lproj/SUAutomaticUpdateAlert.nib + + hash + + YpT086oHMS9O2TvSNLZh+39oy80= + + hash2 + + YyE1WN1/ryPt2H0D9gYJv/r0SSv8VYTcxiiNeELiJIQ= + + optional + + + Resources/cs.lproj/SUUpdateAlert.nib + + hash + + v2ac1JQZvkm8EHZiTUc/q4aBcU0= + + hash2 + + LWRxgLZHNGYOe63gf0aOD8zoP03Z1s7ldxndzkWbzGw= + + optional + + + Resources/cs.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2ANG1NY1o8ndm0xcmHwYUvrRk6w= + + hash2 + + dyM1bkEKAH1sW3J5pxDKHpNZ6ZJX7YH/x6jeICRqpkc= + + optional + + + Resources/cs.lproj/Sparkle.strings + + hash + + bY3rkqi/NJtXtjpK3FbV2o0gxbQ= + + hash2 + + RfJgT2b3STcLu71+1iU9ZcSXbfwMWG1EE1C7Wrf3xBk= + + optional + + + Resources/da.lproj/SUAutomaticUpdateAlert.nib + + hash + + XCqcLv38cTpbjAE4zjN/JWeT3+U= + + hash2 + + ivTLD912Rxy2BqIYFAQjsqh4PofwMLljqh6ncdYEdy8= + + optional + + + Resources/da.lproj/SUUpdateAlert.nib + + hash + + BY0imp6dA7C0GSOK81VXTJsRccM= + + hash2 + + BZL9bUc/f5RpZHoQGkA/XXKvykMh/LwkqI+1XW14Bxk= + + optional + + + Resources/da.lproj/SUUpdatePermissionPrompt.nib + + hash + + Ev2Nvw9c6bVU5ZF63yVhcyNp84w= + + hash2 + + N3Os+6xHdP9Y/QLv2okENWzAaaY73ZZ1wAa+vhQKSWg= + + optional + + + Resources/da.lproj/Sparkle.strings + + hash + + 0t7SuLDMBZVsY240PAEsVfH/1qw= + + hash2 + + wu0CpGqE79+TXKIQm+q7ycPTuXhOlwRr/wD5uGHJzLM= + + optional + + + Resources/de.lproj/SUAutomaticUpdateAlert.nib + + hash + + i/BaQXOSENNulhl0b5jssezuU3Y= + + hash2 + + vZn/tXvSzWZPBBo0cVnIiPjRYfiMEtABs2gGlmJ3DKo= + + optional + + + Resources/de.lproj/SUUpdateAlert.nib + + hash + + lieYpCoCaCKAA3EL3/EsBr46vqI= + + hash2 + + pOQG4CEenyMCs6E53Yf2+yYR99NwtjC9ESL6Hp719iM= + + optional + + + Resources/de.lproj/SUUpdatePermissionPrompt.nib + + hash + + oy0dHoyKmH2uV/KCHJzCagE+QIE= + + hash2 + + aXEwUfPGaGK1ndjF84VGCstTDsw+y3qn6bW9197R/wc= + + optional + + + Resources/de.lproj/Sparkle.strings + + hash + + fsC7FJvExHE/2681tuUrjkSF2+A= + + hash2 + + XUpgsFH8KmcbgggpdYbJScCg0tBic9tNLdFh+8cbPyw= + + optional + + + Resources/el.lproj/SUAutomaticUpdateAlert.nib + + hash + + s3rpfaKP5+1+vGc44qpcWy+h0t8= + + hash2 + + 8Dy4OJ7vlhUCXCV6cjyExPoQWOtUSRnuNLpDxfel5ss= + + optional + + + Resources/el.lproj/SUUpdateAlert.nib + + hash + + sJcnQqAH4BsB+2rz9riB7iqePh0= + + hash2 + + oZ9SfHTeIGNZtJjH75VsT01y5Vo2tq2VCPVF8bDddeE= + + optional + + + Resources/el.lproj/SUUpdatePermissionPrompt.nib + + hash + + mYyXqqWSoYqVG1zNp1vopIw8r1k= + + hash2 + + j3xNys0dFAL/2iqvjfz2PopHNj9kPZSLHI5SyE8Pb5c= + + optional + + + Resources/el.lproj/Sparkle.strings + + hash + + NbIN+TRHORCL5Gfj68VRq4KdPXo= + + hash2 + + wt+2xyusmWAQuJ5kAQlRlvFb1wO4L7/rFdG+VmNjl+Y= + + optional + + + Resources/en.lproj/SUAutomaticUpdateAlert.nib + + hash + + Y6bIF/+bAP3t5gBwPcdqxsj4co4= + + hash2 + + LK+XUVI/B5vkE00baFJQzgTVPcWQu2vfztwnjkmtAdg= + + optional + + + Resources/en.lproj/SUUpdateAlert.nib + + hash + + L3hoxekBQAtpmyDXNhTX7kRXRtc= + + hash2 + + c1eSzlRx9vqCBLiF84w+iiiGeii8RIOVaoC8Ds3gndI= + + optional + + + Resources/en.lproj/SUUpdatePermissionPrompt.nib + + hash + + uLKIwoprHw35+b4+/KP/j9X2zVg= + + hash2 + + kXFQNOUYJFVMleLIk/wvetRZoFi+Es/ChIGsKEkPdTs= + + optional + + + Resources/en.lproj/Sparkle.strings + + hash + + cHZov5FaqzfNhnBo0XdRuTMT4SY= + + hash2 + + 39CdfZZ1CQQz1Gd1+Ukxo2JHl0XESoc/cqWKF091WUk= + + optional + + + Resources/es.lproj/SUAutomaticUpdateAlert.nib + + hash + + BoS6NAq1zyVcmkbrKJhcI9Zrezk= + + hash2 + + GtD3UAnIT5BoshJo4areAKSruPfavkvTIyNd0gjejDM= + + optional + + + Resources/es.lproj/SUUpdateAlert.nib + + hash + + UMa1QcJf8zfpVUnIZUGFfJ64wTk= + + hash2 + + 3KPglR1oBAj4L7IA3Y4fYKtWrk2kpbl7jPZwPCByWfo= + + optional + + + Resources/es.lproj/SUUpdatePermissionPrompt.nib + + hash + + tre2iSm68OK3ztgNotyXuz1MkzI= + + hash2 + + nPkeNlTVifGs1wwWJbh4cKzN6KGsoSoFWhhX8gcD+Zc= + + optional + + + Resources/es.lproj/Sparkle.strings + + hash + + QPG88BN+x/l2Qk1NLLe3wRa26mQ= + + hash2 + + mtOoKdoTpGzeTNyzxkVGOMsE0Z3ZZOsmIKDfgA9aj8c= + + optional + + + Resources/fi.lproj/SUAutomaticUpdateAlert.nib + + hash + + 6aUnn3XSgWKnVuYVA/PVSrwora8= + + hash2 + + QVnq+Bn52L27jHSSWCd5PGIBzAeU4HwlTMuL0+M3JMI= + + optional + + + Resources/fi.lproj/SUUpdateAlert.nib + + hash + + k8QjxmBhk5B6v1fGnFkwwX6oakg= + + hash2 + + R5U4ry1iLGtnxFs4Ex8GdV7tpXKbo1HoH0rsSQKIO1M= + + optional + + + Resources/fi.lproj/SUUpdatePermissionPrompt.nib + + hash + + HB5ASms7UIZfv0WaGh6tCLBEDP8= + + hash2 + + QgdpF8+b+1E1FOKUQmo9gfa/0naxptxTvxgPAiBPnzc= + + optional + + + Resources/fi.lproj/Sparkle.strings + + hash + + yd6pIoSj19HMDIUos4Td1Fch7bs= + + hash2 + + +AiiKWEdH3lesozLJBn3tfK6vi/VSI1/TnWVmIdVVsc= + + optional + + + Resources/fr.lproj/SUAutomaticUpdateAlert.nib + + hash + + Nj5v0wIECbpjSTU74xKBngH4DeY= + + hash2 + + iJPr/YNl1hGgBNcbjpEttX7EgNKwsu+R3oVtXyoxxV0= + + optional + + + Resources/fr.lproj/SUUpdateAlert.nib + + hash + + RPgJubd38D/WH1H6B1jSejqILE8= + + hash2 + + KtHl8n7bnlZ3Ir0ymG0RdRWNezTCdzyBh9HO0AB2TrA= + + optional + + + Resources/fr.lproj/SUUpdatePermissionPrompt.nib + + hash + + cVJfS2Nx3QvdbWEq+tSt8xi9hIg= + + hash2 + + LTWELwsYH8j9IGZy23C/qmUvJ0/E498TrPfWsXFOM8c= + + optional + + + Resources/fr.lproj/Sparkle.strings + + hash + + X3URilwJPVqMTGbtrYdorODwrMA= + + hash2 + + fyqJl0MhXYRILalxRHpv/JorWLOVLPtNcJioiPtlnYg= + + optional + + + Resources/fr_CA.lproj + + symlink + fr.lproj + + Resources/he.lproj/Sparkle.strings + + hash + + U2WmlYGYmeeIlSW66R8awwmNXIE= + + hash2 + + 4gUlWkwTANV/jd7n4OZoXyT8CAcgWVk/tI3a25wmuLg= + + optional + + + Resources/hr.lproj/SUAutomaticUpdateAlert.nib + + hash + + SkgPcXJYp5dizLAgiXfyl9EsPoI= + + hash2 + + 2h4d04V7H95/KuIy1kBjzDQqtfxnZrRzKlxEjk3NYRo= + + optional + + + Resources/hr.lproj/SUUpdateAlert.nib + + hash + + 47iMWOA+94RZGJW+QJCeM4xOUsA= + + hash2 + + ReDJwTVXlm8iJWToPp6haL3A35LkgyNtEYfEYQ38l+A= + + optional + + + Resources/hr.lproj/SUUpdatePermissionPrompt.nib + + hash + + S2YV0JmEwfPtYsMBBMuvddrPEis= + + hash2 + + s4Q/66AafiJ25LzYFLwYJRMdqr1W2awMSkxlZjy9JtM= + + optional + + + Resources/hr.lproj/Sparkle.strings + + hash + + 7LLOVs76ioMwEDV8Gah+6sV/5No= + + hash2 + + TwklhrooHTXgV6Q9fbvvAB3mPIh7qDbEsNtUzo2fQuU= + + optional + + + Resources/hu.lproj/SUAutomaticUpdateAlert.nib + + hash + + PfYCBbOThC1gBDzoxD+ijdyQ3T0= + + hash2 + + C9vH4mEYy0VzQEvjXYfCMPM4ggBQF1APABRkUOUQwPA= + + optional + + + Resources/hu.lproj/SUUpdateAlert.nib + + hash + + 6RlHCvHc9GNh1M7iJhvn12iFGpg= + + hash2 + + 8vAkRUe47lFmMm7zUZM55/XRK21KahmSbRy0Axp6gw0= + + optional + + + Resources/hu.lproj/SUUpdatePermissionPrompt.nib + + hash + + 1Yz7vPBCFCly2cHjtbQJPK9PzjE= + + hash2 + + GyEkgG0mW1s+T6Nz7aQ/eEvLYoysvr7BYots62oHX1w= + + optional + + + Resources/hu.lproj/Sparkle.strings + + hash + + bNEmsO2LyUsMjTESH1I42V9sAOo= + + hash2 + + sRkp8c3Bx1qWdhhSNdOap1PbfmiTziINy1HxGea3SWU= + + optional + + + Resources/is.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9eOJ/dQvTMu45Z1UowMPaKHYQOI= + + hash2 + + QpgLimdJiwdp2DVF/DMQyJ2Zg8L2ihsreE7vcI8Uqh0= + + optional + + + Resources/is.lproj/SUUpdateAlert.nib + + hash + + F0aP96zh7QOSkAdFsBlIzBhmCIg= + + hash2 + + t7A1i/lrse5T6UZtyfTdB/7HRH5vPNuOj2I+QkIjAEI= + + optional + + + Resources/is.lproj/SUUpdatePermissionPrompt.nib + + hash + + xRBgLwOX0xZhrXGjHDHL6S+qCQc= + + hash2 + + VWBW48lOFIc7lprCjCV9s4BfRYheTgsJnhe5dnQbqOY= + + optional + + + Resources/is.lproj/Sparkle.strings + + hash + + 8fxzD9ZhrvIZVZB1+QSJaPzg80M= + + hash2 + + xcV1yh/zU3U3TsRUT6vGybvIQitf+ThrogN/uOWmD8k= + + optional + + + Resources/it.lproj/SUAutomaticUpdateAlert.nib + + hash + + JXGMS3rtry8HTWEbBibxVCkBEmw= + + hash2 + + 5+DJlDvCzlPxtarex6vBp6GBNUjc//XUg1dP+YKgQvo= + + optional + + + Resources/it.lproj/SUUpdateAlert.nib + + hash + + W45+n9zTkxt1E3v6cZYLzXNtDlc= + + hash2 + + dMHKLXO9jQ/ephXEzJ8zaCiJ2TD94Xdtlfwqn0liUVo= + + optional + + + Resources/it.lproj/SUUpdatePermissionPrompt.nib + + hash + + o6d6uYDAajCHTJJOXT7zDECTmIM= + + hash2 + + abK36qjugFrOyJCWuehmLWtrUUaY2xV+kVIPmNJXKjA= + + optional + + + Resources/it.lproj/Sparkle.strings + + hash + + bk1J6vpZjWeUFhBYWuWZf8TDv1A= + + hash2 + + Y+caNW+g0mt7HP4JrBxJw+uDwN3j19UYb+q5r9ch4Ow= + + optional + + + Resources/ja.lproj/SUAutomaticUpdateAlert.nib + + hash + + 9fRyIZvZMUaRTaWCda7NsPqJle0= + + hash2 + + 17Hm2P7maddnurefS4zzxjw/i66hgAqMfPCnw5etp8c= + + optional + + + Resources/ja.lproj/SUUpdateAlert.nib + + hash + + L07PCZt4pHgRzMPxV0N6F2QK9kM= + + hash2 + + qzSS0s4cMsrK7155WvW8tp+ToVcFs5pmCwrBC9Lr/ec= + + optional + + + Resources/ja.lproj/SUUpdatePermissionPrompt.nib + + hash + + b1mgRruuDPTLXfhBEjTV72kV1m0= + + hash2 + + 5nEPrrpTyzn6ealGjKbkHDtrZ2hvu6zXmQjlL7x8UGY= + + optional + + + Resources/ja.lproj/Sparkle.strings + + hash + + f4EbR/GfMsKeWJ5DN/vhwg/lUoE= + + hash2 + + dSPIvpFbelHRv8liJjN3TUVPbgD1DfhVSGmE+S99quI= + + optional + + + Resources/ko.lproj/SUAutomaticUpdateAlert.nib + + hash + + gi+8llNjjuFSKRkNAA7vN/mf1uA= + + hash2 + + xhSLkCd/oWSClxzjFJOGmPOAS3u0od2to6thPF+1hi8= + + optional + + + Resources/ko.lproj/SUUpdateAlert.nib + + hash + + UNAQQTvtG7+MN/4w4ouu9ZHXfCM= + + hash2 + + rU7gnpi3PsnD0n0noPJN7LKUwxXFHCjixcW7WHa1q2Y= + + optional + + + Resources/ko.lproj/SUUpdatePermissionPrompt.nib + + hash + + ycrHyxLA68Lf8rq4IXBVK62UpGc= + + hash2 + + bvNquF3puRjnZvG2nQUQsz5WyXUsO3LC6BCtEjw2+9g= + + optional + + + Resources/ko.lproj/Sparkle.strings + + hash + + FRHRQPCWEk9GdJawYTuccg+E2tA= + + hash2 + + +bxn0NPgkxdHLa1MHRT+JRlYmy1jpIuaenpst5RT+RA= + + optional + + + Resources/nb.lproj/SUAutomaticUpdateAlert.nib + + hash + + kJLypTD4VsCOwsXiOd6700pn0Cc= + + hash2 + + k7N16zCtXUz9SCO2rA2PSAb+B9zsqeF9kPAlIPeRQq0= + + optional + + + Resources/nb.lproj/SUUpdateAlert.nib + + hash + + WIJIO1qR0uNQtJpVkhIarVOfgFw= + + hash2 + + 8g/ElSO4yIXKBPWT/sbST8vdzCsbEVIeHZSxJbCzJ4M= + + optional + + + Resources/nb.lproj/SUUpdatePermissionPrompt.nib + + hash + + sGjRl91qI6175CwJYuqSYLYylJg= + + hash2 + + /oxFNXMvdoD2D7ykStspwXUrcEpRCaVbYebxNXyRuI4= + + optional + + + Resources/nb.lproj/Sparkle.strings + + hash + + sgrDElwUxXtzdw8WaUFWyK3pG9Y= + + hash2 + + FG+w+OnLI7nwnNCWiMT50LU98VWj1d08ElfX4k7Ok4w= + + optional + + + Resources/nl.lproj/SUAutomaticUpdateAlert.nib + + hash + + wPmqlbK7p5mjPDrezXrYK8CG3NA= + + hash2 + + zV9zmt6+b1fbS8Nzwh35PAkW5vdYcJ64kjUjQyde+Mo= + + optional + + + Resources/nl.lproj/SUUpdateAlert.nib + + hash + + 7EXAJEeeN0k32jvOHKr7Icq4644= + + hash2 + + 3I8r+QxaqIEqBcdZogXU5VuDg87Ls1S5Ss1nhmfM8Po= + + optional + + + Resources/nl.lproj/SUUpdatePermissionPrompt.nib + + hash + + QHLHLZGOJJ4eN75aG1K1VUHNPz8= + + hash2 + + arRlqOWae64Pqn6yNw466S0RNtPGSxvbi7FEd00g1x8= + + optional + + + Resources/nl.lproj/Sparkle.strings + + hash + + PWbC08zHFLROqivY2MAklDh6gkA= + + hash2 + + xnQkqxaO8zP1xpjY3nyjOd4Fe0gJon2Dbt456ukd/Gw= + + optional + + + Resources/pl.lproj/SUAutomaticUpdateAlert.nib + + hash + + 4aIS8LbPLTj63EhS7xEk+qjNzD8= + + hash2 + + PvUb+gVq9mfBJ4c4Rp3cyMOr6Fw7EqVwOSYCXdskwUA= + + optional + + + Resources/pl.lproj/SUUpdateAlert.nib + + hash + + pLII26utl65JwmAFqLiMefDsrGs= + + hash2 + + KNo3HyQuwczwWYBLgpAwz1nk4Lo/IuCN4SZpFhn8diY= + + optional + + + Resources/pl.lproj/SUUpdatePermissionPrompt.nib + + hash + + pZo0pXve6jqBertG5IixAzSpgV4= + + hash2 + + ILzDGh3jJc3hPKR/ADk0jiaDQaHZu7Zq8YLRXT+OXjc= + + optional + + + Resources/pl.lproj/Sparkle.strings + + hash + + o7deBXE2Ct8/vQxouej5KkwTcUA= + + hash2 + + pDq+41jhfESgJauedrYncFY1O5EMEU3nRyl7mmyYj+s= + + optional + + + Resources/pt.lproj + + symlink + pt_BR.lproj + + Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib + + hash + + 8fQxJZw+LQiExfyPaqUbNnASsWU= + + hash2 + + 7Xu6H76LF/BEqK9o0LJHpt54NV3JbY/TPoinZv9GfSQ= + + optional + + + Resources/pt_BR.lproj/SUUpdateAlert.nib + + hash + + zW5zEa6rDpqLuTDfixuKKE93E5o= + + hash2 + + fbgvSsPkysitkmLfP26OmYpP/r+044gpfMHqQoQp9Ns= + + optional + + + Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib + + hash + + 2dtbduILRWtmfjobyd2yOVhQNH4= + + hash2 + + 18VMio0CRtcvr8i0M+O3/t41QS15KVxSGxoVhQYapxE= + + optional + + + Resources/pt_BR.lproj/Sparkle.strings + + hash + + /adUv04OXQkCFv+Oed6qktFVQ3E= + + hash2 + + lY5EZJwPc/Rmfhw1gotkeEKB+ANXqZUlM2G92sZwdJc= + + optional + + + Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib + + hash + + Svc6e9pHrp1wA7rxw7oKc5HB8qQ= + + hash2 + + L4KDh1UJEm3ta+qgzCe3s3RI7xXrAY8y5h5eqneS3Uo= + + optional + + + Resources/pt_PT.lproj/SUUpdateAlert.nib + + hash + + NtD144OppS+BPXNmsReGJoN2Qdo= + + hash2 + + NOQ75dz/Mq7PLhwssExcWXdtTFQzx8m/lLpLBDCaZCI= + + optional + + + Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5B8hYUrKag0Unyt6Uk0D2K5opL8= + + hash2 + + D42TQ5lV8E73WOOXTsUDSu2jqOFUt1+WMcLUTVab8W8= + + optional + + + Resources/pt_PT.lproj/Sparkle.strings + + hash + + Mji9loJOJvuDY9hz3FhQ4H+HY5E= + + hash2 + + RUq6VJjn/QyydkNbpklLwfCgRF62+uHhXen2dYLBNuQ= + + optional + + + Resources/ro.lproj/SUAutomaticUpdateAlert.nib + + hash + + 7HEo1dlbwSnit0+4DsAqKDz1jR4= + + hash2 + + KUqcmkrRCh+XjXh9F7fudt94MreG4bKqDh1PLat/FpI= + + optional + + + Resources/ro.lproj/SUUpdateAlert.nib + + hash + + FPWtaRuYrVSPrfAozq/4bSQfMK4= + + hash2 + + 2mIpfTwoRvVuhY/Aa3Bqw5VsjpV93xFNcBMQG83Q8DU= + + optional + + + Resources/ro.lproj/SUUpdatePermissionPrompt.nib + + hash + + GY/ufItfyKYpgw54TfqJlPlymb0= + + hash2 + + hMvl0YhsMlkusdQxVcUiUDeQQqNn/KFwXfIqWaDSrG0= + + optional + + + Resources/ro.lproj/Sparkle.strings + + hash + + 9U+OTz29kXKZHY/nmvbtemMsB3g= + + hash2 + + NNvDsecglQ/utR6YEqxyMj5K976YRWieCIC/PZuWCtQ= + + optional + + + Resources/ru.lproj/SUAutomaticUpdateAlert.nib + + hash + + YHane6xWVhvpJGf5HuoxCyQ/gDs= + + hash2 + + qwuyYXCO4H5IuXiRhZR3ucGl5S1CtG2e7kxxCkM8vtA= + + optional + + + Resources/ru.lproj/SUUpdateAlert.nib + + hash + + NxM+W+qAegxK4lKy0uzCclpkVjo= + + hash2 + + Kvykt9h+o+SUuINROlMCXornVL/uEH2Uz5Kd0bNSm6k= + + optional + + + Resources/ru.lproj/SUUpdatePermissionPrompt.nib + + hash + + AqJRrBMp2yA+umSXxQIQVmpnCN4= + + hash2 + + 7Guwtd2PDpjTWg/qX1UODxgRfQDDxNUgN4qr4Siv/Cc= + + optional + + + Resources/ru.lproj/Sparkle.strings + + hash + + VpSLGNvZ6sbRYsF23L8m6TG+P6E= + + hash2 + + wJZ5NG+mvj4anRFPUFyvSD0kGrg+ZAqklsPfHuCxLQY= + + optional + + + Resources/sk.lproj/SUAutomaticUpdateAlert.nib + + hash + + 5k+e1kFtgoVo77RhyhZSXpRQGOQ= + + hash2 + + qZSxDMjtIXyNwOZYeUz2g9w6NFmKFStqy5UbKU9N3BA= + + optional + + + Resources/sk.lproj/SUUpdateAlert.nib + + hash + + l8XR02tvN10SX2aM9CtZ7BpIcqw= + + hash2 + + fD0W6cMr/MZ0C0qrsMD3jEbXdK+eq8L7tl87ZPDspmY= + + optional + + + Resources/sk.lproj/SUUpdatePermissionPrompt.nib + + hash + + burLhfFkzbPjAIqMXw1qKn94xm8= + + hash2 + + D5XPrSBItdufc2zVCFazX3SEmRVqQBdCZ0ADlLmHE4E= + + optional + + + Resources/sk.lproj/Sparkle.strings + + hash + + qn/mo2EFOyw6keezS64Wo5ZGZXU= + + hash2 + + e3cyzJ87ohC1ff/BzZ5O00MnwRE02U+J1KwXlSZeSSg= + + optional + + + Resources/sl.lproj/SUAutomaticUpdateAlert.nib + + hash + + htsZnsf4S+omnk4Z8En8qOOjrPM= + + hash2 + + 9kzvdHcdVqdxsioITBt+Am06twXcJpKMreMKHDDj4RY= + + optional + + + Resources/sl.lproj/SUUpdateAlert.nib + + hash + + /9xrGwHXOdPKFp82w4OjF+Q0WG8= + + hash2 + + r48ahwyC8EFi+44X/EtUfWvh8QCu9klpjqwwwzQHiXE= + + optional + + + Resources/sl.lproj/SUUpdatePermissionPrompt.nib + + hash + + lT25Wn73ZrIgcDJsgzSvIQ97TtY= + + hash2 + + L8TvfX3/5nLCXk5oNeUzyieunqgEUrKzk/t+6Ldxn/0= + + optional + + + Resources/sl.lproj/Sparkle.strings + + hash + + kwvdisufBenuQzrVg8tYKTX+qgg= + + hash2 + + t8QC+9TBONwKLQvV3fKV0umsnAS8ZDpqPikVksFPtWc= + + optional + + + Resources/sv.lproj/SUAutomaticUpdateAlert.nib + + hash + + K0QpkucJxh5CRA9TYJCT5+gSHZY= + + hash2 + + 2GEt92QWRuf/s8NnoWD/HidalRNoLUoeJoC7UM3Y2Ng= + + optional + + + Resources/sv.lproj/SUUpdateAlert.nib + + hash + + dOxfomMC/X9MFFdsk1MyjKv1yi8= + + hash2 + + wIPOqj0JUePKauMIl6sT8YUjoxw1q36g+rrQvwkxfw4= + + optional + + + Resources/sv.lproj/SUUpdatePermissionPrompt.nib + + hash + + W5vyz7ueX3DVKxQC82/3FnvJfeQ= + + hash2 + + C0r3bNAO7WAAZa4WiYUeBQMt+kmEndHyzPT1//HBsYo= + + optional + + + Resources/sv.lproj/Sparkle.strings + + hash + + 98/sk+A2Ew1fmKpuKZ3rq8eS1EM= + + hash2 + + mJY6aeXFnSx38bF630z5lNPmPtsoYVAwadh0KC+9vfQ= + + optional + + + Resources/th.lproj/SUAutomaticUpdateAlert.nib + + hash + + /iyQcDW4+Is40OSlKcWlEe9zKdo= + + hash2 + + gFeu5nmtM/aL1yTSsW9TPh9ZdN8r/lX5jhns53LlcfA= + + optional + + + Resources/th.lproj/SUUpdateAlert.nib + + hash + + cQCWeOMdd6mZEd9k0pl3FrZDT9g= + + hash2 + + vmbxfC0cI7IAAQRNDQ43V6vocH93TiejQmBr23NDAlQ= + + optional + + + Resources/th.lproj/SUUpdatePermissionPrompt.nib + + hash + + JIh/Ueyxh8+us+26dxQRmrPiVAE= + + hash2 + + Hbc+V8H4hfQh3PucUY1S+6vXRBZErhTaZLYJ0jQooLo= + + optional + + + Resources/th.lproj/Sparkle.strings + + hash + + HQwGW1Ebf0i+Bl4synks3x2SY2M= + + hash2 + + nlP7repbMz6EqHo3sZWnK3tzx47WKSWnULdUHCYPgKk= + + optional + + + Resources/tr.lproj/SUAutomaticUpdateAlert.nib + + hash + + fjnMF3XlYLFPJSpmIFizoGwOVl8= + + hash2 + + 3T/cO/cEIAM8QBGVg53bYvcYseEMPOhn0C+yReVVJEU= + + optional + + + Resources/tr.lproj/SUUpdateAlert.nib + + hash + + Y9dabfD0a7F1cV9OuFnyQL5BIIc= + + hash2 + + qwss8xg3cxWWQXqCLxXd6z1ygf6MHYjMDKtVGzTpF0M= + + optional + + + Resources/tr.lproj/SUUpdatePermissionPrompt.nib + + hash + + 5wxy4Op51XjVl1MvUlCnSUfvsj8= + + hash2 + + eXL2eqxEpbACqNQkixNqqVrLeqRbdnpPCndQcIXqdas= + + optional + + + Resources/tr.lproj/Sparkle.strings + + hash + + whUQco5F2wcYdjc+cPKlk+mtx7Q= + + hash2 + + xEXUfrylPld+eFGrPyj4wTRPj7vUWOZ2f94sWydq03M= + + optional + + + Resources/uk.lproj/SUAutomaticUpdateAlert.nib + + hash + + YRRVgJ26NZd9+ebTI3UGdpi35eo= + + hash2 + + /lUZP3n163WFPUjg+ohhY7pnbmtrxuc164ew0tFmDd4= + + optional + + + Resources/uk.lproj/SUUpdateAlert.nib + + hash + + PoeaXUHUKNIm0bkX+GNnvFHlq9w= + + hash2 + + Pmyouw5QFzbN7VYg1RXFNm3IB4jOmZagTi9k8g2CyQE= + + optional + + + Resources/uk.lproj/SUUpdatePermissionPrompt.nib + + hash + + HEubU7VtIHZcWJ6RfdC038Os1gw= + + hash2 + + 7ljWihx3qnfD/0BWdbNtzQirBF95hZ1sSXu5vTiVHe0= + + optional + + + Resources/uk.lproj/Sparkle.strings + + hash + + JXhpqvLkX0yDWjbWgsk2wbSObKU= + + hash2 + + u0572QZYh6sB0GQdMGMePalOf4zkxE7YQG7pp898SEg= + + optional + + + Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib + + hash + + 2pvLfCu7EiI6OkCxu3+aLyeTPcU= + + hash2 + + tu9oMdBCiHiyRSJMEmm5x3oGY3aCnmwXPsRf1hjj7fY= + + optional + + + Resources/zh_CN.lproj/SUUpdateAlert.nib + + hash + + +xvQE3bFW1QXIUggZBlZkKn0gag= + + hash2 + + b6nNjM4vCBrXBbjH5GtbkPjZjJyMeSSBXO/tCRn+LFY= + + optional + + + Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib + + hash + + FT+kQgUNxKGrbheU8uSqkYFSHtI= + + hash2 + + 2J873kYqy/0EyE6QXT6tqYkydTGcrw3/Ncv9cnSRl7M= + + optional + + + Resources/zh_CN.lproj/Sparkle.strings + + hash + + OnR96Z9tB0noODRSYssSs63+zGA= + + hash2 + + zvMbFdgVGI0ls9vIRT+sie7dj2g1UjQu7iS+pOgyBo4= + + optional + + + Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib + + hash + + KQcqbpIyw3yhI0eEyo06cNq5MlM= + + hash2 + + POFOdG9uGjcVXkx39sW2Rwl2FJ2zs6A007yF1UpDskE= + + optional + + + Resources/zh_TW.lproj/SUUpdateAlert.nib + + hash + + 6/Rgln3/89vly1RFa1gBfRhITxU= + + hash2 + + AFd6Yy2xbmYtz1+r9u+r9qNka3oTzho3/n3DCwxWKoE= + + optional + + + Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib + + hash + + qO2OAmNcqk2/bSzwAjGcXTD4+PY= + + hash2 + + JNnNodE8g22fkHlnQzxC9vap/jefD5NkjUjmErvl940= + + optional + + + Resources/zh_TW.lproj/Sparkle.strings + + hash + + 1FLKoM5jZ8JGBG/nmyEIA+/aalA= + + hash2 + + Vlf/4QD7/3S0SFqxmTWWcSwtTLWISKUSvLjpgWb7lxQ= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Dependencies/Sparkle/Sparkle.framework/Versions/Current b/Dependencies/Sparkle/Sparkle.framework/Versions/Current new file mode 120000 index 00000000..8c7e5a66 --- /dev/null +++ b/Dependencies/Sparkle/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Dependencies/Sparkle/VERSION b/Dependencies/Sparkle/VERSION new file mode 100644 index 00000000..53cc1a6f --- /dev/null +++ b/Dependencies/Sparkle/VERSION @@ -0,0 +1 @@ +1.24.0 diff --git a/Dependencies/Sparkle/bin/BinaryDelta b/Dependencies/Sparkle/bin/BinaryDelta new file mode 100755 index 00000000..2e924b92 Binary files /dev/null and b/Dependencies/Sparkle/bin/BinaryDelta differ diff --git a/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist b/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist new file mode 100644 index 00000000..325809b9 --- /dev/null +++ b/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.BinaryDelta + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta b/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta new file mode 100644 index 00000000..b92195ce Binary files /dev/null and b/Dependencies/Sparkle/bin/BinaryDelta.dSYM/Contents/Resources/DWARF/BinaryDelta differ diff --git a/Dependencies/Sparkle/bin/generate_appcast b/Dependencies/Sparkle/bin/generate_appcast new file mode 100755 index 00000000..20549c04 Binary files /dev/null and b/Dependencies/Sparkle/bin/generate_appcast differ diff --git a/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist b/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist new file mode 100644 index 00000000..85e47831 --- /dev/null +++ b/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.generate_appcast + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast b/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast new file mode 100644 index 00000000..55a86e24 Binary files /dev/null and b/Dependencies/Sparkle/bin/generate_appcast.dSYM/Contents/Resources/DWARF/generate_appcast differ diff --git a/Dependencies/Sparkle/bin/generate_keys b/Dependencies/Sparkle/bin/generate_keys new file mode 100755 index 00000000..2dfbb29f Binary files /dev/null and b/Dependencies/Sparkle/bin/generate_keys differ diff --git a/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist b/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist new file mode 100644 index 00000000..3caf8909 --- /dev/null +++ b/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.generate_keys + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys b/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys new file mode 100644 index 00000000..85e379d5 Binary files /dev/null and b/Dependencies/Sparkle/bin/generate_keys.dSYM/Contents/Resources/DWARF/generate_keys differ diff --git a/Dependencies/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only b/Dependencies/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only new file mode 100755 index 00000000..56f195fd --- /dev/null +++ b/Dependencies/Sparkle/bin/old_dsa_scripts/generate_dsa_keys_macos_10.12_only @@ -0,0 +1,24 @@ +#!/bin/bash +set -e +for file in "dsaparam.pem" "dsa_priv.pem" "dsa_pub.pem"; do + if [ -e "$file" ]; then + echo "There's already a $file here! Move it aside or be more careful!" + exit 1 + fi +done + +openssl="/usr/bin/openssl" +$openssl gendsa <($openssl dsaparam 2047) -out dsa_priv.pem +chmod 0400 dsa_priv.pem +$openssl dsa -in dsa_priv.pem -pubout -out dsa_pub.pem + +echo " +Generated two files: +dsa_priv.pem: your private key. Keep it secret and don't share it! +dsa_pub.pem: public counterpart to include in the app bundle. + +BACK UP YOUR PRIVATE KEY AND KEEP IT SAFE! +If you lose it, your users will be unable to upgrade! +" + +open -R dsa_priv.pem diff --git a/Dependencies/Sparkle/bin/old_dsa_scripts/sign_update b/Dependencies/Sparkle/bin/old_dsa_scripts/sign_update new file mode 100755 index 00000000..fddaae83 --- /dev/null +++ b/Dependencies/Sparkle/bin/old_dsa_scripts/sign_update @@ -0,0 +1,18 @@ +#!/bin/bash +set -e +set -o pipefail +if [ "$#" -ne 2 ]; then + echo "Usage: $0 update_archive_file dsa_priv.pem" + echo "This is an old DSA signing script for deprecated DSA keys." + echo "Do not use this for new applications." + exit 1 +fi +openssl=/usr/bin/openssl +version=`$openssl version` +if [[ $version =~ "OpenSSL 0.9" ]]; then + # pre-10.13 system: Fall back to OpenSSL DSS1 digest because it does not like the -sha1 option + $openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | $openssl enc -base64 +else + # 10.13 and later: Use LibreSSL SHA1 digest + $openssl dgst -sha1 -binary < "$1" | $openssl dgst -sha1 -sign "$2" | $openssl enc -base64 +fi diff --git a/Dependencies/Sparkle/bin/sign_update b/Dependencies/Sparkle/bin/sign_update new file mode 100755 index 00000000..8193e1f5 Binary files /dev/null and b/Dependencies/Sparkle/bin/sign_update differ diff --git a/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Info.plist b/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Info.plist new file mode 100644 index 00000000..07393896 --- /dev/null +++ b/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.sign_update + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update b/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update new file mode 100644 index 00000000..db676a7b Binary files /dev/null and b/Dependencies/Sparkle/bin/sign_update.dSYM/Contents/Resources/DWARF/sign_update differ diff --git a/Dependencies/update_dependencies.sh b/Dependencies/update_dependencies.sh new file mode 100755 index 00000000..f264e253 --- /dev/null +++ b/Dependencies/update_dependencies.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +tmp_dir=$(mktemp -d -t switch-update-dependencies-XXXXXX) +deps_dir="$(cd "$(dirname "$0")" && pwd -P)" +pushd "$deps_dir" > /dev/null + +for f in *.giturl; do + project=${f%".giturl"} + repo=$(head -n1 $f | tr -d '[:space:]') + echo "Updating $project at $repo..." + rm -rf "$project" &> /dev/null + pushd "$tmp_dir" > /dev/null + git clone "$repo" "$project" &> /dev/null + cd "$project" + echo "$(git branch --show-current):$(git rev-parse HEAD)" > "$deps_dir/$project.gitcheckout" + rm -rf ".git" + cd .. + mv "$project" "$deps_dir" + popd > /dev/null + echo -e "\t$(cat $project.gitcheckout | tr -d '[:space:]')" +done + +popd > /dev/null +rm -r "$tmp_dir" diff --git a/Documents/Decisions.md b/Documents/Decisions.md new file mode 100644 index 00000000..e8cda727 --- /dev/null +++ b/Documents/Decisions.md @@ -0,0 +1,36 @@ +# Decisions + +This file exists to provide a historical record of the motivation for important technical decisions in the project. It's inspired by Architectural Decision Records, but with minimal bureaucracy: + +* New decisions are appended to the file +* Existing decisions may not be edited (but they can be superceded) + +### Contents: + +* [Isolated, Reactive Switcher State Machine](#isolated--reactive-switcher-state-machine) +* [Value Types for Windows and Window Groups](#value-types-for-windows-and-window-groups) +* [Integrated Dependencies](#integrated-dependencies) + + + +## Isolated, Reactive Switcher State Machine + +The switcher's window management behaviour and interface display logic represent a state machine each, the combination of which quickly becomes difficult to manage when integrated with a traditional MVC controller. + +This implementation complexity is what led to the use of FRP in Switch, assisted at the time by Reactive Cocoa and now implemented in standalone Swift. A basic fuzzer has provided valuable testing coverage, uncovering issues during development that defy imagination. + +While the state machine code is still imperative, its isolation combined with a values-in/callbacks-out architecture has significantly improved product quality and code readability. + +## Value Types for Windows and Window Groups + +Earlier versions of Switch were difficult to validate due to the inability to represent certain system states in the test environment. Specifically, traits like "app is active" or "app can be activated" weren't encoded in value types that could be used in unit tests. + +Having watched [Boundaries](https://www.destroyallsoftware.com/talks/boundaries), and realizing that the success of the [state machine implementation](#isolated--reactive-switcher-state-machine) was the result of moving closer to a functional core/imperative shell model, Switch's transition to Swift included value types for representing _all_ aspects of windows, window groups, and applications. The resulting architecture is _much_ easier to test, and does not appear to suffer from the additional overhead of gathering occasionally-unused pieces of information to populate the values. + +## Integrated Dependencies + +After a string of bad experiences over the years with `git submodule`, dependency deaths, and overly-clever tooling all contributing to widespread bitrot breaking the build, I have settled on the dumbest solution I can think of: + +### All of Switch's dependencies are checked into its repository. + +The goal is that people should be able to get up and running with a `git clone`, `open Switch.xcworkspace`, and `⌘R`. diff --git a/Documents/TODO.md b/Documents/TODO.md new file mode 100644 index 00000000..38fc0c68 --- /dev/null +++ b/Documents/TODO.md @@ -0,0 +1,40 @@ +# TODO/Feature Work + +## 0.0.11β Plan + +* [ ] Ghost selected window overlays (depends on window content) + +### 0.0.10β parity + +1. [ ] Window content rendering +* [ ] Preferences + * [ ] Launch at login + * Probably copy whatever I was doing before (Preserve history??), but there's also [sindresorhus/LaunchAtLogin](https://github.com/sindresorhus/LaunchAtLogin/blob/master/before-after.md) +* [x] Status bar item + * [ ] Snapshots/logging +* [x] Sparkle (ish) +* [ ] Mouse/[scroll](https://github.com/numist/Switch/blob/develop/SwitchTests/SWScrollControlTests.m) support in switcher +* [ ] Backport quirks tests + * [ ] [SWDashTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWDashTests.m) + * [ ] [SWFinderTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWFinderTests.m) + * [ ] [SWGithubTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWGithubTests.m) + * [ ] [SWIsolatorTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWIsolatorTests.m) + * [ ] [SWMacVimTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWMacVimTests.m) + * [ ] [SWPowerboxTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWPowerboxTests.m) + * [ ] [SWSafariTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWSafariTests.m) + * [ ] [SWSanityTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWSanityTests.m) + * [ ] [SWSheetTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWSheetTests.m) + * [ ] [SWTweetbotTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWTweetbotTests.m) + * [ ] [SWWordTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWWordTests.m) + * [ ] [SWXcodeTests](https://github.com/numist/Switch/blob/develop/SwitchTests/SWXcodeTests.m) +* [ ] New quirks tests + * [ ] Add quirk for Calendar with event detail + * [ ] Add quirk for Sonos + +## Future + +* [ ] Icons? +* [ ] First launch experience? +* [ ] [Feature parity with Witch](https://manytricks.com/osticket/kb/faq.php?id=96)? +* [ ] Pasteboard history +* [ ] Programmer's calculator diff --git a/Frameworks/Haxcessibility b/Frameworks/Haxcessibility deleted file mode 160000 index 4d2d3eb1..00000000 --- a/Frameworks/Haxcessibility +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4d2d3eb17924519ce75453638580824cf07a3fef diff --git a/Frameworks/NNKit/NNKit.xcodeproj/project.pbxproj b/Frameworks/NNKit/NNKit.xcodeproj/project.pbxproj deleted file mode 100644 index 95e6f76a..00000000 --- a/Frameworks/NNKit/NNKit.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1065 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - BC12437F183AEFAC00461433 /* NNCleanupProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = BC12437D183AEFAC00461433 /* NNCleanupProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC124380183AEFAC00461433 /* NNCleanupProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = BC12437D183AEFAC00461433 /* NNCleanupProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC124381183AEFAC00461433 /* NNCleanupProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = BC12437E183AEFAC00461433 /* NNCleanupProxy.m */; }; - BC124382183AEFAC00461433 /* NNCleanupProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = BC12437E183AEFAC00461433 /* NNCleanupProxy.m */; }; - BC14BF031D038F230041DAC2 /* NNWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF011D038F230041DAC2 /* NNWeakSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC14BF041D038F230041DAC2 /* NNWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF011D038F230041DAC2 /* NNWeakSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC14BF051D038F230041DAC2 /* NNWeakSet.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF021D038F230041DAC2 /* NNWeakSet.m */; }; - BC14BF061D038F230041DAC2 /* NNWeakSet.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF021D038F230041DAC2 /* NNWeakSet.m */; }; - BC14BF081D0390C70041DAC2 /* NNWeakSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF071D0390C70041DAC2 /* NNWeakSetTests.m */; }; - BC14BF091D0390C70041DAC2 /* NNWeakSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF071D0390C70041DAC2 /* NNWeakSetTests.m */; }; - BC14BF0E1D03911D0041DAC2 /* _NNWeakArrayTombstone.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF0A1D03911D0041DAC2 /* _NNWeakArrayTombstone.h */; }; - BC14BF0F1D03911D0041DAC2 /* _NNWeakArrayTombstone.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF0A1D03911D0041DAC2 /* _NNWeakArrayTombstone.h */; }; - BC14BF101D03911D0041DAC2 /* _NNWeakArrayTombstone.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF0B1D03911D0041DAC2 /* _NNWeakArrayTombstone.m */; }; - BC14BF111D03911D0041DAC2 /* _NNWeakArrayTombstone.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF0B1D03911D0041DAC2 /* _NNWeakArrayTombstone.m */; }; - BC14BF121D03911D0041DAC2 /* _NNWeakSetEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF0C1D03911D0041DAC2 /* _NNWeakSetEnumerator.h */; }; - BC14BF131D03911D0041DAC2 /* _NNWeakSetEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = BC14BF0C1D03911D0041DAC2 /* _NNWeakSetEnumerator.h */; }; - BC14BF141D03911D0041DAC2 /* _NNWeakSetEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF0D1D03911D0041DAC2 /* _NNWeakSetEnumerator.m */; }; - BC14BF151D03911D0041DAC2 /* _NNWeakSetEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = BC14BF0D1D03911D0041DAC2 /* _NNWeakSetEnumerator.m */; }; - BC19D3651810C6C3009CEC1F /* NNService.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19D3631810C6C3009CEC1F /* NNService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC19D3661810C6C3009CEC1F /* NNService.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19D3631810C6C3009CEC1F /* NNService.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC19D3671810C6C3009CEC1F /* NNService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3641810C6C3009CEC1F /* NNService.m */; }; - BC19D3681810C6C3009CEC1F /* NNService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3641810C6C3009CEC1F /* NNService.m */; }; - BC19D3711810C85B009CEC1F /* NNServiceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19D36F1810C85B009CEC1F /* NNServiceManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC19D3721810C85B009CEC1F /* NNServiceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BC19D36F1810C85B009CEC1F /* NNServiceManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC19D3731810C85B009CEC1F /* NNServiceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3701810C85B009CEC1F /* NNServiceManager.m */; }; - BC19D3741810C85B009CEC1F /* NNServiceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3701810C85B009CEC1F /* NNServiceManager.m */; }; - BC19D3A01811BBC9009CEC1F /* NNServiceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D39F1811BBC9009CEC1F /* NNServiceTests.m */; }; - BC19D3A11811BBC9009CEC1F /* NNServiceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D39F1811BBC9009CEC1F /* NNServiceTests.m */; }; - BC275C0A18CE9D9D00761247 /* NSInvocation+NNCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C0918CE9D1A00761247 /* NSInvocation+NNCopying.m */; }; - BC275C0B18CE9DA200761247 /* NSInvocation+NNCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = BC275C0818CE9D1A00761247 /* NSInvocation+NNCopying.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC275C7618CFE4BD00761247 /* NNComprehensionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C7518CFE4BD00761247 /* NNComprehensionTests.m */; }; - BC275C7718CFE4BD00761247 /* NNComprehensionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C7518CFE4BD00761247 /* NNComprehensionTests.m */; }; - BC82B3C9183C1BC300FDD3B9 /* NNCleanupProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3C8183C1BC300FDD3B9 /* NNCleanupProxyTests.m */; }; - BC82B3CA183C1BC300FDD3B9 /* NNCleanupProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3C8183C1BC300FDD3B9 /* NNCleanupProxyTests.m */; }; - BC82B3D5183C29B600FDD3B9 /* NNMultiDispatchManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BC82B3D3183C29B600FDD3B9 /* NNMultiDispatchManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC82B3D6183C29B600FDD3B9 /* NNMultiDispatchManager.h in Headers */ = {isa = PBXBuildFile; fileRef = BC82B3D3183C29B600FDD3B9 /* NNMultiDispatchManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC82B3D7183C29B600FDD3B9 /* NNMultiDispatchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3D4183C29B600FDD3B9 /* NNMultiDispatchManager.m */; }; - BC82B3D8183C29B600FDD3B9 /* NNMultiDispatchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3D4183C29B600FDD3B9 /* NNMultiDispatchManager.m */; }; - BC82B3DA183C317000FDD3B9 /* NNMultiDispatchManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3D9183C317000FDD3B9 /* NNMultiDispatchManagerTests.m */; }; - BC82B3DB183C317000FDD3B9 /* NNMultiDispatchManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3D9183C317000FDD3B9 /* NNMultiDispatchManagerTests.m */; }; - BC82B3DD183C37E500FDD3B9 /* NNService+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = BC82B3DC183C37E500FDD3B9 /* NNService+Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC82B3DE183C37E500FDD3B9 /* NNService+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = BC82B3DC183C37E500FDD3B9 /* NNService+Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC85E88E18E78331001DF986 /* NSInvocation+NNCopying.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C0918CE9D1A00761247 /* NSInvocation+NNCopying.m */; }; - BC85E89318E78519001DF986 /* NSInvocation+NNCopying.h in Headers */ = {isa = PBXBuildFile; fileRef = BC275C0818CE9D1A00761247 /* NSInvocation+NNCopying.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC961A8418BDB377009F3F94 /* macros.h in Headers */ = {isa = PBXBuildFile; fileRef = BC961A8318BDB377009F3F94 /* macros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BC961A8518BDB377009F3F94 /* macros.h in Headers */ = {isa = PBXBuildFile; fileRef = BC961A8318BDB377009F3F94 /* macros.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCABC3831B3A3CB90099CAC5 /* NNSynthesizedObjectStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCABC3821B3A3CB90099CAC5 /* NNSynthesizedObjectStorageTests.m */; }; - BCABC3861B3A43980099CAC5 /* memoize.m in Sources */ = {isa = PBXBuildFile; fileRef = BCABC3851B3A43980099CAC5 /* memoize.m */; }; - BCB1DBA6183D14B50081107F /* NNTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB1DBA5183D14B50081107F /* NNTestCase.m */; }; - BCB1DBA7183D14B50081107F /* NNTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB1DBA5183D14B50081107F /* NNTestCase.m */; }; - BCB1DBA9183D196E0081107F /* nn_autofreeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB1DBA8183D196E0081107F /* nn_autofreeTests.m */; }; - BCB1DBAA183D196E0081107F /* nn_autofreeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB1DBA8183D196E0081107F /* nn_autofreeTests.m */; }; - BCB8A26B18BDBB5000E4AC18 /* NSCollections+NNComprehensions.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB8A26918BDBB5000E4AC18 /* NSCollections+NNComprehensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCB8A26C18BDBB5000E4AC18 /* NSCollections+NNComprehensions.h in Headers */ = {isa = PBXBuildFile; fileRef = BCB8A26918BDBB5000E4AC18 /* NSCollections+NNComprehensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCB8A26D18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB8A26A18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m */; }; - BCB8A26E18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB8A26A18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m */; }; - BCCA79F518075C6F00CE36E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCCA79F418075C6F00CE36E5 /* Foundation.framework */; }; - BCCA7A1E1807608600CE36E5 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1D54117D9471A00A3EBD4 /* XCTest.framework */; }; - BCCA7A1F1807608600CE36E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCCA79F418075C6F00CE36E5 /* Foundation.framework */; }; - BCCA7A201807608600CE36E5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCCA7A0418075C6F00CE36E5 /* UIKit.framework */; }; - BCCA7A301807622200CE36E5 /* NNPollingObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D59B17D9BAB000A3EBD4 /* NNPollingObjectTests.m */; }; - BCCA7A311807622200CE36E5 /* NNSelfInvalidatingObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D56717D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BCCA7A321807622200CE36E5 /* NNDelegateProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D56917D96C4500A3EBD4 /* NNDelegateProxyTests.m */; }; - BCCA7A331807622200CE36E5 /* NNStrongifiedPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D57F17D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m */; }; - BCCA7A341807622200CE36E5 /* nn_isaSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D59117D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m */; }; - BCCA7A351807626200CE36E5 /* despatch.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5A617D9C72F00A3EBD4 /* despatch.m */; }; - BCCA7A361807626200CE36E5 /* runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5A717D9C72F00A3EBD4 /* runtime.c */; }; - BCCA7A371807626200CE36E5 /* NNPollingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5AE17D9C79F00A3EBD4 /* NNPollingObject.m */; }; - BCCA7A381807626200CE36E5 /* NNSelfInvalidatingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5B117D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BCCA7A391807626200CE36E5 /* NNDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5B817D9C84000A3EBD4 /* NNDelegateProxy.m */; }; - BCCA7A3A1807626200CE36E5 /* NNStrongifiedProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5BC17D9C88F00A3EBD4 /* NNStrongifiedProperties.m */; }; - BCCA7A3B1807626200CE36E5 /* nn_autofree.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5C017DECC8E00A3EBD4 /* nn_autofree.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BCCA7A3C1807626200CE36E5 /* nn_isaSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D58917D9A14300A3EBD4 /* nn_isaSwizzling.m */; }; - BCCA7A3D1807626200CE36E5 /* NNISASwizzledObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D58B17D9A14300A3EBD4 /* NNISASwizzledObject.m */; }; - BCCA7A3E1807626200CE36E5 /* NNKit.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D53A17D9471A00A3EBD4 /* NNKit.m */; }; - BCCA7A401807628B00CE36E5 /* despatch.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5A517D9C72F00A3EBD4 /* despatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A411807629100CE36E5 /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5A817D9C72F00A3EBD4 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A421807629700CE36E5 /* NNPollingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5AD17D9C79F00A3EBD4 /* NNPollingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A431807629C00CE36E5 /* NNPollingObject+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5AF17D9C79F00A3EBD4 /* NNPollingObject+Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A44180762A200CE36E5 /* NNSelfInvalidatingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5B017D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A45180762A800CE36E5 /* NNDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5B717D9C84000A3EBD4 /* NNDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A46180762AF00CE36E5 /* NNStrongifiedProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5BB17D9C88F00A3EBD4 /* NNStrongifiedProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A47180762B500CE36E5 /* nn_autofree.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5BF17DECC8E00A3EBD4 /* nn_autofree.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A48180762BB00CE36E5 /* nn_isaSwizzling_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58717D9A14300A3EBD4 /* nn_isaSwizzling_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - BCCA7A49180762C000CE36E5 /* nn_isaSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58817D9A14300A3EBD4 /* nn_isaSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A4A180762C700CE36E5 /* NNISASwizzledObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58A17D9A14300A3EBD4 /* NNISASwizzledObject.h */; settings = {ATTRIBUTES = (Private, ); }; }; - BCCA7A4B180762CC00CE36E5 /* NNKit.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D53917D9471A00A3EBD4 /* NNKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCA7A4C180765D800CE36E5 /* libNNKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BCCA79F318075C6F00CE36E5 /* libNNKit.a */; }; - BCCB1BF31835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = BCCB1BF11835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCB1BF41835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = BCCB1BF11835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCCB1BF51835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB1BF21835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m */; }; - BCCB1BF61835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB1BF21835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m */; }; - BCCB1BFE1835FD9A0029EBF6 /* NNWeakObserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB1BFD1835FD9A0029EBF6 /* NNWeakObserverTests.m */; }; - BCCB1BFF1835FD9A0029EBF6 /* NNWeakObserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCB1BFD1835FD9A0029EBF6 /* NNWeakObserverTests.m */; }; - BCD1D52D17D9471A00A3EBD4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1D52C17D9471A00A3EBD4 /* Cocoa.framework */; }; - BCD1D53B17D9471A00A3EBD4 /* NNKit.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D53A17D9471A00A3EBD4 /* NNKit.m */; }; - BCD1D54217D9471A00A3EBD4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1D54117D9471A00A3EBD4 /* XCTest.framework */; }; - BCD1D54317D9471A00A3EBD4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1D52C17D9471A00A3EBD4 /* Cocoa.framework */; }; - BCD1D54617D9471A00A3EBD4 /* NNKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1D52917D9471A00A3EBD4 /* NNKit.framework */; }; - BCD1D55717D9472D00A3EBD4 /* NNKit.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D53917D9471A00A3EBD4 /* NNKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D56817D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D56717D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BCD1D56A17D96C4500A3EBD4 /* NNDelegateProxyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D56917D96C4500A3EBD4 /* NNDelegateProxyTests.m */; }; - BCD1D58017D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D57F17D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m */; }; - BCD1D58C17D9A14300A3EBD4 /* nn_isaSwizzling_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58717D9A14300A3EBD4 /* nn_isaSwizzling_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - BCD1D58D17D9A14300A3EBD4 /* nn_isaSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58817D9A14300A3EBD4 /* nn_isaSwizzling.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D58E17D9A14300A3EBD4 /* nn_isaSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D58917D9A14300A3EBD4 /* nn_isaSwizzling.m */; }; - BCD1D58F17D9A14300A3EBD4 /* NNISASwizzledObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D58A17D9A14300A3EBD4 /* NNISASwizzledObject.h */; settings = {ATTRIBUTES = (Private, ); }; }; - BCD1D59017D9A14300A3EBD4 /* NNISASwizzledObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D58B17D9A14300A3EBD4 /* NNISASwizzledObject.m */; }; - BCD1D59217D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D59117D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m */; }; - BCD1D59C17D9BAB000A3EBD4 /* NNPollingObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D59B17D9BAB000A3EBD4 /* NNPollingObjectTests.m */; }; - BCD1D5A917D9C72F00A3EBD4 /* despatch.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5A517D9C72F00A3EBD4 /* despatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5AA17D9C72F00A3EBD4 /* despatch.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5A617D9C72F00A3EBD4 /* despatch.m */; }; - BCD1D5AB17D9C72F00A3EBD4 /* runtime.c in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5A717D9C72F00A3EBD4 /* runtime.c */; }; - BCD1D5AC17D9C72F00A3EBD4 /* runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5A817D9C72F00A3EBD4 /* runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5B217D9C79F00A3EBD4 /* NNPollingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5AD17D9C79F00A3EBD4 /* NNPollingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5B317D9C79F00A3EBD4 /* NNPollingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5AE17D9C79F00A3EBD4 /* NNPollingObject.m */; }; - BCD1D5B417D9C79F00A3EBD4 /* NNPollingObject+Protected.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5AF17D9C79F00A3EBD4 /* NNPollingObject+Protected.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5B517D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5B017D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5B617D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5B117D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BCD1D5B917D9C84000A3EBD4 /* NNDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5B717D9C84000A3EBD4 /* NNDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5BA17D9C84000A3EBD4 /* NNDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5B817D9C84000A3EBD4 /* NNDelegateProxy.m */; }; - BCD1D5BD17D9C88F00A3EBD4 /* NNStrongifiedProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5BB17D9C88F00A3EBD4 /* NNStrongifiedProperties.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5BE17D9C88F00A3EBD4 /* NNStrongifiedProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5BC17D9C88F00A3EBD4 /* NNStrongifiedProperties.m */; }; - BCD1D5C117DECC8E00A3EBD4 /* nn_autofree.h in Headers */ = {isa = PBXBuildFile; fileRef = BCD1D5BF17DECC8E00A3EBD4 /* nn_autofree.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BCD1D5C217DECC8E00A3EBD4 /* nn_autofree.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD1D5C017DECC8E00A3EBD4 /* nn_autofree.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - BCCA7A2A1807608700CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCD1D52017D9471A00A3EBD4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BCCA79F218075C6F00CE36E5; - remoteInfo = "NNKit iOS"; - }; - BCD1D54417D9471A00A3EBD4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCD1D52017D9471A00A3EBD4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BCD1D52817D9471A00A3EBD4; - remoteInfo = NNKit; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - BC12437D183AEFAC00461433 /* NNCleanupProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNCleanupProxy.h; path = Actors/NNCleanupProxy.h; sourceTree = ""; }; - BC12437E183AEFAC00461433 /* NNCleanupProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNCleanupProxy.m; path = Actors/NNCleanupProxy.m; sourceTree = ""; }; - BC14BF011D038F230041DAC2 /* NNWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNWeakSet.h; path = Collections/NNWeakSet.h; sourceTree = ""; }; - BC14BF021D038F230041DAC2 /* NNWeakSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNWeakSet.m; path = Collections/NNWeakSet.m; sourceTree = ""; }; - BC14BF071D0390C70041DAC2 /* NNWeakSetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNWeakSetTests.m; sourceTree = ""; }; - BC14BF0A1D03911D0041DAC2 /* _NNWeakArrayTombstone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = _NNWeakArrayTombstone.h; path = Collections/_NNWeakArrayTombstone.h; sourceTree = ""; }; - BC14BF0B1D03911D0041DAC2 /* _NNWeakArrayTombstone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = _NNWeakArrayTombstone.m; path = Collections/_NNWeakArrayTombstone.m; sourceTree = ""; }; - BC14BF0C1D03911D0041DAC2 /* _NNWeakSetEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = _NNWeakSetEnumerator.h; path = Collections/_NNWeakSetEnumerator.h; sourceTree = ""; }; - BC14BF0D1D03911D0041DAC2 /* _NNWeakSetEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = _NNWeakSetEnumerator.m; path = Collections/_NNWeakSetEnumerator.m; sourceTree = ""; }; - BC19D3631810C6C3009CEC1F /* NNService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNService.h; path = Services/NNService.h; sourceTree = ""; }; - BC19D3641810C6C3009CEC1F /* NNService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNService.m; path = Services/NNService.m; sourceTree = ""; }; - BC19D36F1810C85B009CEC1F /* NNServiceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNServiceManager.h; path = Services/NNServiceManager.h; sourceTree = ""; }; - BC19D3701810C85B009CEC1F /* NNServiceManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNServiceManager.m; path = Services/NNServiceManager.m; sourceTree = ""; }; - BC19D39F1811BBC9009CEC1F /* NNServiceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNServiceTests.m; sourceTree = ""; }; - BC275C0818CE9D1A00761247 /* NSInvocation+NNCopying.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSInvocation+NNCopying.h"; path = "API Extension/NSInvocation+NNCopying.h"; sourceTree = ""; }; - BC275C0918CE9D1A00761247 /* NSInvocation+NNCopying.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSInvocation+NNCopying.m"; path = "API Extension/NSInvocation+NNCopying.m"; sourceTree = ""; }; - BC275C7518CFE4BD00761247 /* NNComprehensionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNComprehensionTests.m; sourceTree = ""; }; - BC82B3C8183C1BC300FDD3B9 /* NNCleanupProxyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNCleanupProxyTests.m; sourceTree = ""; }; - BC82B3D3183C29B600FDD3B9 /* NNMultiDispatchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNMultiDispatchManager.h; path = Actors/NNMultiDispatchManager.h; sourceTree = ""; }; - BC82B3D4183C29B600FDD3B9 /* NNMultiDispatchManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNMultiDispatchManager.m; path = Actors/NNMultiDispatchManager.m; sourceTree = ""; }; - BC82B3D9183C317000FDD3B9 /* NNMultiDispatchManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNMultiDispatchManagerTests.m; path = NNKitTests/NNMultiDispatchManagerTests.m; sourceTree = SOURCE_ROOT; }; - BC82B3DC183C37E500FDD3B9 /* NNService+Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NNService+Protected.h"; path = "Services/NNService+Protected.h"; sourceTree = ""; }; - BC961A8318BDB377009F3F94 /* macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macros.h; sourceTree = ""; }; - BCABC3821B3A3CB90099CAC5 /* NNSynthesizedObjectStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNSynthesizedObjectStorageTests.m; sourceTree = ""; }; - BCABC3841B3A438A0099CAC5 /* memoize.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = memoize.h; path = Hacks/memoize.h; sourceTree = ""; }; - BCABC3851B3A43980099CAC5 /* memoize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = memoize.m; path = Hacks/memoize.m; sourceTree = ""; }; - BCB1DBA4183D14B50081107F /* NNTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NNTestCase.h; sourceTree = ""; }; - BCB1DBA5183D14B50081107F /* NNTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNTestCase.m; sourceTree = ""; }; - BCB1DBA8183D196E0081107F /* nn_autofreeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = nn_autofreeTests.m; sourceTree = ""; }; - BCB8A26918BDBB5000E4AC18 /* NSCollections+NNComprehensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCollections+NNComprehensions.h"; path = "Collections/NSCollections+NNComprehensions.h"; sourceTree = ""; }; - BCB8A26A18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCollections+NNComprehensions.m"; path = "Collections/NSCollections+NNComprehensions.m"; sourceTree = ""; }; - BCCA79F318075C6F00CE36E5 /* libNNKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libNNKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; - BCCA79F418075C6F00CE36E5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - BCCA7A0418075C6F00CE36E5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - BCCA7A1718075DDB00CE36E5 /* NNKit iOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NNKit iOS-Prefix.pch"; sourceTree = ""; }; - BCCA7A1D1807608600CE36E5 /* NNKit iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NNKit iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - BCCA7A2F180761DB00CE36E5 /* NNKit iOS Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NNKit iOS Tests-Info.plist"; sourceTree = ""; }; - BCCB1BF11835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSNotificationCenter+NNAdditions.h"; path = "API Extension/NSNotificationCenter+NNAdditions.h"; sourceTree = ""; }; - BCCB1BF21835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSNotificationCenter+NNAdditions.m"; path = "API Extension/NSNotificationCenter+NNAdditions.m"; sourceTree = ""; }; - BCCB1BFD1835FD9A0029EBF6 /* NNWeakObserverTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNWeakObserverTests.m; sourceTree = ""; }; - BCD1D52917D9471A00A3EBD4 /* NNKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NNKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BCD1D52C17D9471A00A3EBD4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - BCD1D52F17D9471A00A3EBD4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - BCD1D53017D9471A00A3EBD4 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; - BCD1D53117D9471A00A3EBD4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - BCD1D53417D9471A00A3EBD4 /* NNKit-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NNKit-Info.plist"; sourceTree = ""; }; - BCD1D53817D9471A00A3EBD4 /* NNKit Mac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NNKit Mac-Prefix.pch"; sourceTree = ""; }; - BCD1D53917D9471A00A3EBD4 /* NNKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NNKit.h; sourceTree = ""; }; - BCD1D53A17D9471A00A3EBD4 /* NNKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NNKit.m; sourceTree = ""; }; - BCD1D54017D9471A00A3EBD4 /* NNKit Mac Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NNKit Mac Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - BCD1D54117D9471A00A3EBD4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; - BCD1D54917D9471A00A3EBD4 /* NNKit Mac Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NNKit Mac Tests-Info.plist"; sourceTree = ""; }; - BCD1D56717D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNSelfInvalidatingObjectTests.m; path = NNKitTests/NNSelfInvalidatingObjectTests.m; sourceTree = SOURCE_ROOT; }; - BCD1D56917D96C4500A3EBD4 /* NNDelegateProxyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNDelegateProxyTests.m; path = NNKitTests/NNDelegateProxyTests.m; sourceTree = SOURCE_ROOT; }; - BCD1D57F17D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNStrongifiedPropertiesTests.m; path = NNKitTests/NNStrongifiedPropertiesTests.m; sourceTree = SOURCE_ROOT; }; - BCD1D58717D9A14300A3EBD4 /* nn_isaSwizzling_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nn_isaSwizzling_Private.h; path = Swizzling/nn_isaSwizzling_Private.h; sourceTree = ""; }; - BCD1D58817D9A14300A3EBD4 /* nn_isaSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nn_isaSwizzling.h; path = Swizzling/nn_isaSwizzling.h; sourceTree = ""; }; - BCD1D58917D9A14300A3EBD4 /* nn_isaSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = nn_isaSwizzling.m; path = Swizzling/nn_isaSwizzling.m; sourceTree = ""; }; - BCD1D58A17D9A14300A3EBD4 /* NNISASwizzledObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNISASwizzledObject.h; path = Swizzling/NNISASwizzledObject.h; sourceTree = ""; }; - BCD1D58B17D9A14300A3EBD4 /* NNISASwizzledObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNISASwizzledObject.m; path = Swizzling/NNISASwizzledObject.m; sourceTree = ""; }; - BCD1D59117D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = nn_isaSwizzlingTests.m; path = NNKitTests/nn_isaSwizzlingTests.m; sourceTree = SOURCE_ROOT; }; - BCD1D59B17D9BAB000A3EBD4 /* NNPollingObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNPollingObjectTests.m; path = NNKitTests/NNPollingObjectTests.m; sourceTree = SOURCE_ROOT; }; - BCD1D5A517D9C72F00A3EBD4 /* despatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = despatch.h; path = "API Extension/despatch.h"; sourceTree = ""; }; - BCD1D5A617D9C72F00A3EBD4 /* despatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = despatch.m; path = "API Extension/despatch.m"; sourceTree = ""; }; - BCD1D5A717D9C72F00A3EBD4 /* runtime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = runtime.c; path = "API Extension/runtime.c"; sourceTree = ""; }; - BCD1D5A817D9C72F00A3EBD4 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runtime.h; path = "API Extension/runtime.h"; sourceTree = ""; }; - BCD1D5AD17D9C79F00A3EBD4 /* NNPollingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNPollingObject.h; path = Actors/NNPollingObject.h; sourceTree = ""; }; - BCD1D5AE17D9C79F00A3EBD4 /* NNPollingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNPollingObject.m; path = Actors/NNPollingObject.m; sourceTree = ""; }; - BCD1D5AF17D9C79F00A3EBD4 /* NNPollingObject+Protected.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NNPollingObject+Protected.h"; path = "Actors/NNPollingObject+Protected.h"; sourceTree = ""; }; - BCD1D5B017D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNSelfInvalidatingObject.h; path = Actors/NNSelfInvalidatingObject.h; sourceTree = ""; }; - BCD1D5B117D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNSelfInvalidatingObject.m; path = Actors/NNSelfInvalidatingObject.m; sourceTree = ""; }; - BCD1D5B717D9C84000A3EBD4 /* NNDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNDelegateProxy.h; path = Concurrency/NNDelegateProxy.h; sourceTree = ""; }; - BCD1D5B817D9C84000A3EBD4 /* NNDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNDelegateProxy.m; path = Concurrency/NNDelegateProxy.m; sourceTree = ""; }; - BCD1D5BB17D9C88F00A3EBD4 /* NNStrongifiedProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NNStrongifiedProperties.h; path = Hacks/NNStrongifiedProperties.h; sourceTree = ""; }; - BCD1D5BC17D9C88F00A3EBD4 /* NNStrongifiedProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NNStrongifiedProperties.m; path = Hacks/NNStrongifiedProperties.m; sourceTree = ""; }; - BCD1D5BF17DECC8E00A3EBD4 /* nn_autofree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nn_autofree.h; path = Hacks/nn_autofree.h; sourceTree = ""; }; - BCD1D5C017DECC8E00A3EBD4 /* nn_autofree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = nn_autofree.m; path = Hacks/nn_autofree.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - BCCA79F018075C6F00CE36E5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BCCA79F518075C6F00CE36E5 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCCA7A1A1807608600CE36E5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BCCA7A4C180765D800CE36E5 /* libNNKit.a in Frameworks */, - BCCA7A1E1807608600CE36E5 /* XCTest.framework in Frameworks */, - BCCA7A201807608600CE36E5 /* UIKit.framework in Frameworks */, - BCCA7A1F1807608600CE36E5 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D52517D9471A00A3EBD4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BCD1D52D17D9471A00A3EBD4 /* Cocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D53D17D9471A00A3EBD4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BCD1D54317D9471A00A3EBD4 /* Cocoa.framework in Frameworks */, - BCD1D54217D9471A00A3EBD4 /* XCTest.framework in Frameworks */, - BCD1D54617D9471A00A3EBD4 /* NNKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - BC1243631837022700461433 /* Collections */ = { - isa = PBXGroup; - children = ( - BC14BF0A1D03911D0041DAC2 /* _NNWeakArrayTombstone.h */, - BC14BF0B1D03911D0041DAC2 /* _NNWeakArrayTombstone.m */, - BC14BF0C1D03911D0041DAC2 /* _NNWeakSetEnumerator.h */, - BC14BF0D1D03911D0041DAC2 /* _NNWeakSetEnumerator.m */, - BC14BF011D038F230041DAC2 /* NNWeakSet.h */, - BC14BF021D038F230041DAC2 /* NNWeakSet.m */, - BCB8A26918BDBB5000E4AC18 /* NSCollections+NNComprehensions.h */, - BCB8A26A18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m */, - ); - name = Collections; - sourceTree = ""; - }; - BC19D3621810C63E009CEC1F /* Services */ = { - isa = PBXGroup; - children = ( - BC19D3631810C6C3009CEC1F /* NNService.h */, - BC82B3DC183C37E500FDD3B9 /* NNService+Protected.h */, - BC19D3641810C6C3009CEC1F /* NNService.m */, - BC19D36F1810C85B009CEC1F /* NNServiceManager.h */, - BC19D3701810C85B009CEC1F /* NNServiceManager.m */, - ); - name = Services; - sourceTree = ""; - }; - BCD1D51F17D9471A00A3EBD4 = { - isa = PBXGroup; - children = ( - BCD1D53217D9471A00A3EBD4 /* NNKit */, - BCD1D54717D9471A00A3EBD4 /* NNKitTests */, - BCD1D52B17D9471A00A3EBD4 /* Frameworks */, - BCD1D52A17D9471A00A3EBD4 /* Products */, - ); - sourceTree = ""; - }; - BCD1D52A17D9471A00A3EBD4 /* Products */ = { - isa = PBXGroup; - children = ( - BCD1D52917D9471A00A3EBD4 /* NNKit.framework */, - BCD1D54017D9471A00A3EBD4 /* NNKit Mac Tests.xctest */, - BCCA79F318075C6F00CE36E5 /* libNNKit.a */, - BCCA7A1D1807608600CE36E5 /* NNKit iOS Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - BCD1D52B17D9471A00A3EBD4 /* Frameworks */ = { - isa = PBXGroup; - children = ( - BCD1D52E17D9471A00A3EBD4 /* System Frameworks */, - ); - name = Frameworks; - sourceTree = ""; - }; - BCD1D52E17D9471A00A3EBD4 /* System Frameworks */ = { - isa = PBXGroup; - children = ( - BCD1D53117D9471A00A3EBD4 /* AppKit.framework */, - BCD1D52C17D9471A00A3EBD4 /* Cocoa.framework */, - BCD1D53017D9471A00A3EBD4 /* CoreData.framework */, - BCD1D52F17D9471A00A3EBD4 /* Foundation.framework */, - BCCA79F418075C6F00CE36E5 /* Foundation.framework */, - BCCA7A0418075C6F00CE36E5 /* UIKit.framework */, - BCD1D54117D9471A00A3EBD4 /* XCTest.framework */, - ); - name = "System Frameworks"; - sourceTree = ""; - }; - BCD1D53217D9471A00A3EBD4 /* NNKit */ = { - isa = PBXGroup; - children = ( - BCD1D53917D9471A00A3EBD4 /* NNKit.h */, - BCD1D53A17D9471A00A3EBD4 /* NNKit.m */, - BC961A8318BDB377009F3F94 /* macros.h */, - BCD1D58117D9940500A3EBD4 /* API extension */, - BCD1D56D17D9776400A3EBD4 /* Actors */, - BC1243631837022700461433 /* Collections */, - BCD1D55817D9474600A3EBD4 /* Concurrency */, - BCD1D57617D97CB100A3EBD4 /* Hacks */, - BC19D3621810C63E009CEC1F /* Services */, - BCD1D58617D9A0AF00A3EBD4 /* Swizzling */, - BCD1D53317D9471A00A3EBD4 /* Supporting Files */, - ); - path = NNKit; - sourceTree = ""; - }; - BCD1D53317D9471A00A3EBD4 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - BCCA7A1718075DDB00CE36E5 /* NNKit iOS-Prefix.pch */, - BCD1D53817D9471A00A3EBD4 /* NNKit Mac-Prefix.pch */, - BCD1D53417D9471A00A3EBD4 /* NNKit-Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BCD1D54717D9471A00A3EBD4 /* NNKitTests */ = { - isa = PBXGroup; - children = ( - BCABC3821B3A3CB90099CAC5 /* NNSynthesizedObjectStorageTests.m */, - BCD1D59117D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m */, - BCD1D56917D96C4500A3EBD4 /* NNDelegateProxyTests.m */, - BC82B3D9183C317000FDD3B9 /* NNMultiDispatchManagerTests.m */, - BCB1DBA8183D196E0081107F /* nn_autofreeTests.m */, - BCD1D59B17D9BAB000A3EBD4 /* NNPollingObjectTests.m */, - BCD1D56717D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m */, - BC19D39F1811BBC9009CEC1F /* NNServiceTests.m */, - BCD1D57F17D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m */, - BCCB1BFD1835FD9A0029EBF6 /* NNWeakObserverTests.m */, - BC14BF071D0390C70041DAC2 /* NNWeakSetTests.m */, - BCD1D54817D9471A00A3EBD4 /* Supporting Files */, - BC82B3C8183C1BC300FDD3B9 /* NNCleanupProxyTests.m */, - BC275C7518CFE4BD00761247 /* NNComprehensionTests.m */, - ); - path = NNKitTests; - sourceTree = ""; - }; - BCD1D54817D9471A00A3EBD4 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - BCB1DBA4183D14B50081107F /* NNTestCase.h */, - BCB1DBA5183D14B50081107F /* NNTestCase.m */, - BCCA7A2F180761DB00CE36E5 /* NNKit iOS Tests-Info.plist */, - BCD1D54917D9471A00A3EBD4 /* NNKit Mac Tests-Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BCD1D55817D9474600A3EBD4 /* Concurrency */ = { - isa = PBXGroup; - children = ( - BCD1D5B717D9C84000A3EBD4 /* NNDelegateProxy.h */, - BCD1D5B817D9C84000A3EBD4 /* NNDelegateProxy.m */, - ); - name = Concurrency; - sourceTree = ""; - }; - BCD1D56D17D9776400A3EBD4 /* Actors */ = { - isa = PBXGroup; - children = ( - BC12437D183AEFAC00461433 /* NNCleanupProxy.h */, - BC12437E183AEFAC00461433 /* NNCleanupProxy.m */, - BC82B3D3183C29B600FDD3B9 /* NNMultiDispatchManager.h */, - BC82B3D4183C29B600FDD3B9 /* NNMultiDispatchManager.m */, - BCD1D5AD17D9C79F00A3EBD4 /* NNPollingObject.h */, - BCD1D5AF17D9C79F00A3EBD4 /* NNPollingObject+Protected.h */, - BCD1D5AE17D9C79F00A3EBD4 /* NNPollingObject.m */, - BCD1D5B017D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h */, - BCD1D5B117D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m */, - ); - name = Actors; - sourceTree = ""; - }; - BCD1D57617D97CB100A3EBD4 /* Hacks */ = { - isa = PBXGroup; - children = ( - BCD1D5BF17DECC8E00A3EBD4 /* nn_autofree.h */, - BCD1D5C017DECC8E00A3EBD4 /* nn_autofree.m */, - BCD1D5BB17D9C88F00A3EBD4 /* NNStrongifiedProperties.h */, - BCD1D5BC17D9C88F00A3EBD4 /* NNStrongifiedProperties.m */, - BCABC3841B3A438A0099CAC5 /* memoize.h */, - BCABC3851B3A43980099CAC5 /* memoize.m */, - ); - name = Hacks; - sourceTree = ""; - }; - BCD1D58117D9940500A3EBD4 /* API extension */ = { - isa = PBXGroup; - children = ( - BC275C0818CE9D1A00761247 /* NSInvocation+NNCopying.h */, - BC275C0918CE9D1A00761247 /* NSInvocation+NNCopying.m */, - BCD1D5A517D9C72F00A3EBD4 /* despatch.h */, - BCD1D5A617D9C72F00A3EBD4 /* despatch.m */, - BCD1D5A817D9C72F00A3EBD4 /* runtime.h */, - BCD1D5A717D9C72F00A3EBD4 /* runtime.c */, - BCCB1BF11835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h */, - BCCB1BF21835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m */, - ); - name = "API extension"; - sourceTree = ""; - }; - BCD1D58617D9A0AF00A3EBD4 /* Swizzling */ = { - isa = PBXGroup; - children = ( - BCD1D58817D9A14300A3EBD4 /* nn_isaSwizzling.h */, - BCD1D58717D9A14300A3EBD4 /* nn_isaSwizzling_Private.h */, - BCD1D58917D9A14300A3EBD4 /* nn_isaSwizzling.m */, - BCD1D58A17D9A14300A3EBD4 /* NNISASwizzledObject.h */, - BCD1D58B17D9A14300A3EBD4 /* NNISASwizzledObject.m */, - ); - name = Swizzling; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - BCCA7A3F1807627C00CE36E5 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - BCCA7A401807628B00CE36E5 /* despatch.h in Headers */, - BCCA7A47180762B500CE36E5 /* nn_autofree.h in Headers */, - BCCA7A44180762A200CE36E5 /* NNSelfInvalidatingObject.h in Headers */, - BCCB1BF41835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h in Headers */, - BCCA7A421807629700CE36E5 /* NNPollingObject.h in Headers */, - BC14BF131D03911D0041DAC2 /* _NNWeakSetEnumerator.h in Headers */, - BCCA7A49180762C000CE36E5 /* nn_isaSwizzling.h in Headers */, - BCCA7A4A180762C700CE36E5 /* NNISASwizzledObject.h in Headers */, - BC82B3DE183C37E500FDD3B9 /* NNService+Protected.h in Headers */, - BC82B3D6183C29B600FDD3B9 /* NNMultiDispatchManager.h in Headers */, - BCCA7A431807629C00CE36E5 /* NNPollingObject+Protected.h in Headers */, - BC961A8518BDB377009F3F94 /* macros.h in Headers */, - BCCA7A45180762A800CE36E5 /* NNDelegateProxy.h in Headers */, - BCCA7A48180762BB00CE36E5 /* nn_isaSwizzling_Private.h in Headers */, - BC19D3661810C6C3009CEC1F /* NNService.h in Headers */, - BC19D3721810C85B009CEC1F /* NNServiceManager.h in Headers */, - BCCA7A4B180762CC00CE36E5 /* NNKit.h in Headers */, - BCCA7A46180762AF00CE36E5 /* NNStrongifiedProperties.h in Headers */, - BC14BF0F1D03911D0041DAC2 /* _NNWeakArrayTombstone.h in Headers */, - BC14BF041D038F230041DAC2 /* NNWeakSet.h in Headers */, - BCB8A26C18BDBB5000E4AC18 /* NSCollections+NNComprehensions.h in Headers */, - BC124380183AEFAC00461433 /* NNCleanupProxy.h in Headers */, - BC85E89318E78519001DF986 /* NSInvocation+NNCopying.h in Headers */, - BCCA7A411807629100CE36E5 /* runtime.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D52617D9471A00A3EBD4 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - BCD1D58D17D9A14300A3EBD4 /* nn_isaSwizzling.h in Headers */, - BCD1D58F17D9A14300A3EBD4 /* NNISASwizzledObject.h in Headers */, - BCD1D5B917D9C84000A3EBD4 /* NNDelegateProxy.h in Headers */, - BCCB1BF31835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.h in Headers */, - BCD1D5C117DECC8E00A3EBD4 /* nn_autofree.h in Headers */, - BC14BF121D03911D0041DAC2 /* _NNWeakSetEnumerator.h in Headers */, - BCD1D5B417D9C79F00A3EBD4 /* NNPollingObject+Protected.h in Headers */, - BCD1D5BD17D9C88F00A3EBD4 /* NNStrongifiedProperties.h in Headers */, - BC82B3DD183C37E500FDD3B9 /* NNService+Protected.h in Headers */, - BC82B3D5183C29B600FDD3B9 /* NNMultiDispatchManager.h in Headers */, - BC275C0B18CE9DA200761247 /* NSInvocation+NNCopying.h in Headers */, - BCD1D5AC17D9C72F00A3EBD4 /* runtime.h in Headers */, - BC961A8418BDB377009F3F94 /* macros.h in Headers */, - BCD1D5A917D9C72F00A3EBD4 /* despatch.h in Headers */, - BCD1D58C17D9A14300A3EBD4 /* nn_isaSwizzling_Private.h in Headers */, - BC19D3651810C6C3009CEC1F /* NNService.h in Headers */, - BC19D3711810C85B009CEC1F /* NNServiceManager.h in Headers */, - BCD1D5B517D9C79F00A3EBD4 /* NNSelfInvalidatingObject.h in Headers */, - BC14BF0E1D03911D0041DAC2 /* _NNWeakArrayTombstone.h in Headers */, - BC14BF031D038F230041DAC2 /* NNWeakSet.h in Headers */, - BCD1D55717D9472D00A3EBD4 /* NNKit.h in Headers */, - BCB8A26B18BDBB5000E4AC18 /* NSCollections+NNComprehensions.h in Headers */, - BC12437F183AEFAC00461433 /* NNCleanupProxy.h in Headers */, - BCD1D5B217D9C79F00A3EBD4 /* NNPollingObject.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - BCCA79F218075C6F00CE36E5 /* NNKit iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCCA7A1118075C6F00CE36E5 /* Build configuration list for PBXNativeTarget "NNKit iOS" */; - buildPhases = ( - BCCA7A3F1807627C00CE36E5 /* Headers */, - BCCA79EF18075C6F00CE36E5 /* Sources */, - BCCA79F018075C6F00CE36E5 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "NNKit iOS"; - productName = "NNKit iOS"; - productReference = BCCA79F318075C6F00CE36E5 /* libNNKit.a */; - productType = "com.apple.product-type.library.static"; - }; - BCCA7A1C1807608600CE36E5 /* NNKit iOS Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCCA7A2C1807608700CE36E5 /* Build configuration list for PBXNativeTarget "NNKit iOS Tests" */; - buildPhases = ( - BCCA7A191807608600CE36E5 /* Sources */, - BCCA7A1A1807608600CE36E5 /* Frameworks */, - BCCA7A1B1807608600CE36E5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - BCCA7A2B1807608700CE36E5 /* PBXTargetDependency */, - ); - name = "NNKit iOS Tests"; - productName = "NNKit iOS Tests"; - productReference = BCCA7A1D1807608600CE36E5 /* NNKit iOS Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - BCD1D52817D9471A00A3EBD4 /* NNKit Mac */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCD1D55117D9471A00A3EBD4 /* Build configuration list for PBXNativeTarget "NNKit Mac" */; - buildPhases = ( - BCD1D52617D9471A00A3EBD4 /* Headers */, - BCD1D52417D9471A00A3EBD4 /* Sources */, - BCD1D52517D9471A00A3EBD4 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "NNKit Mac"; - productName = NNKit; - productReference = BCD1D52917D9471A00A3EBD4 /* NNKit.framework */; - productType = "com.apple.product-type.framework"; - }; - BCD1D53F17D9471A00A3EBD4 /* NNKit Mac Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCD1D55417D9471A00A3EBD4 /* Build configuration list for PBXNativeTarget "NNKit Mac Tests" */; - buildPhases = ( - BCD1D53C17D9471A00A3EBD4 /* Sources */, - BCD1D53D17D9471A00A3EBD4 /* Frameworks */, - BCD1D53E17D9471A00A3EBD4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - BCD1D54517D9471A00A3EBD4 /* PBXTargetDependency */, - ); - name = "NNKit Mac Tests"; - productName = NNKitTests; - productReference = BCD1D54017D9471A00A3EBD4 /* NNKit Mac Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BCD1D52017D9471A00A3EBD4 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0600; - ORGANIZATIONNAME = "Scott Perry"; - TargetAttributes = { - BCCA7A1C1807608600CE36E5 = { - TestTargetID = BCD1D52817D9471A00A3EBD4; - }; - }; - }; - buildConfigurationList = BCD1D52317D9471A00A3EBD4 /* Build configuration list for PBXProject "NNKit" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - English, - en, - ); - mainGroup = BCD1D51F17D9471A00A3EBD4; - productRefGroup = BCD1D52A17D9471A00A3EBD4 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - BCD1D52817D9471A00A3EBD4 /* NNKit Mac */, - BCD1D53F17D9471A00A3EBD4 /* NNKit Mac Tests */, - BCCA79F218075C6F00CE36E5 /* NNKit iOS */, - BCCA7A1C1807608600CE36E5 /* NNKit iOS Tests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - BCCA7A1B1807608600CE36E5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D53E17D9471A00A3EBD4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - BCCA79EF18075C6F00CE36E5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BC85E88E18E78331001DF986 /* NSInvocation+NNCopying.m in Sources */, - BCCA7A351807626200CE36E5 /* despatch.m in Sources */, - BCCA7A361807626200CE36E5 /* runtime.c in Sources */, - BCCA7A371807626200CE36E5 /* NNPollingObject.m in Sources */, - BC82B3D8183C29B600FDD3B9 /* NNMultiDispatchManager.m in Sources */, - BCCB1BF61835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m in Sources */, - BC14BF151D03911D0041DAC2 /* _NNWeakSetEnumerator.m in Sources */, - BCCA7A381807626200CE36E5 /* NNSelfInvalidatingObject.m in Sources */, - BC124382183AEFAC00461433 /* NNCleanupProxy.m in Sources */, - BCCA7A391807626200CE36E5 /* NNDelegateProxy.m in Sources */, - BC19D3681810C6C3009CEC1F /* NNService.m in Sources */, - BCB8A26E18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m in Sources */, - BCCA7A3A1807626200CE36E5 /* NNStrongifiedProperties.m in Sources */, - BC14BF061D038F230041DAC2 /* NNWeakSet.m in Sources */, - BCCA7A3B1807626200CE36E5 /* nn_autofree.m in Sources */, - BCCA7A3C1807626200CE36E5 /* nn_isaSwizzling.m in Sources */, - BCCA7A3D1807626200CE36E5 /* NNISASwizzledObject.m in Sources */, - BC19D3741810C85B009CEC1F /* NNServiceManager.m in Sources */, - BCCA7A3E1807626200CE36E5 /* NNKit.m in Sources */, - BC14BF111D03911D0041DAC2 /* _NNWeakArrayTombstone.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCCA7A191807608600CE36E5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCCA7A301807622200CE36E5 /* NNPollingObjectTests.m in Sources */, - BCCA7A311807622200CE36E5 /* NNSelfInvalidatingObjectTests.m in Sources */, - BCB1DBA7183D14B50081107F /* NNTestCase.m in Sources */, - BC82B3CA183C1BC300FDD3B9 /* NNCleanupProxyTests.m in Sources */, - BCCB1BFF1835FD9A0029EBF6 /* NNWeakObserverTests.m in Sources */, - BCCA7A321807622200CE36E5 /* NNDelegateProxyTests.m in Sources */, - BC82B3DB183C317000FDD3B9 /* NNMultiDispatchManagerTests.m in Sources */, - BC19D3A11811BBC9009CEC1F /* NNServiceTests.m in Sources */, - BCCA7A331807622200CE36E5 /* NNStrongifiedPropertiesTests.m in Sources */, - BCCA7A341807622200CE36E5 /* nn_isaSwizzlingTests.m in Sources */, - BC275C7718CFE4BD00761247 /* NNComprehensionTests.m in Sources */, - BC14BF091D0390C70041DAC2 /* NNWeakSetTests.m in Sources */, - BCB1DBAA183D196E0081107F /* nn_autofreeTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D52417D9471A00A3EBD4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCABC3861B3A43980099CAC5 /* memoize.m in Sources */, - BC275C0A18CE9D9D00761247 /* NSInvocation+NNCopying.m in Sources */, - BCD1D5AA17D9C72F00A3EBD4 /* despatch.m in Sources */, - BCD1D5BE17D9C88F00A3EBD4 /* NNStrongifiedProperties.m in Sources */, - BC14BF051D038F230041DAC2 /* NNWeakSet.m in Sources */, - BCD1D53B17D9471A00A3EBD4 /* NNKit.m in Sources */, - BC82B3D7183C29B600FDD3B9 /* NNMultiDispatchManager.m in Sources */, - BCCB1BF51835F3E80029EBF6 /* NSNotificationCenter+NNAdditions.m in Sources */, - BCD1D5AB17D9C72F00A3EBD4 /* runtime.c in Sources */, - BC124381183AEFAC00461433 /* NNCleanupProxy.m in Sources */, - BCD1D5C217DECC8E00A3EBD4 /* nn_autofree.m in Sources */, - BC19D3671810C6C3009CEC1F /* NNService.m in Sources */, - BCB8A26D18BDBB5000E4AC18 /* NSCollections+NNComprehensions.m in Sources */, - BCD1D5B317D9C79F00A3EBD4 /* NNPollingObject.m in Sources */, - BCD1D5B617D9C79F00A3EBD4 /* NNSelfInvalidatingObject.m in Sources */, - BC14BF141D03911D0041DAC2 /* _NNWeakSetEnumerator.m in Sources */, - BC14BF101D03911D0041DAC2 /* _NNWeakArrayTombstone.m in Sources */, - BCD1D59017D9A14300A3EBD4 /* NNISASwizzledObject.m in Sources */, - BCD1D5BA17D9C84000A3EBD4 /* NNDelegateProxy.m in Sources */, - BC19D3731810C85B009CEC1F /* NNServiceManager.m in Sources */, - BCD1D58E17D9A14300A3EBD4 /* nn_isaSwizzling.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCD1D53C17D9471A00A3EBD4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCD1D56817D963E000A3EBD4 /* NNSelfInvalidatingObjectTests.m in Sources */, - BCD1D59217D9A34D00A3EBD4 /* nn_isaSwizzlingTests.m in Sources */, - BCB1DBA6183D14B50081107F /* NNTestCase.m in Sources */, - BC82B3C9183C1BC300FDD3B9 /* NNCleanupProxyTests.m in Sources */, - BCCB1BFE1835FD9A0029EBF6 /* NNWeakObserverTests.m in Sources */, - BCD1D58017D9818500A3EBD4 /* NNStrongifiedPropertiesTests.m in Sources */, - BCABC3831B3A3CB90099CAC5 /* NNSynthesizedObjectStorageTests.m in Sources */, - BC82B3DA183C317000FDD3B9 /* NNMultiDispatchManagerTests.m in Sources */, - BC14BF081D0390C70041DAC2 /* NNWeakSetTests.m in Sources */, - BC19D3A01811BBC9009CEC1F /* NNServiceTests.m in Sources */, - BCD1D56A17D96C4500A3EBD4 /* NNDelegateProxyTests.m in Sources */, - BCD1D59C17D9BAB000A3EBD4 /* NNPollingObjectTests.m in Sources */, - BC275C7618CFE4BD00761247 /* NNComprehensionTests.m in Sources */, - BCB1DBA9183D196E0081107F /* nn_autofreeTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - BCCA7A2B1807608700CE36E5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BCCA79F218075C6F00CE36E5 /* NNKit iOS */; - targetProxy = BCCA7A2A1807608700CE36E5 /* PBXContainerItemProxy */; - }; - BCD1D54517D9471A00A3EBD4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BCD1D52817D9471A00A3EBD4 /* NNKit Mac */; - targetProxy = BCD1D54417D9471A00A3EBD4 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - BCCA7A1218075C6F00CE36E5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DSTROOT = /tmp/NNKit_iOS.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/$(TARGET_NAME)-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/$(PROJECT_NAME)/Private"; - PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include/$(PROJECT_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - BCCA7A1318075C6F00CE36E5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - DSTROOT = /tmp/NNKit_iOS.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/$(TARGET_NAME)-Prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/$(PROJECT_NAME)/Private"; - PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include/$(PROJECT_NAME)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - BCCA7A2D1807608700CE36E5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/libNNKit.a"; - CLANG_ENABLE_MODULES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/NNKit iOS-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = "NNKitTests/NNKit iOS Tests-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", - "-framework", - XCTest, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - TEST_HOST = "$(BUNDLE_LOADER)"; - WRAPPER_EXTENSION = xctest; - }; - name = Debug; - }; - BCCA7A2E1807608700CE36E5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/libNNKit.a"; - CLANG_ENABLE_MODULES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - "$(DEVELOPER_FRAMEWORKS_DIR)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/NNKit iOS-Prefix.pch"; - INFOPLIST_FILE = "NNKitTests/NNKit iOS Tests-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", - "-framework", - XCTest, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - TEST_HOST = "$(BUNDLE_LOADER)"; - VALIDATE_PRODUCT = YES; - WRAPPER_EXTENSION = xctest; - }; - name = Release; - }; - BCD1D54F17D9471A00A3EBD4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = NO; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_RECEIVER_WEAK = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; - OTHER_CFLAGS = "-Wno-incompatible-pointer-types"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = macosx; - }; - name = Debug; - }; - BCD1D55017D9471A00A3EBD4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = NO; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_RECEIVER_WEAK = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.8; - OTHER_CFLAGS = "-Wno-incompatible-pointer-types"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(PROJECT_NAME)"; - SDKROOT = macosx; - }; - name = Release; - }; - BCD1D55217D9471A00A3EBD4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/$(TARGET_NAME)-Prefix.pch"; - INFOPLIST_FILE = "NNKit/NNKit-Info.plist"; - INSTALL_PATH = "@rpath"; - LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; - WRAPPER_EXTENSION = framework; - }; - name = Debug; - }; - BCD1D55317D9471A00A3EBD4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - FRAMEWORK_VERSION = A; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/$(TARGET_NAME)-Prefix.pch"; - INFOPLIST_FILE = "NNKit/NNKit-Info.plist"; - INSTALL_PATH = "@rpath"; - LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; - WRAPPER_EXTENSION = framework; - }; - name = Release; - }; - BCD1D55517D9471A00A3EBD4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/NNKit Mac-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = "NNKitTests/NNKit Mac Tests-Info.plist"; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = xctest; - }; - name = Debug; - }; - BCD1D55617D9471A00A3EBD4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(DEVELOPER_FRAMEWORKS_DIR)", - "$(inherited)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "NNKit/NNKit Mac-Prefix.pch"; - INFOPLIST_FILE = "NNKitTests/NNKit Mac Tests-Info.plist"; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = xctest; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - BCCA7A1118075C6F00CE36E5 /* Build configuration list for PBXNativeTarget "NNKit iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCCA7A1218075C6F00CE36E5 /* Debug */, - BCCA7A1318075C6F00CE36E5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCCA7A2C1807608700CE36E5 /* Build configuration list for PBXNativeTarget "NNKit iOS Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCCA7A2D1807608700CE36E5 /* Debug */, - BCCA7A2E1807608700CE36E5 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCD1D52317D9471A00A3EBD4 /* Build configuration list for PBXProject "NNKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCD1D54F17D9471A00A3EBD4 /* Debug */, - BCD1D55017D9471A00A3EBD4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCD1D55117D9471A00A3EBD4 /* Build configuration list for PBXNativeTarget "NNKit Mac" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCD1D55217D9471A00A3EBD4 /* Debug */, - BCD1D55317D9471A00A3EBD4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCD1D55417D9471A00A3EBD4 /* Build configuration list for PBXNativeTarget "NNKit Mac Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCD1D55517D9471A00A3EBD4 /* Debug */, - BCD1D55617D9471A00A3EBD4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BCD1D52017D9471A00A3EBD4 /* Project object */; -} diff --git a/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.h b/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.h deleted file mode 100644 index e1a2aa79..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSInvocation+NNCopying.h -// NNKit -// -// Created by Scott Perry on 03/10/14. -// Copyright © 2014 Scott Perry. -// -// 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 - -@interface NSInvocation (NNCopying) - -- (instancetype)nn_copy; - -@end diff --git a/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.m b/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.m deleted file mode 100644 index bd205e9f..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/NSInvocation+NNCopying.m +++ /dev/null @@ -1,58 +0,0 @@ -// -// NSInvocation+NNCopying.m -// NNKit -// -// Created by Scott Perry on 03/10/14. -// Copyright © 2014 Scott Perry. -// -// 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 "NSInvocation+NNCopying.h" - -@implementation NSInvocation (NNCopying) - -- (instancetype)nn_copy; -{ - NSMethodSignature *signature = [self methodSignature]; - NSUInteger const arguments = signature.numberOfArguments; - - NSInvocation *result = [NSInvocation invocationWithMethodSignature:signature]; - - void *heapBuffer = NULL; - size_t heapBufferSize = 0; - - NSUInteger alignp = 0; - for (NSUInteger i = 0; i < arguments; i++) { - const char *type = [signature getArgumentTypeAtIndex:i]; - NSGetSizeAndAlignment(type, NULL, &alignp); - - if (alignp > heapBufferSize) { - heapBuffer = heapBuffer - ? reallocf(heapBuffer, alignp) - : malloc(alignp); - heapBufferSize = alignp; - } - - [self getArgument:heapBuffer atIndex:i]; - [result setArgument:heapBuffer atIndex:i]; - } - - if (heapBuffer) { - free(heapBuffer); - } - - result.target = self.target; - - if (self.argumentsRetained) { - [result retainArguments]; - } - - return result; -} - -@end diff --git a/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.h b/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.h deleted file mode 100644 index 8996fb18..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// NSNotificationCenter+NNAdditions.h -// NNKit -// -// Created by Scott Perry on 11/14/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @category NNAdditions - * - * @discussion - * Provides weak observer support to NSNotificationCenter. - */ -@interface NSNotificationCenter (NNAdditions) - -/*! - * @method addWeakObserver:selector:name:object: - * - * @abstract - * Adds an entry to the receiver's dispatch table with an observer, a notification - * selector and optional criteria: notification name and sender. - * - * @discussion - * The observer is referenced weakly and does not need to be explicitly removed when - * the object is deallocated. - * - * @param observer - * Object registering as an observer. This value must not be nil. - * - * @param aSelector - * Selector that specifies the message the receiver sends observer to notify - * it of the notification posting. The method specified by aSelector must have - * one and only one argument (an instance of NSNotification). - * - * @param aName - * The name of the notification for which to register the observer; that is, only - * notifications with this name are delivered to the observer. If you pass - * nil, the notification center doesn’t use a notification’s name to - * decide whether to deliver it to the observer. - * - * @param anObject - * The object whose notifications the observer wants to receive; that is, only - * notifications sent by this sender are delivered to the observer. If you pass - * nil, the notification center doesn’t use a notification’s sender - * to decide whether to deliver it to the observer. - */ -- (void)addWeakObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject; - -@end diff --git a/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.m b/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.m deleted file mode 100644 index b1816658..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/NSNotificationCenter+NNAdditions.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// NSNotificationCenter+NNAdditions.m -// NNKit -// -// Created by Scott Perry on 11/14/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NSNotificationCenter+NNAdditions.h" - -#import "NNCleanupProxy.h" - - -@implementation NSNotificationCenter (NNAdditions) - -- (void)addWeakObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject; -{ - NSString *key = [NSString stringWithFormat:@"%p-%@-%@-%p", anObject, NSStringFromSelector(aSelector), aName, self]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:observer withKey:[key hash]]; - - __weak NSNotificationCenter *weakCenter = self; - __unsafe_unretained NNCleanupProxy *unsafeProxy = proxy; - - [proxy cacheMethodSignatureForSelector:aSelector]; - proxy.cleanupBlock = ^{ - NSNotificationCenter *center = weakCenter; - - [center removeObserver:unsafeProxy name:aName object:anObject]; - }; - [self addObserver:proxy selector:aSelector name:aName object:anObject]; -} - -@end diff --git a/Frameworks/NNKit/NNKit/API Extension/README.md b/Frameworks/NNKit/NNKit/API Extension/README.md deleted file mode 100644 index 99c6de85..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/README.md +++ /dev/null @@ -1,48 +0,0 @@ -NNKit API Extensions -==================== - -For its own internal use, as well as your enjoyment, NNKit contains a set of extensions to existing system APIs. - -despatch --------- - -[Despatch](http://numist.net/define/?despatch) contains helper functions to make dealing with GCD easier. At the moment this is one function: - -### `despatch_sync_main_reentrant` #### - -`despatch_sync_main_reentrant` is a function for making synchronous dispatch onto the main queue simpler. The block argument is invoked directly if the sender is already executing on the main thread, and dispatched synchronously onto the main queue otherwise. - -### `despatch_group_yield` ### - -The yield concept is borrowed from Python and other languages as a way for a path of execution to pause and allow other work scheduled for that thread to proceed. In this case it's most useful in unit tests to allow asynchronous work that takes place on the main thread to proceed. - -A very basic example from `NNDelegateProxyTests.m`: - - - (void)testGlobalAsync; - { - dispatch_group_enter(group); - [[[MYClass alloc] initWithDelegate:self] globalAsync]; - - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:0.1]; - while (!despatch_group_yield(group) && [[NSDate date] compare:timeout] == NSOrderedAscending); - XCTAssertFalse(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was never received (timed out)!"); - } - -runtime -------- - -Runtime provides functions that should exist in the Objective-C runtime, but don't. - -### `nn_selector_belongsToProtocol` ### - -`nn_selector_belongsToProtocol` returns whether or not a selector belongs to a protocol, with additional arguments that inform its search pattern and return information about the selector found in the protocol. Providing default values for `instance` and `required` begin the search with those attributes, and their values on return indicate the attributes of the first match found. - -NSNotificationCenter --------------------- - -The `NNAdditions` category to `NSNotificationCenter` adds a `addWeakObserver:selector:name:object:` method which references the observer weakly so no observer removal is required—it is automatically cleaned up when the observer is deallocated. - -NSInvocation ------------- - -The `NNCopying` category to `NSInvocation` adds an `nn_copy` method which returns a copy of the receiver. diff --git a/Frameworks/NNKit/NNKit/API Extension/despatch.h b/Frameworks/NNKit/NNKit/API Extension/despatch.h deleted file mode 100644 index 6a76053b..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/despatch.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// despatch.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -#ifndef NNKit_despatch_h -#define NNKit_despatch_h - -/*! - * @function despatch_sync_main_reentrant - * - * @abstract - * Runs a block on the main queue, reentrantly if already on the main queue. - * - * @discussion - * When on the main queue, executes the block synchronously before returning. - * Otherwise, submits a block to the main queue and does not return until the block - * has finished. - * - * @param block - * The block to be invoked on the main queue. - * The result of passing NULL in this parameter is undefined. - * Which is to say it will probably crash. - */ -void despatch_sync_main_reentrant(dispatch_block_t block); - -/*! - * @function despatch_group_yield - * - * @abstract - * Yields control of the current runloop. Return value indicates if the dispatch - * group is clear. - * - * @discussion - * When waiting for asynchronous jobs in a dispatch group that may block on the - * current thread, this function yields the runloop and then returns the group - * state. - * - * @param group - * The group for which the caller is waiting. - * - * @result - * YES if the group has no members, NO otherwise. - */ -BOOL despatch_group_yield(dispatch_group_t group); - -#endif diff --git a/Frameworks/NNKit/NNKit/API Extension/despatch.m b/Frameworks/NNKit/NNKit/API Extension/despatch.m deleted file mode 100644 index c443c881..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/despatch.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// despatch.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -#include "despatch.h" - - -void despatch_sync_main_reentrant(dispatch_block_t block) -{ - if ([[NSThread currentThread] isMainThread]) { - block(); - } else { - dispatch_sync(dispatch_get_main_queue(), block); - } -} - -BOOL despatch_group_yield(dispatch_group_t group) -{ - // Let the runloop consume another event. - NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; - assert(currentRunLoop); // I sure have gotten paranoid in my old age. - (void)[currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - - // dispatch_group_wait docs say it returns zero or nonzero. Luckily, it needs to be inverted anyway, so a valid BOOL value gets enforced. - return !dispatch_group_wait(group, DISPATCH_TIME_NOW); -} diff --git a/Frameworks/NNKit/NNKit/API Extension/runtime.c b/Frameworks/NNKit/NNKit/API Extension/runtime.c deleted file mode 100644 index 4c7b6b11..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/runtime.c +++ /dev/null @@ -1,43 +0,0 @@ -// -// runtime.c -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -#include "runtime.h" - -#include -#include - - -BOOL nn_selector_belongsToProtocol(SEL selector, Protocol *protocol, BOOL *requiredPtr, BOOL *instancePtr) -{ - BOOL required = requiredPtr ? !!*requiredPtr : NO; - BOOL instance = instancePtr ? !!*instancePtr : NO; - - for (int i = 0; i < (1 << 2); ++i) { - BOOL checkRequired = required ^ (i & 1); - BOOL checkInstance = instance ^ ((i & (1 << 1)) >> 1); - - struct objc_method_description hasMethod = protocol_getMethodDescription(protocol, selector, checkRequired, checkInstance); - if (hasMethod.name || hasMethod.types) { - if (requiredPtr) { - *requiredPtr = checkRequired; - } - if (instancePtr) { - *instancePtr = checkInstance; - } - return YES; - } - } - - return NO; -} diff --git a/Frameworks/NNKit/NNKit/API Extension/runtime.h b/Frameworks/NNKit/NNKit/API Extension/runtime.h deleted file mode 100644 index 0122a1c9..00000000 --- a/Frameworks/NNKit/NNKit/API Extension/runtime.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// runtime.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -#ifndef NNKit_rantime_h -#define NNKit_rantime_h - -#include - -#import - -// Memory-backed property generation for categories -#define NNSynthesizeObjectStorage(type, name, getter, setter) \ -- (type)getter { return objc_getAssociatedObject(self, NNSelfSelector(getter)); } \ -- (void)setter(type)name { \ - int association = [NNMemoize(^{ \ - objc_property_t property = class_getProperty([self class], #name); \ - int association = OBJC_ASSOCIATION_ASSIGN; \ - char *value = NULL; \ - if ((value = property_copyAttributeValue(property, "W"))) { \ - free(value); \ - @throw [NSException exceptionWithName:@"oops" reason:@"Not implemented" userInfo:nil]; \ - } else if ((value = property_copyAttributeValue(property, "C"))) { \ - free(value); \ - association = OBJC_ASSOCIATION_COPY; \ - } else if ((value = property_copyAttributeValue(property, "&"))) { \ - free(value); \ - association = OBJC_ASSOCIATION_RETAIN; \ - } \ - return @(association); \ - }) intValue]; \ - objc_setAssociatedObject(self, NNSelfSelector(getter), name, association); \ -} - -/*! - * @function nn_selector_belongsToProtocol - * - * @abstract - * Returns whether a selector belongs to a specific protocol. - * - * @discussion - * Search hinting can be performed by using the required and instance parameters, which are set on - * successful match to the instance/class, required/optional settings of the match. - * - * @param selector - * The selector to be found. - * - * @param protocol - * The protocol to be searched. - * - * @param required - * Whether the match should be required or not. Can be NULL. - * If not-NULL and the selector is found in the protocol, - * the parameter is set to the requirement setting of the match. - * - * @param instance - * Whether the match should be instance or class-level. Can be NULL. - * If not-NULL and the selector is found in the protocol, the - * parameter is set to the instance/class setting of the match. - * - * @result - * YES if the protocol contains any selector matching selector. - * NO otherwise. - */ -BOOL nn_selector_belongsToProtocol(SEL selector, Protocol *protocol, BOOL *required, BOOL *instance); - -#endif diff --git a/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.h b/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.h deleted file mode 100644 index c8977db8..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// NNCleanupProxy.h -// NNKit -// -// Created by Scott Perry on 11/18/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @class NNCleanupProxy - * - * @discussion - * A cleanup proxy is used when a weak reference to an object is desired, but not possible. An example - * of this is registering for notifications—the notification center should maintain a weak reference - * to the object instead of requiring bracketed add/remove observer calls. - * - * The one caveat to using a weak proxy in this situation is that method signatures must be pre-cached, - * since the runtime will explode if a nil method signature is returned during the message forwarding - * process. The NNCleanupProxy object will helpfully assert if a method signature does not already - * exist in cache at the time it is needed. - * - * For example use, see - * NSNotificationCenter+NNAdditions addWeakObserver:selector:name:object:. - */ -@interface NNCleanupProxy : NSProxy - -/*! - * @method cleanupProxyForTarget: - * - * @abstract - * Creates a proxy object holding a weak reference to, forwarding messages to, and with an object - * lifetime dependant on target. - */ -+ (NNCleanupProxy *)cleanupProxyForTarget:(id)target withKey:(uintptr_t)key; - -/*! - * @method cleanupProxyForTarget:conformingToProtocol: - * - * @abstract - * Creates a proxy object holding a weak reference to, forwarding messages to, and with an object - * lifetime dependant on target, conforming to protocol protocol. - */ -+ (NNCleanupProxy *)cleanupProxyForTarget:(id)target conformingToProtocol:(Protocol *)protocol withKey:(uintptr_t)key; - -+ (void)cleanupAfterTarget:(id)target withBlock:(void (^)())block withKey:(uintptr_t)key; - -+ (void)cancelCleanupForTarget:(id)target withKey:(uintptr_t)key; - -/*! - * @property cleanupBlock - * - * @abstract - * The block run when target is deallocated (in the absence of other strong references to the proxy). - * - * @discussion - * This block is used to clean up any registrations that have been made for the proxy that require - * bracketed calls to remove. Messages sent to the proxy intended for its target will react - * with the same semantics as messaging nil. - */ -@property (nonatomic, readwrite, copy) void (^cleanupBlock)(); - -/*! - * @method cacheMethodSignatureForSelector: - * - * @abstract - * Caches the method signature of a selector that the proxy is expected to forward to the target. - * - * @discussion - * If an unexpected method is sent to the proxy with the expectation that it will be forwarded, - * the proxy will throw an exception as if the selector is not recognized. Likewise if a method - * signature cannot be cached, the proxy will throw an exception. - * - * @param aSelector - * The selector for which the method signature should be fetched from the proxy's target. - */ -- (void)cacheMethodSignatureForSelector:(SEL)aSelector; - -//- (void)cacheMethodSignaturesForProtocol:(Protocol)aProtocol; - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.m b/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.m deleted file mode 100644 index f0e5046b..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNCleanupProxy.m +++ /dev/null @@ -1,144 +0,0 @@ -// -// NNCleanupProxy.m -// NNKit -// -// Created by Scott Perry on 11/18/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNCleanupProxy.h" - -#import - -#import "nn_autofree.h" - - -@interface NNCleanupProxy () - -@property (nonatomic, readonly, weak) NSObject *target; -@property (nonatomic, readonly, assign) NSUInteger hash; -@property (nonatomic, readonly, strong) NSMutableDictionary *signatureCache; - -@end - - -// XXX: rdar://15478132 means no explicit local strongification here due to retain leak :( -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreceiver-is-weak" - -@implementation NNCleanupProxy - -+ (NNCleanupProxy *)cleanupProxyForTarget:(id)target withKey:(uintptr_t)key; -{ - return [self cleanupProxyForTarget:target conformingToProtocol:@protocol(NSObject) withKey:key]; -} - -+ (NNCleanupProxy *)cleanupProxyForTarget:(id)target conformingToProtocol:(Protocol *)protocol withKey:(uintptr_t)key; -{ - NSParameterAssert([target conformsToProtocol:protocol]); - - NNCleanupProxy *result = [NNCleanupProxy alloc]; - result->_target = target; - result->_signatureCache = [NSMutableDictionary new]; - objc_setAssociatedObject(target, (void *)key, result, OBJC_ASSOCIATION_RETAIN); - - [result cacheMethodSignaturesForProcotol:protocol]; - - return result; -} - -+ (void)cleanupAfterTarget:(id)target withBlock:(void (^)())block withKey:(uintptr_t)key; -{ - NNCleanupProxy *result = [NNCleanupProxy cleanupProxyForTarget:target withKey:(uintptr_t)key]; - result.cleanupBlock = block; -} - -+ (void)cancelCleanupForTarget:(id)target withKey:(uintptr_t)key; -{ - objc_setAssociatedObject(target, (void *)key, nil, OBJC_ASSOCIATION_RETAIN); -} - -- (void)dealloc; -{ - // If the proxy is in dealloc and the target is still live, then no cleanup is needed—the proxy has been removed or replaced. - if (self->_target) { - return; - } - - if (self->_cleanupBlock) { - self->_cleanupBlock(); - } -} - -#pragma mark NSObject protocol - -@synthesize hash = _hash; -- (NSUInteger)hash; -{ - @synchronized(self) { - if (!self->_hash) { - self->_hash = self.target.hash; - } - } - - return self->_hash; -} - -- (BOOL)isEqual:(id)object; -{ - return [object isEqual:self.target]; -} - -#pragma mark Message forwarding - -- (id)forwardingTargetForSelector:(SEL)aSelector; -{ - if ([self.signatureCache objectForKey:NSStringFromSelector(aSelector)]) { - return self.target; - } - - return self; -} - -#pragma mark NNCleanupProxy - -- (void)cacheMethodSignatureForSelector:(SEL)aSelector; -{ - NSMethodSignature *signature = [self.target methodSignatureForSelector:aSelector]; - - if (signature) { - [self.signatureCache setObject:signature forKey:NSStringFromSelector(aSelector)]; - } else { - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Unable to get method signature for selector %@ from target of instance %p", NSStringFromSelector(aSelector), self] userInfo:nil]; - } -} - -// This could be faster/lighter if method signature was late-binding, at the cost of higher complexity. -- (void)cacheMethodSignaturesForProcotol:(Protocol *)protocol; -{ - unsigned int totalCount; - for (uint8_t i = 0; i < 1 << 1; ++i) { - struct objc_method_description *methodDescriptions = nn_autofree(protocol_copyMethodDescriptionList(protocol, i & 1, YES, &totalCount)); - - for (unsigned j = 0; j < totalCount; j++) { - struct objc_method_description *methodDescription = methodDescriptions + j; - [self.signatureCache setObject:[NSMethodSignature signatureWithObjCTypes:methodDescription->types] forKey:NSStringFromSelector(methodDescription->name)]; - } - } - - // Recurse to include other protocols to which this protocol adopts - Protocol * __unsafe_unretained *adoptions = protocol_copyProtocolList(protocol, &totalCount); - for (unsigned j = 0; j < totalCount; j++) { - [self cacheMethodSignaturesForProcotol:adoptions[j]]; - } -} - -@end - -#pragma clang diagnostic pop diff --git a/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.h b/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.h deleted file mode 100644 index ee919265..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// NNMultiDispatchManager.h -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface NNMultiDispatchManager : NSObject - -@property (nonatomic, assign, readwrite, getter = isEnabled, setter = setIsEnabled:) BOOL enabled; -@property (nonatomic, readonly, assign) Protocol *protocol; - -- (instancetype)initWithProtocol:(Protocol *)protocol; - -- (void)addObserver:(id)observer; -- (BOOL)hasObserver:(id)observer; -- (void)removeObserver:(id)observer; - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.m b/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.m deleted file mode 100644 index aa8261fb..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNMultiDispatchManager.m +++ /dev/null @@ -1,167 +0,0 @@ -// -// NNMultiDispatchManager.m -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNMultiDispatchManager.h" - -#import "NNWeakSet.h" -#import "despatch.h" -#import "nn_autofree.h" -#import "NSInvocation+NNCopying.h" -#import "runtime.h" - - -@interface NNMultiDispatchManager () - -@property (nonatomic, readonly, strong) NSMutableDictionary *signatureCache; -@property (nonatomic, readonly, strong) NNWeakSet *observers; - -@end - - -@implementation NNMultiDispatchManager - -- (instancetype)initWithProtocol:(Protocol *)protocol; -{ - if (!(self = [super init])) { return nil; } - - self->_enabled = YES; - self->_protocol = protocol; - self->_signatureCache = [NSMutableDictionary new]; - [self _cacheMethodSignaturesForProcotol:protocol]; - self->_observers = [NNWeakSet new]; - - return self; -} - -@synthesize enabled = _enabled; - -- (BOOL)isEnabled; -{ - @synchronized(self) { - return self->_enabled; - } -} - -- (void)setIsEnabled:(BOOL)enabled; -{ - @synchronized(self) { - self->_enabled = enabled; - } -} - -- (void)addObserver:(id)observer; -{ - NSParameterAssert([observer conformsToProtocol:self.protocol]); - - @synchronized(self) { - [self.observers addObject:observer]; - } -} - -- (BOOL)hasObserver:(id)observer; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - - @synchronized(self) { - return [self.observers containsObject:observer]; - } -} - -- (void)removeObserver:(id)observer; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - - @synchronized(self) { - [self.observers removeObject:observer]; - } -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; -{ - @synchronized(self) { - return [self.signatureCache objectForKey:NSStringFromSelector(aSelector)]; - } -} - -- (void)forwardInvocation:(NSInvocation *)anInvocation; -{ - @synchronized(self) { - if (self.enabled) { - if (anInvocation.methodSignature.isOneway) { - // If we're going async, copy the invocation to avoid multiple threads calling -invoke or otherwise acting in a thread-unsafe manner. - anInvocation = [anInvocation nn_copy]; - [anInvocation retainArguments]; - } - - NSAssert(strstr(anInvocation.methodSignature.methodReturnType, "v"), @"Method return type must be void."); - dispatch_block_t dispatch = ^{ - [self private_forwardInvocation:anInvocation]; - }; - if (anInvocation.methodSignature.isOneway) { - dispatch_async(dispatch_get_main_queue(), dispatch); - } else { - despatch_sync_main_reentrant(dispatch); - } - } - - anInvocation.target = nil; - [anInvocation invoke]; - } -} - -#pragma mark Private - -- (void)_cacheMethodSignaturesForProcotol:(Protocol *)protocol; -{ - @synchronized(self) { - unsigned int totalCount; - for (uint8_t i = 0; i < 1 << 1; ++i) { - struct objc_method_description *methodDescriptions = nn_autofree(protocol_copyMethodDescriptionList(protocol, i & 1, YES, &totalCount)); - - for (unsigned j = 0; j < totalCount; j++) { - struct objc_method_description *methodDescription = methodDescriptions + j; - [self.signatureCache setObject:[NSMethodSignature signatureWithObjCTypes:methodDescription->types] forKey:NSStringFromSelector(methodDescription->name)]; - } - } - - // Recurse to include other protocols to which this protocol adopts - Protocol * __unsafe_unretained *adoptions = (Protocol * __unsafe_unretained *)nn_autofree(protocol_copyProtocolList(protocol, &totalCount)); - for (unsigned j = 0; j < totalCount; j++) { - [self _cacheMethodSignaturesForProcotol:adoptions[j]]; - } - } -} - -- (void)private_forwardInvocation:(NSInvocation *)anInvocation; -{ - @synchronized(self) { - BOOL required = YES; - BOOL instance = YES; - BOOL sanity = nn_selector_belongsToProtocol(anInvocation.selector, self.protocol, &required, &instance); - -#ifndef NS_BLOCK_ASSERTIONS - NSAssert(sanity && instance, @"Selector %@ is not actually part of protocol %@?!", NSStringFromSelector(anInvocation.selector), NSStringFromProtocol(self.protocol)); -#else - (void)sanity; -#endif - - for (id obj in self.observers) { - if ([obj respondsToSelector:anInvocation.selector] || required) { - [anInvocation invokeWithTarget:obj]; - } - } - } -} - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNPollingObject+Protected.h b/Frameworks/NNKit/NNKit/Actors/NNPollingObject+Protected.h deleted file mode 100644 index b34c3e7a..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNPollingObject+Protected.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// NNPollingObject+Protected.h -// NNKit -// -// Created by Scott Perry on 07/12/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface NNPollingObject (Protected) - -- (void)postNotification:(NSDictionary *)userInfo; - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNPollingObject.h b/Frameworks/NNKit/NNKit/Actors/NNPollingObject.h deleted file mode 100644 index 61348b6e..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNPollingObject.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// NNPollingObject.h -// NNKit -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface NNPollingObject : NSObject - -+ (NSString *)notificationName; - -// I think this is the first time where I've wanted the default (atomic, assign, readwrite) flags for a property! -// Too bad I have all warnings turned on: -@property (atomic, assign, readwrite) NSTimeInterval interval; - -- (instancetype)initWithQueue:(dispatch_queue_t)queue; -- (void)main; - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNPollingObject.m b/Frameworks/NNKit/NNKit/Actors/NNPollingObject.m deleted file mode 100644 index dbe904e1..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNPollingObject.m +++ /dev/null @@ -1,83 +0,0 @@ -// -// NNPollingObject.m -// NNKit -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNPollingObject.h" - -#import "despatch.h" - - -@interface NNPollingObject () - -@property (nonatomic, strong, readonly) dispatch_queue_t queue; - -@end - - -@implementation NNPollingObject - -+ (NSString *)notificationName; -{ - return [NSString stringWithFormat:@"%@%@", NSStringFromClass(self), @"PollCompleteNotification"]; -} - -- (instancetype)initWithQueue:(dispatch_queue_t)queue; -{ - self = [super init]; - if (!self) return nil; - - _queue = queue; - - dispatch_async(_queue, ^{ - [self workerLoop]; - }); - - return self; -} - -- (instancetype)init; -{ - return [self initWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; -} - -- (void)workerLoop; -{ - [self main]; - - NSTimeInterval interval = self.interval; - if (interval <= 0.0) { - return; - } - - __weak id weakSelf = self; - double delayInSeconds = interval; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, self.queue, ^(void){ - id self = weakSelf; - [self workerLoop]; - }); -} - -- (void)postNotification:(NSDictionary *)userInfo; -{ - despatch_sync_main_reentrant(^{ - [[NSNotificationCenter defaultCenter] postNotificationName:[[self class] notificationName] object:self userInfo:userInfo]; - }); -} - -- (void)main; -{ - @throw [NSException exceptionWithName:@"NNPollingObjectException" reason:@"Method must be overridden by subclass!" userInfo:@{ @"_cmd" : NSStringFromSelector(_cmd), @"class" : NSStringFromClass([self class]) }]; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.h b/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.h deleted file mode 100644 index 3c290229..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// NNSelfInvalidatingObject.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @class NNSelfInvalidatingObject - * - * @discussion - * For use when an object has to do cleanup work asynchronously, or outside of - * dealloc. Simply implement the invalidate method and - * call [super invalidate] when the actor has finished cleaning up. - */ -@interface NNSelfInvalidatingObject : NSObject - -/*! - * @method invalidate - * - * @discussion - * Called only once, either explicitly (by an interested object) or when the - * object has been fully-released. - * - * When invalidation is complete, [super invalidate] must be called to complete - * deallocation of the object. - */ -- (void)invalidate; - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.m b/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.m deleted file mode 100644 index 8ffbe9bb..00000000 --- a/Frameworks/NNKit/NNKit/Actors/NNSelfInvalidatingObject.m +++ /dev/null @@ -1,106 +0,0 @@ -// -// NNSelfInvalidatingObject.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// -// Requires -fno-objc-arc -// - -#import "NNSelfInvalidatingObject.h" - -#import - - -@interface NNSelfInvalidatingObject () { - _Bool _valid; -} - -@property (nonatomic, assign) long refCount; - -@end - - -@implementation NNSelfInvalidatingObject - -#pragma mark NSObject - -- (instancetype)init; -{ - if (!(self = [super init])) { return nil; } - - self->_refCount = 0; - self->_valid = true; - - /* - * -invalidate supports two conditions: - * * invalidate may be called before the refCount hits zero, in which case the object should survive until its natural death. - * * invalidate may be called at refCount zero, in which case the object should survive until the end of the current runloop. - * To satisfy both of these constraints, retain/release messages are forwarded to super and one extra retain is made (here) balanced by an autorelease in -invalidate to keep the object alive while it is still valid/invalidating. - * Calling dealloc directly is an error and is not supported. - */ - [self retain]; - - return self; -} - -- (instancetype)retain; -{ - @synchronized(self) { - ++self.refCount; - } - [super retain]; - return self; -} - -- (oneway void)release; -{ - @synchronized(self) { - --self.refCount; - } - [super release]; -} - -- (oneway void)dealloc; -{ - if (self->_valid) { - [super dealloc]; - @throw [NSException exceptionWithName:@"NNObjectLifetimeException" reason:@"Calling dealloc directly on a self-invalidating object is not supported (object destroyed without invalidation)." userInfo:nil]; - } - - [super dealloc]; -} - -#pragma mark NNSelfInvalidatingObject - -- (void)invalidate; -{ - @synchronized(self) { - if (self->_valid) { - self->_valid = false; - [self autorelease]; - } - } -} - -#pragma mark Internal - -- (void)setRefCount:(long)refCount; -{ - self->_refCount = refCount; - - if (!refCount && self->_valid) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self invalidate]; - }); - } -} - -@end diff --git a/Frameworks/NNKit/NNKit/Actors/README.md b/Frameworks/NNKit/NNKit/Actors/README.md deleted file mode 100644 index faee37c4..00000000 --- a/Frameworks/NNKit/NNKit/Actors/README.md +++ /dev/null @@ -1,121 +0,0 @@ -NNKit Actor Model -================= - -The classes in this component solve some common problems encountered when using the actor pattern in Objective-C. - -NNCleanupProxy --------------- - -A cleanup proxy is used to perform some kind of work after an object is deallocated. The proxy itself can be used as a message forwarder, so long as the methods are pre-declared using `-cacheMethodSignatureForSelector:`. The implementation of [`NSNotificationCenter+NNAdditions`](https://github.com/numist/NNKit/blob/master/NNKit/API%20Extension/NSNotificationCenter%2BNNAdditions.m) uses a cleanup proxy to provide its functionality. - -NNMultiDispatchManager ----------------------- - -The multi-dispatch manager is a new mechanism to enable structured to-many message dispatch. Instead of using global notifications, which are very error-prone with magic keys into parochial `userInfo` dictionaries, multi-dispatch acts more like a delegate where observers conform to a common protocol, and messages belonging to that protocol that are sent to the multi-dispatch manager are forwarded to all of the observers. All protocol methods must return `void`, and methods decorated with `oneway` are dispatched asynchronously. All dispatch messages are sent on the main thread, and messages sent to the multi-dispatch manager can be sent on any thread. - -#### Example: #### - -``` objective-c -// -// Interface -// - -@protocol MyProtocol -- (oneway void)foo:(id)bar; -@end - - -@interface MyDispatcher : NSObject - -- (void)addObserver:(id)observer; -- (void)removeObserver:(id)observer; - -@end - -@interface MyObserver : NSObject - -@end - - -// -// Implementation -// - -@interface MyDispatcher () - -@property (nonatomic, strong, readonly) NNMultiDispatchManager *dispatchManager; - -@end - -@implementation MyDispatcher - -+ (instancetype)sharedDispatcher; -{ - static MyDispatcher *singleton; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - singleton = [MyDispatcher new]; - }); - return singleton; -} - -- (id)init; -{ - if (!(self = [super init])) { return nil; } - - _dispatchManager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(MyProtocol)]; - - return self; -} - -- (void)addObserver:(id)observer; -{ - [self.dispatchManager addObserver:observer]; -} - -- (void)removeObserver:(id)observer; -{ - [self.dispatchManager removeObserver:observer]; -} - -- (void)dispatchEvent; -{ - [(id)self.dispatchManager foo:[NSObject new]]; -} - -@end - - -@implementation MyObserver - -- (id)init; -{ - if (!(self = [super init])) { return nil; } - - // There's no need to removeObserver in dealloc, since NNMultiDispatchManager references observers weakly. - [[MyDispatcher sharedDispatcher] addObserver:self]; - - return self; -} - -- (oneway void)foo:(id)bar; -{ - // … -} - -@end -``` - -NNPollingObject ---------------- - -Sometimes there is no way to have information pushed to you, and it has to be checked occasionally by a polling object. This base class provides basic interval and queue priority support with a polling worker thread that terminates when the object is released. - -Subclasses need only override `-main` to use, and it's recommended that the built in `-postNotification:` method be used to emit events to interested parties. The `interval` property can be set to any time interval, with values less than or equal to zero causing the worker thread to terminate when it has finished its next scheduled iteration. - -NNSelfInvalidatingObject ------------------------- - -Some objects may encapsulate resources that require work to clean up, such as an open file handle. In some cases, these operations can take time or may otherwise require that the actor be alive for an extended period of time after its owner has released it. Subclassing `NNSelfInvalidatingObject` allows this condition to be handled easily. Simply override `-invalidate`, and it is called asynchronously on the main queue once the internal refCount of the object has reached zero. When cleanup is complete, calling `[super invalidate]` puts the object in the nearest autorelease pool and it is finally destroyed on the next iteration of the runloop. Calling `[self invalidate]` early prevents it from being called when the object refCount reaches zero (the object is destroyed immediately). - -This base class was inspired by [Andy Matuschak](https://github.com/andymatuschak). diff --git a/Frameworks/NNKit/NNKit/Collections/NNWeakSet.h b/Frameworks/NNKit/NNKit/Collections/NNWeakSet.h deleted file mode 100644 index 64bcafb2..00000000 --- a/Frameworks/NNKit/NNKit/Collections/NNWeakSet.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// NNWeakSet.h -// NNKit -// -// Created by Scott Perry on 06/04/16. -// Copyright © 2016 Scott Perry. -// -// 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 - -/*! - * @class NNMutableWeakSet - * - * @abstract - * Provides a set-type collection that references its members weakly. - * - * @discussion - * NNWeakSet implements the same API as NSMutableSet, but holds weak - * references to its members. - * - * No compaction is required, members are automatically removed when they are - * deallocated. - * - * This collection type may be slower than NSHashTable, but it also has - * fewer bugs. - */ -@interface NNWeakSet : NSMutableSet - -@end diff --git a/Frameworks/NNKit/NNKit/Collections/NNWeakSet.m b/Frameworks/NNKit/NNKit/Collections/NNWeakSet.m deleted file mode 100644 index dc5611f0..00000000 --- a/Frameworks/NNKit/NNKit/Collections/NNWeakSet.m +++ /dev/null @@ -1,124 +0,0 @@ -// -// NNWeakSet.m -// NNKit -// -// Created by Scott Perry on 11/15/13. -// Copyright © 2016 Scott Perry. -// -// 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 "NNWeakSet.h" - -#import "_NNWeakArrayTombstone.h" -#import "_NNWeakSetEnumerator.h" -#import "NNCleanupProxy.h" - - -@interface NNWeakSet () - -@property (nonatomic, readonly, strong) NSMutableSet *backingStore; - -- (void)_removeObjectAllowingNil:(id)object; - -@end - - -/** - - collection -> tombstone // Unavoidable. The whole point of this exercise. - tombstone -> object [style = "dotted"]; // Obvious. - cleanup -> tombstone [style = "dotted"]; // For removing the tombstone from the collection. - cleanup -> collection [style = "dotted"]; // For removing the tombstone from the collection. - object -> cleanup; // Object association, the only strong reference to the proxy. - object -> tombstone [style = "dotted]; // Object association so the collection can look at the object and find the tombstone. - - */ - - -@implementation NNWeakSet - -- (instancetype)initWithCapacity:(NSUInteger)numItems; -{ - if (!(self = [super init])) { return nil; } - - self->_backingStore = [[NSMutableSet alloc] initWithCapacity:numItems]; - - return self; -} - -- (id)init; -{ - if (!(self = [super init])) { return nil; } - - self->_backingStore = [NSMutableSet new]; - - return self; -} - -#pragma mark NSSet - -- (NSUInteger)count; -{ - @synchronized(self.backingStore) { - return self.backingStore.count; - } -} - -- (id)member:(id)object; -{ - @synchronized(self.backingStore) { - return ((_NNWeakArrayTombstone *)[self.backingStore member:object]).target; - } -} - -- (NSEnumerator *)objectEnumerator; -{ - @synchronized(self.backingStore) { - return [[_NNWeakSetEnumerator alloc] initWithWeakSet:self]; - } -} - -#pragma mark NSMutableSet - -- (void)addObject:(id)object; -{ - _NNWeakArrayTombstone *tombstone = [_NNWeakArrayTombstone tombstoneWithTarget:object]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:object withKey:(uintptr_t)self]; - - __weak NNWeakSet *weakCollection = self; - __weak _NNWeakArrayTombstone *weakTombstone = tombstone; - proxy.cleanupBlock = ^{ - NNWeakSet *collection = weakCollection; - [collection _removeObjectAllowingNil:weakTombstone]; - }; - - @synchronized(self.backingStore) { - [self.backingStore addObject:tombstone]; - } -} - -- (void)removeObject:(id)object; -{ - [NNCleanupProxy cancelCleanupForTarget:object withKey:(uintptr_t)self]; - @synchronized(self.backingStore) { - [self.backingStore removeObject:object]; - } -} - -#pragma mark Private - -- (void)_removeObjectAllowingNil:(id)object; -{ - if (!object) { return; } - - @synchronized(self.backingStore) { - [self.backingStore removeObject:object]; - } -} - -@end \ No newline at end of file diff --git a/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.h b/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.h deleted file mode 100644 index 76dbde46..00000000 --- a/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// NSArray+NNComprehensions.h -// NNKit -// -// Created by Scott Perry on 02/25/14. -// Copyright © 2014 Scott Perry. -// -// 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 - -typedef BOOL (^nn_filter_block_t)(id item); -typedef id (^nn_map_block_t)(id item); -typedef id (^nn_reduce_block_t)(id accumulator, id item); - -@protocol NNCollection -- (instancetype)nn_filter:(nn_filter_block_t)block; -- (instancetype)nn_map:(nn_map_block_t)block; -- (id)nn_reduce:(nn_reduce_block_t)block; -@end - -@interface NSArray (NNComprehensions) -@end - -@interface NSSet (NNComprehensions) -@end - -@interface NSOrderedSet (NNComprehensions) -@end diff --git a/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.m b/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.m deleted file mode 100644 index 64082ec7..00000000 --- a/Frameworks/NNKit/NNKit/Collections/NSCollections+NNComprehensions.m +++ /dev/null @@ -1,100 +0,0 @@ -// -// NSArray+NNComprehensions.m -// NNKit -// -// Created by Scott Perry on 02/25/14. -// Copyright © 2014 Scott Perry. -// -// 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 "NSCollections+NNComprehensions.h" - -#define FILTER_DECLARATION() - (instancetype)nn_filter:(nn_filter_block_t)block -#define FILTER_DEFINITION(__type__) \ - id result = [[[__type__ class] new] mutableCopy]; \ - for (id object in self) { \ - if (block(object)) { \ - [result addObject:object]; \ - } \ - } \ - return result - -#define MAP_DECLARATION() - (instancetype)nn_map:(nn_map_block_t)block -#define MAP_DEFINITION(__type__) \ - id result = [[[__type__ class] new] mutableCopy]; \ - for (id object in self) { \ - [result addObject:block(object)]; \ - } \ - return result - - -#define REDUCE_DECLARATION() - (id)nn_reduce:(nn_reduce_block_t)block -#define REDUCE_DEFINITION(__type__) \ - id accumulator = nil; \ - for (id object in self) { \ - accumulator = block(accumulator, object); \ - } \ - return accumulator - - -@implementation NSArray (NNComprehensions) - -FILTER_DECLARATION(); -{ - FILTER_DEFINITION(NSArray); -} - -MAP_DECLARATION(); -{ - MAP_DEFINITION(NSArray); -} - -REDUCE_DECLARATION(); -{ - REDUCE_DEFINITION(NSArray); -} - -@end - -@implementation NSSet (NNComprehensions) - -FILTER_DECLARATION(); -{ - FILTER_DEFINITION(NSSet); -} - -MAP_DECLARATION(); -{ - MAP_DEFINITION(NSSet); -} - -REDUCE_DECLARATION(); -{ - REDUCE_DEFINITION(NSSet); -} - -@end - -@implementation NSOrderedSet (NNComprehensions) - -FILTER_DECLARATION(); -{ - FILTER_DEFINITION(NSOrderedSet); -} - -MAP_DECLARATION(); -{ - MAP_DEFINITION(NSOrderedSet); -} - -REDUCE_DECLARATION(); -{ - REDUCE_DEFINITION(NSOrderedSet); -} - -@end diff --git a/Frameworks/NNKit/NNKit/Collections/README.md b/Frameworks/NNKit/NNKit/Collections/README.md deleted file mode 100644 index 0c3422e4..00000000 --- a/Frameworks/NNKit/NNKit/Collections/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# NNKit Collections # - -The code in this component gives collections superpowers. - -## Category: NNComprehensions ## - -The `NNComprehensions` category on `NSArray`, `NSSet`, and `NSOrderedSet` implements the following comprehensions: - -### nn_filter: ### -Returns a new collection of the same (immutable) type that contains a subset of the items in the original array, as chosen by the method's argument, a block that takes an `id` and returns a `BOOL`. - -#### Example: #### -``` objective-c -// Returns @[@3, @6, @9] -[@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10] nn_filter:^(id item){ return (BOOL)!([item integerValue] % 3); }]; -``` - -### nn_map: ### -Returns a new collection of the same (immutable) type that contains new values based on the result of the block parameter, which takes an id and returns an id. - -Returning `nil` from the block is not supported. A separate filter step should be used first to remove unwanted items from the collection. - -#### Example: #### -``` objective-c -// Returns @[@2, @4, @6, @8, @10, @12, @14, @16, @18, @20] -[@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10] nn_map:^(id item){ return @([item integerValue] * 2); }]; -``` - -### nn_reduce: ### -Returns a reduction of the collection as defined by the block parameter, which takes an accumulator value (an `id` which starts as nil) and an item and returns the new value of the accumulator. - -#### Example: #### -``` objective-c -// Returns @55 -[@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10] nn_reduce:^(id acc, id item){ return @([acc integerValue] + [item integerValue]); }]; -``` diff --git a/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.h b/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.h deleted file mode 100644 index 17240cfe..00000000 --- a/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// _NNWeakArrayTombstone.h -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface _NNWeakArrayTombstone : NSObject - -+ (_NNWeakArrayTombstone *)tombstoneWithTarget:(id)target; - -@property (nonatomic, readonly, weak) id target; - -@end diff --git a/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.m b/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.m deleted file mode 100644 index 2a1668da..00000000 --- a/Frameworks/NNKit/NNKit/Collections/_NNWeakArrayTombstone.m +++ /dev/null @@ -1,59 +0,0 @@ -// -// _NNWeakArrayTombstone.m -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 "_NNWeakArrayTombstone.h" - - -@interface _NNWeakArrayTombstone () - -@property (nonatomic, readonly, assign) NSUInteger hash; - -@end - - -@implementation _NNWeakArrayTombstone - -+ (_NNWeakArrayTombstone *)tombstoneWithTarget:(id)target; -{ - _NNWeakArrayTombstone *tombstone = [_NNWeakArrayTombstone new]; - tombstone->_target = target; - return tombstone; -} - -@synthesize hash = _hash; -- (NSUInteger)hash; -{ - if (!self->_hash) { - @synchronized(self) { - if (!self->_hash) { - id target = self.target; - if (target) { - self->_hash = [target hash]; - } else { - self->_hash = (uintptr_t)self; - } - } - } - } - - return self->_hash; -} - -- (BOOL)isEqual:(id)object; -{ - id target = self.target; - return [target isEqual:object] ? YES : (uintptr_t)object == (uintptr_t)self; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.h b/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.h deleted file mode 100644 index b1955b37..00000000 --- a/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// _NNWeakSetEnumerator.h -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@class NNWeakSet; - - -@interface _NNWeakSetEnumerator : NSEnumerator - -- (instancetype)initWithWeakSet:(NNWeakSet *)set; - -@end diff --git a/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.m b/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.m deleted file mode 100644 index 296c2d2a..00000000 --- a/Frameworks/NNKit/NNKit/Collections/_NNWeakSetEnumerator.m +++ /dev/null @@ -1,73 +0,0 @@ -// -// _NNWeakSetEnumerator.m -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 "_NNWeakSetEnumerator.h" - -#import "_NNWeakArrayTombstone.h" -#import "NNWeakSet.h" - - -@interface NNWeakSet (Private) - -@property (nonatomic, readonly, strong) NSSet *backingStore; - -@end - - -@interface _NNWeakSetEnumerator () - -@property (nonatomic, readonly, strong) NSEnumerator *tombstoneEnumerator; - -@end - - -@implementation _NNWeakSetEnumerator - -- (instancetype)initWithWeakSet:(NNWeakSet *)set; -{ - if (!(self = [super init])) { return nil; } - - self->_tombstoneEnumerator = [set.backingStore.copy objectEnumerator]; - - return self; -} - -- (NSArray *)allObjects; -{ - NSMutableArray<_NNWeakArrayTombstone *> *result = [NSMutableArray new]; - - for (_NNWeakArrayTombstone *tombstone in self.tombstoneEnumerator.allObjects) { - id obj = tombstone.target; - if (obj) { - [result addObject:obj]; - } - } - - return result; -} - -- (id)nextObject; -{ - id obj; - _NNWeakArrayTombstone *tombstone; - - do { - tombstone = self.tombstoneEnumerator.nextObject; - obj = tombstone.target; - } while (!obj && tombstone); - - return obj; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.h b/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.h deleted file mode 100644 index ab2c996f..00000000 --- a/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// NNDelegateProxy.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @class NNDelegateProxy - * - * @discussion - * A proxy object ensuring that messages to the delegate are dispatched on the - * main thread. - * - * Messages declared as oneway void are dispatched asynchronously, messages that - * are optional are dispatched against nil if the delegate does not implement them. - * Messages that are optional and non-void are a bad idea and you shouldn't use them. - */ -@interface NNDelegateProxy : NSProxy - -/*! - * @method proxyWithDelegate:protocol: - * - * @discussion - * Creates a new proxy for delegate conforming to - * protocol. - * - * @param delegate - * The object to receive delegate messages. - * - * @param protocol - * The protocol to which delegate conforms. Can be NULL, - * but shouldn't be. - * - * @result - * Proxy to stand in for delegate for messages conforming to - * protocol. - */ -+ (id)proxyWithDelegate:(id)delegate protocol:(Protocol *)protocol; - -@end diff --git a/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.m b/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.m deleted file mode 100644 index 7a37769c..00000000 --- a/Frameworks/NNKit/NNKit/Concurrency/NNDelegateProxy.m +++ /dev/null @@ -1,82 +0,0 @@ -// -// NNDelegateProxy.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNDelegateProxy.h" - -#import "despatch.h" -#import "runtime.h" - - -@interface NNDelegateProxy () - -@property (readonly, weak) id delegate; -@property (readonly, assign) Protocol *protocol; - -@end - - -@implementation NNDelegateProxy - -+ (id)proxyWithDelegate:(id)delegate protocol:(Protocol *)protocol; -{ - if (protocol && delegate) { - NSAssert([delegate conformsToProtocol:protocol], @"Object %@ does not conform to protocol %@", delegate, NSStringFromProtocol(protocol)); - } - - NNDelegateProxy *proxy = [self alloc]; - proxy->_delegate = delegate; - proxy->_protocol = protocol; - return proxy; -} - -// Helper function to provide an autoreleasing reference to the delegate property -- (id)strongDelegate; -{ - id delegate = self.delegate; - return delegate; -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel; -{ - return [self.strongDelegate methodSignatureForSelector:sel]; -} - -- (void)forwardInvocation:(NSInvocation *)invocation; -{ -# ifndef NS_BLOCK_ASSERTIONS - { - BOOL instanceMethod = YES; - NSAssert(nn_selector_belongsToProtocol(invocation.selector, self.protocol, NULL, &instanceMethod) && instanceMethod, @"Instance method %@ not found in protocol %@", NSStringFromSelector(invocation.selector), NSStringFromProtocol(self.protocol)); - } -# endif - - id delegate = self.delegate; - BOOL requiredMethod = NO; - nn_selector_belongsToProtocol(invocation.selector, self.protocol, &requiredMethod, NULL); - if (!requiredMethod && ![delegate respondsToSelector:invocation.selector]) { - return; - } - - if (invocation.methodSignature.isOneway) { - dispatch_async(dispatch_get_main_queue(), ^{ - [invocation invokeWithTarget:delegate]; - }); - } else { - despatch_sync_main_reentrant(^{ - [invocation invokeWithTarget:delegate]; - }); - } -} - -@end diff --git a/Frameworks/NNKit/NNKit/Concurrency/README.md b/Frameworks/NNKit/NNKit/Concurrency/README.md deleted file mode 100644 index d52a84a1..00000000 --- a/Frameworks/NNKit/NNKit/Concurrency/README.md +++ /dev/null @@ -1,60 +0,0 @@ -NNKit Concurrency -================= - -NNKit provides tools to help make concurrency less of a headache, following the model of islands of serialization in a sea of concurrency. Since not all code can be expected to follow this model, NNKit also assumes that the only safe manner of passing messages between modules is on the main thread, and recievers are expected to get off the main thread as needed. - -NNDelegateProxy ---------------- - -The `NNDelegateProxy` class provides a mechanism to ensure that all messages sent to a delegate are dispatched on the main thread. - -### Example ### - - // You should already have a protocol for your use of the delegate pattern: - @protocol MYClassDelegate - - (void)objectCalledDelegateMethod:(id)obj; - @end - - - @interface MYClass : NSObject - - // And should already have a weak property for your delegate in your class declaration: - @property (nonatomic, weak) id delegate; - - // Add a strong reference to a new property, the delegate proxy: - @property (strong) id delegateProxy; - - @end - - @implementation MYClass - - - (instancetype)init; - { - if (!(self = [super init])) { return nil; } - - // Initialize the delegate proxy, feel free to set the delegate later if you don't have one handy. - _delegateProxy = [NNDelegateProxy proxyWithDelegate:nil protocol:@protocol(MYClassDelegate)]; - - // … - - return self; - } - - // If you have a writable delegate property, you'll need a custom delegate setter to ensure that the proxy gets updated: - - (void)setDelegate:(id)delegate; - { - self->_delegate = delegate; - - ((NNDelegateProxy *)self.delegateProxy).delegate = delegate; - // NOTE: A cast is necessary here because the delegateProxy property is typed id to retain as much static checking as possible elsewhere in your code, which fails here because the compiler doesn't realise that it's still an NNDelegateProxy under the hood. - } - - - (void)method; - { - // Who cares how we got to where we are, or where that even is; when it's time to dispatch a delegate message just send it to the proxy: - [self.delegateProxy objectCalledDelegateMethod:self]; - } - - @end - -This proxy class was inspired by [Andy Matuschak](https://github.com/andymatuschak). diff --git a/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.h b/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.h deleted file mode 100644 index 800a8e48..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// NNStrongifiedProperties.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -/*! - * @class NNStrongifiedProperties - * - * @discussion - * Used with isa-swizzling to provide strong getters for weak properties in the - * form of methods named strongProperty, where - * Property is the unambiguous capitalized name of a weak - * property on the object. - */ -@interface NNStrongifiedProperties : NSObject - -@end diff --git a/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.m b/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.m deleted file mode 100644 index 3249fced..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/NNStrongifiedProperties.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// NNStrongifiedProperties.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNStrongifiedProperties.h" - -#import -#import - -#import "nn_autofree.h" - - -static SEL weakGetterForPropertyName(Class myClass, NSString *propertyName) { - objc_property_t property = NULL; - do { - property = class_getProperty(myClass, [propertyName UTF8String]); - if (property) { - break; - } - } while ((myClass = class_getSuperclass(myClass))); - - if (!property) { - return NO; - } - - objc_property_attribute_t *attributes = nn_autofree(property_copyAttributeList(property, NULL)); - BOOL propertyIsWeak = NO; - SEL getter = NSSelectorFromString(propertyName); - for (int i = 0; attributes[i].name != NULL; ++i) { - if (!strcmp(attributes[i].name, "W")) { - propertyIsWeak = YES; - } - if (!strncmp(attributes[i].name, "G", 1)) { - getter = NSSelectorFromString([NSString stringWithFormat:@"%s", attributes[i].name + 1]); - } - if (!strcmp(attributes[i].name, "T") && strcmp(attributes[i].value, "@")) { - return NO; - } - } - attributes = NULL; - - if (!propertyIsWeak) { - return NULL; - } - - return getter; -} - - -static _Bool selectorIsStrongGetter(Class myClass, SEL sel, SEL *weakGetter) { - NSString *selectorName = NSStringFromSelector(sel); - NSRange prefixRange = [selectorName rangeOfString:@"strong"]; - - BOOL selectorIsStrongGetter = prefixRange.location == 0; - - if (!selectorIsStrongGetter) { - if (weakGetter) { - *weakGetter = NULL; - } - return NO; - } - - // Also check uppercase in case it's an acronym? - - NSString *upperName = [selectorName substringFromIndex:prefixRange.length]; - NSString *lowerName = [NSString stringWithFormat:@"%@%@", - [[selectorName substringWithRange:(NSRange){prefixRange.length, 1}] lowercaseString], - [selectorName substringFromIndex:prefixRange.length + 1]]; - - SEL lowerGetter = weakGetterForPropertyName(myClass, lowerName); - SEL upperGetter = weakGetterForPropertyName(myClass, upperName); - - if (lowerGetter && upperGetter) { - // Selector is ambiguous, do not support synthesizing a strongified getter for this property. - return NO; - } - - if (!lowerGetter && !upperGetter) { - return NO; - } - - *weakGetter = lowerGetter ?: upperGetter; - - return YES; -} - - -static id strongGetterIMP(id self, SEL _cmd) { - SEL weakSelector = NULL; - -# ifndef NS_BLOCK_ASSERTIONS - { - BOOL sane = selectorIsStrongGetter([self class], _cmd, &weakSelector); - NSAssert(sane, @"Selector %@ does not represent a valid strongifying getter method", NSStringFromSelector(_cmd)); - } -# endif - - if (!weakSelector) { return nil; } - - id strongRef = ((id (*)(id, SEL))objc_msgSend)(self, weakSelector); - - return strongRef; -} - - -@implementation NNStrongifiedProperties - -+ (BOOL)resolveInstanceMethod:(SEL)sel; -{ - SEL weakSelector = NULL; - if (selectorIsStrongGetter(self, sel, &weakSelector)) { - Method weakGetter = class_getInstanceMethod(self, weakSelector); - const char *getterEncoding = method_getTypeEncoding(weakGetter); - class_addMethod(self, sel, (IMP)strongGetterIMP, getterEncoding); - return YES; - } - - return NO; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Hacks/README.md b/Frameworks/NNKit/NNKit/Hacks/README.md deleted file mode 100644 index a2712d03..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/README.md +++ /dev/null @@ -1,59 +0,0 @@ -NNKit Hacks -=========== - -This portion of NNKit contains things that, while well-tested and reliable, shouldn't pass a sane person's test for acceptable complexity per unit utility. - -Automatically Freed C Buffers ------------------------------ - -Have a complicated function with a lot of logic and early returns? Avoid the possibility of forgetting to free your buffers with `nn_autofree`! - - @autoreleasepool { - int *foo = nn_autofree(malloc(size)); - } - -Don't forget, you're still in charge of NULLing your pointers, so it's easiest if you create your own autorelease pool, which will also create an extra scope for the buffer's pointer which it can't escape. - -Strongified Property Access ---------------------------- - -Weak properties can be accessed with autoreleasing getters by either inheriting from or isa-swizzling `NNStrongifiedProperties`. - - @interface WeakDemo : NNStrongifiedProperties - @property (weak) id foo; - @end - - @interface WeakDemo (StrongAccessors) - - (id)strongFoo; - @end - - @implementation WeakDemo - @end - - int main() { - // Whenever you need an autoreleased reference to foo: - [[WeakDemo new] strongFoo] - } - -If you already inherit from a more useful class, this behaviour can be "learned" by an existing object by using isa swizzling: - - @interface WeakDemo : NSObject - @property (weak) id foo; - @end - - @interface WeakDemo (StrongAccessors) - - (id)strongFoo; - @end - - @implementation WeakDemo - @end - - int main() { - id obj = [WeakDemo new]; - nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - - // Whenever you need an autoreleased reference to foo: - [obj strongFoo] - } - -This hack was concieved after enabling the `-Wreceiver-is-weak` warning in clang and learning about the race condition it polices. diff --git a/Frameworks/NNKit/NNKit/Hacks/memoize.h b/Frameworks/NNKit/NNKit/Hacks/memoize.h deleted file mode 100644 index 63a62204..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/memoize.h +++ /dev/null @@ -1,11 +0,0 @@ -// -// memoize.h -// NNKit -// -// Created by Scott Perry on 06/23/15. -// Copyright © 2015 Scott Perry. All rights reserved. -// - -#define NNMemoize(block) _NNMemoize(self, _cmd, block) - -id _NNMemoize(id self, SEL _cmd, id (^block)()); diff --git a/Frameworks/NNKit/NNKit/Hacks/memoize.m b/Frameworks/NNKit/NNKit/Hacks/memoize.m deleted file mode 100644 index 53824699..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/memoize.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// memoize.c -// NNKit -// -// Created by Scott Perry on 06/23/15. -// Copyright © 2015 Scott Perry. All rights reserved. -// - -#import "memoize.h" - -#import - - -id _NNMemoize(id self, SEL _cmd, id (^block)()) { - id result; - void *key = (void *)((uintptr_t)(__bridge void *)self ^ (uintptr_t)(void *)_cmd ^ (uintptr_t)&_NNMemoize); - - @synchronized(self) { - result = objc_getAssociatedObject(self, key); - if (!result) { - result = block(); - objc_setAssociatedObject(self, key, result, OBJC_ASSOCIATION_COPY_NONATOMIC); - } - } - - return result; -} diff --git a/Frameworks/NNKit/NNKit/Hacks/nn_autofree.h b/Frameworks/NNKit/NNKit/Hacks/nn_autofree.h deleted file mode 100644 index be39e0a6..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/nn_autofree.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// nn_autofree.h -// NNKit -// -// Created by Scott Perry on 09/09/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -/*! - * @function nn_autofree - * - * @abstract - * Adds the buffer to the current autorelease pool. - * - * @discussion - * This function ties the buffer's lifetime to that of an object added to the - * current autorelease pool, causing it to be freed when the pool is drained. - * - * @param ptr - * The buffer to add to the current autorelease pool. - */ -void *nn_autofree(void *ptr); diff --git a/Frameworks/NNKit/NNKit/Hacks/nn_autofree.m b/Frameworks/NNKit/NNKit/Hacks/nn_autofree.m deleted file mode 100644 index 29d601ec..00000000 --- a/Frameworks/NNKit/NNKit/Hacks/nn_autofree.m +++ /dev/null @@ -1,25 +0,0 @@ -// -// nn_autofree.m -// NNKit -// -// Created by Scott Perry on 09/09/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// -// Requires -fno-objc-arc -// - -#import "nn_autofree.h" - -void *nn_autofree(void *ptr) -{ - if (ptr) { - [NSData dataWithBytesNoCopy:ptr length:1 freeWhenDone:YES]; - } - return ptr; -} diff --git a/Frameworks/NNKit/NNKit/NNKit Mac-Prefix.pch b/Frameworks/NNKit/NNKit/NNKit Mac-Prefix.pch deleted file mode 100644 index 35d76409..00000000 --- a/Frameworks/NNKit/NNKit/NNKit Mac-Prefix.pch +++ /dev/null @@ -1,9 +0,0 @@ -// -// Prefix header -// -// The contents of this file are implicitly included at the beginning of every source file. -// - -#ifdef __OBJC__ - #import -#endif diff --git a/Frameworks/NNKit/NNKit/NNKit iOS-Prefix.pch b/Frameworks/NNKit/NNKit/NNKit iOS-Prefix.pch deleted file mode 100644 index eb2007ec..00000000 --- a/Frameworks/NNKit/NNKit/NNKit iOS-Prefix.pch +++ /dev/null @@ -1,9 +0,0 @@ -// -// Prefix header -// -// The contents of this file are implicitly included at the beginning of every source file. -// - -#ifdef __OBJC__ - #import -#endif diff --git a/Frameworks/NNKit/NNKit/NNKit.h b/Frameworks/NNKit/NNKit/NNKit.h deleted file mode 100644 index 1e78e7a9..00000000 --- a/Frameworks/NNKit/NNKit/NNKit.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// NNKit.h -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import -#import -#import -#import -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import diff --git a/Frameworks/NNKit/NNKit/NNKit.m b/Frameworks/NNKit/NNKit/NNKit.m deleted file mode 100644 index fa6c55a3..00000000 --- a/Frameworks/NNKit/NNKit/NNKit.m +++ /dev/null @@ -1,15 +0,0 @@ -// -// NNKit.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNKit.h" diff --git a/Frameworks/NNKit/NNKit/Services/NNService+Protected.h b/Frameworks/NNKit/NNKit/Services/NNService+Protected.h deleted file mode 100644 index 00a7bbd3..00000000 --- a/Frameworks/NNKit/NNKit/Services/NNService+Protected.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// NNService+Protected.h -// NNKit -// -// Created by Scott Perry on 10/17/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@class NNMultiDispatchManager; - - -@interface NNService (Protected) - -@property (atomic, readonly, strong) NNMultiDispatchManager *subscriberDispatcher; - -@end diff --git a/Frameworks/NNKit/NNKit/Services/NNService.h b/Frameworks/NNKit/NNKit/Services/NNService.h deleted file mode 100644 index 81742d22..00000000 --- a/Frameworks/NNKit/NNKit/Services/NNService.h +++ /dev/null @@ -1,97 +0,0 @@ -// -// NNService.h -// NNKit -// -// Created by Scott Perry on 10/17/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @enum NNServiceType - * - * @discussion - * Represents the type of a service. Persistent services are started once all of - * their dependencies have been started, on-demand services are started when an - * object subscribes to the service. - */ -typedef NS_ENUM(uint8_t, NNServiceType) { - NNServiceTypePersistent, - NNServiceTypeOnDemand, -}; - - -/*! - * @class NNService - * - * @discussion - * Discuss. - */ -@interface NNService : NSObject - -/*! - * @method sharedService - * - * @discussion - * Service singleton accessor. - * - * @result - * Singleton object for the service. - */ -+ (instancetype)sharedService; - -/*! - * @method serviceType - * - * @discussion - * The type of the service. Must be overridden. Valid services must not return NNServiceTypeNone. - */ -+ (NNServiceType)serviceType; - -/*! - * @method dependencies - * - * @discussion - * Services are not started until their dependencies have all been started first. - * This means multiple services can be made on-demand by having a root service - * that is on-demand and multiple dependant services that are persistent. - * - * @result - * Returns a set of Classes that this service depends on to run. - * Default implementation returns nil; - */ -+ (NSSet *)dependencies; - -/*! - * @method subscriberProtocol - * - * @discussion - * Protocol for subscribers to conform to. Default implementation returns @protocol(NSObject). - */ -+ (Protocol *)subscriberProtocol; - -/*! - * @method startService - * - * @discussion - * Called when the service is started. - */ -- (void)startService __attribute__((objc_requires_super)); - -/*! - * @method stopService - * - * @discussion - * Called when the service is stopped. - */ -- (void)stopService __attribute__((objc_requires_super)); - -@end diff --git a/Frameworks/NNKit/NNKit/Services/NNService.m b/Frameworks/NNKit/NNKit/Services/NNService.m deleted file mode 100644 index 3047741f..00000000 --- a/Frameworks/NNKit/NNKit/Services/NNService.m +++ /dev/null @@ -1,86 +0,0 @@ -// -// NNService.m -// NNKit -// -// Created by Scott Perry on 10/17/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNService+Protected.h" - -#import "NNServiceManager.h" -#import "NNMultiDispatchManager.h" - - -@interface NNService () - -@property (atomic, readonly, strong) NNMultiDispatchManager *subscriberDispatcher; - -@end - - -@implementation NNService - -+ (instancetype)sharedService; -{ - static NSMutableDictionary *instances; - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instances = [NSMutableDictionary new]; - }); - - NNService *result; - - @synchronized(instances) { - result = [instances objectForKey:self]; - if (!result) { - result = [self new]; - [instances setObject:result forKey:self]; - } - } - - return result; -} - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -+ (NSSet *)dependencies; -{ - return [NSSet set]; -} - -+ (Protocol *)subscriberProtocol; -{ - return @protocol(NSObject); -} - -- (id)init; -{ - if (!(self = [super init])) { return nil; } - - self->_subscriberDispatcher = [[NNMultiDispatchManager alloc] initWithProtocol:self.class.subscriberProtocol]; - - return self; -} - -- (void)startService; -{ - NSAssert([[NSThread currentThread] isMainThread], @"Service must be started on the main thread"); -} - -- (void)stopService; -{ - NSAssert([[NSThread currentThread] isMainThread], @"Service must be stopped on the main thread"); -} - -@end diff --git a/Frameworks/NNKit/NNKit/Services/NNServiceManager.h b/Frameworks/NNKit/NNKit/Services/NNServiceManager.h deleted file mode 100644 index da4f86c9..00000000 --- a/Frameworks/NNKit/NNKit/Services/NNServiceManager.h +++ /dev/null @@ -1,139 +0,0 @@ -// -// NNServiceManager.h -// NNKit -// -// Created by Scott Perry on 10/17/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import - - -/*! - * @class NNServiceManager - * - * @discussion - * Manages the running state of registered services based on their dependencies and subscriptions. - */ -@interface NNServiceManager : NSObject - -/*! - * @method sharedManager - * - * @discussion - * Singleton service manager accessor. - * - * @result - * Global shared service manager object. - */ -+ (NNServiceManager *)sharedManager; - -/*! - * @method registerAllPossibleServices - * - * @abstract - * Registers all subclasses of NNService known to the runtime with this service manager. - */ -- (void)registerAllPossibleServices; - -/*! - * @method registerService: - * - * @discussion - * Registers service with the service manager, and starts it if its - * dependencies have all been met. - * - * @param service - * The Class of a service to be registered with this service manager. - */ -- (void)registerService:(Class)service; - -/*! - * @method instanceForService - * - * @discussion - * Accessor method to get the instance of a service class, started or not, - * managed by this service manager. - * - * @result - * An instance of the requested service. nil if the service has - * not been registered with this service manager. - */ -- (NNService *)instanceForService:(Class)service; - -/*! - * @method addObserver:forService: - * - * @discussion - * Adds an observer to the service's notification group. Observers must conform - * to the protocol specified by the service's subscriberProtocol. - * - * Observers are automatically removed if they are deallocated while observing - * the service. - * - * @param observer - * The object that is interested in the service's events. The observer must - * conform to the service's subscriberProtocol. - * - * @param service - * The service that the caller wishes to observe. - */ -- (void)addObserver:(id)observer forService:(Class)service; - -/*! - * @method removeSubscriber:forService: - * - * @discussion - * Removes the observer from the service's notification group. - * - * @param observer - * The object that is no longer interested in the service. - * - * @param service - * The service from which the caller is unsubscribing. - */ -- (void)removeObserver:(id)observer forService:(Class)service; - -/*! - * @method addSubscriber:forService:: - * - * @discussion - * Increments the service's subscriber count. Services that are run on demand - * will be started by calls to this method if there were not other subscribers. - * - * Subscribers are automatically removed if they are deallocated while - * subscribed to the service. - * - * @param subscriber - * The object that is interested in the service's events. As with observers, the - * subscriber must conform to the service's subscriberProtocol. - * - * @param service - * The service to which the caller is subscribing. - */ -- (void)addSubscriber:(id)subscriber forService:(Class)service; - -/*! - * @method removeSubscriber:forService: - * - * @discussion - * Decrements the service's subscriber count. Services that are run on demand - * will be stopped by calls to this method when the subscriber count reaches zero. - * - * @param subscriber - * The object that is no longer interested in the service. - * - * @param service - * The service from which the caller is unsubscribing. - */ -- (void)removeSubscriber:(id)subscriber forService:(Class)service; - -@end diff --git a/Frameworks/NNKit/NNKit/Services/NNServiceManager.m b/Frameworks/NNKit/NNKit/Services/NNServiceManager.m deleted file mode 100644 index ab04d21f..00000000 --- a/Frameworks/NNKit/NNKit/Services/NNServiceManager.m +++ /dev/null @@ -1,348 +0,0 @@ -// -// NNServiceManager.m -// NNKit -// -// Created by Scott Perry on 10/17/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNServiceManager.h" - -#import - -#import "nn_autofree.h" -#import "macros.h" -#import "NNCleanupProxy.h" -#import "NNMultiDispatchManager.h" -#import "NNService+Protected.h" -#import "NNWeakSet.h" - - -static NSMutableSet *claimedServices; - - -#ifndef NS_BLOCK_ASSERTIONS -static BOOL _serviceIsValid(Class service) -{ - return [service isSubclassOfClass:[NNService class]]; -} -#endif - - -@interface _NNServiceInfo : NSObject - -@property (nonatomic, strong, readonly) NNWeakSet *subscribers; -@property (nonatomic, assign, readonly) NNServiceType type; -@property (nonatomic, strong, readonly) NNService *instance; -@property (nonatomic, strong, readonly) NSSet *dependencies; -@property (nonatomic, strong, readonly) Protocol *subscriberProtocol; - -- (instancetype)initWithService:(Class)service; - -@end - - -@implementation _NNServiceInfo - -- (instancetype)initWithService:(Class)service; -{ - NSParameterAssert(_serviceIsValid(service)); - if (!(self = [super init])) { return nil; } - - self->_subscribers = [NNWeakSet new]; - self->_instance = [service sharedService]; - self->_instance.subscriberDispatcher.enabled = NO; - self->_type = [service serviceType]; - self->_dependencies = [service dependencies] ?: [NSSet set]; - self->_subscriberProtocol = [service subscriberProtocol] ?: @protocol(NSObject); - - return self; -} - -@end - - -@interface NNServiceManager () - -// Class => _NNServiceInfo -@property (nonatomic, strong) NSMutableDictionary *lookup; -#define SERVICEINFO(service) ((_NNServiceInfo *)self.lookup[(service)]) - -// Class -@property (nonatomic, strong) NSMutableSet *runningServices; - -// Class => NSMutableSet -@property (nonatomic, strong) NSMutableDictionary *dependantServices; - -@end - - -@implementation NNServiceManager - -#pragma mark - Initialization - -+ (void)initialize; -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - claimedServices = [NSMutableSet new]; - }); -} - -+ (NNServiceManager *)sharedManager; -{ - static NNServiceManager *_sharedManager; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _sharedManager = [NNServiceManager new]; - }); - - return _sharedManager; -} - -- (void)registerAllPossibleServices; -{ - BOOL (^classIsService)(Class) = ^(Class class){ - while ((class = class_getSuperclass(class))) { - if (class == objc_getClass("NNService")) { - return YES; - } - } - - return NO; - }; - - int numClasses = objc_getClassList(NULL, 0); - Class *buffer = (__unsafe_unretained Class *)nn_autofree(malloc(numClasses * sizeof(Class *))); - (void)objc_getClassList(buffer, numClasses); - - for (size_t i = 0; i < numClasses; ++i) { - if (classIsService(buffer[i])) { - [self registerService:buffer[i]]; - } - } -} - -- (instancetype)init; -{ - if (!(self = [super init])) { return nil; } - - self->_lookup = [NSMutableDictionary new]; - self->_runningServices = [NSMutableSet new]; - self->_dependantServices = [NSMutableDictionary new]; - - return self; -} - -- (void)dealloc; -{ - while (self->_runningServices.count) { - for (Class service in self->_runningServices) { - if (SERVICEINFO(service).dependencies.count == 0) { - [self _stopService:service]; - break; - } - } - } - - @synchronized([NNServiceManager class]) { - for (Class service in self->_lookup) { - [claimedServices removeObject:service]; - } - } -} - -#pragma mark - NSObject - -- (NSString *)description; -{ - NSMutableString *result = [NSMutableString stringWithFormat:@"<%@: %p>, services:", NSStringFromClass([self class]), self]; - for (Class service in self->_lookup) { - [result appendFormat:@"\n\t%@: %@", ([self->_runningServices containsObject:service] ? @"running" : @"stopped"), service]; - } - return result; -} - -#pragma mark - NNServiceManager - -- (void)registerService:(Class)service; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - - NSParameterAssert(_serviceIsValid(service)); - if (SERVICEINFO(service)) { - return; - } - - _NNServiceInfo *info = [[_NNServiceInfo alloc] initWithService:service]; - - @synchronized([NNServiceManager class]) { - if ([claimedServices containsObject:service]) { - @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"Service %@ already being managed", NSStringFromClass(service)] userInfo:nil]; - } - [claimedServices addObject:service]; - } - - self.lookup[service] = info; - - for (Class dependency in info.dependencies) { - NSMutableSet *deps = self.dependantServices[dependency]; - if (!deps) { - self.dependantServices[dependency] = deps = [NSMutableSet new]; - } - - [deps addObject:service]; - } - - [self _startServiceIfReady:service]; -} - -- (NNService *)instanceForService:(Class)service; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - - // May be nil if service is not registered! - return SERVICEINFO(service).instance; -} - -- (void)addObserver:(id)observer forService:(Class)service; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - - if (!SERVICEINFO(service)) { - NSLog(@"Service %@ was not already known, attempting to register with %@.", NSStringFromClass(service), self); - [self registerService:service]; - NSAssert(SERVICEINFO(service), @"Failed to register service %@ with %@", NSStringFromClass(service), self); - } - - NSParameterAssert([observer conformsToProtocol:SERVICEINFO(service).subscriberProtocol]); - - [SERVICEINFO(service).instance.subscriberDispatcher addObserver:observer]; -} - -- (void)removeObserver:(id)observer forService:(Class)service; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - NSParameterAssert(SERVICEINFO(service)); - NSParameterAssert(![SERVICEINFO(service).subscribers containsObject:observer]); - NSParameterAssert([SERVICEINFO(service).instance.subscriberDispatcher hasObserver:observer]); - - [SERVICEINFO(service).instance.subscriberDispatcher removeObserver:observer]; -} - -- (void)addSubscriber:(id)subscriber forService:(Class)service; -{ - // -addObserver already takes care of parameter validity checking and thread-safety checks. - [self addObserver:subscriber forService:service]; - - if ([SERVICEINFO(service).subscribers containsObject:subscriber]) { - NSLog(@"Object %@ is already subscribed to service %@", subscriber, NSStringFromClass(service)); - return; - } - - [SERVICEINFO(service).subscribers addObject:subscriber]; - __weak typeof(self) weakSelf = self; - [NNCleanupProxy cleanupAfterTarget:subscriber withBlock:^{ dispatch_async(dispatch_get_main_queue(), ^{ - typeof(self) self = weakSelf; - [self _stopServiceIfDone:service]; - }); } withKey:((uintptr_t)service ^ (uintptr_t)self)]; - - [self _startServiceIfReady:service]; -} - -- (void)removeSubscriber:(id)subscriber forService:(Class)service; -{ - NSAssert([NSThread isMainThread], @"Boundary call was not made on main thread"); - NSParameterAssert(SERVICEINFO(service)); - NSParameterAssert([SERVICEINFO(service).subscribers containsObject:subscriber]); - - [NNCleanupProxy cancelCleanupForTarget:subscriber withKey:((uintptr_t)service ^ (uintptr_t)self)]; - [SERVICEINFO(service).subscribers removeObject:subscriber]; - - // This must come after the removal of the subscriber to catch the case where a caller tries to remove an observer that is actually a subscriber. - [self removeObserver:subscriber forService:service]; - - [self _stopServiceIfDone:service]; -} - -#pragma mark Private - -- (void)_startServiceIfReady:(Class)service; -{ - if ([self.runningServices containsObject:service]) { - return; - } - - if (SERVICEINFO(service).type == NNServiceTypeOnDemand && SERVICEINFO(service).subscribers.count == 0) { - return; - } - - if (![SERVICEINFO(service).dependencies isSubsetOfSet:self.runningServices]) { - return; - } - - [self _startService:service]; -} - -- (void)_stopServiceIfDone:(Class)service; -{ - // This is not needed for correctness, but let's avoid all that recursion if possible. - if (![self.runningServices containsObject:service]) { - return; - } - - // Why is this calling count on subscribers.allObjects instead of on subscribers? Because: - // NSHashMap was giving a count of 1 when the actual membership of the set was 0 (count didn't update synchronously with object death). allObjects (correctly) returns an empty array in this case. - // This was the one feature that NNMutableWeakSet had that cost it the most in terms of performance, so it's understandable that NSHashMap doesn't support it.. - NSUInteger actualSubscriberCount = SERVICEINFO(service).subscribers.allObjects.count; - - BOOL dependenciesMet = [SERVICEINFO(service).dependencies isSubsetOfSet:self.runningServices]; - BOOL serviceIsWanted = SERVICEINFO(service).type != NNServiceTypeOnDemand || actualSubscriberCount > 0; - if (dependenciesMet && serviceIsWanted) { - return; - } - - [self _stopService:service]; -} - -- (void)_startService:(Class)service; -{ - NSParameterAssert(![self.runningServices containsObject:service]); - - NNService *instance = SERVICEINFO(service).instance; - - instance.subscriberDispatcher.enabled = YES; - - [instance startService]; - - [self.runningServices addObject:service]; - - for (Class dependantClass in self.dependantServices[service]) { - [self _startServiceIfReady:dependantClass]; - } -} - -- (void)_stopService:(Class)service; -{ - NSParameterAssert([self.runningServices containsObject:service]); - - NNService *instance = SERVICEINFO(service).instance; - - [self.runningServices removeObject:service]; - - for (Class dependantClass in self.dependantServices[service]) { - [self _stopServiceIfDone:dependantClass]; - } - - [instance stopService]; - - instance.subscriberDispatcher.enabled = NO; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Services/README.md b/Frameworks/NNKit/NNKit/Services/README.md deleted file mode 100644 index 52488c38..00000000 --- a/Frameworks/NNKit/NNKit/Services/README.md +++ /dev/null @@ -1,39 +0,0 @@ -NNKit Services -============== - -NNKit provides a framework for services running in your application, including dependency management, subscriber and observer dispatch, automatic starting and stopping of services based on subscriptions, and an easy mechanism to register all the services in your application in your app delegate, keeping your code small and readable. - -What makes a service --------------------- - -Services are classes that inherit from `NNService` and respond to `serviceType` with a type other than `NNServiceTypeNone`. The other two types are: - -* `NNServiceTypePersistent`: the service will run as long as its dependencies (if any) are running. -* `NNServiceTypeOnDemand`: the service will run as long as its dependencies (if any) are running and at least one object has subscribed to the service using the service manager's `addSubscriber:forService:` method. - -Dependency management ---------------------- - -Service dependencies are defined by the `dependencies` method, which returns an NSSet of class objects. Dependencies that are not already known to the service manager will be added automatically if possible. - -Subscriber (and observer) message dispatch ------------------------------------------- - -Services whose instances dispatch messages to their subscribers must respond to `subscriberProtocol` with the appropriate protocol. Subscribers must conform to this protocol and will be checked at runtime. Sending a message to subscribers can be accomplished with code resembling the following: - -``` objective-c -- (void)sendMessage; -{ - [(id)self.subscriberDispatcher serviceWillDoThing:self]; -} -``` - -Convenience hacks ------------------ - -Automatically adding all services known to the runtime is accomplished by calling `registerAllPossibleServices` on a service manager, preferably the `sharedManager`. - -Would you like to know more? ----------------------------- - -For a deeper dive into services, check out the [unit tests](https://github.com/numist/NNKit/blob/master/NNKitTests/NNServiceTests.m). diff --git a/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.h b/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.h deleted file mode 100644 index e6190f4b..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// NNISASwizzledObject.h -// NNKit -// -// Created by Scott Perry on 02/07/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@protocol NNISASwizzledObject @end - - -@interface NNISASwizzledObject : NSObject - -+ (void)prepareObjectForSwizzling:(NSObject *)anObject; - -@end diff --git a/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.m b/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.m deleted file mode 100644 index ef9724f1..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/NNISASwizzledObject.m +++ /dev/null @@ -1,81 +0,0 @@ -// -// NNISASwizzledObject.m -// NNKit -// -// Created by Scott Perry on 02/07/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNISASwizzledObject.h" - -#import - -#import "nn_isaSwizzling_Private.h" - - -static void *_NNSwizzleSuperclassKey; - - -__attribute__((constructor)) -static void nn_isaSwizzling_init() { - arc4random_buf(&_NNSwizzleSuperclassKey, sizeof(_NNSwizzleSuperclassKey)); -} - - -@implementation NNISASwizzledObject - -#pragma mark Swizzler runtime support. - -+ (void)prepareObjectForSwizzling:(NSObject *)anObject; -{ - // Cache the original value of -class so the swizzled object can lie about itself later. - objc_setAssociatedObject(anObject, _NNSwizzleSuperclassKey, [anObject class], OBJC_ASSOCIATION_ASSIGN); -} - -#pragma mark Private swizzled methods - -- (Class)_swizzler_actualClass -{ - return object_getClass(self); -} - -#pragma mark Swizzled object overrides - -- (Class)class -{ - Class superclass = objc_getAssociatedObject(self, _NNSwizzleSuperclassKey); - - if (!superclass) { - NSLog(@"ERROR: couldn't find stashed superclass for swizzled object, falling back to parent class—if you're using KVO, this might break everything!"); - return class_getSuperclass(object_getClass(self)); - } - - return superclass; -} - -- (BOOL)conformsToProtocol:(Protocol *)aProtocol -{ - return [[self _swizzler_actualClass] conformsToProtocol:aProtocol]; -} - -- (BOOL)respondsToSelector:(SEL)aSelector -{ - return [[self _swizzler_actualClass] instancesRespondToSelector:aSelector]; -} - -- (BOOL)isKindOfClass:(Class)aClass; -{ - if (nn_alreadySwizzledObjectWithSwizzlingClass(self, aClass)) { - return YES; - } - - return [super isKindOfClass:aClass]; -} - -@end diff --git a/Frameworks/NNKit/NNKit/Swizzling/README.md b/Frameworks/NNKit/NNKit/Swizzling/README.md deleted file mode 100644 index 378e9078..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/README.md +++ /dev/null @@ -1,57 +0,0 @@ -NNKit Swizzling -=============== - -NNKit provides robust, general-purpose swizzling utilities. - -If you think this is a good idea, you should probably stop and make sure that your needs are not a symptom of more severe architectural problems. - -Isa Swizzling -------------- - -Robust isa swizzling is provided using the `nn_object_swizzleIsa` function. The following conditions must be met: - -* The object is an instance of the swizzling class's superclass, or a subclass of the swizzling class's superclass. -* The swizzling class does not add any ivars or non-dynamic properties. - -An object has been swizzled by a class if it responds YES to `isKindOfClass:` with the swizzling class as an argument. Protocols can also be used, and queried using `conformsToProtocol:`, as usual. - -To avoid any confusion, your swizzling class should not implement an allocator or initializer. They will never be called for swizzled objects. - -### Usage ### - -First, you'll need to define your swizzling class. For example: - - @interface MYClass : NSObject - @property (nonatomic, readonly) NSUInteger random; - - (void)duck; - @end - - @implementation MYClass - - (NSUInteger)random { return 7; } - - (void)duck { NSLog(@"quack!"); } - - (void)dog { NSLog(@"woof!"); } - @end - -To swizzle your object and use its newfound functionality, just call `nn_object_swizzleIsa`: - - #import - - @implementation MYCode - - (void)main { - NSObject *bar = [[NSObject alloc] init]; - nn_object_swizzleIsa(bar, [MYClass class]); - if ([bar isKindOfClass:[MYClass class]]) { - [(MYClass *)bar duck]; - } - if ([bar conformsToProtocol:@protocol(NSDog)]) { - [(id)bar dog]; - } - } - @end - -See the tests for more explicit examples of what is supposed to work and what is supposed to be an error. - -Credits -======= - -If not for [Rob Rix](https://github.com/robrix/), the Swizzling component of NNKit would not exist. Which probably would have been a good thing. diff --git a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.h b/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.h deleted file mode 100644 index 69b2b81b..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// nn_isaSwizzling.h -// NNKit -// -// Created by Scott Perry on 02/07/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -/*! - * @function nn_object_swizzleIsa - * - * @abstract - * Swizzles the class of obj to a dynamic subclass including the - * qualities of swizzlingClass. - * - * @discussion - * This function implements generic isa swizzling assuming the following conditions are met: - * • A protocol with the same name as swizzlingClass exists and is - * implemented by swizzlingClass. - * • obj is an instance of the swizzlingClass's - * superclass, or a subclass of swizzlingClass's superclass. - * • swizzlingClass does not add any ivars or non-dynamic properties. - * - * An object has been swizzled by a class if it conforms to that class's - * complementing protocol, allowing you to cast the object (after checking!) to - * a type that explicitly implements the protocol. - * - * For more details about use, see the tests in nn_isaSwizzlingTests.m. - * - * @param obj - * The object to be swizzled. - * - * @param swizzlingClass - * The class to apply to obj. - */ -BOOL nn_object_swizzleIsa(id obj, Class swizzlingClass) __attribute__((nonnull(1, 2))); diff --git a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.m b/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.m deleted file mode 100644 index 60e2027f..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling.m +++ /dev/null @@ -1,200 +0,0 @@ -// -// nn_isaSwizzling.m -// NNKit -// -// Created by Scott Perry on 02/07/13. -// Copyright © 2013 Scott Perry. -// -// 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 "nn_isaSwizzling_Private.h" - -#import - -#import "NNISASwizzledObject.h" -#import "nn_autofree.h" - - -static NSString *_prefixForSwizzlingClass(Class aClass) -{ - return [NSString stringWithFormat:@"SwizzledWith%s_", class_getName(aClass)]; -} - -static NSString * _classNameForObjectWithSwizzlingClass(id anObject, Class aClass) -{ - return [NSString stringWithFormat:@"%@%s", _prefixForSwizzlingClass(aClass), object_getClassName(anObject)]; -} - -#pragma mark Class copying functions - -static void _class_addMethods(Class targetClass, Method *methods) { - Method method; - for (NSUInteger i = 0; methods && (method = methods[i]); i++) { - // targetClass is a brand new shiny class, so this should never fail because it already implements a method (even though its superclass(es) might). - if(!class_addMethod(targetClass, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method))) { - // numist/NNKit#17 - NSLog(@"Warning: Replacing method %@ previously defined by class %@?", NSStringFromSelector(method_getName(method)), NSStringFromClass(targetClass)); - class_replaceMethod(targetClass, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method)); - } - } -} - -static void _class_addClassMethodsFromClass(Class targetClass, Class source) -{ - _class_addMethods(object_getClass(targetClass), nn_autofree(class_copyMethodList(object_getClass(source), NULL))); -} - -static void _class_addInstanceMethodsFromClass(Class targetClass, Class source) -{ - _class_addMethods(targetClass, nn_autofree(class_copyMethodList(source, NULL))); -} - -static void _class_addProtocolsFromClass(Class targetClass, Class aClass) -{ - Protocol * __unsafe_unretained *protocols = (Protocol * __unsafe_unretained *)nn_autofree(class_copyProtocolList(aClass, NULL)); - Protocol __unsafe_unretained *protocol; - - for (NSUInteger i = 0; protocols && (protocol = protocols[i]); i++) { - // targetClass is a brand new shiny class, so this should never fail because it already conforms to a protocol (even though its superclass(es) might). - if (!class_addProtocol(targetClass, protocol)) { - NSLog(@"Warning: class %@ already conforms to protocol %@?", NSStringFromClass(targetClass), NSStringFromProtocol(protocol)); - } - } -} - -static void _class_addPropertiesFromClass(Class targetClass, Class aClass) -{ - objc_property_t *properties = nn_autofree(class_copyPropertyList(aClass, NULL)); - objc_property_t property; - - for (NSUInteger i = 0; properties && (property = properties[i]); i++) { - unsigned attributeCount; - objc_property_attribute_t *attributes = nn_autofree(property_copyAttributeList(property, &attributeCount)); - - // targetClass is a brand new shiny class, so this should never fail because it already has certain properties (even though its superclass(es) might). - if(!class_addProperty(targetClass, property_getName(property), attributes, attributeCount)) { - // numist/NNKit#17 - NSLog(@"Warning: Replacing property %s previously defined by class %@?", property_getName(property), NSStringFromClass(targetClass)); - class_replaceProperty(targetClass, property_getName(property), attributes, attributeCount); - } - } -} - -#pragma mark Swizzling safety checks - -static void _class_checkForNonDynamicProperties(Class aClass) -{ - objc_property_t *properties = nn_autofree(class_copyPropertyList(aClass, NULL)); - - for (unsigned i = 0; properties && properties[i]; i++) { - objc_property_attribute_t *attributes = nn_autofree(property_copyAttributeList(properties[i], NULL)); - - for (unsigned j = 0; attributes && attributes[j].name; j++) { - if (!strcmp(attributes[j].name, "D")) { // The property is dynamic (@dynamic). - NSLog(@"Warning: Swizzling class %s contains non-dynamic property %s", class_getName(aClass), property_getName(properties[i])); - } - } - } -} - -static BOOL _class_containsIvars(Class aClass) -{ - unsigned ivars; - free(class_copyIvarList(aClass, &ivars)); - return ivars != 0; -} - -#pragma mark Isa swizzling - -static Class _targetClassForObjectWithSwizzlingClass(id anObject, Class aClass) -{ - Class targetClass = objc_getClass(_classNameForObjectWithSwizzlingClass(anObject, aClass).UTF8String); - - if (!targetClass) { - BOOL success = YES; - const char *swizzlingClassName = class_getName(aClass); - - Class sharedAncestor = class_getSuperclass(aClass); - if (![anObject isKindOfClass:sharedAncestor]) { - NSLog(@"Target object %@ must be a subclass of %@ to be swizzled with class %s.", anObject, sharedAncestor, swizzlingClassName); - success = NO; - } - - _class_checkForNonDynamicProperties(aClass); - - if (_class_containsIvars(aClass)) { - NSLog(@"Swizzling class %s cannot contain ivars not inherited from its superclass", swizzlingClassName); - success = NO; - } - - if (!success) { - return Nil; - } - - targetClass = objc_allocateClassPair(object_getClass(anObject), _classNameForObjectWithSwizzlingClass(anObject, aClass).UTF8String, 0); - _class_addClassMethodsFromClass(targetClass, aClass); - _class_addInstanceMethodsFromClass(targetClass, aClass); - _class_addProtocolsFromClass(targetClass, aClass); - _class_addPropertiesFromClass(targetClass, aClass); - - objc_registerClassPair(targetClass); - } - - return targetClass; -} - -static BOOL _object_swizzleIsa(id anObject, Class aClass) -{ - assert(!nn_alreadySwizzledObjectWithSwizzlingClass(anObject, aClass)); - - Class targetClass = _targetClassForObjectWithSwizzlingClass(anObject, aClass); - - if (!targetClass) { - return NO; - } - - object_setClass(anObject, targetClass); - - return YES; -} - -#pragma mark Privately-exported functions - -BOOL nn_alreadySwizzledObjectWithSwizzlingClass(id anObject, Class aClass) -{ - NSString *classPrefix = _prefixForSwizzlingClass(aClass); - - for (Class candidate = object_getClass(anObject); candidate; candidate = class_getSuperclass(candidate)) { - if ([[NSString stringWithUTF8String:class_getName(candidate)] hasPrefix:classPrefix]) { - return YES; - } - } - - return NO; -} - -#pragma mark Publicly-exported funtions - -BOOL nn_object_swizzleIsa(id anObject, Class aClass) { - BOOL success = YES; - - @autoreleasepool { - // Bootstrap the object with the necessary lies, like overriding -class to report the original class. - if (!nn_alreadySwizzledObjectWithSwizzlingClass(anObject, [NNISASwizzledObject class])) { - [NNISASwizzledObject prepareObjectForSwizzling:anObject]; - - success = _object_swizzleIsa(anObject, [NNISASwizzledObject class]); - } - - if (success && !nn_alreadySwizzledObjectWithSwizzlingClass(anObject, aClass)) { - success = _object_swizzleIsa(anObject, aClass); - } - } - - return success; -} diff --git a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling_Private.h b/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling_Private.h deleted file mode 100644 index e42f4d92..00000000 --- a/Frameworks/NNKit/NNKit/Swizzling/nn_isaSwizzling_Private.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// nn_isaSwizzling_Private.h -// NNKit -// -// Created by Scott Perry on 02/08/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -BOOL nn_alreadySwizzledObjectWithSwizzlingClass(id anObject, Class aClass) __attribute__((nonnull(1, 2))); - diff --git a/Frameworks/NNKit/NNKit/macros.h b/Frameworks/NNKit/NNKit/macros.h deleted file mode 100644 index bd8d36d7..00000000 --- a/Frameworks/NNKit/NNKit/macros.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// macros.h -// NNKit -// -// Created by Scott Perry on 02/25/14. -// Copyright © 2014 Scott Perry. -// -// 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. -// - -#pragma once - -// Compile-time key path additions -#define NNKeyPath(object, keyPath) ({ if (NO) { (void)((object).keyPath); } @#keyPath; }) -#define NNSelfKeyPath(keyPath) NNKeyPath(self, keyPath) -#define NNTypedKeyPath(ObjectClass, keyPath) NNKeyPath(((ObjectClass *)nil), keyPath) -#define NNProtocolKeyPath(Protocol, keyPath) NNKeyPath(((id )nil), keyPath) - -// Compile-time selector additions -#define NNSelector(object, selectorName) ({ if (NO) { (void)[(object) selectorName]; } @selector(selectorName); }) -#define NNSelfSelector(selectorName) NNSelector(self, selectorName) -#define NNTypedSelector(ObjectClass, selectorName) NNSelector(((ObjectClass *)nil), selectorName) -#define NNProtocolSelector(Protocol, selectorName) NNSelector(((id )nil), selectorName) - -#define NNSelector1(object, selectorName) ({ if (NO) { (void)[(object) selectorName nil]; } @selector(selectorName); }) -#define NNSelfSelector1(selectorName) NNSelector1(self, selectorName) -#define NNTypedSelector1(ObjectClass, selectorName) NNSelector1(((ObjectClass *)nil), selectorName) -#define NNProtocolSelector1(Protocol, selectorName) NNSelector1(((id )nil), selectorName) diff --git a/Frameworks/NNKit/NNKitTests/NNCleanupProxyTests.m b/Frameworks/NNKit/NNKitTests/NNCleanupProxyTests.m deleted file mode 100644 index 97109c44..00000000 --- a/Frameworks/NNKit/NNKitTests/NNCleanupProxyTests.m +++ /dev/null @@ -1,149 +0,0 @@ -// -// NNCleanupProxyTests.m -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import -#import - - -@protocol NNCleanupProxyTestProtocol -- (BOOL)someKindOfSelectorWithObject:(id)foo; -@end - - -@protocol NNCleanupProxyTestProtocol2 -- (BOOL)someKindOfSelectorWithObject2:(id)foo; -@end - - -@interface NNCleanupProxyTestClass : NSObject -@end -@implementation NNCleanupProxyTestClass -- (NSUInteger)hash; -{ - return (uintptr_t)self; -} - -- (BOOL)isEqual:(NSObject *)object; -{ - return self.hash == object.hash; -} - -- (BOOL)someKindOfSelectorWithObject:(id)foo; -{ - return YES; -} -- (BOOL)someKindOfSelectorWithObject2:(id)foo; -{ - return YES; -} -- (BOOL)anotherKindofSelectorWithObject:(id)foo; -{ - return YES; -} -@end - - -@interface NNCleanupProxyTests : XCTestCase - -@end - - -@implementation NNCleanupProxyTests - -- (void)testCleanupBlock; -{ - __block BOOL cleanupComplete = NO; - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:foo withKey:(uintptr_t)foo]; - proxy.cleanupBlock = ^{ cleanupComplete = YES; }; - } - XCTAssertTrue(cleanupComplete, @""); -} - -- (void)testProtocolMethodDispatch; -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - id proxy = (id)[NNCleanupProxy cleanupProxyForTarget:foo conformingToProtocol:@protocol(NNCleanupProxyTestProtocol2) withKey:(uintptr_t)foo]; - XCTAssertTrue([proxy someKindOfSelectorWithObject:self], @""); - XCTAssertTrue([proxy someKindOfSelectorWithObject2:self], @""); - } -} - -- (void)testUndeclaredMethodDispatch; -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - NNCleanupProxyTestClass *proxy = (id)[NNCleanupProxy cleanupProxyForTarget:foo withKey:(uintptr_t)foo]; - XCTAssertThrows([proxy someKindOfSelectorWithObject:self], @""); - XCTAssertThrows([proxy someKindOfSelectorWithObject2:self], @""); - XCTAssertThrows([proxy anotherKindofSelectorWithObject:self], @""); - } -} - -- (void)testExplicitlyDeclaredMethodDispatch; -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:foo withKey:(uintptr_t)foo]; - [proxy cacheMethodSignatureForSelector:@selector(anotherKindofSelectorWithObject:)]; - - NNCleanupProxyTestClass *castProxy = (id)proxy; - XCTAssertTrue([castProxy anotherKindofSelectorWithObject:self], @""); - } -} - -- (void)testNilMessagingAfterTargetDealloc; -{ - NNCleanupProxyTestClass *proxy = nil; - - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - proxy = (id)[NNCleanupProxy cleanupProxyForTarget:foo conformingToProtocol:@protocol(NNCleanupProxyTestProtocol2) withKey:(uintptr_t)foo]; - } - - XCTAssertThrows([proxy someKindOfSelectorWithObject:self], @""); - XCTAssertThrows([proxy someKindOfSelectorWithObject2:self], @""); - XCTAssertThrows([proxy anotherKindofSelectorWithObject:self], @""); -} - -- (void)testCachingInvalidMethod; -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:foo withKey:(uintptr_t)foo]; - XCTAssertThrows([proxy cacheMethodSignatureForSelector:@selector(testCachingInvalidMethod)]); - } -} - -- (void)testProxyMatchesObject; -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NNCleanupProxyTestClass new]; - NNCleanupProxy *proxy = [NNCleanupProxy cleanupProxyForTarget:foo withKey:(uintptr_t)foo]; - - XCTAssertTrue([foo isEqual:proxy]); - NSSet *set = [NSSet setWithObject:proxy]; - XCTAssertTrue([set containsObject:foo]); - - XCTAssertTrue([proxy isEqual:foo]); - set = [NSSet setWithObject:foo]; - XCTAssertTrue([set containsObject:proxy]); - } -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNComprehensionTests.m b/Frameworks/NNKit/NNKitTests/NNComprehensionTests.m deleted file mode 100644 index 1eee8cc9..00000000 --- a/Frameworks/NNKit/NNKitTests/NNComprehensionTests.m +++ /dev/null @@ -1,73 +0,0 @@ -// -// NNComprehensionTests.m -// NNKit -// -// Created by Scott Perry on 03/11/14. -// Copyright © 2014 Scott Perry. -// -// 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 - -#import - - -@interface NNComprehensionTests : XCTestCase - -@end - -@implementation NNComprehensionTests - -- (void)testFilter; -{ - NSArray *inputArray = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]; - NSSet *inputSet = [NSSet setWithArray:inputArray]; - NSOrderedSet *inputOrderedSet = [NSOrderedSet orderedSetWithArray:inputArray]; - - NSArray *outputArray = @[@3, @6, @9]; - NSSet *outputSet = [NSSet setWithArray:outputArray]; - NSOrderedSet *outputOrderedSet = [NSOrderedSet orderedSetWithArray:outputArray]; - - nn_filter_block_t filterBlock = ^(id item){ return (BOOL)!([item integerValue] % 3); }; - - XCTAssertEqualObjects(outputArray, [inputArray nn_filter:filterBlock], @"Filtered array did not match expectations"); - XCTAssertEqualObjects(outputSet, [inputSet nn_filter:filterBlock], @"Filtered set did not match expectations"); - XCTAssertEqualObjects(outputOrderedSet, [inputOrderedSet nn_filter:filterBlock], @"Filtered array did not match expectations"); -} - -- (void)testMap; -{ - NSArray *inputArray = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]; - NSSet *inputSet = [NSSet setWithArray:inputArray]; - NSOrderedSet *inputOrderedSet = [NSOrderedSet orderedSetWithArray:inputArray]; - - NSArray *outputArray = @[@2, @4, @6, @8, @10, @12, @14, @16, @18, @20]; - NSSet *outputSet = [NSSet setWithArray:outputArray]; - NSOrderedSet *outputOrderedSet = [NSOrderedSet orderedSetWithArray:outputArray]; - - nn_map_block_t mapBlock = ^(id item){ return @([item integerValue] * 2); }; - - XCTAssertEqualObjects(outputArray, [inputArray nn_map:mapBlock], @"Filtered array did not match expectations"); - XCTAssertEqualObjects(outputSet, [inputSet nn_map:mapBlock], @"Filtered set did not match expectations"); - XCTAssertEqualObjects(outputOrderedSet, [inputOrderedSet nn_map:mapBlock], @"Filtered array did not match expectations"); -} - -- (void)testReduce; -{ - NSArray *inputArray = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]; - NSSet *inputSet = [NSSet setWithArray:inputArray]; - NSOrderedSet *inputOrderedSet = [NSOrderedSet orderedSetWithArray:inputArray]; - - NSNumber *output = @55; - - XCTAssertEqualObjects(output, [inputArray nn_reduce:^(id acc, id item){ return @([acc integerValue] + [item integerValue]); }], @""); - XCTAssertEqualObjects(output, [inputSet nn_reduce:^(id acc, id item){ return @([acc integerValue] + [item integerValue]); }], @""); - XCTAssertEqualObjects(output, [inputOrderedSet nn_reduce:^(id acc, id item){ return @([acc integerValue] + [item integerValue]); }], @""); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNDelegateProxyTests.m b/Frameworks/NNKit/NNKitTests/NNDelegateProxyTests.m deleted file mode 100644 index c7567d2e..00000000 --- a/Frameworks/NNKit/NNKitTests/NNDelegateProxyTests.m +++ /dev/null @@ -1,181 +0,0 @@ -// -// NNDelegateProxyTests.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import - - -@protocol MYClassDelegate - -- (void)objectCalledDelegateMethod:(id)obj; -- (oneway void)asyncMethod; - -@optional - -- (void)unimplementedOptionalMethod; -- (oneway void)unimplementedOnewayOptionalMethod; - -@end - - -@interface NNDelegateProxyTests : XCTestCase - -@end - - -@interface MYClass : NSObject - -@property (strong) id delegateProxy; - -@end - -@implementation MYClass - -- (instancetype)initWithDelegate:(id)delegate; -{ - if (!(self = [super init])) { return nil; } - - _delegateProxy = [NNDelegateProxy proxyWithDelegate:delegate protocol:@protocol(MYClassDelegate)]; - - return self; -} - -- (void)globalAsync; -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self.delegateProxy objectCalledDelegateMethod:self]; - }); -} - -- (void)globalSync; -{ - dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [self.delegateProxy objectCalledDelegateMethod:self]; - }); -} - -- (void)mainAsync; -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self.delegateProxy objectCalledDelegateMethod:self]; - }); -} - -- (void)invalid; -{ - [(id)self.delegateProxy willChangeValueForKey:@""]; -} - -- (void)async; -{ - [self.delegateProxy asyncMethod]; -} - -- (void)optional; -{ - [self.delegateProxy unimplementedOptionalMethod]; -} - -- (void)onewayOptional; -{ - [self.delegateProxy unimplementedOnewayOptionalMethod]; -} - -@end - - -static dispatch_group_t group; - - -@implementation NNDelegateProxyTests - -- (void)setUp -{ - [super setUp]; - - group = dispatch_group_create(); -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testGlobalAsync; -{ - dispatch_group_enter(group); - [[[MYClass alloc] initWithDelegate:self] globalAsync]; - - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:0.1]; - while (!despatch_group_yield(group) && [[NSDate date] compare:timeout] == NSOrderedAscending); - XCTAssertFalse(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was never received (timed out)!"); -} - -- (void)testGlobalSync; -{ - dispatch_group_enter(group); - [[[MYClass alloc] initWithDelegate:self] globalSync]; - XCTAssertFalse(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was not received synchronously!"); -} - -- (void)testMainAsync; -{ - dispatch_group_enter(group); - [[[MYClass alloc] initWithDelegate:self] mainAsync]; - - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:0.1]; - while (!despatch_group_yield(group) && [[NSDate date] compare:timeout] == NSOrderedAscending); - XCTAssertFalse(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was never received (timed out)!"); -} - -- (void)testInvalidDelegateMethod; -{ - XCTAssertThrows([[[MYClass alloc] initWithDelegate:self] invalid], @"Invalid delegate method was allowed to pass through!"); -} - -- (void)testOnewayAsync; -{ - dispatch_group_enter(group); - [[[MYClass alloc] initWithDelegate:self] async]; - - XCTAssertTrue(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was not supposed to be sent synchronously."); - - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:0.1]; - while (!despatch_group_yield(group) && [[NSDate date] compare:timeout] == NSOrderedAscending); - XCTAssertFalse(dispatch_group_wait(group, DISPATCH_TIME_NOW), @"Delegate message was never received (timed out)!"); -} - -- (void)testOptional; -{ - XCTAssertNoThrow([[[MYClass alloc] initWithDelegate:self] optional], @"Sending optional messages to delegate proxies should never blow up."); - XCTAssertNoThrow([[[MYClass alloc] initWithDelegate:self] onewayOptional], @"Sending optional messages to delegate proxies should never blow up."); -} - - -#pragma mark MYClassDelegate - -- (oneway void)asyncMethod; -{ - [self objectCalledDelegateMethod:nil]; -} - -- (void)objectCalledDelegateMethod:(id)obj; -{ - XCTAssertTrue([[NSThread currentThread] isMainThread], @"Delegate message was not dispatched on the main queue!"); - dispatch_group_leave(group); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNMultiDispatchManagerTests.m b/Frameworks/NNKit/NNKitTests/NNMultiDispatchManagerTests.m deleted file mode 100644 index 3f505063..00000000 --- a/Frameworks/NNKit/NNKitTests/NNMultiDispatchManagerTests.m +++ /dev/null @@ -1,187 +0,0 @@ -// -// NNMultiDispatchManagerTests.m -// NNKit -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 -#import -#import - - -unsigned callCount = 0; -dispatch_group_t group; - - -@protocol NNMultiDispatchManagerTestProtocol -- (void)foo:(id)sender; -- (oneway void)bar:(id)sender; -@optional -- (void)baz:(id)sender; -- (id)qux:(id)sender; -@end - - -@interface NNMultiDispatchManagerTestObject : NSObject -@end -@implementation NNMultiDispatchManagerTestObject -- (void)foo:(id)sender; -{ - callCount++; -} -- (oneway void)bar:(id)sender; -{ - callCount++; - dispatch_group_leave(group); -} -@end - - -@interface NNMultiDispatchManagerTestObject2 : NSObject -@end -@implementation NNMultiDispatchManagerTestObject2 -- (void)foo:(id)sender; -{ - callCount++; -} -- (oneway void)bar:(id)sender; -{ - callCount++; - dispatch_group_leave(group); -} -- (void)baz:(id)sender; -{ - callCount++; -} -@end - - -@interface NNMultiDispatchManagerTests : XCTestCase - -@end - - -@implementation NNMultiDispatchManagerTests - -- (void)setUp -{ - [super setUp]; - - callCount = 0; - group = dispatch_group_create(); -} - -- (void)testSync -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo2 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo3 = [NNMultiDispatchManagerTestObject2 new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - - [(id)manager foo:self]; - XCTAssertEqual(callCount, (unsigned)4, @""); -} - -- (void)testAsync; -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo2 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo3 = [NNMultiDispatchManagerTestObject2 new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - dispatch_group_enter(group); - dispatch_group_enter(group); - dispatch_group_enter(group); - dispatch_group_enter(group); - - XCTAssertEqual(callCount, (unsigned)0, @""); - [(id)manager bar:self]; - - while(!despatch_group_yield(group)); - XCTAssertEqual(callCount, (unsigned)4, @""); -} - -- (void)testOptionalSelector; -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo2 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo3 = [NNMultiDispatchManagerTestObject2 new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - - XCTAssertNoThrow([(id)manager baz:self], @""); - XCTAssertEqual(callCount, (unsigned)2, @""); -} - -- (void)testBadSelector; -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo2 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo3 = [NNMultiDispatchManagerTestObject2 new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - - XCTAssertThrows([(id)manager invokeWithTarget:self], @""); - XCTAssertEqual(callCount, (unsigned)0, @""); -} - -- (void)testWeakDispatch -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - @autoreleasepool { - id foo2 = [NNMultiDispatchManagerTestObject new]; - id foo3 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - } - - [(id)manager foo:self]; - XCTAssertEqual(callCount, (unsigned)2, @""); -} - -- (void)testIllegalReturnType -{ - NNMultiDispatchManager *manager = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(NNMultiDispatchManagerTestProtocol)]; - __attribute__((objc_precise_lifetime)) id foo1 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo2 = [NNMultiDispatchManagerTestObject new]; - __attribute__((objc_precise_lifetime)) id foo3 = [NNMultiDispatchManagerTestObject2 new]; - __attribute__((objc_precise_lifetime)) id foo4 = [NNMultiDispatchManagerTestObject2 new]; - [manager addObserver:foo1]; - [manager addObserver:foo2]; - [manager addObserver:foo3]; - [manager addObserver:foo4]; - - XCTAssertThrows((void)[(id)manager qux:self], @""); - XCTAssertEqual(callCount, (unsigned)0, @""); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNPollingObjectTests.m b/Frameworks/NNKit/NNKitTests/NNPollingObjectTests.m deleted file mode 100644 index 88a6de7b..00000000 --- a/Frameworks/NNKit/NNKitTests/NNPollingObjectTests.m +++ /dev/null @@ -1,146 +0,0 @@ -// -// NNPollingObjectTests.m -// NNKit -// -// Created by Scott Perry on 09/06/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import -#import - - -@interface NNPollingObjectTests : XCTestCase - -@end - - -dispatch_group_t pollingObjectGroup; - - -@interface NNTestObject : NNPollingObject -@end -@implementation NNTestObject - -- (instancetype)init; -{ - if (!(self = [super init])) { return nil; } - self.interval = 0.0001; - dispatch_group_enter(pollingObjectGroup); - return self; -} - -- (void)main; -{ - [self postNotification:nil]; -} - -- (void)dealloc; -{ - dispatch_group_leave(pollingObjectGroup); -} - -@end - - -static int iterations; - - -@implementation NNPollingObjectTests - -+ (void)initialize; -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - pollingObjectGroup = dispatch_group_create(); - }); -} - -- (void)setUp -{ - [super setUp]; - - iterations = 0; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(objectNotification:) name:[NNTestObject notificationName] object:nil]; -} - -- (void)tearDown -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super tearDown]; -} - -- (void)testBasicPolling -{ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunused-variable" - __attribute__((objc_precise_lifetime)) NNTestObject *obj = [NNTestObject new]; - #pragma clang diagnostic pop - - NSDate *until = [NSDate dateWithTimeIntervalSinceNow:0.05]; - while ([[NSDate date] compare:until] == NSOrderedAscending && !iterations) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:until]; - } - XCTAssert(iterations > 0, @"Polling object iterated zero times!"); -} - -- (void)testZeroInterval -{ - __attribute__((objc_precise_lifetime)) NNTestObject *obj = [NNTestObject new]; - obj.interval = 0.0; - - NSDate *until = [NSDate dateWithTimeIntervalSinceNow:0.01]; - while ([[NSDate date] compare:until] == NSOrderedAscending && iterations < 2) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:until]; - } - XCTAssert(iterations == 1, @"Polling object iterated more than once!"); -} - -- (void)testObjectDeath -{ - @autoreleasepool { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunused-variable" - __attribute__((objc_precise_lifetime)) NNTestObject *obj = [NNTestObject new]; - #pragma clang diagnostic pop - } - - while(!despatch_group_yield(pollingObjectGroup)); - iterations = 0; - - NSDate *until = [NSDate dateWithTimeIntervalSinceNow:0.05]; - while ([[NSDate date] compare:until] == NSOrderedAscending && !iterations) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:until]; - } - XCTAssert(iterations == 0, @"Object continued polling after it was released!"); -} - -- (void)testNoSubclassing; -{ - @autoreleasepool { - dispatch_queue_t q = dispatch_queue_create("testQ", DISPATCH_QUEUE_SERIAL); - dispatch_suspend(q); - NNPollingObject *obj = [[NNPollingObject alloc] initWithQueue:q]; - obj.interval = -1; - XCTAssertThrows([obj main]); - } -} - -- (void)objectNotification:(NSNotification *)notification; -{ - XCTAssert([[NSThread currentThread] isMainThread], @"Poll notification was not dispatched on the main thread!"); - - iterations++; -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNSelfInvalidatingObjectTests.m b/Frameworks/NNKit/NNKitTests/NNSelfInvalidatingObjectTests.m deleted file mode 100644 index f4fd86d4..00000000 --- a/Frameworks/NNKit/NNKitTests/NNSelfInvalidatingObjectTests.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// NNSelfInvalidatingObjectTests.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import - - -// Fuckin' state, man. -static BOOL objectInvalidated; -static BOOL objectDestroyed; - - -@interface NNSelfInvalidatingObjectTests : XCTestCase -// The XCT macros don't work outside of an XCTestCase object, so events have to be forwarded back to the test case. -- (void)invalidated:(id)obj; -- (void)destroyed:(id)obj; -@end - - -@interface NNInvalidatingTestObject : NNSelfInvalidatingObject - -@property (assign, readonly) NNSelfInvalidatingObjectTests *test; - -@end - - -@implementation NNInvalidatingTestObject - -- (instancetype)initWithTestObject:(NNSelfInvalidatingObjectTests *)obj; -{ - if (!(self = [super init])) { return nil; } - - _test = obj; - - return self; -} - -- (void)invalidate; -{ - [self.test invalidated:self]; - - [super invalidate]; -} -- (void)dealloc; -{ - [_test destroyed:self]; - - [super dealloc]; -} -@end - - -@implementation NNSelfInvalidatingObjectTests - -- (void)setUp -{ - [super setUp]; - - objectInvalidated = NO; - objectDestroyed = NO; -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)invalidated:(id)obj; -{ - XCTAssertTrue([[NSThread currentThread] isMainThread], @"Invalidation happened on a queue other than the main queue!"); - XCTAssertFalse(objectInvalidated, @"Object was invalidated multiple times!"); - objectInvalidated = YES; -} - -- (void)destroyed:(id)obj; -{ - XCTAssertTrue(objectInvalidated, @"Object was destroyed before it was invalidated!"); - objectDestroyed = YES; -} - -- (void)testDeallocInvalidation -{ - NNInvalidatingTestObject *obj = [[NNInvalidatingTestObject alloc] initWithTestObject:self]; - - XCTAssertFalse(objectInvalidated, @"Object was still valid before it was released"); - - [obj release]; - obj = nil; - - while (!objectDestroyed) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } - - XCTAssertTrue(objectInvalidated, @"Object was not invalidated before it was destroyed"); -} - -- (void)testManualInvalidation -{ - NNInvalidatingTestObject *obj = [[NNInvalidatingTestObject alloc] initWithTestObject:self]; - - XCTAssertFalse(objectInvalidated, @"Object was still valid before it was released"); - - // Ensure the autorelease pool gets drained within the scope of this test. - @autoreleasepool { - [obj invalidate]; - } - - XCTAssertTrue(objectInvalidated, @"Object was manually invalidated before it was destroyed"); - - [obj release]; - obj = nil; - - while (!objectDestroyed) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - } - - XCTAssertTrue(objectInvalidated, @"Object was not invalidated before it was destroyed"); -} - -- (void)testManualDealloc -{ - XCTAssertThrows([[NNSelfInvalidatingObject new] dealloc], @"Invalidating objects are not supposed to accept -dealloc quietly"); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNServiceTests.m b/Frameworks/NNKit/NNKitTests/NNServiceTests.m deleted file mode 100644 index b35fa1df..00000000 --- a/Frameworks/NNKit/NNKitTests/NNServiceTests.m +++ /dev/null @@ -1,307 +0,0 @@ -// -// NNServiceTests.m -// NNKit -// -// Created by Scott Perry on 10/18/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNTestCase.h" - -#import - -#import "NNServiceManager.h" -#import "NNService+Protected.h" - - -/* - C -> A - C -> B - B -> A - - Start order: A, B, C - Stop order: C, B, A - */ - -static BOOL serviceARunning = NO; -static BOOL serviceBRunning = NO; -static BOOL serviceCRunning = NO; - -@interface TestServiceA : NNService @end -@implementation TestServiceA -+ (NNServiceType)serviceType { return NNServiceTypeOnDemand; } -- (void)startService { - [super startService]; - NSAssert(!serviceARunning, @""); - serviceARunning = YES; -} -- (void)stopService { - NSAssert(!serviceCRunning, @""); - NSAssert(!serviceBRunning, @""); - NSAssert(serviceARunning, @""); - serviceARunning = NO; - [super stopService]; -} -@end - -@interface TestServiceB : NNService @end -@implementation TestServiceB -+ (NSSet *)dependencies { return [NSSet setWithObject:[TestServiceA self]]; } -- (void)startService { - [super startService]; - NSAssert(serviceARunning, @""); - NSAssert(!serviceBRunning, @""); - NSAssert(!serviceCRunning, @""); - serviceBRunning = YES; -} -- (void)stopService { - NSAssert(serviceBRunning, @""); - serviceBRunning = NO; - [super stopService]; -} -@end - -@interface TestServiceC : NNService @end -@implementation TestServiceC -+ (NNServiceType)serviceType { return NNServiceTypePersistent; } -+ (NSSet *)dependencies { return [NSSet setWithArray:@[[TestServiceA self], [TestServiceB self]]]; } -- (void)startService { - [super startService]; - NSAssert(serviceARunning, @""); - NSAssert(serviceBRunning, @""); - NSAssert(!serviceCRunning, @""); - serviceCRunning = YES; -} -- (void)stopService { - NSAssert(serviceCRunning, @""); - serviceCRunning = NO; - [super stopService]; -} -@end - -// Service D has no dependencies, is always running -static BOOL serviceDRunning = NO; -@interface TestServiceD : NNService @end -@implementation TestServiceD -+ (NNServiceType)serviceType { return NNServiceTypePersistent; } -- (void)startService { [super startService]; NSAssert(!serviceDRunning, @""); serviceDRunning = YES; } -- (void)stopService { NSAssert(serviceDRunning, @""); serviceDRunning = NO; [super stopService];} -@end - -// Service E depends on Service D, runs on demand -static BOOL serviceERunning = NO; -@protocol TestServiceEProtocol -- (void)foo:(id)sender; -@end -@interface TestServiceE : NNService @end -@implementation TestServiceE -+ (NNServiceType)serviceType { return NNServiceTypeOnDemand; } -+ (NSSet *)dependencies { return [NSSet setWithObject:[TestServiceD self]]; } -+ (Protocol *)subscriberProtocol { return @protocol(TestServiceEProtocol); } -- (void)startService { - [super startService]; - serviceERunning = YES; - [self sendMessage]; -} -- (void)sendMessage; -{ - [(id)self.subscriberDispatcher foo:self]; -} -- (void)stopService { - serviceERunning = NO; - [super stopService]; -} -@end - - -unsigned eventsDispatched; - - -@interface TestServiceESubscriber : NSObject -@end -@implementation TestServiceESubscriber -- (void)foo:(id)sender; -{ - eventsDispatched++; -} -@end - - -@interface NNServiceTests : NNTestCase - -@property (nonatomic, readwrite, assign) size_t memoryUsageInBytes; - -@end - -@implementation NNServiceTests - -- (void)setUp; -{ - [super setUp]; - - eventsDispatched = 0; -} - -- (void)tearDown; -{ - // Give cleanup tasks scheduled on the main queue an opportunity to run. - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; -} - -- (void)testBasic -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceD self]]; - XCTAssertTrue(serviceDRunning, @""); - manager = nil; - XCTAssertFalse(serviceDRunning, @""); -} - -- (void)testCustodyDispute -{ - NNServiceManager *manager1 = [NNServiceManager new]; - NNServiceManager *manager2 = [NNServiceManager new]; - [manager1 registerService:[TestServiceD self]]; - // Manager 2 can't claim manager 1's service - XCTAssertThrows([manager2 registerService:[TestServiceD self]], @""); - // But manager 1's registration is idempotent - XCTAssertNoThrow([manager1 registerService:[TestServiceD self]], @""); -} - -- (void)testDependencies -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceA self]]; - [manager registerService:[TestServiceB self]]; - XCTAssertFalse(serviceARunning, @""); - XCTAssertFalse(serviceBRunning, @""); - [manager addSubscriber:self forService:[TestServiceA self]]; - XCTAssertTrue(serviceARunning, @""); - XCTAssertTrue(serviceBRunning, @""); - [manager addSubscriber:self forService:[TestServiceA self]]; - XCTAssertTrue(serviceARunning, @""); - XCTAssertTrue(serviceBRunning, @""); - [manager removeSubscriber:self forService:[TestServiceA self]]; - XCTAssertFalse(serviceARunning, @""); - XCTAssertFalse(serviceBRunning, @""); - [manager addSubscriber:self forService:[TestServiceA self]]; - XCTAssertTrue(serviceARunning, @""); - XCTAssertTrue(serviceBRunning, @""); - [manager registerService:[TestServiceC self]]; - XCTAssertTrue(serviceCRunning, @""); - [manager removeSubscriber:self forService:[TestServiceA self]]; - XCTAssertFalse(serviceARunning, @""); - XCTAssertFalse(serviceBRunning, @""); - XCTAssertFalse(serviceCRunning, @""); - [manager addSubscriber:self forService:[TestServiceA self]]; - XCTAssertTrue(serviceARunning, @""); - XCTAssertTrue(serviceBRunning, @""); - XCTAssertTrue(serviceCRunning, @""); - manager = nil; - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertFalse(serviceARunning, @""); - XCTAssertFalse(serviceBRunning, @""); - XCTAssertFalse(serviceCRunning, @""); -} - -- (void)testWeakSubscribers -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceA self]]; - XCTAssertFalse(serviceARunning, @""); - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - [manager addSubscriber:foo forService:[TestServiceA self]]; - XCTAssertTrue(serviceARunning, @""); - } - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertFalse(serviceARunning, @""); -} - -- (void)testSubscriberDispatch -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceD self]]; - [manager registerService:[TestServiceE self]]; - XCTAssertFalse(serviceERunning, @""); - @autoreleasepool { - __attribute__((objc_precise_lifetime)) TestServiceESubscriber *foo = [TestServiceESubscriber new]; - [manager addSubscriber:foo forService:[TestServiceE self]]; - XCTAssertTrue(serviceERunning, @""); - XCTAssertEqual(eventsDispatched, (unsigned)1, @""); - [(TestServiceE *)[manager instanceForService:[TestServiceE self]] sendMessage]; - XCTAssertEqual(eventsDispatched, (unsigned)2, @""); - } - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertFalse(serviceERunning, @""); -} - -- (void)testSubscriberDispatchWhenServiceStopped -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceE self]]; - XCTAssertFalse(serviceERunning, @""); - @autoreleasepool { - __attribute__((objc_precise_lifetime)) TestServiceESubscriber *foo = [TestServiceESubscriber new]; - [manager addSubscriber:foo forService:[TestServiceE self]]; - XCTAssertFalse(serviceERunning, @""); - [(TestServiceE *)[manager instanceForService:[TestServiceE self]] sendMessage]; - XCTAssertEqual(eventsDispatched, (unsigned)0, @""); - } - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertFalse(serviceERunning, @""); -} - -- (void)testMemoryLeaks; -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceD self]]; - [manager registerService:[TestServiceE self]]; - XCTAssertFalse(serviceERunning, @""); - __attribute__((objc_precise_lifetime)) TestServiceESubscriber *foo = [TestServiceESubscriber new]; - - [self testForMemoryLeaksWithBlock:^{ - [manager addSubscriber:foo forService:[TestServiceE self]]; - [manager removeSubscriber:foo forService:[TestServiceE self]]; - } iterations:5e4]; -} - -- (void)testSubscribingToMultipleServices; -{ - NNServiceManager *manager = [NNServiceManager new]; - [manager registerService:[TestServiceA self]]; - XCTAssertFalse(serviceARunning, @""); - - [manager registerService:[TestServiceD self]]; - [manager registerService:[TestServiceE self]]; - XCTAssertFalse(serviceERunning, @""); - - @autoreleasepool { - __attribute__((objc_precise_lifetime)) TestServiceESubscriber *foo = [TestServiceESubscriber new]; - [manager addSubscriber:foo forService:[TestServiceA self]]; - [manager addSubscriber:foo forService:[TestServiceA self]]; - [manager addSubscriber:foo forService:[TestServiceE self]]; - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertTrue(serviceARunning, @""); - XCTAssertTrue(serviceERunning, @""); - } - (void)[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]]; - XCTAssertFalse(serviceARunning, @""); - XCTAssertFalse(serviceERunning, @""); -} - -// This test must run last. -- (void)testZSharedManager; -{ - NNServiceManager *manager = [NNServiceManager sharedManager]; - [manager registerService:[TestServiceA self]]; - NSLog(@"%@", manager); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNStrongifiedPropertiesTests.m b/Frameworks/NNKit/NNKitTests/NNStrongifiedPropertiesTests.m deleted file mode 100644 index 633ed7ac..00000000 --- a/Frameworks/NNKit/NNKitTests/NNStrongifiedPropertiesTests.m +++ /dev/null @@ -1,174 +0,0 @@ -// -// NNStrongifiedPropertiesTests.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import - - -@interface NNStrongifiedPropertiesTests : XCTestCase - -@end - - - -@interface NNStrongifyTestClass : NNStrongifiedProperties - -@property (weak) id foo; -@property id bar; -@property (weak) id TLA; -@property (weak) id qux; -@property (weak) id Qux; - -@end -@interface NNStrongifyTestClass (NNStrongGetters) - -- (id)strongFoo; // Good -- (id)strongBar; // Bad -- strong -- (id)strongTLA; // Good -- (id)strongQux; // Bad -- ambiguous - -@end -@implementation NNStrongifyTestClass -@end - - - -@interface NNSwizzledStrongifierTestClass : NSObject - -@property (weak) id foo; -@property id bar; -@property (weak) id TLA; -@property (weak) id qux; -@property (weak) id Qux; - -@end -@interface NNSwizzledStrongifierTestClass (NNStrongGetters) - -- (id)strongFoo; // Good -- (id)strongBar; // Bad -- strong -- (id)strongTLA; // Good -- (id)strongQux; // Bad -- ambiguous - -@end -@implementation NNSwizzledStrongifierTestClass - - -@end - - - -@implementation NNStrongifiedPropertiesTests - -- (void)setUp -{ - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testBasicStrongGetter -{ - NNStrongifyTestClass *obj = [NNStrongifyTestClass new]; - id boo = [NSObject new]; - obj.foo = boo; - XCTAssertEqual([obj strongFoo], boo, @"Basic weak property did not resolve a strong getter."); - [boo self]; -} - -- (void)testCapitalizedStrongGetter -{ - NNStrongifyTestClass *obj = [NNStrongifyTestClass new]; - id boo = [NSObject new]; - obj.TLA = boo; - XCTAssertEqual([obj strongTLA], boo, @"Capitalized weak property did not resolve a strong getter."); - [boo self]; -} - -- (void)testStrongGetterWithStrongProperty -{ - XCTAssertThrows([[NNStrongifyTestClass new] strongBar], @"Strongified strong property access resulted in a valid IMP."); -} - -- (void)testAmbiguousStrongGetter -{ - XCTAssertThrows([[NNStrongifyTestClass new] strongQux], @"Ambiguous property access found an IMP."); -} - -- (void)testNilling -{ - NNStrongifyTestClass *obj = [NNStrongifyTestClass new]; - @autoreleasepool { - id boo = [NSObject new]; - obj.foo = boo; - [boo self]; - } - XCTAssertNil([obj strongFoo], @"Weak property did not nil as expected."); -} - -- (void)testSwizzledBasicStrongGetter -{ - NNSwizzledStrongifierTestClass *obj = [NNSwizzledStrongifierTestClass new]; - BOOL swizzled = nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - XCTAssertTrue(swizzled); - id boo = [NSObject new]; - obj.foo = boo; - XCTAssertEqual([obj strongFoo], boo, @"Basic weak property did not resolve a strong getter."); - [boo self]; -} - -- (void)testSwizzledCapitalizedStrongGetter -{ - NNSwizzledStrongifierTestClass *obj = [NNSwizzledStrongifierTestClass new]; - BOOL swizzled = nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - XCTAssertTrue(swizzled); - id boo = [NSObject new]; - obj.TLA = boo; - XCTAssertEqual([obj strongTLA], boo, @"Capitalized weak property did not resolve a strong getter."); - [boo self]; -} - -- (void)testSwizzledStrongGetterWithStrongProperty -{ - NNSwizzledStrongifierTestClass *obj = [NNSwizzledStrongifierTestClass new]; - nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - XCTAssertThrows([obj strongBar], @"Strongified strong property access resulted in a valid IMP."); -} - -- (void)testSwizzledAmbiguousStrongGetter -{ - NNSwizzledStrongifierTestClass *obj = [NNSwizzledStrongifierTestClass new]; - nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - XCTAssertThrows([obj strongQux], @"Ambiguous property access resulted in a single IMP."); -} - -- (void)testSwizzledNilling -{ - NNSwizzledStrongifierTestClass *obj = [NNSwizzledStrongifierTestClass new]; - BOOL swizzled = nn_object_swizzleIsa(obj, [NNStrongifiedProperties class]); - XCTAssertTrue(swizzled); - @autoreleasepool { - id boo = [NSObject new]; - obj.foo = boo; - [boo self]; - } - XCTAssertNil([obj strongFoo], @"Weak property did not nil as expected."); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNSynthesizedObjectStorageTests.m b/Frameworks/NNKit/NNKitTests/NNSynthesizedObjectStorageTests.m deleted file mode 100644 index 22e1306e..00000000 --- a/Frameworks/NNKit/NNKitTests/NNSynthesizedObjectStorageTests.m +++ /dev/null @@ -1,113 +0,0 @@ -// -// NNSynthesizedObjectStorageTests.m -// NNKit -// -// Created by Scott Perry on 06/23/15. -// Copyright © 2015 Scott Perry. All rights reserved. -// - -#import - -#import "runtime.h" - -#import "memoize.h" - - -@interface NNSynthesizedObjectStorageTestObject : NSObject - -@property (nonatomic, strong) NSNumber *number; - -@end - - -@implementation NNSynthesizedObjectStorageTestObject - -- (instancetype)init { - if (!(self = [super init])) { return nil; } - _number = @(arc4random()); - return self; -} - -- (nonnull id)copyWithZone:(nullable NSZone *)zone { - NNSynthesizedObjectStorageTestObject *result = [NNSynthesizedObjectStorageTestObject new]; - result.number = self.number; - return result; -} - -- (BOOL)isEqual:(id)object { - return ([object isKindOfClass:[self class]] && [[object number] isEqualToNumber:self.number]); -} - -@end - - -@interface NNSynthesizedObjectStorageTests : XCTestCase -@end - - -@interface NNSynthesizedObjectStorageTests (NNSynthesizedProperties) - -@property (nonatomic, strong) NNSynthesizedObjectStorageTestObject *prop_ns; -@property (nonatomic, assign) NNSynthesizedObjectStorageTestObject *prop_na; -@property (nonatomic, weak) NNSynthesizedObjectStorageTestObject *prop_nw; -@property (nonatomic, copy) NNSynthesizedObjectStorageTestObject *prop_nc; - -@end - - -@implementation NNSynthesizedObjectStorageTests (NNSynthesizedProperties) - -NNSynthesizeObjectStorage(id, prop_ns, prop_ns, setProp_ns:) -NNSynthesizeObjectStorage(id, prop_na, prop_na, setProp_na:) -NNSynthesizeObjectStorage(id, prop_nw, prop_nw, setProp_nw:) -NNSynthesizeObjectStorage(id, prop_nc, prop_nc, setProp_nc:) - -@end - - -@implementation NNSynthesizedObjectStorageTests - -- (void)testTestObjects { - XCTAssertNotNil([[NNSynthesizedObjectStorageTestObject new] number]); -} - -- (void)testStrong { - @autoreleasepool { - id obj = [NNSynthesizedObjectStorageTestObject new]; - self.prop_ns = obj; - XCTAssertEqual(self.prop_ns, obj); - } - XCTAssertNotNil(self.prop_ns); - XCTAssertNotNil([self.prop_ns number]); -} - -- (void)testAssign { - @autoreleasepool { - id obj = [NNSynthesizedObjectStorageTestObject new]; - self.prop_na = obj; - XCTAssertEqual(self.prop_na, obj); - } -} - -#warning This is not implemented yet. -//- (void)testWeak { -// @autoreleasepool { -// id obj = [NNSynthesizedObjectStorageTestObject new]; -// self.prop_nw = obj; -// XCTAssertEqual(self.prop_nw, obj); -// } -// XCTAssertNil(self.prop_nw); -//} - -- (void)testCopy { - @autoreleasepool { - id obj = [NNSynthesizedObjectStorageTestObject new]; - self.prop_nc = obj; - XCTAssertEqualObjects(self.prop_nc, obj); - XCTAssertNotEqual(self.prop_nc, obj); - } - XCTAssertNotNil(self.prop_nc); - XCTAssertNotNil([self.prop_nc number]); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNTestCase.h b/Frameworks/NNKit/NNKitTests/NNTestCase.h deleted file mode 100644 index 4f409a6f..00000000 --- a/Frameworks/NNKit/NNKitTests/NNTestCase.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NNTestCase.h -// NNKit -// -// Created by Scott Perry on 11/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -@interface NNTestCase : XCTestCase - -- (BOOL)testForMemoryLeaksWithBlock:(void (^)())block iterations:(size_t)iterations; - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNTestCase.m b/Frameworks/NNKit/NNKitTests/NNTestCase.m deleted file mode 100644 index e4f89440..00000000 --- a/Frameworks/NNKit/NNKitTests/NNTestCase.m +++ /dev/null @@ -1,63 +0,0 @@ -// -// NNTestCase.m -// NNKit -// -// Created by Scott Perry on 11/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNTestCase.h" - -#import - - -static size_t report_memory(void) { - struct task_basic_info info; - mach_msg_type_number_t size = sizeof(info); - kern_return_t kerr = task_info(mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&info, - &size); - if( kerr != KERN_SUCCESS ) { - @throw [NSException exceptionWithName:@"wtf" reason:[NSString stringWithFormat:@"Error with task_info(): %s", mach_error_string(kerr)] userInfo:nil]; - } - return info.resident_size; -} - - -@interface NNTestCase () - -@end - - -@implementation NNTestCase - -- (BOOL)testForMemoryLeaksWithBlock:(void (^)())block iterations:(size_t)iterations; -{ - XCTAssertTrue(iterations > 4096, @"Memory leak tests are not accurate with iteration counts less than 4096!"); - - // Three bytes per iteration is allowed to leak because that's basically impossible. - size_t bytes_expected = iterations * 3; - size_t memory_usage_at_start = report_memory(); - - while (--iterations != 0) { - @autoreleasepool { - block(); - } - } - - size_t bytes_actual = report_memory() - memory_usage_at_start; - BOOL memory_usage_is_good = bytes_actual < bytes_expected; - NSLog(@"Memory usage increased by %zu bytes by end of test", bytes_actual); - XCTAssertTrue(memory_usage_is_good, @"Memory usage increased by %zu bytes by end of test (expected < %zu)", bytes_actual, bytes_expected); - - return memory_usage_is_good; -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNWeakObserverTests.m b/Frameworks/NNKit/NNKitTests/NNWeakObserverTests.m deleted file mode 100644 index f2ba90ad..00000000 --- a/Frameworks/NNKit/NNKitTests/NNWeakObserverTests.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// NNWeakObserverTests.m -// NNKit -// -// Created by Scott Perry on 11/14/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import - - -static NSString *notificationName = @"somenotification"; -unsigned receivedNotifications; - - -@interface _NNWeakObserverTestObserver : NSObject -@end -@implementation _NNWeakObserverTestObserver -- (void)notify:(NSNotification *)note; -{ - receivedNotifications++; -} -- (void)dealloc; -{ - NSLog(@"Destroyed object!"); -} -@end - - -@interface NNWeakObserverTests : XCTestCase - -@end - - -@implementation NNWeakObserverTests - -- (void)setUp -{ - [super setUp]; - - receivedNotifications = 0; -} - -- (void)testWeakObservers -{ - @autoreleasepool { - __attribute__((objc_precise_lifetime)) _NNWeakObserverTestObserver *observer = [_NNWeakObserverTestObserver new]; - [[NSNotificationCenter defaultCenter] addWeakObserver:observer selector:@selector(notify:) name:notificationName object:nil]; - [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self]; - XCTAssertEqual(receivedNotifications, (unsigned)1, @""); - } - XCTAssertNoThrow([[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self], @""); - XCTAssertEqual(receivedNotifications, (unsigned)1, @""); -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/NNWeakSetTests.m b/Frameworks/NNKit/NNKitTests/NNWeakSetTests.m deleted file mode 100644 index f5a5ba66..00000000 --- a/Frameworks/NNKit/NNKitTests/NNWeakSetTests.m +++ /dev/null @@ -1,114 +0,0 @@ -// -// NNWeakSetTests.m -// NNKit -// -// Created by Scott Perry on 11/18/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNTestCase.h" - -#import - - -@interface NNWeakSetTests : NNTestCase - -@end - - -@implementation NNWeakSetTests - -- (void)testAddObject; -{ - NNWeakSet *set = [NNWeakSet new]; - __attribute__((objc_precise_lifetime)) id bar = [NSObject new]; - [set addObject:bar]; - XCTAssertEqual(set.count, (NSUInteger)1, @""); -} - -- (void)testMemberExists; -{ - NNWeakSet *set = [NNWeakSet new]; - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - - [set addObject:foo]; - - XCTAssertEqual(set.count, (NSUInteger)1, @""); - XCTAssertEqualObjects([set member:foo], foo, @""); - XCTAssertNil([set member:[NSObject new]], @""); -} - -- (void)testMemberDoesNotExist; -{ - NNWeakSet *set = [NNWeakSet new]; - - XCTAssertNil([set member:[NSObject new]], @""); - - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - [set addObject:foo]; - XCTAssertNil([set member:[NSObject new]], @""); -} - -- (void)testRemoveObject; -{ - NNWeakSet *set = [NNWeakSet new]; - __attribute__((objc_precise_lifetime)) id bar = [NSObject new]; - - [set addObject:bar]; - XCTAssertEqual(set.count, (NSUInteger)1, @""); - [set removeObject:bar]; - XCTAssertEqual(set.count, (NSUInteger)0, @""); -} - -- (void)testWeakRemoval; -{ - NNWeakSet *set = [NNWeakSet new]; - - @autoreleasepool { - @autoreleasepool { - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - [set addObject:foo]; - XCTAssertEqual(set.count, (NSUInteger)1, @""); - } - - [set addObject:[NSObject new]]; - [set addObject:[NSObject new]]; - } - - XCTAssertEqual(set.count, (NSUInteger)0, @""); -} - -- (void)testEnumeration; -{ - NSUInteger enumCount = 0; - NNWeakSet *set = [NNWeakSet new]; - __attribute__((objc_precise_lifetime)) id foo = [NSObject new]; - [set addObject:foo]; - - for (NSObject *obj in set) { - enumCount++; - XCTAssertEqualObjects(obj, foo, @""); - } - - XCTAssertEqual(enumCount, set.count, @""); - XCTAssertEqual(set.count, (NSUInteger)1, @""); -} - -- (void)testMemoryLeaks; -{ - NNWeakSet *set = [NNWeakSet new]; - __attribute__((objc_precise_lifetime)) id bar = [NSObject new]; - - [self testForMemoryLeaksWithBlock:^{ - [set addObject:bar]; - [set removeObject:bar]; - } iterations:1e4]; -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/nn_autofreeTests.m b/Frameworks/NNKit/NNKitTests/nn_autofreeTests.m deleted file mode 100644 index bf7b9ed2..00000000 --- a/Frameworks/NNKit/NNKitTests/nn_autofreeTests.m +++ /dev/null @@ -1,46 +0,0 @@ -// -// nn_autofreeTests.m -// NNKit -// -// Created by Scott Perry on 11/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NNTestCase.h" - -#import - - -@interface nn_autofreeTests : NNTestCase - -@end - - -@implementation nn_autofreeTests - -- (void)setUp -{ - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -- (void)testExample -{ - [self testForMemoryLeaksWithBlock:^{ - nn_autofree(malloc(2)); - } iterations:1e5]; -} - -@end diff --git a/Frameworks/NNKit/NNKitTests/nn_isaSwizzlingTests.m b/Frameworks/NNKit/NNKitTests/nn_isaSwizzlingTests.m deleted file mode 100644 index 596f82a1..00000000 --- a/Frameworks/NNKit/NNKitTests/nn_isaSwizzlingTests.m +++ /dev/null @@ -1,201 +0,0 @@ -// -// nn_isaSwizzlingTests.m -// NNKit -// -// Created by Scott Perry on 09/05/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -#import -#import - -// Class ISAGood can be used for swizzling any NSObject -@protocol ISAGood - (void)foo; @end -@interface ISAGood : NSObject @end -@implementation ISAGood - (void)foo { NSLog(@"foooooo! "); } - (void)doesNotRecognizeSelector:(__attribute__((unused)) SEL)aSelector { NSLog(@"FAUX NOES!"); } @end - - -// Class ISANoSharedAncestor can only be used to swizzle instances that areKindOf NSArray -@protocol ISANoSharedAncestor - (void)foo; @end -@interface ISANoSharedAncestor : NSArray @end -@implementation ISANoSharedAncestor - (void)foo { NSLog(@"foooooo! "); } @end - - -// Class ISANoProtocol doesn't have a corersponding protocol and cannot be used for swizzling -@interface ISANoProtocol : NSObject @end -@implementation ISANoProtocol - (void)foo { NSLog(@"foooooo! "); } @end - - -// Class ISAAddsProperties adds properties to its superclass and thus cannot be used for swizzling -@protocol ISAAddsProperties - (void)foo; @end -@interface ISAAddsProperties : NSObject @property (nonatomic, assign) NSUInteger bar; @end -@implementation ISAAddsProperties - (void)foo { NSLog(@"foooooo! "); } @end - - -// Class ISAAddsProperties adds legal properties to its superclass -@protocol ISAAddsLegalProperties @end -@interface ISAAddsLegalProperties : NSObject @property (nonatomic, readonly, assign) NSUInteger bar; @end -@implementation ISAAddsLegalProperties @dynamic bar; - (NSUInteger)bar { NSLog(@"foooooo! "); return 7; } @end - - -// Class ISAAddsIvars adds ivars to its superclass and thus cannot be used for swizzling -@protocol ISAAddsIvars - (void)foo; @end -@interface ISAAddsIvars : NSObject { NSUInteger bar; } @end -@implementation ISAAddsIvars - (void)foo { NSLog(@"foooooo! "); } @end - - -// Class ISAExtraProtocol adds an extra protocol that the swizzled object must conform to. -@protocol ISAExtraProtocol - (void)foo; @end -@interface ISAExtraProtocol : NSObject @end -@implementation ISAExtraProtocol - (void)foo { NSLog(@"foooooo! "); } @end - -// Class ISANameConflicts can be used for swizzling any NSObject and provides a class and instance method with the same selector -@protocol ISANameConflicts - (BOOL)isClassMethod; + (BOOL)isClassMethod; @end -@interface ISANameConflicts : NSObject @end -@implementation ISANameConflicts - (BOOL)isClassMethod { return NO; } + (BOOL)isClassMethod { return YES; } @end - -@interface nn_isaSwizzlingTests : XCTestCase - -@end - -@implementation nn_isaSwizzlingTests - -- (void)testInteractionWithKVO; -{ - #pragma message "Not even sure how to do this without making the world's biggest mess. There's no reason why it shouldn't work, but it's not tested." -} - -- (void)testExtraProtocol; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertFalse([bar conformsToProtocol:@protocol(ISAExtraProtocol)], @"Object is not virgin"); - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAExtraProtocol class]), @"Failed to swizzle object"); - - XCTAssertTrue([bar conformsToProtocol:@protocol(ISAExtraProtocol)], @"Object is not swizzled correctly"); - XCTAssertTrue([bar conformsToProtocol:@protocol(NSCacheDelegate)], @"Object is missing extra protocol"); -} - -- (void)testAddsProperties; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertFalse(nn_object_swizzleIsa(bar, [ISAAddsProperties class]), @"Failed to fail to swizzle object"); -} - -- (void)testAddsLegalProperties; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAAddsLegalProperties class]), @"Failed to swizzle object"); - XCTAssertEqual(((ISAAddsLegalProperties *)bar).bar, (NSUInteger)7, @"Oops properties"); -} - -- (void)testAddsIvars; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertFalse(nn_object_swizzleIsa(bar, [ISAAddsIvars class]), @"Failed to fail to swizzle object"); -} - -- (void)testDoubleSwizzle; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertFalse([bar conformsToProtocol:@protocol(ISAGood)], @"Object is not virgin"); - XCTAssertFalse([bar respondsToSelector:@selector(foo)], @"Object is not virgin"); - - XCTAssertThrows([(id)bar foo], @"foooooo!"); - XCTAssertThrows([bar doesNotRecognizeSelector:nil], @"FAUX NOES!"); - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAGood class]), @"Failed to swizzle object"); - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAGood class]), @"Failed to swizzle object"); - - XCTAssertTrue([bar conformsToProtocol:@protocol(ISAGood)], @"Object is not swizzled correctly"); - - XCTAssertTrue([bar respondsToSelector:@selector(foo)], @"Object is not swizzled correctly"); - - XCTAssertNoThrow([(id)bar foo], @"foooooo!"); - XCTAssertNoThrow([bar doesNotRecognizeSelector:nil], @"FAUX NOES!"); - - XCTAssertEqual([bar class], [NSObject class], @"Object should report itself as still being an NSObject"); -} - -- (void)testSharedAncestor; -{ - NSObject *bar = [[NSObject alloc] init]; - NSArray *arr = [[NSArray alloc] init]; - - XCTAssertFalse(nn_object_swizzleIsa(bar, [ISANoSharedAncestor class]), @"Failed to fail to swizzle object"); - XCTAssertTrue(nn_object_swizzleIsa(arr, [ISANoSharedAncestor class]), @"Failed to swizzle object"); -} - -- (void)testNoProto; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISANoProtocol class]), @"Failed to swizzle object"); -} - -- (void)testImplementationDetails; -{ - NSObject *bar = [[NSObject alloc] init]; - -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wundeclared-selector" - - XCTAssertFalse([bar respondsToSelector:@selector(actualClass)], @"Object is not virgin"); - XCTAssertThrows([bar performSelector:@selector(actualClass)], @"actualClass exists?"); - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAGood class]), @"Failed to swizzle object"); - - XCTAssertTrue([bar respondsToSelector:@selector(_swizzler_actualClass)], @"Object is not swizzled correctly"); - XCTAssertNoThrow([bar performSelector:@selector(_swizzler_actualClass)], @"Internal swizzle method actualClass not implemented?"); - -# pragma clang diagnostic pop - -} - -- (void)testGood; -{ - NSObject *bar = [[NSObject alloc] init]; - - XCTAssertFalse([bar conformsToProtocol:@protocol(ISAGood)], @"Object is not virgin"); - XCTAssertFalse([bar respondsToSelector:@selector(foo)], @"Object is not virgin"); - - XCTAssertThrows([(id)bar foo], @"foooooo!"); - XCTAssertThrows([bar doesNotRecognizeSelector:nil], @"FAUX NOES!"); - - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISAGood class]), @"Failed to swizzle object"); - - XCTAssertTrue([bar conformsToProtocol:@protocol(ISAGood)], @"Object is not swizzled correctly"); - XCTAssertTrue([bar isKindOfClass:[ISAGood class]], @"Object is not swizzled correctly"); - - XCTAssertTrue([bar respondsToSelector:@selector(foo)], @"Object is not swizzled correctly"); - - XCTAssertNoThrow([(id)bar foo], @"foooooo!"); - XCTAssertNoThrow([bar doesNotRecognizeSelector:nil], @"FAUX NOES!"); - - XCTAssertEqual([bar class], [NSObject class], @"Object should report itself as still being an NSObject"); -} - -- (void)testSelectorNameConflicts; -{ - NSObject *bar = [[NSObject alloc] init]; - XCTAssertTrue(nn_object_swizzleIsa(bar, [ISANameConflicts class]), @"Failed to swizzle object"); - - XCTAssertFalse([(id)bar isClassMethod], @"Instance method was swizzled with the class method"); - XCTAssertTrue([object_getClass(bar) isClassMethod], @"Class method was swizzled with the instance method"); - XCTAssertEqual([bar class], [NSObject class], @"Object should report itself as still being an NSObject"); -} - -@end diff --git a/Frameworks/NNKit/README.md b/Frameworks/NNKit/README.md deleted file mode 100644 index fa2cbdd0..00000000 --- a/Frameworks/NNKit/README.md +++ /dev/null @@ -1,8 +0,0 @@ -NNKit -===== - -This library represents a collection of well-tested, robust, occasionally idiotic, but always well-meaning widgets for the Objective-C developer. - -At the moment it is a poorly-documented collection of code samples for my [NSMeetup talk](http://www.meetup.com/nsmeetup/events/136648202/) as well as a shared repository of solutions to common problems for [Switch](https://github.com/numist/Switch) and tmbo, the latter being a sort of promise to keep on adding, testing, and documenting the framework. - -The framework is broken up by purpose, the most entertaining of which are likely [Swizzling](https://github.com/numist/NNKit/tree/master/NNKit/Swizzling) and [Hacks](https://github.com/numist/NNKit/tree/master/NNKit/Hacks). diff --git a/Frameworks/ReactiveCocoa b/Frameworks/ReactiveCocoa deleted file mode 160000 index 0e497efe..00000000 --- a/Frameworks/ReactiveCocoa +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0e497efe6328d34d67fa799c4e79da804cfd7ba0 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..eb8311e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +With the eception of software in Dependencies/—which may use differing, but still permissive, licenses—the software in this repository is released under the terms of the MIT license: + +Copyright 2013-2020 Scott Perry + +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. diff --git a/Podfile b/Podfile deleted file mode 100644 index 0bc1128f..00000000 --- a/Podfile +++ /dev/null @@ -1,10 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :osx, '10.10' - -target :Switch do - pod 'MASPreferences' - pod 'Sparkle' - target :SwitchTests do - pod 'OCMock' - end -end diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index 856a706e..00000000 --- a/Podfile.lock +++ /dev/null @@ -1,24 +0,0 @@ -PODS: - - MASPreferences (1.1.4) - - OCMock (3.1.2) - - Sparkle (1.14.0) - -DEPENDENCIES: - - MASPreferences - - OCMock - - Sparkle - -SPEC REPOS: - https://github.com/CocoaPods/Specs.git: - - MASPreferences - - OCMock - - Sparkle - -SPEC CHECKSUMS: - MASPreferences: 7bdcfe891d7840453cf48f95fa866c3e152ace7a - OCMock: a10ea9f0a6e921651f96f78b6faee95ebc813b92 - Sparkle: ccd95233b12a3e3d4eeb55ff01dd4c8bb8188b07 - -PODFILE CHECKSUM: f98179f647f587219a5246f66f8624262c7dd81d - -COCOAPODS: 1.9.3 diff --git a/README.md b/README.md index 215658d7..24d9fd40 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,30 @@ Switch, Window-Based Context Switching ====================================== -Switch is a window-based (as opposed to application-based) context switcher. By default it is bound to using `⌥⇥` and `⌥⇧⇥` to cycle through the visible windows on the current space. When the interface is active, `⌥W` can be used to close windows and `⌥,` used to show a preferences window. The main interface looks like this: +Switch is a window-based (as opposed to application-based) context switcher. By default it is bound to using `⌥⇥` and `⌥⇧⇥` to cycle through the visible windows on the current space. When the interface is active, `⌥W` can be used to close windows and `⌥,` used to show a preferences window. + +The main interface looks like this: ![Interface Screenshot](http://numist.net/random/switch.png) -This project is functional, but not finished. That said, I've been using it every day. +This project is functional, but not finished. That said, I use it every day. [Patches](https://github.com/numist/Switch/pull/new) and [issues](https://github.com/numist/Switch/issues) are welcome. If a window is being improperly shown or omitted from the interface, please include a [snapshot](https://github.com/numist/Switch/wiki/About-Snapshots). -Getting Started ---------------- +I want it! +---------- Download the [latest release](https://github.com/numist/Switch/releases), put the application bundle in your `/Applications` or `~/Applications` directory, and run it! It will check for updates automatically so you can stay up to date as the project develops. -Switch requires Mac OS X version 10.14 or newer and is still in development. For a list of known issues, check out the [bug tag](https://github.com/numist/Switch/issues?labels=bug&state=open) for general badness and the [quirk tag](https://github.com/numist/Switch/issues?labels=quirk&state=open) for windows being improperly shown or omitted from the interface. +Switch requires Mac OS X version 11.0 or newer and is still in development. For a list of known issues, check out the [bug tag](https://github.com/numist/Switch/issues?labels=bug&state=open) for general badness and the [quirk tag](https://github.com/numist/Switch/issues?labels=quirk&state=open) for windows being improperly shown/omitted in the interface. Contributing ------------ -[![Build Status](https://travis-ci.org/numist/Switch.png?branch=develop)](https://travis-ci.org/numist/Switch) - To check out the project, its submodules, and open in Xcode: git clone git://github.com/numist/Switch.git cd Switch/ - rake deps open Switch.xcworkspace -At this point, `⌘R` should have you up and running! - -The release process uses the project `Rakefile`; if you have a Developer ID certificate installed, you should be able to run `rake release` without any issues. Without a Developer ID, the `analyze`, `test`, and `app` targets (and their dependencies) should succeed. - -Thanks! -------- - -Switch relies on a number of external frameworks, either as [submodules](https://github.com/numist/Switch/tree/develop/Frameworks) or [pods](https://github.com/numist/Switch/tree/develop/Podfile). Thanks to the people responsible for them, you've saved me time and tears. +`⌘R` should have you up and running! Any changes require Accessibility re-authorization in the Security & Privacy preferences pane—it's useful to just keep it open when iterating. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 538668dc..00000000 --- a/Rakefile +++ /dev/null @@ -1,296 +0,0 @@ -require 'rake/clean' -require 'rake/packagetask' - -require_relative 'Scripts/console' - -# -# TODO: deliverables should live in INTERMEDIATESDIR until the very last possible moment, when they have been fully verified. -# - -XCODE_BUILD_FILTER = "xcpretty -c" -XCODE_TEST_FILTER = "xcpretty -tc" - -# -# Constants -# - -RELEASE_BRANCHES = ["develop", "master"] - -BUILDDIR = File.absolute_path("Build") - -INTERMEDIATESDIR = "#{BUILDDIR}/Intermediates" - -DERIVEDDATA = "#{INTERMEDIATESDIR}/DerivedData" - -# -# Environmental -# - -# Switch.xcodeproj -PROJECT = FileList['*.xcodeproj'][0] - -# Switch -PRODUCT = PROJECT.slice(0..(PROJECT.index('.') - 1)) - -def git_branch - branch = `git symbolic-ref HEAD 2> /dev/null | sed -e 's|^refs/heads/||'`.strip - if branch == "" - branch = `git show-ref --head -s --abbrev | head -n1`.strip - end - return branch -end - -BRANCH = git_branch - -# -# Synthesized -# - -BRANCH_IS_RELEASE = RELEASE_BRANCHES.include?(BRANCH) - -BRANCH_IS_GM = (BRANCH == "master") - -DELIVERABLE_ARCHIVE = File.absolute_path("#{BUILDDIR}/#{PRODUCT}.xcarchive") - -DELIVERABLE_APP = File.absolute_path("#{BUILDDIR}/#{PRODUCT}.app") - -DELIVERABLE_ZIP = File.absolute_path("#{BUILDDIR}/#{PRODUCT}.zip") - -PODS_PROJECT_FILE = 'Pods/Pods.xcodeproj/project.pbxproj' - -def formatted_fail(message) - fail Console.background_red(Console.white(message)) -end - -def check_ruby_version - ruby_version = RUBY_VERSION.split('.') - required_version = '2.0.0'.split('.') - formatted_fail "#{deliverable_path} wasn't produced!" if ruby_version.count > 3 - (0..(ruby_version.count - 1)).each do |index| - if ruby_version[index] < required_version[index] - formatted_fail "Sorry, this Rakefile requires Ruby #{required_version.join('.')} or newer." - elsif ruby_version[index] > required_version[index] - break - end - end -end -check_ruby_version - -CLOBBER.include(FileList["#{File.expand_path('~')}/Library/Developer/Xcode/DerivedData/#{PRODUCT}-*"]) -CLOBBER.include(DERIVEDDATA) - -CLEAN.include(FileList[BUILDDIR]) - -XCODEFLAGS = [ - "-workspace \"#{PRODUCT}.xcworkspace\"", - "-scheme \"#{PRODUCT}\"", - "-derivedDataPath \"#{DERIVEDDATA}\"", -].join(' ') - -# -# Helpers -# - -def prettify_xcode_task(task, test=false) - unless shell_non_fatal("set -e; set -o pipefail; #{task} | #{test ? XCODE_TEST_FILTER : XCODE_BUILD_FILTER}") - Console.print(Console.background_red(Console.white("Task failed, retrying without filter"))) - shell(task) - end - -end - -def run_task(task, args=nil) - if args.nil? - Rake::Task[task].invoke - else - Rake::Task[task].invoke(args) - end -end - -def verify_codesign(app_path) - shell "codesign --verify --verbose --deep \"#{app_path}\"" - shell "spctl --assess --verbose=4 --type execute \"#{app_path}\"" -end - -def verify_deliverable(deliverable_path) - formatted_fail "#{deliverable_path} wasn't produced!" unless File.exists? deliverable_path -end - -def xcode(action) - run_task DERIVEDDATA - prettify_xcode_task("xcodebuild #{XCODEFLAGS} #{action}") -end - -def shell(action) - Console.puts(Console.bold(Console.black("#{action}"))) - formatted_fail "Shell command failed: #{action}" unless system(action) -end - -def shell_non_fatal(action) - Console.puts(Console.bold(Console.black("#{action}"))) - return system(action) -end - -def echo_step(step) - Console.puts(Console.bold(Console.black(Console.background_white(step)))) -end - -# -# Targets -# - -directory BUILDDIR -directory DERIVEDDATA -directory INTERMEDIATESDIR - -task :default => [:analyze, :test] - -task :pods => [PODS_PROJECT_FILE] -file PODS_PROJECT_FILE => 'Podfile' do - echo_step "Updating Pods" - if not shell_non_fatal "pod install" - shell "pod repo update > /dev/null" - shell "pod install" - end -end - -desc "Install/update dependencies required for building the project." -task :deps do - echo_step "Installing/updating dependencies" - # Rakefile deps - # TODO: check if xcpretty gem is already installed - shell "gem install xcpretty" if not shell_non_fatal "which xcpretty" - - # Submodules - shell "git submodule sync" - shell "git submodule update --init --recursive" - - # Pods - # TODO: check if cocoapods gem is already installed - - shell "gem install cocoapods" if not shell_non_fatal "which pod" - Rake::FileTask[PODS_PROJECT_FILE].invoke -end - -# XXX: can these just be tasks dependant on another task with an argument? -task :analyze do - xcode "analyze" -end - -desc "Build project." -task :build do - xcode "build" -end - -task :clean do - xcode "clean" -end - -desc "Run project unit tests." -task :test => [:build] do - echo_step("Testing #{PRODUCT}") - prettify_xcode_task("xcodebuild -scheme \"#{PRODUCT}\" -workspace \"#{PRODUCT}.xcworkspace\" -derivedDataPath \"#{DERIVEDDATA}\" test", true) -end - - -# :archive "Builds #{PRODUCT}.xcarchive in #{BUILDDIR}" -task :archive => [DELIVERABLE_ARCHIVE] -task DELIVERABLE_ARCHIVE => [File.dirname(DELIVERABLE_ARCHIVE)] do - echo_step("Building archive bundle: #{File.basename(DELIVERABLE_ARCHIVE)}") - - archive_path = DELIVERABLE_ARCHIVE.slice(0..(DELIVERABLE_ARCHIVE.rindex('.') - 1)) - xcode "-archivePath \"#{archive_path}\" archive" - verify_deliverable DELIVERABLE_ARCHIVE - - Console.puts "Finished: #{Console.green(DELIVERABLE_ARCHIVE)}\n" -end - -desc "Build application deliverable." -task :app => [DELIVERABLE_APP] -task DELIVERABLE_APP => [DELIVERABLE_ARCHIVE, File.dirname(DELIVERABLE_APP)] do - echo_step("Building application bundle: #{File.basename(DELIVERABLE_APP)}") - - FileUtils.rm_r DELIVERABLE_APP if File.exist? DELIVERABLE_APP - - app_path = DELIVERABLE_APP.slice(0..(DELIVERABLE_APP.rindex('/') - 1)) - shell "xcodebuild -exportArchive -exportOptionsPlist exportOptionsApp.plist -archivePath \"#{DELIVERABLE_ARCHIVE}\" -exportPath \"#{app_path}\"" - - verify_deliverable DELIVERABLE_APP - - if shell_non_fatal "codesign --force --deep --sign \"Developer ID\" \"#{DELIVERABLE_APP}\"" - verify_codesign DELIVERABLE_APP - else - # If you're reading this message, you probably don't have a Developer ID certificate for signing the app. - # This is fine if you want to use your own build, but if you're planning on distributing the deliverables you're going to need to get a Developer ID certificate. - Console.puts ' _____________________________________ ' - Console.puts '/ WARNING: unable to sign app \\' - Console.puts '\\ deliverable with Developer ID! /' - Console.puts ' ------------------------------------- ' - Console.puts ' \\ ^__^ ' - Console.puts ' \\ (oo)\\_______ ' - Console.puts ' (__)\\ )\\/\\ ' - Console.puts ' ||----w | ' - Console.puts ' || || ' - end - - Console.puts "Finished: #{Console.green(DELIVERABLE_APP)}\n" -end - -task :zip => [DELIVERABLE_ZIP] -task DELIVERABLE_ZIP => [DELIVERABLE_APP, File.dirname(DELIVERABLE_ZIP), INTERMEDIATESDIR] do - echo_step("Building zip archive: #{File.basename(DELIVERABLE_ZIP)}") - - FileUtils.rm DELIVERABLE_ZIP if File.exist? DELIVERABLE_ZIP - shell "cd \"#{File.dirname(DELIVERABLE_APP)}\" && zip --symlinks -rq9o \"#{DELIVERABLE_ZIP}\" \"#{File.basename(DELIVERABLE_APP)}\"" - verify_deliverable DELIVERABLE_ZIP - - # Verify that zip didn't ruin code signing (this happened to Switch 0.0.6) - unzipped_app = "#{INTERMEDIATESDIR}/#{File.basename(DELIVERABLE_APP)}" - FileUtils.rm_r unzipped_app if File.exist? unzipped_app - shell "cd \"#{INTERMEDIATESDIR}\" && unzip -q \"#{DELIVERABLE_ZIP}\"" - verify_deliverable unzipped_app - - # Verify that unzipped app and app deliverable do not differ. - shell "diff -r \"#{unzipped_app}\" \"#{DELIVERABLE_APP}\"" - - # XXX: this will fail if the application was not code signed! - # Verify unzipped application launches successfully. - shell "open \"#{unzipped_app}\"" - # grep for "[/]Users/foo/bar" to prevent grep from showing up in the list of processes matching the query. - match = "[#{unzipped_app[0]}]#{unzipped_app[1..-1]}" - shell "ps auxwww | grep \"#{match}\" | awk '{ print $2; }' | xargs kill" - - unless shell_non_fatal "spctl --assess --verbose=4 --type execute \"#{unzipped_app}\"" - Console.puts Console.background_red(Console.white("WARNING: The application bundle inside #{File.basename(DELIVERABLE_ZIP)} is not properly code signed and is not suitable for distribution!")) - end - - # Clean up - FileUtils.rm_r unzipped_app - - Console.puts "Finished: #{Console.green(DELIVERABLE_ZIP)}\n" -end - -task :release_ready? do - formatted_fail "Releases can only be made from branches: #{RELEASE_BRANCHES.inspect}" unless BRANCH_IS_RELEASE - - gst = 'git status -uno --ignore-submodules=untracked' - formatted_fail "Uncommitted files detected!\n#{`#{gst} --short`}" unless `#{gst} --porcelain | wc -l`.strip == "0" -end - -desc "Build release zip and submit symbols to Hockey." -task :release => [:release_ready?, :analyze, :test, :zip] do - shell("git push") - - # The zip build step might succeed without a code signature, but a deliverable for release must be signed! - verify_codesign DELIVERABLE_APP - - hockey_api_token_file = File.open("Secrets-local/Hockey.api_token.asc", "r") - hockey_api_token = hockey_api_token_file.read - - hockey_app_id_file = File.open("Secrets-local/Hockey.app_id.asc", "r") - hockey_app_id = hockey_app_id_file.read - - # TODO: get SHA from git - # TODO: release type from BRANCH_IS_GM - shell("/usr/local/bin/puck -submit=auto -download=true -source_path=\".\" -api_token=\"#{hockey_api_token}\" -app_id=\"#{hockey_app_id}\" \"#{DELIVERABLE_ARCHIVE}\"") -end diff --git a/Switch.xcodeproj/project.pbxproj b/Switch.xcodeproj/project.pbxproj deleted file mode 100644 index 3bfb788f..00000000 --- a/Switch.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1591 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 92846F56EC75454259B87943 /* libPods-Switch.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FCF2042E2AFC2AAF3D0B00FD /* libPods-Switch.a */; }; - B0AB657ABC91AAC1D332D080 /* libPods-Switch-SwitchTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E510FB1A0E68F4315B2D76 /* libPods-Switch-SwitchTests.a */; }; - BC097E6419DB87BA00B5F447 /* SWScrollControl.m in Sources */ = {isa = PBXBuildFile; fileRef = BC097E6319DB87BA00B5F447 /* SWScrollControl.m */; }; - BC097E6619DBDA0200B5F447 /* SWScrollControlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC097E6519DBDA0200B5F447 /* SWScrollControlTests.m */; }; - BC0BBE8A1CE64224000AB84E /* SWXcodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0BBE891CE64224000AB84E /* SWXcodeTests.m */; }; - BC0BBE921CE64BD9000AB84E /* SWFinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0BBE911CE64BD9000AB84E /* SWFinderTests.m */; }; - BC0BBE941CE65263000AB84E /* SWSanityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0BBE931CE65263000AB84E /* SWSanityTests.m */; }; - BC15C903191F446800595EAB /* debugger.c in Sources */ = {isa = PBXBuildFile; fileRef = BC15C901191F446800595EAB /* debugger.c */; }; - BC19D3DA181436FB009CEC1F /* SWPreferencesService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3D9181436FB009CEC1F /* SWPreferencesService.m */; }; - BC19D3DD18143AB1009CEC1F /* SWAccessibilityService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC19D3DC18143AB1009CEC1F /* SWAccessibilityService.m */; }; - BC1B45B7190F379900246529 /* SWKeyboardPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1B45B5190F379900246529 /* SWKeyboardPreferencesViewController.m */; }; - BC1B45B8190F379900246529 /* SWKeyboardPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC1B45B6190F379900246529 /* SWKeyboardPreferencesViewController.xib */; }; - BC1B45BA190F37B400246529 /* Keyboard.icns in Resources */ = {isa = PBXBuildFile; fileRef = BC1B45B9190F37B400246529 /* Keyboard.icns */; }; - BC1B45F3190F925900246529 /* NSScreen+SWAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1B45F2190F925900246529 /* NSScreen+SWAdditions.m */; }; - BC1B46401911A56100246529 /* SWIsolatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1B463F1911A56100246529 /* SWIsolatorTests.m */; }; - BC1B46571911A77F00246529 /* SWAdvancedPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1B46551911A77F00246529 /* SWAdvancedPreferencesViewController.m */; }; - BC1B46581911A77F00246529 /* SWAdvancedPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC1B46561911A77F00246529 /* SWAdvancedPreferencesViewController.xib */; }; - BC275C1218CFD3C900761247 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC275C1118CFD3C900761247 /* Foundation.framework */; }; - BC275C1518CFD3CA00761247 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C1418CFD3CA00761247 /* main.m */; }; - BC275C2918CFD55000761247 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCA734E316DAC53F00CD4C74 /* AppKit.framework */; }; - BC275C2C18CFD5AA00761247 /* SWTerminationListener.m in Sources */ = {isa = PBXBuildFile; fileRef = BC275C2B18CFD5AA00761247 /* SWTerminationListener.m */; }; - BC275C3218CFD84000761247 /* relaunch in Resources */ = {isa = PBXBuildFile; fileRef = BC275C1018CFD3C900761247 /* relaunch */; }; - BC301A8119D6325600E64ADF /* SWStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = BC301A8019D6325600E64ADF /* SWStateMachine.m */; }; - BC301A9019D6328100E64ADF /* SWStateMachineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC301A8F19D6328100E64ADF /* SWStateMachineTests.m */; }; - BC3C78C4188262EB0021C244 /* SWMacVimTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3C78C3188262EB0021C244 /* SWMacVimTests.m */; }; - BC5A9798180E5A6F002ECD56 /* SWStatusBarMenuService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A9797180E5A6F002ECD56 /* SWStatusBarMenuService.m */; }; - BC5A979B180E6949002ECD56 /* SWLoggingService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5A979A180E6949002ECD56 /* SWLoggingService.m */; }; - BC63AB8F1755907D00016B9E /* SWHUDCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC63AB8E1755907D00016B9E /* SWHUDCollectionView.m */; }; - BC63AB921756FBE400016B9E /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC63AB911756FBE300016B9E /* Carbon.framework */; }; - BC63AB9A175701D900016B9E /* Haxcessibility.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC63AB99175701C200016B9E /* Haxcessibility.framework */; }; - BC660D5B1796573200CE8333 /* Sparkle.dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = BC660D5A1796573200CE8333 /* Sparkle.dsa_pub.pem */; }; - BC662BA7186A42E4003CF66C /* SWWindowListService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC662BA6186A42E4003CF66C /* SWWindowListService.m */; }; - BC662BB2186A7662003CF66C /* SWWindowGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = BC662BB1186A7662003CF66C /* SWWindowGroup.m */; }; - BC6B6D00178B711E001D691D /* NNMainThreadGuard.m in Sources */ = {isa = PBXBuildFile; fileRef = BC6B6CFF178B711E001D691D /* NNMainThreadGuard.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BC6B6D13178DDBDA001D691D /* SWCoreWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC6B6D12178DDBDA001D691D /* SWCoreWindowController.m */; }; - BC6B6D1C178DE8E5001D691D /* SWAPIEnabledWorker.m in Sources */ = {isa = PBXBuildFile; fileRef = BC6B6D1B178DE8E5001D691D /* SWAPIEnabledWorker.m */; }; - BC82B3F1183C48EE00FDD3B9 /* SWCoreWindowService.m in Sources */ = {isa = PBXBuildFile; fileRef = BC82B3F0183C48EE00FDD3B9 /* SWCoreWindowService.m */; }; - BC85E79B18DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = BC85E79A18DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.m */; }; - BC85E7B718DEA7C4001DF986 /* NSSet+SWChaining.m in Sources */ = {isa = PBXBuildFile; fileRef = BC85E7B618DEA7C4001DF986 /* NSSet+SWChaining.m */; }; - BC8E07AC188C890400C208D4 /* SWChromeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC8E07AB188C890400C208D4 /* SWChromeTests.m */; }; - BC948E2E180CFAA6008ED28A /* weave.png in Resources */ = {isa = PBXBuildFile; fileRef = BC948E2D180CFAA6008ED28A /* weave.png */; }; - BC9F0E0B19DA1DD300616BDB /* SWTestApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = BC9F0E0A19DA1DD300616BDB /* SWTestApplication.m */; }; - BCA5E99818766FE1004D70EE /* SWWindowContentsService.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5E99718766FE1004D70EE /* SWWindowContentsService.m */; }; - BCA5E9A6187A21A9004D70EE /* SWSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5E9A5187A21A9004D70EE /* SWSelector.m */; }; - BCA5E9A8187A307F004D70EE /* SWSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5E9A7187A307F004D70EE /* SWSelectorTests.m */; }; - BCA5E9AA187A3CCA004D70EE /* SWWindowListServiceTestSuperclass.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5E9A9187A3CCA004D70EE /* SWWindowListServiceTestSuperclass.m */; }; - BCA5E9AD187A4020004D70EE /* SWDashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA5E9AC187A4020004D70EE /* SWDashTests.m */; }; - BCA734E116DAC53F00CD4C74 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCA734E016DAC53F00CD4C74 /* Cocoa.framework */; }; - BCA734EB16DAC53F00CD4C74 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BCA734E916DAC53F00CD4C74 /* InfoPlist.strings */; }; - BCA734ED16DAC53F00CD4C74 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA734EC16DAC53F00CD4C74 /* main.m */; }; - BCA734F116DAC53F00CD4C74 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = BCA734EF16DAC53F00CD4C74 /* Credits.rtf */; }; - BCA734F416DAC53F00CD4C74 /* SWAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA734F316DAC53F00CD4C74 /* SWAppDelegate.m */; }; - BCA734F716DAC53F00CD4C74 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCA734F516DAC53F00CD4C74 /* MainMenu.xib */; }; - BCA7350016DAC54000CD4C74 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCA734E016DAC53F00CD4C74 /* Cocoa.framework */; }; - BCA7350816DAC54000CD4C74 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BCA7350616DAC54000CD4C74 /* InfoPlist.strings */; }; - BCA7351B16DAC5E200CD4C74 /* SWWindowListWorker.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7351A16DAC5E200CD4C74 /* SWWindowListWorker.m */; }; - BCA7351E16DAC61F00CD4C74 /* SWWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7351D16DAC61F00CD4C74 /* SWWindow.m */; }; - BCA7352216DAC6A900CD4C74 /* SWWindowWorker.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7352116DAC6A900CD4C74 /* SWWindowWorker.m */; }; - BCA7352516DAC93C00CD4C74 /* SWApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7352416DAC93C00CD4C74 /* SWApplication.m */; }; - BCA7352F16DB239900CD4C74 /* SWWindowThumbnailView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7352E16DB239900CD4C74 /* SWWindowThumbnailView.m */; }; - BCA7353816E148EF00CD4C74 /* SWRoundedRectView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7353716E148EF00CD4C74 /* SWRoundedRectView.m */; }; - BCA7353A16E14EF000CD4C74 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCA7353916E14EF000CD4C74 /* QuartzCore.framework */; }; - BCA7354316E1621400CD4C74 /* constants.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7354216E1621400CD4C74 /* constants.m */; }; - BCA7354916E1F14D00CD4C74 /* SWSelectionBoxView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7354816E1F14D00CD4C74 /* SWSelectionBoxView.m */; }; - BCA7354D16E2ED5000CD4C74 /* SWHUDView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA7354C16E2ED5000CD4C74 /* SWHUDView.m */; }; - BCA8AD5B18AC5E3F0059F253 /* SWWordTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA8AD5A18AC5E3F0059F253 /* SWWordTests.m */; }; - BCA8AD6A18AD61A00059F253 /* SWEventTap.m in Sources */ = {isa = PBXBuildFile; fileRef = BCA8AD6918AD61A00059F253 /* SWEventTap.m */; }; - BCBAD89F19D7CCD200891AD9 /* SWWindowListServiceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCBAD89E19D7CCD200891AD9 /* SWWindowListServiceTests.m */; }; - BCBC848F1882138B00E5B52E /* SWWindow+TweetbotQuirks.m in Sources */ = {isa = PBXBuildFile; fileRef = BCBC848E1882138B00E5B52E /* SWWindow+TweetbotQuirks.m */; }; - BCBF525017B079FA00716200 /* SWHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = BCBF524D17B079FA00716200 /* SWHotKey.m */; }; - BCCA7A5F18076AF500CE36E5 /* NNKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCCA7A5618076ADB00CE36E5 /* NNKit.framework */; }; - BCCA7A631807894A00CE36E5 /* SWGeneralPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCA7A611807894A00CE36E5 /* SWGeneralPreferencesViewController.m */; }; - BCCA7A641807894A00CE36E5 /* SWGeneralPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCCA7A621807894A00CE36E5 /* SWGeneralPreferencesViewController.xib */; }; - BCCA7AB21807CD2B00CE36E5 /* NNKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCCA7A5618076ADB00CE36E5 /* NNKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - BCCA7AD0180896D100CE36E5 /* helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCA7ACF180896D100CE36E5 /* helpers.m */; }; - BCCE8CEB18091A4F006B0059 /* NSWindow+NNScreenCapture.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CEA18091A4F006B0059 /* NSWindow+NNScreenCapture.m */; }; - BCCE8CEE18091AFC006B0059 /* NSImage+NNFilesystem.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CED18091AFC006B0059 /* NSImage+NNFilesystem.m */; }; - BCCE8CF018091F75006B0059 /* SWTweetbotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CEF18091F75006B0059 /* SWTweetbotTests.m */; }; - BCCE8CF31809266A006B0059 /* SWSafariTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CF21809266A006B0059 /* SWSafariTests.m */; }; - BCCE8CF51809297B006B0059 /* SWSheetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CF41809297B006B0059 /* SWSheetTests.m */; }; - BCCE8CF718092CDF006B0059 /* SWPowerboxTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CF618092CDF006B0059 /* SWPowerboxTests.m */; }; - BCCE8CF918093037006B0059 /* SWGithubTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCCE8CF818093037006B0059 /* SWGithubTests.m */; }; - BCDDB2BE178EF16E00C22E47 /* Haxcessibility.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BC63AB99175701C200016B9E /* Haxcessibility.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - BCEA426819DC668100C5A1D2 /* SWInterfaceController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCEA426719DC668100C5A1D2 /* SWInterfaceController.m */; }; - BCF5C4A019DB5B7A00E59B62 /* SWImageComparisonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCF5C49F19DB5B7A00E59B62 /* SWImageComparisonTests.m */; }; - BCF5C4AB19DB5F8900E59B62 /* 1376.ar in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCF5C4A119DB5E0900E59B62 /* 1376.ar */; }; - BCF5C4AC19DB5F8900E59B62 /* 5891.ar in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCF5C4A319DB5E0900E59B62 /* 5891.ar */; }; - BCF5C4AD19DB5F8900E59B62 /* 5904.ar in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCF5C4A519DB5E0900E59B62 /* 5904.ar */; }; - BCFB702E1AA9112A00D99F4C /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCFB70271AA9110E00D99F4C /* ReactiveCocoa.framework */; }; - BCFB702F1AA9113B00D99F4C /* ReactiveCocoa.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCFB70271AA9110E00D99F4C /* ReactiveCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - BC275C2D18CFD78C00761247 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCA734D516DAC53F00CD4C74 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BC275C0F18CFD3C900761247; - remoteInfo = relaunch; - }; - BC63AB98175701C200016B9E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BC63AB94175701C200016B9E /* Haxcessibility.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DC2EF5B0486A6940098B216; - remoteInfo = Haxcessibility; - }; - BC63AB9B175701DD00016B9E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BC63AB94175701C200016B9E /* Haxcessibility.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DC2EF4F0486A6940098B216; - remoteInfo = Haxcessibility; - }; - BCA7350116DAC54000CD4C74 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCA734D516DAC53F00CD4C74 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BCA734DC16DAC53F00CD4C74; - remoteInfo = Switch; - }; - BCCA7A5518076ADB00CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = BCD1D52917D9471A00A3EBD4; - remoteInfo = "NNKit Mac"; - }; - BCCA7A5718076ADB00CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = BCD1D54017D9471A00A3EBD4; - remoteInfo = "NNKit Mac Tests"; - }; - BCCA7A5918076ADB00CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = BCCA79F318075C6F00CE36E5; - remoteInfo = "NNKit iOS"; - }; - BCCA7A5B18076ADB00CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = BCCA7A1D1807608600CE36E5; - remoteInfo = "NNKit iOS Tests"; - }; - BCCA7A5D18076AED00CE36E5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = BCD1D52817D9471A00A3EBD4; - remoteInfo = "NNKit Mac"; - }; - BCFB70261AA9110E00D99F4C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D04725EA19E49ED7006002AA; - remoteInfo = "ReactiveCocoa-Mac"; - }; - BCFB70281AA9110E00D99F4C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D04725F519E49ED7006002AA; - remoteInfo = "ReactiveCocoa-MacTests"; - }; - BCFB702A1AA9110E00D99F4C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D047260C19E49F82006002AA; - remoteInfo = "ReactiveCocoa-iOS"; - }; - BCFB702C1AA9110E00D99F4C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D047261619E49F82006002AA; - remoteInfo = "ReactiveCocoa-iOSTests"; - }; - BCFB70311AA9114C00D99F4C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = D04725E919E49ED7006002AA; - remoteInfo = "ReactiveCocoa-Mac"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - BC275C0E18CFD3C900761247 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; - BCDDB2BC178EF15B00C22E47 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - BCFB702F1AA9113B00D99F4C /* ReactiveCocoa.framework in CopyFiles */, - BCCA7AB21807CD2B00CE36E5 /* NNKit.framework in CopyFiles */, - BCDDB2BE178EF16E00C22E47 /* Haxcessibility.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCF5C4AA19DB5F7800E59B62 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 7; - files = ( - BCF5C4AB19DB5F8900E59B62 /* 1376.ar in CopyFiles */, - BCF5C4AC19DB5F8900E59B62 /* 5891.ar in CopyFiles */, - BCF5C4AD19DB5F8900E59B62 /* 5904.ar in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 23C5A096931A640E810631DE /* Pods-Switch-SwitchTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switch-SwitchTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests.debug.xcconfig"; sourceTree = ""; }; - 31E0F06E5DF1342924FFACC5 /* Pods-Switch-SwitchTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switch-SwitchTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests.release.xcconfig"; sourceTree = ""; }; - 566966697F19AA05013D724F /* Pods-Switch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switch.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Switch/Pods-Switch.debug.xcconfig"; sourceTree = ""; }; - 889139ABCF76353CB055A23D /* Pods-Switch.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Switch.release.xcconfig"; path = "Pods/Target Support Files/Pods-Switch/Pods-Switch.release.xcconfig"; sourceTree = ""; }; - A1E510FB1A0E68F4315B2D76 /* libPods-Switch-SwitchTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Switch-SwitchTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - BC097E6219DB87BA00B5F447 /* SWScrollControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWScrollControl.h; sourceTree = ""; }; - BC097E6319DB87BA00B5F447 /* SWScrollControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWScrollControl.m; sourceTree = ""; }; - BC097E6519DBDA0200B5F447 /* SWScrollControlTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWScrollControlTests.m; sourceTree = ""; }; - BC0BBE891CE64224000AB84E /* SWXcodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWXcodeTests.m; sourceTree = ""; }; - BC0BBE911CE64BD9000AB84E /* SWFinderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWFinderTests.m; sourceTree = ""; }; - BC0BBE931CE65263000AB84E /* SWSanityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWSanityTests.m; sourceTree = ""; }; - BC15C901191F446800595EAB /* debugger.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = debugger.c; sourceTree = ""; }; - BC15C902191F446800595EAB /* debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debugger.h; sourceTree = ""; }; - BC19D3D8181436FB009CEC1F /* SWPreferencesService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWPreferencesService.h; sourceTree = ""; }; - BC19D3D9181436FB009CEC1F /* SWPreferencesService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWPreferencesService.m; sourceTree = ""; }; - BC19D3DB18143AB1009CEC1F /* SWAccessibilityService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAccessibilityService.h; sourceTree = ""; }; - BC19D3DC18143AB1009CEC1F /* SWAccessibilityService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWAccessibilityService.m; sourceTree = ""; }; - BC1B45B4190F379900246529 /* SWKeyboardPreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWKeyboardPreferencesViewController.h; sourceTree = ""; }; - BC1B45B5190F379900246529 /* SWKeyboardPreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWKeyboardPreferencesViewController.m; sourceTree = ""; }; - BC1B45B6190F379900246529 /* SWKeyboardPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWKeyboardPreferencesViewController.xib; sourceTree = ""; }; - BC1B45B9190F37B400246529 /* Keyboard.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Keyboard.icns; path = Resources/Keyboard.icns; sourceTree = ""; }; - BC1B45F1190F925900246529 /* NSScreen+SWAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSScreen+SWAdditions.h"; sourceTree = ""; }; - BC1B45F2190F925900246529 /* NSScreen+SWAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSScreen+SWAdditions.m"; sourceTree = ""; }; - BC1B463F1911A56100246529 /* SWIsolatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWIsolatorTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BC1B46541911A77F00246529 /* SWAdvancedPreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAdvancedPreferencesViewController.h; sourceTree = ""; }; - BC1B46551911A77F00246529 /* SWAdvancedPreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWAdvancedPreferencesViewController.m; sourceTree = ""; }; - BC1B46561911A77F00246529 /* SWAdvancedPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWAdvancedPreferencesViewController.xib; sourceTree = ""; }; - BC275C1018CFD3C900761247 /* relaunch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = relaunch; sourceTree = BUILT_PRODUCTS_DIR; }; - BC275C1118CFD3C900761247 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - BC275C1418CFD3CA00761247 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - BC275C1718CFD3CA00761247 /* relaunch-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "relaunch-Prefix.pch"; sourceTree = ""; }; - BC275C2A18CFD5AA00761247 /* SWTerminationListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWTerminationListener.h; sourceTree = ""; }; - BC275C2B18CFD5AA00761247 /* SWTerminationListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWTerminationListener.m; sourceTree = ""; }; - BC301A7F19D6325600E64ADF /* SWStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SWStateMachine.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - BC301A8019D6325600E64ADF /* SWStateMachine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWStateMachine.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BC301A8F19D6328100E64ADF /* SWStateMachineTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWStateMachineTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BC3C78C3188262EB0021C244 /* SWMacVimTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWMacVimTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BC5A9796180E5A6F002ECD56 /* SWStatusBarMenuService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWStatusBarMenuService.h; sourceTree = ""; }; - BC5A9797180E5A6F002ECD56 /* SWStatusBarMenuService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWStatusBarMenuService.m; sourceTree = ""; }; - BC5A9799180E6949002ECD56 /* SWLoggingService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWLoggingService.h; sourceTree = ""; }; - BC5A979A180E6949002ECD56 /* SWLoggingService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWLoggingService.m; sourceTree = ""; }; - BC63AB8D1755907D00016B9E /* SWHUDCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWHUDCollectionView.h; sourceTree = ""; }; - BC63AB8E1755907D00016B9E /* SWHUDCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWHUDCollectionView.m; sourceTree = ""; }; - BC63AB911756FBE300016B9E /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; - BC63AB94175701C200016B9E /* Haxcessibility.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Haxcessibility.xcodeproj; path = Frameworks/Haxcessibility/Haxcessibility.xcodeproj; sourceTree = ""; }; - BC660D5A1796573200CE8333 /* Sparkle.dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Sparkle.dsa_pub.pem; sourceTree = ""; }; - BC662BA5186A42E4003CF66C /* SWWindowListService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowListService.h; sourceTree = ""; }; - BC662BA6186A42E4003CF66C /* SWWindowListService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowListService.m; sourceTree = ""; }; - BC662BB0186A7662003CF66C /* SWWindowGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowGroup.h; sourceTree = ""; }; - BC662BB1186A7662003CF66C /* SWWindowGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowGroup.m; sourceTree = ""; }; - BC6B6CFF178B711E001D691D /* NNMainThreadGuard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NNMainThreadGuard.m; sourceTree = ""; }; - BC6B6D11178DDBDA001D691D /* SWCoreWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWCoreWindowController.h; sourceTree = ""; }; - BC6B6D12178DDBDA001D691D /* SWCoreWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWCoreWindowController.m; sourceTree = ""; }; - BC6B6D1A178DE8E5001D691D /* SWAPIEnabledWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAPIEnabledWorker.h; sourceTree = ""; }; - BC6B6D1B178DE8E5001D691D /* SWAPIEnabledWorker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWAPIEnabledWorker.m; sourceTree = ""; }; - BC82B3EF183C48EE00FDD3B9 /* SWCoreWindowService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWCoreWindowService.h; sourceTree = ""; }; - BC82B3F0183C48EE00FDD3B9 /* SWCoreWindowService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWCoreWindowService.m; sourceTree = ""; }; - BC85E79918DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSLayoutConstraint+SWConstraintHelpers.h"; sourceTree = ""; }; - BC85E79A18DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSLayoutConstraint+SWConstraintHelpers.m"; sourceTree = ""; }; - BC85E7B518DEA7C4001DF986 /* NSSet+SWChaining.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSet+SWChaining.h"; sourceTree = ""; }; - BC85E7B618DEA7C4001DF986 /* NSSet+SWChaining.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSet+SWChaining.m"; sourceTree = ""; }; - BC8E07AB188C890400C208D4 /* SWChromeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWChromeTests.m; sourceTree = ""; }; - BC948E2D180CFAA6008ED28A /* weave.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = weave.png; path = Resources/weave.png; sourceTree = ""; }; - BC9F0E0919DA1DD300616BDB /* SWTestApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWTestApplication.h; sourceTree = ""; }; - BC9F0E0A19DA1DD300616BDB /* SWTestApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWTestApplication.m; sourceTree = ""; }; - BCA5E99618766FE1004D70EE /* SWWindowContentsService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowContentsService.h; sourceTree = ""; }; - BCA5E99718766FE1004D70EE /* SWWindowContentsService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowContentsService.m; sourceTree = ""; }; - BCA5E9A4187A21A9004D70EE /* SWSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWSelector.h; sourceTree = ""; }; - BCA5E9A5187A21A9004D70EE /* SWSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWSelector.m; sourceTree = ""; }; - BCA5E9A7187A307F004D70EE /* SWSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWSelectorTests.m; sourceTree = ""; }; - BCA5E9A9187A3CCA004D70EE /* SWWindowListServiceTestSuperclass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowListServiceTestSuperclass.m; sourceTree = ""; }; - BCA5E9AC187A4020004D70EE /* SWDashTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWDashTests.m; sourceTree = ""; }; - BCA5E9AE187A4070004D70EE /* SWWindowListServiceTestSuperclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowListServiceTestSuperclass.h; sourceTree = ""; }; - BCA734DD16DAC53F00CD4C74 /* Switch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Switch.app; sourceTree = BUILT_PRODUCTS_DIR; }; - BCA734E016DAC53F00CD4C74 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - BCA734E316DAC53F00CD4C74 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - BCA734E816DAC53F00CD4C74 /* Switch-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Switch-Info.plist"; sourceTree = ""; }; - BCA734EA16DAC53F00CD4C74 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - BCA734EC16DAC53F00CD4C74 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - BCA734EE16DAC53F00CD4C74 /* Switch-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Switch-Prefix.pch"; sourceTree = ""; }; - BCA734F016DAC53F00CD4C74 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; - BCA734F216DAC53F00CD4C74 /* SWAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWAppDelegate.h; sourceTree = ""; }; - BCA734F316DAC53F00CD4C74 /* SWAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SWAppDelegate.m; sourceTree = ""; }; - BCA734F616DAC53F00CD4C74 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; - BCA734FD16DAC54000CD4C74 /* SwitchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwitchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - BCA7350516DAC54000CD4C74 /* SwitchTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SwitchTests-Info.plist"; sourceTree = ""; }; - BCA7350716DAC54000CD4C74 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - BCA7351916DAC5E200CD4C74 /* SWWindowListWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowListWorker.h; sourceTree = ""; }; - BCA7351A16DAC5E200CD4C74 /* SWWindowListWorker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowListWorker.m; sourceTree = ""; }; - BCA7351C16DAC61F00CD4C74 /* SWWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindow.h; sourceTree = ""; }; - BCA7351D16DAC61F00CD4C74 /* SWWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindow.m; sourceTree = ""; }; - BCA7352016DAC6A900CD4C74 /* SWWindowWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowWorker.h; sourceTree = ""; }; - BCA7352116DAC6A900CD4C74 /* SWWindowWorker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowWorker.m; sourceTree = ""; }; - BCA7352316DAC93C00CD4C74 /* SWApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWApplication.h; sourceTree = ""; }; - BCA7352416DAC93C00CD4C74 /* SWApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWApplication.m; sourceTree = ""; }; - BCA7352D16DB239900CD4C74 /* SWWindowThumbnailView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWWindowThumbnailView.h; sourceTree = ""; }; - BCA7352E16DB239900CD4C74 /* SWWindowThumbnailView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowThumbnailView.m; sourceTree = ""; }; - BCA7353616E148EF00CD4C74 /* SWRoundedRectView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWRoundedRectView.h; sourceTree = ""; }; - BCA7353716E148EF00CD4C74 /* SWRoundedRectView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWRoundedRectView.m; sourceTree = ""; }; - BCA7353916E14EF000CD4C74 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - BCA7354116E1621400CD4C74 /* constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = constants.h; sourceTree = ""; }; - BCA7354216E1621400CD4C74 /* constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = constants.m; sourceTree = ""; }; - BCA7354716E1F14D00CD4C74 /* SWSelectionBoxView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWSelectionBoxView.h; sourceTree = ""; }; - BCA7354816E1F14D00CD4C74 /* SWSelectionBoxView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWSelectionBoxView.m; sourceTree = ""; }; - BCA7354A16E2AC3F00CD4C74 /* imageComparators.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = imageComparators.h; sourceTree = ""; }; - BCA7354B16E2ED5000CD4C74 /* SWHUDView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWHUDView.h; sourceTree = ""; }; - BCA7354C16E2ED5000CD4C74 /* SWHUDView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWHUDView.m; sourceTree = ""; }; - BCA8AD5A18AC5E3F0059F253 /* SWWordTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWWordTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BCA8AD6818AD61A00059F253 /* SWEventTap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWEventTap.h; sourceTree = ""; }; - BCA8AD6918AD61A00059F253 /* SWEventTap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWEventTap.m; sourceTree = ""; }; - BCBAD89E19D7CCD200891AD9 /* SWWindowListServiceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWWindowListServiceTests.m; sourceTree = ""; }; - BCBC848D1882138B00E5B52E /* SWWindow+TweetbotQuirks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SWWindow+TweetbotQuirks.h"; sourceTree = ""; }; - BCBC848E1882138B00E5B52E /* SWWindow+TweetbotQuirks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SWWindow+TweetbotQuirks.m"; sourceTree = ""; }; - BCBF524C17B079FA00716200 /* SWHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWHotKey.h; sourceTree = ""; }; - BCBF524D17B079FA00716200 /* SWHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWHotKey.m; sourceTree = ""; }; - BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = NNKit.xcodeproj; path = Frameworks/NNKit/NNKit.xcodeproj; sourceTree = ""; }; - BCCA7A601807894A00CE36E5 /* SWGeneralPreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWGeneralPreferencesViewController.h; sourceTree = ""; }; - BCCA7A611807894A00CE36E5 /* SWGeneralPreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWGeneralPreferencesViewController.m; sourceTree = ""; }; - BCCA7A621807894A00CE36E5 /* SWGeneralPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SWGeneralPreferencesViewController.xib; sourceTree = ""; }; - BCCA7ACE180896D100CE36E5 /* helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = helpers.h; sourceTree = ""; }; - BCCA7ACF180896D100CE36E5 /* helpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = helpers.m; sourceTree = ""; }; - BCCE8CE918091A4F006B0059 /* NSWindow+NNScreenCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSWindow+NNScreenCapture.h"; sourceTree = ""; }; - BCCE8CEA18091A4F006B0059 /* NSWindow+NNScreenCapture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSWindow+NNScreenCapture.m"; sourceTree = ""; }; - BCCE8CEC18091AFC006B0059 /* NSImage+NNFilesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+NNFilesystem.h"; sourceTree = ""; }; - BCCE8CED18091AFC006B0059 /* NSImage+NNFilesystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+NNFilesystem.m"; sourceTree = ""; }; - BCCE8CEF18091F75006B0059 /* SWTweetbotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWTweetbotTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BCCE8CF118092584006B0059 /* SWWindowFilteringTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SWWindowFilteringTests.h; sourceTree = ""; }; - BCCE8CF21809266A006B0059 /* SWSafariTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWSafariTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BCCE8CF41809297B006B0059 /* SWSheetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWSheetTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BCCE8CF618092CDF006B0059 /* SWPowerboxTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SWPowerboxTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - BCCE8CF818093037006B0059 /* SWGithubTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWGithubTests.m; sourceTree = ""; }; - BCEA426619DC668100C5A1D2 /* SWInterfaceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWInterfaceController.h; sourceTree = ""; }; - BCEA426719DC668100C5A1D2 /* SWInterfaceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWInterfaceController.m; sourceTree = ""; }; - BCF5C49F19DB5B7A00E59B62 /* SWImageComparisonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWImageComparisonTests.m; sourceTree = ""; }; - BCF5C4A219DB5E0900E59B62 /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/1376.ar; sourceTree = ""; }; - BCF5C4A419DB5E0900E59B62 /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/5891.ar; sourceTree = ""; }; - BCF5C4A619DB5E0900E59B62 /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/5904.ar; sourceTree = ""; }; - BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactiveCocoa.xcodeproj; path = Frameworks/ReactiveCocoa/ReactiveCocoa.xcodeproj; sourceTree = ""; }; - FCF2042E2AFC2AAF3D0B00FD /* libPods-Switch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Switch.a"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - BC275C0D18CFD3C900761247 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BC275C2918CFD55000761247 /* AppKit.framework in Frameworks */, - BC275C1218CFD3C900761247 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCA734DA16DAC53F00CD4C74 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BC63AB921756FBE400016B9E /* Carbon.framework in Frameworks */, - BCA734E116DAC53F00CD4C74 /* Cocoa.framework in Frameworks */, - BCA7353A16E14EF000CD4C74 /* QuartzCore.framework in Frameworks */, - BC63AB9A175701D900016B9E /* Haxcessibility.framework in Frameworks */, - BCCA7A5F18076AF500CE36E5 /* NNKit.framework in Frameworks */, - BCFB702E1AA9112A00D99F4C /* ReactiveCocoa.framework in Frameworks */, - 92846F56EC75454259B87943 /* libPods-Switch.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCA734F916DAC54000CD4C74 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BCA7350016DAC54000CD4C74 /* Cocoa.framework in Frameworks */, - B0AB657ABC91AAC1D332D080 /* libPods-Switch-SwitchTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - BC1B45A1190F2B0100246529 /* Preferences */ = { - isa = PBXGroup; - children = ( - BC1B46541911A77F00246529 /* SWAdvancedPreferencesViewController.h */, - BC1B46551911A77F00246529 /* SWAdvancedPreferencesViewController.m */, - BC1B46561911A77F00246529 /* SWAdvancedPreferencesViewController.xib */, - BCCA7A601807894A00CE36E5 /* SWGeneralPreferencesViewController.h */, - BCCA7A611807894A00CE36E5 /* SWGeneralPreferencesViewController.m */, - BCCA7A621807894A00CE36E5 /* SWGeneralPreferencesViewController.xib */, - BC1B45B4190F379900246529 /* SWKeyboardPreferencesViewController.h */, - BC1B45B5190F379900246529 /* SWKeyboardPreferencesViewController.m */, - BC1B45B6190F379900246529 /* SWKeyboardPreferencesViewController.xib */, - ); - name = Preferences; - sourceTree = ""; - }; - BC275C1318CFD3C900761247 /* relaunch */ = { - isa = PBXGroup; - children = ( - BC275C2A18CFD5AA00761247 /* SWTerminationListener.h */, - BC275C2B18CFD5AA00761247 /* SWTerminationListener.m */, - BC275C1418CFD3CA00761247 /* main.m */, - BC275C1618CFD3CA00761247 /* Supporting Files */, - ); - path = relaunch; - sourceTree = ""; - }; - BC275C1618CFD3CA00761247 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - BC275C1718CFD3CA00761247 /* relaunch-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BC5A978D180E5A66002ECD56 /* Services */ = { - isa = PBXGroup; - children = ( - BC19D3DB18143AB1009CEC1F /* SWAccessibilityService.h */, - BC19D3DC18143AB1009CEC1F /* SWAccessibilityService.m */, - BC82B3EF183C48EE00FDD3B9 /* SWCoreWindowService.h */, - BC82B3F0183C48EE00FDD3B9 /* SWCoreWindowService.m */, - BCA8AD6818AD61A00059F253 /* SWEventTap.h */, - BCA8AD6918AD61A00059F253 /* SWEventTap.m */, - BC5A9799180E6949002ECD56 /* SWLoggingService.h */, - BC5A979A180E6949002ECD56 /* SWLoggingService.m */, - BC19D3D8181436FB009CEC1F /* SWPreferencesService.h */, - BC19D3D9181436FB009CEC1F /* SWPreferencesService.m */, - BC5A9796180E5A6F002ECD56 /* SWStatusBarMenuService.h */, - BC5A9797180E5A6F002ECD56 /* SWStatusBarMenuService.m */, - BCA5E99618766FE1004D70EE /* SWWindowContentsService.h */, - BCA5E99718766FE1004D70EE /* SWWindowContentsService.m */, - BC662BA5186A42E4003CF66C /* SWWindowListService.h */, - BC662BA6186A42E4003CF66C /* SWWindowListService.m */, - ); - name = Services; - sourceTree = ""; - }; - BC63AB901755C6EC00016B9E /* Controller */ = { - isa = PBXGroup; - children = ( - BC1B45A1190F2B0100246529 /* Preferences */, - BCA734F216DAC53F00CD4C74 /* SWAppDelegate.h */, - BCA734F316DAC53F00CD4C74 /* SWAppDelegate.m */, - BCEA426619DC668100C5A1D2 /* SWInterfaceController.h */, - BCEA426719DC668100C5A1D2 /* SWInterfaceController.m */, - BC6B6D11178DDBDA001D691D /* SWCoreWindowController.h */, - BC6B6D12178DDBDA001D691D /* SWCoreWindowController.m */, - ); - name = Controller; - sourceTree = ""; - }; - BC63AB93175701BA00016B9E /* Haxcessibility */ = { - isa = PBXGroup; - children = ( - BC63AB94175701C200016B9E /* Haxcessibility.xcodeproj */, - ); - name = Haxcessibility; - sourceTree = ""; - }; - BC63AB95175701C200016B9E /* Products */ = { - isa = PBXGroup; - children = ( - BC63AB99175701C200016B9E /* Haxcessibility.framework */, - ); - name = Products; - sourceTree = ""; - }; - BC6B6CFB178B7115001D691D /* Debug */ = { - isa = PBXGroup; - children = ( - BC15C901191F446800595EAB /* debugger.c */, - BC15C902191F446800595EAB /* debugger.h */, - BC6B6CFF178B711E001D691D /* NNMainThreadGuard.m */, - ); - name = Debug; - sourceTree = ""; - }; - BC948E24180CFA97008ED28A /* Resources */ = { - isa = PBXGroup; - children = ( - BC1B45B9190F37B400246529 /* Keyboard.icns */, - BC948E2D180CFAA6008ED28A /* weave.png */, - ); - name = Resources; - sourceTree = ""; - }; - BC9A0263191F9452008E5103 /* Categories */ = { - isa = PBXGroup; - children = ( - BC85E79918DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.h */, - BC85E79A18DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.m */, - BC85E7B518DEA7C4001DF986 /* NSSet+SWChaining.h */, - BC85E7B618DEA7C4001DF986 /* NSSet+SWChaining.m */, - BC1B45F1190F925900246529 /* NSScreen+SWAdditions.h */, - BC1B45F2190F925900246529 /* NSScreen+SWAdditions.m */, - ); - name = Categories; - sourceTree = ""; - }; - BCA5E9AB187A3FF4004D70EE /* Regression */ = { - isa = PBXGroup; - children = ( - BC8E07AB188C890400C208D4 /* SWChromeTests.m */, - BCA5E9AC187A4020004D70EE /* SWDashTests.m */, - BC0BBE911CE64BD9000AB84E /* SWFinderTests.m */, - BCCE8CF818093037006B0059 /* SWGithubTests.m */, - BC1B463F1911A56100246529 /* SWIsolatorTests.m */, - BC3C78C3188262EB0021C244 /* SWMacVimTests.m */, - BCCE8CF618092CDF006B0059 /* SWPowerboxTests.m */, - BCCE8CF21809266A006B0059 /* SWSafariTests.m */, - BC0BBE931CE65263000AB84E /* SWSanityTests.m */, - BCCE8CF41809297B006B0059 /* SWSheetTests.m */, - BCCE8CEF18091F75006B0059 /* SWTweetbotTests.m */, - BCA8AD5A18AC5E3F0059F253 /* SWWordTests.m */, - BC0BBE891CE64224000AB84E /* SWXcodeTests.m */, - ); - name = Regression; - sourceTree = ""; - }; - BCA734D416DAC53F00CD4C74 = { - isa = PBXGroup; - children = ( - BCA734E616DAC53F00CD4C74 /* Switch */, - BCA7350316DAC54000CD4C74 /* SwitchTests */, - BC275C1318CFD3C900761247 /* relaunch */, - BCA734DF16DAC53F00CD4C74 /* Frameworks */, - BCA734DE16DAC53F00CD4C74 /* Products */, - DCCCBA49A345244F314199A7 /* Pods */, - ); - sourceTree = ""; - }; - BCA734DE16DAC53F00CD4C74 /* Products */ = { - isa = PBXGroup; - children = ( - BCA734DD16DAC53F00CD4C74 /* Switch.app */, - BCA734FD16DAC54000CD4C74 /* SwitchTests.xctest */, - BC275C1018CFD3C900761247 /* relaunch */, - ); - name = Products; - sourceTree = ""; - }; - BCA734DF16DAC53F00CD4C74 /* Frameworks */ = { - isa = PBXGroup; - children = ( - BC63AB93175701BA00016B9E /* Haxcessibility */, - BCCA7A4D18076AD700CE36E5 /* NNKit */, - BCF357D5176CEE0200458AFF /* ReactiveCocoa */, - BCA734E216DAC53F00CD4C74 /* System Frameworks */, - FCF2042E2AFC2AAF3D0B00FD /* libPods-Switch.a */, - A1E510FB1A0E68F4315B2D76 /* libPods-Switch-SwitchTests.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - BCA734E216DAC53F00CD4C74 /* System Frameworks */ = { - isa = PBXGroup; - children = ( - BCA734E316DAC53F00CD4C74 /* AppKit.framework */, - BC63AB911756FBE300016B9E /* Carbon.framework */, - BCA734E016DAC53F00CD4C74 /* Cocoa.framework */, - BC275C1118CFD3C900761247 /* Foundation.framework */, - BCA7353916E14EF000CD4C74 /* QuartzCore.framework */, - ); - name = "System Frameworks"; - sourceTree = ""; - }; - BCA734E616DAC53F00CD4C74 /* Switch */ = { - isa = PBXGroup; - children = ( - BC9A0263191F9452008E5103 /* Categories */, - BC63AB901755C6EC00016B9E /* Controller */, - BC6B6CFB178B7115001D691D /* Debug */, - BCA7353016DB2BDC00CD4C74 /* Logic */, - BCA7351416DAC54C00CD4C74 /* Model */, - BCA7352C16DB237E00CD4C74 /* View */, - BCA734E716DAC53F00CD4C74 /* Supporting Files */, - ); - path = Switch; - sourceTree = ""; - }; - BCA734E716DAC53F00CD4C74 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - BC948E24180CFA97008ED28A /* Resources */, - BCA7354116E1621400CD4C74 /* constants.h */, - BCA7354216E1621400CD4C74 /* constants.m */, - BCA734EF16DAC53F00CD4C74 /* Credits.rtf */, - BCA734E916DAC53F00CD4C74 /* InfoPlist.strings */, - BCA734EC16DAC53F00CD4C74 /* main.m */, - BCA734F516DAC53F00CD4C74 /* MainMenu.xib */, - BC660D5A1796573200CE8333 /* Sparkle.dsa_pub.pem */, - BCA734E816DAC53F00CD4C74 /* Switch-Info.plist */, - BCA734EE16DAC53F00CD4C74 /* Switch-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BCA7350316DAC54000CD4C74 /* SwitchTests */ = { - isa = PBXGroup; - children = ( - BCA5E9AB187A3FF4004D70EE /* Regression */, - BC301A8F19D6328100E64ADF /* SWStateMachineTests.m */, - BCF5C49F19DB5B7A00E59B62 /* SWImageComparisonTests.m */, - BC097E6519DBDA0200B5F447 /* SWScrollControlTests.m */, - BCA5E9A7187A307F004D70EE /* SWSelectorTests.m */, - BCBAD89E19D7CCD200891AD9 /* SWWindowListServiceTests.m */, - BCA7350416DAC54000CD4C74 /* Supporting Files */, - ); - path = SwitchTests; - sourceTree = ""; - }; - BCA7350416DAC54000CD4C74 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - BCF5C4A119DB5E0900E59B62 /* 1376.ar */, - BCF5C4A319DB5E0900E59B62 /* 5891.ar */, - BCF5C4A519DB5E0900E59B62 /* 5904.ar */, - BC9F0E0919DA1DD300616BDB /* SWTestApplication.h */, - BC9F0E0A19DA1DD300616BDB /* SWTestApplication.m */, - BCA5E9AE187A4070004D70EE /* SWWindowListServiceTestSuperclass.h */, - BCA5E9A9187A3CCA004D70EE /* SWWindowListServiceTestSuperclass.m */, - BCCE8CF118092584006B0059 /* SWWindowFilteringTests.h */, - BCA7350516DAC54000CD4C74 /* SwitchTests-Info.plist */, - BCA7350616DAC54000CD4C74 /* InfoPlist.strings */, - BCCE8CE918091A4F006B0059 /* NSWindow+NNScreenCapture.h */, - BCCE8CEA18091A4F006B0059 /* NSWindow+NNScreenCapture.m */, - BCCE8CEC18091AFC006B0059 /* NSImage+NNFilesystem.h */, - BCCE8CED18091AFC006B0059 /* NSImage+NNFilesystem.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - BCA7351416DAC54C00CD4C74 /* Model */ = { - isa = PBXGroup; - children = ( - BCA7352316DAC93C00CD4C74 /* SWApplication.h */, - BCA7352416DAC93C00CD4C74 /* SWApplication.m */, - BCA7351C16DAC61F00CD4C74 /* SWWindow.h */, - BCA7351D16DAC61F00CD4C74 /* SWWindow.m */, - BC662BB0186A7662003CF66C /* SWWindowGroup.h */, - BC662BB1186A7662003CF66C /* SWWindowGroup.m */, - ); - name = Model; - sourceTree = ""; - }; - BCA7352C16DB237E00CD4C74 /* View */ = { - isa = PBXGroup; - children = ( - BCA7354B16E2ED5000CD4C74 /* SWHUDView.h */, - BCA7354C16E2ED5000CD4C74 /* SWHUDView.m */, - BC63AB8D1755907D00016B9E /* SWHUDCollectionView.h */, - BC63AB8E1755907D00016B9E /* SWHUDCollectionView.m */, - BCA7353616E148EF00CD4C74 /* SWRoundedRectView.h */, - BCA7353716E148EF00CD4C74 /* SWRoundedRectView.m */, - BCA7354716E1F14D00CD4C74 /* SWSelectionBoxView.h */, - BCA7354816E1F14D00CD4C74 /* SWSelectionBoxView.m */, - BCA7352D16DB239900CD4C74 /* SWWindowThumbnailView.h */, - BCA7352E16DB239900CD4C74 /* SWWindowThumbnailView.m */, - ); - name = View; - sourceTree = ""; - }; - BCA7353016DB2BDC00CD4C74 /* Logic */ = { - isa = PBXGroup; - children = ( - BC097E6219DB87BA00B5F447 /* SWScrollControl.h */, - BC097E6319DB87BA00B5F447 /* SWScrollControl.m */, - BCA5E9A4187A21A9004D70EE /* SWSelector.h */, - BCA5E9A5187A21A9004D70EE /* SWSelector.m */, - BC301A7F19D6325600E64ADF /* SWStateMachine.h */, - BC301A8019D6325600E64ADF /* SWStateMachine.m */, - BCBF525517B079FF00716200 /* Hotkeys */, - BCA7358F16E585D400CD4C74 /* Polling */, - BC5A978D180E5A66002ECD56 /* Services */, - BCFF230F177DD42E008759C4 /* Window Filtering */, - BCA7354A16E2AC3F00CD4C74 /* imageComparators.h */, - BCCA7ACE180896D100CE36E5 /* helpers.h */, - BCCA7ACF180896D100CE36E5 /* helpers.m */, - ); - name = Logic; - sourceTree = ""; - }; - BCA7358F16E585D400CD4C74 /* Polling */ = { - isa = PBXGroup; - children = ( - BC6B6D1A178DE8E5001D691D /* SWAPIEnabledWorker.h */, - BC6B6D1B178DE8E5001D691D /* SWAPIEnabledWorker.m */, - BCA7352016DAC6A900CD4C74 /* SWWindowWorker.h */, - BCA7352116DAC6A900CD4C74 /* SWWindowWorker.m */, - BCA7351916DAC5E200CD4C74 /* SWWindowListWorker.h */, - BCA7351A16DAC5E200CD4C74 /* SWWindowListWorker.m */, - ); - name = Polling; - sourceTree = ""; - }; - BCBF525517B079FF00716200 /* Hotkeys */ = { - isa = PBXGroup; - children = ( - BCBF524C17B079FA00716200 /* SWHotKey.h */, - BCBF524D17B079FA00716200 /* SWHotKey.m */, - ); - name = Hotkeys; - sourceTree = ""; - }; - BCCA7A4D18076AD700CE36E5 /* NNKit */ = { - isa = PBXGroup; - children = ( - BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */, - ); - name = NNKit; - sourceTree = ""; - }; - BCCA7A4F18076ADB00CE36E5 /* Products */ = { - isa = PBXGroup; - children = ( - BCCA7A5618076ADB00CE36E5 /* NNKit.framework */, - BCCA7A5818076ADB00CE36E5 /* NNKit Mac Tests.xctest */, - BCCA7A5A18076ADB00CE36E5 /* libNNKit.a */, - BCCA7A5C18076ADB00CE36E5 /* NNKit iOS Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - BCF357D5176CEE0200458AFF /* ReactiveCocoa */ = { - isa = PBXGroup; - children = ( - BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */, - ); - name = ReactiveCocoa; - sourceTree = ""; - }; - BCFB70201AA9110D00D99F4C /* Products */ = { - isa = PBXGroup; - children = ( - BCFB70271AA9110E00D99F4C /* ReactiveCocoa.framework */, - BCFB70291AA9110E00D99F4C /* ReactiveCocoa-MacTests.xctest */, - BCFB702B1AA9110E00D99F4C /* ReactiveCocoa.framework */, - BCFB702D1AA9110E00D99F4C /* ReactiveCocoa-iOSTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - BCFF230F177DD42E008759C4 /* Window Filtering */ = { - isa = PBXGroup; - children = ( - BCBC848D1882138B00E5B52E /* SWWindow+TweetbotQuirks.h */, - BCBC848E1882138B00E5B52E /* SWWindow+TweetbotQuirks.m */, - ); - name = "Window Filtering"; - sourceTree = ""; - }; - DCCCBA49A345244F314199A7 /* Pods */ = { - isa = PBXGroup; - children = ( - 566966697F19AA05013D724F /* Pods-Switch.debug.xcconfig */, - 889139ABCF76353CB055A23D /* Pods-Switch.release.xcconfig */, - 23C5A096931A640E810631DE /* Pods-Switch-SwitchTests.debug.xcconfig */, - 31E0F06E5DF1342924FFACC5 /* Pods-Switch-SwitchTests.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - BC275C0F18CFD3C900761247 /* relaunch */ = { - isa = PBXNativeTarget; - buildConfigurationList = BC275C2818CFD3CA00761247 /* Build configuration list for PBXNativeTarget "relaunch" */; - buildPhases = ( - BC275C0C18CFD3C900761247 /* Sources */, - BC275C0D18CFD3C900761247 /* Frameworks */, - BC275C0E18CFD3C900761247 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = relaunch; - productName = relaunch; - productReference = BC275C1018CFD3C900761247 /* relaunch */; - productType = "com.apple.product-type.tool"; - }; - BCA734DC16DAC53F00CD4C74 /* Switch */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCA7350E16DAC54000CD4C74 /* Build configuration list for PBXNativeTarget "Switch" */; - buildPhases = ( - 1CB401E39B937B2CAB4125C1 /* [CP] Check Pods Manifest.lock */, - BCA734D916DAC53F00CD4C74 /* Sources */, - BCA734DA16DAC53F00CD4C74 /* Frameworks */, - BCA734DB16DAC53F00CD4C74 /* Resources */, - BCDDB2BC178EF15B00C22E47 /* CopyFiles */, - 09C30F7B8432EE90C0A5EAA0 /* [CP] Embed Pods Frameworks */, - CFF945A4CDD95BF4C674EF7F /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - BCFB70321AA9114C00D99F4C /* PBXTargetDependency */, - BC275C2E18CFD78C00761247 /* PBXTargetDependency */, - BCCA7A5E18076AED00CE36E5 /* PBXTargetDependency */, - BC63AB9C175701DD00016B9E /* PBXTargetDependency */, - ); - name = Switch; - productName = Switch; - productReference = BCA734DD16DAC53F00CD4C74 /* Switch.app */; - productType = "com.apple.product-type.application"; - }; - BCA734FC16DAC54000CD4C74 /* SwitchTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BCA7351116DAC54000CD4C74 /* Build configuration list for PBXNativeTarget "SwitchTests" */; - buildPhases = ( - D5FC3174EAA94D5335B606F7 /* [CP] Check Pods Manifest.lock */, - BCA734F816DAC54000CD4C74 /* Sources */, - BCA734F916DAC54000CD4C74 /* Frameworks */, - BCA734FA16DAC54000CD4C74 /* Resources */, - BCF5C4AA19DB5F7800E59B62 /* CopyFiles */, - E7757EEB278E9CE9F20BA6B0 /* [CP] Embed Pods Frameworks */, - 325CC820A31A55BDD3144CAA /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - BCA7350216DAC54000CD4C74 /* PBXTargetDependency */, - ); - name = SwitchTests; - productName = SwitchTests; - productReference = BCA734FD16DAC54000CD4C74 /* SwitchTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BCA734D516DAC53F00CD4C74 /* Project object */ = { - isa = PBXProject; - attributes = { - CLASSPREFIX = NN; - LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0600; - ORGANIZATIONNAME = "Scott Perry"; - TargetAttributes = { - BCA734FC16DAC54000CD4C74 = { - TestTargetID = BCA734DC16DAC53F00CD4C74; - }; - }; - }; - buildConfigurationList = BCA734D816DAC53F00CD4C74 /* Build configuration list for PBXProject "Switch" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = BCA734D416DAC53F00CD4C74; - productRefGroup = BCA734DE16DAC53F00CD4C74 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = BC63AB95175701C200016B9E /* Products */; - ProjectRef = BC63AB94175701C200016B9E /* Haxcessibility.xcodeproj */; - }, - { - ProductGroup = BCCA7A4F18076ADB00CE36E5 /* Products */; - ProjectRef = BCCA7A4E18076ADB00CE36E5 /* NNKit.xcodeproj */; - }, - { - ProductGroup = BCFB70201AA9110D00D99F4C /* Products */; - ProjectRef = BCFB701F1AA9110D00D99F4C /* ReactiveCocoa.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - BCA734DC16DAC53F00CD4C74 /* Switch */, - BCA734FC16DAC54000CD4C74 /* SwitchTests */, - BC275C0F18CFD3C900761247 /* relaunch */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - BC63AB99175701C200016B9E /* Haxcessibility.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Haxcessibility.framework; - remoteRef = BC63AB98175701C200016B9E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCCA7A5618076ADB00CE36E5 /* NNKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = NNKit.framework; - remoteRef = BCCA7A5518076ADB00CE36E5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCCA7A5818076ADB00CE36E5 /* NNKit Mac Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "NNKit Mac Tests.xctest"; - remoteRef = BCCA7A5718076ADB00CE36E5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCCA7A5A18076ADB00CE36E5 /* libNNKit.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libNNKit.a; - remoteRef = BCCA7A5918076ADB00CE36E5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCCA7A5C18076ADB00CE36E5 /* NNKit iOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "NNKit iOS Tests.xctest"; - remoteRef = BCCA7A5B18076ADB00CE36E5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCFB70271AA9110E00D99F4C /* ReactiveCocoa.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = ReactiveCocoa.framework; - remoteRef = BCFB70261AA9110E00D99F4C /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCFB70291AA9110E00D99F4C /* ReactiveCocoa-MacTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "ReactiveCocoa-MacTests.xctest"; - remoteRef = BCFB70281AA9110E00D99F4C /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCFB702B1AA9110E00D99F4C /* ReactiveCocoa.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = ReactiveCocoa.framework; - remoteRef = BCFB702A1AA9110E00D99F4C /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - BCFB702D1AA9110E00D99F4C /* ReactiveCocoa-iOSTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "ReactiveCocoa-iOSTests.xctest"; - remoteRef = BCFB702C1AA9110E00D99F4C /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - BCA734DB16DAC53F00CD4C74 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BC275C3218CFD84000761247 /* relaunch in Resources */, - BC1B45BA190F37B400246529 /* Keyboard.icns in Resources */, - BCA734F116DAC53F00CD4C74 /* Credits.rtf in Resources */, - BCA734EB16DAC53F00CD4C74 /* InfoPlist.strings in Resources */, - BCA734F716DAC53F00CD4C74 /* MainMenu.xib in Resources */, - BC1B45B8190F379900246529 /* SWKeyboardPreferencesViewController.xib in Resources */, - BC948E2E180CFAA6008ED28A /* weave.png in Resources */, - BCCA7A641807894A00CE36E5 /* SWGeneralPreferencesViewController.xib in Resources */, - BC660D5B1796573200CE8333 /* Sparkle.dsa_pub.pem in Resources */, - BC1B46581911A77F00246529 /* SWAdvancedPreferencesViewController.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCA734FA16DAC54000CD4C74 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCA7350816DAC54000CD4C74 /* InfoPlist.strings in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 09C30F7B8432EE90C0A5EAA0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Switch/Pods-Switch-frameworks.sh", - "${PODS_ROOT}/Sparkle/Sparkle.framework", - "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework", - "${DWARF_DSYM_FOLDER_PATH}/Sparkle.framework.dSYM", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Switch/Pods-Switch-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 1CB401E39B937B2CAB4125C1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Switch-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 325CC820A31A55BDD3144CAA /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests-resources.sh", - "${PODS_ROOT}/MASPreferences/Framework/en.lproj/MASPreferencesWindow.xib", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MASPreferencesWindow.nib", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - CFF945A4CDD95BF4C674EF7F /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Switch/Pods-Switch-resources.sh", - "${PODS_ROOT}/MASPreferences/Framework/en.lproj/MASPreferencesWindow.xib", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MASPreferencesWindow.nib", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Switch/Pods-Switch-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - D5FC3174EAA94D5335B606F7 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Switch-SwitchTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - E7757EEB278E9CE9F20BA6B0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests-frameworks.sh", - "${PODS_ROOT}/Sparkle/Sparkle.framework", - "${PODS_ROOT}/Sparkle/Sparkle.framework.dSYM", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework", - "${DWARF_DSYM_FOLDER_PATH}/Sparkle.framework.dSYM", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Switch-SwitchTests/Pods-Switch-SwitchTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - BC275C0C18CFD3C900761247 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BC275C2C18CFD5AA00761247 /* SWTerminationListener.m in Sources */, - BC275C1518CFD3CA00761247 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCA734D916DAC53F00CD4C74 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCA734ED16DAC53F00CD4C74 /* main.m in Sources */, - BCEA426819DC668100C5A1D2 /* SWInterfaceController.m in Sources */, - BCA734F416DAC53F00CD4C74 /* SWAppDelegate.m in Sources */, - BCA7351B16DAC5E200CD4C74 /* SWWindowListWorker.m in Sources */, - BCA7351E16DAC61F00CD4C74 /* SWWindow.m in Sources */, - BCA7352216DAC6A900CD4C74 /* SWWindowWorker.m in Sources */, - BC19D3DA181436FB009CEC1F /* SWPreferencesService.m in Sources */, - BC1B45F3190F925900246529 /* NSScreen+SWAdditions.m in Sources */, - BCCA7AD0180896D100CE36E5 /* helpers.m in Sources */, - BC1B46571911A77F00246529 /* SWAdvancedPreferencesViewController.m in Sources */, - BCA7352516DAC93C00CD4C74 /* SWApplication.m in Sources */, - BCA7352F16DB239900CD4C74 /* SWWindowThumbnailView.m in Sources */, - BC5A9798180E5A6F002ECD56 /* SWStatusBarMenuService.m in Sources */, - BC6B6D13178DDBDA001D691D /* SWCoreWindowController.m in Sources */, - BC6B6D00178B711E001D691D /* NNMainThreadGuard.m in Sources */, - BCBC848F1882138B00E5B52E /* SWWindow+TweetbotQuirks.m in Sources */, - BCCA7A631807894A00CE36E5 /* SWGeneralPreferencesViewController.m in Sources */, - BC6B6D1C178DE8E5001D691D /* SWAPIEnabledWorker.m in Sources */, - BCA7353816E148EF00CD4C74 /* SWRoundedRectView.m in Sources */, - BC85E7B718DEA7C4001DF986 /* NSSet+SWChaining.m in Sources */, - BCA5E9A6187A21A9004D70EE /* SWSelector.m in Sources */, - BC662BA7186A42E4003CF66C /* SWWindowListService.m in Sources */, - BCA5E99818766FE1004D70EE /* SWWindowContentsService.m in Sources */, - BCBF525017B079FA00716200 /* SWHotKey.m in Sources */, - BCA7354316E1621400CD4C74 /* constants.m in Sources */, - BC15C903191F446800595EAB /* debugger.c in Sources */, - BC5A979B180E6949002ECD56 /* SWLoggingService.m in Sources */, - BC85E79B18DE8C73001DF986 /* NSLayoutConstraint+SWConstraintHelpers.m in Sources */, - BC097E6419DB87BA00B5F447 /* SWScrollControl.m in Sources */, - BCA8AD6A18AD61A00059F253 /* SWEventTap.m in Sources */, - BCA7354916E1F14D00CD4C74 /* SWSelectionBoxView.m in Sources */, - BC82B3F1183C48EE00FDD3B9 /* SWCoreWindowService.m in Sources */, - BC301A8119D6325600E64ADF /* SWStateMachine.m in Sources */, - BCA7354D16E2ED5000CD4C74 /* SWHUDView.m in Sources */, - BC1B45B7190F379900246529 /* SWKeyboardPreferencesViewController.m in Sources */, - BC63AB8F1755907D00016B9E /* SWHUDCollectionView.m in Sources */, - BC19D3DD18143AB1009CEC1F /* SWAccessibilityService.m in Sources */, - BC662BB2186A7662003CF66C /* SWWindowGroup.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BCA734F816DAC54000CD4C74 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BC9F0E0B19DA1DD300616BDB /* SWTestApplication.m in Sources */, - BCCE8CF918093037006B0059 /* SWGithubTests.m in Sources */, - BC097E6619DBDA0200B5F447 /* SWScrollControlTests.m in Sources */, - BCCE8CF31809266A006B0059 /* SWSafariTests.m in Sources */, - BC301A9019D6328100E64ADF /* SWStateMachineTests.m in Sources */, - BCCE8CF018091F75006B0059 /* SWTweetbotTests.m in Sources */, - BCA5E9AA187A3CCA004D70EE /* SWWindowListServiceTestSuperclass.m in Sources */, - BC0BBE8A1CE64224000AB84E /* SWXcodeTests.m in Sources */, - BC8E07AC188C890400C208D4 /* SWChromeTests.m in Sources */, - BC1B46401911A56100246529 /* SWIsolatorTests.m in Sources */, - BCCE8CEB18091A4F006B0059 /* NSWindow+NNScreenCapture.m in Sources */, - BC0BBE921CE64BD9000AB84E /* SWFinderTests.m in Sources */, - BCCE8CF51809297B006B0059 /* SWSheetTests.m in Sources */, - BC0BBE941CE65263000AB84E /* SWSanityTests.m in Sources */, - BCF5C4A019DB5B7A00E59B62 /* SWImageComparisonTests.m in Sources */, - BCA8AD5B18AC5E3F0059F253 /* SWWordTests.m in Sources */, - BCCE8CF718092CDF006B0059 /* SWPowerboxTests.m in Sources */, - BC3C78C4188262EB0021C244 /* SWMacVimTests.m in Sources */, - BCCE8CEE18091AFC006B0059 /* NSImage+NNFilesystem.m in Sources */, - BCBAD89F19D7CCD200891AD9 /* SWWindowListServiceTests.m in Sources */, - BCA5E9AD187A4020004D70EE /* SWDashTests.m in Sources */, - BCA5E9A8187A307F004D70EE /* SWSelectorTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - BC275C2E18CFD78C00761247 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BC275C0F18CFD3C900761247 /* relaunch */; - targetProxy = BC275C2D18CFD78C00761247 /* PBXContainerItemProxy */; - }; - BC63AB9C175701DD00016B9E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Haxcessibility; - targetProxy = BC63AB9B175701DD00016B9E /* PBXContainerItemProxy */; - }; - BCA7350216DAC54000CD4C74 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BCA734DC16DAC53F00CD4C74 /* Switch */; - targetProxy = BCA7350116DAC54000CD4C74 /* PBXContainerItemProxy */; - }; - BCCA7A5E18076AED00CE36E5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "NNKit Mac"; - targetProxy = BCCA7A5D18076AED00CE36E5 /* PBXContainerItemProxy */; - }; - BCFB70321AA9114C00D99F4C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "ReactiveCocoa-Mac"; - targetProxy = BCFB70311AA9114C00D99F4C /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - BCA734E916DAC53F00CD4C74 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - BCA734EA16DAC53F00CD4C74 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - BCA734EF16DAC53F00CD4C74 /* Credits.rtf */ = { - isa = PBXVariantGroup; - children = ( - BCA734F016DAC53F00CD4C74 /* en */, - ); - name = Credits.rtf; - sourceTree = ""; - }; - BCA734F516DAC53F00CD4C74 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - BCA734F616DAC53F00CD4C74 /* en */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; - BCA7350616DAC54000CD4C74 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - BCA7350716DAC54000CD4C74 /* en */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - BCF5C4A119DB5E0900E59B62 /* 1376.ar */ = { - isa = PBXVariantGroup; - children = ( - BCF5C4A219DB5E0900E59B62 /* en */, - ); - name = 1376.ar; - sourceTree = ""; - }; - BCF5C4A319DB5E0900E59B62 /* 5891.ar */ = { - isa = PBXVariantGroup; - children = ( - BCF5C4A419DB5E0900E59B62 /* en */, - ); - name = 5891.ar; - sourceTree = ""; - }; - BCF5C4A519DB5E0900E59B62 /* 5904.ar */ = { - isa = PBXVariantGroup; - children = ( - BCF5C4A619DB5E0900E59B62 /* en */, - ); - name = 5904.ar; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - BC275C1A18CFD3CA00761247 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "relaunch/relaunch-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - BC275C1B18CFD3CA00761247 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - ENABLE_NS_ASSERTIONS = NO; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "relaunch/relaunch-Prefix.pch"; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Release; - }; - BCA7350C16DAC54000CD4C74 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = NO; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = NO; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GENERATE_PROFILING_CODE = YES; - LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks $(inherited)"; - MACOSX_DEPLOYMENT_TARGET = 10.14; - OTHER_CODE_SIGN_FLAGS = "--deep"; - SDKROOT = macosx; - }; - name = Debug; - }; - BCA7350D16DAC54000CD4C74 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = NO; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = ""; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = NO; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks $(inherited)"; - LLVM_LTO = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - OTHER_CODE_SIGN_FLAGS = "--deep"; - SDKROOT = macosx; - }; - name = Release; - }; - BCA7350F16DAC54000CD4C74 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 566966697F19AA05013D724F /* Pods-Switch.debug.xcconfig */; - buildSettings = { - CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; - CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; - CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; - CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_RECEIVER_WEAK = YES; - CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/Frameworks", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Switch/Switch-Prefix.pch"; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = NO; - GCC_WARN_SHADOW = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = NO; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "Switch/Switch-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(BUILT_PRODUCTS_DIR)", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - BCA7351016DAC54000CD4C74 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 889139ABCF76353CB055A23D /* Pods-Switch.release.xcconfig */; - buildSettings = { - CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; - CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; - CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; - CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES; - CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_RECEIVER_WEAK = YES; - CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; - CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SRCROOT)/Frameworks", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Switch/Switch-Prefix.pch"; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = NO; - GCC_WARN_SHADOW = YES; - GCC_WARN_SIGN_COMPARE = YES; - GCC_WARN_STRICT_SELECTOR_MATCH = NO; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNKNOWN_PRAGMAS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = "Switch/Switch-Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(BUILT_PRODUCTS_DIR)", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; - BCA7351216DAC54000CD4C74 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 23C5A096931A640E810631DE /* Pods-Switch-SwitchTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Switch.app/Contents/MacOS/Switch"; - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - "$(inherited)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Switch/Switch-Prefix.pch"; - INFOPLIST_FILE = "SwitchTests/SwitchTests-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(BUILT_PRODUCTS_DIR)", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUNDLE_LOADER)"; - }; - name = Debug; - }; - BCA7351316DAC54000CD4C74 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 31E0F06E5DF1342924FFACC5 /* Pods-Switch-SwitchTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Switch.app/Contents/MacOS/Switch"; - CODE_SIGN_IDENTITY = ""; - COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - "$(inherited)", - ); - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Switch/Switch-Prefix.pch"; - INFOPLIST_FILE = "SwitchTests/SwitchTests-Info.plist"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(BUILT_PRODUCTS_DIR)", - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUNDLE_LOADER)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - BC275C2818CFD3CA00761247 /* Build configuration list for PBXNativeTarget "relaunch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BC275C1A18CFD3CA00761247 /* Debug */, - BC275C1B18CFD3CA00761247 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCA734D816DAC53F00CD4C74 /* Build configuration list for PBXProject "Switch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCA7350C16DAC54000CD4C74 /* Debug */, - BCA7350D16DAC54000CD4C74 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCA7350E16DAC54000CD4C74 /* Build configuration list for PBXNativeTarget "Switch" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCA7350F16DAC54000CD4C74 /* Debug */, - BCA7351016DAC54000CD4C74 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BCA7351116DAC54000CD4C74 /* Build configuration list for PBXNativeTarget "SwitchTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BCA7351216DAC54000CD4C74 /* Debug */, - BCA7351316DAC54000CD4C74 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BCA734D516DAC53F00CD4C74 /* Project object */; -} diff --git a/Switch.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Switch.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8..00000000 --- a/Switch.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/B10416B6-59AF-46B0-8C59-971F2DDC08B3.plist b/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/B10416B6-59AF-46B0-8C59-971F2DDC08B3.plist deleted file mode 100644 index 03e32c00..00000000 --- a/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/B10416B6-59AF-46B0-8C59-971F2DDC08B3.plist +++ /dev/null @@ -1,95 +0,0 @@ - - - - - classNames - - SWImageComparisonTests - - testBitmapContextComparisonDifferent - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.01951 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testBitmapContextComparisonSame - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.016262 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testCGDataProviderComparisonDifferent - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.0074387 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testCGDataProviderComparisonSame - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.0074524 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testTIFFComparisonDifferent - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.0094832 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testTIFFComparisonSame - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.011315 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - - SWWindowListServiceTests - - testEmpty - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 3.9768e-05 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - testNormalDay - - com.apple.XCTPerformanceMetric_WallClockTime - - baselineAverage - 0.00070195 - baselineIntegrationDisplayName - 10-01-2014, 0:24:03 - - - - - - diff --git a/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/Info.plist b/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/Info.plist deleted file mode 100644 index 515bd140..00000000 --- a/Switch.xcodeproj/xcshareddata/xcbaselines/BCA734FC16DAC54000CD4C74.xcbaseline/Info.plist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - runDestinationsByUUID - - B10416B6-59AF-46B0-8C59-971F2DDC08B3 - - localComputer - - busSpeedInMHz - 100 - cpuCount - 1 - cpuKind - Intel Core i7 - cpuSpeedInMHz - 2000 - logicalCPUCoresPerPackage - 4 - modelCode - MacBookAir5,1 - physicalCPUCoresPerPackage - 2 - platformIdentifier - com.apple.platform.macosx - - targetArchitecture - x86_64 - - - - diff --git a/Switch.xcodeproj/xcshareddata/xcschemes/Switch.xcscheme b/Switch.xcodeproj/xcshareddata/xcschemes/Switch.xcscheme deleted file mode 100644 index bd2e3d19..00000000 --- a/Switch.xcodeproj/xcshareddata/xcschemes/Switch.xcscheme +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Switch.xcworkspace/contents.xcworkspacedata b/Switch.xcworkspace/contents.xcworkspacedata index 16abddd8..4d10c716 100644 --- a/Switch.xcworkspace/contents.xcworkspacedata +++ b/Switch.xcworkspace/contents.xcworkspacedata @@ -2,9 +2,28 @@ + location = "group:README.md"> + location = "group:Switch/Switch.xcodeproj"> + + + + + + + + + + + + diff --git a/Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Switch.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Frameworks/NNKit/NNKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Switch.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Switch/.swiftlint.yml b/Switch/.swiftlint.yml new file mode 100644 index 00000000..f9c02daa --- /dev/null +++ b/Switch/.swiftlint.yml @@ -0,0 +1,4 @@ +disabled_rules: + - todo +trailing_comma: + mandatory_comma: true diff --git a/Switch/LICENSE b/Switch/LICENSE new file mode 100644 index 00000000..4c58505c --- /dev/null +++ b/Switch/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Unless otherwise noted, all files in this directory tree are copyright © 2013-2020 Scott Perry + +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. diff --git a/Switch/NNMainThreadGuard.m b/Switch/NNMainThreadGuard.m deleted file mode 100644 index 7812f9b6..00000000 --- a/Switch/NNMainThreadGuard.m +++ /dev/null @@ -1,103 +0,0 @@ -// -// NNMainThreadGuard.m -// -// Mostly taken from the commercial iOS PDF framework http://pspdfkit.com. -// Copyright © 2013 Peter Steinberger. All rights reserved. -// -// 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. -// -// You should only use this in debug builds. It doesn't use private API, but I wouldn't ship it. -// -// Modified from https://gist.github.com/steipete/5664345/391a9b069307448d5cc573f1cd077ad7e1f949bc for use with non-touch Cocoa. -// - -#if DEBUG - -#import -#import - -// Compile-time selector checks. -#if DEBUG -#define PROPERTY(propName) NSStringFromSelector(@selector(propName)) -#else -#define PROPERTY(propName) @#propName -#endif - -// A better assert. NSAssert is too runtime dependant, and assert() doesn't log. -// http://www.mikeash.com/pyblog/friday-qa-2013-05-03-proper-use-of-asserts.html -// Accepts both: -// - NNAssert(x > 0); -// - NNAssert(y > 3, @"Bad value for y"); -#define NNAssert(expression, ...) \ -do { if(!(expression)) { \ -NSLog(@"%@", [NSString stringWithFormat: @"Assertion failure: %s in %s on line %s:%d. %@", #expression, __PRETTY_FUNCTION__, __FILE__, __LINE__, [NSString stringWithFormat:@"" __VA_ARGS__]]); \ -abort(); }} while(0) - -/////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - Helper for Swizzling - -BOOL NNReplaceMethodWithBlock(Class c, SEL origSEL, SEL newSEL, id block) { - NNAssert(c && origSEL && newSEL && block); - Method origMethod = class_getInstanceMethod(c, origSEL); - const char *encoding = method_getTypeEncoding(origMethod); - - // Add the new method. - IMP impl = imp_implementationWithBlock(block); - if (!class_addMethod(c, newSEL, impl, encoding)) { - NSLog(@"Failed to add method: %@ on %@", NSStringFromSelector(newSEL), c); - return NO; - } else { - // Ensure the new selector has the same parameters as the existing selector. - Method newMethod = class_getInstanceMethod(c, newSEL); - NNAssert(strcmp(method_getTypeEncoding(origMethod), method_getTypeEncoding(newMethod)) == 0, @"Encoding must be the same."); - - // If original doesn't implement the method we want to swizzle, create it. - if (class_addMethod(c, origSEL, method_getImplementation(newMethod), encoding)) { - class_replaceMethod(c, newSEL, method_getImplementation(origMethod), encoding); - }else { - method_exchangeImplementations(origMethod, newMethod); - } - } - return YES; -} - -/////////////////////////////////////////////////////////////////////////////////////////// -#pragma mark - Tracks down calls into Cocoa from a Thread other than Main - -static void AssertIfNotMainThread(void) { - NNAssert([[NSThread currentThread] isMainThread], @"ERROR: All calls into Cocoa need to happen on the main thread. You have a bug in your code. Use dispatch_async(dispatch_get_main_queue(), ^{ ... }); if you're unsure what thread you're in.\n\nBreak on AssertIfNotMainThread to find out where.\n\nStacktrace: %@", [NSThread callStackSymbols]); -} - -// This installs a small guard that checks for the most common threading-errors in Cocoa. -// This won't really slow down performance but still only is compiled in DEBUG versions of the application. -// @note No private API is used here. -__attribute__((constructor)) static void NNUIKitMainThreadGuard(void) { - @autoreleasepool { - - NSString *selStr = PROPERTY(setNeedsDisplayInRect:); - SEL selector = NSSelectorFromString(selStr); - SEL newSelector = NSSelectorFromString([NSString stringWithFormat:@"nn_%@", selStr]); - - NNReplaceMethodWithBlock(NSView.class, selector, newSelector, ^(__unsafe_unretained NSView *_self, CGRect r) { - AssertIfNotMainThread(); - ((void ( *)(id, SEL, CGRect))objc_msgSend)(_self, newSelector, r); - }); - - - for (selStr in @[PROPERTY(setNeedsLayout:), PROPERTY(setNeedsDisplay:)]) { - selector = NSSelectorFromString(selStr); - newSelector = NSSelectorFromString([NSString stringWithFormat:@"nn_%@", selStr]); - - NNReplaceMethodWithBlock(NSView.class, selector, newSelector, ^(__unsafe_unretained NSView *_self, BOOL b) { - AssertIfNotMainThread(); - ((void ( *)(id, SEL, BOOL))objc_msgSend)(_self, newSelector, b); - }); - } - } -} - -#endif diff --git a/Switch/NSLayoutConstraint+SWConstraintHelpers.h b/Switch/NSLayoutConstraint+SWConstraintHelpers.h deleted file mode 100644 index 3aaa68d6..00000000 --- a/Switch/NSLayoutConstraint+SWConstraintHelpers.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSLayoutConstraint+SWConstraintHelpers.h -// Switch -// -// Created by Scott Perry on 03/22/14. -// Copyright © 2014 Scott Perry. -// -// 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 - -@interface NSLayoutConstraint (SWConstraintHelpers) - -+ (NSArray *)sw_constraintsCenteringView:(NSView *)view toView:(NSView *)anchor; - -@end diff --git a/Switch/NSLayoutConstraint+SWConstraintHelpers.m b/Switch/NSLayoutConstraint+SWConstraintHelpers.m deleted file mode 100644 index 0addf9b8..00000000 --- a/Switch/NSLayoutConstraint+SWConstraintHelpers.m +++ /dev/null @@ -1,34 +0,0 @@ -// -// NSLayoutConstraint+SWConstraintHelpers.m -// Switch -// -// Created by Scott Perry on 03/22/14. -// Copyright © 2014 Scott Perry. -// -// 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 "NSLayoutConstraint+SWConstraintHelpers.h" - -@implementation NSLayoutConstraint (SWConstraintHelpers) - -+ (NSArray *)sw_constraintsCenteringView:(NSView *)view toView:(NSView *)anchor; -{ - return @[ - [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:anchor attribute:NSLayoutAttributeCenterX - multiplier:1.f constant:0.f], - - [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:anchor attribute:NSLayoutAttributeCenterY - multiplier:1.f constant:0.f] - ]; -} - -@end diff --git a/Switch/NSScreen+SWAdditions.h b/Switch/NSScreen+SWAdditions.h deleted file mode 100644 index 889d95fd..00000000 --- a/Switch/NSScreen+SWAdditions.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSScreen+SWAdditions.h -// Switch -// -// Created by Scott Perry on 04/29/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@interface NSScreen (SWAdditions) - -+ (CGFloat)sw_totalScreenHeight; - -- (CGDirectDisplayID)sw_screenNumber; - -@end diff --git a/Switch/NSScreen+SWAdditions.m b/Switch/NSScreen+SWAdditions.m deleted file mode 100644 index 9212eee0..00000000 --- a/Switch/NSScreen+SWAdditions.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// NSScreen+SWAdditions.m -// Switch -// -// Created by Scott Perry on 04/29/14. -// Copyright © 2014 Scott Perry. -// -// 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 "NSScreen+SWAdditions.h" - - -@implementation NSScreen (SWAdditions) - -+ (CGFloat)sw_totalScreenHeight; -{ - return [[[NSScreen screens] nn_reduce:^id(id accumulator, id item) { - if (!accumulator) { accumulator = @(0.0); } - CGRect screenFrame = [item private_sw_absoluteFrame]; - CGFloat screenHeight = screenFrame.origin.y + screenFrame.size.height; - if ([accumulator floatValue] < screenFrame.origin.y + screenFrame.size.height) { - accumulator = @(screenHeight); - } - return accumulator; - }] floatValue]; -} - -- (CGDirectDisplayID)sw_screenNumber; -{ - NSDictionary *description = self.deviceDescription; - return [description[@"NSScreenNumber"] unsignedIntValue]; -} - -- (CGRect)private_sw_absoluteFrame; -{ - CGPoint offset = CGPointZero; - for (NSScreen *screen in [NSScreen screens]) { - if (screen.frame.origin.x < offset.x) { - offset.x = screen.frame.origin.x; - } - if (screen.frame.origin.y < offset.y) { - offset.y = screen.frame.origin.y; - } - } - - CGRect result = self.frame; - result.origin.x -= offset.x; - result.origin.y -= offset.y; - return result; -} - -@end diff --git a/Switch/NSSet+SWChaining.h b/Switch/NSSet+SWChaining.h deleted file mode 100644 index 64973c6b..00000000 --- a/Switch/NSSet+SWChaining.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSSet+SWChaining.h -// Switch -// -// Created by Scott Perry on 03/22/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@interface NSSet (SWChaining) - -- (instancetype)sw_unionSet:(NSSet *)otherSet; -- (instancetype)sw_minusSet:(NSSet *)otherSet; -- (instancetype)sw_intersectSet:(NSSet *)otherSet; - -@end diff --git a/Switch/NSSet+SWChaining.m b/Switch/NSSet+SWChaining.m deleted file mode 100644 index 489fd8e5..00000000 --- a/Switch/NSSet+SWChaining.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// NSSet+SWChaining.m -// Switch -// -// Created by Scott Perry on 03/22/14. -// Copyright © 2014 Scott Perry. -// -// 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 "NSSet+SWChaining.h" - - -@implementation NSSet (SWChaining) - -- (instancetype)sw_unionSet:(NSSet *)otherSet; -{ - NSMutableSet *result = [self mutableCopy]; - [result unionSet:otherSet]; - return result; -} - -- (instancetype)sw_minusSet:(NSSet *)otherSet; -{ - NSMutableSet *result = [self mutableCopy]; - [result minusSet:otherSet]; - return result; -} - -- (instancetype)sw_intersectSet:(NSSet *)otherSet; -{ - NSMutableSet *result = [self mutableCopy]; - [result intersectSet:otherSet]; - return result; -} - -@end diff --git a/Switch/Resources/Keyboard.icns b/Switch/Resources/Keyboard.icns deleted file mode 100644 index 9056fd4c..00000000 Binary files a/Switch/Resources/Keyboard.icns and /dev/null differ diff --git a/Switch/Resources/weave.png b/Switch/Resources/weave.png deleted file mode 100644 index 8596df41..00000000 Binary files a/Switch/Resources/weave.png and /dev/null differ diff --git a/Switch/SWAPIEnabledWorker.h b/Switch/SWAPIEnabledWorker.h deleted file mode 100644 index a053e970..00000000 --- a/Switch/SWAPIEnabledWorker.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// SWAPIEnabledWorker.h -// Switch -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - - -extern NSString *SWAXAPIEnabledKey; - - -@interface SWAPIEnabledWorker : NNPollingObject - -+ (BOOL)isAPIEnabled; - -- (instancetype)init; - -@end diff --git a/Switch/SWAPIEnabledWorker.m b/Switch/SWAPIEnabledWorker.m deleted file mode 100644 index c064f770..00000000 --- a/Switch/SWAPIEnabledWorker.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// SWAPIEnabledWorker.m -// Switch -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWAPIEnabledWorker.h" - -#import -#import - - -NSString *SWAXAPIEnabledKey = @"AXAPIEnabled"; - - -@interface SWAPIEnabledWorker () - -@property (nonatomic, assign, readwrite) BOOL APIEnabled; - -@end - - -@implementation SWAPIEnabledWorker - -#pragma mark - Initialization - -- (instancetype)init; -{ - if (!(self = [super initWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)])) { return nil; } - - _APIEnabled = [[self class] isAPIEnabled]; - self.interval = 0.25; - - return self; -} - -#pragma mark - NNPollingObject - -- (void)main; -{ - BOOL enabled = [[self class] isAPIEnabled]; - if (enabled != self.APIEnabled) { - self.APIEnabled = enabled; - [self postNotification:@{ SWAXAPIEnabledKey : @(enabled) }]; - } -} - -#pragma mark - SWAPIEnabledWorker - -+ (BOOL)isAPIEnabled; -{ - return (BOOL)AXIsProcessTrustedWithOptions(nil); -} - -@end diff --git a/Switch/SWAccessibilityService.h b/Switch/SWAccessibilityService.h deleted file mode 100644 index c39efc32..00000000 --- a/Switch/SWAccessibilityService.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// SWAccessibilityService.h -// Switch -// -// Created by Scott Perry on 10/20/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@class SWWindow; - - -@interface SWAccessibilityService : NNService - -- (void)checkAPI; - -- (void)raiseWindow:(SWWindow *)window completion:(void(^)(NSError *))completionBlock; -- (void)closeWindow:(SWWindow *)window completion:(void(^)(NSError *))completionBlock; - -@end diff --git a/Switch/SWAccessibilityService.m b/Switch/SWAccessibilityService.m deleted file mode 100644 index 7250cc0f..00000000 --- a/Switch/SWAccessibilityService.m +++ /dev/null @@ -1,272 +0,0 @@ -// -// SWAccessibilityService.m -// Switch -// -// Created by Scott Perry on 10/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWAccessibilityService.h" - -#import -#import -#import - -#import "NSScreen+SWAdditions.h" -#import "SWAPIEnabledWorker.h" -#import "SWAppDelegate.h" -#import "SWApplication.h" -#import "SWWindow.h" -#import "SWWindowGroup.h" - - -@interface SWAccessibilityService () - -@property (nonatomic, copy) NSSet *windows; -@property (nonatomic, strong) SWAPIEnabledWorker *worker; -@property (nonatomic, strong, readonly) dispatch_queue_t haxQueue; - -@end - - -@implementation SWAccessibilityService - -#pragma mark - Initialization - -- (instancetype)init; -{ - BailUnless(self = [super init], nil); - - _haxQueue = dispatch_queue_create("haxQueue", DISPATCH_QUEUE_SERIAL); - - return self; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -- (void)startService; -{ - [super startService]; - - [self checkAPI]; -} - -#pragma mark - SWAccessibilityService - -- (void)setWorker:(SWAPIEnabledWorker *)worker; -{ - if (worker == _worker) { - return; - } - if (_worker) { - [[NSNotificationCenter defaultCenter] removeObserver:self name:SWAPIEnabledWorker.notificationName object:_worker]; - } - if (worker) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:NNSelfSelector1(private_accessibilityAPIAvailabilityChangedNotification:) name:SWAPIEnabledWorker.notificationName object:self.worker]; - } - _worker = worker; -} - -- (void)checkAPI; -{ - if (![SWAPIEnabledWorker isAPIEnabled]) { - self.worker = [SWAPIEnabledWorker new]; - - AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{ (__bridge NSString *)kAXTrustedCheckOptionPrompt : @YES }); - } -} - -- (void)raiseWindow:(SWWindow *)window completion:(void (^)(NSError *))completionBlock; -{ - dispatch_async(self.haxQueue, ^{ - // If sending events to Switch itself, we have to use the main thread! - if ([window.application isLiveApplication]) { - dispatch_sync(dispatch_get_main_queue(), ^{ - [self _raiseWindow:window completion:completionBlock]; - }); - } else { - [self _raiseWindow:window completion:completionBlock]; - } - }); -} - -- (void)closeWindow:(SWWindow *)window completion:(void (^)(NSError *))completionBlock; -{ - dispatch_async(self.haxQueue, ^{ - // If sending events to Switch itself, we have to use the main thread! - if ([window.application isLiveApplication]) { - dispatch_sync(dispatch_get_main_queue(), ^{ - [self _closeWindow:window completion:completionBlock]; - }); - } else { - [self _closeWindow:window completion:completionBlock]; - } - }); -} - -#pragma mark - Internal - -- (void)private_accessibilityAPIAvailabilityChangedNotification:(NSNotification *)notification; -{ - BOOL accessibilityEnabled = [notification.userInfo[SWAXAPIEnabledKey] boolValue]; - - SWLog(@"Accessibility API is %@abled", accessibilityEnabled ? @"en" : @"dis"); - - if (accessibilityEnabled) { - self.worker = nil; - [(SWAppDelegate *)NSApplication.sharedApplication.delegate relaunch:nil]; - } -} - -- (void)_raiseWindow:(SWWindow *)window completion:(void (^)(NSError *))completionBlock; -{ - if (!completionBlock) { - completionBlock = ^(NSError *error){}; - } - - if (!window) { - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(nil); - }); - return; - } - - NSError *error = nil; - HAXWindow *haxWindow = [self _haxWindowForWindow:window]; - if (!haxWindow) { - NSString *errorString = [NSString stringWithFormat:@"Failed to get accessibility object for window %@", window]; - SWLog(@"%@", errorString); - error = [NSError errorWithDomain:@"SWAccessibilityServiceDomain" code:__LINE__ userInfo:@{NSLocalizedDescriptionKey : errorString}]; - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - - SWTimeTask(SWCodeBlock({ - // First, raise the window - if (![haxWindow performAction:(__bridge NSString *)kAXRaiseAction error:&error]) { - SWLog(@"Raise operation for %@ failed after %.3fs: %@", window, [[NSDate date] timeIntervalSinceDate:start], error); - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - - // Then raise the application (if it's not already topmost) - if (![window.application isActiveApplication]) { - NSRunningApplication *runningApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:window.application.pid]; - if (![runningApplication activateWithOptions:NSApplicationActivateIgnoringOtherApps]) { - NSString *errorString = [NSString stringWithFormat:@"Raise operation for %@ failed.", window.application]; - SWLog(@"%@", errorString); - error = [NSError errorWithDomain:@"SWAccessibilityServiceDomain" code:__LINE__ userInfo:@{NSLocalizedDescriptionKey : errorString}]; - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - } - }), @"Raise operation for %@", window); - - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(nil); - }); -} - -- (void)_closeWindow:(SWWindow *)window completion:(void (^)(NSError *))completionBlock; -{ - if (!completionBlock) { - completionBlock = ^(NSError *error){}; - } - - if (!window) { - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(nil); - }); - return; - } - - NSError *error = nil; - HAXWindow *haxWindow = [self _haxWindowForWindow:window]; - if (!Check(haxWindow)) { - NSString *errorString = [NSString stringWithFormat:@"Failed to get accessibility object for window %@", window]; - SWLog(@"%@", errorString); - error = [NSError errorWithDomain:@"SWAccessibilityServiceDomain" code:__LINE__ userInfo:@{NSLocalizedDescriptionKey : errorString}]; - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - - SWTimeTask(SWCodeBlock({ - HAXElement *element = [haxWindow elementOfClass:[HAXElement class] forKey:(__bridge NSString *)kAXCloseButtonAttribute error:&error]; - if (!element) { - SWLog(@"Couldn't get close button for %@ window %@ after %.3fs: %@", window.application.name, window, [[NSDate date] timeIntervalSinceDate:start], error); - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - - if (![element performAction:(__bridge NSString *)kAXPressAction error:&error]) { - SWLog(@"Closing %@ window %@ failed after %.3fs: %@", window.application.name, window, [[NSDate date] timeIntervalSinceDate:start], error); - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(error); - }); - return; - } - }), @"Closing %@ window %@", window.application.name, window); - - dispatch_async(dispatch_get_main_queue(), ^{ - completionBlock(nil); - }); -} - -- (HAXWindow *)_haxWindowForWindow:(SWWindow *)window; -{ - // If window is a group, the frame will be calculated incorrectly, and no accessibility object for the window will be found! - while (![window isMemberOfClass:[SWWindow class]]) { - if ([window isKindOfClass:[SWWindowGroup class]]) { - window = [(SWWindowGroup *)window mainWindow]; - } else { - // TODO: If I had nothing but time, I'd trap on launch if SWWindow had any other subclasses but ain't nobody got time for that, maybe if this project ever has more than one developer. - DebugBreak(); - break; - } - } - - HAXApplication *haxApplication = [HAXApplication applicationWithPID:window.application.pid]; - BailUnless(haxApplication, nil); - - return [[haxApplication windows] nn_reduce:^id(id accumulator, HAXWindow *haxWindow){ - NSString *haxTitle = haxWindow.title; - - // This should ultimately be flipped in Haxcessibility, per #89. - CGRect haxFrame = haxWindow.carbonFrame; - haxFrame.origin.y = NSScreen.sw_totalScreenHeight - (haxFrame.origin.y + haxFrame.size.height); - - BOOL framesMatch = NNCGRectsEqual(window.frame, haxFrame); - // AX will return an empty string when CG returns nil/unset! - BOOL namesMatch = (window.name.length == 0 && haxTitle.length == 0) || [window.name isEqualToString:haxTitle]; - - // For some reason, the window names for Dash have been seen to differ. - if (framesMatch && (!accumulator || namesMatch)) { - return haxWindow; - } - - return accumulator; - }]; -} - -@end diff --git a/Switch/SWAdvancedPreferencesViewController.h b/Switch/SWAdvancedPreferencesViewController.h deleted file mode 100644 index 257f3fc3..00000000 --- a/Switch/SWAdvancedPreferencesViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// SWAdvancedPreferencesViewController.h -// Switch -// -// Created by Scott Perry on 04/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 "MASPreferencesViewController.h" - - -@interface SWAdvancedPreferencesViewController : NSViewController - -@end diff --git a/Switch/SWAdvancedPreferencesViewController.m b/Switch/SWAdvancedPreferencesViewController.m deleted file mode 100644 index b41b05a6..00000000 --- a/Switch/SWAdvancedPreferencesViewController.m +++ /dev/null @@ -1,103 +0,0 @@ -// -// SWAdvancedPreferencesViewController.m -// Switch -// -// Created by Scott Perry on 04/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWAdvancedPreferencesViewController.h" - -#import "SWPreferencesService.h" - - -@interface SWAdvancedPreferencesViewController () - -@property (nonatomic, weak) IBOutlet NSButton *multimonBox; -@property (nonatomic, weak) IBOutlet NSButton *groupByMonBox; -@property (nonatomic, weak) IBOutlet NSButton *statusItemBox; - -@property (nonatomic, weak) IBOutlet NSTextField *groupByMonLabel; - -@end - - -@implementation SWAdvancedPreferencesViewController - -- (void)viewWillAppear; -{ - NSButton *multimonBox = self.multimonBox; - multimonBox.state = [SWPreferencesService sharedService].multimonInterface ? NSOnState : NSOffState; - - NSButton *groupByMonBox = self.groupByMonBox; - groupByMonBox.state = [SWPreferencesService sharedService].multimonGroupByMonitor ? NSOnState : NSOffState; - - NSButton *statusItemBox = self.statusItemBox; - statusItemBox.state = [SWPreferencesService sharedService].showStatusItem ? NSOnState : NSOffState; - - [self updateInterface]; -} - -#pragma mark - MASPreferencesViewController - -- (NSString *)identifier -{ - return NSStringFromClass([self class]); -} - -- (NSImage *)toolbarItemImage -{ - return [NSImage imageNamed:NSImageNameAdvanced]; -} - -- (NSString *)toolbarItemLabel -{ - return NSLocalizedString(@"Advanced", @"Toolbar item name for the Advanced preference pane"); -} - -- (BOOL)hasResizableWidth; -{ - return NO; -} - -- (BOOL)hasResizableHeight; -{ - return NO; -} - -#pragma mark - IBAction - -- (IBAction)multimonChanged:(NSButton *)sender; -{ - [SWPreferencesService sharedService].multimonInterface = (sender.state == NSOnState); - [self updateInterface]; -} - -- (void)updateInterface; -{ - NSButton *multimonBox = self.multimonBox; - NSButton *groupByMonBox = self.groupByMonBox; - NSTextField *groupByMonLabel = self.groupByMonLabel; - - groupByMonLabel.textColor = (multimonBox.state == NSOnState) ? [NSColor disabledControlTextColor] : [[NSColor disabledControlTextColor] colorWithAlphaComponent:0.5] ; - groupByMonBox.enabled = (multimonBox.state == NSOnState); - groupByMonBox.state = (multimonBox.state == NSOnState && [SWPreferencesService sharedService].multimonGroupByMonitor) ? NSOnState : NSOffState; -} - -- (IBAction)groupByMonChanged:(NSButton *)sender; -{ - [SWPreferencesService sharedService].multimonGroupByMonitor = (sender.state == NSOnState); -} - -- (IBAction)statusItemChanged:(NSButton *)sender; -{ - [SWPreferencesService sharedService].showStatusItem = (sender.state == NSOnState); -} - -@end diff --git a/Switch/SWAdvancedPreferencesViewController.xib b/Switch/SWAdvancedPreferencesViewController.xib deleted file mode 100644 index cc5590ce..00000000 --- a/Switch/SWAdvancedPreferencesViewController.xib +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Switch/SWAppDelegate.h b/Switch/SWAppDelegate.h deleted file mode 100644 index 5e5a6560..00000000 --- a/Switch/SWAppDelegate.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// SWAppDelegate.h -// Switch -// -// Created by Scott Perry on 02/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface SWAppDelegate : NSObject - -- (void)relaunch:(id)sender; -- (IBAction)showPreferences:(id)sender; - -@end diff --git a/Switch/SWAppDelegate.m b/Switch/SWAppDelegate.m deleted file mode 100644 index 2e517037..00000000 --- a/Switch/SWAppDelegate.m +++ /dev/null @@ -1,80 +0,0 @@ -// -// SWAppDelegate.m -// Switch -// -// Created by Scott Perry on 02/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWAppDelegate.h" - -#import - -#import "SWPreferencesService.h" -#import "SWWindowListService.h" - - -@interface SWAppDelegate () -@end - - -@implementation SWAppDelegate - -#pragma mark - NSApplicationDelegate - -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; -{ - [[NNServiceManager sharedManager] registerAllPossibleServices]; - - NSURL *feedURL = [NSURL URLWithString:[SWPreferencesService sharedService].appcastURL]; - if (!feedURL) { - [SWPreferencesService sharedService].appcastURL = nil; - feedURL = [NSURL URLWithString:[SWPreferencesService sharedService].appcastURL]; - } - Assert(feedURL); - [SUUpdater sharedUpdater].feedURL = feedURL; - - SWLog(@"Launched %@ %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:(__bridge id)kCFBundleNameKey], [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); -} - -- (void)relaunch:(id)sender; -{ - // TODO: This should pop up a "please restart Switch" dialog. It sucks, but it's the best we can do in this situation. - void (^failureBlock)() = ^{}; - - NSString *launcherSource = [[NSBundle bundleForClass:[self class]] pathForResource:@"relaunch" ofType:nil]; - BailWithBlockUnless(launcherSource, failureBlock); - - NSString *launcherTarget = [NSTemporaryDirectory() stringByAppendingPathComponent:[launcherSource lastPathComponent]]; - NSString *appPath = [[NSBundle mainBundle] bundlePath]; - NSString *processID = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]; - - NSError *error = NULL; - BOOL success = YES; - success = [[NSFileManager defaultManager] removeItemAtPath:launcherTarget error:&error]; - if (!success) { - // Code 4: "The operation couldn’t be completed. No such file or directory" - BailWithBlockUnless(error.code == 4, failureBlock); - } - - success = [[NSFileManager defaultManager] copyItemAtPath:launcherSource toPath:launcherTarget error:&error]; - BailWithBlockUnless(success, failureBlock); - - [NSTask launchedTaskWithLaunchPath:launcherTarget arguments:@[appPath, processID]]; - [NSApp terminate:sender]; -} - -#pragma mark - IBAction - -- (IBAction)showPreferences:(id)sender; -{ - [[SWPreferencesService sharedService] showPreferencesWindow:sender]; -} - -@end diff --git a/Switch/SWApplication.h b/Switch/SWApplication.h deleted file mode 100644 index c727b24b..00000000 --- a/Switch/SWApplication.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// SWApplication.h -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface SWApplication : NSObject - -+ (instancetype)applicationWithPID:(pid_t)pid name:(NSString *)name; - -@property (nonatomic, assign, readonly) pid_t pid; -@property (nonatomic, copy, readonly) NSImage *cachedIcon; -@property (nonatomic, strong, readonly) NSString *name; - -- (NSImage *)loadIcon; - -- (BOOL)isActiveApplication; -- (BOOL)isLiveApplication; -- (BOOL)canBeActivated; - -@end diff --git a/Switch/SWApplication.m b/Switch/SWApplication.m deleted file mode 100644 index ec4dee17..00000000 --- a/Switch/SWApplication.m +++ /dev/null @@ -1,170 +0,0 @@ -// -// SWApplication.m -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWApplication.h" - -#import - -#import "SWWindow.h" - - -static NSCache *imageCache; -static NSMapTable *objectCache; - - -@interface SWApplication () - -@property (nonatomic, strong, readonly) NSRunningApplication *runningApplication; -@property (nonatomic, strong, readonly) NSString *path; - -@end - - -@implementation SWApplication - -#pragma mark - Initialization - -+ (void)initialize; -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - imageCache = [[NSCache alloc] init]; - imageCache.name = @"Application Icon Cache"; - - objectCache = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory]; - }); -} - -+ (instancetype)applicationWithPID:(pid_t)pid name:(NSString *)name; -{ - // Each application is represented by a single SWApplication object - @synchronized(objectCache) { - SWApplication *result = [objectCache objectForKey:@(pid)]; - if (![result.name isEqualToString:name]) { - result = nil; - [objectCache removeObjectForKey:@(pid)]; - } - if (!result) { - Class factory = self; - if (NSClassFromString(@"SWTestApplication")) { - factory = NSClassFromString(@"SWTestApplication"); - } - result = [[factory alloc] initWithPID:pid name:name]; - [objectCache setObject:result forKey:@(pid)]; - } - return result; - } -} - -- (instancetype)initWithPID:(pid_t)pid name:(NSString *)name; -{ - BailUnless(self = [super init], nil); - - _pid = pid; - _name = name ?: [self.runningApplication localizedName]; - - return self; -} - -#pragma mark - NSObject - -- (instancetype)copyWithZone:(NSZone *)zone; -{ - Check(!zone); - return self; -} - -- (NSUInteger)hash; -{ - return (NSUInteger)self.pid; -} - -- (BOOL)isEqual:(id)object; -{ - Check(object); - return ([object isKindOfClass:[self class]] && [self hash] == [object hash]); -} - -- (NSString *)description; -{ - return [NSString stringWithFormat:@"%p <%d (%@)>", self, self.pid, self.name]; -} - -#pragma mark - SWApplication - -@synthesize path = _path; - -- (NSString *)path; -{ - if (!_path) { - _path = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:[self.runningApplication bundleIdentifier]]; - } - return _path; -} - -- (NSImage *)cachedIcon; -{ - @synchronized(imageCache) { - return [imageCache objectForKey:self.path]; - } -} - -- (NSImage *)loadIcon; -{ - @synchronized(self) { - NSImage *result = self.cachedIcon; - if (result) { return result; } - - SWTimeTask(SWCodeBlock({ - NSString *path = self.path; - SWLogBackgroundThreadOnly(); - result = [[NSWorkspace sharedWorkspace] iconForFile:path]; - @synchronized(imageCache) { - Check([imageCache objectForKey:self.path] == nil); - [imageCache setObject:result forKey:path]; - } - }), @"Loading application icon for %@", self.name); - return result; - } -} - -- (BOOL)isActiveApplication; -{ - return self.runningApplication.active; -} - -- (BOOL)isLiveApplication; -{ - return self.pid == [[NSProcessInfo processInfo] processIdentifier]; -} - -- (BOOL)canBeActivated; -{ - NSApplicationActivationPolicy activationPolicy = self.runningApplication.activationPolicy; - return activationPolicy == NSApplicationActivationPolicyRegular || activationPolicy == NSApplicationActivationPolicyAccessory; -} - -@synthesize runningApplication = _runningApplication; - -- (NSRunningApplication *)runningApplication; -{ - @synchronized(self) { - if (_runningApplication == nil || _runningApplication.terminated) { - _runningApplication = [NSRunningApplication runningApplicationWithProcessIdentifier:self.pid]; - } - return _runningApplication; - } -} - -@end diff --git a/Switch/SWCoreWindowController.h b/Switch/SWCoreWindowController.h deleted file mode 100644 index 5081516b..00000000 --- a/Switch/SWCoreWindowController.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// SWCoreWindowController.h -// Switch -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@class SWCoreWindowController; -@class SWWindow; - - -@protocol SWCoreWindowControllerDelegate - -- (void)coreWindowController:(SWCoreWindowController *)controller didSelectWindow:(SWWindow *)window; -- (void)coreWindowController:(SWCoreWindowController *)controller didActivateWindow:(SWWindow *)window; -- (void)coreWindowControllerDidClickOutsideInterface:(SWCoreWindowController *)controller; - -@end - - -// A poor attempt at getting core window controllers to be well-behaved targets for the multi-dispatch manager. -@protocol SWCoreWindowControllerAPI - -@required -- (void)updateWindowList:(NSOrderedSet *)windowList; -- (void)selectWindow:(SWWindow *)window; -- (void)disableWindow:(SWWindow *)window; -- (void)enableWindow:(SWWindow *)window; - -@end - - -@interface SWCoreWindowController : NSWindowController - -@property (nonatomic, strong, setter=updateWindowList:) NSOrderedSet *windowList; -@property (nonatomic, weak) id delegate; - -- (id)initWithScreen:(NSScreen *)screen; - -@end diff --git a/Switch/SWCoreWindowController.m b/Switch/SWCoreWindowController.m deleted file mode 100644 index afbeaecb..00000000 --- a/Switch/SWCoreWindowController.m +++ /dev/null @@ -1,225 +0,0 @@ -// -// SWCoreWindowController.m -// Switch -// -// Created by Scott Perry on 07/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWCoreWindowController.h" - -#import "NSSet+SWChaining.h" -#import "SWEventTap.h" -#import "SWHUDCollectionView.h" -#import "SWWindow.h" -#import "SWWindowThumbnailView.h" - - -@interface SWCoreWindowController () - -@property (nonatomic, assign, readwrite) BOOL interfaceLoaded; -@property (nonatomic, assign, readonly) NSScreen *screen; -@property (nonatomic, strong, readwrite) SWHUDCollectionView *collectionView; -@property (nonatomic, strong, readonly) NSMutableDictionary *collectionCells; - -@end - - -@implementation SWCoreWindowController - -#pragma mark - Initialization - -- (id)initWithScreen:(NSScreen *)screen; -{ - BailUnless(self = [super initWithWindow:nil], nil); - - _collectionCells = [NSMutableDictionary new]; - _screen = screen; - - Check(![self isWindowLoaded]); - (void)self.window; - - @weakify(self); - [[SWEventTap sharedService] registerForEventsWithType:kCGEventMouseMoved object:self block:^(CGEventRef event) { - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - - if (!self.window.visible) { return; } - - NSPoint mouseLocationInWindow = [self private_pointInWindowFromScreen:[NSEvent mouseLocation]]; - if(NSPointInRect([self.collectionView convertPoint:mouseLocationInWindow fromView:nil], [self.collectionView bounds])) { - NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved location:mouseLocationInWindow modifierFlags:NSAlternateKeyMask timestamp:(NSTimeInterval)0 windowNumber:self.window.windowNumber context:(NSGraphicsContext *)nil eventNumber:0 clickCount:0 pressure:1.0]; - [self.collectionView mouseMoved:mouseEvent]; - } - }); - }]; - - return self; -} - -- (void)dealloc; -{ - [[SWEventTap sharedService] removeBlockForEventsWithType:kCGEventMouseMoved object:self]; -} - -#pragma mark - NSWindowController - -- (BOOL)isWindowLoaded; -{ - return self.interfaceLoaded; -} - -- (void)loadWindow; -{ - SWTimeTask(SWCodeBlock({ - CGRect contentRect = self.screen.frame; - NSWindow *switcherWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:self.screen]; - switcherWindow.movableByWindowBackground = NO; - switcherWindow.hasShadow = NO; - switcherWindow.opaque = NO; - switcherWindow.backgroundColor = [NSColor clearColor]; - switcherWindow.ignoresMouseEvents = NO; - switcherWindow.level = NSPopUpMenuWindowLevel; - self.window = switcherWindow; - - CGRect displayRect = [switcherWindow convertRectFromScreen:contentRect]; - SWHUDCollectionView *collectionView = [[SWHUDCollectionView alloc] initWithFrame:displayRect]; - collectionView.dataSource = self; - collectionView.delegate = self; - self.collectionView = collectionView; - - self.window.contentView = self.collectionView; - self.interfaceLoaded = YES; - }), @"Setting up switcher window"); -} - -#pragma mark - SWCoreWindowController - -- (void)updateWindowList:(NSOrderedSet *)windowList; -{ - SWTimeTask(SWCodeBlock({ - // Throw out any cells that aren't needed anymore. - NSSet *removedWindows = [[NSSet setWithArray:self.collectionCells.allKeys] sw_minusSet:windowList.set]; - [self.collectionCells removeObjectsForKeys:removedWindows.allObjects]; - - _windowList = windowList; - [self.collectionView reloadData]; - }), @"Updating switcher window list"); -} - -- (void)selectWindow:(SWWindow *)window; -{ - NSUInteger index = [self.windowList indexOfObject:window]; - - if (index < self.windowList.count) { - [self.collectionView selectCellAtIndex:index]; - } else { - [self.collectionView deselectCell]; - } -} - -- (void)disableWindow:(SWWindow *)window; -{ - if (![self.windowList containsObject:window]) { return; } - - [self private_thumbnailForWindow:window].active = NO; -} - -- (void)enableWindow:(SWWindow *)window; -{ - if (![self.windowList containsObject:window]) { return; } - - [self private_thumbnailForWindow:window].active = YES; -} - -#pragma mark - SWHUDCollectionViewDataSource - -- (CGFloat)HUDCollectionViewMaximumCellSize:(SWHUDCollectionView *)view; -{ - return kNNMaxWindowThumbnailSize; -} - -- (NSUInteger)HUDCollectionViewNumberOfCells:(SWHUDCollectionView *)view; -{ - return self.windowList.count; -} - -- (NSView *)HUDCollectionView:(SWHUDCollectionView *)view viewForCellAtIndex:(NSUInteger)index; -{ - BailUnless([view isEqual:self.collectionView], [[NSView alloc] initWithFrame:CGRectZero]); - - // Boundary method, index may not be in-bounds. - SWWindow *window = index < self.windowList.count ? self.windowList[index] : nil; - BailUnless(window, [[NSView alloc] initWithFrame:CGRectZero]); - - if (!self.collectionCells[window]) { - self.collectionCells[window] = [[SWWindowThumbnailView alloc] initWithFrame:CGRectZero window:window]; - } - - return self.collectionCells[window]; -} - -#pragma mark - SWHUDCollectionViewDelegate - -- (void)HUDCollectionView:(SWHUDCollectionView *)view didSelectCellAtIndex:(NSUInteger)index; -{ - if (!Check(index < self.windowList.count)) { - index = self.windowList.count - 1; - } - id delegate = self.delegate; - [delegate coreWindowController:self didSelectWindow:self.windowList[index]]; -} - -- (void)HUDCollectionView:(SWHUDCollectionView *)view activateCellAtIndex:(NSUInteger)index; -{ - if (!Check(index < self.windowList.count)) { - index = self.windowList.count - 1; - } - id delegate = self.delegate; - [delegate coreWindowController:self didActivateWindow:self.windowList[index]]; -} - -- (void)HUDCollectionViewClickOutsideCollection:(SWHUDCollectionView *)view; -{ - id delegate = self.delegate; - [delegate coreWindowControllerDidClickOutsideInterface:self]; -} - -#pragma mark - Private - -- (SWWindowThumbnailView *)private_thumbnailForWindow:(SWWindow *)window; -{ - NSUInteger index = [self.windowList indexOfObject:window]; - - if (index < self.windowList.count) { - id thumb = [self.collectionView cellForIndex:index]; - - if (!Check([thumb isKindOfClass:[SWWindowThumbnailView class]])) { - return nil; - } else { - return thumb; - } - } - - BailUnless(NO, nil); -} - -- (NSPoint)private_pointInWindowFromScreen:(NSPoint)point; -{ - NSRect inputRect = { - .origin.x = point.x, - .origin.y = point.y, - .size.width = 0, - .size.height = 0 - }; - - return [self.window convertRectFromScreen:inputRect].origin; -} - -@end diff --git a/Switch/SWCoreWindowService.h b/Switch/SWCoreWindowService.h deleted file mode 100644 index 35b881d9..00000000 --- a/Switch/SWCoreWindowService.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// SWCoreWindowService.h -// Switch -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@interface SWCoreWindowService : NNService - -@end diff --git a/Switch/SWCoreWindowService.m b/Switch/SWCoreWindowService.m deleted file mode 100644 index c1436936..00000000 --- a/Switch/SWCoreWindowService.m +++ /dev/null @@ -1,474 +0,0 @@ -// -// SWCoreWindowService.m -// Switch -// -// Created by Scott Perry on 11/19/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWCoreWindowService.h" - -#import "NSScreen+SWAdditions.h" -#import "SWAccessibilityService.h" -#import "SWApplication.h" -#import "SWEventTap.h" -#import "SWInterfaceController.h" -#import "SWPreferencesService.h" -#import "SWScrollControl.h" -#import "SWStateMachine.h" -#import "SWWindow.h" -#import "SWWindowListService.h" - - -static NSTimeInterval const kWindowDisplayDelay = 0.2; -static int const kScrollThreshold = 50; - - -@interface SWCoreWindowService () - -#pragma mark - State machine inputs -@property (nonatomic, strong) NSTimer *displayTimer; -@property (nonatomic, strong) SWScrollControl *scroller; - -@property (nonatomic, readonly, strong) SWStateMachine *stateMachine; - -#pragma mark - UI -@property (nonatomic, readonly, strong) SWInterfaceController *interface; - -@end - - -@implementation SWCoreWindowService - -#pragma mark - Initialization - -- (instancetype)init; -{ - BailUnless(self = [super init], nil); - - self->_stateMachine = [SWStateMachine stateMachineWithDelegate:self]; - - self->_interface = [[SWInterfaceController alloc] initWithDelegate:self]; - - @weakify(self); - self->_scroller = [[SWScrollControl alloc] initWithThreshold:kScrollThreshold incHandler:^{ - @strongify(self); - [self.stateMachine incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:true]; - } decHandler:^{ - @strongify(self); - [self.stateMachine incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:true]; - }]; - - // update the selected window group with the state machine's update of selector.selectedWindow - [RACObserve(self, stateMachine.selectedWindow) subscribeNext:^(SWWindow *window) { - @strongify(self); - [self.interface selectWindow:window]; - }]; - // start and stop window list updates based on stateMachine.windowListUpdates - [[[RACObserve(self, stateMachine.windowListUpdates) - distinctUntilChanged] skip:1] - subscribeNext:^(NSNumber *windowListUpdates) { - if ([windowListUpdates boolValue]) { - [[NNServiceManager sharedManager] addSubscriber:self forService:[SWWindowListService self]]; - // Update with the service's current set of windows, in case it's already running. - [self.stateMachine updateWindowList:[SWWindowListService sharedService].windows]; - } else { - [[NNServiceManager sharedManager] removeSubscriber:self forService:[SWWindowListService self]]; - } - }]; - // show or hide the interface based on stateMachine.interfaceVisible - [[[RACObserve(self, stateMachine.interfaceVisible) - distinctUntilChanged] skip:1] - subscribeNext:^(NSNumber *interfaceVisible) { - if ([interfaceVisible boolValue]) { - [self private_showInterface]; - } else { - [self private_hideInterface]; - } - }]; - // set [SWEventTap sharedService].suppressKeyEvents = self.stateMachine.invoked; - RAC([SWEventTap sharedService], suppressKeyEvents) = RACObserve(self, stateMachine.invoked); - - return self; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -+ (NSSet *)dependencies; -{ - return [NSSet setWithArray:@[[SWEventTap class], [SWPreferencesService class], [SWAccessibilityService class]]]; -} - -- (void)startService; -{ - [super startService]; - - @weakify(self); - SWEventTap *eventTap = [SWEventTap sharedService]; - - BOOL (^updateSelector)(CGEventRef, BOOL, SWIncrementDirection) = ^(CGEventRef event, BOOL invokesInterface, SWIncrementDirection direction) { - @strongify(self); - BailUnless(event, YES); - - // If this hotKey doesn't invoke the interface and it is not already active, pass the event through and do nothing. - if (!invokesInterface && !self.stateMachine.invoked) { - return YES; - } - - // The event is passed by reference. Copy it in case it mutates after control returns to the caller. Released in the async block below. - event = CGEventCreateCopy(event); - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - - // Avoid leaking the event if this block early-returns. - NNCFAutorelease(event); - - if (CGEventGetType(event) == kCGEventKeyDown) { - _Bool autorepeat = CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat); - - [self.stateMachine incrementWithInvoke:invokesInterface direction:direction isRepeating:autorepeat]; - - [self.scroller reset]; - } - }); - - return NO; - }; - - // Incrementing/invoking is bound to option-tab by default. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_Tab modifiers:SWHotKeyModifierOption] object:self block:^BOOL(CGEventRef event) { - _Bool invoking = true; - SWIncrementDirection direction = SWIncrementDirectionIncreasing; - return updateSelector(event, invoking, direction); - }]; - - // Option-arrow can be used to change the selection when Switch has been invoked. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_RightArrow modifiers:SWHotKeyModifierOption] object:self block:^BOOL(CGEventRef event) { - _Bool invoking = false; - SWIncrementDirection direction = SWIncrementDirectionIncreasing; - return updateSelector(event, invoking, direction); - }]; - - // Decrementing/invoking is bound to option-shift-tab by default. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_Tab modifiers:(SWHotKeyModifierOption|SWHotKeyModifierShift)] object:self block:^BOOL(CGEventRef event) { - _Bool invoking = true; - SWIncrementDirection direction = SWIncrementDirectionDecreasing; - return updateSelector(event, invoking, direction); - }]; - - // Option-arrow can be used to change the selection when Switch has been invoked. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_LeftArrow modifiers:SWHotKeyModifierOption] object:self block:^BOOL(CGEventRef event) { - _Bool invoking = false; - SWIncrementDirection direction = SWIncrementDirectionDecreasing; - return updateSelector(event, invoking, direction); - }]; - - // Closing a window is bound to option-W when the interface is open. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_ANSI_W modifiers:SWHotKeyModifierOption] object:self block:^BOOL(CGEventRef event) { - @strongify(self); - - if (CGEventGetType(event) != kCGEventKeyDown) { - return YES; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - [self.stateMachine closeWindow]; - }); - return !self.stateMachine.interfaceVisible; - }]; - - // Showing the preferences is bound to option-, when the interface is open. This action closes the interface. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_ANSI_Comma modifiers:SWHotKeyModifierOption] object:self block:^BOOL(CGEventRef event) { - @strongify(self); - if (CGEventGetType(event) == kCGEventKeyDown) { - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - if (self.stateMachine.active) { - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - [[SWPreferencesService sharedService] showPreferencesWindow:self]; - }); - } - [self.stateMachine cancelInvocation]; - }); - return NO; - } - return YES; - }]; - - // Cancelling the switcher is bound to option-escape. This action closes the interface. - [eventTap registerHotKey:[SWHotKey hotKeyWithKeycode:kVK_Escape modifiers:SWHotKeyModifierOption] object:self block:^(CGEventRef event){ - @strongify(self); - if (CGEventGetType(event) == kCGEventKeyDown) { - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - [self.stateMachine cancelInvocation]; - }); - return (BOOL)!self.stateMachine.active; - } - return YES; - }]; - - // Releasing the option key when the interface is open raises the selected window. If that action is successful, it will close the interface. - [eventTap registerModifier:SWHotKeyModifierOption object:self block:^(BOOL matched) { - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - if (!matched) { - [self.stateMachine endInvocation]; - } - }); - }]; -} - -- (void)stopService; -{ - SWEventTap *eventTap = [SWEventTap sharedService]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_Tab modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_RightArrow modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_Tab modifiers:(SWHotKeyModifierOption|SWHotKeyModifierShift)] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_LeftArrow modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_ANSI_W modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_ANSI_Comma modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForHotKey:[SWHotKey hotKeyWithKeycode:kVK_Escape modifiers:SWHotKeyModifierOption] object:self]; - [eventTap removeBlockForModifier:SWHotKeyModifierOption object:self]; - - [super stopService]; -} - -#pragma mark - SWWindowListSubscriber - -- (oneway void)windowListService:(SWWindowListService *)service updatedList:(NSOrderedSet *)windows; -{ - [self.stateMachine updateWindowList:windows]; - // Do not update the interface if the state machine has an action pending. - if (!self.stateMachine.pendingSwitch) { - [self.interface updateWindowList:self.stateMachine.windowList]; - } -} - -#pragma mark - SWStateMachineDelegate - -- (void)stateMachineWantsDisplayTimerStarted:(SWStateMachine *)stateMachine; -{ - self.displayTimer = [NSTimer scheduledTimerWithTimeInterval:kWindowDisplayDelay target:self selector:NNSelfSelector1(private_displayTimerFired:) userInfo:nil repeats:NO]; -} - -- (void)stateMachineWantsDisplayTimerInvalidated:(SWStateMachine *)stateMachine; -{ - [self.displayTimer invalidate]; - self.displayTimer = nil; -} - -- (void)stateMachine:(SWStateMachine *)stateMachine wantsWindowRaised:(SWWindow *)window; -{ - SWWindow *selectedWindow = self.stateMachine.selectedWindow; - if (!selectedWindow) { - return; - } - - NSUInteger selectedIndex = [self.stateMachine.windowList indexOfObject:self.stateMachine.selectedWindow]; - - if (selectedIndex == 0 && [((SWWindow *)self.stateMachine.windowList.firstObject).application isActiveApplication]) { - [self.stateMachine cancelInvocation]; - return; - } - - [self.interface disableWindow:selectedWindow]; - - [self private_raiseWindowWithStartTime:nil]; -} - -- (void)stateMachine:(SWStateMachine *)stateMachine wantsWindowClosed:(SWWindow *)window; -{ - SWWindow *selectedWindow = self.stateMachine.selectedWindow; - if (!selectedWindow) { return; } - NSOrderedSet *windowList = self.stateMachine.windowList; - - /** Closing a window will change the window list ordering in unwanted ways if all of the following are true: - * • The first window is being closed - * ? The first window's application is active - * • The first window's application has another window open in the list - * • The first window and second window belong to different applications - * This can be worked around by first raising the second window. - * This may still result in odd behaviour if firstWindow.close fails, but applications not responding to window close events is incorrect behaviour (performance is a feature!) whereas window list shenanigans are (relatively) expected. - */ - SWWindow *nextWindow = nil; - { - NSUInteger selectedIndex = [windowList indexOfObject:selectedWindow]; - BOOL onlyChild = ([windowList indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){ - // Do not count the selected window, we're looking for the existence of a sibling. - if (idx == selectedIndex) { return NO; } - return [CLASS_CAST(SWWindow, obj).application isEqual:selectedWindow.application]; - }] == NSNotFound); - - BOOL differentApplications = [windowList count] > 1 && ![[windowList[0] application] isEqual:[windowList[1] application]]; - - if (selectedIndex == 0 && !onlyChild && differentApplications) { - nextWindow = [windowList objectAtIndex:1]; - } - } - - [self.interface disableWindow:selectedWindow]; - - @weakify(self); - [[SWAccessibilityService sharedService] raiseWindow:nextWindow completion:^(NSError *raiseError){ - if (raiseError) { - SWLog(@"Failed to raise window group %@: %@", nextWindow, raiseError); - } - [[SWAccessibilityService sharedService] closeWindow:selectedWindow completion:^(NSError *closeError) { - if (closeError) { - SWLog(@"Failed to close window group %@: %@", selectedWindow, closeError); - // We *should* re-raise selectedWindow, but if it didn't succeed at -close it will also probably fail to -raise. - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - [self.interface enableWindow:selectedWindow]; - }); - } - }]; - }]; -} - -#pragma mark - SWInterfaceControllerDelegate - -- (void)interfaceController:(SWInterfaceController *)controller didSelectWindow:(SWWindow *)window; -{ - [self.stateMachine selectWindow:window]; - [self.scroller reset]; -} - -- (void)interfaceController:(SWInterfaceController *)controller didActivateWindow:(SWWindow *)window; -{ - [self.stateMachine activateWindow:window]; - [self.scroller reset]; -} - -- (void)interfaceControllerDidClickOutsideInterface:(SWInterfaceController *)controller; -{ - [self.stateMachine cancelInvocation]; -} - -#pragma mark - Private callbacks - -- (void)private_displayTimerFired:(NSTimer *)timer; -{ - if (![timer isEqual:self.displayTimer]) { - return; - } - - [self.stateMachine displayTimerCompleted]; - self.displayTimer = nil; -} - -- (void)private_showInterface; -{ - @weakify(self); - [[SWEventTap sharedService] registerForEventsWithType:kCGEventScrollWheel object:self block:^(CGEventRef event) { - // The event may be passed by reference and reused later. Copy it in case it mutates after control returns to the caller. Released in the async block below. - event = CGEventCreateCopy(event); - dispatch_async(dispatch_get_main_queue(), ^{ - NNCFAutorelease(event); - @strongify(self); - - int delta = (int)CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1); - if (delta == 0) { return; } - - [self.scroller feed:delta]; - }); - }]; - - [self.interface shouldShowInterface:true]; -} - -- (void)private_hideInterface; -{ - [[SWEventTap sharedService] removeBlockForEventsWithType:kCGEventScrollWheel object:self]; - - [self.interface shouldShowInterface:false]; -} - -- (void)private_raiseWindowWithStartTime:(NSDate *)start; -{ - SWWindow *selectedWindow = self.stateMachine.selectedWindow; - if (!Check(selectedWindow)) { return; } - - BOOL firstTry; - if (start == nil) { - firstTry = YES; - start = [NSDate new]; - SWLog(@"Raising window %@", selectedWindow); - } else { - firstTry = NO; - } - Check(-[start timeIntervalSinceNow] < 5.0); - - @weakify(self); - [[SWAccessibilityService sharedService] raiseWindow:selectedWindow completion:^(NSError *error) { - @strongify(self); - - NSOrderedSet *windowList = self.stateMachine.windowList; - NSUInteger selectedIndex = [windowList indexOfObject:selectedWindow]; - - if (selectedIndex == 0) { - // If the selected index was 0, this action won't change the window order so this code must replay the last update event. - [self.stateMachine updateWindowList:self.stateMachine.windowList]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - @strongify(self); - [self private_verifyRaiseWithStartTime:start window:selectedWindow error:error firstTry:firstTry]; - }); - } else { - // Otherwise, force a window list reload to verify. - // Wait a bit since Accessibility returns before the action has actually propagated. - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - // Waiting guarantees that the state machine is updated before this method returns. - [[SWWindowListService sharedService] refreshWindowListAndWait]; - dispatch_async(dispatch_get_main_queue(), ^{ - @strongify(self); - [self private_verifyRaiseWithStartTime:start window:selectedWindow error:error firstTry:firstTry]; - }); - }); - } - }]; -} - -- (void)private_verifyRaiseWithStartTime:(NSDate *)start window:(SWWindow *)selectedWindow error:(NSError *)error firstTry:(BOOL)firstTry; -{ - NSTimeInterval elapsed = -[start timeIntervalSinceNow]; - Check(elapsed < 1.0); - - if (self.stateMachine.pendingSwitch ) { - if (![selectedWindow isEqual:self.stateMachine.selectedWindow]) { - if (Unlikely(self.stateMachine.interfaceVisible) && [self.stateMachine.windowList containsObject:selectedWindow]) { - [self.interface enableWindow:selectedWindow]; - } - - SWLog(@"User no longer wants to raise %@, has selected %@ instead (elapsed: %0.3fs)", selectedWindow, self.stateMachine.selectedWindow, elapsed); - } else if ([self.stateMachine.windowList containsObject:selectedWindow]) { - SWLog(@"Attempt to raise %@ %@, trying again... (elapsed: %0.3fs)", selectedWindow, (error ? [NSString stringWithFormat:@"failed (%@)", error] : @"may have been ineffective"), elapsed); - [self private_raiseWindowWithStartTime:start]; - } else { - SWLog(@"Window %@ is gone, giving up (elapsed: %0.3fs)", selectedWindow, elapsed); - } - } else { - if (Unlikely(self.stateMachine.interfaceVisible) && [self.stateMachine.windowList containsObject:selectedWindow]) { - [self.interface enableWindow:selectedWindow]; - } - - if (elapsed > (1.0/60.0) || !firstTry) { - SWLog(@"Raise operation %@ for %@ (elapsed: %.3fs)", (error ? [NSString stringWithFormat:@"failed (%@)", error] : @"succeeded"), selectedWindow, elapsed); - } - } -} - -@end diff --git a/Switch/SWEventTap.h b/Switch/SWEventTap.h deleted file mode 100644 index 5e6a218e..00000000 --- a/Switch/SWEventTap.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// SWEventTap.h -// Switch -// -// Created by Scott Perry on 02/13/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWHotKey.h" - - -typedef BOOL (^SWEventTapKeyFilter)(CGEventRef event); -typedef void (^SWEventTapModifierCallback)(BOOL matched); -typedef void (^SWEventTapCallback)(CGEventRef event); - - -@interface SWEventTap : NNService - -@property (nonatomic, assign, readwrite) BOOL suppressKeyEvents; - -// For key bindings. Block can return NO to stop the event's further propagation. -- (void)registerHotKey:(SWHotKey *)hotKey object:(id)owner block:(SWEventTapKeyFilter)eventFilter; -- (void)removeBlockForHotKey:(SWHotKey *)hotKey object:(id)owner; - -// For modifier key state updates. Used to dismiss the interface. -- (void)registerModifier:(SWHotKeyModifierKey)modifiers object:(id)owner block:(SWEventTapModifierCallback)eventCallback; -- (void)removeBlockForModifier:(SWHotKeyModifierKey)modifiers object:(id)owner; - -// Primarily for mouse move and scroll events. Used for selector updates.. -- (void)registerForEventsWithType:(CGEventType)eventType object:(id)owner block:(SWEventTapCallback)eventCallback; -- (void)removeBlockForEventsWithType:(CGEventType)eventType object:(id)owner; - -@end diff --git a/Switch/SWEventTap.m b/Switch/SWEventTap.m deleted file mode 100644 index 31fa048f..00000000 --- a/Switch/SWEventTap.m +++ /dev/null @@ -1,278 +0,0 @@ -// -// SWEventTap.m -// Switch -// -// Created by Scott Perry on 02/13/14. -// Copyright © 2014 Scott Perry. -// -// 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. -// -// Inspiration from alterkeys.c : http://osxbook.com -// - -#import "SWEventTap.h" - -#import - -#import "SWAPIEnabledWorker.h" -#import "SWHotKey.h" - - -@interface SWEventTap () - -@property (nonatomic, assign, readwrite) CFMachPortRef eventTap; -@property (nonatomic, assign, readwrite) SWHotKeyModifierKey modifiers; -@property (nonatomic, assign, readwrite) CFRunLoopSourceRef runLoopSource; - -@property (nonatomic, strong, readonly) NSMutableDictionary *eventTypeCallbacks; -@property (nonatomic, strong, readonly) NSMutableDictionary *modifierCallbacks; -@property (nonatomic, strong, readonly) NSMutableDictionary *keyFilters; - -@end - -static CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon); - -@implementation SWEventTap - -- (instancetype)init; -{ - BailUnless(self = [super init], nil); - - _eventTypeCallbacks = [NSMutableDictionary new]; - _modifierCallbacks = [NSMutableDictionary new]; - _keyFilters = [NSMutableDictionary new]; - - return self; -} - -- (void)dealloc; -{ - [self private_removeEventTap]; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -- (void)startService; -{ - [super startService]; - - [self private_insertEventTap]; -} - -- (void)stopService; -{ - [self private_removeEventTap]; - - [super stopService]; -} - -#pragma mark - SWEventTap - -- (void)registerHotKey:(SWHotKey *)hotKey object:(id)owner block:(SWEventTapKeyFilter)eventFilter; -{ - SWLogMainThreadOnly(); - - if (!self.keyFilters[hotKey]) { - self.keyFilters[hotKey] = [NSMutableDictionary new]; - } - - NSNumber *ownerKey = @((uintptr_t)owner); - - // I don't love this limitation, but removing it is complicated and enables questionable functionality anyway. - Assert(!self.keyFilters[hotKey][ownerKey]); - - [self.keyFilters[hotKey] setObject:eventFilter forKey:ownerKey]; -} - -- (void)removeBlockForHotKey:(SWHotKey *)hotKey object:(id)owner; -{ - [self.keyFilters[hotKey] removeObjectForKey:@((uintptr_t)owner)]; -} - -- (void)registerModifier:(SWHotKeyModifierKey)modifiers object:(id)owner block:(SWEventTapModifierCallback)eventCallback; -{ - SWLogMainThreadOnly(); - - if (!self.modifierCallbacks[@(modifiers)]) { - self.modifierCallbacks[@(modifiers)] = [NSMutableDictionary new]; - } - - NSNumber *ownerKey = @((uintptr_t)owner); - - // I don't love this limitation, but removing it is complicated and enables questionable functionality anyway. - Assert(!self.modifierCallbacks[@(modifiers)][ownerKey]); - - [self.modifierCallbacks[@(modifiers)] setObject:eventCallback forKey:@((uintptr_t)owner)]; -} - -- (void)removeBlockForModifier:(SWHotKeyModifierKey)modifiers object:(id)owner; -{ - [self.modifierCallbacks[@(modifiers)] removeObjectForKey:@((uintptr_t)owner)]; -} - -- (void)registerForEventsWithType:(CGEventType)eventType object:(id)owner block:(SWEventTapCallback)eventCallback; -{ - SWLogMainThreadOnly(); - - if (!self.eventTypeCallbacks[@(eventType)]) { - self.eventTypeCallbacks[@(eventType)] = [NSMutableDictionary new]; - } - - NSNumber *ownerKey = @((uintptr_t)owner); - - // I don't love this limitation, but removing it is complicated and enables questionable functionality anyway. - Assert(!self.eventTypeCallbacks[@(eventType)][ownerKey]); - - [self.eventTypeCallbacks[@(eventType)] setObject:eventCallback forKey:@((uintptr_t)owner)]; -} - -- (void)removeBlockForEventsWithType:(CGEventType)eventType object:(id)owner; -{ - [self.eventTypeCallbacks[@(eventType)] removeObjectForKey:@((uintptr_t)owner)]; -} - -#pragma mark - Internal - -// Called from dealloc, use direct ivar access. -- (void)private_removeEventTap; -{ - if (self->_runLoopSource) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), self->_runLoopSource, kCFRunLoopCommonModes); - self->_runLoopSource = NULL; - } - if (self->_eventTap) { - CFRelease(self->_eventTap); - self->_eventTap = NULL; - } -} - -- (BOOL)private_insertEventTap; -{ - Assert(!self.eventTap); - - self.modifiers = 0; - - // Create an event tap. For now the list of event types is limited to mouse & keyboard events. - CGEventMask eventMask = ( - CGEventMaskBit(kCGEventKeyDown) | - CGEventMaskBit(kCGEventKeyUp) | - CGEventMaskBit(kCGEventFlagsChanged) | - CGEventMaskBit(kCGEventMouseMoved) | - CGEventMaskBit(kCGEventScrollWheel) | - 0); - - // apps may check the authorization status without triggering the approval prompt, using the IOHIDCheckAccess function with the kIOHIDRequestTypeListenEvent parameter - // apps can request an approval dialog to be displayed without creating an event tap or trying to post an event by using the IOHIDRequestAccess function, again with the same parameter - self.eventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, eventMask, eventCallback, (__bridge void *)(self)); - BailUnless(self.eventTap, NO); - - // Create a run loop source. - // XXX: why does calling the property setter here tickle the static analyzer the wrong way, but setting the ivar directly doesn't? - self->_runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.eventTap, 0); - BailWithBlockUnless(self.runLoopSource, ^{ - [self private_removeEventTap]; - return NO; - }); - - // Add to the current run loop. - CFRunLoopAddSource(CFRunLoopGetCurrent(), self.runLoopSource, kCFRunLoopCommonModes); - - // Enable the event tap. - CGEventTapEnable(self.eventTap, true); - - return YES; -} - -static CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) -{ -#if DEBUG - Assert([NSThread isMainThread]); -#endif - - SWEventTap *eventTap = (__bridge SWEventTap *)refcon; - - // - // Event tap administration - // - if (type == kCGEventTapDisabledByTimeout) { - dispatch_async(dispatch_get_main_queue(), ^{ - // Re-enable the event tap. - SWLog(@"Event tap timed out?!"); - CGEventTapEnable(eventTap.eventTap, true); - }); - return event; - } else if (type == kCGEventTapDisabledByUserInput) { - NotTested(); - return event; - } - - // - // Event type callbacks. - // - for (SWEventTapCallback callback in [eventTap.eventTypeCallbacks[@(type)] allValues]) { - callback(event); - } - - // Prevent other applications from receiving scroll events when the application is consuming keyboard events. - if (type == kCGEventScrollWheel) { - return eventTap.suppressKeyEvents ? NULL : event; - } - - // Escape if the event is not a key/modifier change event. - if ((type != kCGEventKeyDown) && (type != kCGEventKeyUp) && (type != kCGEventFlagsChanged)) { - return event; - } - - // - // Modifier key callbacks. - // - SWHotKey *key = [SWHotKey hotKeyFromEvent:event]; - if (type == kCGEventFlagsChanged) { - SWHotKeyModifierKey flagChanges = key.modifiers ^ eventTap.modifiers; - - for (NSNumber *boxedRegisteredModifiers in eventTap.modifierCallbacks) { - SWHotKeyModifierKey registeredModifiers = boxedRegisteredModifiers.unsignedIntValue; - - // Changes can't affect registrant. - if (!(flagChanges & registeredModifiers)) continue; - - // Changes didn't affect registrant - BOOL matched = (eventTap.modifiers & registeredModifiers) == registeredModifiers; - BOOL matches = (key.modifiers & registeredModifiers) == registeredModifiers; - if (matched == matches) continue; - - for (SWEventTapModifierCallback callback in [eventTap.modifierCallbacks[boxedRegisteredModifiers] allValues]) { - callback(matches); - } - } - - eventTap.modifiers = key.modifiers; - - return event; - } - - // - // Hotkey callbacks. - // - - for (SWEventTapKeyFilter filter in [eventTap.keyFilters[key] allValues]) { - if (!filter(event)) { - event = NULL; - break; - } - } - - return eventTap.suppressKeyEvents ? NULL : event; -} - - -@end diff --git a/Switch/SWGeneralPreferencesViewController.h b/Switch/SWGeneralPreferencesViewController.h deleted file mode 100644 index 14600a47..00000000 --- a/Switch/SWGeneralPreferencesViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// SWGeneralPreferencesViewController.h -// Switch -// -// Created by Scott Perry on 10/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 "MASPreferencesViewController.h" - - -@interface SWGeneralPreferencesViewController : NSViewController - -@end diff --git a/Switch/SWGeneralPreferencesViewController.m b/Switch/SWGeneralPreferencesViewController.m deleted file mode 100644 index daad26ac..00000000 --- a/Switch/SWGeneralPreferencesViewController.m +++ /dev/null @@ -1,175 +0,0 @@ -// -// SWGeneralPreferencesViewController.m -// Switch -// -// Created by Scott Perry on 10/10/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWGeneralPreferencesViewController.h" - -#import - - -@interface SWGeneralPreferencesViewController () - -@property (nonatomic, weak) IBOutlet NSTextFieldCell *currentVersionCell; -@property (nonatomic, weak) IBOutlet NSButton *autoLaunchEnabledBox; - -- (IBAction)autoLaunchChanged:(NSButton *)sender; -- (IBAction)autoUpdatesChanged:(NSButton *)sender; -- (IBAction)checkForUpdatesPressed:(NSButton *)sender; -- (IBAction)preReleaseUpdatesChanged:(NSButton *)sender; -- (IBAction)changelogPressed:(NSButton *)sender; -- (IBAction)quitPressed:(NSButton *)sender; - -@property (nonatomic, weak) IBOutlet NSButtonCell *automaticUpdateCell; -@property (nonatomic, weak) IBOutlet NSButtonCell *prereleaseUpdatesCell; - -@end - - -@implementation SWGeneralPreferencesViewController - -#pragma mark - NSViewController - -- (void)awakeFromNib; -{ - NSTextFieldCell *currentVersionCell = self.currentVersionCell; - currentVersionCell.title = [NSString stringWithFormat:@"Currently using version %@", [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]]; - - NSButton *autoLaunchEnabled = self.autoLaunchEnabledBox; - autoLaunchEnabled.state = [self private_isAutoLaunchEnabled] ? NSOnState : NSOffState; - - NSButtonCell *buttonCell = self.automaticUpdateCell; - buttonCell.enabled = NO; - buttonCell = self.prereleaseUpdatesCell; - buttonCell.enabled = NO; -} - -#pragma mark - MASPreferencesViewController - -- (NSString *)identifier -{ - return NSStringFromClass([self class]); -} - -- (NSImage *)toolbarItemImage -{ - return [NSImage imageNamed:NSImageNamePreferencesGeneral]; -} - -- (NSString *)toolbarItemLabel -{ - return NSLocalizedString(@"General", @"Toolbar item name for the General preference pane"); -} - -- (BOOL)hasResizableWidth; -{ - return NO; -} - -- (BOOL)hasResizableHeight; -{ - return NO; -} - -#pragma mark - IBAction - -- (IBAction)autoLaunchChanged:(NSButton *)sender { - if (sender.state == NSOnState) { - [self private_enableAutoLaunch]; - } else { - [self private_disableAutolaunch]; - } -} - -- (IBAction)autoUpdatesChanged:(NSButton *)sender { - // Disallow turning off auto updates when there is no GM build. - sender.state = NSOnState; -} - -- (IBAction)preReleaseUpdatesChanged:(NSButton *)sender { - // Disallow turning off pre-release updates when there is no GM build. - sender.state = NSOnState; -} - -- (IBAction)changelogPressed:(NSButton *)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/numist/Switch/releases"]]; -} - -- (IBAction)quitPressed:(NSButton *)sender { - [[NSApplication sharedApplication] terminate:self]; -} - -- (IBAction)checkForUpdatesPressed:(NSButton *)sender { - [[SUUpdater sharedUpdater] checkForUpdates:self]; -} - -#pragma mark - Internal - -// -// Launch at login helper methods! -// -// If you ever want to make this app sandbox-compatible, you're going to have to make a helper app and use SMLoginItemSetEnabled and LSRegisterURL. -// See http://www.delitestudio.com/2011/10/25/start-dockless-apps-at-login-with-app-sandbox-enabled/ for more info. -// - -- (BOOL)private_isAutoLaunchEnabled; -{ - return !![self private_selfFromLSSharedFileList]; -} - -- (void)private_enableAutoLaunch; -{ - NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]; - - LSSharedFileListRef loginItems = NNCFAutorelease(LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL)); - Check(loginItems); - if (loginItems) { - NNCFAutorelease(LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)url, NULL, NULL)); - } -} - -- (void)private_disableAutolaunch; -{ - LSSharedFileListItemRef itemRef = [self private_selfFromLSSharedFileList]; - Check(itemRef); - if (itemRef) { - LSSharedFileListRef loginItems = NNCFAutorelease(LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL)); - LSSharedFileListItemRemove(loginItems, itemRef); - } -} - -- (LSSharedFileListItemRef)private_selfFromLSSharedFileList; -{ - LSSharedFileListRef loginItems = NNCFAutorelease(LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL)); - BailUnless(loginItems, NULL); - - UInt32 snapshotSeed; - NSArray *loginItemsArray = (NSArray *)CFBridgingRelease(LSSharedFileListCopySnapshot(loginItems, &snapshotSeed)); - Check(loginItemsArray); - - NSString * appPath = [[NSBundle mainBundle] bundlePath]; - for (id item in loginItemsArray) { - LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item; - - CFURLRef url; - if (LSSharedFileListItemResolve(itemRef, 0, &url, NULL) == noErr) { - NSString * urlPath = [(__bridge NSURL *)NNCFAutorelease(url) path]; - if ([urlPath compare:appPath] == NSOrderedSame) { - return NNCFAutorelease(CFRetain(itemRef)); - } - } - } - - return NULL; -} - -@end diff --git a/Switch/SWGeneralPreferencesViewController.xib b/Switch/SWGeneralPreferencesViewController.xib deleted file mode 100644 index 78f571db..00000000 --- a/Switch/SWGeneralPreferencesViewController.xib +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Switch/SWHUDCollectionView.h b/Switch/SWHUDCollectionView.h deleted file mode 100644 index d597d436..00000000 --- a/Switch/SWHUDCollectionView.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// SWHUDCollectionView.h -// Switch -// -// Created by Scott Perry on 05/28/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@protocol SWHUDCollectionViewDataSource; -@protocol SWHUDCollectionViewDelegate; - - -@interface SWHUDCollectionView : NSView - -@property (nonatomic, weak) id dataSource; -@property (nonatomic, weak) id delegate; - -@property (nonatomic, readonly) NSUInteger selectedIndex; - -- (NSUInteger)numberOfCells; - -- (NSView *)cellForIndex:(NSUInteger)index; - -- (void)selectCellAtIndex:(NSUInteger)index; -- (void)deselectCell; - -- (void)reloadData; - -@end - - -@protocol SWHUDCollectionViewDataSource - -- (CGFloat)HUDCollectionViewMaximumCellSize:(SWHUDCollectionView *)view; -- (NSUInteger)HUDCollectionViewNumberOfCells:(SWHUDCollectionView *)view; -- (NSView *)HUDCollectionView:(SWHUDCollectionView *)view viewForCellAtIndex:(NSUInteger)index; - -@end - - -@protocol SWHUDCollectionViewDelegate - -@optional -- (void)HUDCollectionView:(SWHUDCollectionView *)view willSelectCellAtIndex:(NSUInteger)index; -- (void)HUDCollectionView:(SWHUDCollectionView *)view didSelectCellAtIndex:(NSUInteger)index; -- (void)HUDCollectionView:(SWHUDCollectionView *)view activateCellAtIndex:(NSUInteger)index; -- (void)HUDCollectionViewClickOutsideCollection:(SWHUDCollectionView *)view; - -@end diff --git a/Switch/SWHUDCollectionView.m b/Switch/SWHUDCollectionView.m deleted file mode 100644 index ea7090c4..00000000 --- a/Switch/SWHUDCollectionView.m +++ /dev/null @@ -1,408 +0,0 @@ -// -// SWHUDCollectionView.m -// Switch -// -// Created by Scott Perry on 05/28/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWHUDCollectionView.h" - -#import "NSLayoutConstraint+SWConstraintHelpers.h" -#import "SWHUDView.h" -#import "SWSelectionBoxView.h" - - -@interface SWHUDCollectionView () - -// dataSource information. -@property (nonatomic, assign, readwrite) CGFloat maxCellSize; -@property (nonatomic, assign, readwrite) NSUInteger numberOfCells; -@property (nonatomic, strong, readonly) NSMutableArray *cells; - -// Persistent views. -@property (nonatomic, strong, readonly) SWHUDView *hud; -@property (nonatomic, strong, readonly) SWSelectionBoxView *selectionBox; - -// Constraint tracking. -@property (nonatomic, strong, readwrite) NSArray *selectionBoxConstraints; -@property (nonatomic, strong, readwrite) NSArray *collectionConstraints; - -// Internal state. -@property (nonatomic, assign, readwrite) BOOL reloading; -@property (nonatomic, assign, readwrite) NSUInteger selectedIndex; - -@end - - -@implementation SWHUDCollectionView - -#pragma mark - Initialization - -- (id)initWithFrame:(CGRect)frame -{ - BailUnless(self = [super initWithFrame:frame], nil); - - _cells = [NSMutableArray new]; - _hud = [[SWHUDView alloc] initWithFrame:CGRectZero]; - _selectionBox = [[SWSelectionBoxView alloc] initWithFrame:CGRectZero]; - _selectedIndex = NSNotFound; - - return self; -} - -#pragma mark - NSResponder - -- (BOOL)acceptsFirstResponder; -{ - return YES; -} - -- (void)mouseMoved:(NSEvent *)theEvent; -{ - NSUInteger i = [self private_indexForCellAtPoint:theEvent.locationInWindow]; - - if (i < self.numberOfCells && self.selectedIndex != i) { - id delegate = self.delegate; - - self.selectedIndex = i; - - if ([delegate respondsToSelector:@selector(HUDCollectionView:willSelectCellAtIndex:)]) { - [delegate HUDCollectionView:self willSelectCellAtIndex:i]; - } - - [self selectCellAtIndex:self.selectedIndex]; - - if ([delegate respondsToSelector:@selector(HUDCollectionView:didSelectCellAtIndex:)]) { - [delegate HUDCollectionView:self didSelectCellAtIndex:self.selectedIndex]; - } - } - - [super mouseMoved:theEvent]; -} - -- (void)mouseUp:(NSEvent *)theEvent; -{ - NSUInteger i = [self private_indexForCellAtPoint:theEvent.locationInWindow]; - - if (i < self.numberOfCells) { - id delegate = self.delegate; - - Check(self.selectedIndex == i); - self.selectedIndex = i; - - if ([delegate respondsToSelector:@selector(HUDCollectionView:activateCellAtIndex:)]) { - [delegate HUDCollectionView:self activateCellAtIndex:i]; - } - } else { - [super mouseUp:theEvent]; - } -} - -- (void)mouseDown:(NSEvent *)theEvent; -{ - if ([self private_indexForCellAtPoint:theEvent.locationInWindow] == NSNotFound) { - id delegate = self.delegate; - if ([delegate respondsToSelector:@selector(HUDCollectionViewClickOutsideCollection:)]) { - [delegate HUDCollectionViewClickOutsideCollection:self]; - } - } - - // Do not pass this event up the responder chain. - return; -} - -#pragma mark - NSView - -+ (BOOL)requiresConstraintBasedLayout; -{ - return YES; -} - -- (void)viewWillMoveToSuperview:(NSView *)newSuperview; -{ - if (![self.subviews containsObject:self.hud]) { - self.hud.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.hud]; - } - - // Force layout and subview insertion of the selection box if necessary. - [self selectCellAtIndex:self.selectedIndex]; -} - -- (void)updateConstraints; -{ - if (!self.collectionConstraints) { - [self private_updateConstraintsForCollection]; - } - - if (!self.selectionBoxConstraints) { - [self private_updateConstraintsForSelectionBox]; - } - [super updateConstraints]; -} - -#pragma mark - SWHUDCollectionView - -- (void)setDataSource:(id)dataSource; -{ - SWLogMainThreadOnly(); - _dataSource = dataSource; - - [self reloadData]; -} - -- (NSView *)cellForIndex:(NSUInteger)index; -{ - SWLogMainThreadOnly(); - - // Flush pending reloadData - [self private_reloadDataIfNeeded]; - - if (index < [self.cells count]) { - return [self.cells objectAtIndex:index]; - } - return nil; -} - -- (void)selectCellAtIndex:(NSUInteger)index; -{ - SWLogMainThreadOnly(); - Check(self.selectionBox); - - self.selectedIndex = index; - - if (self.selectedIndex < self.numberOfCells) { - [self private_constraintsForSelectionBoxNeedUpdate]; - } else if ([self.hud.subviews containsObject:self.selectionBox]) { - [self.selectionBox removeFromSuperview]; - } -} - -- (void)deselectCell; -{ - [self selectCellAtIndex:NSNotFound]; -} - -- (void)reloadData; -{ - SWLogMainThreadOnly(); - - if (!self.reloading) { - self.reloading = YES; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self private_reloadDataIfNeeded]; - }); - } -} - -#pragma mark - Internal - -- (NSUInteger)private_indexForCellAtPoint:(NSPoint)point; -{ - for (NSUInteger i = 0; i < self.numberOfCells; ++i) { - NSPoint relativePoint = [self convertPoint:point toView:self.cells[i]]; - CGRect cellFrame = CLASS_CAST(NSView, self.cells[i]).bounds; - - if (NSPointInRect(relativePoint, cellFrame)) { - return i; - } - } - - return NSNotFound; -} - -- (void)private_reloadDataIfNeeded; -{ - if (!self.reloading) { return; } - - @weakify(self); - dispatch_block_t cleanupData = ^{ - @strongify(self); - self.numberOfCells = 0; - [self.cells makeObjectsPerformSelector:NNTypedSelector(NSView, removeFromSuperview)]; - [self.cells removeAllObjects]; - }; - - self.reloading = NO; - - __strong __typeof__(self.dataSource) dataSource = self.dataSource; - - if (self.maxCellSize == 0) { - self.maxCellSize = [dataSource HUDCollectionViewMaximumCellSize:self]; - // dataSource side effect may have called reloadData, in which case it's not safe to continue anymore. - BailWithBlockUnless(!self.reloading, cleanupData); - } - - cleanupData(); - - self.numberOfCells = [dataSource HUDCollectionViewNumberOfCells:self]; - // dataSource side effect may have called reloadData, in which case it's not safe to continue anymore. - BailWithBlockUnless(!self.reloading, cleanupData); - - for (NSUInteger i = 0; i < self.numberOfCells; i++) { - NSView *cell = [dataSource HUDCollectionView:self viewForCellAtIndex:i]; - // dataSource side effect may have called reloadData, in which case it's not safe to continue anymore. - BailWithBlockUnless(!self.reloading, cleanupData); - - [self.cells insertObject:cell atIndex:i]; - [self.hud addSubview:cell]; - } - - if (self.selectionBox) { - [self.hud addSubview:self.selectionBox positioned:NSWindowBelow relativeTo:nil]; - } - - [self private_constraintsForCollectionNeedUpdate]; - [self private_constraintsForSelectionBoxNeedUpdate]; -} - -- (void)private_constraintsForCollectionNeedUpdate; -{ - if (self.collectionConstraints) { - [self removeConstraints:self.collectionConstraints]; - self.collectionConstraints = nil; - } - [self setNeedsUpdateConstraints:YES]; -} - -- (void)private_updateConstraintsForCollection; -{ - if (!Check(!self.collectionConstraints.count)) { - [self removeConstraints:self.collectionConstraints]; - self.collectionConstraints = nil; - } - - NSMutableArray *constraints = [NSMutableArray new]; - - NSDictionary *views = @{ - @"hud" : self.hud, - @"collection" : self, - }; - - NSDictionary *metrics = @{ - @"hudPadding" : @(kNNScreenToWindowInset), - @"cellPadding" : @(kNNWindowToThumbInset), - @"maxThumbSize" : @(self.maxCellSize), - @"emptyHUDSize" : @(self.maxCellSize + (kNNWindowToThumbInset * 2.0)), - @"windowWidth" : @(self.frame.size.width), - @"windowHeight" : @(self.frame.size.height), - }; - - // Maintain the size of the frame in case there are too many objects in the collection and Auto Layout attempts to enlarge it. - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[collection(windowWidth)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[collection(windowHeight)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - - // Center the HUD inside its container view. - [constraints addObjectsFromArray:[NSLayoutConstraint sw_constraintsCenteringView:self.hud toView:self]]; - - // Establish flexible minimum HUD size, for when there are no cells to display. - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[hud(>=emptyHUDSize@500)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[hud(>=emptyHUDSize@500)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - - // Ensure that the hud always has a minimum padding within its container view. - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=hudPadding)-[hud]-(>=hudPadding)-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=hudPadding)-[hud]-(>=hudPadding)-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; - - for (NSUInteger i = 0; i < self.numberOfCells; i++) { - NSView *prevCell = i != 0 ? self.cells[i - 1] : nil; - NSView *cell = self.cells[i]; - NSView *nextCell = i < (self.numberOfCells - 1) ? self.cells[i + 1] : nil; - - NSDictionary *cellViews = @{ - @"prevCell" : prevCell ?: [NSNull null], - @"cell": cell, - @"nextCell" : nextCell ?: [NSNull null], - @"hud" : self.hud, - }; - - if (!prevCell) { - // First cell in the collection establishes the size that all of the others follow. Max size, with lower priority so it will be compromised if layout pressure exists due to too many cells. - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"[cell(maxThumbSize@750)]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:cellViews]]; - - // First cell in the collection must have RHS padding to its superview (the hud). - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(cellPadding)-[cell]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:cellViews]]; - } else { - // Non-first cells set their width (and thus their size due to the aspect ratio constraint) to be equal to the first cell's width. - [constraints addObject:[NSLayoutConstraint constraintWithItem:cell attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:self.cells[0] attribute:NSLayoutAttributeWidth - multiplier:1.f constant:0.f]]; - - // Middle cells in the collection must have LHS padding to their neighbouring cell. - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[prevCell]-(cellPadding)-[cell]" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:cellViews]]; - } - - // Cells have a fixed aspect ratio (square). - [constraints addObject:[NSLayoutConstraint constraintWithItem:cell attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:cell attribute:NSLayoutAttributeWidth - multiplier:1.f constant:0.f]]; - - // Cell must have top/bottom padding to its superview (the hud). - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(cellPadding)-[cell]-(cellPadding)-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:cellViews]]; - - if (!nextCell) { - // Last cell in the collection must have LHS padding to its superview (the hud). - [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[cell]-(cellPadding)-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:cellViews]]; - } - } - - self.collectionConstraints = [constraints copy]; - [self addConstraints:self.collectionConstraints]; -} - -- (void)private_constraintsForSelectionBoxNeedUpdate; -{ - if (self.selectionBoxConstraints) { - [self removeConstraints:self.selectionBoxConstraints]; - self.selectionBoxConstraints = nil; - } - [self setNeedsUpdateConstraints:YES]; -} - -- (void)private_updateConstraintsForSelectionBox; -{ - if (!Check(!self.selectionBoxConstraints.count)) { - [self removeConstraints:self.selectionBoxConstraints]; - self.selectionBoxConstraints = nil; - } - - NSMutableArray *constraints = [NSMutableArray new]; - - if (self.selectedIndex < self.numberOfCells) { - Check(self.selectionBox); - - // Selection box (re)insertion into the view hierarchy happens during layout-time to prevent the box appearing to jump between its old location (pre-deselection) and its new location. - if (![self.hud.subviews containsObject:self.selectionBox]) { - [self.hud addSubview:self.selectionBox positioned:NSWindowBelow relativeTo:nil]; - } - - NSView *selectedView = self.cells[self.selectedIndex]; - - // Constraint: selection box must be centered over selection thumb - [constraints addObjectsFromArray:[NSLayoutConstraint sw_constraintsCenteringView:self.selectionBox toView:selectedView]]; - - // Selection box height and width must be thumb [height|width] + const - [constraints addObject:[NSLayoutConstraint constraintWithItem:self.selectionBox attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:selectedView attribute:NSLayoutAttributeWidth - multiplier:1.f constant:(kNNWindowToThumbInset + kNNItemBorderWidth)]]; - [constraints addObject:[NSLayoutConstraint constraintWithItem:self.selectionBox attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:selectedView attribute:NSLayoutAttributeHeight - multiplier:1.f constant:(kNNWindowToThumbInset + kNNItemBorderWidth)]]; - } - - self.selectionBoxConstraints = [constraints copy]; - [self addConstraints:self.selectionBoxConstraints]; -} - -@end diff --git a/Switch/SWHUDView.h b/Switch/SWHUDView.h deleted file mode 100644 index 33a959e8..00000000 --- a/Switch/SWHUDView.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// SWHUDView.h -// Switch -// -// Created by Scott Perry on 03/02/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWRoundedRectView.h" - - -@interface SWHUDView : SWRoundedRectView - -@end diff --git a/Switch/SWHUDView.m b/Switch/SWHUDView.m deleted file mode 100644 index 7e9d500d..00000000 --- a/Switch/SWHUDView.m +++ /dev/null @@ -1,33 +0,0 @@ -// -// SWHUDView.m -// Switch -// -// Created by Scott Perry on 03/02/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWHUDView.h" - - -@implementation SWHUDView - -#pragma mark - Initialization - -- (id)initWithFrame:(CGRect)frame -{ - BailUnless(self = [super initWithFrame:frame], nil); - - self.border = 0.0; - self.radius = kNNWindowRoundRectRadius; - self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - - return self; -} - -@end diff --git a/Switch/SWHotKey.h b/Switch/SWHotKey.h deleted file mode 100644 index e45fe2bf..00000000 --- a/Switch/SWHotKey.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// SWHotKey.h -// Switch -// -// Created by Scott Perry on 07/16/13. -// Copyright © 2013 Scott Perry. -// -// 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 -#import - - -typedef NS_OPTIONS(unsigned, SWHotKeyModifierKey) { - SWHotKeyModifierShift = controlKey, - SWHotKeyModifierOption = optionKey, - SWHotKeyModifierControl = shiftKey, - SWHotKeyModifierCmd = cmdKey, -}; - - -@interface SWHotKey : NSObject - -@property (nonatomic, readonly) CGKeyCode code; -@property (nonatomic, readonly) SWHotKeyModifierKey modifiers; - -+ (SWHotKey *)hotKeyWithKeycode:(CGKeyCode)code modifiers:(SWHotKeyModifierKey)modifiers; -+ (SWHotKey *)hotKeyFromEvent:(CGEventRef)event; - -- (NSString *)modifierDescription; - -@end diff --git a/Switch/SWHotKey.m b/Switch/SWHotKey.m deleted file mode 100644 index 5025ee2c..00000000 --- a/Switch/SWHotKey.m +++ /dev/null @@ -1,195 +0,0 @@ -// -// SWHotKey.m -// Switch -// -// Created by Scott Perry on 07/16/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWHotKey.h" - - -static NSDictionary *specialKeys = nil; - - -@implementation SWHotKey - -#pragma mark - Initialization - -+ (void)initialize; -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - specialKeys = @{ - @(kVK_Return) : @"⏎", - @(kVK_Tab) : @"⇥", - @(kVK_Space) : @"␠", - @(kVK_Delete) : @"⌫", - @(kVK_Escape) : @"⎋", - @(kVK_Command) : @"⌘", // Modifier key - @(kVK_Shift) : @"⇧", // Modifier key - @(kVK_CapsLock) : @"⇪", - @(kVK_Option) : @"⌥", // Modifier key - @(kVK_Control) : @"⌃", // Modifier key - /// @(kVK_RightShift) : @"", // Unsupported - /// @(kVK_RightOption) : @"", // Unsupported - /// @(kVK_RightControl) : @"", // Unsupported - /// @(kVK_Function) : @"", // Modifier key, unsupported - @(kVK_F17) : @"F17", - /// @(kVK_VolumeUp) : @"", // Unsupported - /// @(kVK_VolumeDown) : @"", // Unsupported - /// @(kVK_Mute) : @"", // Unsupported - @(kVK_F18) : @"F18", - @(kVK_F19) : @"F19", - @(kVK_F20) : @"F20", - @(kVK_F5) : @"F5", - @(kVK_F6) : @"F6", - @(kVK_F7) : @"F7", - @(kVK_F3) : @"F3", - @(kVK_F8) : @"F8", - @(kVK_F9) : @"F9", - @(kVK_F11) : @"F11", - @(kVK_F13) : @"F13", - @(kVK_F16) : @"F16", - @(kVK_F14) : @"F14", - @(kVK_F10) : @"F10", - @(kVK_F12) : @"F12", - @(kVK_F15) : @"F15", - /// @(kVK_Help) : @"", // Unsupported - /// @(kVK_Home) : @"", // Unsupported - @(kVK_PageUp) : @"⇞", - @(kVK_ForwardDelete) : @"⌦", - @(kVK_F4) : @"F4", - /// @(kVK_End) : @"", // Unsupported - @(kVK_F2) : @"F2", - @(kVK_PageDown) : @"⇟", - @(kVK_F1) : @"F1", - @(kVK_LeftArrow) : @"←", - @(kVK_RightArrow) : @"→", - @(kVK_DownArrow) : @"↓", - @(kVK_UpArrow) : @"↑" - }; - }); -} - -+ (SWHotKey *)hotKeyWithKeycode:(CGKeyCode)code modifiers:(SWHotKeyModifierKey)modifiers; -{ - return [[SWHotKey alloc] initWithKeycode:code modifiers:modifiers]; -} - -+ (SWHotKey *)hotKeyFromEvent:(CGEventRef)event; -{ - SWHotKeyModifierKey modifiers = 0; - if ((CGEventGetFlags(event) & kCGEventFlagMaskAlternate) == kCGEventFlagMaskAlternate) { - modifiers |= SWHotKeyModifierOption; - } - if ((CGEventGetFlags(event) & kCGEventFlagMaskShift) == kCGEventFlagMaskShift) { - modifiers |= SWHotKeyModifierShift; - } - if ((CGEventGetFlags(event) & kCGEventFlagMaskControl) == kCGEventFlagMaskControl) { - modifiers |= SWHotKeyModifierControl; - } - if ((CGEventGetFlags(event) & kCGEventFlagMaskCommand) == kCGEventFlagMaskCommand) { - modifiers |= SWHotKeyModifierCmd; - } - - CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); - - return [self hotKeyWithKeycode:keycode modifiers:modifiers]; -} - -- (instancetype)initWithKeycode:(CGKeyCode)code modifiers:(SWHotKeyModifierKey)modifiers; -{ - BailUnless(self = [super init], nil); - - _modifiers = modifiers; - _code = code; - - return self; -} - -#pragma mark - NSObject - -- (NSUInteger)hash; -{ - return self.modifiers ^ self.code; -} - -- (BOOL)isEqual:(id)object; -{ - return [self isKindOfClass:[object class]] && [object isKindOfClass:[self class]] && ((SWHotKey *)object).code == self.code && ((SWHotKey *)object).modifiers == self.modifiers; -} - -- (instancetype)copyWithZone:(NSZone *)zone; -{ - return self; -} - -- (NSString *)description; -{ - return [NSString stringWithFormat:@"%@%@", - [self modifierDescription], - [self codeDescription]]; -} - -#pragma mark - Internal - -- (NSString *)codeDescription; -{ - NSString *result = specialKeys[@(self.code)]; - - if (!result) { - TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); - CFDataRef layoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); - const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); - - UInt32 keysDown = 0; - UniChar chars[4]; - UniCharCount realLength; - - UCKeyTranslate(keyboardLayout, - self.code, - kUCKeyActionDisplay, - self.modifiers, - LMGetKbdType(), - kUCKeyTranslateNoDeadKeysBit, - &keysDown, - sizeof(chars) / sizeof(chars[0]), - &realLength, - chars); - CFRelease(currentKeyboard); - - result = [NSString stringWithCharacters:chars length:realLength]; - } - - return result; -} - - -- (NSString *)modifierDescription; -{ - NSString *result = @""; - - if (self.modifiers & SWHotKeyModifierCmd) { - result = [result stringByAppendingString:specialKeys[@(kVK_Command)]]; - } - if (self.modifiers & SWHotKeyModifierShift) { - result = [result stringByAppendingString:specialKeys[@(kVK_Shift)]]; - } - if (self.modifiers & SWHotKeyModifierOption) { - result = [result stringByAppendingString:specialKeys[@(kVK_Option)]]; - } - if (self.modifiers & SWHotKeyModifierControl) { - result = [result stringByAppendingString:specialKeys[@(kVK_Control)]]; - } - - return result; -} - -@end diff --git a/Switch/SWInterfaceController.h b/Switch/SWInterfaceController.h deleted file mode 100644 index 03c997b8..00000000 --- a/Switch/SWInterfaceController.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// SWInterfaceController.h -// Switch -// -// Created by Scott Perry on 10/01/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@class SWInterfaceController; -@class SWWindow; - - -@protocol SWInterfaceControllerDelegate - -- (void)interfaceController:(SWInterfaceController *)controller didSelectWindow:(SWWindow *)window; -- (void)interfaceController:(SWInterfaceController *)controller didActivateWindow:(SWWindow *)window; -- (void)interfaceControllerDidClickOutsideInterface:(SWInterfaceController *)controller; - -@end - - -@interface SWInterfaceController : NSObject - -@property (nonatomic, readwrite, assign, getter=showingInterface, setter=shouldShowInterface:) _Bool showInterface; - -- (instancetype)initWithDelegate:(id)delegate; - -- (void)updateWindowList:(NSOrderedSet *)windowList; -- (void)selectWindow:(SWWindow *)window; -- (void)disableWindow:(SWWindow *)window; -- (void)enableWindow:(SWWindow *)window; - -@end diff --git a/Switch/SWInterfaceController.m b/Switch/SWInterfaceController.m deleted file mode 100644 index 088ab31f..00000000 --- a/Switch/SWInterfaceController.m +++ /dev/null @@ -1,263 +0,0 @@ -// -// SWInterfaceController.m -// Switch -// -// Created by Scott Perry on 10/01/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWInterfaceController.h" - -#import "NSScreen+SWAdditions.h" -#import "SWCoreWindowController.h" -#import "SWEventTap.h" -#import "SWPreferencesService.h" -#import "SWWindow.h" - - -@interface SWInterfaceController () - -@property (nonatomic, readonly, weak) id delegate; - -@property (nonatomic, readonly, strong) NSMutableDictionary *windowControllerCache; -@property (nonatomic, readwrite, strong) NSDictionary *windowControllersByScreenID; -@property (nonatomic, readwrite, strong) id windowControllerDispatcher; - -@property (nonatomic, readwrite, copy) NSOrderedSet *windowList; -@property (nonatomic, readwrite, strong) SWWindow *selectedWindow; - -@end - - -@implementation SWInterfaceController - -- (instancetype)initWithDelegate:(id)delegate; -{ - BailUnless(self = [super init], nil); - - self->_delegate = delegate; - - self->_windowControllerCache = [NSMutableDictionary new]; - BailUnless(self->_windowControllerCache, nil); - - return self; -} - -#pragma mark - SWCoreWindowControllerDelegate - -- (void)coreWindowController:(SWCoreWindowController *)controller didSelectWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - id delegate = self.delegate; - [delegate interfaceController:self didSelectWindow:window]; -} - -- (void)coreWindowController:(SWCoreWindowController *)controller didActivateWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - id delegate = self.delegate; - [delegate interfaceController:self didActivateWindow:window]; -} - -- (void)coreWindowControllerDidClickOutsideInterface:(SWCoreWindowController *)controller; -{ - SWLogMainThreadOnly(); - id delegate = self.delegate; - [delegate interfaceControllerDidClickOutsideInterface:self]; -} - -#pragma mark - SWInterfaceController - -- (void)shouldShowInterface:(_Bool)showInterface; -{ - SWLogMainThreadOnly(); - if (showInterface == self->_showInterface) { return; } - self->_showInterface = showInterface; - - if (showInterface ) { - [self private_showInterface]; - } else { - [self private_hideInterface]; - } -} - -- (void)updateWindowList:(NSOrderedSet *)windowList; -{ - SWLogMainThreadOnly(); - - self.windowList = windowList; - - if (!self.showingInterface) { return; } - - [self private_updateWindowList]; -} - -- (void)selectWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - - self.selectedWindow = window; - - if (!self.showingInterface) { return; } - - [self private_updateSelection]; -} - -- (void)disableWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - - [self.windowControllerDispatcher disableWindow:window]; -} - -- (void)enableWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - - [self.windowControllerDispatcher enableWindow:window]; -} - -#pragma mark - Private - -- (void)private_updateSelection; -{ - SWLogMainThreadOnly(); - - SWWindow *selectedWindow = self.selectedWindow; - [self.windowControllerDispatcher selectWindow:(SWWindow *)selectedWindow]; - Check(selectedWindow); -} - -- (void)private_updateWindowList; -{ - SWLogMainThreadOnly(); - - NSOrderedSet *windowList = self.windowList; - - if (!windowList) { - id dispatch = self.windowControllerDispatcher; - Check(dispatch); - [dispatch updateWindowList:nil]; - return; - } - - // Create a dictionary of ordered sets of windows where each key refers to a screen… - NSDictionary *windowsByScreen = ^{ - NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:self.windowControllersByScreenID.count]; - for (NSNumber *screenNumber in self.windowControllersByScreenID.allKeys) { - result[screenNumber] = [NSMutableOrderedSet new]; - } - return result; - }(); - - // …and each value is a windowList of the windows on that screen. - for (SWWindow *window in windowList) { - NSNumber *screenNumber = @([window screen].sw_screenNumber); - - /** This shouldn't be possible! We just built windowsByScreen. - * I think Past Me intended this to check self.windowControllersByScreenID[screenNumber], - * but even then a controller responsible for the main screen isn't necessarily guaranteed - * to exist if this check failed. - */ - if (!Check([windowsByScreen objectForKey:screenNumber])) { - screenNumber = @([NSScreen mainScreen].sw_screenNumber); - } - - [windowsByScreen[screenNumber] addObject:window]; - } - - for (NSNumber *screenNumber in windowsByScreen.allKeys) { - SWCoreWindowController *windowController = self.windowControllersByScreenID[screenNumber]; - Check(windowController); - windowController.windowList = windowsByScreen[screenNumber]; - } - - [self private_updateSelection]; -} - -- (void)private_showInterface; -{ - SWLogMainThreadOnly(); - Check(self.windowList); - Check(!self.windowControllersByScreenID); - - // Build up a hash of window controllers, each one representing/covering one screen. - self.windowControllersByScreenID = ^{ - NSMutableDictionary *windowControllers = [NSMutableDictionary new]; - NSArray *screens = [SWPreferencesService sharedService].multimonInterface - ? [NSScreen screens] - : @[[NSScreen mainScreen]]; - - for (NSScreen *screen in screens) { - // Cache: Fetch/fulfull window controllers. - SWCoreWindowController *windowController = [self.windowControllerCache objectForKey:@(screen.sw_screenNumber)]; - if (!windowController) { - windowController = [[SWCoreWindowController alloc] initWithScreen:screen]; - self.windowControllerCache[@(screen.sw_screenNumber)] = windowController; - } - - // Initialize window controllers for their screens. - [windowController.window setFrame:screen.frame display:YES]; - windowController.delegate = self; - windowControllers[@(screen.sw_screenNumber)] = windowController; - } - - // Cache: evict window controllers for screens that no longer exist. - for (NSNumber *screenNumber in self.windowControllerCache.allKeys.copy) { - if (![windowControllers.allValues containsObject:self.windowControllerCache[screenNumber]]) { - [self.windowControllerCache removeObjectForKey:screenNumber]; - } - } - - return windowControllers; - }(); - - // Build a dispatch manager responsible for messaging all the window controllers. - self.windowControllerDispatcher = (SWCoreWindowController *)^{ - NNMultiDispatchManager *dispatcher = [[NNMultiDispatchManager alloc] initWithProtocol:@protocol(SWCoreWindowControllerAPI)]; - - for (SWCoreWindowController *windowController in self.windowControllersByScreenID.allValues) { - [dispatcher addObserver:windowController]; - } - - return dispatcher; - }(); - - // Populate window controllers with the data that's already on hand. - [self private_updateWindowList]; - - // layoutSubviewsIfNeeded isn't instant due to Auto Layout magic, so let everything take effect before showing the window. - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.showInterface) { - for (SWCoreWindowController *windowController in self.windowControllersByScreenID.allValues) { - [windowController.window orderFront:self]; - } - } - }); -} - -- (void)private_hideInterface; -{ - SWLogMainThreadOnly(); - Check(self.windowControllersByScreenID); - - // Hide all the windows - for (SWCoreWindowController *windowController in self.windowControllersByScreenID.allValues) { - [windowController.window orderOut:self]; - windowController.delegate = nil; - } - - self.windowList = nil; - [self private_updateWindowList]; - - self.windowControllersByScreenID = nil; - self.windowControllerDispatcher = nil; -} - -@end diff --git a/Switch/SWKeyboardPreferencesViewController.h b/Switch/SWKeyboardPreferencesViewController.h deleted file mode 100644 index 0175b7e6..00000000 --- a/Switch/SWKeyboardPreferencesViewController.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// SWKeyboardPreferencesViewController.h -// Switch -// -// Created by Scott Perry on 04/28/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@interface SWKeyboardPreferencesViewController : NSViewController - -@end diff --git a/Switch/SWKeyboardPreferencesViewController.m b/Switch/SWKeyboardPreferencesViewController.m deleted file mode 100644 index ad478694..00000000 --- a/Switch/SWKeyboardPreferencesViewController.m +++ /dev/null @@ -1,52 +0,0 @@ -// -// SWKeyboardPreferencesViewController.m -// Switch -// -// Created by Scott Perry on 04/28/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWKeyboardPreferencesViewController.h" - - -@interface SWKeyboardPreferencesViewController () - -@end - - -@implementation SWKeyboardPreferencesViewController - -#pragma mark - MASPreferencesViewController - -- (NSString *)identifier -{ - return NSStringFromClass([self class]); -} - -- (NSImage *)toolbarItemImage -{ - return [NSImage imageNamed:@"Keyboard.icns"]; -} - -- (NSString *)toolbarItemLabel -{ - return NSLocalizedString(@"Hotkeys", @"Toolbar item name for the Hotkeys preference pane"); -} - -- (BOOL)hasResizableWidth; -{ - return NO; -} - -- (BOOL)hasResizableHeight; -{ - return NO; -} - -@end diff --git a/Switch/SWKeyboardPreferencesViewController.xib b/Switch/SWKeyboardPreferencesViewController.xib deleted file mode 100644 index 38330d66..00000000 --- a/Switch/SWKeyboardPreferencesViewController.xib +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Switch/SWLoggingService.h b/Switch/SWLoggingService.h deleted file mode 100644 index aee4034f..00000000 --- a/Switch/SWLoggingService.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// SWLoggingService.h -// Switch -// -// Created by Scott Perry on 10/15/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface SWLoggingService : NNService - -- (NSString *)logDirectoryPath; -- (void)rotateLogIfNecessary; -- (void)takeWindowListSnapshot; - -@end - -#define SWLog(fmt, ...) do { \ - [[SWLoggingService sharedService] rotateLogIfNecessary]; \ - Log(fmt, ##__VA_ARGS__); \ - } while(0) - -#define SWLogBackgroundThreadOnly() do { \ - if ([NSThread isMainThread]) { \ - SWLog(@"ERROR: -[%@ %@] was called on the main thread %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [NSThread callStackSymbols]); \ - Assert(![NSThread isMainThread]); \ - } \ - } while(0) - -#define SWLogMainThreadOnly() do { \ - if (![NSThread isMainThread]) { \ - SWLog(@"ERROR: -[%@ %@] was called on a background thread %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [NSThread callStackSymbols]); \ - Assert([NSThread isMainThread]); \ - } \ - } while(0) - -#define SWCodeBlock(...) __VA_ARGS__ - -#define SWTimeTask(codeBlock, fmt, ...) do { \ - NSDate *start = [NSDate new]; \ - codeBlock \ - NSString *logmsg = [NSString stringWithFormat:fmt, ##__VA_ARGS__]; \ - NSTimeInterval elapsed = -[start timeIntervalSinceNow]; \ - if (elapsed > (1.0 / ([NSThread isMainThread] ? 60.0 : 10.0))) { \ - SWLog(@"%@ took %.3fs", logmsg, elapsed); \ - } \ - } while(0) diff --git a/Switch/SWLoggingService.m b/Switch/SWLoggingService.m deleted file mode 100644 index 5467762f..00000000 --- a/Switch/SWLoggingService.m +++ /dev/null @@ -1,269 +0,0 @@ -// -// SWLoggingService.m -// Switch -// -// Created by Scott Perry on 10/15/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWLoggingService.h" - -#import "SWWindow.h" -#import "SWWindowListService.h" - - -@interface SWLoggingService () - -@property (nonatomic, strong) NSDateComponents *logDate; - -@end - - -@implementation SWLoggingService - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -#pragma mark - SWLoggingService - -- (NSString *)logDirectoryPath; -{ - NSString *libraryLogsPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; - return [libraryLogsPath stringByAppendingPathComponent:@"Logs/Switch"]; -} - -- (void)rotateLogIfNecessary; -{ - if (![self private_dayChanged]) { return; } - - // In theory this code is running a race against other invocations of -rotateLogIfNecessary, but -_dayChanged returns YES so infrequently that it's not worth worrying about. The worst possible race condition is if the first time this is called in the program's lifetime is at 23:59:59.9999 and then again at 00:00:00. - NSString *logDir = [self logDirectoryPath]; - BailUnless([self private_createDirectory:logDir],); - - // Remove old log files. - NSTimeInterval longTime = 671993.28; - NSFileManager *manager = [NSFileManager defaultManager]; - for (NSString *file in [manager enumeratorAtPath:logDir]) { - NSDate *creationDate = [[manager attributesOfItemAtPath:[logDir stringByAppendingPathComponent:file] error:nil] fileCreationDate]; - - if ([[NSDate date] timeIntervalSinceDate:creationDate] > longTime && [[file pathExtension] isEqualToString:@"log"]) { - NSError *error = nil; - NSString *absolutePath = [logDir stringByAppendingPathComponent:file]; - - if (![manager removeItemAtPath:absolutePath error:&error]) { - Log(@"Failed to remove log file %@: %@", absolutePath, error); - } - } - } - - // Do not redirect output if attached to a console. - if (isatty(STDERR_FILENO)) { return; } - freopen([[self private_logFilePath] cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr); -} - -- (void)takeWindowListSnapshot; -{ - NSArray *rawList = ^{ - CFArrayRef cgInfo = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); - return CFBridgingRelease(cgInfo); - }(); - - NSOrderedSet *windowList = [SWWindowListService filterInfoDictionariesToWindowObjects:rawList]; - NSOrderedSet *windowGroupList = [SWWindowListService filterWindowObjectsToWindowGroups:windowList]; - NSOrderedSet *sortedGroupList = [SWWindowListService sortedWindowGroups:windowGroupList]; - - NSString *snapshotDir = [[self logDirectoryPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"snapshot-%llu", (uint64_t)[[NSDate date] timeIntervalSince1970]]]; - BailUnless([self private_createDirectory:snapshotDir],); - - NSString *listFile = [snapshotDir stringByAppendingPathComponent:@"windowlist.txt"]; - - NSFileManager *manager = [NSFileManager defaultManager]; - - BailWithBlockUnless([manager createFileAtPath:listFile contents:nil attributes:nil], ^{ - Log(@"Failed to create file %@", listFile); - }); - NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:listFile]; - BailWithBlockUnless(handle, ^{ - Log(@"Failed to open %@ for writing", listFile); - }); - - [handle writeData:[@"Raw list:\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[[self private_formattedWindowList:rawList] dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[@"\nWindow list:\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[[windowList debugDescription] dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[@"\n\nWindow group list:\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[[windowGroupList debugDescription] dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[@"\n\nSorted window group list:\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[[sortedGroupList debugDescription] dataUsingEncoding:NSUTF8StringEncoding]]; - [handle writeData:[@"\n\n" dataUsingEncoding:NSUTF8StringEncoding]]; - - // TODO: log screen information as well - - for (NSDictionary *description in rawList) { - CGWindowID windowID = (CGWindowID)[[description objectForKey:(__bridge NSString *)kCGWindowNumber] unsignedLongValue]; - CGImageRef cgContents = NNCFAutorelease(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming)); - - if (!cgContents) { - [handle writeData:[[NSString stringWithFormat:@"Failed to save window contents for window ID %u: CGWindowListCreateImage returned NULL\n", windowID] dataUsingEncoding:NSUTF8StringEncoding]]; - continue; - } - - if (CGImageGetHeight(cgContents) < 1.0 || CGImageGetWidth(cgContents) < 1.0) { - [handle writeData:[[NSString stringWithFormat:@"Failed to save window contents for window ID %u: image is zero size\n", windowID] dataUsingEncoding:NSUTF8StringEncoding]]; - continue; - } - - NSImage *contents = [[NSImage alloc] initWithCGImage:cgContents size:(NSSize){.width = CGImageGetWidth(cgContents), .height = CGImageGetHeight(cgContents)}]; -// [NSArchiver archiveRootObject:contents toFile:[snapshotDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%u.ar", windowID]]]; - [contents lockFocus]; - NSBitmapImageRep *bitmapContents = [[NSBitmapImageRep alloc] initWithFocusedViewRect:(CGRect){.origin = NSZeroPoint, .size = contents.size}]; - [contents unlockFocus]; - NSData *pngContents = [bitmapContents representationUsingType:NSPNGFileType properties:@{}]; - if (!pngContents) { - [handle writeData:[[NSString stringWithFormat:@"Failed to save window contents for window ID %u: conversion to png failed\n", windowID] dataUsingEncoding:NSUTF8StringEncoding]]; - } - - if (![manager createFileAtPath:[snapshotDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%u.png", windowID]] contents:pngContents attributes:nil]) { - [handle writeData:[[NSString stringWithFormat:@"Failed to save window contents for window ID %u: file creation failure\n", windowID] dataUsingEncoding:NSUTF8StringEncoding]]; - } - } - - handle = nil; -} - -#pragma mark - Internal - -- (NSString *)private_logFilePath; -{ - Assert(self.logDate); - - NSString *filename = [NSString stringWithFormat:@"Switch-%02ld-%02ld-%02ld.log", self.logDate.year, self.logDate.month, self.logDate.day]; - return [[self logDirectoryPath] stringByAppendingPathComponent:filename]; -} - -- (BOOL)private_dayChanged; -{ - @synchronized(self) { - NSDateComponents *todaysComponents = ^{ - NSDate *today = [NSDate date]; - static NSCalendar *gregorian; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; - gregorian.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; - }); - return [gregorian components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:today]; - }(); - - if (![todaysComponents isEqual:self.logDate]) { - self.logDate = todaysComponents; - return YES; - } - return NO; - } -} - -- (BOOL)private_createDirectory:(NSString *)path; -{ - NSFileManager *manager = [NSFileManager defaultManager]; - - // Create and verify log directory, if it doesn't already exist. - if (![manager fileExistsAtPath:path]) { - [manager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; - } - - BOOL isDirectory = NO; - BailWithBlockUnless([manager fileExistsAtPath:path isDirectory:&isDirectory], ^{ - Log(@"Directory %@ does not exist, even after attempting to create it!", path); - return NO; - }); - BailWithBlockUnless(isDirectory, ^{ - Log(@"Could not create directory %@ because a non-folder file already exists at that path.", path); - return NO; - }); - - return YES; -} - -- (NSString *)private_formattedWindowList:(NSArray *)rawList; -{ - NSMutableString *result = [@"@[\n" mutableCopy]; - - for (NSDictionary *windowDescription in rawList) { - [result appendString:@"\t@{\n"]; - - for (NSString *key in windowDescription) { - [result appendString:@"\t\t"]; - [result appendString:[key stringByReplacingOccurrencesOfString:@"kCG" withString:@"NN"]]; - [result appendString:@" : "]; - - id value = windowDescription[key]; - - [result appendString:@"@"]; - if ([value isKindOfClass:[NSNumber class]]) { - const char *type = [(NSNumber *)value objCType]; - - if (!strcmp(type, "q")) { - [result appendFormat:@"%lld", [value longLongValue]]; - } else if (!strcmp(type, "c")) { - [result appendFormat:@"%d", [value charValue]]; - } else if (!strcmp(type, "d")) { - [result appendFormat:@"%f", [value doubleValue]]; - } else if (!strcmp(type, "i")) { - [result appendFormat:@"%d", [value intValue]]; - } else { - NotTested(); - } - } else if ([value isKindOfClass:[NSString class]]) { - [result appendFormat:@"\"%@\"", value]; - } else if ([value isKindOfClass:[NSDictionary class]]) { - [result appendString:@"{\n"]; - - Check(!strcmp([((NSDictionary *)value)[@"Height"] objCType], "d")); - [result appendString:@"\t\t\t@\"Height\" : @"]; - [result appendFormat:@"%d", [((NSDictionary *)value)[@"Height"] intValue]]; - [result appendString:@",\n"]; - - Check(!strcmp([((NSDictionary *)value)[@"Width"] objCType], "d")); - [result appendString:@"\t\t\t@\"Width\" : @"]; - [result appendFormat:@"%d", [((NSDictionary *)value)[@"Width"] intValue]]; - [result appendString:@",\n"]; - - Check(!strcmp([((NSDictionary *)value)[@"X"] objCType], "d")); - [result appendString:@"\t\t\t@\"X\" : @"]; - [result appendFormat:@"%d", [((NSDictionary *)value)[@"X"] intValue]]; - [result appendString:@",\n"]; - - Check(!strcmp([((NSDictionary *)value)[@"Y"] objCType], "d")); - [result appendString:@"\t\t\t@\"Y\" : @"]; - [result appendFormat:@"%d", [((NSDictionary *)value)[@"Y"] intValue]]; - [result appendString:@",\n"]; - - [result appendString:@"\t\t}"]; - } else { - SWLog(@"Value is of type %@", NSStringFromClass([value class])); - NotTested(); - } - - [result appendString:@",\n"]; - } - - [result appendString:@"\t},\n"]; - } - - [result appendString:@"];"]; - - return result; -} - -@end diff --git a/Switch/SWPreferencesService.h b/Switch/SWPreferencesService.h deleted file mode 100644 index 1e2baa47..00000000 --- a/Switch/SWPreferencesService.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// SWPreferencesService.h -// Switch -// -// Created by Scott Perry on 10/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@class SWPreferencesService; - - -@interface SWPreferencesService : NNService - -@property (nonatomic, assign, readwrite) _Bool multimonInterface; -@property (nonatomic, assign, readwrite) _Bool multimonGroupByMonitor; -@property (nonatomic, assign, readwrite) _Bool showStatusItem; -@property (nonatomic, strong, readwrite, null_resettable) NSString *appcastURL; - -- (void)showPreferencesWindow:(nonnull id)sender; - -@end diff --git a/Switch/SWPreferencesService.m b/Switch/SWPreferencesService.m deleted file mode 100644 index 98d43f46..00000000 --- a/Switch/SWPreferencesService.m +++ /dev/null @@ -1,164 +0,0 @@ -// -// SWPreferencesService.m -// Switch -// -// Created by Scott Perry on 10/20/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWPreferencesService.h" - -#import -#import - -#import "SWAPIEnabledWorker.h" -#import "SWAdvancedPreferencesViewController.h" -#import "SWGeneralPreferencesViewController.h" -#import "SWKeyboardPreferencesViewController.h" - - -/** - * Defining new preferences: - * • Put a property in the header. Do not set a fancy getter or setter or you'll have to define shim methods yourself. - * • Add a key to the list below. - * • Generate a getter/setter under the "Preferences: setters" pragma. - * • Set a default value in -defaultValues - */ - - -static NSString const * const kSWFirstLaunchKey = @"firstLaunch"; -static NSString const * const kSWMultimonInterfaceKey = @"multimonInterface"; -static NSString const * const kSWMultimonGroupByMonitorKey = @"multimonGroupByMonitor"; -static NSString const * const kSWShowStatusItemKey = @"showStatusItem"; -static NSString const * const kSWAppcastURLKey = @"SUFeedURL"; - - -@interface SWPreferencesService () - -@property (nonatomic, strong) MASPreferencesWindowController *preferencesWindowController; - -@end - - -@implementation SWPreferencesService - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -- (instancetype)init; -{ - BailUnless(self = [super init], nil); - - [[NSUserDefaults standardUserDefaults] registerDefaults:self.defaultValues]; - - return self; -} - -- (void)startService; -{ - [super startService]; - - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - -# if DEBUG - { - BOOL resetDefaults = NO; - - if (resetDefaults) { - for (NSString *key in self.defaultValues.allKeys) { - [defaults removeObjectForKey:key]; - } - } - } -# endif - - NSViewController *(^prefPaneForClass)(Class) = ^(Class class){ - return [[class alloc] initWithNibName:NSStringFromClass(class) bundle:[NSBundle mainBundle]]; - }; - NSViewController *generalViewController = prefPaneForClass([SWGeneralPreferencesViewController class]); - NSViewController *keyboardViewController = prefPaneForClass([SWKeyboardPreferencesViewController class]); - NSViewController *advancedViewController = prefPaneForClass([SWAdvancedPreferencesViewController class]); - NSArray *controllers = @[generalViewController, keyboardViewController, advancedViewController]; - NSString *title = NSLocalizedString(@"Preferences", @"Common title for Preferences window"); - - self.preferencesWindowController = [[MASPreferencesWindowController alloc] initWithViewControllers:controllers title:title]; - - if ([defaults boolForKey:[kSWFirstLaunchKey copy]] && [SWAPIEnabledWorker isAPIEnabled]) { - [self private_setObject:@NO forKey:kSWFirstLaunchKey]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self showPreferencesWindow:self]; - }); - } - - [defaults synchronize]; -} - -#pragma mark - Preferences: setters - -#define generateObjectPropertyMethods(setter, property, key) \ -- (void)setter(id)property; { [self private_setObject:property forKey:key]; } \ -- (id)property; { return [self private_objectForKey:key]; } - -#define generateBoolPropertyMethods(setter, property, key) \ -generateObjectPropertyMethods(GENERATED_obj_##setter, GENERATED_obj_##property, key) \ -- (void)setter(_Bool)property; { [self GENERATED_obj_##setter@(property)]; } \ -- (_Bool)property; { return [[self GENERATED_obj_##property] boolValue]; } - -generateBoolPropertyMethods(setMultimonInterface:, multimonInterface, kSWMultimonInterfaceKey) -generateBoolPropertyMethods(setMultimonGroupByMonitor:, multimonGroupByMonitor, kSWMultimonGroupByMonitorKey) -generateBoolPropertyMethods(setShowStatusItem:, showStatusItem, kSWShowStatusItemKey) -generateObjectPropertyMethods(setAppcastURL:, appcastURL, kSWAppcastURLKey) - -#pragma mark Preferences: default values - -- (NSDictionary *)defaultValues; -{ - static NSDictionary *_defaultValues = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _defaultValues = @{ - kSWFirstLaunchKey : @YES, - kSWMultimonInterfaceKey : @YES, - kSWMultimonGroupByMonitorKey : @NO, - kSWShowStatusItemKey : @YES, - kSWAppcastURLKey : @"https://raw.github.com/numist/Switch/develop/appcast.xml", - }; - }); - return _defaultValues; -} - -#pragma mark - SWPreferencesService - -- (void)showPreferencesWindow:(id)sender; -{ - [self.preferencesWindowController showWindow:sender]; - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; - [self.preferencesWindowController.window makeKeyAndOrderFront:sender]; -} - -#pragma mark - Internal - -- (void)private_setObject:(id)object forKey:(NSString const * const)key; -{ - if (object == nil) { - object = self.defaultValues[key]; - } - [[NSUserDefaults standardUserDefaults] setObject:object forKey:[key copy]]; -} - -- (id)private_objectForKey:(NSString const * const)key; -{ - return [[NSUserDefaults standardUserDefaults] objectForKey:[key copy]]; -} - -@end diff --git a/Switch/SWRoundedRectView.h b/Switch/SWRoundedRectView.h deleted file mode 100644 index 42cd5503..00000000 --- a/Switch/SWRoundedRectView.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// SWRoundedRectView.h -// Switch -// -// Created by Scott Perry on 03/01/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@interface SWRoundedRectView : NSView - -@property (nonatomic, assign) CGFloat border; -@property (nonatomic, assign) CGFloat radius; - -@end diff --git a/Switch/SWRoundedRectView.m b/Switch/SWRoundedRectView.m deleted file mode 100644 index da2af255..00000000 --- a/Switch/SWRoundedRectView.m +++ /dev/null @@ -1,44 +0,0 @@ -// -// SWRoundedRectView.m -// Switch -// -// Created by Scott Perry on 03/01/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWRoundedRectView.h" - - -@implementation SWRoundedRectView - -#pragma mark - Initialization - -- (instancetype)initWithFrame:(CGRect)frame; -{ - BailUnless(self = [super initWithFrame:frame], nil); - - [self setWantsLayer:YES]; - - return self; -} - -#pragma mark - NSView - -- (void)updateLayer; -{ - if (self.border > 0) { - self.layer.borderWidth = self.border; - self.layer.borderColor = [[[NSColor whiteColor] colorWithAlphaComponent:0.8] CGColor]; - } - - self.layer.cornerRadius = self.radius; - self.layer.backgroundColor = [[[NSColor blackColor] colorWithAlphaComponent:0.3] CGColor]; -} - -@end diff --git a/Switch/SWScrollControl.h b/Switch/SWScrollControl.h deleted file mode 100644 index 39bf80d4..00000000 --- a/Switch/SWScrollControl.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// SWScrollControl.h -// Switch -// -// Created by Scott Perry on 09/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@interface SWScrollControl : NSObject - -- (instancetype)initWithThreshold:(NSInteger)threshold incHandler:(dispatch_block_t)incBlock decHandler:(dispatch_block_t)decBlock; - -- (void)feed:(NSInteger)numEvents; -- (void)reset; - -@end diff --git a/Switch/SWScrollControl.m b/Switch/SWScrollControl.m deleted file mode 100644 index 42b4fdf4..00000000 --- a/Switch/SWScrollControl.m +++ /dev/null @@ -1,74 +0,0 @@ -// -// SWScrollControl.m -// Switch -// -// Created by Scott Perry on 09/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWScrollControl.h" - - -static NSInteger const kMaxThreshold = NSIntegerMax / 2; - - -@interface SWScrollControl () - -@property (nonatomic, readonly, strong) dispatch_block_t inc; -@property (nonatomic, readonly, strong) dispatch_block_t dec; -@property (nonatomic, readonly, assign) NSInteger threshold; -@property (nonatomic, readwrite, assign) NSInteger offset; - -@end - - -@implementation SWScrollControl - -- (instancetype)initWithThreshold:(NSInteger)threshold incHandler:(dispatch_block_t)incBlock decHandler:(dispatch_block_t)decBlock; -{ - BailUnless(self = [super init], nil); - - self->_inc = incBlock; - self->_dec = decBlock; - self->_threshold = labs(threshold); - - if (self->_threshold > kMaxThreshold) { - NSLog(@"%@ does not support thresholds larger than %ld", NSStringFromClass([self class]), kMaxThreshold); - return nil; - } - - return self; -} - -- (void)feed:(NSInteger)numEvents; -{ - NSInteger units = (numEvents / self.threshold); - numEvents %= self.threshold; - - self.offset += numEvents; - - units += (self.offset / self.threshold); - self.offset %= self.threshold; - - while (units > 0) { - self.inc(); - units--; - } - while (units < 0) { - self.dec(); - units++; - } -} - -- (void)reset; -{ - self.offset = 0; -} - -@end diff --git a/Switch/SWSelectionBoxView.h b/Switch/SWSelectionBoxView.h deleted file mode 100644 index 453c88b4..00000000 --- a/Switch/SWSelectionBoxView.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// SWSelectionBoxView.h -// Switch -// -// Created by Scott Perry on 03/02/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWRoundedRectView.h" - - -@interface SWSelectionBoxView : SWRoundedRectView - -@end diff --git a/Switch/SWSelectionBoxView.m b/Switch/SWSelectionBoxView.m deleted file mode 100644 index 6434a4f9..00000000 --- a/Switch/SWSelectionBoxView.m +++ /dev/null @@ -1,33 +0,0 @@ -// -// SWSelectionBoxView.m -// Switch -// -// Created by Scott Perry on 03/02/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWSelectionBoxView.h" - - -@implementation SWSelectionBoxView - -#pragma mark - Initialization - -- (id)initWithFrame:(CGRect)frame -{ - BailUnless(self = [super initWithFrame:frame], nil); - - self.border = kNNItemBorderWidth; - self.radius = kNNSelectionRoundRectRadius; - self.translatesAutoresizingMaskIntoConstraints = NO; - - return self; -} - -@end diff --git a/Switch/SWSelector.h b/Switch/SWSelector.h deleted file mode 100644 index f21f736c..00000000 --- a/Switch/SWSelector.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// SWSelector.h -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@class SWWindow; - - -@interface SWSelector : NSObject - -@property (nonatomic, copy, readonly) SWWindow *selectedWindow; -@property (nonatomic, assign, readonly) NSInteger selectedIndex; -@property (nonatomic, copy, readonly) NSOrderedSet *windowList; - -- (NSUInteger)selectedUIndex; - -- (instancetype)increment; -- (instancetype)incrementWithoutWrapping; -- (instancetype)decrement; -- (instancetype)decrementWithoutWrapping; -- (instancetype)selectIndex:(NSInteger)index; -- (instancetype)updateWithWindowList:(NSOrderedSet *)windowList; - -@end diff --git a/Switch/SWSelector.m b/Switch/SWSelector.m deleted file mode 100644 index 7ab06828..00000000 --- a/Switch/SWSelector.m +++ /dev/null @@ -1,195 +0,0 @@ -// -// SWSelector.m -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWSelector.h" -#import "SWWindow.h" - - -@implementation SWSelector - -#pragma mark - Initialization - -- (instancetype)initWithWindowList:(NSOrderedSet *)windowList selectedIndex:(NSInteger)index; -{ - BailUnless(self = [super init], nil); - - if ((windowList && !windowList.count) || index == NSNotFound) { - Check(index == NSNotFound); - index = NSNotFound; - } else if (windowList.count) { - while (index < 0) { - index += windowList.count; - } - if ((NSUInteger)index >= windowList.count) { - index = index % (NSInteger)windowList.count; - } - } - - _windowList = windowList; - _selectedIndex = index; - _selectedWindow = (NSUInteger)index >= windowList.count ? nil : windowList[(NSUInteger)index]; - - return self; -} - -- (instancetype)init; -{ - return [self initWithWindowList:nil selectedIndex:0]; -} - -#pragma mark - SWSelector - -- (NSUInteger)selectedUIndex; -{ - if (self.selectedIndex < 0) { - @throw [self private_exceptionForInvalidIndex:self.selectedIndex functionName:__PRETTY_FUNCTION__]; - } - - return (NSUInteger)self.selectedIndex; -} - -- (instancetype)increment; -{ - NSInteger newSelectedIndex = (self.selectedIndex == NSNotFound) - ? 0 - : self.selectedIndex + 1; - - if (self.windowList && !self.windowList.count) { - Check(self.selectedIndex == NSNotFound); - return self; - } - - return [[[self class] alloc] initWithWindowList:self.windowList selectedIndex:newSelectedIndex]; -} - -- (instancetype)incrementWithoutWrapping; -{ - if (!self.windowList) { - return [self increment]; - } - - if (self.selectedUIndex != (self.windowList.count - 1)) { - return [self increment]; - } - - return self; -} - -- (instancetype)decrement; -{ - Check(!self.windowList || self.selectedUIndex < self.windowList.count || self.selectedIndex == NSNotFound); - - NSInteger newSelectedIndex = (self.selectedIndex == NSNotFound) - ? (NSInteger)self.windowList.count - 1 - : self.selectedIndex - 1; - - if (self.windowList && !self.windowList.count) { - Check(self.selectedIndex == NSNotFound); - return self; - } - - return [[[self class] alloc] initWithWindowList:self.windowList selectedIndex:newSelectedIndex]; -} - -- (instancetype)decrementWithoutWrapping; -{ - if (!self.windowList) { - return [self decrement]; - } - - if (self.selectedIndex != 0) { - return [self decrement]; - } - - return self; -} - -- (instancetype)selectIndex:(NSInteger)index; -{ - if (self.windowList) { - if (!self.windowList.count) { - Check(self.selectedIndex == NSNotFound); - if (index != NSNotFound) { - @throw [self private_exceptionForInvalidIndex:index functionName:__PRETTY_FUNCTION__]; - } - index = NSNotFound; - } else if (index > (NSInteger)self.windowList.count) { - if (index != NSNotFound) { - @throw [self private_exceptionForInvalidIndex:index functionName:__PRETTY_FUNCTION__]; - } - index = NSNotFound; - } else { - if (index < 0) { - @throw [self private_exceptionForInvalidIndex:index functionName:__PRETTY_FUNCTION__]; - } - index = MAX(index, 0); - - Check(index < (NSInteger)self.windowList.count); - index = MIN(index, (NSInteger)self.windowList.count - 1); - } - } - - return [[[self class] alloc] initWithWindowList:self.windowList selectedIndex:index]; -} - -- (instancetype)updateWithWindowList:(NSOrderedSet *)windowList; -{ - // Carry over the current selected index by default. - NSInteger newSelectedIndex = self.selectedIndex; - NSUInteger selectedWindowIndex; - - if (windowList && !windowList.count) { - // Empty set? Selected window not found. - newSelectedIndex = NSNotFound; - } else if (!Check(windowList) || !self.windowList) { - newSelectedIndex = self.selectedIndex; - } else if ((selectedWindowIndex = [self private_indexOfWindow:self.selectedWindow inWindowList:windowList]) != NSNotFound) { - // Select the same window group as was previously selected, if it's still there. - newSelectedIndex = (NSInteger)selectedWindowIndex; - } else if ((NSInteger)windowList.count <= newSelectedIndex) { - // Clamp the selected index to the end of the window list. - newSelectedIndex = (NSInteger)windowList.count - 1; - } - - return [[[self class] alloc] initWithWindowList:windowList selectedIndex:newSelectedIndex]; -} - -- (NSUInteger)private_indexOfWindow:(id)selectedWindow inWindowList:(NSOrderedSet *)windowList; -{ - if (selectedWindow == nil) { - return NSNotFound; - } - if (!Check(windowList != nil)) { - return NSNotFound; - } - - // Most of the unit tests for this class just shove NSNumbers into the collections instead of SWWindow objects - if (![selectedWindow respondsToSelector:NNTypedSelector1(SWWindow, isSameWindow:)]) { - return [windowList indexOfObject:selectedWindow]; - } - - return [windowList indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { - return [selectedWindow isSameWindow:obj]; - }]; -} - -- (NSException *)private_exceptionForInvalidIndex:(NSInteger)index functionName:(const char *)functionName; -{ - if (self.windowList.count == 0) { - return [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"%s: index %lu out of bounds for empty window list", functionName, index] userInfo:nil]; - } - return [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"%s: index %lu beyond bounds [%d .. %lu]", functionName, index, 0, self.windowList.count - 1] userInfo:nil]; -} - -@end diff --git a/Switch/SWStateMachine.h b/Switch/SWStateMachine.h deleted file mode 100644 index 586454dd..00000000 --- a/Switch/SWStateMachine.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// SWStateMachine.h -// Switch -// -// Created by Scott Perry on 09/26/14. -// Copyright © 2014 Scott Perry. -// -// 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 - - -@class SWStateMachine, SWWindow; - - -typedef NS_ENUM(uint8_t, SWIncrementDirection) { - SWIncrementDirectionIncreasing, - SWIncrementDirectionDecreasing -}; - - -@protocol SWStateMachineDelegate - -- (void)stateMachineWantsDisplayTimerStarted:(SWStateMachine *)stateMachine; -// Delegate may call -displayTimerCompleted sometime between these two calls to show display. -- (void)stateMachineWantsDisplayTimerInvalidated:(SWStateMachine *)stateMachine; - -- (void)stateMachine:(SWStateMachine *)stateMachine wantsWindowRaised:(SWWindow *)window; -- (void)stateMachine:(SWStateMachine *)stateMachine wantsWindowClosed:(SWWindow *)window; - -@end - - -@interface SWStateMachine : NSObject - -+ (instancetype)stateMachineWithDelegate:(id) delegate; - -@property (nonatomic, readonly, assign, getter=wantsInterfaceVisible) _Bool interfaceVisible; -@property (nonatomic, readonly, assign, getter=wantsWindowListUpdates) _Bool windowListUpdates; - -- (instancetype)initWithDelegate:(id) delegate NS_DESIGNATED_INITIALIZER; -- (instancetype)init UNAVAILABLE_ATTRIBUTE; - -#pragma mark - Delegate responses -- (void)displayTimerCompleted; - -#pragma mark - Keyboard interactions -// Key events return whether the event should be allowed to propagate. -- (void)incrementWithInvoke:(_Bool)invokesInterface direction:(SWIncrementDirection)direction isRepeating:(_Bool)autorepeat; -- (void)closeWindow; -- (void)cancelInvocation; -- (void)endInvocation; - -#pragma mark - GUI interactions -- (void)selectWindow:(SWWindow *)window; -- (void)activateWindow:(SWWindow *)window; - -#pragma mark - Data updates -- (void)updateWindowList:(NSOrderedSet *)windowList; - -@end - -@interface SWStateMachine (Observables) - -// Valid when .interfaceVisible == true or .pendingSwitch == true -@property (nonatomic, readonly, copy) NSOrderedSet *windowList; -// Valid when .interfaceVisible == true or .pendingSwitch == true -@property (nonatomic, readonly, copy) SWWindow *selectedWindow; - -@property (nonatomic, readonly, assign) _Bool invoked; -@property (nonatomic, readonly, assign, getter=wantsDisplayTimer) _Bool displayTimer; -@property (nonatomic, readonly, assign) _Bool pendingSwitch; -@property (nonatomic, readonly, assign, getter=isActive) _Bool active; - -@end diff --git a/Switch/SWStateMachine.m b/Switch/SWStateMachine.m deleted file mode 100644 index 2fdaadde..00000000 --- a/Switch/SWStateMachine.m +++ /dev/null @@ -1,384 +0,0 @@ -// -// SWStateMachine.m -// Switch -// -// Created by Scott Perry on 09/26/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWApplication.h" -#import "SWSelector.h" -#import "SWStateMachine.h" -#import "SWWindow.h" - - -#ifdef STATE_MACHINE_DEBUG - #define StateLog NSLog -#else - #define StateLog(...) -#endif - - -@interface SWStateMachine () - -@property (nonatomic, readwrite, weak) id delegate; -@property (nonatomic, readwrite, copy) NSOrderedSet *windowList; -@property (nonatomic, readwrite, copy) SWWindow *selectedWindow; - -#pragma mark - Core state - -@property (nonatomic, readwrite, assign) _Bool invoked; -@property (nonatomic, readwrite, assign, getter=wantsDisplayTimer) _Bool displayTimer; -@property (nonatomic, readwrite, assign) _Bool pendingSwitch; -@property (nonatomic, readwrite, assign) _Bool windowListLoaded; -@property (nonatomic, readwrite, assign, getter=wantsWindowListUpdates) _Bool windowListUpdates; - -#pragma mark - Dependant state - -@property (nonatomic, readwrite, assign, getter=isActive) _Bool active; -@property (nonatomic, readwrite, assign, getter=wantsInterfaceVisible) _Bool interfaceVisible; - -#pragma mark - Selector state - -@property (nonatomic, readwrite, assign) _Bool selectorAdjusted; -@property (nonatomic, readwrite, strong) SWSelector *selector; -@property (nonatomic, readwrite, assign) int scrollOffset; - -@end - - -@implementation SWStateMachine - -+ (instancetype)stateMachineWithDelegate:(id) delegate; -{ - return [[self alloc] initWithDelegate:delegate]; -} - -- (instancetype)initWithDelegate:(id) delegate; -{ - BailUnless(self = [super init], nil); - - self.delegate = delegate; - - return self; -} - -- (void)setActive:(_Bool)active; -{ - SWLogMainThreadOnly(); - if (active == self.active) { return; } - self->_active = active; - - id delegate = self.delegate; - - if (active) { - // This shouldn't ever happen because of pendingSwitch. - Check(self.invoked); - - if (!Check(!self.displayTimer)) { - [delegate stateMachineWantsDisplayTimerInvalidated:self]; - } - [delegate stateMachineWantsDisplayTimerStarted:self]; - self.displayTimer = true; - - Check(!self.selector); - self.selector = [SWSelector new]; - - Check(!self.windowList.count); - Check(!self.windowListLoaded); - self.windowListUpdates = true; - } - - [self private_updateInterfaceVisible]; - - if (!active) { - self.windowListUpdates = false; - [self private_updateWindowList:nil]; - Check(!self.windowList.count); - Check(!self.windowListLoaded); - - if (self.displayTimer) { - [delegate stateMachineWantsDisplayTimerInvalidated:self]; - self.displayTimer = false; - } - - self.selector = nil; - self.pendingSwitch = false; - } -} - -- (void)setInterfaceVisible:(_Bool)interfaceVisible; -{ - SWLogMainThreadOnly(); - if (interfaceVisible == self.interfaceVisible) { return; } - self->_interfaceVisible = interfaceVisible; - - if (interfaceVisible) { - [self private_adjustSelectorIfNecessary]; - } -} - -- (void)setInvoked:(bool)invoked { - SWLogMainThreadOnly(); - if (invoked == _invoked) { - return; - } - _invoked = invoked; - - [self private_updateActive]; -} - -- (void)setPendingSwitch:(bool)pendingSwitch { - SWLogMainThreadOnly(); - if (pendingSwitch == _pendingSwitch) { - return; - } - _pendingSwitch = pendingSwitch; - - [self private_updateActive]; - [self private_raiseWindowIfNeeded]; -} - -- (void)setDisplayTimer:(bool)displayTimer { - SWLogMainThreadOnly(); - if (displayTimer == _displayTimer) { - return; - } - _displayTimer = displayTimer; - - [self private_updateInterfaceVisible]; -} - -- (void)setWindowListLoaded:(bool)windowListLoaded { - SWLogMainThreadOnly(); - if (windowListLoaded == _windowListLoaded) { - return; - } - _windowListLoaded = windowListLoaded; - - [self private_updateInterfaceVisible]; - [self private_raiseWindowIfNeeded]; -} - -- (void)setSelector:(SWSelector *)selector { - SWLogMainThreadOnly(); - if (selector == _selector) { - return; - } - _selector = selector; - - [self private_updateSelectedWindow]; -} - -#pragma mark - Feedback - -- (void)displayTimerCompleted; -{ - SWLogMainThreadOnly(); - if (!self.pendingSwitch) { - StateLog(@"State machine display timer completed"); - self.displayTimer = false; - } -} - -#pragma mark - Keyboard interactions - -- (void)incrementWithInvoke:(_Bool)invokesInterface direction:(SWIncrementDirection)direction isRepeating:(_Bool)autorepeat; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine key event with invoke:%@ direction:%@ repeating:%@", - invokesInterface ? @"true" : @"false", - direction == SWIncrementDirectionIncreasing ? @"increasing" : @"decreasing", - autorepeat ? @"true" : @"false"); - - if (invokesInterface) { - self.invoked = true; - } - - if (self.pendingSwitch) { - self.invoked = true; - self.pendingSwitch = false; - } - - if (autorepeat) { - self.selector = direction == SWIncrementDirectionIncreasing ? self.selector.incrementWithoutWrapping : self.selector.decrementWithoutWrapping; - } else { - self.selector = direction == SWIncrementDirectionIncreasing ? self.selector.increment : self.selector.decrement; - } - - self.scrollOffset = 0; -} - -- (void)closeWindow; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine event close window"); - - if (self.interfaceVisible) { - if (self.selectedWindow) { - id delegate = self.delegate; - [delegate stateMachine:self wantsWindowClosed:self.selectedWindow]; - } - } -} - -- (void)cancelInvocation; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine event cancel invocation"); - - if (self.invoked) { - self.invoked = false; - } -} - -- (void)endInvocation; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine event end invocation"); - - if (self.invoked) { - self.pendingSwitch = true; - self.invoked = false; - } -} - -#pragma mark - GUI interactions - -- (void)selectWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine mouse select window group: %@", window); - - if (!self.windowList) { return; } - - [self private_adjustSelectorIfNecessary]; - - NSUInteger index = [self.selector.windowList indexOfObject:window]; - Check(index < self.selector.windowList.count || index == NSNotFound); - self.selector = [self.selector selectIndex:(NSInteger)index]; -} - -- (void)activateWindow:(SWWindow *)window; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine mouse activate window group: %@", window); - - if (!self.windowList) { return; } - - if (![self.selector.selectedWindow isEqual:window]) { - [self selectWindow:window]; - } - - if ([self.windowList containsObject:window]) { - self.pendingSwitch = true; - // Clicking on an item cancels the keyboard invocation. - self.invoked = false; - } -} - -#pragma mark - Data updates - -- (void)updateWindowList:(NSOrderedSet *)windowList; -{ - SWLogMainThreadOnly(); - StateLog(@"State machine update window groups: %@", windowList); - - [self private_updateWindowList:windowList]; -} - -#pragma mark - Internal - -- (void)private_adjustSelectorIfNecessary; -{ - SWLogMainThreadOnly(); - if (!self.selectorAdjusted) { - Assert(self.windowListLoaded); - Assert(self.selector.windowList == nil); - if (self.selector.selectedIndex == 1 && [self.windowList count] > 1 && ![CLASS_CAST(SWWindow, [self.windowList objectAtIndex:0]).application isActiveApplication]) { - self.selector = [[SWSelector new] updateWithWindowList:self.windowList]; - } - self.selectorAdjusted = YES; - self.selector = [self.selector updateWithWindowList:self.windowList]; - } -} - -- (void)private_updateWindowList:(NSOrderedSet *)windowList; -{ - SWLogMainThreadOnly(); - // A nil update means clean up, we're shutting down until the next invocation. - if (!windowList || !self.active) { - self.windowList = nil; - self.windowListLoaded = false; - self.selectorAdjusted = false; - return; - } - - self.windowList = windowList; - - if (!self.windowListLoaded) { - self.windowListLoaded = true; - } else if (self.selectorAdjusted) { - self.selector = [self.selector updateWithWindowList:windowList]; - } - - if (self.pendingSwitch && [[windowList firstObject] isEqual:self.selectedWindow] && [self.selectedWindow.application isActiveApplication]) { - self.pendingSwitch = NO; - } -} - -- (void)private_raiseSelectedWindow; -{ - SWLogMainThreadOnly(); - BailUnless(self.pendingSwitch && self.windowListLoaded,); - - [self private_adjustSelectorIfNecessary]; - - SWWindow *selectedWindow = self.selector.selectedWindow; - _Bool noSelection = !selectedWindow; - _Bool alreadyActiveWindow = !noSelection && [selectedWindow isEqual:self.windowList[0]] && [selectedWindow.application isActiveApplication]; - if (noSelection || alreadyActiveWindow) { - self.pendingSwitch = false; - return; - } - - id delegate = self.delegate; - [delegate stateMachine:self wantsWindowRaised:selectedWindow]; -} - -#pragma mark Computed property updaters - -- (void)private_updateInterfaceVisible { - _Bool interfaceVisible = (_invoked || _pendingSwitch) && !_displayTimer && _windowListLoaded; - if (self.interfaceVisible != interfaceVisible) { - self.interfaceVisible = interfaceVisible; - } -} - -- (void)private_updateActive { - _Bool active = _invoked || _pendingSwitch; - if (self.active != active) { - self.active = active; - } -} - -- (void)private_updateSelectedWindow { - SWWindow *selectedWindow = _selector.selectedWindow; - if (self.selectedWindow != selectedWindow) { - self.selectedWindow = selectedWindow; - } -} - -- (void)private_raiseWindowIfNeeded { - if (_pendingSwitch && _windowListLoaded) { - [self private_raiseSelectedWindow]; - } -} - -@end diff --git a/Switch/SWStatusBarMenuService.h b/Switch/SWStatusBarMenuService.h deleted file mode 100644 index 49062c9a..00000000 --- a/Switch/SWStatusBarMenuService.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SWStatusBarMenuService.h -// Switch -// -// Created by Scott Perry on 10/15/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -@interface SWStatusBarMenuService : NNService - -@end diff --git a/Switch/SWStatusBarMenuService.m b/Switch/SWStatusBarMenuService.m deleted file mode 100644 index 36414003..00000000 --- a/Switch/SWStatusBarMenuService.m +++ /dev/null @@ -1,150 +0,0 @@ -// -// SWStatusBarMenuService.m -// Switch -// -// Created by Scott Perry on 10/15/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWStatusBarMenuService.h" - -#import "SWAppDelegate.h" -#import "SWPreferencesService.h" - - -@interface SWStatusBarMenuService () - -@property (nonatomic, strong) NSStatusItem *statusItem; -@property (nonatomic, strong) NSSet *debugItems; - -@end - - -@implementation SWStatusBarMenuService - -#pragma mark - Initialization - -- (instancetype)init; -{ - BailUnless(self = [super init], nil); - - @weakify(self); - - RAC(self,statusItem) = [[RACObserve([SWPreferencesService sharedService], showStatusItem) - distinctUntilChanged] - map:^id(NSNumber *value) { - @strongify(self); - BOOL shouldShowStatusItem = [value boolValue]; - NSStatusItem *statusItem = self.statusItem; - - if (!shouldShowStatusItem) { - [statusItem.statusBar removeStatusItem:statusItem]; - return nil; - } - - if (statusItem) { - return statusItem; - } - - return [self private_newStatusItem]; - }]; - - return self; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypePersistent; -} - -#pragma mark - SWStatusBarMenuService - -- (IBAction)snapshot:(id)sender; -{ - [[SWLoggingService sharedService] takeWindowListSnapshot]; - [self openLogFolder:self]; -} - -- (IBAction)openLogFolder:(id)sender; -{ - [[NSWorkspace sharedWorkspace] openFile:[[SWLoggingService sharedService] logDirectoryPath]]; -} - -#pragma mark - NSMenuDelegate - -- (void)menuNeedsUpdate:(NSMenu *)menu; -{ - NSUInteger flags = ([NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask); - BOOL hideDebugItems = !(flags == NSAlternateKeyMask); - - for (NSMenuItem *item in self.debugItems) { - item.hidden = hideDebugItems; - } -} - -#pragma mark - Internal - -- (NSStatusItem *)private_newStatusItem; -{ - NSStatusItem *statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; - statusItem.image = [[NSBundle mainBundle] imageForResource:@"weave"]; - statusItem.highlightMode = YES; - statusItem.target = self; - - NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Status Bar Menu"]; - - NSMenuItem * menuItem; - menuItem = [[NSMenuItem alloc] initWithTitle:@"Preferences…" action:NNTypedSelector1(SWAppDelegate, showPreferences:) keyEquivalent:@""]; - menuItem.target = [NSApplication sharedApplication].delegate; - [menu addItem:menuItem]; - - [menu addItem:[NSMenuItem separatorItem]]; - - { // These get filtered out when option is not pressed: - NSMutableSet *debugItems = [NSMutableSet set]; - - menuItem = [[NSMenuItem alloc] initWithTitle:@"Hi there!" action:NULL keyEquivalent:@""]; - menuItem.enabled = NO; - [menu addItem:menuItem]; - [debugItems addObject:menuItem]; - - menuItem = [[NSMenuItem alloc] initWithTitle:@"Take Snapshot…" action:NNSelfSelector1(snapshot:) keyEquivalent:@""]; - menuItem.target = self; - [menu addItem:menuItem]; - [debugItems addObject:menuItem]; - - menuItem = [[NSMenuItem alloc] initWithTitle:@"Open Log Folder…" action:NNSelfSelector1(openLogFolder:) keyEquivalent:@""]; - menuItem.target = self; - [menu addItem:menuItem]; - [debugItems addObject:menuItem]; - - menuItem = [NSMenuItem separatorItem]; - [menu addItem:menuItem]; - [debugItems addObject:menuItem]; - - _debugItems = debugItems; - } - -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wselector" - menuItem = [[NSMenuItem alloc] initWithTitle:@"Quit" action:NNTypedSelector1(NSApplication, terminate:) keyEquivalent:@""]; -# pragma clang diagnostic pop - menuItem.target = [NSApplication sharedApplication]; - [menu addItem:menuItem]; - - menu.delegate = self; - - statusItem.menu = menu; - - return statusItem; -} - -@end diff --git a/Switch/SWWindow+TweetbotQuirks.h b/Switch/SWWindow+TweetbotQuirks.h deleted file mode 100644 index 676c0567..00000000 --- a/Switch/SWWindow+TweetbotQuirks.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// SWWindow+TweetBotQuirks.h -// Switch -// -// Created by Scott Perry on 01/11/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindow.h" - -@interface SWWindow (TweetbotQuirks) - -- (BOOL)tweetbot_isRelatedToLowerWindow:(SWWindow *)window; - -@end diff --git a/Switch/SWWindow+TweetbotQuirks.m b/Switch/SWWindow+TweetbotQuirks.m deleted file mode 100644 index cfd438ae..00000000 --- a/Switch/SWWindow+TweetbotQuirks.m +++ /dev/null @@ -1,68 +0,0 @@ -// -// SWWindow+TweetBotQuirks.m -// Switch -// -// Created by Scott Perry on 01/11/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindow+TweetbotQuirks.h" - - -@implementation SWWindow (TweetbotQuirks) - -- (BOOL)tweetbot_isRelatedToLowerWindow:(SWWindow *)window; -{ - // Tweetbot, which *does* name its main window, has spurious decorator windows, but does not name its popup windows ಠ_ಠ - - /** - * Catch the table view section header window. It: - * • has no name. - * • floats over a window that has a name (in practice, the main window). - * • is fully enclosed by the window it decorates. - * • has a height of < 33 points (in practice, 30). - */ - if (!self.name.length && self.frame.size.height < 33.0 && [self enclosedByWindow:window]) { - return YES; - } - - /** - * Catch any shadowing windows. They: - * • have no name - * • shadow a window that has a name (in practice, the main window). - * • are positioned (nearly) centered underneath the shadowed window, within (20, 20) points center to center. - * • extend beyond the edges of the shadowed window on all sides - * • do not exceed the height or width of the shadowed window by more than 100 points (in practice, (90, 85)). - * - * These rules should be sufficient to reduce the likelihood of false positives to an acceptable level. - */ - if (![window.name length]) { - NNVec2 c2cOffset = [window offsetOfCenterToCenterOfWindow:self]; - NNVec2 absC2COffset = (NNVec2){ .x = fabs(c2cOffset.x), .y = fabs(c2cOffset.y) }; - NSSize sizeDifference = [window sizeDifferenceFromWindow:self]; - - // Windows have center origins that are within (20, 20) points of each other. - BOOL centered = absC2COffset.x < 20.0 && absC2COffset.y < 20.0; - - // Window fully encloses the window it shadows. - BOOL enclosing = [self enclosedByWindow:window]; - - // Window to the rear has dimensions larger than the window it shadows, not exceeding (100, 100) points. - BOOL saneSize = sizeDifference.width < 100.0 - && sizeDifference.height < 100.0; - - if (centered && enclosing && saneSize) { - return YES; - } - } - - return NO; -} - -@end diff --git a/Switch/SWWindow.h b/Switch/SWWindow.h deleted file mode 100644 index efef297f..00000000 --- a/Switch/SWWindow.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// SWWindow.h -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -typedef NSPoint NNVec2; - - -@class SWApplication; - - -@interface SWWindow : NSObject - -+ (instancetype)windowWithDescription:(NSDictionary *)description; - -@property (nonatomic, strong, readonly) SWApplication *application; -@property (atomic, copy, readonly) NSDictionary *windowDescription; - -- (CGRect)frame; -- (CGRect)flippedFrame; -- (NSString *)name; -- (NSScreen *)screen; -- (CGWindowID)windowID; - -- (NSString *)displayName; - -- (BOOL)isSameWindow:(SWWindow *)window; -- (BOOL)isRelatedToLowerWindow:(SWWindow *)window; -- (NNVec2)offsetOfCenterToCenterOfWindow:(SWWindow *)window; -- (NSSize)sizeDifferenceFromWindow:(SWWindow *)window; -- (BOOL)enclosedByWindow:(SWWindow *)window; - -@end diff --git a/Switch/SWWindow.m b/Switch/SWWindow.m deleted file mode 100644 index 3c18d21e..00000000 --- a/Switch/SWWindow.m +++ /dev/null @@ -1,223 +0,0 @@ -// -// SWWindow.m -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindow.h" -#import "SWWindow+TweetbotQuirks.h" - -#import -#import - -#import "NSScreen+SWAdditions.h" -#import "SWApplication.h" - - -@implementation SWWindow - -#pragma mark - Initialization - -+ (instancetype)windowWithDescription:(NSDictionary *)description; -{ - return [[self alloc] initWithDescription:description]; -} - -- (instancetype)initWithDescription:(NSDictionary *)description; -{ - BailUnless(self = [super init], nil); - - if (!description) { - return nil; - } - - _windowDescription = [description copy]; - _application = [SWApplication applicationWithPID:[[self.windowDescription objectForKey:(NSString *)kCGWindowOwnerPID] intValue] name:[self.windowDescription objectForKey:(NSString *)kCGWindowOwnerName]]; - - return self; -} - -#pragma mark - NSObject - -- (instancetype)copyWithZone:(NSZone *)zone; -{ - Check(!zone); - return self; -} - -- (NSUInteger)hash; -{ - return self.windowID; -} - -- (BOOL)isEqual:(id)object; -{ - return ([object isKindOfClass:[self class]] && [[self windowDescription] isEqual:[object windowDescription]]); -} - -- (BOOL)isSameWindow:(SWWindow *)window; -{ - if (window.windowID != self.windowID) { - return NO; - } - - if (![window.application.name isEqualToString:self.application.name]) { - return NO; - } - - return YES; -} - -- (NSString *)description; -{ - return [NSString stringWithFormat:@"%p <%u (%@)>", self, self.windowID, self.name]; -} - -#pragma mark - SWWindow - -- (CGRect)flippedFrame; -{ - CGRect result = {{},{}}; - bool success = CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)[self.windowDescription objectForKey:(NSString *)kCGWindowBounds], &result); - BailUnless(success, ((CGRect){{0.0,0.0},{0.0,0.0}})); - return result; -} - -- (CGRect)frame; -{ - CGFloat totalScreenHeight = NSScreen.sw_totalScreenHeight; - CGRect flippedFrame = self.flippedFrame; - flippedFrame.origin.y = totalScreenHeight - (flippedFrame.origin.y + flippedFrame.size.height); - return flippedFrame; -} - -- (NSString *)name; -{ - return [self.windowDescription objectForKey:(__bridge NSString *)kCGWindowName]; -} - -- (NSScreen *)screen; -{ - CGRect cgFrame = self.flippedFrame; - - return [[NSScreen screens] nn_reduce:^id(NSScreen *accumulator, NSScreen *item) { - if (!accumulator) { - accumulator = [NSScreen mainScreen]; - } - - CGFloat (^overlapWithScreen)(NSScreen *) = ^(NSScreen *screen) { - CGDirectDisplayID displayID = [screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue]; - CGRect displayRect = CGDisplayBounds(displayID); - CGRect intersection = CGRectIntersection(displayRect, cgFrame); - return intersection.size.width * intersection.size.height; - }; - - if (overlapWithScreen(item) > overlapWithScreen(accumulator)) { - return item; - } else { - return accumulator; - } - }]; -} - -- (CGWindowID)windowID; -{ - return (CGWindowID)[[self.windowDescription objectForKey:(__bridge NSString *)kCGWindowNumber] unsignedLongValue]; -} - -- (NSString *)displayName; -{ - NSString *result = self.name; - if (result == nil || result.length == 0) { - result = self.application.name; - } - return result; -} - -- (BOOL)isRelatedToLowerWindow:(SWWindow *)window; -{ - NSParameterAssert(window.application.canBeActivated); - - // Powerbox (for example) names its windows, but cannot be activated. - if (!self.application.canBeActivated) { - return YES; - } - - // Windows belonging to different applications are unrelated. - if (![self.application isEqual:window.application]) { - return NO; - } - - // Named windows are (usually) main windows themselves - if (self.name.length && window.name.length) { - return NO; - } - - // Windows on different screens are not equal - if (![self.screen isEqual:window.screen]) { - return NO; - } - - // This is a special case for catching the shadow opening for sheets - if (self.frame.size.height < 20.0 && [self.windowDescription[(__bridge NSString *)kCGWindowAlpha] floatValue] < 1.0) { - return YES; - } - - if ([self.application.name isEqualToString:@"Tweetbot"]) { - return [self tweetbot_isRelatedToLowerWindow:window]; - } else if ([self.application.name isEqualToString:@"MacVim"]) { - // MacVim isn't known to have any extraneous unnamed windows… yet? - return NO; - } - - // This is intended to fix some window grouping issues around full-screen applications (such as Xcode). - if ( - // higher window and lower window have the same origin.x, and - self.frame.origin.x == window.frame.origin.x && - // higher window and lower window have the same size.width, and - self.frame.size.width == window.frame.size.width && - // higer window and lower window intersect - !CGRectIsNull(CGRectIntersection(self.frame, window.frame)) - ) { - return YES; - } - - return [self enclosedByWindow:window]; -} - -- (NNVec2)offsetOfCenterToCenterOfWindow:(SWWindow *)window; -{ - CGRect selfBounds = self.frame; - CGRect windowBounds = window.frame; - - return (NNVec2){ - .x = ((windowBounds.origin.x + (windowBounds.size.width / 2.0)) - (selfBounds.origin.x + (selfBounds.size.width / 2.0))), - .y = ((windowBounds.origin.y + (windowBounds.size.height / 2.0)) - (selfBounds.origin.y + (selfBounds.size.height / 2.0))) - }; -} - -- (NSSize)sizeDifferenceFromWindow:(SWWindow *)window; -{ - CGRect selfBounds = self.frame; - CGRect windowBounds = window.frame; - - return (NSSize){ - .width = selfBounds.size.width - windowBounds.size.width, - .height = selfBounds.size.height - windowBounds.size.height - }; -} - -- (BOOL)enclosedByWindow:(SWWindow *)window; -{ - return CGRectContainsRect(window.frame, self.frame); -} - -@end diff --git a/Switch/SWWindowContentsService.h b/Switch/SWWindowContentsService.h deleted file mode 100644 index 00a4c306..00000000 --- a/Switch/SWWindowContentsService.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// SWWindowContentsService.h -// Switch -// -// Created by Scott Perry on 01/02/14. -// Copyright © 2014 Scott Perry. -// -// 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. -// - -@class SWWindow; -@class SWWindowContentsService; - - -@protocol SWWindowContentsSubscriber - -- (oneway void)windowContentService:(SWWindowContentsService *)windowService updatedContent:(NSImage *)content forWindow:(SWWindow *)window; - -@end - - -@interface SWWindowContentsService : NNService - -- (NSImage *)contentForWindow:(SWWindow *)window; - -@end diff --git a/Switch/SWWindowContentsService.m b/Switch/SWWindowContentsService.m deleted file mode 100644 index 56af33f7..00000000 --- a/Switch/SWWindowContentsService.m +++ /dev/null @@ -1,194 +0,0 @@ -// -// SWWindowContentsService.m -// Switch -// -// Created by Scott Perry on 01/02/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowContentsService.h" - -#import - -#import "SWWindowGroup.h" -#import "SWWindowListService.h" -#import "SWWindowWorker.h" - - -@interface _SWWindowContentContainer : NSObject - -@property (nonatomic, strong) NSImage *content; -@property (nonatomic, strong) SWWindow *window; -@property (nonatomic, strong, readonly) SWWindowWorker *worker; - -@end - - -@implementation _SWWindowContentContainer - -- (void)setWindow:(SWWindow *)window; -{ - if (!_window) { - self->_worker = [[SWWindowWorker alloc] initWithModelObject:window]; - } else { - Check(_window.windowID == window.windowID); - } - _window = window; -} - -@end - - -@interface SWWindowContentsService () - -@property (nonatomic, strong, readonly) NSMutableDictionary *contentContainers; -@property (nonatomic, strong, readonly) dispatch_queue_t queue; - -@end - - -@implementation SWWindowContentsService - -#pragma mark - Initialization - -- (id)init; -{ - BailUnless(self = [super init], nil); - - _contentContainers = [NSMutableDictionary new]; - _queue = dispatch_queue_create([[NSString stringWithFormat:@"SWWindowContentsService"] UTF8String], DISPATCH_QUEUE_SERIAL); - - [[NSNotificationCenter defaultCenter] addWeakObserver:self selector:NNSelfSelector1(private_windowUpdateNotification:) name:[SWWindowWorker notificationName] object:nil]; - - return self; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypeOnDemand; -} - -+ (NSSet *)dependencies; -{ - return [NSSet setWithArray:@[[SWWindowListService class]]]; -} - -+ (Protocol *)subscriberProtocol; -{ - return @protocol(SWWindowContentsSubscriber); -} - -- (void)startService; -{ - [super startService]; - - self->_contentContainers = [NSMutableDictionary new]; - - NSOrderedSet *windows = [SWWindowListService sharedService].windows; - if (windows) { - [self windowListService:nil updatedList:windows]; - } - - [[NNServiceManager sharedManager] addObserver:self forService:[SWWindowListService class]]; -} - -- (void)stopService; -{ - dispatch_async(self.queue, ^{ - [self.contentContainers removeAllObjects]; - self->_contentContainers = nil; - }); - - [[NNServiceManager sharedManager] removeObserver:self forService:[SWWindowListService class]]; - - [super stopService]; -} - -#pragma mark - SWWindowContentsService - -- (NSImage *)contentForWindow:(SWWindow *)window; -{ - __block _SWWindowContentContainer *contentContainerObject; - dispatch_sync(self.queue, ^{ - contentContainerObject = [self.contentContainers objectForKey:@(window.windowID)]; - }); - return contentContainerObject.content; -} - -#pragma mark - SWWindowListSubscriber - -- (oneway void)windowListService:(SWWindowListService *)service updatedList:(NSOrderedSet *)windowList; -{ - @weakify(self); - dispatch_async(self.queue, ^{ - @strongify(self); - // Flatten the window group hierarchy into an unordered set of windows. - NSMutableSet *existingWindows = [NSMutableSet new]; - for (SWWindowGroup *windowGroup in windowList) { - for (SWWindow *window in windowGroup.windows) { - [existingWindows addObject:window]; - } - } - - // Update/create content containers for all windows that exist. - for (SWWindow *window in existingWindows) { - _SWWindowContentContainer *contentContainerObject = [self.contentContainers objectForKey:@(window.windowID)]; - if (!contentContainerObject) { - contentContainerObject = [_SWWindowContentContainer new]; - [self.contentContainers setObject:contentContainerObject forKey:@(window.windowID)]; - } - - contentContainerObject.window = window; - } - - // Remove content containers for windows that don't exist. - for (_SWWindowContentContainer *contentContainer in [self.contentContainers allValues]) { - if (![existingWindows containsObject:contentContainer.window]) { - [self.contentContainers removeObjectForKey:@(contentContainer.window.windowID)]; - } - } - }); -} - -- (oneway void)windowListServiceStopped:(SWWindowListService *)service; -{ - [self windowListService:nil updatedList:[NSOrderedSet orderedSet]]; -} - -#pragma mark - Internal - -- (void)private_windowUpdateNotification:(NSNotification *)notification; -{ - SWWindowWorker *worker = notification.object; - NSImage *content = notification.userInfo[@"content"]; - - @weakify(self); - dispatch_async(self.queue, ^{ - @strongify(self); - _SWWindowContentContainer *contentContainerObject = [self.contentContainers objectForKey:@(worker.windowID)]; - if (!contentContainerObject) { - return; - } - - if (!Check(content)) { - return; - } - - contentContainerObject.content = content; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - @strongify(self); - [(id)self.subscriberDispatcher windowContentService:self updatedContent:content forWindow:contentContainerObject.window]; - }); - }); -} - -@end diff --git a/Switch/SWWindowGroup.h b/Switch/SWWindowGroup.h deleted file mode 100644 index a9cefa27..00000000 --- a/Switch/SWWindowGroup.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// SWWindowGroup.h -// Switch -// -// Created by Scott Perry on 12/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindow.h" - - -@class SWApplication; - - -@interface SWWindowGroup : SWWindow - -@property (nonatomic, strong, readonly) SWWindow *mainWindow; -@property (nonatomic, strong, readonly) NSOrderedSet *windows; - -- (instancetype)initWithWindows:(NSOrderedSet *)windows mainWindow:(SWWindow *)mainWindow; - -- (BOOL)isRelatedToLowerGroup:(SWWindowGroup *)group; - -@end diff --git a/Switch/SWWindowGroup.m b/Switch/SWWindowGroup.m deleted file mode 100644 index 96c3af86..00000000 --- a/Switch/SWWindowGroup.m +++ /dev/null @@ -1,172 +0,0 @@ -// -// SWWindowGroup.m -// Switch -// -// Created by Scott Perry on 12/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowGroup.h" - -#import "SWApplication.h" -#import "SWWindow.h" - - -@implementation SWWindowGroup - -#pragma mark - Initialization - -- (instancetype)initWithWindows:(NSOrderedSet *)windows mainWindow:(SWWindow *)mainWindow; -{ - NSParameterAssert([windows containsObject:mainWindow]); - BailUnless(self = [super init], nil); - - _windows = [windows copy]; - _mainWindow = mainWindow; - - return self; -} - -#pragma mark - NSObject - -- (NSUInteger)hash; -{ - return self.mainWindow.hash; -} - -- (BOOL)isEqual:(id)object; -{ - if (![object isKindOfClass:[SWWindowGroup class]]) { - return NO; - } - - if (![[object windows] isEqual:self.windows]) { - return NO; - } - - if (![[object mainWindow] isEqual:self.mainWindow]) { - return NO; - } - - return YES; -} - -- (NSString *)description; -{ - return [NSString stringWithFormat:@"<%u (%@), %lu windows>", self.mainWindow.windowID, self.name, self.windows.count]; -} - -- (instancetype)copyWithZone:(NSZone *)zone; -{ - Check(!zone); - return self; -} - -#pragma mark - SWWindow - -- (SWApplication *)application; -{ - return self.mainWindow.application; -} - -- (NSString *)name; -{ - return self.mainWindow.displayName; -} - -- (CGRect)frame; -{ - NSPoint min = self.mainWindow.frame.origin; - NSPoint max = min; - - for (SWWindow *window in self.windows) { - CGRect frame = window.frame; - min.x = MIN(min.x, frame.origin.x); - min.y = MIN(min.y, frame.origin.y); - max.x = MAX(max.x, frame.origin.x + frame.size.width); - max.y = MAX(max.y, frame.origin.y + frame.size.height); - } - - return (CGRect){.origin = min, .size.width = max.x - min.x, .size.height = max.y - min.y}; -} - -- (CGRect)flippedFrame; -{ - NSPoint min = self.mainWindow.flippedFrame.origin; - NSPoint max = min; - - for (SWWindow *window in self.windows) { - CGRect flippedFrame = window.flippedFrame; - min.x = MIN(min.x, flippedFrame.origin.x); - min.y = MIN(min.y, flippedFrame.origin.y); - max.x = MAX(max.x, flippedFrame.origin.x + flippedFrame.size.width); - max.y = MAX(max.y, flippedFrame.origin.y + flippedFrame.size.height); - } - - return (CGRect){.origin = min, .size.width = max.x - min.x, .size.height = max.y - min.y}; -} - -#pragma mark - SWWindowGroup - -- (BOOL)isRelatedToLowerGroup:(SWWindowGroup *)group; -{ - // Ensure concentricity of groups, as a fast fail. - if (![self enclosedByWindow:group]) { - return NO; - } - - // Ensure that both windows belong to the same application, as a fast fail. - if (![self.application isEqual:group.application]) { - return NO; - } - - BOOL isSaveDialog = NO; - for (SWWindow *window in self.windows) { - if ([window.windowDescription[(__bridge NSString *)kCGWindowAlpha] doubleValue] < 1.0 && window.frame.size.height < 12.0 && self.windows.count > 1) { - isSaveDialog = YES; - break; - } - - if ([window.application.name isEqualToString:@"com.apple.security.pboxd"] - || [window.application.name isEqualToString:@"com.apple.appkit.xpc.openAndSav"]) - { - isSaveDialog = YES; - /** XXX: in case of recursive calls, should check to make sure the window that the save dialog refers to is not already included in the group. Example: - - (lldb) po [windowGroupList[7] windows] - {( - 0x6080002268c0 <48051 ((null))>, - 0x608000225be0 <48050 ()>, - 0x608000226900 <48045 (Save)>, - 0x608000226500 <48046 (Save)>, - 0x608000226520 <48043 (Untitled.txt)> - )} - - (lldb) po [((NSOrderedSet *)[windowGroupList[7] windows])[2] application] - 0x6080002268e0 <82769 (com.apple.appkit.xpc.openAndSavePanelService)> - (lldb) p (CGRect)[((NSOrderedSet *)[windowGroupList[7] windows])[2] frame] - (CGRect) $8 = (x=222, y=69), (width=489, height=319) - - (lldb) po [((NSOrderedSet *)[windowGroupList[7] windows])[3] application] - 0x608000226560 <82763 (TextEdit)> - (lldb) p (CGRect)[((NSOrderedSet *)[windowGroupList[7] windows])[3] frame] - (CGRect) $7 = (x=222, y=69), (width=490, height=319) - - (lldb) p (CGRect)[((NSOrderedSet *)[windowGroupList[7] windows])[4] frame] - (CGRect) $9 = (x=147, y=47), (width=640, height=412) - - */ - break; - } - } - - return isSaveDialog; -} - -@end diff --git a/Switch/SWWindowListService.h b/Switch/SWWindowListService.h deleted file mode 100644 index bf21b68c..00000000 --- a/Switch/SWWindowListService.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// SWWindowListService.h -// Switch -// -// Created by Scott Perry on 12/24/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@class SWWindowListService; - - -@protocol SWWindowListSubscriber -@optional - -- (oneway void)windowListServiceStarted:(SWWindowListService *)service; -- (oneway void)windowListService:(SWWindowListService *)service updatedList:(NSOrderedSet *)windows; -- (oneway void)windowListServiceStopped:(SWWindowListService *)service; - -@end - - -@interface SWWindowListService : NNService - -+ (NSOrderedSet *)filterInfoDictionariesToWindowObjects:(NSArray *)infoDicts; -+ (NSOrderedSet *)filterWindowObjectsToWindowGroups:(NSOrderedSet *)rawWindowList; -+ (NSOrderedSet *)sortedWindowGroups:(NSOrderedSet *)windowGroups; - -@property (nonatomic, copy, readonly) NSOrderedSet *windows; - -// Background threads only! -- (void)refreshWindowListAndWait; - -@end diff --git a/Switch/SWWindowListService.m b/Switch/SWWindowListService.m deleted file mode 100644 index 19c4d849..00000000 --- a/Switch/SWWindowListService.m +++ /dev/null @@ -1,250 +0,0 @@ -// -// SWWindowListService.m -// Switch -// -// Created by Scott Perry on 12/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListService.h" - -#import - -#import "SWApplication.h" -#import "SWPreferencesService.h" -#import "SWWindow.h" -#import "SWWindowGroup.h" -#import "SWWindowListWorker.h" - - -@interface SWWindowListService () - -@property (nonatomic, copy, readwrite) NSOrderedSet *windows; -@property (nonatomic, strong, readwrite) SWWindowListWorker *worker; - -@end - - -static NSMutableSet *loggedWindows; - - -@implementation SWWindowListService - -#pragma mark - Initialization - -- (id)init; -{ - SWLogMainThreadOnly(); - BailUnless(self = [super init], nil); - - [[NSNotificationCenter defaultCenter] addWeakObserver:self selector:NNSelfSelector1(private_workerUpdatedWindowList:) name:[SWWindowListWorker notificationName] object:nil]; - - return self; -} - -#pragma mark - NNService - -+ (NNServiceType)serviceType; -{ - return NNServiceTypeOnDemand; -} - -+ (Protocol *)subscriberProtocol; -{ - return @protocol(SWWindowListSubscriber); -} - -- (void)startService; -{ - SWLogMainThreadOnly(); - - [super startService]; - - loggedWindows = [NSMutableSet new]; - self.worker = [SWWindowListWorker new]; - - [(id)self.subscriberDispatcher windowListServiceStarted:self]; -} - -- (void)stopService; -{ - SWLogMainThreadOnly(); - - loggedWindows = nil; - self.worker = nil; - self.windows = nil; - - [(id)self.subscriberDispatcher windowListServiceStopped:self]; - - [super stopService]; -} - -#pragma mark - SWWindowListService - -+ (NSOrderedSet *)filterInfoDictionariesToWindowObjects:(NSArray *)infoDicts; -{ - return [NSOrderedSet orderedSetWithArray:[[infoDicts nn_filter:(nn_filter_block_t)^(NSDictionary *windowInfo) { - NSString *applicationName = windowInfo[(__bridge NSString *)kCGWindowOwnerName]; - - // Non-normal windows are filtered out as the accuracy of their ordering in the window list cannot be guaranteed. - BOOL normalWindow = ([windowInfo[(__bridge NSString *)kCGWindowLayer] longValue] == kCGNormalWindowLevel); - BOOL transparentWindow = ([windowInfo[(__bridge NSString *)kCGWindowAlpha] doubleValue] == 0.0); - - BOOL msWordWindow = [applicationName isEqualToString:@"Microsoft Word"]; - BOOL badMSWordWindow = msWordWindow && [windowInfo[(__bridge NSString *)kCGWindowName] isEqualToString:@"Microsoft Word"]; - - BOOL isolatorWindow = [applicationName isEqualToString:@"Isolator"]; - BOOL isolatorShield = isolatorWindow && ([windowInfo[(__bridge NSString *)kCGWindowAlpha] doubleValue] < 0.9); - - return normalWindow && !transparentWindow && !badMSWordWindow && !isolatorShield; - }] nn_map:(nn_map_block_t)^(NSDictionary *windowInfo) { - return [SWWindow windowWithDescription:windowInfo]; - }]]; -} - -+ (NSOrderedSet *)filterWindowObjectsToWindowGroups:(NSOrderedSet *)rawWindowList; -{ - NSMutableOrderedSet *mutableWindowGroupList = [NSMutableOrderedSet new]; - - __block NSMutableOrderedSet *windows = [NSMutableOrderedSet new]; - __block SWWindow *mainWindow = nil; - dispatch_block_t addWindowGroup = ^{ - if (windows.count) { - SWWindowGroup *group = [[SWWindowGroup alloc] initWithWindows:[windows reversedOrderedSet] mainWindow:mainWindow]; - - if (mutableWindowGroupList.count && [group isRelatedToLowerGroup:mutableWindowGroupList.lastObject]) { - NSMutableOrderedSet *windowList = [group.windows mutableCopy]; - [windowList addObjectsFromArray:((SWWindowGroup *)mutableWindowGroupList.lastObject).windows.array]; - group = [[SWWindowGroup alloc] initWithWindows:windowList mainWindow:((SWWindowGroup *)mutableWindowGroupList.lastObject).mainWindow]; - [mutableWindowGroupList removeObject:mutableWindowGroupList.lastObject]; - } - - [mutableWindowGroupList addObject:group]; - - if (![loggedWindows containsObject:group]) { - [loggedWindows addObject:group]; - } - - windows = [NSMutableOrderedSet new]; - mainWindow = nil; - } - }; - - for (NSInteger i = (NSInteger)rawWindowList.count - 1; i >= 0; --i) { - SWWindow *window = rawWindowList[(NSUInteger)i]; - - if (mainWindow && ![window isRelatedToLowerWindow:mainWindow]) { - addWindowGroup(); - } - - if (window.application.canBeActivated) { - // Some applications don't name their windows, some people juggle geese. Make sure there's always a main window for the group. - if (!mainWindow) { - mainWindow = window; - } else - // Named windows always supercede unnamed siblings in the same window group. - if (window.name.length && !mainWindow.name.length) { - mainWindow = window; - } - } - - [windows addObject:window]; - } - if (mainWindow) { - addWindowGroup(); - } - - // Hacky filter to remove Finder Quicklook windows - for (SWWindowGroup *group in [mutableWindowGroupList copy]) { - if (group.windows.count == 1 && [group.application.name isEqualToString:@"Finder"] && group.mainWindow.name.length == 0 && !group.application.isActiveApplication) { - [mutableWindowGroupList removeObject:group]; - } - } - - return [mutableWindowGroupList reversedOrderedSet]; -} - -// XXX: This cannot be tested unless SW provides a type that encapsulates an NSScreen -+ (NSOrderedSet *)sortedWindowGroups:(NSOrderedSet *)windowGroups; -{ - // Default behaviour is to order by recency - NSOrderedSet *result = windowGroups; - - // Grouping windows by monitor, then recency - SWPreferencesService *prefs = [SWPreferencesService sharedService]; - if (prefs.multimonInterface && prefs.multimonGroupByMonitor) { - // I actually want an ordered dictionary here, order: screen by recency, key: screen ID, value: ordered collection of windows - NSMutableArray *orderedDisplayIDs = [NSMutableArray new]; - NSMutableDictionary *windowsByDisplayID = [NSMutableDictionary new]; - - // Break up the windows into separate lists by screen - for (SWWindowGroup *windowGroup in windowGroups) { - // TODO: SW wrapper for NSScreen because this is gross. - CGDirectDisplayID displayID = [windowGroup.screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue]; - - if (!windowsByDisplayID[@(displayID)]) { - [orderedDisplayIDs addObject:@(displayID)]; - windowsByDisplayID[@(displayID)] = [NSMutableArray new]; - } - - [windowsByDisplayID[@(displayID)] addObject:windowGroup]; - } - - // Don't bother with this extra work if there's only one display - if (orderedDisplayIDs.count > 1) { - // Recombine the windows into one list grouped by screen - NSMutableOrderedSet *mutableWindowGroupList = [NSMutableOrderedSet new]; - for (NSNumber *boxedDisplayID in orderedDisplayIDs) { - for (SWWindowGroup *windowGroup in windowsByDisplayID[boxedDisplayID]) { - [mutableWindowGroupList addObject:windowGroup]; - } - } - - NSLog(@"Grouped windows by recency in %ld screens", orderedDisplayIDs.count); - - result = mutableWindowGroupList; - } - } - - return result; -} - -- (void)refreshWindowListAndWait; -{ - SWLogBackgroundThreadOnly(); - [self.worker refreshWindowListAndWait]; -} - -#pragma mark - Internal - -- (void)private_workerUpdatedWindowList:(NSNotification *)notification; -{ - SWLogMainThreadOnly(); - - if (notification.object != self.worker) { return; } - - NSParameterAssert([notification.userInfo[@"windows"] isKindOfClass:[NSArray class]]); - - [self private_updateWindowList:notification.userInfo[@"windows"]]; -} - -- (void)private_updateWindowList:(NSArray *)windowInfoList; -{ - BailUnless(windowInfoList,); - NSOrderedSet *windowObjectList = [[self class] filterInfoDictionariesToWindowObjects:windowInfoList]; - NSOrderedSet *windowGroupList = [[self class] filterWindowObjectsToWindowGroups:windowObjectList]; - NSOrderedSet *sortedWindowGroupList = [[self class] sortedWindowGroups:windowGroupList]; - - if (![self.windows isEqualToOrderedSet:sortedWindowGroupList]) { - self.windows = sortedWindowGroupList; - [(id)self.subscriberDispatcher windowListService:self updatedList:self.windows]; - } -} - -@end diff --git a/Switch/SWWindowListWorker.h b/Switch/SWWindowListWorker.h deleted file mode 100644 index e5ffb8e0..00000000 --- a/Switch/SWWindowListWorker.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SWWindowListWorker.h -// Switch -// -// Created by Scott Perry on 02/22/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@interface SWWindowListWorker : NNPollingObject - -- (void)refreshWindowListAndWait; - -@end diff --git a/Switch/SWWindowListWorker.m b/Switch/SWWindowListWorker.m deleted file mode 100644 index 4c61c6e1..00000000 --- a/Switch/SWWindowListWorker.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// SWWindowListWorker.m -// Switch -// -// Created by Scott Perry on 02/22/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListWorker.h" - -#import - - -static NSTimeInterval refreshInterval = 0.1; - - -@interface SWWindowListWorker () - -@property (nonatomic, copy, readwrite) NSArray *windowInfoList; -@property (nonatomic, strong) dispatch_queue_t private_queue; - -@end - - -@implementation SWWindowListWorker - -#pragma mark - Initialization - -- (instancetype)init; -{ - dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - if (!(self = [super initWithQueue:q])) { return nil; } - - self.private_queue = q; - self.interval = refreshInterval; - - return self; -} - -#pragma mark - NNPollingObject - -- (oneway void)main; -{ - [self private_refreshWindowList]; -} - -#pragma mark - SWWindowListWorker - -- (void)refreshWindowListAndWait; -{ - dispatch_sync(self.private_queue, ^{ - [self private_refreshWindowList]; - }); -} - -- (void)private_refreshWindowList; -{ - SWLogBackgroundThreadOnly(); - - SWTimeTask(SWCodeBlock({ - CFArrayRef cgWindowInfoList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); - NSArray *windowInfoList = CFBridgingRelease(cgWindowInfoList); - if (![self.windowInfoList isEqualToArray:windowInfoList]) { - self.windowInfoList = windowInfoList; - [self postNotification:@{@"windows" : self.windowInfoList}]; - } - }), @"Copying window info list"); -} - -@end diff --git a/Switch/SWWindowThumbnailView.h b/Switch/SWWindowThumbnailView.h deleted file mode 100644 index 40d1b6a9..00000000 --- a/Switch/SWWindowThumbnailView.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// SWWindowThumbnailView.h -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -@class SWWindow; - - -@interface SWWindowThumbnailView : NSView - -- (instancetype)initWithFrame:(CGRect)frameRect window:(SWWindow *)window; - -- (void)setActive:(BOOL)active; - -@end diff --git a/Switch/SWWindowThumbnailView.m b/Switch/SWWindowThumbnailView.m deleted file mode 100644 index c2ff8219..00000000 --- a/Switch/SWWindowThumbnailView.m +++ /dev/null @@ -1,330 +0,0 @@ -// -// SWWindowThumbnailView.m -// Switch -// -// Created by Scott Perry on 02/21/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowThumbnailView.h" - -#import -#import - -#import "SWApplication.h" -#import "SWWindowContentsService.h" -#import "SWWindowGroup.h" -#import "SWWindowListService.h" - - -@interface SWWindowThumbnailView () - -@property (nonatomic, strong, readonly) NSOrderedSet *windowIDList; -@property (nonatomic, strong, readonly) NSMutableDictionary *windowFrames; -@property (nonatomic, assign) BOOL valid; - - -@property (nonatomic, strong) SWWindowGroup *windowGroup; -@property (nonatomic, strong, readonly) RACSignal *thumbnailSignal; - -@property (nonatomic, strong) NSImage *icon; - -@property (nonatomic, strong) CALayer *thumbnailLayer; -@property (nonatomic, strong) CALayer *iconLayer; - -@end - - -@implementation SWWindowThumbnailView - -#pragma mark - Initialization - -- (id)initWithFrame:(CGRect)frame window:(SWWindow *)windowGroup; -{ - BailUnless(self = [super initWithFrame:frame], nil); - - // - // Data initialization - // - if (!Check([windowGroup isKindOfClass:[SWWindowGroup class]])) { - return nil; - } - _windowGroup = CLASS_CAST(SWWindowGroup, windowGroup); - - NSMutableOrderedSet *windowIDList = [NSMutableOrderedSet new]; - NSMutableDictionary *windowFrames = [NSMutableDictionary new]; - for (SWWindow *window in _windowGroup.windows) { - [windowIDList addObject:@(window.windowID)]; - [windowFrames setObject:[NSValue valueWithRect:window.frame] forKey:@(window.windowID)]; - } - _windowIDList = windowIDList; - _windowFrames = windowFrames; - - _valid = YES; - - // - // View initialization - // - - self.translatesAutoresizingMaskIntoConstraints = NO; - [self setWantsLayer:YES]; - self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; - [self private_createLayers]; - - for (SWWindow *window in _windowGroup.windows) { - NSImage *content = [[SWWindowContentsService sharedService] contentForWindow:window]; - if (content) { - [self private_sublayerForWindow:window].contents = content; - } - } - - if (!(self.icon = windowGroup.application.cachedIcon)) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSImage *icon = windowGroup.application.loadIcon; - dispatch_async(dispatch_get_main_queue(), ^{ - self.icon = icon; - }); - }); - } - - // - // Configuration initialization - // - - // Disable implicit animation on frame changes. - ((id)self.animator).animations = @{@"frame" : [NSNull null]}; - - [self setNeedsLayout:YES]; - - [[NNServiceManager sharedManager] addSubscriber:self forService:[SWWindowContentsService class]]; - - return self; -} - -#pragma mark - NSView - -- (void)layout; -{ - [super layout]; - - self.thumbnailLayer.frame = self.bounds; - - NSSize thumbSize = self.thumbnailLayer.bounds.size; - CGRect windowGroupFrame = self.windowGroup.frame; - - CGFloat scale = MIN(thumbSize.width / windowGroupFrame.size.width, thumbSize.height / windowGroupFrame.size.height); - - CGFloat scaledXOffset = (thumbSize.width - (windowGroupFrame.size.width * scale)) / 2.0; - CGFloat scaledYOffset = (thumbSize.height - (windowGroupFrame.size.height * scale)) / 2.0; - - for (NSUInteger i = 0; i < self.windowGroup.windows.count; i++) { - SWWindow *window = self.windowGroup.windows[i]; - CALayer *layer = [self private_sublayerForWindow:window]; - CGRect frame = window.frame; - - // Move the frame's origin to be anchored at the "bottom left" of the windowFrame. - frame.origin.x -= windowGroupFrame.origin.x; - frame.origin.y -= windowGroupFrame.origin.y; - - // Scale the frame into the layer's space. - frame.origin.x *= scale; - frame.origin.y *= scale; - frame.size.width *= scale; - frame.size.height *= scale; - - // Center the group within the thumbnail frame. - frame.origin.x += scaledXOffset; - frame.origin.y += scaledYOffset; - - layer.frame = frame; - } - - [self private_updateIconLayout]; -} - -#pragma mark - SWWindowThumbnailView - -@synthesize icon = _icon; - -- (void)setIcon:(NSImage *)icon; -{ - if (icon) { - // This is necessary or the CALayer may draw a low resolution representation when a higher resolution is needed; it's better to be too high-resolution than too low. - NSSize imageSize = icon.size; - CGFloat scale = kNNMaxApplicationIconSize / MAX(imageSize.width, imageSize.height); - icon.size = NSMakeSize(round(imageSize.width * scale), round(imageSize.height * scale)); - } - - _icon = icon; - _iconLayer.contents = icon; - [self setNeedsLayout:YES]; -} - -- (void)setActive:(BOOL)active; -{ - if (!self.valid) { - return; - } - - float opacity = active ? 1.0 : 0.5; - - CABasicAnimation *animation = [CABasicAnimation animation]; - animation.fromValue = @(((CALayer *)self.layer.presentationLayer).opacity); - animation.toValue = @(opacity); - animation.duration = 0.15; - [self.layer addAnimation:animation forKey:@"opacity"]; - self.layer.opacity = opacity; -} - -- (void)setValid:(BOOL)valid; -{ - if (!Check(_valid != valid)) { - return; - } - - // Punting on this for now, it's copy-pasta from setActive. - float opacity = valid ? 1.0 : 0.5; - - CABasicAnimation *animation = [CABasicAnimation animation]; - animation.fromValue = @(((CALayer *)self.layer.presentationLayer).opacity); - animation.toValue = @(opacity); - animation.duration = 0.15; - [self.layer addAnimation:animation forKey:@"opacity"]; - self.layer.opacity = opacity; -} - -#pragma mark - SWWindowListSubscriber - -- (oneway void)windowListService:(SWWindowListService *)service updatedList:(NSOrderedSet *)windows; -{ - BOOL thisWindowExists = NO; - for (SWWindowGroup *windowGroup in windows) { - // Make a window ID list to represent this window group. - NSMutableOrderedSet *windowIDList = [NSMutableOrderedSet new]; - for (SWWindow *window in windowGroup.windows) { - [windowIDList addObject:@(window.windowID)]; - } - - if ([windowIDList isEqual:self.windowIDList]) { - thisWindowExists = YES; - - for (SWWindow *window in windowGroup.windows) { - NSValue *oldBoxedRect = self.windowFrames[@(window.windowID)]; - NSValue *newBoxedRect = [NSValue valueWithRect:window.frame]; - - if (![newBoxedRect isEqualToValue:oldBoxedRect]) { - self.windowFrames[@(window.windowID)] = newBoxedRect; - [self setNeedsLayout:YES]; - - } - } - - self.windowGroup = windowGroup; - - break; - } - } - - self.valid = thisWindowExists; -} - -#pragma mark - SWWindowContentsSubscriber - -- (oneway void)windowContentService:(SWWindowContentsService *)windowService updatedContent:(NSImage *)content forWindow:(SWWindow *)window; -{ - if (!self.valid) { - return; - } - - CGWindowID windowID = window.windowID; - if (![self.windowIDList containsObject:@(windowID)]) { - return; - } - - if (![[self.windowFrames objectForKey:@(windowID)] isEqualToValue:[NSValue valueWithRect:window.frame]]) { - [self setNeedsLayout:YES]; - } - - [self private_sublayerForWindow:window].contents = content; -} - -#pragma mark - Internal - -- (void)private_createLayers; -{ - CALayer *(^newLayer)() = ^{ - CALayer *result = [CALayer layer]; - result.magnificationFilter = kCAFilterTrilinear; - result.minificationFilter = kCAFilterTrilinear; - result.contentsGravity = kCAGravityResizeAspect; - result.actions = @{@"contents" : [NSNull null]}; - return result; - }; - - self.thumbnailLayer = newLayer(); - self.thumbnailLayer.zPosition = 1.0; - [self.layer addSublayer:self.thumbnailLayer]; - - for (sw_unused SWWindow *window in self.windowGroup.windows) { - [self.thumbnailLayer addSublayer:newLayer()]; - } - - self.iconLayer = newLayer(); - self.iconLayer.zPosition = 2.0; - [self.layer addSublayer:self.iconLayer]; -} - -- (void)private_updateIconLayout; -{ - CGRect thumbFrame = self.bounds; - CGFloat thumbSize = thumbFrame.size.width; - - // imageSize is a lie, but it does give the correct aspect ratio. It's always been a square anecdotally, but you can't be too careful! - NSSize imageSize = self.icon.size; - - CGFloat iconSize = thumbSize * kNNMaxApplicationIconSize / kNNMaxWindowThumbnailSize; - CGFloat scale = iconSize / MAX(imageSize.width, imageSize.height); - - // make the size fit correctly - imageSize = NSMakeSize(MIN(round(imageSize.width * scale), iconSize), MIN(round(imageSize.height * scale), iconSize)); - - self.iconLayer.frame = (CGRect){ - .size = imageSize, - .origin.x = thumbFrame.origin.x + (thumbFrame.size.width - imageSize.width), - .origin.y = thumbFrame.origin.y - }; -} - -- (CALayer *)private_sublayerForWindow:(SWWindow *)window; -{ - NSUInteger windowCount = self.windowGroup.windows.count; - - NSUInteger index = [self.windowGroup.windows indexOfObject:window]; - - // It's possible that we're racing a window list update. If we lost, find the window in the list by windowID instead of failing right away. - if (index > self.thumbnailLayer.sublayers.count) { - for (SWWindow *storedWindow in self.windowGroup.windows) { - if (storedWindow.windowID == window.windowID) { - index = [self.windowGroup.windows indexOfObject:storedWindow]; - break; - } - } - } - - // Sublayers are in reverse order as windows. - index = windowCount - (index + 1); - - if (!Check(index < self.thumbnailLayer.sublayers.count)) { - return nil; - } - - return self.thumbnailLayer.sublayers[index]; -} - -@end diff --git a/Switch/SWWindowWorker.h b/Switch/SWWindowWorker.h deleted file mode 100644 index 77859bc9..00000000 --- a/Switch/SWWindowWorker.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// SWWindowWorker.h -// Switch -// -// Created by Scott Perry on 02/22/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -@class SWWindow; - - -@interface SWWindowWorker : NNPollingObject - -- (instancetype)initWithModelObject:(SWWindow *)window __attribute__((nonnull(1))); - -- (CGWindowID) windowID; - -@end diff --git a/Switch/SWWindowWorker.m b/Switch/SWWindowWorker.m deleted file mode 100644 index cacd50c1..00000000 --- a/Switch/SWWindowWorker.m +++ /dev/null @@ -1,128 +0,0 @@ -// -// SWWindowWorker.m -// Switch -// -// Created by Scott Perry on 02/22/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowWorker.h" - -#import - -#import "imageComparators.h" -#import "SWWindow.h" - - -static const NSTimeInterval NNPollingIntervalFast = 1.0 / (24.0 * 1000.0 / 1001.0); // 24p applied to NTSC, drawn on 1's. -static const NSTimeInterval NNPollingIntervalSlow = 1.0; - - -@interface SWWindowWorker () - -@property (nonatomic, copy, readonly) SWWindow *window; - -@property (nonatomic, assign) _Bool firstUpdate; -@property (nonatomic, strong) NSImage *previousCapture; - -@end - - -@implementation SWWindowWorker - -#pragma mark - Initialization - -- (instancetype)initWithModelObject:(SWWindow *)window; -{ - BailUnless(window, nil); - if (!(self = [super initWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)])) { return nil; } - - _window = window; - self.interval = NNPollingIntervalSlow; - - _firstUpdate = true; - - return self; -} - -#pragma mark - NNPollingObject - -- (oneway void)main; -{ - SWLogBackgroundThreadOnly(); - - SWTimeTask(SWCodeBlock({ - CGImageRef cgImage = NNCFAutorelease(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, self.window.windowID, kCGWindowImageBoundsIgnoreFraming)); - CGFloat width = CGImageGetWidth(cgImage); - CGFloat height = CGImageGetHeight(cgImage); - - if (height < 1.0 || width < 1.0) { - cgImage = NULL; - } - - NSImage *image = [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(width, height)]; - - if (cgImage) { - BOOL imageChanged = NO; - - size_t newWidth = CGImageGetWidth(cgImage); - size_t newHeight = CGImageGetHeight(cgImage); - { // Did the image change? - if (!self.previousCapture) { - imageChanged = YES; - } else { - CGFloat oldWidth = self.previousCapture.size.width; - CGFloat oldHeight = self.previousCapture.size.height; - - if (newWidth != oldWidth || newHeight != oldHeight) { - imageChanged = YES; - } else { - imageChanged = imagesDifferByCachedTIFFComparison(image, self.previousCapture); - } - } - } - - if (!imageChanged) { - self.interval = MIN(NNPollingIntervalSlow, self.interval * 2.0); - } else { - if (self.firstUpdate) { - self.interval = NNPollingIntervalSlow; - } else { - self.interval = NNPollingIntervalFast; - } - - self.previousCapture = image; - - [self postNotification:@{ - @"window" : self.window, - @"content" : [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(newWidth, newHeight)], - }]; - } - } else if ([CFBridgingRelease(CGWindowListCreate(kCGWindowListOptionIncludingWindow, self.window.windowID)) count]) { - // Didn't get a real image, but the window exists. Try again ASAP. - self.interval = NNPollingIntervalFast; - } else { - // Window does not exist. Stop the worker loop. - self.interval = -1.0; - } - - if (self.firstUpdate) { - self.firstUpdate = false; - } - }), @"Window content capture for %@", self.window); -} - -#pragma mark - SWWindowWorker - -- (CGWindowID)windowID; -{ - return self.window.windowID; -} - -@end diff --git a/Switch/Switch-Bridging-Header.h b/Switch/Switch-Bridging-Header.h deleted file mode 100644 index 1b2cb5d6..00000000 --- a/Switch/Switch-Bridging-Header.h +++ /dev/null @@ -1,4 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - diff --git a/Switch/Switch-Info.plist b/Switch/Switch-Info.plist deleted file mode 100644 index ee468ed8..00000000 --- a/Switch/Switch-Info.plist +++ /dev/null @@ -1,51 +0,0 @@ - - - - - NSAppleEventsUsageDescription - - LSUIElement - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - - CFBundleIdentifier - net.numist.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.0.10 (β) - CFBundleSignature - ???? - CFBundleVersion - 0.0.10 - LSApplicationCategoryType - public.app-category.productivity - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} - NSHumanReadableCopyright - Copyright © 2016 Scott Perry. -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. - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - SUEnableAutomaticChecks - - SUShowReleaseNotes - - SUPublicDSAKeyFile - Sparkle.dsa_pub.pem - - diff --git a/Switch/Switch-Prefix.pch b/Switch/Switch-Prefix.pch deleted file mode 100644 index 4a4aeec5..00000000 --- a/Switch/Switch-Prefix.pch +++ /dev/null @@ -1,21 +0,0 @@ -// -// Prefix header for all source files of the 'Switch' target in the 'Switch' project -// - -#define Log(fmt, ...) do { \ - NSLog(@"%@:%d %@", [[[NSString alloc] initWithCString:(__FILE__) encoding:NSUTF8StringEncoding] lastPathComponent], __LINE__, [NSString stringWithFormat:(fmt), ##__VA_ARGS__]); \ -} while(0) - -#ifdef __OBJC__ -# import -# import -# import -# import -# import "debugger.h" -# import "constants.h" -# import "helpers.h" -# import "SWLoggingService.h" -#endif - -#define sw_deprecated __attribute__((deprecated)) -#define sw_unused __attribute__((unused)) diff --git a/Switch/Switch.xcodeproj/project.pbxproj b/Switch/Switch.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ed16446b --- /dev/null +++ b/Switch/Switch.xcodeproj/project.pbxproj @@ -0,0 +1,1023 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + BC1A123D260471E400DB3906 /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1A123C260471E400DB3906 /* Previews.swift */; }; + BC2121B424E25CE9009EAE3B /* PasteboardHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2121B324E25CE9009EAE3B /* PasteboardHistoryView.swift */; }; + BC2121B624E29323009EAE3B /* Debugger Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2121B524E29323009EAE3B /* Debugger Support.swift */; }; + BC2121B924E3B4AF009EAE3B /* PasteboardHistory.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BC2121B724E3B4AF009EAE3B /* PasteboardHistory.xcdatamodeld */; }; + BC2121BB24E3B61E009EAE3B /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2121BA24E3B61E009EAE3B /* CoreData.framework */; }; + BC2121CD24E3DDD8009EAE3B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2121CC24E3DDD8009EAE3B /* main.m */; }; + BC2121D424E3DE22009EAE3B /* SWTerminationListener.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2121D224E3DE22009EAE3B /* SWTerminationListener.m */; }; + BC2121D624E3DE80009EAE3B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2121D524E3DE80009EAE3B /* AppKit.framework */; }; + BC2121DA24E3DEB5009EAE3B /* relaunch in Copy Executable Files */ = {isa = PBXBuildFile; fileRef = BC2121CA24E3DDD8009EAE3B /* relaunch */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + BC2121DF24E63F7C009EAE3B /* SwitcherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2121DE24E63F7C009EAE3B /* SwitcherView.swift */; }; + BC2121E124E669EB009EAE3B /* NSRect+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2121E024E669EB009EAE3B /* NSRect+Extensions.swift */; }; + BC56727F24DFC84B00206A39 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56727E24DFC84B00206A39 /* Services.swift */; }; + BC56728224DFD27400206A39 /* PasteboardHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56728124DFD27400206A39 /* PasteboardHistory.swift */; }; + BC56728724E0BABC00206A39 /* WindowInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56728624E0BABC00206A39 /* WindowInfo.swift */; }; + BC56728924E0F0E200206A39 /* Haxcessibility.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC56728824E0F0E200206A39 /* Haxcessibility.framework */; }; + BC56728E24E23BA300206A39 /* WindowInfoGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56728D24E23BA300206A39 /* WindowInfoGroup.swift */; }; + BC9CE8AC25216A8A00262FC2 /* Keyboard+NestedTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9CE8AB25216A8A00262FC2 /* Keyboard+NestedTypes.swift */; }; + BC9CE8CA2522CE1000262FC2 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC9CE8C92522CE1000262FC2 /* Sparkle.framework */; }; + BC9CE8CB2522CE2900262FC2 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2121D524E3DE80009EAE3B /* AppKit.framework */; }; + BC9CE8E72522DAA000262FC2 /* LetsMove.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC9CE8E62522DAA000262FC2 /* LetsMove.framework */; }; + BC9CE8ED2522DAC200262FC2 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC9CE8EC2522DAC200262FC2 /* Security.framework */; }; + BC9CE8F72522DBD700262FC2 /* Sparkle.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = BC9CE8C92522CE1000262FC2 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BC9CE9472523CB0E00262FC2 /* Publishers+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9CE9462523CB0E00262FC2 /* Publishers+Extensions.swift */; }; + BC9CE94C2523D27C00262FC2 /* Sparkle.dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = BC9CE94B2523D27C00262FC2 /* Sparkle.dsa_pub.pem */; }; + BC9CE9502523D2A900262FC2 /* Haxcessibility.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = BC56728824E0F0E200206A39 /* Haxcessibility.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BC9CE9542523D33F00262FC2 /* LetsMove.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = BC9CE8E62522DAA000262FC2 /* LetsMove.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BCA9E288250CB2A400EB5540 /* NSThread+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E287250CB2A400EB5540 /* NSThread+Extensions.swift */; }; + BCA9E28A250DA35F00EB5540 /* SwitcherState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E289250DA35F00EB5540 /* SwitcherState.swift */; }; + BCA9E28D250DF2B000EB5540 /* Set+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E28C250DF2B000EB5540 /* Set+Extensions.swift */; }; + BCA9E28F250DF32A00EB5540 /* Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E28E250DF32A00EB5540 /* Switcher.swift */; }; + BCA9E291250E006700EB5540 /* WeakBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E290250E006700EB5540 /* WeakBox.swift */; }; + BCA9E296250F34D100EB5540 /* CGSize+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E295250F34D100EB5540 /* CGSize+Extensions.swift */; }; + BCA9E29C25101EEF00EB5540 /* WindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E29B25101EEF00EB5540 /* WindowView.swift */; }; + BCA9E29E25117E4400EB5540 /* NSImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E29D25117E4300EB5540 /* NSImage+Extensions.swift */; }; + BCA9E2A02511AD1400EB5540 /* SwitcherWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E29F2511AD1400EB5540 /* SwitcherWindow.swift */; }; + BCA9E2A52511C59F00EB5540 /* NSScreen+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E2A42511C59F00EB5540 /* NSScreen+Extensions.swift */; }; + BCA9E2A9251E871C00EB5540 /* WindowInfoListPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA9E2A8251E871B00EB5540 /* WindowInfoListPublisher.swift */; }; + BCD18FD424DD41C000941A86 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD18FD324DD41C000941A86 /* AppDelegate.swift */; }; + BCD18FD824DD41C800941A86 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BCD18FD724DD41C800941A86 /* Assets.xcassets */; }; + BCD18FDB24DD41C800941A86 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BCD18FDA24DD41C800941A86 /* Preview Assets.xcassets */; }; + BCD18FDE24DD41C800941A86 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BCD18FDC24DD41C800941A86 /* Main.storyboard */; }; + BCD18FEA24DD41C800941A86 /* SwitcherStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD18FE924DD41C800941A86 /* SwitcherStateTests.swift */; }; + BCD18FF524DD41C900941A86 /* SwitchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD18FF424DD41C900941A86 /* SwitchUITests.swift */; }; + BCD1901324DF773100941A86 /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1901224DF773100941A86 /* Keyboard.swift */; }; + BCD1901724DF77B200941A86 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1901524DF77A700941A86 /* Carbon.framework */; }; + BCD1901924DF83DB00941A86 /* EventTap.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1901824DF83DB00941A86 /* EventTap.swift */; }; + BCD1901B24DF9D1400941A86 /* LongCmdQ.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1901A24DF9D1400941A86 /* LongCmdQ.swift */; }; + BCF7E2152523F216002C4D67 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF7E2142523F216002C4D67 /* Defaults.swift */; }; + BCF7E22D2523F597002C4D67 /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCF7E22C2523F597002C4D67 /* Defaults.framework */; }; + BCF7E2312523F5A3002C4D67 /* Defaults.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = BCF7E22C2523F597002C4D67 /* Defaults.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BCF7E236252407C2002C4D67 /* StatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF7E235252407C2002C4D67 /* StatusItem.swift */; }; + BCF7E23B252412DF002C4D67 /* NSMenuItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF7E23A252412DF002C4D67 /* NSMenuItem+Extensions.swift */; }; + BCFEB15F251EE71B00CFA605 /* WindowInfoGroupTests+Chrome.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFEB15E251EE71B00CFA605 /* WindowInfoGroupTests+Chrome.swift */; }; + BCFEB16125200A3300CFA605 /* Collection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFEB16025200A3300CFA605 /* Collection+Extensions.swift */; }; + BCFEB16425200C4B00CFA605 /* SwitcherStateFuzzerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFEB16325200C4B00CFA605 /* SwitcherStateFuzzerTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BC2121D724E3DE94009EAE3B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCD18FC824DD41C000941A86 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC2121C924E3DDD8009EAE3B; + remoteInfo = relaunch; + }; + BCD18FE624DD41C800941A86 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCD18FC824DD41C000941A86 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BCD18FCF24DD41C000941A86; + remoteInfo = Switch; + }; + BCD18FF124DD41C900941A86 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BCD18FC824DD41C000941A86 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BCD18FCF24DD41C000941A86; + remoteInfo = Switch; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BC2121C824E3DDD8009EAE3B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + BC2121D924E3DEA9009EAE3B /* Copy Executable Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + BC2121DA24E3DEB5009EAE3B /* relaunch in Copy Executable Files */, + ); + name = "Copy Executable Files"; + runOnlyForDeploymentPostprocessing = 0; + }; + BC9CE8F62522DBC000262FC2 /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + BCF7E2312523F5A3002C4D67 /* Defaults.framework in Copy Frameworks */, + BC9CE9502523D2A900262FC2 /* Haxcessibility.framework in Copy Frameworks */, + BC9CE9542523D33F00262FC2 /* LetsMove.framework in Copy Frameworks */, + BC9CE8F72522DBD700262FC2 /* Sparkle.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BC1A123C260471E400DB3906 /* Previews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = ""; }; + BC2121B324E25CE9009EAE3B /* PasteboardHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardHistoryView.swift; sourceTree = ""; }; + BC2121B524E29323009EAE3B /* Debugger Support.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Debugger Support.swift"; sourceTree = ""; }; + BC2121B824E3B4AF009EAE3B /* PasteboardHistory.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = PasteboardHistory.xcdatamodel; sourceTree = ""; }; + BC2121BA24E3B61E009EAE3B /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + BC2121CA24E3DDD8009EAE3B /* relaunch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = relaunch; sourceTree = BUILT_PRODUCTS_DIR; }; + BC2121CC24E3DDD8009EAE3B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + BC2121D124E3DE22009EAE3B /* SWTerminationListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWTerminationListener.h; sourceTree = ""; }; + BC2121D224E3DE22009EAE3B /* SWTerminationListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWTerminationListener.m; sourceTree = ""; }; + BC2121D524E3DE80009EAE3B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + BC2121DE24E63F7C009EAE3B /* SwitcherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitcherView.swift; sourceTree = ""; }; + BC2121E024E669EB009EAE3B /* NSRect+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRect+Extensions.swift"; sourceTree = ""; }; + BC56727E24DFC84B00206A39 /* Services.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = ""; }; + BC56728124DFD27400206A39 /* PasteboardHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardHistory.swift; sourceTree = ""; }; + BC56728624E0BABC00206A39 /* WindowInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowInfo.swift; sourceTree = ""; }; + BC56728824E0F0E200206A39 /* Haxcessibility.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Haxcessibility.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BC56728D24E23BA300206A39 /* WindowInfoGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowInfoGroup.swift; sourceTree = ""; }; + BC9CE8AB25216A8A00262FC2 /* Keyboard+NestedTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Keyboard+NestedTypes.swift"; sourceTree = ""; }; + BC9CE8C92522CE1000262FC2 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ../Dependencies/Sparkle/Sparkle.framework; sourceTree = ""; }; + BC9CE8E62522DAA000262FC2 /* LetsMove.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LetsMove.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BC9CE8EC2522DAC200262FC2 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + BC9CE9462523CB0E00262FC2 /* Publishers+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publishers+Extensions.swift"; sourceTree = ""; }; + BC9CE94B2523D27C00262FC2 /* Sparkle.dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Sparkle.dsa_pub.pem; sourceTree = ""; }; + BCA9E287250CB2A400EB5540 /* NSThread+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSThread+Extensions.swift"; sourceTree = ""; }; + BCA9E289250DA35F00EB5540 /* SwitcherState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitcherState.swift; sourceTree = ""; }; + BCA9E28C250DF2B000EB5540 /* Set+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Set+Extensions.swift"; sourceTree = ""; }; + BCA9E28E250DF32A00EB5540 /* Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Switcher.swift; sourceTree = ""; }; + BCA9E290250E006700EB5540 /* WeakBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakBox.swift; sourceTree = ""; }; + BCA9E295250F34D100EB5540 /* CGSize+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGSize+Extensions.swift"; sourceTree = ""; }; + BCA9E29B25101EEF00EB5540 /* WindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowView.swift; sourceTree = ""; }; + BCA9E29D25117E4300EB5540 /* NSImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+Extensions.swift"; sourceTree = ""; }; + BCA9E29F2511AD1400EB5540 /* SwitcherWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitcherWindow.swift; sourceTree = ""; }; + BCA9E2A42511C59F00EB5540 /* NSScreen+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScreen+Extensions.swift"; sourceTree = ""; }; + BCA9E2A8251E871B00EB5540 /* WindowInfoListPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowInfoListPublisher.swift; sourceTree = ""; }; + BCD18FD024DD41C000941A86 /* Switch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Switch.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BCD18FD324DD41C000941A86 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + BCD18FD724DD41C800941A86 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BCD18FDA24DD41C800941A86 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + BCD18FDD24DD41C800941A86 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + BCD18FDF24DD41C800941A86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BCD18FE524DD41C800941A86 /* SwitchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwitchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BCD18FE924DD41C800941A86 /* SwitcherStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitcherStateTests.swift; sourceTree = ""; }; + BCD18FEB24DD41C800941A86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BCD18FF024DD41C800941A86 /* SwitchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwitchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BCD18FF424DD41C900941A86 /* SwitchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchUITests.swift; sourceTree = ""; }; + BCD18FF624DD41C900941A86 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BCD1901224DF773100941A86 /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = ""; }; + BCD1901524DF77A700941A86 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + BCD1901824DF83DB00941A86 /* EventTap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTap.swift; sourceTree = ""; }; + BCD1901A24DF9D1400941A86 /* LongCmdQ.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongCmdQ.swift; sourceTree = ""; }; + BCD1901D24DFB1AF00941A86 /* Switch.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Switch.entitlements; sourceTree = ""; }; + BCF7E2142523F216002C4D67 /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = ""; }; + BCF7E22C2523F597002C4D67 /* Defaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Defaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BCF7E235252407C2002C4D67 /* StatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItem.swift; sourceTree = ""; }; + BCF7E23A252412DF002C4D67 /* NSMenuItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSMenuItem+Extensions.swift"; sourceTree = ""; }; + BCFEB15B251EAC2F00CFA605 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + BCFEB15C251EAFFF00CFA605 /* Structure.monopic */ = {isa = PBXFileReference; lastKnownFileType = file; path = Structure.monopic; sourceTree = ""; }; + BCFEB15E251EE71B00CFA605 /* WindowInfoGroupTests+Chrome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WindowInfoGroupTests+Chrome.swift"; sourceTree = ""; }; + BCFEB16025200A3300CFA605 /* Collection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Extensions.swift"; sourceTree = ""; }; + BCFEB16325200C4B00CFA605 /* SwitcherStateFuzzerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitcherStateFuzzerTests.swift; sourceTree = ""; }; + BCFEB16625203DB700CFA605 /* appcast.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = appcast.xml; path = ../../../appcast.xml; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BC2121C724E3DDD8009EAE3B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BC2121D624E3DE80009EAE3B /* AppKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FCD24DD41C000941A86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BC9CE8CB2522CE2900262FC2 /* AppKit.framework in Frameworks */, + BCD1901724DF77B200941A86 /* Carbon.framework in Frameworks */, + BC2121BB24E3B61E009EAE3B /* CoreData.framework in Frameworks */, + BCF7E22D2523F597002C4D67 /* Defaults.framework in Frameworks */, + BC56728924E0F0E200206A39 /* Haxcessibility.framework in Frameworks */, + BC9CE8E72522DAA000262FC2 /* LetsMove.framework in Frameworks */, + BC9CE8ED2522DAC200262FC2 /* Security.framework in Frameworks */, + BC9CE8CA2522CE1000262FC2 /* Sparkle.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FE224DD41C800941A86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FED24DD41C800941A86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BC2121B224E25CD3009EAE3B /* Pasteboard */ = { + isa = PBXGroup; + children = ( + BC56728124DFD27400206A39 /* PasteboardHistory.swift */, + BC2121B724E3B4AF009EAE3B /* PasteboardHistory.xcdatamodeld */, + BC2121B324E25CE9009EAE3B /* PasteboardHistoryView.swift */, + ); + path = Pasteboard; + sourceTree = ""; + }; + BC2121CB24E3DDD8009EAE3B /* relaunch */ = { + isa = PBXGroup; + children = ( + BC2121CC24E3DDD8009EAE3B /* main.m */, + BC2121D124E3DE22009EAE3B /* SWTerminationListener.h */, + BC2121D224E3DE22009EAE3B /* SWTerminationListener.m */, + ); + path = relaunch; + sourceTree = ""; + }; + BC56728024DFD22C00206A39 /* Switcher */ = { + isa = PBXGroup; + children = ( + BCFEB15C251EAFFF00CFA605 /* Structure.monopic */, + BCFEB15B251EAC2F00CFA605 /* README.md */, + BCA9E28E250DF32A00EB5540 /* Switcher.swift */, + BCA9E289250DA35F00EB5540 /* SwitcherState.swift */, + BC2121DE24E63F7C009EAE3B /* SwitcherView.swift */, + BCA9E29F2511AD1400EB5540 /* SwitcherWindow.swift */, + BC56728624E0BABC00206A39 /* WindowInfo.swift */, + BC56728D24E23BA300206A39 /* WindowInfoGroup.swift */, + BCA9E2A8251E871B00EB5540 /* WindowInfoListPublisher.swift */, + BCA9E29B25101EEF00EB5540 /* WindowView.swift */, + ); + path = Switcher; + sourceTree = ""; + }; + BC9CE95B2523EEC500262FC2 /* Platform Extensions */ = { + isa = PBXGroup; + children = ( + BCA9E295250F34D100EB5540 /* CGSize+Extensions.swift */, + BC2121E024E669EB009EAE3B /* NSRect+Extensions.swift */, + BCA9E287250CB2A400EB5540 /* NSThread+Extensions.swift */, + BCA9E28C250DF2B000EB5540 /* Set+Extensions.swift */, + BCA9E29D25117E4300EB5540 /* NSImage+Extensions.swift */, + BCA9E2A42511C59F00EB5540 /* NSScreen+Extensions.swift */, + BC9CE9462523CB0E00262FC2 /* Publishers+Extensions.swift */, + BCF7E23A252412DF002C4D67 /* NSMenuItem+Extensions.swift */, + ); + path = "Platform Extensions"; + sourceTree = ""; + }; + BCD18FC724DD41C000941A86 = { + isa = PBXGroup; + children = ( + BCD18FD224DD41C000941A86 /* Switch */, + BCD18FE824DD41C800941A86 /* SwitchTests */, + BCD18FF324DD41C900941A86 /* SwitchUITests */, + BC2121CB24E3DDD8009EAE3B /* relaunch */, + BCD18FD124DD41C000941A86 /* Products */, + BCD1901424DF77A700941A86 /* Frameworks */, + ); + sourceTree = ""; + }; + BCD18FD124DD41C000941A86 /* Products */ = { + isa = PBXGroup; + children = ( + BCD18FD024DD41C000941A86 /* Switch.app */, + BCD18FE524DD41C800941A86 /* SwitchTests.xctest */, + BCD18FF024DD41C800941A86 /* SwitchUITests.xctest */, + BC2121CA24E3DDD8009EAE3B /* relaunch */, + ); + name = Products; + sourceTree = ""; + }; + BCD18FD224DD41C000941A86 /* Switch */ = { + isa = PBXGroup; + children = ( + BCD1901124DF769700941A86 /* Common */, + BC9CE95B2523EEC500262FC2 /* Platform Extensions */, + BCD1901C24DF9D1B00941A86 /* Services */, + BCFEB165252011BB00CFA605 /* Supporting Files */, + BCD18FD324DD41C000941A86 /* AppDelegate.swift */, + ); + path = Switch; + sourceTree = ""; + }; + BCD18FE824DD41C800941A86 /* SwitchTests */ = { + isa = PBXGroup; + children = ( + BCFEB16225200C3200CFA605 /* SwitcherState */, + BCFEB15D251EE47200CFA605 /* WindowInfoGroupTests */, + BCD18FEB24DD41C800941A86 /* Info.plist */, + BCFEB16025200A3300CFA605 /* Collection+Extensions.swift */, + ); + path = SwitchTests; + sourceTree = ""; + }; + BCD18FF324DD41C900941A86 /* SwitchUITests */ = { + isa = PBXGroup; + children = ( + BCD18FF424DD41C900941A86 /* SwitchUITests.swift */, + BCD18FF624DD41C900941A86 /* Info.plist */, + ); + path = SwitchUITests; + sourceTree = ""; + }; + BCD1901124DF769700941A86 /* Common */ = { + isa = PBXGroup; + children = ( + BC2121B524E29323009EAE3B /* Debugger Support.swift */, + BCD1901824DF83DB00941A86 /* EventTap.swift */, + BCD1901224DF773100941A86 /* Keyboard.swift */, + BC9CE8AB25216A8A00262FC2 /* Keyboard+NestedTypes.swift */, + BCA9E290250E006700EB5540 /* WeakBox.swift */, + BCF7E2142523F216002C4D67 /* Defaults.swift */, + BC1A123C260471E400DB3906 /* Previews.swift */, + ); + path = Common; + sourceTree = ""; + }; + BCD1901424DF77A700941A86 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BCF7E22C2523F597002C4D67 /* Defaults.framework */, + BC9CE8EC2522DAC200262FC2 /* Security.framework */, + BC9CE8E62522DAA000262FC2 /* LetsMove.framework */, + BC2121D524E3DE80009EAE3B /* AppKit.framework */, + BCD1901524DF77A700941A86 /* Carbon.framework */, + BC2121BA24E3B61E009EAE3B /* CoreData.framework */, + BC56728824E0F0E200206A39 /* Haxcessibility.framework */, + BC9CE8C92522CE1000262FC2 /* Sparkle.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BCD1901C24DF9D1B00941A86 /* Services */ = { + isa = PBXGroup; + children = ( + BC2121B224E25CD3009EAE3B /* Pasteboard */, + BC56728024DFD22C00206A39 /* Switcher */, + BCD1901A24DF9D1400941A86 /* LongCmdQ.swift */, + BC56727E24DFC84B00206A39 /* Services.swift */, + BCF7E235252407C2002C4D67 /* StatusItem.swift */, + ); + path = Services; + sourceTree = ""; + }; + BCFEB15D251EE47200CFA605 /* WindowInfoGroupTests */ = { + isa = PBXGroup; + children = ( + BCFEB15E251EE71B00CFA605 /* WindowInfoGroupTests+Chrome.swift */, + ); + path = WindowInfoGroupTests; + sourceTree = ""; + }; + BCFEB16225200C3200CFA605 /* SwitcherState */ = { + isa = PBXGroup; + children = ( + BCD18FE924DD41C800941A86 /* SwitcherStateTests.swift */, + BCFEB16325200C4B00CFA605 /* SwitcherStateFuzzerTests.swift */, + ); + path = SwitcherState; + sourceTree = ""; + }; + BCFEB165252011BB00CFA605 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + BCFEB16625203DB700CFA605 /* appcast.xml */, + BCD18FD724DD41C800941A86 /* Assets.xcassets */, + BCD18FDF24DD41C800941A86 /* Info.plist */, + BCD18FDC24DD41C800941A86 /* Main.storyboard */, + BCD18FDA24DD41C800941A86 /* Preview Assets.xcassets */, + BC9CE94B2523D27C00262FC2 /* Sparkle.dsa_pub.pem */, + BCD1901D24DFB1AF00941A86 /* Switch.entitlements */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BC2121C924E3DDD8009EAE3B /* relaunch */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC2121D024E3DDD8009EAE3B /* Build configuration list for PBXNativeTarget "relaunch" */; + buildPhases = ( + BC2121C624E3DDD8009EAE3B /* Sources */, + BC2121C724E3DDD8009EAE3B /* Frameworks */, + BC2121C824E3DDD8009EAE3B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = relaunch; + productName = relaunch; + productReference = BC2121CA24E3DDD8009EAE3B /* relaunch */; + productType = "com.apple.product-type.tool"; + }; + BCD18FCF24DD41C000941A86 /* Switch */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCD18FF924DD41C900941A86 /* Build configuration list for PBXNativeTarget "Switch" */; + buildPhases = ( + BC56728524E093E100206A39 /* Lint Sources */, + BCD18FCC24DD41C000941A86 /* Sources */, + BCD18FCD24DD41C000941A86 /* Frameworks */, + BCD18FCE24DD41C000941A86 /* Resources */, + BC2121D924E3DEA9009EAE3B /* Copy Executable Files */, + BC9CE8F62522DBC000262FC2 /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + BC2121D824E3DE94009EAE3B /* PBXTargetDependency */, + ); + name = Switch; + productName = Switch; + productReference = BCD18FD024DD41C000941A86 /* Switch.app */; + productType = "com.apple.product-type.application"; + }; + BCD18FE424DD41C800941A86 /* SwitchTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCD18FFC24DD41C900941A86 /* Build configuration list for PBXNativeTarget "SwitchTests" */; + buildPhases = ( + BCD18FE124DD41C800941A86 /* Sources */, + BCD18FE224DD41C800941A86 /* Frameworks */, + BCD18FE324DD41C800941A86 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BCD18FE724DD41C800941A86 /* PBXTargetDependency */, + ); + name = SwitchTests; + productName = SwitchTests; + productReference = BCD18FE524DD41C800941A86 /* SwitchTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + BCD18FEF24DD41C800941A86 /* SwitchUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCD18FFF24DD41C900941A86 /* Build configuration list for PBXNativeTarget "SwitchUITests" */; + buildPhases = ( + BCD18FEC24DD41C800941A86 /* Sources */, + BCD18FED24DD41C800941A86 /* Frameworks */, + BCD18FEE24DD41C800941A86 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BCD18FF224DD41C900941A86 /* PBXTargetDependency */, + ); + name = SwitchUITests; + productName = SwitchUITests; + productReference = BCD18FF024DD41C800941A86 /* SwitchUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BCD18FC824DD41C000941A86 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1200; + LastUpgradeCheck = 1200; + TargetAttributes = { + BC2121C924E3DDD8009EAE3B = { + CreatedOnToolsVersion = 12.0; + }; + BCD18FCF24DD41C000941A86 = { + CreatedOnToolsVersion = 12.0; + }; + BCD18FE424DD41C800941A86 = { + CreatedOnToolsVersion = 12.0; + TestTargetID = BCD18FCF24DD41C000941A86; + }; + BCD18FEF24DD41C800941A86 = { + CreatedOnToolsVersion = 12.0; + TestTargetID = BCD18FCF24DD41C000941A86; + }; + }; + }; + buildConfigurationList = BCD18FCB24DD41C000941A86 /* Build configuration list for PBXProject "Switch" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BCD18FC724DD41C000941A86; + productRefGroup = BCD18FD124DD41C000941A86 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BCD18FCF24DD41C000941A86 /* Switch */, + BCD18FE424DD41C800941A86 /* SwitchTests */, + BCD18FEF24DD41C800941A86 /* SwitchUITests */, + BC2121C924E3DDD8009EAE3B /* relaunch */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BCD18FCE24DD41C000941A86 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCD18FDE24DD41C800941A86 /* Main.storyboard in Resources */, + BC9CE94C2523D27C00262FC2 /* Sparkle.dsa_pub.pem in Resources */, + BCD18FDB24DD41C800941A86 /* Preview Assets.xcassets in Resources */, + BCD18FD824DD41C800941A86 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FE324DD41C800941A86 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FEE24DD41C800941A86 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + BC56728524E093E100206A39 /* Lint Sources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Lint Sources"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BC2121C624E3DDD8009EAE3B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC2121D424E3DE22009EAE3B /* SWTerminationListener.m in Sources */, + BC2121CD24E3DDD8009EAE3B /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FCC24DD41C000941A86 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCD1901324DF773100941A86 /* Keyboard.swift in Sources */, + BCA9E2A52511C59F00EB5540 /* NSScreen+Extensions.swift in Sources */, + BC2121DF24E63F7C009EAE3B /* SwitcherView.swift in Sources */, + BCA9E288250CB2A400EB5540 /* NSThread+Extensions.swift in Sources */, + BC2121E124E669EB009EAE3B /* NSRect+Extensions.swift in Sources */, + BC56728224DFD27400206A39 /* PasteboardHistory.swift in Sources */, + BC2121B424E25CE9009EAE3B /* PasteboardHistoryView.swift in Sources */, + BCA9E29C25101EEF00EB5540 /* WindowView.swift in Sources */, + BCF7E236252407C2002C4D67 /* StatusItem.swift in Sources */, + BC9CE9472523CB0E00262FC2 /* Publishers+Extensions.swift in Sources */, + BCA9E28D250DF2B000EB5540 /* Set+Extensions.swift in Sources */, + BCD1901924DF83DB00941A86 /* EventTap.swift in Sources */, + BC56728724E0BABC00206A39 /* WindowInfo.swift in Sources */, + BCA9E2A02511AD1400EB5540 /* SwitcherWindow.swift in Sources */, + BC2121B624E29323009EAE3B /* Debugger Support.swift in Sources */, + BC56727F24DFC84B00206A39 /* Services.swift in Sources */, + BC9CE8AC25216A8A00262FC2 /* Keyboard+NestedTypes.swift in Sources */, + BCA9E28A250DA35F00EB5540 /* SwitcherState.swift in Sources */, + BCF7E2152523F216002C4D67 /* Defaults.swift in Sources */, + BC2121B924E3B4AF009EAE3B /* PasteboardHistory.xcdatamodeld in Sources */, + BCD1901B24DF9D1400941A86 /* LongCmdQ.swift in Sources */, + BCD18FD424DD41C000941A86 /* AppDelegate.swift in Sources */, + BCA9E29E25117E4400EB5540 /* NSImage+Extensions.swift in Sources */, + BC56728E24E23BA300206A39 /* WindowInfoGroup.swift in Sources */, + BCA9E28F250DF32A00EB5540 /* Switcher.swift in Sources */, + BCF7E23B252412DF002C4D67 /* NSMenuItem+Extensions.swift in Sources */, + BCA9E2A9251E871C00EB5540 /* WindowInfoListPublisher.swift in Sources */, + BCA9E291250E006700EB5540 /* WeakBox.swift in Sources */, + BC1A123D260471E400DB3906 /* Previews.swift in Sources */, + BCA9E296250F34D100EB5540 /* CGSize+Extensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FE124DD41C800941A86 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCFEB16425200C4B00CFA605 /* SwitcherStateFuzzerTests.swift in Sources */, + BCD18FEA24DD41C800941A86 /* SwitcherStateTests.swift in Sources */, + BCFEB15F251EE71B00CFA605 /* WindowInfoGroupTests+Chrome.swift in Sources */, + BCFEB16125200A3300CFA605 /* Collection+Extensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCD18FEC24DD41C800941A86 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCD18FF524DD41C900941A86 /* SwitchUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BC2121D824E3DE94009EAE3B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC2121C924E3DDD8009EAE3B /* relaunch */; + targetProxy = BC2121D724E3DE94009EAE3B /* PBXContainerItemProxy */; + }; + BCD18FE724DD41C800941A86 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BCD18FCF24DD41C000941A86 /* Switch */; + targetProxy = BCD18FE624DD41C800941A86 /* PBXContainerItemProxy */; + }; + BCD18FF224DD41C900941A86 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BCD18FCF24DD41C000941A86 /* Switch */; + targetProxy = BCD18FF124DD41C900941A86 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BCD18FDC24DD41C800941A86 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BCD18FDD24DD41C800941A86 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BC2121CE24E3DDD8009EAE3B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + BC2121CF24E3DDD8009EAE3B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + BCD18FF724DD41C900941A86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = "\"$SRCROOT/../Dependencies\"/**"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BCD18FF824DD41C900941A86 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; + CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = "\"$SRCROOT/../Dependencies\"/**"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "NDEBUG=1"; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_DISABLE_SAFETY_CHECKS = YES; + SWIFT_ENFORCE_EXCLUSIVE_ACCESS = "debug-only"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + BCD18FFA24DD41C900941A86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Switch/Supporting Files/${PRODUCT_NAME}.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Switch/Supporting Files\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Switch/Supporting Files/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.Switch; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + BCD18FFB24DD41C900941A86 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Switch/Supporting Files/${PRODUCT_NAME}.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Switch/Supporting Files\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = "Switch/Supporting Files/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.Switch; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + BCD18FFD24DD41C900941A86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = SwitchTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.SwitchTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Switch.app/Contents/MacOS/Switch"; + }; + name = Debug; + }; + BCD18FFE24DD41C900941A86 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = SwitchTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.SwitchTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Switch.app/Contents/MacOS/Switch"; + }; + name = Release; + }; + BCD1900024DD41C900941A86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = SwitchUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.SwitchUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Switch; + }; + name = Debug; + }; + BCD1900124DD41C900941A86 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = SwitchUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = net.numist.SwitchUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Switch; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BC2121D024E3DDD8009EAE3B /* Build configuration list for PBXNativeTarget "relaunch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC2121CE24E3DDD8009EAE3B /* Debug */, + BC2121CF24E3DDD8009EAE3B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCD18FCB24DD41C000941A86 /* Build configuration list for PBXProject "Switch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCD18FF724DD41C900941A86 /* Debug */, + BCD18FF824DD41C900941A86 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCD18FF924DD41C900941A86 /* Build configuration list for PBXNativeTarget "Switch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCD18FFA24DD41C900941A86 /* Debug */, + BCD18FFB24DD41C900941A86 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCD18FFC24DD41C900941A86 /* Build configuration list for PBXNativeTarget "SwitchTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCD18FFD24DD41C900941A86 /* Debug */, + BCD18FFE24DD41C900941A86 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCD18FFF24DD41C900941A86 /* Build configuration list for PBXNativeTarget "SwitchUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCD1900024DD41C900941A86 /* Debug */, + BCD1900124DD41C900941A86 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + BC2121B724E3B4AF009EAE3B /* PasteboardHistory.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + BC2121B824E3B4AF009EAE3B /* PasteboardHistory.xcdatamodel */, + ); + currentVersion = BC2121B824E3B4AF009EAE3B /* PasteboardHistory.xcdatamodel */; + path = PasteboardHistory.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = BCD18FC824DD41C000941A86 /* Project object */; +} diff --git a/Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Switch/Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 72% rename from Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Switch/Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 9570607d..919434a6 100644 --- a/Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Switch/Switch.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/exportOptionsApp.plist b/Switch/Switch.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 72% rename from exportOptionsApp.plist rename to Switch/Switch.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index a1c358a8..18d98100 100644 --- a/exportOptionsApp.plist +++ b/Switch/Switch.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -2,7 +2,7 @@ - method - mac-application + IDEDidComputeMac32BitWarning + - \ No newline at end of file + diff --git a/Switch/Switch/AppDelegate.swift b/Switch/Switch/AppDelegate.swift new file mode 100644 index 00000000..af14920e --- /dev/null +++ b/Switch/Switch/AppDelegate.swift @@ -0,0 +1,91 @@ +/* + Goals: + - Hold ⌘Q to quit + - Window switcher + - Paste history + - Battery menu item with runtime? + */ + +import Cocoa +import Combine +import Defaults +import LetsMove +import OSLog +import Sparkle +import SwiftUI + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + func applicationDidFinishLaunching(_ aNotification: Notification) { + if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil { + // Don't set up the app when running for previews + return + } + + if ProcessInfo.processInfo.environment["XCTestBundlePath"] != nil { + // Don't set up the app when running unit tests + return + } + + if !amIBeingDebugged() { + PFMoveToApplicationsFolderIfNecessary() + } + + Services.start(.statusItem) + + // AX permissions shenanigans + guard AXIsProcessTrustedWithOptions(nil) else { + promptForAccessibilityPermissions() + return + } + // The only acceptable reason for an event tap to fail is when `!AXIsProcessTrustedWithOptions(nil)` + try! Keyboard.enableHotKeys() // swiftlint:disable:this force_try + + Defaults.observe(.SUFeedURL) { change in + SUUpdater.shared()?.feedURL = URL(string: change.newValue)! + }.tieToLifetime(of: self) + + if Defaults[.firstLaunch] { + print("This is our first launch!!") + // TODO(numist): show prefs + } + + Services.start(.switcher) + + let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") + let appName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) + print("Launched \(appName!) \(version!)") + } + + @objc func showPreferences(sender: NSObject?) { + // TODO(numist): preferences window + } +} + +// MARK: Accessibility dance + +private var timerCancellable: AnyCancellable? + +private func promptForAccessibilityPermissions() { + assert(timerCancellable == nil, "promptForAccessibilityPermissions() was called more than once") + + os_log(.info, "Switch is not trusted, prompting for AX permissions") + AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary) + + timerCancellable = Timer.publish(every: 0.25, on: .main, in: .common) + .autoconnect() + .map({ _ in + AXIsProcessTrustedWithOptions(nil) + }) + .removeDuplicates() + .filter({ isTrusted in + isTrusted + }) + .sink { _ in + os_log(.info, "AX now trusts us") + if !amIBeingDebugged() { + // TODO(numist): time to relaunch! + os_log(.info, "time to relaunch!") + } + } +} diff --git a/Switch/Switch/Common/Debugger Support.swift b/Switch/Switch/Common/Debugger Support.swift new file mode 100644 index 00000000..bbda94a3 --- /dev/null +++ b/Switch/Switch/Common/Debugger Support.swift @@ -0,0 +1,21 @@ +import Darwin +import Foundation + +func amIBeingDebugged() -> Bool { + var info = kinfo_proc() + var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()] + var size = MemoryLayout.stride + let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0) + assert(junk == 0, "sysctl failed") + return (info.kp_proc.p_flag & P_TRACED) != 0 +} + +func stopwatch(_ title: String, threshold: Double, _ closure: () -> T) -> T { + let start = Date() + let result = closure() + let elapsed = -start.timeIntervalSinceNow + if elapsed > threshold { + print("⏱: \(title) took \(elapsed) seconds") + } + return result +} diff --git a/Switch/Switch/Common/Defaults.swift b/Switch/Switch/Common/Defaults.swift new file mode 100644 index 00000000..c6dae162 --- /dev/null +++ b/Switch/Switch/Common/Defaults.swift @@ -0,0 +1,15 @@ +import Defaults +import Foundation + +extension Defaults.Keys { + static let firstLaunch = Key("firstLaunch", default: true) + static let multimonInterface = Key("multimonInterface", default: true) + static let multimonGroupByMonitor = Key("multimonGroupByMonitor", default: true) + static let showStatusItem = Key("showStatusItem", default: true) + + // This would be better as a URL, but previous shipping versions of Switch already used a string for this default + static let SUFeedURL = Key( + "SUFeedURL", + default: Bundle.main.object(forInfoDictionaryKey: "SUFeedURL")! as! String // swiftlint:disable:this force_cast + ) +} diff --git a/Switch/Switch/Common/EventTap.swift b/Switch/Switch/Common/EventTap.swift new file mode 100644 index 00000000..960ff7d0 --- /dev/null +++ b/Switch/Switch/Common/EventTap.swift @@ -0,0 +1,109 @@ +import Carbon +import OSLog + +private func eventCallback( + _: CGEventTapProxy, + type: CGEventType, + event: CGEvent, + userInfo: UnsafeMutableRawPointer? +) -> Unmanaged? { + let box = Unmanaged>.fromOpaque(userInfo!).takeUnretainedValue() + guard let this = box.value else { return Unmanaged.passUnretained(event) } + + if type == .tapDisabledByTimeout { + if !amIBeingDebugged() { + os_log(.fault, "tap disabled: tapDisabledByTimeout") + Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in + os_log("Re-enabling event tap") + CGEvent.tapEnable(tap: this.eventTap, enable: true) + } + } else { + os_log("tap disabled: tapDisabledByTimeout, will not re-enable") + } + return Unmanaged.passUnretained(event) + } else if type == .tapDisabledByUserInput { + return Unmanaged.passUnretained(event) + } + + if let result = this.callback(type, event) { + return Unmanaged.passRetained(result) + } else { + return nil + } +} + +class EventTap { + static var eventThread: Thread = { + let thread = Thread(block: { + while true { + // Docs: If no input sources or timers are attached to the run loop, this method exits immediately + RunLoop.current.run() + usleep(1000) + } + }) + thread.name = "net.numist.Switch.EventTapCallbackThread" + thread.qualityOfService = .userInteractive + thread.threadPriority = 1.0 + thread.start() + return thread + }() + + struct Error: Swift.Error {} + struct EventTypes: OptionSet { + let rawValue: Int64 + + static let null = EventTypes(rawValue: 1 << CGEventType.null.rawValue) + static let leftMouseDown = EventTypes(rawValue: 1 << CGEventType.leftMouseDown.rawValue) + static let leftMouseUp = EventTypes(rawValue: 1 << CGEventType.leftMouseUp.rawValue) + static let rightMouseDown = EventTypes(rawValue: 1 << CGEventType.rightMouseDown.rawValue) + static let rightMouseUp = EventTypes(rawValue: 1 << CGEventType.rightMouseUp.rawValue) + static let mouseMoved = EventTypes(rawValue: 1 << CGEventType.mouseMoved.rawValue) + static let leftMouseDragged = EventTypes(rawValue: 1 << CGEventType.leftMouseDragged.rawValue) + static let rightMouseDragged = EventTypes(rawValue: 1 << CGEventType.rightMouseDragged.rawValue) + static let keyDown = EventTypes(rawValue: 1 << CGEventType.keyDown.rawValue) + static let keyUp = EventTypes(rawValue: 1 << CGEventType.keyUp.rawValue) + static let flagsChanged = EventTypes(rawValue: 1 << CGEventType.flagsChanged.rawValue) + static let scrollWheel = EventTypes(rawValue: 1 << CGEventType.scrollWheel.rawValue) + static let tabletPointer = EventTypes(rawValue: 1 << CGEventType.tabletPointer.rawValue) + static let tabletProximity = EventTypes(rawValue: 1 << CGEventType.tabletProximity.rawValue) + static let otherMouseDown = EventTypes(rawValue: 1 << CGEventType.otherMouseDown.rawValue) + static let otherMouseUp = EventTypes(rawValue: 1 << CGEventType.otherMouseUp.rawValue) + static let otherMouseDragged = EventTypes(rawValue: 1 << CGEventType.otherMouseDragged.rawValue) + } + + fileprivate let callback: (CGEventType, CGEvent) -> CGEvent? + fileprivate var eventTap: CFMachPort! + private var selfBox: WeakBox! + + init(observing: EventTypes, callback: @escaping (CGEventType, CGEvent) -> CGEvent?) throws { + self.callback = callback + + selfBox = WeakBox(self) + + guard let tap = CGEvent.tapCreate( + tap: .cgSessionEventTap, + place: .headInsertEventTap, + options: .defaultTap, + eventsOfInterest: CGEventMask(observing.rawValue), + callback: eventCallback, + userInfo: Unmanaged.passRetained(selfBox).toOpaque() + ) else { + throw EventTap.Error() + } + + eventTap = tap + EventTap.eventThread.sync { + RunLoop.current.add(tap, forMode: .common) + } + } + + deinit { + if let eventTap = eventTap { + CGEvent.tapEnable(tap: eventTap, enable: false) + EventTap.eventThread.sync { + RunLoop.current.remove(self.eventTap, forMode: .common) + } + } + Unmanaged.passUnretained(selfBox).release() + } +} diff --git a/Switch/Switch/Common/Keyboard+NestedTypes.swift b/Switch/Switch/Common/Keyboard+NestedTypes.swift new file mode 100644 index 00000000..8fd1818d --- /dev/null +++ b/Switch/Switch/Common/Keyboard+NestedTypes.swift @@ -0,0 +1,340 @@ +import Carbon + +// MARK: - Modifiers +extension Keyboard { + struct Modifiers: OptionSet, Hashable, CustomStringConvertible { + let rawValue: Int + + static let shift = Modifiers(rawValue: shiftKey) + static let control = Modifiers(rawValue: controlKey) + static let option = Modifiers(rawValue: optionKey) + static let command = Modifiers(rawValue: cmdKey) + + init(rawValue: Int = 0) { + self.rawValue = rawValue + } + + init(_ flags: CGEventFlags) { + var mods = Modifiers() + if flags.contains(.maskShift) { + mods.insert(.shift) + } + if flags.contains(.maskControl) { + mods.insert(.control) + } + if flags.contains(.maskAlternate) { + mods.insert(.option) + } + if flags.contains(.maskCommand) { + mods.insert(.command) + } + rawValue = mods.rawValue + } + + var description: String { + var result = "" + if self.contains(.control) { + result += "⌃" + } + if self.contains(.option) { + result += "⌥" + } + if self.contains(.shift) { + result += "⇧" + } + if self.contains(.command) { + result += "⌘" + } + return result + } + } +} + +// MARK: - KeyCode +extension Keyboard { + enum KeyCode: UInt16 { + // swiftlint:disable identifier_name + case a = 0x00 // kVK_ANSI_A + case s = 0x01 // kVK_ANSI_S + case d = 0x02 // kVK_ANSI_D + case f = 0x03 // kVK_ANSI_F + case h = 0x04 // kVK_ANSI_H + case g = 0x05 // kVK_ANSI_G + case z = 0x06 // kVK_ANSI_Z + case x = 0x07 // kVK_ANSI_X + case c = 0x08 // kVK_ANSI_C + case v = 0x09 // kVK_ANSI_V + case b = 0x0B // kVK_ANSI_B + case q = 0x0C // kVK_ANSI_Q + case w = 0x0D // kVK_ANSI_W + case e = 0x0E // kVK_ANSI_E + case r = 0x0F // kVK_ANSI_R + case y = 0x10 // kVK_ANSI_Y + case t = 0x11 // kVK_ANSI_T + case one = 0x12 // kVK_ANSI_1 + case two = 0x13 // kVK_ANSI_2 + case three = 0x14 // kVK_ANSI_3 + case four = 0x15 // kVK_ANSI_4 + case six = 0x16 // kVK_ANSI_6 + case five = 0x17 // kVK_ANSI_5 + case equal = 0x18 // kVK_ANSI_Equal + case nine = 0x19 // kVK_ANSI_9 + case seven = 0x1A // kVK_ANSI_7 + case minus = 0x1B // kVK_ANSI_Minus + case eight = 0x1C // kVK_ANSI_8 + case zero = 0x1D // kVK_ANSI_0 + case rightBracket = 0x1E // kVK_ANSI_RightBracket + case o = 0x1F // kVK_ANSI_O + case u = 0x20 // kVK_ANSI_U + case leftBracket = 0x21 // kVK_ANSI_LeftBracket + case i = 0x22 // kVK_ANSI_I + case p = 0x23 // kVK_ANSI_P + case l = 0x25 // kVK_ANSI_L + case j = 0x26 // kVK_ANSI_J + case quote = 0x27 // kVK_ANSI_Quote + case k = 0x28 // kVK_ANSI_K + case semicolon = 0x29 // kVK_ANSI_Semicolon + case backslash = 0x2A // kVK_ANSI_Backslash + case comma = 0x2B // kVK_ANSI_Comma + case slash = 0x2C // kVK_ANSI_Slash + case n = 0x2D // kVK_ANSI_N + case m = 0x2E // kVK_ANSI_M + case period = 0x2F // kVK_ANSI_Period + case grave = 0x32 // kVK_ANSI_Grave + case keypadDecimal = 0x41 // kVK_ANSI_KeypadDecimal + case keypadMultiply = 0x43 // kVK_ANSI_KeypadMultiply + case keypadPlus = 0x45 // kVK_ANSI_KeypadPlus + case keypadClear = 0x47 // kVK_ANSI_KeypadClear + case keypadDivide = 0x4B // kVK_ANSI_KeypadDivide + case keypadEnter = 0x4C // kVK_ANSI_KeypadEnter + case keypadMinus = 0x4E // kVK_ANSI_KeypadMinus + case keypadEquals = 0x51 // kVK_ANSI_KeypadEquals + case keypad0 = 0x52 // kVK_ANSI_Keypad0 + case keypad1 = 0x53 // kVK_ANSI_Keypad1 + case keypad2 = 0x54 // kVK_ANSI_Keypad2 + case keypad3 = 0x55 // kVK_ANSI_Keypad3 + case keypad4 = 0x56 // kVK_ANSI_Keypad4 + case keypad5 = 0x57 // kVK_ANSI_Keypad5 + case keypad6 = 0x58 // kVK_ANSI_Keypad6 + case keypad7 = 0x59 // kVK_ANSI_Keypad7 + case keypad8 = 0x5B // kVK_ANSI_Keypad8 + case keypad9 = 0x5C // kVK_ANSI_Keypad9 + case enter = 0x24 // kVK_Return (sorry nerds, "return" is reserved and "enter" isn't differentiated) + case tab = 0x30 // kVK_Tab + case space = 0x31 // kVK_Space + case delete = 0x33 // kVK_Delete + case escape = 0x35 // kVK_Escape + case command = 0x37 // kVK_Command + case shift = 0x38 // kVK_Shift + case capsLock = 0x39 // kVK_CapsLock + case option = 0x3A // kVK_Option + case control = 0x3B // kVK_Control + case rightCommand = 0x36 // kVK_RightCommand + case rightShift = 0x3C // kVK_RightShift + case rightOption = 0x3D // kVK_RightOption + case rightControl = 0x3E // kVK_RightControl + case function = 0x3F // kVK_Function + case f17 = 0x40 // kVK_F17 + case volumeUp = 0x48 // kVK_VolumeUp + case volumeDown = 0x49 // kVK_VolumeDown + case mute = 0x4A // kVK_Mute + case f18 = 0x4F // kVK_F18 + case f19 = 0x50 // kVK_F19 + case f20 = 0x5A // kVK_F20 + case f5 = 0x60 // kVK_F5 + case f6 = 0x61 // kVK_F6 + case f7 = 0x62 // kVK_F7 + case f3 = 0x63 // kVK_F3 + case f8 = 0x64 // kVK_F8 + case f9 = 0x65 // kVK_F9 + case f11 = 0x67 // kVK_F11 + case f13 = 0x69 // kVK_F13 + case f16 = 0x6A // kVK_F16 + case f14 = 0x6B // kVK_F14 + case f10 = 0x6D // kVK_F10 + case f12 = 0x6F // kVK_F12 + case f15 = 0x71 // kVK_F15 + case help = 0x72 // kVK_Help + case home = 0x73 // kVK_Home + case pageUp = 0x74 // kVK_PageUp + case forwardDelete = 0x75 // kVK_ForwardDelete + case f4 = 0x76 // kVK_F4 + case end = 0x77 // kVK_End + case f2 = 0x78 // kVK_F2 + case pageDown = 0x79 // kVK_PageDown + case f1 = 0x7A // kVK_F1 + case leftArrow = 0x7B // kVK_LeftArrow + case rightArrow = 0x7C // kVK_RightArrow + case downArrow = 0x7D // kVK_DownArrow + case upArrow = 0x7E // kVK_UpArrow + case section = 0x0A // kVK_ISO_Section + case yen = 0x5D // kVK_JIS_Yen + case underscore = 0x5E // kVK_JIS_Underscore + case keypadComma = 0x5F // kVK_JIS_KeypadComma + case eisu = 0x66 // kVK_JIS_Eisu + case kana = 0x68 // kVK_JIS_Kana + case unknown = 0xFF + // swiftlint:enable identifier_name + } +} + +extension Keyboard.KeyCode { + var description: String { + switch self { + case .a: return "A" + case .s: return "S" + case .d: return "D" + case .f: return "F" + case .h: return "H" + case .g: return "G" + case .z: return "Z" + case .x: return "X" + case .c: return "C" + case .v: return "V" + case .b: return "B" + case .q: return "Q" + case .w: return "W" + case .e: return "E" + case .r: return "R" + case .y: return "Y" + case .t: return "T" + case .one: return "1" + case .two: return "2" + case .three: return "3" + case .four: return "4" + case .six: return "6" + case .five: return "5" + case .equal: return "=" + case .nine: return "9" + case .seven: return "7" + case .minus: return "-" + case .eight: return "8" + case .zero: return "0" + case .rightBracket: return "]" + case .o: return "O" + case .u: return "U" + case .leftBracket: return "[" + case .i: return "I" + case .p: return "P" + case .l: return "L" + case .j: return "J" + case .quote: return "'" + case .k: return "K" + case .semicolon: return ";" + case .backslash: return "\\" + case .comma: return "," + case .slash: return "/" + case .n: return "N" + case .m: return "M" + case .period: return "." + case .grave: return "`" + case .keypadDecimal: return ".⃣" + case .keypadMultiply: return "×⃣" + case .keypadPlus: return "+⃣" + case .keypadClear: return "⌧⃣" + case .keypadDivide: return "÷⃣" + case .keypadEnter: return "⌤" + case .keypadMinus: return "-⃣" + case .keypadEquals: return "=⃣" + case .keypad0: return "0︎⃣" + case .keypad1: return "1︎⃣" + case .keypad2: return "2︎⃣" + case .keypad3: return "3︎⃣" + case .keypad4: return "4︎⃣" + case .keypad5: return "5︎⃣" + case .keypad6: return "6︎⃣" + case .keypad7: return "7︎⃣" + case .keypad8: return "8︎⃣" + case .keypad9: return "9︎⃣" + case .enter: return "⏎" + case .tab: return "⇥" + case .space: return "␣" + case .delete: return "⌫" + case .escape: return "⎋" + case .command: return "⌘" + case .shift: return "⇧" + case .capsLock: return "⇪" + case .option: return "⌥" + case .control: return "⌃" + case .rightCommand: return "⌘" + case .rightShift: return "⇧" + case .rightOption: return "⌥" + case .rightControl: return "⌃" + case .function: return "fn" + case .f17: return "F17" + case .volumeUp: return "🔊" + case .volumeDown: return "🔉" + case .mute: return "🔇" + case .f18: return "F18" + case .f19: return "F19" + case .f20: return "F20" + case .f5: return "F5" + case .f6: return "F6" + case .f7: return "F7" + case .f3: return "F3" + case .f8: return "F8" + case .f9: return "F9" + case .f11: return "F11" + case .f13: return "F13" + case .f16: return "F16" + case .f14: return "F14" + case .f10: return "F10" + case .f12: return "F12" + case .f15: return "F15" + case .help: return "help" + case .home: return "↖︎" + case .pageUp: return "⇞" + case .forwardDelete: return "⌦" + case .f4: return "F4" + case .end: return "↘︎" + case .f2: return "F2" + case .pageDown: return "⇟" + case .f1: return "F1" + case .leftArrow: return "←" + case .rightArrow: return "→" + case .downArrow: return "↓" + case .upArrow: return "↑" + case .section: return "§" + case .yen: return "¥" + case .underscore: return "_" + case .keypadComma: return ",⃣" + case .eisu, .kana, .unknown: return "�" + } + } +} + +// MARK: - HotKey +extension Keyboard { + struct HotKey: Hashable, CustomStringConvertible, RawRepresentable { + let modifiers: Modifiers + let code: KeyCode + + init(_ modifiers: Modifiers, _ code: KeyCode) { + self.code = code + self.modifiers = modifiers + } + + init(_ event: CGEvent) { + modifiers = Modifiers(event.flags) + code = KeyCode(rawValue: UInt16(event.getIntegerValueField(.keyboardEventKeycode))) ?? .unknown + } + + // MARK: CustomStringConvertible + + var description: String { + return "\(modifiers.description)\(code.description)" + } + + // MARK: RawRepresentable + + typealias RawValue = Int // swiftlint:disable:this nesting + + var rawValue: Int { Int(code.rawValue) | modifiers.rawValue } + + init?(rawValue: Int) { + guard let code = KeyCode(rawValue: UInt16(rawValue & 0xFF)) else { return nil } + self.code = code + modifiers = .init(rawValue: rawValue & ~0xFF) + } + } +} diff --git a/Switch/Switch/Common/Keyboard.swift b/Switch/Switch/Common/Keyboard.swift new file mode 100644 index 00000000..b51f08ad --- /dev/null +++ b/Switch/Switch/Common/Keyboard.swift @@ -0,0 +1,54 @@ +import Carbon +import OSLog + +/// Keyboard interface primarily for hotkey/callbacks +class Keyboard { + /// Try to establish the Keyboard event tap that powers hotkeys + static func enableHotKeys() throws { + assert(eventTap == nil) + eventTap = try EventTap(observing: [.keyDown, .keyUp], callback: Keyboard.keyboardEvent) + } + + /// Remove the event tap powering hotkeys + static func disableHotKeys() { + assert(eventTap != nil) + eventTap = nil + } + + /// Register a callback to be invoked when `hotkey` is pressed or released + static func register(_ hotkey: HotKey, _ closure: @escaping (Bool) -> Bool) { + assert(eventTap != nil) + os_unfair_lock_lock(&lock) + callbacks[hotkey] = closure + os_unfair_lock_unlock(&lock) + } + + /// Remove the registered callback for `hotkey` + static func deregister(_ hotkey: HotKey) { + assert(eventTap != nil) + os_unfair_lock_lock(&lock) + callbacks.removeValue(forKey: hotkey) + os_unfair_lock_unlock(&lock) + } + + // MARK: Internals + + // Would have used CGSSetGlobalHotKeyOperatingMode & co., + // but then I wouldn't be able to do shenanigans with ⌘Q and ⌘⇥ + private static var eventTap: EventTap? + + private static var lock = os_unfair_lock_s() + private static var callbacks = [HotKey: (Bool)->Bool]() + + private static func keyboardEvent(_ type: CGEventType, _ event: CGEvent) -> CGEvent? { + precondition(type == .keyUp || type == .keyDown) + + let hotKey = HotKey(event) + + os_unfair_lock_lock(&lock) + let callback = callbacks[hotKey] + os_unfair_lock_unlock(&lock) + + return (callback?(type == .keyDown) ?? true) ? event : nil + } +} diff --git a/Switch/Switch/Common/Previews.swift b/Switch/Switch/Common/Previews.swift new file mode 100644 index 00000000..015a697f --- /dev/null +++ b/Switch/Switch/Common/Previews.swift @@ -0,0 +1,32 @@ +import SwiftUI + +fileprivate var desktopImage = { + NSImage(contentsOf: NSWorkspace.shared.desktopImageURL(for: NSScreen.main!)!)! + .cropped(to: NSSize(width: 600, height: 600)) +}() + +func previewBackground() -> some View { + GeometryReader { geometry in + ZStack { + Image(nsImage: desktopImage) + ZStack { + Rectangle() + .fill(Color(NSColor.windowBackgroundColor)) + // swiftlint:disable line_length + Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum bibendum nulla quis tincidunt pharetra. Suspendisse mattis aliquet pharetra. Nulla molestie libero sodales, varius felis vel, ullamcorper ante. Donec cursus elit at neque efficitur vestibulum. Ut arcu enim, bibendum eget ex quis, vulputate porta ante. Praesent sed turpis ligula. Aenean id diam leo. Morbi sodales dolor ut elementum eleifend. Curabitur in quam nunc. \n\nSuspendisse potenti. In dapibus, diam id rhoncus facilisis, sem purus luctus odio, vitae elementum nisl urna id orci. Nunc mollis, erat nec pulvinar fermentum, quam ligula accumsan orci, nec scelerisque sem turpis at tortor. Pellentesque") + // swiftlint:enable line_length + } + .frame(width: geometry.size.width / 2.0, height: geometry.size.height) + .position( + x: geometry.size.width / 4.0, + y: geometry.size.height / 2 + ) + } + }.frame(maxWidth: .infinity, maxHeight: .infinity) +} + +struct BackgroundPreview: PreviewProvider { + static var previews: some View { + previewBackground() + } +} diff --git a/Switch/Switch/Common/WeakBox.swift b/Switch/Switch/Common/WeakBox.swift new file mode 100644 index 00000000..3d5cd077 --- /dev/null +++ b/Switch/Switch/Common/WeakBox.swift @@ -0,0 +1,7 @@ +class WeakBox { + private(set) weak var value: T? + + init(_ val: T) { + value = val + } +} diff --git a/Switch/Switch/Platform Extensions/CGSize+Extensions.swift b/Switch/Switch/Platform Extensions/CGSize+Extensions.swift new file mode 100644 index 00000000..d9dd2691 --- /dev/null +++ b/Switch/Switch/Platform Extensions/CGSize+Extensions.swift @@ -0,0 +1,5 @@ +import Foundation + +func / (lhs: CGSize, rhs: CGFloat) -> CGSize { + return CGSize(width: lhs.width / rhs, height: lhs.height / rhs) +} diff --git a/Switch/Switch/Platform Extensions/NSImage+Extensions.swift b/Switch/Switch/Platform Extensions/NSImage+Extensions.swift new file mode 100644 index 00000000..d9217895 --- /dev/null +++ b/Switch/Switch/Platform Extensions/NSImage+Extensions.swift @@ -0,0 +1,31 @@ +import Cocoa + +extension NSImage { + /// Returns a new image containing only the contents within the specified frame + func cropped(to destFrame: NSRect) -> NSImage { + let newImage = NSImage(size: destFrame.size) + newImage.lockFocus() + self.draw( + in: NSRect(origin: NSPoint(x: 0, y: 0), size: destFrame.size), + from: destFrame, + operation: NSCompositingOperation.sourceOver, + fraction: 1.0 + ) + newImage.unlockFocus() + return newImage + } + + /// Returns a crop of the center of the image of the specified size + /// + /// This function was originally authored to speed up preview rendering since only + /// a small fraction of the desktop picture is visible through the preview's viewport. + func cropped(to destSize: NSSize) -> NSImage { + return cropped(to: NSRect( + origin: NSPoint( + x: floor((self.size.width - destSize.width) / 2), + y: floor((self.size.height - destSize.height) / 2) + ), + size: destSize + )) + } +} diff --git a/Switch/Switch/Platform Extensions/NSMenuItem+Extensions.swift b/Switch/Switch/Platform Extensions/NSMenuItem+Extensions.swift new file mode 100644 index 00000000..5fa1f119 --- /dev/null +++ b/Switch/Switch/Platform Extensions/NSMenuItem+Extensions.swift @@ -0,0 +1,13 @@ +import Cocoa + +extension NSMenuItem { + convenience init( + title string: String, + target: AnyObject? = nil, + action selector: Selector? = nil, + keyEquivalent charCode: String = "" + ) { + self.init(title: string, action: selector, keyEquivalent: charCode) + self.target = target + } +} diff --git a/Switch/Switch/Platform Extensions/NSRect+Extensions.swift b/Switch/Switch/Platform Extensions/NSRect+Extensions.swift new file mode 100644 index 00000000..b421e0c1 --- /dev/null +++ b/Switch/Switch/Platform Extensions/NSRect+Extensions.swift @@ -0,0 +1,14 @@ +import Foundation + +extension NSRect: Hashable { + init(origin: NSPoint, size: NSSize) { + self.init(x: origin.x, y: origin.y, width: size.width, height: size.height) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(self.origin.x) + hasher.combine(self.origin.y) + hasher.combine(self.size.width) + hasher.combine(self.size.height) + } +} diff --git a/Switch/Switch/Platform Extensions/NSScreen+Extensions.swift b/Switch/Switch/Platform Extensions/NSScreen+Extensions.swift new file mode 100644 index 00000000..b5684324 --- /dev/null +++ b/Switch/Switch/Platform Extensions/NSScreen+Extensions.swift @@ -0,0 +1,9 @@ +import Cocoa + +extension NSScreen { + var screenNumber: CGDirectDisplayID { + // swiftlint:disable force_cast + return self.deviceDescription[.init(rawValue: "NSScreenNumber")] as! CGDirectDisplayID + // swiftlint:enable force_cast + } +} diff --git a/Switch/Switch/Platform Extensions/NSThread+Extensions.swift b/Switch/Switch/Platform Extensions/NSThread+Extensions.swift new file mode 100644 index 00000000..e1c567dc --- /dev/null +++ b/Switch/Switch/Platform Extensions/NSThread+Extensions.swift @@ -0,0 +1,17 @@ +import Foundation + +extension Thread { + typealias Block = @convention(block) () -> Void + + @objc private func nnk_run(block: Block) { block() } + + /** + Perform block on thread synchronously. + + - parameter block: Work to be executed. + */ + func sync(_ block: @escaping Block) { + guard Thread.current != self else { return block() } + perform(#selector(nnk_run(block:)), on: self, with: block, waitUntilDone: true) + } +} diff --git a/Switch/Switch/Platform Extensions/Publishers+Extensions.swift b/Switch/Switch/Platform Extensions/Publishers+Extensions.swift new file mode 100644 index 00000000..75533fe0 --- /dev/null +++ b/Switch/Switch/Platform Extensions/Publishers+Extensions.swift @@ -0,0 +1,101 @@ +import Combine + +// swiftlint:disable nesting + +extension Publishers { + class SubscribersCounterPublisher: Publisher { + var hasSubscribers: Bool { return subscriberCount > 0 } + private var subscriberCount = 0 + private let upstream: Upstream + private let callback: ((Int) -> Void)? + + init(upstream: Upstream, callback: ((Int) -> Void)? = nil) { + self.upstream = upstream + self.callback = callback + } + + private func increase() { + subscriberCount += 1 + self.callback?(subscriberCount) + } + + private func decrease() { + subscriberCount -= 1 + self.callback?(subscriberCount) + } + + // MARK: Publisher conformance + + typealias Output = Upstream.Output + typealias Failure = Upstream.Failure + + func receive(subscriber: S) + where Upstream.Failure == S.Failure, Upstream.Output == S.Input { + self.increase() + upstream.receive( + subscriber: SubscribersCounterSubscriber( + counter: self, + subscriber: subscriber + ) + ) + } + + // MARK: - + private class SubscribersCounterSubscriber: Subscriber { + private let counter: SubscribersCounterPublisher + private let subscriber: S + + init (counter: SubscribersCounterPublisher, subscriber: S) { + self.counter = counter + self.subscriber = subscriber + } + + // MARK: Subscriber conformance + + typealias Input = S.Input + typealias Failure = S.Failure + + func receive(subscription: Subscription) { + subscriber.receive( + subscription: SubscribersCounterSubscription( + counter: counter, + subscription: subscription + ) + ) + } + + func receive(_ input: S.Input) -> Subscribers.Demand { + return subscriber.receive(input) + } + + func receive(completion: Subscribers.Completion) { + subscriber.receive(completion: completion) + } + } + + // MARK: - + private class SubscribersCounterSubscription: Subscription { + let counter: SubscribersCounterPublisher + let wrapped: Subscription + private var cancelled = false + + init(counter: SubscribersCounterPublisher, subscription: Subscription) { + self.counter = counter + self.wrapped = subscription + } + + deinit { if !cancelled { counter.decrease() } } + + // MARK: Subscription conformance + + func request(_ demand: Subscribers.Demand) { wrapped.request(demand) } + + func cancel() { + precondition(!cancelled) + wrapped.cancel() + counter.decrease() + cancelled = true + } + } + } +} diff --git a/Switch/Switch/Platform Extensions/Set+Extensions.swift b/Switch/Switch/Platform Extensions/Set+Extensions.swift new file mode 100644 index 00000000..22a01888 --- /dev/null +++ b/Switch/Switch/Platform Extensions/Set+Extensions.swift @@ -0,0 +1,10 @@ +extension Set { + func intersects(_ other: Self) -> Bool { + let smaller = self.count < other.count ? self : other + let larger = self.count < other.count ? other : self + for element in smaller { + if larger.contains(element) { return true } + } + return false + } +} diff --git a/Switch/Switch/Services/LongCmdQ.swift b/Switch/Switch/Services/LongCmdQ.swift new file mode 100644 index 00000000..fae0ced1 --- /dev/null +++ b/Switch/Switch/Services/LongCmdQ.swift @@ -0,0 +1,45 @@ +import Foundation +import Carbon.HIToolbox +import OSLog +import Haxcessibility + +// TODO(numist): UI +// TODO(numist): make pretty progress bar +// TODO(numist): enable/disable based on pref +// TODO(numist): staticify, ServiceProtocol for enable/disable? gotta think of how prefs gonna drive behaviour here + +class LongCmdQ { + var timer: Timer? + var injected = false + var timerFired = false + + init() { + Keyboard.register(.init(.command, .q)) { keyDown in + if !keyDown { + self.timer = nil + if self.timerFired { + let result = self.injected + self.injected = false + self.timerFired = false + return result // allow keyup to pass to match injected keydown event + } else { + print("LongCmdQ: ⌘Q cancelled") + } + } else { + if self.timer == nil { + self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in + guard self.timer == timer else { return } + print("LongCmdQ: Timer fired") + self.timerFired = true + } + let appName = NSWorkspace.shared.frontmostApplication?.localizedName ?? "" + print("LongCmdQ: Suppressing ⌘Q on \(appName) (timer started)") + } else if self.timerFired && !self.injected { + self.injected = true + return true + } + } + return false // suppress event + } + } +} diff --git a/Switch/Switch/Services/Pasteboard/PasteboardHistory.swift b/Switch/Switch/Services/Pasteboard/PasteboardHistory.swift new file mode 100644 index 00000000..a5390f18 --- /dev/null +++ b/Switch/Switch/Services/Pasteboard/PasteboardHistory.swift @@ -0,0 +1,164 @@ +import Cocoa +import OSLog +import CoreData + +/* + * TODO(numist): When pushing a historical item into the pasteboard, use type org.nspasteboard.source to properly + * attribute its source: + * + * [pasteboard setString: @"org.nspasteboard.SampleApp" forType: @"org.nspasteboard.source"]; + * + */ + +class PasteboardHistory { + private var sizeLimit = 1024 + private var capacityLimit = 1024 + private var expiration = Date() + + private var changeCount: Int! + private var mouseClickEventTap: EventTap! = nil + + private func recordPasteboard() { + let pasteboard = NSPasteboard.general + guard changeCount != pasteboard.changeCount else { return } + changeCount = pasteboard.changeCount + + if let source = pasteboard.string(forType: .init(rawValue: "org.nspasteboard.source")) { + print("actual source of item: \(source)") + } + + // Courtesy http://nspasteboard.org, maybe we can get listed there someday + let filteredTypes: Set = [ + "org.nspasteboard.TransientType", + "org.nspasteboard.ConcealedType", + "org.nspasteboard.AutoGeneratedType", + "org.nspasteboard.source", + "com.agilebits.onepassword", + "de.petermaurer.TransientPasteboardType", + "com.typeit4me.clipping", + "Pasteboard generator type", + ] + let types = Set(pasteboard.types!.map { $0.rawValue }) + if types.intersects(filteredTypes) { + os_log(.info, "PasteboardHistory: filtered type identifier, skipping") + return + } + + // Other available types include: + // NSPasteboard.PasteboardType.string + // NSPasteboard.PasteboardType.pdf + // NSPasteboard.PasteboardType.tiff + // NSPasteboard.PasteboardType.png + // NSPasteboard.PasteboardType.rtf + // NSPasteboard.PasteboardType.rtfd + // NSPasteboard.PasteboardType.html + // NSPasteboard.PasteboardType.tabularText + // NSPasteboard.PasteboardType.font + // NSPasteboard.PasteboardType.ruler + // NSPasteboard.PasteboardType.color + // NSPasteboard.PasteboardType.sound + // NSPasteboard.PasteboardType.multipleTextSelection + // NSPasteboard.PasteboardType.textFinderOptions + // NSPasteboard.PasteboardType.URL + // NSPasteboard.PasteboardType.fileURL + print(types) // TODO(numist): collect types and build heuristics wrt snippet output formatting? + // But for now we'll just grab NSStringPboardType, if available. + guard let snippet = pasteboard.string(forType: .string) else { + os_log(.info, "PasteboardHistory: no item for type NSStringPboardType, skipping") + return + } + if snippet.allSatisfy({ $0.isWhitespace }) { + os_log(.info, "PasteboardHistory: item is all whitespace, skipping") + return + } + if snippet.count > sizeLimit { + os_log(.info, "PasteboardHistory: item is too big, skipping") + return + } + + // TODO(numist): better app attribution for menu items and immediate-dismiss apps (like Dropbox menu) + let frontmostApp = NSWorkspace.shared.frontmostApplication! + let appBundle = frontmostApp.bundleIdentifier ?? "net.numist.bundle.unknown" + // Get off the main thread when doing I/O + PasteboardHistory.persistentContainer.performBackgroundTask { context in + context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + let item = PasteboardItem(context: context) + item.appBundle = appBundle + item.snippet = snippet + item.lastUsed = Date() + // TODO(numist): possible for db corruption to manifest here and not anywhere else + try? context.save() + os_log(.info, "PasteboardHistory: recorded pasteboard item") + } + } + + private let viewHistory: (Bool) -> Bool = { keyDown in + return false + } + + static var persistentContainer: NSPersistentContainer = { + let container = NSPersistentContainer(name: "PasteboardHistory") + container.loadPersistentStores(completionHandler: { (_, error) in + if let error = error as NSError? { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. + // You should not use this function in a shipping application, although it may be useful during development. + + /* + Typical reasons for an error here include: + * The parent directory does not exist, cannot be created, or disallows writing. + * The persistent store is not accessible, due to permissions or data protection when the device is locked. + * The device is out of space. + * The store could not be migrated to the current model version. + Check the error message to determine what the actual problem was. + */ + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + return container + }() + + init() { + changeCount = NSPasteboard.general.changeCount + + mouseClickEventTap = try? EventTap(observing: .leftMouseUp, callback: { (_, event) -> CGEvent? in + // Longish timeout required because Copy menu items don't appear to + // actually copy until the menu has finished animating out + DispatchQueue.main.asyncAfter(deadline: .now() + 0.50) { + self.recordPasteboard() + } + return event + }) + + let asyncAndRecord: (Bool) -> Bool = { _ in + DispatchQueue.main.async { + self.recordPasteboard() + } + return true + } + Keyboard.register(.init(.command, .x), asyncAndRecord) + Keyboard.register(.init(.command, .c), asyncAndRecord) + Keyboard.register(.init([.command, .option], .v)) { keyDown in + if keyDown { + DispatchQueue.main.async { + // TODO(numist): rubber, meet road + print("Want to paste from history") + } + } + return false + } + + PasteboardHistory.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true + } + + deinit { + Keyboard.deregister(.init(.command, .x)) + Keyboard.deregister(.init(.command, .c)) + Keyboard.deregister(.init([.command, .option], .v)) + } +} + +extension PasteboardItem { + var unwrappedSnippet: String { snippet! } + var unwrappedAppBundle: String { appBundle! } +} diff --git a/Switch/Switch/Services/Pasteboard/PasteboardHistory.xcdatamodeld/PasteboardHistory.xcdatamodel/contents b/Switch/Switch/Services/Pasteboard/PasteboardHistory.xcdatamodeld/PasteboardHistory.xcdatamodel/contents new file mode 100644 index 00000000..e1ea2c8c --- /dev/null +++ b/Switch/Switch/Services/Pasteboard/PasteboardHistory.xcdatamodeld/PasteboardHistory.xcdatamodel/contents @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Switch/Switch/Services/Pasteboard/PasteboardHistoryView.swift b/Switch/Switch/Services/Pasteboard/PasteboardHistoryView.swift new file mode 100644 index 00000000..5da23d78 --- /dev/null +++ b/Switch/Switch/Services/Pasteboard/PasteboardHistoryView.swift @@ -0,0 +1,125 @@ +import Foundation +import Combine +import SwiftUI + +private struct PasteboardItemRow: View { + @ObservedObject var item: PasteboardItem + let index: Int? + let selected: Bool + + var icon: NSImage { + let workspace = NSWorkspace.shared + if let path = workspace.urlForApplication(withBundleIdentifier: item.unwrappedAppBundle)?.path { + return workspace.icon(forFile: path) + } + // TODO(numist): a default app icon doesn't seem to be available via API, copy a stand-in icns into the bundle + return NSImage(named: NSImage.applicationIconName)! + } + + var body: some View { + Button(action: { + print("\(item.unwrappedSnippet)") + }, label: { + HStack { + Image(nsImage: icon).resizable().frame(width: 16, height: 16) + Text(item.unwrappedSnippet.trimmingCharacters(in: .newlines)).lineLimit(1) + Spacer() + Text(index==nil ? "" : "⌘\(index!)").opacity(0.5) + } + }) + .buttonStyle(PlainButtonStyle()) + } +} + +private struct ResultsList: View { + var fetchRequest: FetchRequest + var items: FetchedResults { fetchRequest.wrappedValue } + + // Still some problems around selected state being drawn in the list but this property wrapping appears to be + // effective at preventing out of bounds crashes when `items` shrinks + @State private var _selection: Int? + private var selection: Int? { + if let sel = _selection, sel >= items.count { + if items.isEmpty { + return nil + } + return min(sel, items.count - 1) + } + return _selection + } + + init(query: String) { + fetchRequest = FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \PasteboardItem.lastUsed, ascending: false)], + predicate: query.isEmpty ? nil : NSPredicate(format: "snippet CONTAINS %@", query) + ) + // TODO(numist): selection = (items.isEmpty ? nil : 0), but + // EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) in fetchRequest.wrappedValue + } + + var body: some View { + VStack(spacing: 6) { + HStack { + List(selection: $_selection) { + ForEach(items.indices, id: \.self) { index in + PasteboardItemRow( + item: items[index], + index: (index < 10 ? (index + 1) % 10 : nil), + selected: index == selection + ) + } + }.frame(minWidth: 0, maxWidth: .infinity, alignment: .topLeading) + Text(selection==nil ? "" : items[selection!].snippet!) + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) + }.background(Color(NSColor.windowBackgroundColor)).cornerRadius(4) + } + } +} + +private class Asdf: ObservableObject { + var field = "" { + didSet { + subject.send(field) + } + } + @Published var query = "" + + var subject = PassthroughSubject() + var stream: AnyCancellable? + + init() { + stream = subject + .debounce(for: 0.25, scheduler: RunLoop.main) + .sink(receiveValue: { [weak self] in self?.query = $0 }) + } +} + +struct PasteboardHistoryView: View { + @State fileprivate var asdf = Asdf() + + var body: some View { + VStack(spacing: 6) { + TextField("Filter…", text: $asdf.field) + .lineLimit(1) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .background(Color(NSColor.windowBackgroundColor).opacity(0.9)) + .cornerRadius(4) + ResultsList(query: asdf.query) + .environment(\.managedObjectContext, PasteboardHistory.persistentContainer.viewContext) + } + .padding(8) + .background( + Color(NSColor.underPageBackgroundColor) + .opacity(0.9) + .cornerRadius(10) + ) + } +} + +struct PasteboardHistoryView_Previews: PreviewProvider { + static var previews: some View { + return PasteboardHistoryView() + .frame(width: 500, height: 400, alignment: .center) + .environment(\.managedObjectContext, PasteboardHistory.persistentContainer.viewContext) + } +} diff --git a/Switch/Switch/Services/Services.swift b/Switch/Switch/Services/Services.swift new file mode 100644 index 00000000..039c4e25 --- /dev/null +++ b/Switch/Switch/Services/Services.swift @@ -0,0 +1,39 @@ +import OSLog +import Combine + +/// Top level app services manager +/// +/// Moves top level app functionality management out of the app delegate +final class Services { + enum Service: CaseIterable { + case longCmdQ + case pasteboardHistory + case statusItem + case switcher + } + + // Service starting takes advantage of the fact that static members are initialized lazily + private static let longCmdQ = LongCmdQ() + private static let pasteboardHistory = PasteboardHistory() + private static let statusItem = StatusItem() + private static let switcher = Switcher() + + /// Start an app service + /// + /// Once a service has been started it cannot be stopped + static func start(_ service: Service? = nil) { + guard let service = service else { + for service in Service.allCases { + start(service) + } + return + } + + switch service { + case .longCmdQ: _ = longCmdQ + case .pasteboardHistory: _ = pasteboardHistory + case .statusItem: _ = statusItem + case .switcher: _ = switcher + } + } +} diff --git a/Switch/Switch/Services/StatusItem.swift b/Switch/Switch/Services/StatusItem.swift new file mode 100644 index 00000000..46394246 --- /dev/null +++ b/Switch/Switch/Services/StatusItem.swift @@ -0,0 +1,87 @@ +import Cocoa +import Defaults + +class StatusItem: NSObject, NSMenuDelegate { + var statusItem: NSStatusItem? + var debugItems = Set() + + override init() { + super.init() + Defaults.observe(.showStatusItem) { [weak self] change in + guard let self = self else { return } + if change.newValue && self.statusItem == nil { + self.addStatusItem() + } else if let statusItem = self.statusItem, !change.newValue { + statusItem.statusBar?.removeStatusItem(statusItem) + self.statusItem = nil + } + }.tieToLifetime(of: self) + } + + private func addStatusItem() { + let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) + statusItem.image = NSImage(named: "NSPrivateChaptersTemplate") //TODO(numist): deprecated?? Bundle.main.image(forResource: "weave") +// statusItem.highlightMode = true // TODO(numist): Deprecated?? +// statusItem.target = self // TODO(numist): Deprecated?? + +// NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Status Bar Menu"]; + let menu = NSMenu(title: "Status Bar Menu") // TODO(numist): how much of this is actually necessary anyway + menu.addItem( + NSMenuItem( + title: "Preferences…", + target: NSApplication.shared.delegate, + action: #selector(AppDelegate.showPreferences(sender:)), + keyEquivalent: "" + ) + ) + menu.addItem(NSMenuItem.separator()) + + do { + var debugItems = Set() + + var menuItem = NSMenuItem(title: "Hi there!") + menuItem.isEnabled = false + menu.addItem(menuItem) + debugItems.insert(menuItem) + + // initWithTitle:@"Take Snapshot…" action:NNSelfSelector1(snapshot:) + menuItem = NSMenuItem(title: "Take snapshot…") + menu.addItem(menuItem) + debugItems.insert(menuItem) + + // initWithTitle:@"Open Log Folder…" action:NNSelfSelector1(openLogFolder:) + menuItem = NSMenuItem(title: "Open Log Folder…") + menu.addItem(menuItem) + debugItems.insert(menuItem) + + menuItem = NSMenuItem.separator() + menu.addItem(menuItem) + debugItems.insert(menuItem) + + self.debugItems = debugItems + } + + menu.addItem( + NSMenuItem( + title: "Quit \(Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String)!)", + target: NSApplication.shared, + action: #selector(NSApplication.terminate(_:)) + ) + ) + + menu.delegate = self + + statusItem.menu = menu + + self.statusItem = statusItem + } + + // MARK: NSMenuDelegate + + @objc func menuNeedsUpdate(_ menu: NSMenu) { + let hideDebugItems = NSEvent.modifierFlags != .option + for item in debugItems { + item.isHidden = hideDebugItems + } + } +} diff --git a/Switch/Switch/Services/Switcher/README.md b/Switch/Switch/Services/Switcher/README.md new file mode 100644 index 00000000..69c6fcfd --- /dev/null +++ b/Switch/Switch/Services/Switcher/README.md @@ -0,0 +1,52 @@ +# Window Switching + +The files in this directory make up the window switcher service. + +``` graphviz +digraph { + concentrate=true + Switcher -> SwitcherState [arrowhead="tee"] + SwitcherState -> WindowInfoGroup [arrowhead="invodot"] + WindowInfoGroup -> WindowInfo [arrowhead="inv"] + + Switcher -> SwitcherWindow [arrowhead="invodot"] + SwitcherWindow -> SwitcherView [arrowhead="tee"] + SwitcherView -> WindowView [arrowhead="invodot"] + + Switcher -> WindowInfoGroupListPublisher [arrowhead="odot"] +} + +``` + +## `Switcher` + +The `Switcher` type represents the service, and exists as connective tissue between a `SwitcherState` and its inputs and outputs: + +### State machine inputs + +* `Keyboard` hotkey callbacks +* an `EventTap` to monitor the release of the hotkey's modifier(s) +* window list updates posted by `WindowInfoGroupListPublisher` + +### State machine outputs + +* HAXcessibility for `raise` and `close` operations +* interface presentation, managed by one or more instances of `SwitcherWindow` + +## `SwitcherState` + +`SwitcherState` contains practically all conditional logic for the switcher, and is tested by `SwitcherStateTests` + +## `WindowInfo` + +This value type represents one `NSWindow` displayed on the screen, primarily mirroring information found in each dictionary returned by `CGWindowListCopyWindowInfo`, plus available metadata from `NSRunningApplication` and HAXcessibility. + +A list of the system's windows is returned by `get(onScreenOnly:)`. The string returned by `description` is valid Swift source code invoking `init(:)`, for creating an identical instance in unit tests. + +## `WindowInfoGroup` + +Some visible windows on the system are represented by more than one `NSWindow`. For example, the open/save panel is often a separate window presented by the system on top of the application's window. To account for this system idiom, this value type represents a logical group of windows by storing a "main" `WindowInfo` along with an ordered list (by z-height) of all related `WindowInfo` instances. + +Unfortunately window groups can not be determined via public API, so `list(from:)` uses some heuristics to convert a `[WindowInfo]` into a reasonable `[WindowInfoGroup]`, but the heuristics are imperfect and a sufficiently rich source of issues that [the "quirk" label](https://github.com/numist/Switch/issues?q=is%3Aissue+label%3Aquirk) was created to track them. + +The corpus of window grouping tests lives in `WindowInfoGroupTests/`, but a lot of porting from Switch 0.0.10 (β) is still required. diff --git a/Switch/Switch/Services/Switcher/Structure.monopic b/Switch/Switch/Services/Switcher/Structure.monopic new file mode 100644 index 00000000..0d68248d Binary files /dev/null and b/Switch/Switch/Services/Switcher/Structure.monopic differ diff --git a/Switch/Switch/Services/Switcher/Switcher.swift b/Switch/Switch/Services/Switcher/Switcher.swift new file mode 100644 index 00000000..5103d247 --- /dev/null +++ b/Switch/Switch/Services/Switcher/Switcher.swift @@ -0,0 +1,172 @@ +import Foundation +import Haxcessibility +import Combine + +class Switcher { + let forwardHotKey = Keyboard.HotKey(.option, .tab) + let reverseHotKey = Keyboard.HotKey([.option, .shift], .tab) + let closeHotKey = Keyboard.HotKey(.option, .w) + let cancelHotKey = Keyboard.HotKey(.option, .escape) + + private var commonModifiers: Keyboard.Modifiers { + forwardHotKey.modifiers + .intersection(reverseHotKey.modifiers) + .intersection(closeHotKey.modifiers) + .intersection(cancelHotKey.modifiers) + } + + private var state: SwitcherState! + private var releaseTap: EventTap? + + private func setUpReleaseTapIfNeeded() -> Bool { + assert(Thread.isMainThread) + guard releaseTap == nil else { return true } + + releaseTap = try? EventTap(observing: .flagsChanged, callback: { [weak self] (_, event) -> CGEvent? in + guard let modifiers = self?.commonModifiers else { return event } + if event.flags.isEmpty || !Keyboard.Modifiers(event.flags).isSuperset(of: modifiers) { + DispatchQueue.main.async { + guard let self = self, self.releaseTap != nil else { return } + self.state.hotKeyReleased() + self.cleanUp() + } + } + return event + }) + + // Do not engage the state machine if the releaseTap can not be created + guard releaseTap != nil else { return false } + + Keyboard.register(closeHotKey) { [weak self] keyDown -> Bool in + guard let self = self else { return true } + if keyDown { DispatchQueue.main.async { + if let selectedWindow = self.state.selectedWindow { + print("Switcher: closing \(selectedWindow)") + HAXApplication(pid: selectedWindow.ownerPID)?.window(withID: selectedWindow.id)?.close() + } + } } + return false + } + Keyboard.register(cancelHotKey) { [weak self] keyDown -> Bool in + guard let self = self else { return true } + if keyDown { DispatchQueue.main.async { + self.state.hotKeyReleased(cancel: true) + } } + return false + } + + return true + } + + init() { + state = SwitcherState( + wantsTimerCallback: { [weak self] in self?.startTimer() }, + wantsTimerCancelledCallback: { [weak self] in self?.stopTimer() }, + wantsShowInterfaceCallback: { [weak self] in self?.showInterface() }, + wantsHideInterfaceCallback: { [weak self] in self?.hideInterface() }, + wantsStartWindowListUpdates: { [weak self] in self?.startWindowListPoller() }, + wantsStopWindowListUpdates: { [weak self] in self?.stopWindowListPoller() }, + wantsRaiseCallback: { windowGroup in + print("Switcher: raising \(windowGroup)") + HAXApplication(pid: windowGroup.ownerPID)?.window(withID: windowGroup.id)?.raise() + } + ) + + Keyboard.register(forwardHotKey) { [weak self] keyDown -> Bool in + guard let self = self else { return true } + if keyDown { DispatchQueue.main.async { + if self.setUpReleaseTapIfNeeded() { + self.state.incrementSelection() + } + } } + return false + } + Keyboard.register(reverseHotKey) { [weak self] keyDown -> Bool in + guard let self = self else { return true } + if keyDown { DispatchQueue.main.async { + if self.setUpReleaseTapIfNeeded() { + self.state.decrementSelection() + } + } } + return false + } + } + + private func cleanUp() { + assert(Thread.isMainThread) + if releaseTap != nil { + Keyboard.deregister(closeHotKey) + Keyboard.deregister(cancelHotKey) + releaseTap = nil + } + } + + deinit { + cleanUp() + Keyboard.deregister(forwardHotKey) + Keyboard.deregister(reverseHotKey) + } + + // MARK: - Timer management + + private var timer: Timer? + + private func startTimer() { + assert(Thread.isMainThread) + assert(self.timer == nil) + + self.timer = Timer.scheduledTimer( + withTimeInterval: 0.2, + repeats: false, + block: { [weak self] timer in + guard let self = self else { return } + guard self.timer == timer else { return } + self.state.timerFired() + self.timer = nil + } + ) + } + + private func stopTimer() { + assert(Thread.isMainThread) + assert(self.timer != nil) + + timer?.invalidate() + timer = nil + } + + // MARK: - Interface management + + private var window: SwitcherWindow? + + private func showInterface() { + assert(Thread.isMainThread) + window = SwitcherWindow(displaying: state, for: nil) + } + + private func hideInterface() { + assert(Thread.isMainThread) + window = nil + } + + // MARK: - Window list polling + + private var windowListCancellable: AnyCancellable? + + private func startWindowListPoller() { + assert(Thread.isMainThread) + windowListCancellable = WindowInfoListPublisher.shared + .removeDuplicates() + .map({ WindowInfoGroup.list(from: $0) }) + .sink { [weak self] list in + assert(Thread.isMainThread) + guard let self = self, self.windowListCancellable != nil else { return } + self.state.update(windows: list) + } + } + + private func stopWindowListPoller() { + assert(Thread.isMainThread) + windowListCancellable = nil + } +} diff --git a/Switch/Switch/Services/Switcher/SwitcherState.swift b/Switch/Switch/Services/Switcher/SwitcherState.swift new file mode 100644 index 00000000..4938f618 --- /dev/null +++ b/Switch/Switch/Services/Switcher/SwitcherState.swift @@ -0,0 +1,299 @@ +import SwiftUI + +/// Logic core responsible for powering switcher interface and window management behaviour. +/// +/// Ignoring transitions from one state to itself, the core functionality of the state machine is to implement the +/// following DFA: +/// +/// ``` +/// ╔════╗ {in,de}crementSelection() ┌─────────────────────────────┐ +/// ║Idle╠─────────────────────────────▶│Active (awaiting window list)│ +/// ╚════╝ →wantsStartWindowListUpdates └──────────────┬──────────────┘ +/// ▲ │ +/// │→wantsStopWindowListUpdates ┌────┴────────────┐ +/// │→wantsRaise (if needed) update(windows:)│ │hotKeyReleased() +/// │ ▼ ▼ +/// │ ┌──────┐ ┌──────────────────────┐ +/// ├─────────────────────────────────────────┤Active│ │Active (pending raise)│ +/// │ hotKeyReleased() └──────┘ └───────────┬──────────┘ +/// │ │ +/// └───────────────────────────────────────────────────────────────┘ +/// update(windows:) +/// ``` +/// +/// Mixed in with the above is logic for controlling the display of the switcher interface (again, ignoring +/// self-transitions): +/// +/// ``` +/// ╔════╗ +/// ┌────────────▶║Idle║◀──────────────────────────────┐ +/// →wantsTimerCancelled│ ╚══╦═╝ │ +/// │ │{in,de}crementSelection() │ +/// hotKeyReleased()│ │ │ +/// │ ▼→wantsTimer callback │ +/// ├─────────────────────────────────┐ │ +/// │Active (awaiting: timer, windows)│ │ +/// └─┬─────────────────────────────┬─┘ │→wantsHideInterface +/// timerFired()▼ ▼update(windows:) │ +/// ┌──────────────────────────┐ ┌────────────────────────┐ │hotKeyReleased() +/// │Active (awaiting: windows)│ │Active (awaiting: timer)│ │ +/// └─────────────┬────────────┘ └───────────┬────────────┘ │ +/// update(windows:)└──────────────┬─────────────┘timerFired() │ +/// │ │ +/// ▼→wantsShowInterface │ +/// ┌──────┐ │ +/// │Active│──────────────────────────────┘ +/// └──────┘ +/// ``` +class SwitcherState: ObservableObject { + // MARK: - Ins/outs + + private let wantsTimerCallback: () -> Void + private let wantsTimerCancelledCallback: () -> Void + private let wantsShowInterfaceCallback: () -> Void + private let wantsHideInterfaceCallback: () -> Void + private let wantsStartWindowListUpdates: () -> Void + private let wantsStopWindowListUpdates: () -> Void + private let wantsRaiseCallback: (WindowInfoGroup) -> Void + + /// Creates a new SwitcherState instance with configured callbacks + /// - Parameters: + /// - wantsTimerCallback: Invoked when the state machine wants to perform an action after a delay + /// - wantsTimerCancelledCallback: Invoked when the state machine wants to cancel the issued timer + /// - wantsShowInterfaceCallback: Invoked when the owner should present the switching interface + /// - wantsHideInterfaceCallback: Invoked when the owner should hide the switching interface + /// - wantsStartWindowListUpdates: Invoked when the state machine wishes to recieve window list updates + /// - wantsStopWindowListUpdates: Invoked when the state machine no longer desires window list updates + /// - wantsRaiseCallback: Invoked when the owner should raise the window passed to the closure + init( + wantsTimerCallback: @escaping () -> Void = {}, + wantsTimerCancelledCallback: @escaping () -> Void = {}, + wantsShowInterfaceCallback: @escaping () -> Void = {}, + wantsHideInterfaceCallback: @escaping () -> Void = {}, + wantsStartWindowListUpdates: @escaping () -> Void = {}, + wantsStopWindowListUpdates: @escaping () -> Void = {}, + wantsRaiseCallback: @escaping (WindowInfoGroup) -> Void = {_ in} + ) { + self.wantsTimerCallback = wantsTimerCallback + self.wantsTimerCancelledCallback = wantsTimerCancelledCallback + self.wantsShowInterfaceCallback = wantsShowInterfaceCallback + self.wantsHideInterfaceCallback = wantsHideInterfaceCallback + self.wantsStartWindowListUpdates = wantsStartWindowListUpdates + self.wantsStopWindowListUpdates = wantsStopWindowListUpdates + self.wantsRaiseCallback = wantsRaiseCallback + } + + /// Call this function after the timer requested by this state machine has fired + func timerFired() { + assert(_wantsTimer) + assert(!_timerFired) + _timerFired = true + _wantsTimer = false + showInterfaceIfReady() + } + + private var _active = false + + /// Call this function when the hotkey modifier driving the interaction has been released + func hotKeyReleased(cancel: Bool = false) { + guard _active else { return } + + if cancel { + _deactivate() + } else { + // If hotkey is released before presenting interface, don't bother + if _wantsTimer { + wantsTimerCancelled() + } + _wantsRaiseOnWindowUpdate = true + _raiseIfReady() + } + } + + func setSelection(to window: WindowInfoGroup) { + setSelection(to: windows.firstIndex(of: window)!) + } + + func setSelection(to index: Int) { + guard !_wantsRaiseOnWindowUpdate else { return } + + _active = true + if !_wantsTimer && !_timerFired { + wantsTimer() + } + solicitWindowUpdatesIfNeeded() + _updateSelection(to: index) + } + + /// Call this function when the forward hotkey has been pressed + func incrementSelection(by amt: Int = 1) { + setSelection(to: _selection + amt) + } + + /// Call this function when the reverse hotkey has been pressed + func decrementSelection(by amt: Int = 1) { + incrementSelection(by: -amt) + } + + /// Call this function when the state machine has requested window list updates and a new list is available + func update(windows list: [WindowInfoGroup]) { + assert(_wantsWindowUpdates) + guard _hasUpdatedWindows else { + // On first window update, bump the selection back if the first window is not active + // This way, a quick opt-tab will increment 1, bump back, and activate the topmost window + if _selection > 0 && !(list.first?.mainWindow.isAppActive ?? true) { + _selection -= 1 + } + + windows = list + _hasUpdatedWindows = true + _updateSelection(to: _selection) + showInterfaceIfReady() + _raiseIfReady() + return + } + + if !windows.isEmpty && !list.isEmpty { + if let selectedWindowInList = list.map({ $0.mainWindow.id }).firstIndex(of: windows[_selection].mainWindow.id) { + // If possible, maintain selection on the same window + _selection = selectedWindowInList + } else { + // Otherwise, clamp selection to size of new list + _selection = min(_selection, list.count - 1) + } + } else if list.isEmpty { + _selection = -1 + } else if windows.isEmpty { + _selection = 0 + } + windows = list + showInterfaceIfReady() + _raiseIfReady() + } + + // MARK: - Window list update logic + private var _hasUpdatedWindows = false + private var _wantsWindowUpdates = false + private func solicitWindowUpdatesIfNeeded() { + guard !_wantsWindowUpdates else { return } + _wantsWindowUpdates = true + wantsStartWindowListUpdates() + } + + private func stopWindowUpdates() { + assert(_wantsWindowUpdates) + _wantsWindowUpdates = false + wantsStopWindowListUpdates() + } + + // MARK: - Publicly read-only properties + + /// The window list from the last call to `update(windows:)` + @Published private(set) var windows = [WindowInfoGroup]() + + /// The index of the selected window in `windows`, or `nil` if `windows.isEmpty` + /// + /// This property returns `nil` until the first `update(windows:)` after `→wantsStartWindowListUpdates` + var selection: Int? { + guard _hasUpdatedWindows else { + return nil + } + guard !windows.isEmpty else { + assert(_selection == -1) + return nil + } + assert(_hasUpdatedWindows) + assert(_selection >= 0 && _selection < windows.count) + return _selection % windows.count + } + @Published private var _selection = 0 + + /// The selected window group, if any + /// + /// This property returns `nil` until the first `update(windows:)` after `→wantsStartWindowListUpdates` + var selectedWindow: WindowInfoGroup? { + guard let selection = selection else { return nil } + return windows[selection] + } + + // MARK: - Timer management + private var _timerFired = false + private var _wantsTimer = false + private func wantsTimer() { + assert(!_timerFired) + _wantsTimer = true + wantsTimerCallback() + } + private func wantsTimerCancelled() { + wantsTimerCancelledCallback() + _wantsTimer = false + } + + // MARK: - Interface management + private var _showingInterface = false + private func showInterfaceIfReady() { + guard !_showingInterface else { return } + guard _hasUpdatedWindows else { return } + guard _timerFired else { + assert(_wantsTimer || _wantsRaiseOnWindowUpdate) + return + } + wantsShowInterfaceCallback() + _showingInterface = true + } + private func hideInterface() { + assert(_showingInterface) + wantsHideInterfaceCallback() + _showingInterface = false + } + + // MARK: - Raising logic + private var _wantsRaiseOnWindowUpdate = false + private func _windowToRaise() -> WindowInfoGroup? { + if let selection = selection, selection != 0 || !windows[selection].mainWindow.isAppActive { + return windows[selection] + } + return nil + } + private func _raiseIfReady() { + if _wantsRaiseOnWindowUpdate && _hasUpdatedWindows { + if let window = _windowToRaise() { + wantsRaiseCallback(window) + } + _deactivate() + } + } + + // MARK: - Private + + private func _deactivate() { + if _showingInterface { + hideInterface() + } + if _wantsTimer { + wantsTimerCancelled() + } + stopWindowUpdates() + _selection = 0 + _hasUpdatedWindows = false + _wantsRaiseOnWindowUpdate = false + _timerFired = false + _active = false + } + + private func _updateSelection(to index: Int) { + if _hasUpdatedWindows { + if windows.isEmpty { + _selection = -1 + } else { + _selection = index % windows.count + if _selection < 0 { + _selection += windows.count + } + assert(_selection >= 0 && _selection < windows.count) + } + } else { + _selection = index + } + } +} diff --git a/Switch/Switch/Services/Switcher/SwitcherView.swift b/Switch/Switch/Services/Switcher/SwitcherView.swift new file mode 100644 index 00000000..80533b6c --- /dev/null +++ b/Switch/Switch/Services/Switcher/SwitcherView.swift @@ -0,0 +1,191 @@ +import SwiftUI + +/* Full scale dimensions for SwitcherView: + * + * 8+4+8 4 8 8 128(²) + * │◀────▶│ │↔│ │↔│↔│◀──────▶│ + * └ ┐ ┌ ┘ └─┘ | | | | + * ┌─────────────┼──────────┼─┼─┼────────┼───────────────┬ ─ ─ ─ ─ + * │ │ ┌────────────┐ │ ▲ + * │ ┌────────┐ │ ┌────────┤ │ ├────────┤ ┌────────┐ │ │ + * │ │ │ │ │ │ │ │ │ │ │ │ │ + * │ │ │ │ │ │ │ │ │ │ │ │ │128+8×4+4×2 + * │ │ .─.│ │ │ .─.│ │ │ .─.│ │ .─.├ ─│─ │= 168 + * │ │ ( ) │ │ ( ) │ │ ( ) │ ( ) │↕64(²) │ + * │ └─────`─'┘ │ └─────`─'┘ │ └─────`─'┘ └─────`─'┴ ─│─ │ + * │ └────────────┘ │ ▼ + * └─────────────────────────────────────────────────────┴ ─ ─ ─ ─ + * │◀───────────────────────────────────────────────────▶│ + * 128×n + 4×(n+1) + 8×((n+1)*2) + * + * window radius: 20 + * selection radius: 20(window radius) - 8(inset) + */ + +private let pThumbSz = CGFloat(128.0) +private let pThumbPad = CGFloat(8.0) +private let pSelThck = CGFloat(4.0) +private let pSelPad = CGFloat(8.0) +private let pHeight = (pThumbPad + pSelThck + pSelPad) * 2.0 + pThumbSz +private let pWinRadius = CGFloat(20.0) +private let pSelRadius = pWinRadius - pSelPad +private let pItemSz = pThumbSz + pThumbPad + pSelThck + pSelPad +private func pWidth(for count: Int) -> CGFloat { + let cgn = CGFloat(max(count, 1)) + return pThumbSz * cgn + (pThumbPad + pSelThck + pSelPad) * (cgn + CGFloat(1.0)) +} +private func scalingFactor(for count: Int, in geometry: GeometryProxy) -> CGFloat { + min( + geometry.size.width / pWidth(for: count), + geometry.size.height / pHeight, + 1.0 + ) +} + +struct SwitcherView: View { + + @ObservedObject var state: SwitcherState + @State private var hoverLocation: CGPoint = .zero + let displayID: CGDirectDisplayID? + + init(with state: SwitcherState, on displayID: CGDirectDisplayID? = nil) { + assert(displayID == nil) // TODO(numist): multimon support + self.state = state + self.displayID = displayID + } + + private func cellSize(given geometry: GeometryProxy) -> CGFloat { + return min(geometry.size.width / CGFloat(state.windows.count), geometry.size.height) + } + + private func middleIndex(for index: Int) -> CGFloat { + return (CGFloat(index) - (CGFloat(state.windows.count - 1) / 2.0)) + } + + private func hud(at scale: CGFloat) -> some View { + Rectangle() + .fill(Color(NSColor.shadowColor).opacity(0.25)) + .cornerRadius(pWinRadius * scale) + .frame( + width: pWidth(for: state.windows.count) * scale, + height: pHeight * scale + ) + .animation(.default, value: state.windows) + } + + private func selectionBox(at scale: CGFloat) -> some View { + ZStack { // can't fill *and* stroke a Shape! + RoundedRectangle(cornerRadius: pSelRadius * scale) + .fill(Color(NSColor.shadowColor).opacity(0.5)) + .frame( + width: (pThumbSz + pThumbPad + pThumbPad) * scale, + height: (pThumbSz + pThumbPad + pThumbPad) * scale + ) + RoundedRectangle(cornerRadius: pSelRadius * scale) + .stroke(Color.white.opacity(0.7), lineWidth: pSelThck * scale) + .frame( + width: (pThumbSz + pThumbPad + pThumbPad + pSelThck) * scale, + height: (pThumbSz + pThumbPad + pThumbPad + pSelThck) * scale + ) + } + .offset( + x: middleIndex(for: state.selection!) * scale * pItemSz + ) + .animation(.easeInOut.speed(2), value: state.selection) + } + + var body: some View { + GeometryReader { geometry in + let scale = scalingFactor(for: state.windows.count, in: geometry) + ZStack { + // HUD/background + hud(at: scale) + + // Selection frame + if state.selection != nil { + selectionBox(at: scale) + } + + // Window list + ForEach(Array(state.windows.enumerated()), id: \.element) { index, window in + WindowView(window: window) + .onContinuousHover { phase in + // The selection state should only change when the mouse moves within a view + // This is important in case the Switcher happens to get invoked + // while the cursor is idling within the view's bounds + switch phase { + case .active(let location): + if hoverLocation != .zero && state.selection != index { + state.setSelection(to: window) + } + hoverLocation = location + case .ended: + hoverLocation = .zero + } + } + .frame( + width: pThumbSz * scale, + height: pThumbSz * scale + ) + .offset( + x: middleIndex(for: index) * scale * + (pThumbSz + pThumbPad + pSelThck + pSelPad) + ) + .animation(.default.speed(2), value: state.windows) + } + } + .position( + x: geometry.size.width / 2, + y: geometry.size.height / 2 + ) + } + } +} + +struct SwitcherViewPreviews: PreviewProvider { + + static var previews: some View { + let packedState = SwitcherState() + packedState.incrementSelection(by: 3) + packedState.update(windows: WindowInfoGroup.list(from: [ + // swiftlint:disable line_length + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.dt.Xcode", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(510), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(1426), .cgOwnerName: "System Preferences", .cgName: "Security & Privacy", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), .isFullscreen: false, .ownerBundleID: "com.apple.systempreferences", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(10053), .cgLayer: Int32(0), .cgBounds: CGRect(x: 61.0, y: 23.0, width: 1369.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(16649), .cgOwnerName: "TextMate", .cgName: "untitled 12", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 61.0, y: 0.0, width: 1369.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.macromates.TextMate", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(2179), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(3342), .cgOwnerName: "Safari", .cgName: "Fucking NSImage Syntax", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.Safari", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(5269), .cgLayer: Int32(0), .cgBounds: CGRect(x: 105.0, y: 67.0, width: 788.0, height: 781.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(16649), .cgOwnerName: "TextMate", .cgName: "untitled 11", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 105.0, y: 52.0, width: 788.0, height: 781.0), .isFullscreen: false, .ownerBundleID: "net.numist.Switch", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(79), .cgLayer: Int32(0), .cgBounds: CGRect(x: 168.0, y: 24.0, width: 1120.0, height: 839.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(424), .cgOwnerName: "Terminal", .cgName: "Switch — fish /Users/numist/Code/Switch — -fish — 185×56", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 168.0, y: 37.0, width: 1120.0, height: 839.0), .isFullscreen: false, .ownerBundleID: "com.apple.Terminal", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(9737), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(3342), .cgOwnerName: "Safari", .cgName: "swift - SwiftUI and MVVM - Communication between model and view model - Stack Overflow", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.example.unknowable", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(8336), .cgLayer: Int32(0), .cgBounds: CGRect(x: 72.0, y: 67.0, width: 1368.0, height: 757.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(30660), .cgOwnerName: "Monodraw", .cgName: "Untitled", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 72.0, y: 76.0, width: 1368.0, height: 757.0), .isFullscreen: false, .ownerBundleID: "com.helftone.monodraw", .canActivate: true, .isAppActive: false]), + // swiftlint:enable line_length + ])) + + let fullScaleState = SwitcherState() + fullScaleState.incrementSelection(by: 0) + fullScaleState.update(windows: WindowInfoGroup.list(from: [ + // swiftlint:disable line_length + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.dt.Xcode", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(510), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(1426), .cgOwnerName: "System Preferences", .cgName: "Security & Privacy", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), .isFullscreen: false, .ownerBundleID: "com.apple.systempreferences", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(10053), .cgLayer: Int32(0), .cgBounds: CGRect(x: 61.0, y: 23.0, width: 1369.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(16649), .cgOwnerName: "TextMate", .cgName: "untitled 12", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 61.0, y: 0.0, width: 1369.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.macromates.TextMate", .canActivate: true, .isAppActive: false]), + // swiftlint:enable line_length + ])) + + let emptyState = SwitcherState() + emptyState.incrementSelection() + emptyState.update(windows: []) + + return Group { + SwitcherView(with: packedState) + .frame(width: 588.0, height: 128.0) + .background(previewBackground()) + + SwitcherView(with: fullScaleState) + .frame(width: 588.0, height: 200.0) + .background(previewBackground()) + + SwitcherView(with: emptyState) + .frame(width: 588.0, height: 200.0) + .background(previewBackground()) + } + } +} diff --git a/Switch/Switch/Services/Switcher/SwitcherWindow.swift b/Switch/Switch/Services/Switcher/SwitcherWindow.swift new file mode 100644 index 00000000..fa6e29a4 --- /dev/null +++ b/Switch/Switch/Services/Switcher/SwitcherWindow.swift @@ -0,0 +1,42 @@ +import Cocoa +import SwiftUI + +class SwitcherWindow { + let window: NSWindow! + let screen: NSScreen? + + init(displaying state: SwitcherState, for screen: NSScreen?) { + let displayScreen = screen ?? NSScreen.main! + self.screen = screen + + window = NSWindow( + contentRect: displayScreen.frame, + styleMask: [.borderless, .fullSizeContentView], + backing: .buffered, + defer: false, + screen: displayScreen + ) + window.isMovableByWindowBackground = false + window.hasShadow = false + window.isOpaque = false + window.backgroundColor = NSColor.clear + window.ignoresMouseEvents = false + window.level = .popUpMenu + window.setFrameOrigin(NSPoint(x: 0, y: 0)) + + window.contentView = NSHostingView(rootView: SwitcherView(with: state, on: screen?.screenNumber)) + + window.makeKeyAndOrderFront(nil) + } + + deinit { + let window = self.window! + NSAnimationContext.runAnimationGroup({ context in + context.duration = 0.25 + context.timingFunction = CAMediaTimingFunction(name: .default) + window.animator().alphaValue = 0.0 + }, completionHandler: { + window.orderOut(nil) + }) + } +} diff --git a/Switch/Switch/Services/Switcher/WindowInfo.swift b/Switch/Switch/Services/Switcher/WindowInfo.swift new file mode 100644 index 00000000..c7f96f2d --- /dev/null +++ b/Switch/Switch/Services/Switcher/WindowInfo.swift @@ -0,0 +1,156 @@ +import Foundation +import CoreGraphics +import Haxcessibility + +// Work around the CFString casts and other nonsense by declaring all the keys used in the infoDict passed to WindowInfo +enum WindowInfoDictionaryKey: String { + // https://developer.apple.com/documentation/coregraphics/quartz_window_services/required_window_list_keys + case cgNumber = "kCGWindowNumber" +// case cgStoreType = "kCGWindowStoreType" + case cgLayer = "kCGWindowLayer" + case cgBounds = "kCGWindowBounds" +// case cgSharingState = "kCGWindowSharingState" + case cgAlpha = "kCGWindowAlpha" + case cgOwnerPID = "kCGWindowOwnerPID" +// case cgMemoryUsage = "kCGWindowMemoryUsage" + + // https://developer.apple.com/documentation/coregraphics/quartz_window_services/optional_window_list_keys + case cgOwnerName = "kCGWindowOwnerName" + case cgName = "kCGWindowName" + case cgIsOnscreen = "kCGWindowIsOnscreen" +// case cgBackingLocationVideoMemory = "kCGWindowBackingLocationVideoMemory" + + // https://developer.apple.com/documentation/appkit/nsscreen/1388360-devicedescription + case cgDisplayID = "NSScreenNumber" + + // Custom keys filled by AX + case nsFrame = "NSFrame" + case isFullscreen = "IsFullscreen" + + // Custom keys filled by NSRunningApplication + case canActivate = "FriendlyActivationPolicy" + case isAppActive = "AppIsActive" + case ownerBundleID = "BundleID" +} + +// swiftlint:disable force_cast + +struct WindowInfo { + let id: CGWindowID // swiftlint:disable:this identifier_name + let cgFrame: CGRect + let alpha: Float + let ownerPID: pid_t + + let ownerName: String? + let name: String? + let isOnScreen: Bool? + + let cgDisplayID: CGDirectDisplayID? + let nsFrame: NSRect? + let isFullscreen: Bool? + + let canActivate: Bool + let isAppActive: Bool + let ownerBundleID: String? + + init(_ infoDict: [WindowInfoDictionaryKey: Any]) { + id = infoDict[.cgNumber] as! CGWindowID + assert(infoDict[.cgLayer] as! CGWindowLevel == kCGNormalWindowLevel) + cgFrame = CGRect(dictionaryRepresentation: infoDict[.cgBounds] as! CFDictionary)! + alpha = infoDict[.cgAlpha] as! Float + ownerPID = infoDict[.cgOwnerPID] as! Int32 + + ownerName = infoDict[.cgOwnerName] as? String + name = infoDict[.cgName] as? String + isOnScreen = infoDict[.cgIsOnscreen] as? Bool + + cgDisplayID = infoDict[.cgDisplayID] as? CGDirectDisplayID + nsFrame = infoDict[.nsFrame] as? NSRect + isFullscreen = infoDict[.isFullscreen] as? Bool + + canActivate = infoDict[.canActivate] as? Bool ?? false + /* Usually true for first window in list, usually false for subsequents. This value should never be missing for + * main windows, but the default value is chosen to provide the best state machine behaviour (and least crashing) + * if it's ever missing. + */ + isAppActive = infoDict[.isAppActive] as? Bool ?? true + ownerBundleID = infoDict[.ownerBundleID] as? String + } +} + +extension WindowInfo { + static func get(onScreenOnly: Bool = true) -> [WindowInfo] { + var options = CGWindowListOption.excludeDesktopElements + if onScreenOnly { options.insert(.optionOnScreenOnly) } + return stopwatch("CGWindowListCopyWindowInfo", threshold: 0.25) { + (CGWindowListCopyWindowInfo(options, kCGNullWindowID) as! [[String: Any]]) + .filter { $0[WindowInfoDictionaryKey.cgLayer.rawValue] as! CGWindowLevel == kCGNormalWindowLevel } + .map { Dictionary(uniqueKeysWithValues: + $0.compactMap({ (key, value) -> (WindowInfoDictionaryKey, Any)? in + guard let newKey = WindowInfoDictionaryKey(rawValue: key) else { return nil } + return (newKey, value) + })) + } + .map { infoDict in + let windowID = infoDict[.cgNumber] as! CGWindowID + let processID = infoDict[.cgOwnerPID] as! Int32 + + // Add extra keys from NSRunningApplication to the info dict + var additionalInfo = [WindowInfoDictionaryKey: Any]() + if let runningApp = NSRunningApplication(processIdentifier: processID) { + additionalInfo[.canActivate] = (runningApp.activationPolicy != .prohibited) + additionalInfo[.isAppActive] = runningApp.isActive + additionalInfo[.ownerBundleID] = runningApp.bundleIdentifier + } + + // Add extra keys from HAXcessibility to the info dict + if let haxWindow = HAXApplication(pid: processID)?.window(withID: windowID) { + additionalInfo[.nsFrame] = haxWindow.frame + additionalInfo[.isFullscreen] = haxWindow.isFullscreen + if let title = haxWindow.title { + additionalInfo[.cgName] = title + } + if let screenNumber = haxWindow.screen?.screenNumber { + additionalInfo[.cgDisplayID] = screenNumber + } + } + + return WindowInfo(infoDict.merging(additionalInfo, uniquingKeysWith: { $1 })) + } + } + } +} + +// MARK: Conformances + +extension WindowInfo: Identifiable, Hashable {} + +extension WindowInfo: CustomStringConvertible { + // swiftlint:disable line_length + var description: String { + var result = """ + WindowInfo([ + .cgNumber: UInt32(\(id)), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: \(cgFrame.origin.x), y: \(cgFrame.origin.y), width: \(cgFrame.size.width), height: \(cgFrame.size.height)).dictionaryRepresentation, + .cgAlpha: Float(\(alpha)), + .cgOwnerPID: Int32(\(ownerPID)), + + """ + if let ownerName = ownerName { result += " .cgOwnerName: \"\(ownerName)\",\n" } + if let name = name { result += " .cgName: \"\(name)\",\n" } + if let isOnScreen = isOnScreen { result += " .cgIsOnscreen: \(isOnScreen),\n" } + if let cgDisplayID = cgDisplayID { result += " .cgDisplayID: UInt32(\(cgDisplayID)),\n" } + if let frm = nsFrame { + result += " .nsFrame: NSRect(x: \(frm.origin.x), y: \(frm.origin.y), width: \(frm.size.width), height: \(frm.size.height)),\n" + } + if let isFullscreen = isFullscreen { result += " .isFullscreen: \(isFullscreen),\n" } + if let bundleID = ownerBundleID { result += " .ownerBundleID: \"\(bundleID)\",\n" } + return result + """ + .canActivate: \(canActivate), + .isAppActive: \(isAppActive), + ]) + """ + } + // swiftlint:enable line_length +} diff --git a/Switch/Switch/Services/Switcher/WindowInfoGroup.swift b/Switch/Switch/Services/Switcher/WindowInfoGroup.swift new file mode 100644 index 00000000..65d9cbe4 --- /dev/null +++ b/Switch/Switch/Services/Switcher/WindowInfoGroup.swift @@ -0,0 +1,74 @@ +import Foundation +import CoreGraphics + +struct WindowInfoGroup: Hashable { + let mainWindow: WindowInfo + let windows: [WindowInfo] +} + +// MARK: Conformances + +extension WindowInfoGroup: Identifiable { + // swiftlint:disable:next identifier_name + var id: CGWindowID { return mainWindow.id } +} + +extension WindowInfoGroup: CustomStringConvertible { + var description: String { + "<\(mainWindow.id) (\(mainWindow.name ?? "")), \(windows.count) window\(windows.count == 1 ? "" : "s")>" + } +} + +// MARK: Computed properties + +extension WindowInfoGroup { + var ownerPID: pid_t { return mainWindow.ownerPID } + + var cgFrame: CGRect { + var minPoint = mainWindow.cgFrame.origin + var maxPoint = minPoint + for cgFrame in windows.map({ $0.cgFrame }) { + minPoint.x = min(minPoint.x, cgFrame.origin.x) + minPoint.y = min(minPoint.y, cgFrame.origin.y) + maxPoint.x = max(maxPoint.x, cgFrame.origin.x + cgFrame.size.width) + maxPoint.y = max(maxPoint.y, cgFrame.origin.y + cgFrame.size.height) + } + return CGRect( + origin: minPoint, + size: CGSize(width: maxPoint.x - minPoint.x, height: maxPoint.y - minPoint.y) + ) + } + + var nsFrame: NSRect? { + guard let mainFrame = mainWindow.nsFrame else { return nil } + var minPoint = mainFrame.origin + var maxPoint = minPoint + for nsFrame in windows.map({ $0.nsFrame }) { + guard let nsFrame = nsFrame else { return nil } + minPoint.x = min(minPoint.x, nsFrame.origin.x) + minPoint.y = min(minPoint.y, nsFrame.origin.y) + maxPoint.x = max(maxPoint.x, nsFrame.origin.x + nsFrame.size.width) + maxPoint.y = max(maxPoint.y, nsFrame.origin.y + nsFrame.size.height) + } + return NSRect( + origin: minPoint, + size: NSSize(width: maxPoint.x - minPoint.x, height: maxPoint.y - minPoint.y) + ) + } +} + +// MARK: Group inference + +extension WindowInfoGroup { + // TODO(numist): this is gonna get more sophisticated but let's get off the ground first + /* Heuristics: + * * window grouping is not accurate when title is not set + * * group non-activatable windows with nearest mainwindow candidate + * * …maybe just recreate/port tests from old source + */ + static func list(from infos: [WindowInfo]) -> [WindowInfoGroup] { + return infos + .filter({ $0.alpha != 0.0 && $0.canActivate && $0.name?.count ?? 0 > 0 }) + .map({ WindowInfoGroup(mainWindow: $0, windows: [$0]) }) + } +} diff --git a/Switch/Switch/Services/Switcher/WindowInfoListPublisher.swift b/Switch/Switch/Services/Switcher/WindowInfoListPublisher.swift new file mode 100644 index 00000000..be1a9d41 --- /dev/null +++ b/Switch/Switch/Services/Switcher/WindowInfoListPublisher.swift @@ -0,0 +1,66 @@ +import Combine +import Foundation + +final class WindowInfoListPublisher: Publisher { + static let shared = WindowInfoListPublisher() + + private typealias Subject = PassthroughSubject + + private let subject: Subject + private let counter: Publishers.SubscribersCounterPublisher + + private var timer: Timer? + private var workInProgress = false + + private init() { + assert(Thread.isMainThread) + subject = PassthroughSubject() + counter = Publishers.SubscribersCounterPublisher(upstream: subject) + } + + private func startPoll() { + assert(Thread.isMainThread) + + guard counter.hasSubscribers else { return } + guard !workInProgress else { return } + + timer?.invalidate() + timer = nil + + workInProgress = true + + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + let list = WindowInfo.get() + DispatchQueue.main.async { + self?.completePoll(with: list) + } + } + } + + private func completePoll(with result: [WindowInfo]) { + assert(Thread.isMainThread) + + self.workInProgress = false + + if self.counter.hasSubscribers { + // Schedule another iteration + self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false, block: { [weak self] _ in + guard let self = self else { return } + self.timer = nil + self.startPoll() + }) + + self.subject.send(result) + } + } + + // MARK: Publisher conformance + typealias Output = [WindowInfo] + typealias Failure = Never // TODO(numist): not strictly true if AX is disabled? + + func receive(subscriber: S) where S.Input == Output, S.Failure == Failure { + assert(Thread.isMainThread) + counter.receive(subscriber: subscriber) + startPoll() + } +} diff --git a/Switch/Switch/Services/Switcher/WindowView.swift b/Switch/Switch/Services/Switcher/WindowView.swift new file mode 100644 index 00000000..5b44742f --- /dev/null +++ b/Switch/Switch/Services/Switcher/WindowView.swift @@ -0,0 +1,90 @@ +import SwiftUI + +struct WindowView: View { + @State var window: WindowInfoGroup + // TODO(numist): @ObservableObject WindowContents(for: WindowInfoGroup) publishes nsImage, conveniences view + // Might wind up creating a Big Cache for these images to improve liveness + + func appIcon(for bundleID: String?, in size: CGSize) -> some View { + let workspace = NSWorkspace.shared + let nsImage: NSImage + if let bid = bundleID, let path = workspace.absolutePathForApplication(withBundleIdentifier: bid) { + nsImage = workspace.icon(forFile: path) + } else { + // TODO(numist): a default app icon doesn't seem to be available via API? copy a stand-in icns into the bundle + nsImage = NSImage(named: NSImage.applicationIconName)! + } + let scale = min( + size.width / nsImage.size.width, + size.height / nsImage.size.height + ) + nsImage.size = CGSize( + width: nsImage.size.width * scale, + height: nsImage.size.height * scale + ) + return Image(nsImage: nsImage) + } + + func windowContents(for window: WindowInfoGroup, in size: CGSize) -> some View { + let scale = min( + size.width / window.cgFrame.size.width, + size.height / window.cgFrame.size.height + ) + let width = window.cgFrame.size.width * scale + let height = window.cgFrame.size.height * scale + + return Rectangle() + .redacted(reason: .placeholder) + .frame(width: width, height: height) + } + + var body: some View { + GeometryReader { geometry in + ZStack { + windowContents( + for: window, + in: CGSize( + width: min(geometry.size.width, geometry.size.height), + height: min(geometry.size.width, geometry.size.height) + ) + ) + appIcon(for: window.mainWindow.ownerBundleID, in: geometry.size / 2.0) + .offset( + x: min(geometry.size.width, geometry.size.height) / 4, + y: min(geometry.size.width, geometry.size.height) / 4 + ) + }.frame( + width: min(geometry.size.width, geometry.size.height), + height: min(geometry.size.width, geometry.size.height) + ).offset( + x: (geometry.size.width - min(geometry.size.width, geometry.size.height)) / 2, + y: (geometry.size.height - min(geometry.size.width, geometry.size.height)) / 2) + } + } +} + +struct WindowViewPreview: PreviewProvider { + static var previews: some View { + // swiftlint:disable line_length + return Group { + WindowView(window: WindowInfoGroup.list(from: [ + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.dt.Xcode", .canActivate: true, .isAppActive: false]), + ]).first!) + .frame(width: 256, height: 256) + .background(previewBackground()) + + WindowView(window: WindowInfoGroup.list(from: [ + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 484.0, height: 168.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 484.0, height: 168.0), .isFullscreen: false, .ownerBundleID: "net.numist.Switch", .canActivate: true, .isAppActive: false]), + ]).first!) + .frame(width: 512, height: 128) + .background(previewBackground()) + + WindowView(window: WindowInfoGroup.list(from: [ + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 877.0, height: 1440.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 877.0, height: 1440.0), .isFullscreen: false, .ownerBundleID: "com.example.NoSuch", .canActivate: true, .isAppActive: false]), + ]).first!) + .frame(width: 128, height: 256) + .background(previewBackground()) + } + // swiftlint:enable line_length + } +} diff --git a/Switch/Switch/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json b/Switch/Switch/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Switch/Switch/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Switch/Switch/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json b/Switch/Switch/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..3f00db43 --- /dev/null +++ b/Switch/Switch/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Switch/Switch/Supporting Files/Assets.xcassets/Contents.json b/Switch/Switch/Supporting Files/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Switch/Switch/Supporting Files/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Switch/Switch/Supporting Files/Base.lproj/Main.storyboard b/Switch/Switch/Supporting Files/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f0fc7c18 --- /dev/null +++ b/Switch/Switch/Supporting Files/Base.lproj/Main.storyboard @@ -0,0 +1,691 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Switch/Switch/Supporting Files/Info.plist b/Switch/Switch/Supporting Files/Info.plist new file mode 100644 index 00000000..b17bed58 --- /dev/null +++ b/Switch/Switch/Supporting Files/Info.plist @@ -0,0 +1,46 @@ + + + + + SUAllowsAutomaticUpdates + + SUFeedURL + https://raw.github.com/numist/Switch/develop/appcast.xml + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 0.0.11 (α) + CFBundleVersion + 0.0.11 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + + NSAppleEventsUsageDescription + + NSHumanReadableCopyright + Copyright © 2013-2020 Scott Perry, MIT License + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + SUEnableAutomaticChecks + + SUPublicDSAKeyFile + Sparkle.dsa_pub.pem + SUShowReleaseNotes + + + diff --git a/Switch/Switch/Supporting Files/Preview Assets.xcassets/Contents.json b/Switch/Switch/Supporting Files/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Switch/Switch/Supporting Files/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Switch/Sparkle.dsa_pub.pem b/Switch/Switch/Supporting Files/Sparkle.dsa_pub.pem similarity index 100% rename from Switch/Sparkle.dsa_pub.pem rename to Switch/Switch/Supporting Files/Sparkle.dsa_pub.pem diff --git a/Switch/Switch.entitlements b/Switch/Switch/Supporting Files/Switch.entitlements similarity index 95% rename from Switch/Switch.entitlements rename to Switch/Switch/Supporting Files/Switch.entitlements index 852fa1a4..e89b7f32 100644 --- a/Switch/Switch.entitlements +++ b/Switch/Switch/Supporting Files/Switch.entitlements @@ -3,6 +3,6 @@ com.apple.security.app-sandbox - + diff --git a/Switch/SwitchTests/Collection+Extensions.swift b/Switch/SwitchTests/Collection+Extensions.swift new file mode 100644 index 00000000..0e12239a --- /dev/null +++ b/Switch/SwitchTests/Collection+Extensions.swift @@ -0,0 +1,6 @@ +extension Collection { + var second: Element? { + guard self.count > 1 else { return nil } + return self[self.index(after: self.startIndex)] + } +} diff --git a/Frameworks/NNKit/NNKitTests/NNKit Mac Tests-Info.plist b/Switch/SwitchTests/Info.plist similarity index 67% rename from Frameworks/NNKit/NNKitTests/NNKit Mac Tests-Info.plist rename to Switch/SwitchTests/Info.plist index d6837796..64d65ca4 100644 --- a/Frameworks/NNKit/NNKitTests/NNKit Mac Tests-Info.plist +++ b/Switch/SwitchTests/Info.plist @@ -3,19 +3,19 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable - ${EXECUTABLE_NAME} + $(EXECUTABLE_NAME) CFBundleIdentifier - net.numist.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleName + $(PRODUCT_NAME) CFBundlePackageType - BNDL + $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/Switch/SwitchTests/SwitcherState/SwitcherStateFuzzerTests.swift b/Switch/SwitchTests/SwitcherState/SwitcherStateFuzzerTests.swift new file mode 100644 index 00000000..c67ddc54 --- /dev/null +++ b/Switch/SwitchTests/SwitcherState/SwitcherStateFuzzerTests.swift @@ -0,0 +1,213 @@ +import XCTest +@testable import Switch + +let groups = WindowInfoGroup.list(from: [ + // swiftlint:disable line_length + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.dt.Xcode", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(510), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(1426), .cgOwnerName: "System Preferences", .cgName: "Security & Privacy", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), .isFullscreen: false, .ownerBundleID: "com.apple.systempreferences", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(10053), .cgLayer: Int32(0), .cgBounds: CGRect(x: 61.0, y: 23.0, width: 1369.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(16649), .cgOwnerName: "TextMate", .cgName: "untitled 12", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 61.0, y: 0.0, width: 1369.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.macromates.TextMate", .canActivate: true, .isAppActive: false]), + // swiftlint:enable line_length +]) + +class SwitcherStateFuzzerDerivedTests: XCTestCase { + func testFuzzIncrementAfterTimerFired() { + var wantsTimer = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true } + ) + + state.decrementSelection() + state.incrementSelection() + XCTAssertTrue(wantsTimer) + state.timerFired(); wantsTimer = false + state.incrementSelection() + } + + func testFuzzSomething() { + var wantsTimer = false + var wantsWindowUpdates = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true }, + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false } + ) + + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertTrue(wantsTimer) + state.timerFired(); wantsTimer = false + state.decrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.hotKeyReleased() + state.hotKeyReleased() + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + } + + func testFuzzIncrementAfterRaise() { + var wantsWindowUpdates = false + let state = SwitcherState( + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false } + ) + + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.hotKeyReleased() + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + state.hotKeyReleased() + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + } + + func testFuzzNoMoreWindows() { + var wantsWindowUpdates = false + let state = SwitcherState( + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true } + ) + + state.incrementSelection() + // wantsTimer → true + // wantsWindows → true + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: [groups[0], groups[2]]) + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertNil(state.selection) + } + + func testFuzzAddingWindows() { + var wantsWindowUpdates = false + let state = SwitcherState( + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true } + ) + + state.incrementSelection() + // wantsTimer → true + // wantsWindows → true + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: [groups[0], groups[2]]) + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: Array(groups.dropLast())) + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: Array(groups.dropFirst())) + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: [groups[0], groups[2]]) + state.decrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: Array(groups.dropLast())) + XCTAssertEqual(0, state.selection) + } +} + +class SwitcherStateFuzzerTests: XCTestCase { + // swiftlint:disable:next function_body_length + func testFuzz() { + var script = "" + var wantsTimer = false + var wantsInterface = false + var wantsWindowUpdates = false + var hasUpdatedWindows = false + let state = SwitcherState( + wantsTimerCallback: { + script += "// wantsTimer → true\nXCTAssertTrue(wantsTimer)\n" + XCTAssertFalse(wantsTimer); wantsTimer = true + }, + wantsTimerCancelledCallback: { + script += "// wantsTimer → false\n" + XCTAssertTrue(wantsTimer); wantsTimer = false + }, + wantsShowInterfaceCallback: { + script += "// wantsInterface → true\nXCTAssertTrue(wantsInterface)\n" + XCTAssertFalse(wantsInterface); wantsInterface = true + }, + wantsHideInterfaceCallback: { + script += "// wantsInterface → false\n" + XCTAssertTrue(wantsInterface); wantsInterface = false + }, + wantsStartWindowListUpdates: { + script += "// wantsWindowUpdates → true\nXCTAssertTrue(wantsWindowUpdates)\n" + XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true + }, + wantsStopWindowListUpdates: { + script += "// wantsWindowUpdates → false\nXCTAssertFalse(wantsWindowUpdates)\n" + XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false; hasUpdatedWindows = false + }, + wantsRaiseCallback: {_ in script += "// wantsRaise\n" } + ) + + let start = Date() + var iterations = 0 + while -start.timeIntervalSinceNow < 0.2 { + if self.testRun!.failureCount > 0 { + print(script) + break + } + if wantsInterface { XCTAssertTrue(hasUpdatedWindows) } + if hasUpdatedWindows { + XCTAssertTrue(wantsWindowUpdates) + } + + // These properties don't always make sense, but they have sane default values and should never crash + _ = state.selection + _ = state.windows + _ = state.selectedWindow + + // swiftlint:disable opening_brace + [ + { + script += "state.incrementSelection()\n" + state.incrementSelection(); XCTAssertTrue(wantsWindowUpdates) + script += "XCTAssertTrue(wantsWindowUpdates)\n" + }, + { + script += "state.decrementSelection()\n" + state.decrementSelection(); XCTAssertTrue(wantsWindowUpdates) + script += "XCTAssertTrue(wantsWindowUpdates)\n" + }, + { + script += "state.hotKeyReleased()\n" + state.hotKeyReleased(); XCTAssertFalse(wantsInterface) + script += "XCTAssertFalse(wantsInterface)\n" + }, + { + script += "state.hotKeyReleased(cancel: true)\n" + state.hotKeyReleased(cancel: true); XCTAssertFalse(wantsInterface) + script += "XCTAssertFalse(wantsInterface)\n" + }, + { if wantsTimer { + script += "XCTAssertTrue(wantsTimer)\nstate.timerFired(); wantsTimer = false\n" + state.timerFired(); wantsTimer = false + } }, + { if wantsWindowUpdates { + script += "XCTAssertTrue(wantsWindowUpdates)\nstate.update(windows: [])\n" + hasUpdatedWindows = true; state.update(windows: []) + } }, + { if wantsWindowUpdates { + script += "XCTAssertTrue(wantsWindowUpdates)\nstate.update(windows: groups)\n" + hasUpdatedWindows = true; state.update(windows: groups) + } }, + { if wantsWindowUpdates { + script += "XCTAssertTrue(wantsWindowUpdates)\nstate.update(windows: Array(groups.dropFirst()))\n" + hasUpdatedWindows = true; state.update(windows: Array(groups.dropFirst())) + } }, + { if wantsWindowUpdates { + script += "XCTAssertTrue(wantsWindowUpdates)\nstate.update(windows: [groups[0], groups[2]])\n" + hasUpdatedWindows = true; state.update(windows: [groups[0], groups[2]]) + } }, + { if wantsWindowUpdates { + script += "XCTAssertTrue(wantsWindowUpdates)\nstate.update(windows: Array(groups.dropLast()))\n" + hasUpdatedWindows = true; state.update(windows: Array(groups.dropLast())) + } }, + ].randomElement()!() + // swiftlint:enable opening_brace + iterations += 1 + } + print("Fuzzer completed \(iterations) iterations") + } +} diff --git a/Switch/SwitchTests/SwitcherState/SwitcherStateTests.swift b/Switch/SwitchTests/SwitcherState/SwitcherStateTests.swift new file mode 100644 index 00000000..b235c0a5 --- /dev/null +++ b/Switch/SwitchTests/SwitcherState/SwitcherStateTests.swift @@ -0,0 +1,369 @@ +import XCTest +@testable import Switch + +// swiftlint:disable:next type_body_length +class SwitcherStateTests: XCTestCase { + func testBumpBackOnInactiveFrontmostWindowWhenIncrementing() { + var raised = false + let windowGroupList = WindowInfoGroup.list(from: [ + WindowInfo([ + .cgNumber: UInt32(510), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(1426), + .cgOwnerName: "System Preferences", + .cgName: "Security & Privacy", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + WindowInfo([ + .cgNumber: UInt32(5275), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 61.0, y: 238.0, width: 720.0, height: 445.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(16649), + .cgOwnerName: "TextMate", + .cgName: "untitled 8", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 61.0, y: 217.0, width: 720.0, height: 445.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + ]) + let state = SwitcherState( + wantsRaiseCallback: { windowGroup in + raised = true + XCTAssertEqual(windowGroup, windowGroupList[0]) + } + ) + state.incrementSelection() + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 0) + state.hotKeyReleased() + XCTAssertTrue(raised) + } + + func testNoBumpBackOnInactiveFrontmostWindowWhenDecrementing() { + var raised = false + let windowGroupList = WindowInfoGroup.list(from: [ + WindowInfo([ + .cgNumber: UInt32(510), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(1426), + .cgOwnerName: "System Preferences", + .cgName: "Security & Privacy", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + WindowInfo([ + .cgNumber: UInt32(5275), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 61.0, y: 238.0, width: 720.0, height: 445.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(16649), + .cgOwnerName: "TextMate", + .cgName: "untitled 8", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 61.0, y: 217.0, width: 720.0, height: 445.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + ]) + let state = SwitcherState( + wantsRaiseCallback: { windowGroup in + raised = true + XCTAssertEqual(windowGroup, windowGroupList[1]) + } + ) + state.decrementSelection() + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 1) + state.hotKeyReleased() + XCTAssertTrue(raised) + } + + func testNoBumpBackOnInactiveFrontmostWindowWhenNoChange() { + var raised = false + let windowGroupList = WindowInfoGroup.list(from: [ + WindowInfo([ + .cgNumber: UInt32(510), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(1426), + .cgOwnerName: "System Preferences", + .cgName: "Security & Privacy", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + WindowInfo([ + .cgNumber: UInt32(5275), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 61.0, y: 238.0, width: 720.0, height: 445.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(16649), + .cgOwnerName: "TextMate", + .cgName: "untitled 8", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 61.0, y: 217.0, width: 720.0, height: 445.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + ]) + let state = SwitcherState( + wantsRaiseCallback: { windowGroup in + raised = true + XCTAssertEqual(windowGroup, windowGroupList[0]) + } + ) + state.incrementSelection(by: 0) + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 0) + state.hotKeyReleased() + XCTAssertTrue(raised) + } + + func testNoRaiseForActiveTopmost() { + var raised = false + let windowGroupList = WindowInfoGroup.list(from: [ + WindowInfo([ + .cgNumber: UInt32(510), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(1426), + .cgOwnerName: "System Preferences", + .cgName: "Security & Privacy", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: true, + ]), + WindowInfo([ + .cgNumber: UInt32(5275), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 61.0, y: 238.0, width: 720.0, height: 445.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(16649), + .cgOwnerName: "TextMate", + .cgName: "untitled 8", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 61.0, y: 217.0, width: 720.0, height: 445.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + ]) + let state = SwitcherState( + wantsRaiseCallback: { _ in raised = true } + ) + state.incrementSelection(by: 0) + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 0) + state.hotKeyReleased() + XCTAssertFalse(raised) + } + + func testNoRaiseOnEmptyWindowList() { + var raised = false + let windowGroupList = WindowInfoGroup.list(from: []) + let state = SwitcherState( + wantsRaiseCallback: { _ in raised = true } + ) + state.incrementSelection(by: 0) + state.update(windows: windowGroupList) + XCTAssertNil(state.selection) + state.hotKeyReleased() + XCTAssertFalse(raised) + } + + func testSelectionResetsOnRelease() { + let windowGroupList = WindowInfoGroup.list(from: [ + WindowInfo([ + .cgNumber: UInt32(510), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(1426), + .cgOwnerName: "System Preferences", + .cgName: "Security & Privacy", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: true, + ]), + WindowInfo([ + .cgNumber: UInt32(5275), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 61.0, y: 238.0, width: 720.0, height: 445.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(16649), + .cgOwnerName: "TextMate", + .cgName: "untitled 8", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(69732800), + .nsFrame: NSRect(x: 61.0, y: 217.0, width: 720.0, height: 445.0), + .isFullscreen: false, + .canActivate: true, + .isAppActive: false, + ]), + ]) + let state = SwitcherState() + state.incrementSelection() + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 1) + state.hotKeyReleased() + XCTAssertNil(state.selection) + state.incrementSelection(by: 0) + state.update(windows: windowGroupList) + XCTAssertEqual(state.selection, 0) + } + + func testNilSelectorWithoutWindows() { + let state = SwitcherState() + state.incrementSelection() + state.update(windows: []) + XCTAssertNil(state.selection) + state.incrementSelection() + XCTAssertNil(state.selection) + } + + func testWantsTimerOnEachInvoke() { + var wantsWindowUpdates = false + var wantsTimer = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true }, + wantsTimerCancelledCallback: { XCTAssertTrue(wantsTimer); wantsTimer = false }, + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false } + ) + + XCTAssertFalse(wantsTimer) + state.incrementSelection() + XCTAssertTrue(wantsTimer) + state.hotKeyReleased() + XCTAssertFalse(wantsTimer) + + // While we're here, make sure the state obj wants window updates until it gets one + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertFalse(wantsWindowUpdates) + + state.incrementSelection() + XCTAssertTrue(wantsTimer) + } + + func testWantsInterfaceAfterTimerAndWindows() { + var wantsTimer = false + var wantsInterface = false + var wantsWindowUpdates = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true }, + wantsShowInterfaceCallback: { XCTAssertFalse(wantsInterface); wantsInterface = true }, + wantsHideInterfaceCallback: { XCTAssertTrue(wantsInterface); wantsInterface = false }, + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false } + ) + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + XCTAssertTrue(wantsTimer) + state.timerFired(); wantsTimer = false + state.update(windows: []) + XCTAssertTrue(wantsInterface) + + state.hotKeyReleased() + XCTAssertFalse(wantsInterface) + + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertTrue(wantsTimer) + state.timerFired(); wantsTimer = false + XCTAssertTrue(wantsInterface) + } + + func testNoInterfaceAfterTimerAlone() { + var wantsTimer = false + var wantsInterface = false + var wantsWindowUpdates = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true }, + wantsTimerCancelledCallback: { XCTAssertTrue(wantsTimer); wantsTimer = false }, + wantsShowInterfaceCallback: { XCTAssertFalse(wantsInterface); wantsInterface = true }, + wantsHideInterfaceCallback: { XCTAssertTrue(wantsInterface); wantsInterface = false }, + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false }, + wantsRaiseCallback: {_ in} + ) + + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.timerFired() + XCTAssertFalse(wantsInterface) + } + + func testNoInterfaceAfterWindowsAlone() { + var wantsTimer = false + var wantsInterface = false + var wantsWindowUpdates = false + let state = SwitcherState( + wantsTimerCallback: { XCTAssertFalse(wantsTimer); wantsTimer = true }, + wantsTimerCancelledCallback: { XCTAssertTrue(wantsTimer); wantsTimer = false }, + wantsShowInterfaceCallback: { XCTAssertFalse(wantsInterface); wantsInterface = true }, + wantsHideInterfaceCallback: { XCTAssertTrue(wantsInterface); wantsInterface = false }, + wantsStartWindowListUpdates: { XCTAssertFalse(wantsWindowUpdates); wantsWindowUpdates = true }, + wantsStopWindowListUpdates: { XCTAssertTrue(wantsWindowUpdates); wantsWindowUpdates = false }, + wantsRaiseCallback: {_ in} + ) + + state.incrementSelection() + XCTAssertTrue(wantsWindowUpdates) + state.update(windows: []) + XCTAssertTrue(wantsTimer) + XCTAssertTrue(wantsWindowUpdates) + XCTAssertFalse(wantsInterface) + } + + func testCloseFirstWindow() { + let groups = WindowInfoGroup.list(from: [ + // swiftlint:disable line_length + WindowInfo([.cgNumber: UInt32(100), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 1440.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(425), .cgOwnerName: "Xcode-beta", .cgName: "Switcher.swift", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.apple.dt.Xcode", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(510), .cgLayer: Int32(0), .cgBounds: CGRect(x: 0.0, y: 23.0, width: 668.0, height: 573.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(1426), .cgOwnerName: "System Preferences", .cgName: "Security & Privacy", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 0.0, y: 304.0, width: 668.0, height: 573.0), .isFullscreen: false, .ownerBundleID: "com.apple.systempreferences", .canActivate: true, .isAppActive: false]), + WindowInfo([.cgNumber: UInt32(10053), .cgLayer: Int32(0), .cgBounds: CGRect(x: 61.0, y: 23.0, width: 1369.0, height: 877.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(16649), .cgOwnerName: "TextMate", .cgName: "untitled 12", .cgIsOnscreen: true, .cgDisplayID: UInt32(69732800), .nsFrame: NSRect(x: 61.0, y: 0.0, width: 1369.0, height: 877.0), .isFullscreen: false, .ownerBundleID: "com.macromates.TextMate", .canActivate: true, .isAppActive: false]), + // swiftlint:enable line_length + ]) + + let state = SwitcherState() + state.setSelection(to: 0) + state.update(windows: groups) + XCTAssertEqual(0, state.selection) + state.update(windows: Array(groups.dropFirst())) + XCTAssertEqual(0, state.selection) + } +} diff --git a/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests+Chrome.swift b/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests+Chrome.swift new file mode 100644 index 00000000..f4ad129f --- /dev/null +++ b/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests+Chrome.swift @@ -0,0 +1,34 @@ +import XCTest +@testable import Switch + +// swiftlint:disable line_length + +class WindowInfoGroupTestsChrome: XCTestCase { + func testStatusBar() { + let mainWindow = WindowInfo([.canActivate: true, .cgAlpha: Float(1.0), .cgBounds: CGRect(x: 0.0, y: 22.0, width: 1366.0, height: 731.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "96001-06025-07 BOLT, FLANGE (6X25) $0.74", .cgNumber: UInt32(11304), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let windows = [ + WindowInfo([.canActivate: true, .cgAlpha: Float(1.0), .cgBounds: CGRect(x: 0.0, y: 735.0, width: 456.0, height: 18.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "", .cgNumber: UInt32(1303), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]), + mainWindow, + ] + + let windowGroups = WindowInfoGroup.list(from: windows) + XCTAssertEqual(1, windowGroups.count) + XCTAssertEqual(mainWindow, windowGroups.first?.mainWindow) + } + + func testMultipleWindows() { + let window1 = WindowInfo([.canActivate: true, .cgAlpha: Float(0.0), .cgBounds: CGRect(x: 0.0, y: 752.0, width: 456.0, height: 1.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "", .cgNumber: UInt32(17988), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let window2 = WindowInfo([.canActivate: true, .cgAlpha: Float(1.0), .cgBounds: CGRect(x: 0.0, y: 22.0, width: 1366.0, height: 731.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "Some wobsite", .cgNumber: UInt32(17989), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let window3 = WindowInfo([.canActivate: true, .cgAlpha: Float(0.0), .cgBounds: CGRect(x: 0.0, y: 752.0, width: 456.0, height: 1.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "", .cgNumber: UInt32(17960), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let window4 = WindowInfo([.canActivate: true, .cgAlpha: Float(1.0), .cgBounds: CGRect(x: 0.0, y: 22.0, width: 1366.0, height: 731.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "[Ardent] climbing this week? - numist@numist.net - numist.net Mail", .cgNumber: UInt32(17961), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let window5 = WindowInfo([.canActivate: true, .cgAlpha: Float(0.0), .cgBounds: CGRect(x: 68.0, y: 740.0, width: 410.0, height: 1.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "", .cgNumber: UInt32(17963), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let window6 = WindowInfo([.canActivate: true, .cgAlpha: Float(1.0), .cgBounds: CGRect(x: 68.0, y: 22.0, width: 1229.0, height: 719.0).dictionaryRepresentation, .cgDisplayID: UInt32(69732800), .cgIsOnscreen: true, .cgLayer: Int32(0), .cgName: "Google+ Hangouts", .cgNumber: UInt32(17964), .cgOwnerName: "Google Chrome", .cgOwnerPID: Int32(8705), .isAppActive: false, .isFullscreen: false, .ownerBundleID: "com.google.Chrome"]) + let windows = [window1, window2, window3, window4, window5, window6] + + let windowGroups = WindowInfoGroup.list(from: windows) + XCTAssertEqual(3, windowGroups.count) + XCTAssertEqual(window2, windowGroups.first?.mainWindow) + XCTAssertEqual(window4, windowGroups.second?.mainWindow) + XCTAssertEqual(window6, windowGroups.last?.mainWindow) + } +} diff --git a/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests.swift b/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests.swift new file mode 100644 index 00000000..a61cdc3f --- /dev/null +++ b/Switch/SwitchTests/WindowInfoGroupTests/WindowInfoGroupTests.swift @@ -0,0 +1,77 @@ +import XCTest +@testable import Switch + +// swiftlint:disable line_length + +class WindowInfoGroupTests: XCTestCase { + func testPhotosMainWindowHasEmptyName() { + let window = WindowInfo([ + .cgNumber: UInt32(37086), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 0.0, y: 25.0, width: 1440.0, height: 875.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(63872), + .cgOwnerName: "Photos", + .cgName: "", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(1), + .nsFrame: NSRect(x: -0.0, y: 0.0, width: 1440.0, height: 875.0), + .isFullscreen: false, + .ownerBundleID: "com.apple.Photos", + .canActivate: true, + .isAppActive: false, + ]) + let windowGroups = WindowInfoGroup.list(from: [window]) + XCTAssertEqual(1, windowGroups.count) + } + + func testSafariStatusBar() { + let windows = [ + WindowInfo([.cgNumber: UInt32(860), .cgLayer: Int32(0), .cgBounds: CGRect(x: 455.0, y: 878.0, width: 458.0, height: 20.0).dictionaryRepresentation, .cgAlpha: Float(0.21708053), .cgOwnerPID: Int32(568), .cgOwnerName: "Safari.app", .cgName: "", .cgIsOnscreen: true, .cgDisplayID: UInt32(1), .nsFrame: NSRect(x: 455.0, y: 2.0, width: 458.0, height: 20.0), .isFullscreen: false, .ownerBundleID: "com.apple.Safari", .canActivate: true, .isAppActive: true]), + WindowInfo([.cgNumber: UInt32(116), .cgLayer: Int32(0), .cgBounds: CGRect(x: 452.0, y: 26.0, width: 987.0, height: 875.0).dictionaryRepresentation, .cgAlpha: Float(1.0), .cgOwnerPID: Int32(568), .cgOwnerName: "Safari.app", .cgName: "3 • Inbox | Fastmail", .cgIsOnscreen: true, .cgDisplayID: UInt32(1), .nsFrame: NSRect(x: 452.0, y: -1.0, width: 987.0, height: 875.0), .isFullscreen: false, .ownerBundleID: "com.apple.Safari", .canActivate: true, .isAppActive: true]), + ] + let windowGroups = WindowInfoGroup.list(from: windows) + XCTAssertEqual(1, windowGroups.count) + XCTAssertEqual(windows[1], windowGroups.first?.mainWindow) + } +} + +// TODO: +/* + Bring back snapshots. + + Fucking Zoom shenanigans: + + [WindowInfo([ + .cgNumber: UInt32(82205), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 440.0, y: 236.0, width: 560.0, height: 453.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(94451), + .cgOwnerName: "zoom.us", + .cgName: "", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(1), + .nsFrame: NSRect(x: 440.0, y: 211.0, width: 560.0, height: 453.0), + .isFullscreen: false, + .ownerBundleID: "us.zoom.xos", + .canActivate: true, + .isAppActive: true, + ]), WindowInfo([ + .cgNumber: UInt32(82206), + .cgLayer: Int32(0), + .cgBounds: CGRect(x: 400.0, y: 222.0, width: 640.0, height: 480.0).dictionaryRepresentation, + .cgAlpha: Float(1.0), + .cgOwnerPID: Int32(94451), + .cgOwnerName: "zoom.us", + .cgName: "Login", + .cgIsOnscreen: true, + .cgDisplayID: UInt32(1), + .nsFrame: NSRect(x: 400.0, y: 198.0, width: 640.0, height: 480.0), + .isFullscreen: false, + .ownerBundleID: "us.zoom.xos", + .canActivate: true, + .isAppActive: true, + ])] + + */ diff --git a/Frameworks/NNKit/NNKitTests/NNKit iOS Tests-Info.plist b/Switch/SwitchUITests/Info.plist similarity index 67% rename from Frameworks/NNKit/NNKitTests/NNKit iOS Tests-Info.plist rename to Switch/SwitchUITests/Info.plist index d6837796..64d65ca4 100644 --- a/Frameworks/NNKit/NNKitTests/NNKit iOS Tests-Info.plist +++ b/Switch/SwitchUITests/Info.plist @@ -3,19 +3,19 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable - ${EXECUTABLE_NAME} + $(EXECUTABLE_NAME) CFBundleIdentifier - net.numist.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleName + $(PRODUCT_NAME) CFBundlePackageType - BNDL + $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 - CFBundleSignature - ???? CFBundleVersion 1 diff --git a/Switch/SwitchUITests/SwitchUITests.swift b/Switch/SwitchUITests/SwitchUITests.swift new file mode 100644 index 00000000..395c4c3d --- /dev/null +++ b/Switch/SwitchUITests/SwitchUITests.swift @@ -0,0 +1,36 @@ +import XCTest + +class SwitchUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - + // required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Switch/constants.h b/Switch/constants.h deleted file mode 100644 index cc6335c8..00000000 --- a/Switch/constants.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// constants.h -// Switch -// -// Created by Scott Perry on 03/01/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -// Points -extern const CGFloat kNNMaxWindowThumbnailSize; -extern const CGFloat kNNMaxApplicationIconSize; -extern const CGFloat kNNScreenToWindowInset; -extern const CGFloat kNNWindowToItemInset; -extern const CGFloat kNNItemBorderWidth; -extern const CGFloat kNNItemToThumbInset; -extern const CGFloat kNNWindowToThumbInset; -extern const CGFloat kNNWindowRoundRectRadius; -extern const CGFloat kNNSelectionRoundRectRadius; - -// Seconds -extern const NSTimeInterval delayBeforePresentingSwitcherWindow; - -// Enum stringification -NSString *NNStringFromCGWindowLevel(long level); - -// Helper macros -#define CLASS_CAST(Classname, value) ((Classname *)CLASS_CAST_IMPL([Classname class], (value))) -id CLASS_CAST_IMPL(Class class, id value); diff --git a/Switch/constants.m b/Switch/constants.m deleted file mode 100644 index 4ccc23c3..00000000 --- a/Switch/constants.m +++ /dev/null @@ -1,92 +0,0 @@ -// -// constants.m -// Switch -// -// Created by Scott Perry on 03/01/13. -// Copyright © 2013 Scott Perry. -// -// 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 "constants.h" - - -// Maxima -const CGFloat kNNMaxWindowThumbnailSize = 128.0; -const CGFloat kNNMaxApplicationIconSize = kNNMaxWindowThumbnailSize / 2.0; - -// Window -const CGFloat kNNScreenToWindowInset = 16.0; - -// Items -const CGFloat kNNWindowToItemInset = 8.0; -const CGFloat kNNItemBorderWidth = 3.0; - -// Thumbs -const CGFloat kNNItemToThumbInset = kNNItemBorderWidth + 8.0; -const CGFloat kNNWindowToThumbInset = kNNWindowToItemInset + kNNItemToThumbInset; - -// Round rect radii -const CGFloat kNNWindowRoundRectRadius = 20.0; -const CGFloat kNNSelectionRoundRectRadius = kNNWindowRoundRectRadius - kNNWindowToItemInset; - -// Timing -const NSTimeInterval delayBeforePresentingSwitcherWindow = 0.25; - - -__attribute__((const)) NSString *NNStringFromCGWindowLevel(long level) -{ - if (level == kCGBaseWindowLevel) { - return @"kCGBaseWindowLevel"; - } else if (level == kCGMinimumWindowLevel) { - return @"kCGMinimumWindowLevel"; - } else if (level == kCGDesktopWindowLevel) { - return @"kCGDesktopWindowLevel"; - } else if (level == kCGDesktopIconWindowLevel) { - return @"kCGDesktopIconWindowLevel"; - } else if (level == kCGBackstopMenuLevel) { - return @"kCGBackstopMenuLevel"; - } else if (level == kCGNormalWindowLevel) { - return @"kCGNormalWindowLevel"; - } else if (level == kCGFloatingWindowLevel) { - return @"kCGFloatingWindowLevel"; - } else if (level == kCGTornOffMenuWindowLevel) { - return @"kCGTornOffMenuWindowLevel"; - } else if (level == kCGDockWindowLevel) { - return @"kCGDockWindowLevel"; - } else if (level == kCGMainMenuWindowLevel) { - return @"kCGMainMenuWindowLevel"; - } else if (level == kCGStatusWindowLevel) { - return @"kCGStatusWindowLevel"; - } else if (level == kCGModalPanelWindowLevel) { - return @"kCGModalPanelWindowLevel"; - } else if (level == kCGPopUpMenuWindowLevel) { - return @"kCGPopUpMenuWindowLevel"; - } else if (level == kCGDraggingWindowLevel) { - return @"kCGDraggingWindowLevel"; - } else if (level == kCGScreenSaverWindowLevel) { - return @"kCGScreenSaverWindowLevel"; - } else if (level == kCGCursorWindowLevel) { - return @"kCGCursorWindowLevel"; - } else if (level == kCGOverlayWindowLevel) { - return @"kCGOverlayWindowLevel"; - } else if (level == kCGHelpWindowLevel) { - return @"kCGHelpWindowLevel"; - } else if (level == kCGUtilityWindowLevel) { - return @"kCGUtilityWindowLevel"; - } else if (level == kCGAssistiveTechHighWindowLevel) { - return @"kCGAssistiveTechHighWindowLevel"; - } else if (level == kCGMaximumWindowLevel) { - return @"kCGMaximumWindowLevel"; - } - return @"kNNUnknownWindowLevel"; -} - -id CLASS_CAST_IMPL(Class class, id value) { - Check(!value || [value isKindOfClass:class]); - return value; -} diff --git a/Switch/debugger.c b/Switch/debugger.c deleted file mode 100644 index dc1e6d04..00000000 --- a/Switch/debugger.c +++ /dev/null @@ -1,60 +0,0 @@ -// -// debugger.c -// Debugger -// -// Created by Scott Perry on 8/11/11. -// Public domain. -// - -#if defined(DEBUG) - -#include -#include -#include -#include -#include - -/* - * NOTE: Forward decl of AmIBeingDebugged since this file is built without platform flags - * (resulting in a warning when including debugger.h directly) - */ -bool AmIBeingDebugged(void); - -/* - * This function provided by Apple TechNote QA1361: - * http://developer.apple.com/library/mac/#qa/qa1361/_index.html - */ -bool AmIBeingDebugged(void) - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). -{ - int junk; - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - // Call sysctl. - - size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); - assert(junk == 0); - - // We're being debugged if the P_TRACED flag is set. - - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); -} - -#endif diff --git a/Switch/debugger.h b/Switch/debugger.h deleted file mode 100644 index d26deaa1..00000000 --- a/Switch/debugger.h +++ /dev/null @@ -1,221 +0,0 @@ -// -// debugger.h -// Debugger -// -// Created by Scott Perry on 8/11/11. -// Public domain. -// - -#ifdef __OBJC__ - -#ifndef _DEBUGGER_H_ -#define _DEBUGGER_H_ - -#include -#include - -// Compatibility with non-clang compilers. -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -/* - * The TODO macro allows TODO items to appear as compiler warnings. - * Always enabled—if you've got something you still need to do, do it before you ship! - */ -#define DO_PRAGMA(x) _Pragma (#x) -#define TODO(x) DO_PRAGMA(message ("TODO - " x)) - -#ifdef DEBUG - bool AmIBeingDebugged(void); - - #pragma mark - DebugBreak implementations for all known platforms - - // Legacy implementations of DebugBreak vary considerably based on architecture and platform. - #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - // iOS DebugBreak initial implementations provided by http://iphone.m20.nl/wp/?p=1 (now defunct). This code has been largely rewritten. - #if defined(__arm__) - #pragma mark - iOS(arm) - - #define DebugBreak() \ - do { \ - if(AmIBeingDebugged()) { \ - __asm__ __volatile__ ( \ - "mov r0, %0\n" \ - "mov r1, %1\n" \ - "mov r12, #37\n" \ - "swi 128\n" \ - : : "r" (getpid ()), "r" (SIGINT) : "r12", "r0", "r1", "cc" \ - ); \ - } \ - } while (false) - - #elif defined(__i386__) || defined(__x86_64__) - #pragma mark - iOS(x86) - - #define DebugBreak() \ - do { \ - if(AmIBeingDebugged()) { \ - __asm__ __volatile__ ( \ - "pushl %0\n" \ - "pushl %1\n" \ - "push $0\n" \ - "movl %2, %%eax\n" \ - "int $0x80\n" \ - "add $12, %%esp\n" \ - : : "g" (SIGINT), "g" (getpid ()), "n" (37) : "eax", "cc"); \ - } \ - } while (false) - - #else - #pragma mark - iOS(unknown) - #warning Debugger: Current iOS architecture not supported, please report (Debugger integration disabled) - #define DebugBreak() - #endif - #elif TARGET_OS_MAC - // Mac DebugBreak initial implementations provided by: http://cocoawithlove.com/2008/03/break-into-debugger.html - #if defined(__ppc64__) || defined(__ppc__) - #pragma mark - desktop(ppc) - - #define DebugBreak() \ - if(AmIBeingDebugged()) \ - { \ - __asm__( \ - "li r0, 20\n" \ - "sc\n" \ - "nop\n" \ - "li r0, 37\n" \ - "li r4, 2\n" \ - "sc\n" \ - "nop\n" \ - : : : "memory","r0","r3","r4" \ - ); \ - } - - #elif defined(__x86_64__) || defined(__i386__) - #pragma mark - desktop(x86) - #define DebugBreak() if(AmIBeingDebugged()) {__asm__("int $3\n" : : );} - #else - #pragma mark - desktop(unknown) - #warning Debugger: Current desktop architecture not supported, please report (Debugger integration disabled) - #define DebugBreak() - #endif - #else - #pragma mark - unknown() - #warning Debugger: Current platform not supported, please report (Debugger integration disabled) - #define DebugBreak() - #endif - -#pragma mark - High(er) level debugging macros - #define NotTested() do { \ - Log(@"NOT TESTED"); \ - DebugBreak(); \ - } while(0) - - // The Log, Assert, and NotReached macros are much more mundane, serving to prevent the incidence of NSLog calls in Release builds, improve logging in Debug builds, and kill the program. - #ifndef Log - #define Log(fmt, ...) do { \ - NSLog(@"%@:%d %@", [[[NSString alloc] initWithCString:(__FILE__) encoding:NSUTF8StringEncoding] lastPathComponent], __LINE__, [NSString stringWithFormat:(fmt), ##__VA_ARGS__]); \ - } while(0) - #endif - - // The Check and NotTested functions emit a log message and will break a watching debugger if possible. - #define Check(exp) _InternalCheck((exp), __FILE__, __LINE__, #exp) - static inline _Bool _InternalCheck(_Bool result, char *filename, unsigned lineno, char *expr) { - if (!result) { - Log(@"%s:%u: Failed check `%s` %@", filename, lineno, expr, [NSThread callStackSymbols]); - DebugBreak(); - } - return result; - } - - // Assert is ALWAYS FATAL on DEBUG! If the error was recoverable, you should be using Check() or Bail…Unless()! - #define Assert(exp) do { \ - if (!(exp)) { \ - Log(@"Failed assertion `%s`", #exp); \ - DebugBreak(); \ - abort(); \ - } \ - } while(0) - - // NotReached is ALWAYS FATAL on DEBUG! If the code path is intentionally reachable, you should be using NotTested()! - #define NotReached() do { \ - Log(@"Entered THE TWILIGHT ZONE"); \ - DebugBreak(); \ - abort(); \ - } while(0) - - // Macros that affect control flow on condition - #define BailUnless(exp,return_value) do { \ - if (!(exp)) { \ - Log(@"Failed check `%s`, bailing.", #exp); \ - DebugBreak(); \ - return return_value; \ - } \ - } while(0) - #define BailWithBlockUnless(exp,block) do { \ - if (!(exp)) { \ - Log(@"Failed check `%s`, bailing.", #exp); \ - DebugBreak(); \ - return block(); \ - } \ - } while(0) - #define BailWithGotoUnless(exp,label) do { \ - if (!(exp)) { \ - Log(@"Failed check `%s`, bailing.", #exp); \ - DebugBreak(); \ - goto label; \ - } \ - } while(0) -#else // DEBUG -#pragma mark - Debugging stubs - #define DebugBreak() - - #ifndef Log - #define Log(...) - #endif - - #define Check(exp) _InternalCheck((exp), __FILE__, __LINE__, #exp) - static inline _Bool _InternalCheck(_Bool result, char *filename, unsigned lineno, char *expr) { - if (!result) { - Log(@"%s:%u: Failed check `%s`", filename, lineno, expr); - } - return result; - } - - #define NotTested() - - // Assert degrades into an assert on builds without DEBUG defined. (assert can be disabled by defining NDEBUG) - #define Assert(exp) assert(exp) - - // NotReached is non-fatal on builds without DEBUG defined, but due to the unpredictable nature of code generation around __builtin_unreachable, your app will be unlucky if it survives. - #if __has_builtin(__builtin_unreachable) - #define NotReached() __builtin_unreachable() - #else - #define NotReached() - #endif - - // Macros that affect control flow on condition - #define BailUnless(exp,return_value) do { \ - if (!(exp)) { \ - return return_value; \ - } \ - } while(0) - #define BailWithBlockUnless(exp,block) do { \ - if (!(exp)) { \ - return block(); \ - } \ - } while(0) - #define BailWithGotoUnless(exp,label) do { \ - if (!(exp)) { \ - goto label; \ - } \ - } while(0) -#endif // DEBUG - -#define Likely(x) __builtin_expect(!!(x), 1) -#define Unlikely(x) __builtin_expect(!!(x), 0) - -#endif // _DEBUGGER_H_ - -#endif // __OBJC__ diff --git a/Switch/en.lproj/Credits.rtf b/Switch/en.lproj/Credits.rtf deleted file mode 100644 index 3e793c52..00000000 --- a/Switch/en.lproj/Credits.rtf +++ /dev/null @@ -1,30 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\vieww9600\viewh8400\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 - -\f0\b\fs24 \cf0 Engineering: -\b0 \ - Scott Perry\ -\ - -\b Human Interface Design: -\b0 \ - Nobody\ -\ - -\b Testing: -\b0 \ - So far, nobody\ -\ - -\b Documentation: -\b0 \ - Oops\ -\ - -\b With special thanks to: -\b0 \ - David Smith, Gwynne Raskind, and Rob Rix\ -} \ No newline at end of file diff --git a/Switch/en.lproj/MainMenu.xib b/Switch/en.lproj/MainMenu.xib deleted file mode 100644 index e8b37b85..00000000 --- a/Switch/en.lproj/MainMenu.xib +++ /dev/null @@ -1,535 +0,0 @@ - - - - 1080 - 12E55 - 4510 - 1187.39 - 626.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 4510 - - - NSCustomObject - NSMenu - NSMenuItem - - - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - - NSApplication - - - FirstResponder - - - NSApplication - - - AMainMenu - - - - Switch - - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - Switch - - - - About Switch - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Preferences… - , - 1048576 - 2147483647 - - - - - - Check for Updates… - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - Services - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit Switch - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - File - - 1048576 - 2147483647 - - - submenuAction: - - File - - - - Close - w - 1048576 - 2147483647 - - - - - - - - - Help - - 2147483647 - - - submenuAction: - - Help - - - - Switch Help - ? - 1048576 - 2147483647 - - - - - _NSHelpMenu - - - - _NSMainMenu - - - SWAppDelegate - - - NSFontManager - - - SUUpdater - - - - - - - terminate: - - - - 449 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - delegate - - - - 495 - - - - showHelp: - - - - 493 - - - - performClose: - - - - 556 - - - - showPreferences: - - - - 540 - - - - checkForUpdates: - - - - 539 - - - - - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - - - - - - - - 56 - - - - - - - - 57 - - - - - - - - - - - - - - - 58 - - - - - 136 - - - - - 129 - - - - - 143 - - - - - 236 - - - - - 131 - - - - - - - - 149 - - - - - 130 - - - - - 420 - - - - - 490 - - - - - - - - 491 - - - - - - - - 492 - - - - - 494 - - - - - 536 - - - - - 537 - - - - - 541 - - - - - - - - 542 - - - - - - - - 548 - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - 565 - - - - - SWAppDelegate - NSObject - - showPreferences: - NSMenuItem - - - showPreferences: - - showPreferences: - NSMenuItem - - - - IBProjectSource - ./Classes/SWAppDelegate.h - - - - SUUpdater - NSObject - - checkForUpdates: - id - - - checkForUpdates: - - checkForUpdates: - id - - - - delegate - id - - - delegate - - delegate - id - - - - IBProjectSource - ./Classes/SUUpdater.h - - - - - 0 - IBCocoaFramework - YES - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - 3 - - {11, 11} - {10, 3} - - YES - - diff --git a/Switch/helpers.h b/Switch/helpers.h deleted file mode 100644 index 6c13883e..00000000 --- a/Switch/helpers.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// helpers.h -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 - - -#define NNAssertMainQueue() NSAssert([[NSThread currentThread] isMainThread], @"Current path of execution must be run on the main thread"); - -BOOL NNCGRectsEqual(CGRect a, CGRect b); -BOOL NNNSSizesEqual(NSSize a, NSSize b); - -void *NNCFAutorelease(CFTypeRef cfObject); diff --git a/Switch/helpers.m b/Switch/helpers.m deleted file mode 100644 index 5bc77dff..00000000 --- a/Switch/helpers.m +++ /dev/null @@ -1,42 +0,0 @@ -// -// helpers.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "helpers.h" - - -BOOL NNNSSizesEqual(NSSize a, NSSize b) -{ - return a.width == b.width && a.height == b.height; -} - -BOOL NNCGRectsEqual(CGRect a, CGRect b) -{ - return a.origin.x == b.origin.x && a.origin.y == b.origin.y && NNNSSizesEqual(a.size, b.size); -} - -void *NNCFAutorelease(CFTypeRef cfObject) -{ - if (cfObject) { - static Class arp = Nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - arp = NSClassFromString(@"NSAutoreleasePool"); - Assert(arp); - }); - - [arp addObject:(__bridge id)cfObject]; - } - - return (void *)cfObject; -} diff --git a/Switch/imageComparators.h b/Switch/imageComparators.h deleted file mode 100644 index c4a8f05e..00000000 --- a/Switch/imageComparators.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// imageComparators.h -// Switch -// -// Created by Scott Perry on 03/02/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -static BOOL (^imagesDifferByCachedTIFFComparison)(NSImage *, NSImage *) = ^(NSImage *a, NSImage *b) { - static void *tiffContextKey = (void *)1999428944; // Guaranteed random by arc4random() - NSData *(^TIFFForImage)(NSImage *) = ^(NSImage *image) { - NSData *result = objc_getAssociatedObject(image, tiffContextKey); - if (!result) { - result = [image TIFFRepresentation]; -#ifndef TESTING - objc_setAssociatedObject(image, tiffContextKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -#endif - } - return result; - }; - - NSData *aData = TIFFForImage(a); - NSData *bData = TIFFForImage(b); - return (BOOL)![aData isEqualToData:bData]; -}; - -/// This used to be the fastest option, but something changed in CG and it's now doing a whole lot of buffer copying. I suspect it used to be returning references (instead of copies) from calls to CGDataProviderCopyData -static BOOL (^imagesDifferByCGDataProviderComparison)(CGImageRef, CGImageRef) = ^(CGImageRef a, CGImageRef b) { - BOOL result = NO; - - CGDataProviderRef aDataProvider = CGImageGetDataProvider(a); - CGDataProviderRef bDataProvider = CGImageGetDataProvider(b); - - CFDataRef aData = NNCFAutorelease(CGDataProviderCopyData(aDataProvider)); - CFDataRef bData = NNCFAutorelease(CGDataProviderCopyData(bDataProvider)); - - if (CFDataGetLength(aData) != CFDataGetLength(bData)) { - result = YES; - } - - if (!result) { - // It turns out that striding over the buffers is slower than memcmp. Jesus. - result = !!memcmp(CFDataGetBytePtr(aData), CFDataGetBytePtr(bData), (unsigned long)CFDataGetLength(aData)); - } - - return result; -}; - -/// This was both slow and inexact. -static BOOL (^imagesDifferByCachedBitmapContextComparison)(NSImage *, NSImage *) = ^(NSImage *a, NSImage *b) { - static void *bitmapContextKey = (void *)1567529422; // Guaranteed random by arc4random() - NSSize imageSize = a.size; - assert(a.size.width == b.size.width); - assert(a.size.height == b.size.height); - - // Sane number of testing points per axis. - CGFloat maxSize = 32; - CGFloat x = maxSize * imageSize.width / MAX(imageSize.width, imageSize.height); - CGFloat y = maxSize * imageSize.height / MAX(imageSize.width, imageSize.height); - - CGContextRef (^contextForImage)(NSImage *) = ^(NSImage *image) { - CGContextRef result = (CGContextRef)CFBridgingRetain(objc_getAssociatedObject(image, bitmapContextKey)); - if (!result) { - // Bake the old image data into a (smallish) buffer. - result = CGBitmapContextCreate(NULL, (unsigned)x, (unsigned)y, 8, 0, [[NSColorSpace genericRGBColorSpace] CGColorSpace], kCGBitmapByteOrder32Host|kCGImageAlphaPremultipliedFirst); - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:result flipped:NO]]; - [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationNone]; - [image drawInRect:CGRectMake(0, 0, x, y) fromRect:CGRectZero operation:NSCompositeCopy fraction:1.0]; - [NSGraphicsContext restoreGraphicsState]; -#ifndef TESTING - objc_setAssociatedObject(image, bitmapContextKey, (__bridge id)result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -#endif - } - return result; - }; - - CGContextRef aContext = contextForImage(a); - void *aData = CGBitmapContextGetData(aContext); - - CGContextRef bContext = contextForImage(b); - void *bData = CGBitmapContextGetData(bContext); - - // Determine the size of the buffers. - size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bContext); - size_t height = CGBitmapContextGetHeight(bContext); - size_t len = bytesPerRow * height; - - // Determine image equality, with hoizTestPointCount * vertTestPointCount pixel samples. - BOOL result = !!memcmp(aData, bData, len); - - // Clean up. - CFRelease(aContext); - CFRelease(bContext); - - return result; -}; diff --git a/Switch/main.m b/Switch/main.m deleted file mode 100644 index e04f4b0e..00000000 --- a/Switch/main.m +++ /dev/null @@ -1,20 +0,0 @@ - // -// main.m -// Switch -// -// Created by Scott Perry on 02/24/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -int main(int argc, char *argv[]) -{ - return NSApplicationMain(argc, (const char **)argv); -} diff --git a/relaunch/SWTerminationListener.h b/Switch/relaunch/SWTerminationListener.h similarity index 98% rename from relaunch/SWTerminationListener.h rename to Switch/relaunch/SWTerminationListener.h index 1b5f13a1..1b646f5c 100644 --- a/relaunch/SWTerminationListener.h +++ b/Switch/relaunch/SWTerminationListener.h @@ -14,6 +14,8 @@ // Inspiration from relaunch.m: https://github.com/andymatuschak/Sparkle/blob/7316a00e9c92f54c552076a44c38241c0f1bf975/relaunch.m // +#import + @interface SWTerminationListener : NSObject - (id)initWithExecutablePath:(const char *)execPath parentProcessId:(pid_t)ppid; diff --git a/relaunch/SWTerminationListener.m b/Switch/relaunch/SWTerminationListener.m similarity index 100% rename from relaunch/SWTerminationListener.m rename to Switch/relaunch/SWTerminationListener.m diff --git a/relaunch/main.m b/Switch/relaunch/main.m similarity index 100% rename from relaunch/main.m rename to Switch/relaunch/main.m diff --git a/SwitchTests/NSImage+NNFilesystem.h b/SwitchTests/NSImage+NNFilesystem.h deleted file mode 100644 index 5bef79cc..00000000 --- a/SwitchTests/NSImage+NNFilesystem.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSImage+NNFilesystem.h -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -@interface NSImage (NNFilesystem) - -- (BOOL)writeTIFFToFile:(NSString *)path; - -@end diff --git a/SwitchTests/NSImage+NNFilesystem.m b/SwitchTests/NSImage+NNFilesystem.m deleted file mode 100644 index 98ec6880..00000000 --- a/SwitchTests/NSImage+NNFilesystem.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSImage+NNFilesystem.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NSImage+NNFilesystem.h" - -@implementation NSImage (NNFilesystem) - -- (BOOL)writeTIFFToFile:(NSString *)path; -{ - return [[self TIFFRepresentation] writeToFile:path atomically:NO]; -} - -@end diff --git a/SwitchTests/NSWindow+NNScreenCapture.h b/SwitchTests/NSWindow+NNScreenCapture.h deleted file mode 100644 index 6f9f4e5d..00000000 --- a/SwitchTests/NSWindow+NNScreenCapture.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NSWindow+NNScreenCapture.h -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 - -@interface NSWindow (NNScreenCapture) - -- (NSImage *)nnImage; - -@end diff --git a/SwitchTests/NSWindow+NNScreenCapture.m b/SwitchTests/NSWindow+NNScreenCapture.m deleted file mode 100644 index ada42892..00000000 --- a/SwitchTests/NSWindow+NNScreenCapture.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// NSWindow+NNScreenCapture.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "NSWindow+NNScreenCapture.h" - -@implementation NSWindow (NNScreenCapture) - -- (NSImage *)nnImage; -{ - CGWindowID windowID = (CGWindowID)self.windowNumber; - CGImageRef cgShot = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageDefault); - Check(cgShot); - NSImage *nsShot = [[NSImage alloc] initWithCGImage:cgShot size:NSZeroSize]; - - CFRelease(cgShot); - cgShot = NULL; - - return nsShot; -} - -@end diff --git a/SwitchTests/References/image.tiff b/SwitchTests/References/image.tiff deleted file mode 100644 index 34e9b6f1..00000000 Binary files a/SwitchTests/References/image.tiff and /dev/null differ diff --git a/SwitchTests/SWChromeTests.m b/SwitchTests/SWChromeTests.m deleted file mode 100644 index 46bb5e11..00000000 --- a/SwitchTests/SWChromeTests.m +++ /dev/null @@ -1,203 +0,0 @@ -// -// SWChromeTests.m -// Switch -// -// Created by Scott Perry on 01/19/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWChromeTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWChromeTests - -- (void)testStatusBar -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 731, - .size.width = 1366, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @4350292, - NNWindowName : @"96001-06025-07 BOLT, FLANGE (6X25) $0.74", - NNWindowNumber : @11304, - NNWindowOwnerName : @"Google Chrome", - NNWindowOwnerPID : @8705, - NNWindowSharingState : @1, - NNWindowStoreType : @2 - }; - NSArray *infoList = @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 18, - .size.width = 456, - .origin.x = 0, - .origin.y = 735 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @38140, - NNWindowName : @"", - NNWindowNumber : @1303, - NNWindowOwnerName : @"Google Chrome", - NNWindowOwnerPID : @8705, - NNWindowSharingState : @1, - NNWindowStoreType : @2 - }, - windowDescription - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 1, @"Chrome was incorrectly filtered"); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); - } -} - -- (void)testMultipleWindows; -{ - NSDictionary *window1Description = @{ - NNWindowLayer : @0, - NNWindowName : @"", - NNWindowMemoryUsage : @5372, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17988, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @1, - @"Width" : @456, - @"X" : @0, - @"Y" : @752, - }, - NNWindowAlpha : @0.000000, - }; - NSDictionary *window2Description = @{ - NNWindowLayer : @0, - NNWindowName : @"(2) Facebook", - NNWindowMemoryUsage : @4350292, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17989, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @731, - @"Width" : @1366, - @"X" : @0, - @"Y" : @22, - }, - NNWindowAlpha : @1.000000, - }; - NSDictionary *window3Description = @{ - NNWindowLayer : @0, - NNWindowName : @"", - NNWindowMemoryUsage : @5372, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17960, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @1, - @"Width" : @456, - @"X" : @0, - @"Y" : @752, - }, - NNWindowAlpha : @0.000000, - }; - NSDictionary *window4Description = @{ - NNWindowLayer : @0, - NNWindowName : @"[Ardent] climbing this week? - numist@numist.net - numist.net Mail", - NNWindowMemoryUsage : @4112212, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17961, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @731, - @"Width" : @1366, - @"X" : @0, - @"Y" : @22, - }, - NNWindowAlpha : @1.000000, - }; - NSDictionary *window5Description = @{ - NNWindowLayer : @0, - NNWindowName : @"", - NNWindowMemoryUsage : @5372, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17963, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @1, - @"Width" : @410, - @"X" : @68, - @"Y" : @740, - }, - NNWindowAlpha : @0.000000, - }; - NSDictionary *window6Description = @{ - NNWindowLayer : @0, - NNWindowName : @"Google+ Hangouts", - NNWindowMemoryUsage : @3625300, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @8705, - NNWindowNumber : @17964, - NNWindowOwnerName : @"Google Chrome", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @719, - @"Width" : @1229, - @"X" : @68, - @"Y" : @22, - }, - NNWindowAlpha : @1.000000, - }; - NSArray *infoList = @[ - window1Description, - window2Description, - window3Description, - window4Description, - window5Description, - window6Description, - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 3, @"Chrome was incorrectly filtered"); - if (self.listService.windows.count == 3) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, window2Description, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, window4Description, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:2]).mainWindow.windowDescription, window6Description, @""); - } -} - -@end diff --git a/SwitchTests/SWDashTests.m b/SwitchTests/SWDashTests.m deleted file mode 100644 index ed5c5fed..00000000 --- a/SwitchTests/SWDashTests.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// SWDashTests.m -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWDashTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWDashTests - -// https://github.com/numist/Switch/issues/48 -- (void)testUnnamedDashWindow; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 616, - .size.width = 936, - .origin.x = 44, - .origin.y = 36 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2576052, - NNWindowNumber : @84004, - NNWindowOwnerName : @"Dash", - NNWindowOwnerPID : @7852, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[windowDescription]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 1, @"Dash was incorrectly filtered out"); - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); -} - -@end diff --git a/SwitchTests/SWFinderTests.m b/SwitchTests/SWFinderTests.m deleted file mode 100644 index ad57d13a..00000000 --- a/SwitchTests/SWFinderTests.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// SWFinderTests.m -// Switch -// -// Created by Scott Perry on 05/13/16. -// Copyright © 2016 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWFinderTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWFinderTests - -- (void)testQuicklookWindow; -{ - // This Quicklook window was recorded on OS X 10.11.3 when Finder was not frontmost - NSArray *infoList = @[ - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @58213, - NNWindowNumber : @14164, - NNWindowOwnerName : @"Finder", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @669, - @"Width" : @366, - @"X" : @500, - @"Y" : @59, - }, - NNWindowName : @"", - }, - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 0, @"Finder QuickLook window was incorrectly filtered"); -} - -@end diff --git a/SwitchTests/SWGithubTests.m b/SwitchTests/SWGithubTests.m deleted file mode 100644 index be1e585b..00000000 --- a/SwitchTests/SWGithubTests.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// SWGithubTests.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWGithubTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWGithubTests - -// This never had an Issue on Github, but it was a problem. Fixed in the Github app at the end of 2013 (thanks! <3), test kept for compatibility. -- (void)testUnnamedGithubWindow; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 742, - .size.width = 1311, - .origin.x = 28, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @4804660, - NNWindowNumber : @93247, - NNWindowOwnerName : @"GitHub", - NNWindowOwnerPID : @23598, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[windowDescription]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @""); - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); -} - -@end diff --git a/SwitchTests/SWImageComparisonTests.m b/SwitchTests/SWImageComparisonTests.m deleted file mode 100644 index 88c2e72f..00000000 --- a/SwitchTests/SWImageComparisonTests.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// SWImageComparisonTests.m -// Switch -// -// Created by Scott Perry on 09/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 -#import - -#define TESTING 1 -#import "imageComparators.h" - - -@interface SWImageComparisonTests : XCTestCase { - CGImageRef cgImageA; - CGImageRef cgImageAA; - CGImageRef cgImageB; -} - -@property (nonatomic, strong, readonly) NSImage *imageA; -@property (nonatomic, strong, readonly) NSImage *imageAA; -@property (nonatomic, strong, readonly) NSImage *imageB; - -@end - - -@implementation SWImageComparisonTests - -- (void)setUp { - [super setUp]; - - self.continueAfterFailure = NO; - - NSString *aPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"1376" ofType:@"ar"]; - NSString *bPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"5891" ofType:@"ar"]; - - self->_imageA = [NSUnarchiver unarchiveObjectWithFile:aPath]; - self->_imageAA = [NSUnarchiver unarchiveObjectWithFile:aPath]; - self->_imageB = [NSUnarchiver unarchiveObjectWithFile:bPath]; - - self->cgImageA = [self.imageA CGImageForProposedRect:NULL context:NULL hints:NULL]; - self->cgImageAA = [self.imageAA CGImageForProposedRect:NULL context:NULL hints:NULL]; - self->cgImageB = [self.imageB CGImageForProposedRect:NULL context:NULL hints:NULL]; -} - -- (void)testTIFFComparisonDifferent { - [self measureBlock:^{ - XCTAssertTrue(imagesDifferByCachedTIFFComparison(self.imageA, self.imageB)); - }]; -} - -- (void)testTIFFComparisonSame { - [self measureBlock:^{ - XCTAssertFalse(imagesDifferByCachedTIFFComparison(self.imageA, self.imageAA)); - }]; -} - -- (void)testCGDataProviderComparisonDifferent { - [self measureBlock:^{ - XCTAssertTrue(imagesDifferByCGDataProviderComparison(self->cgImageA, self->cgImageB)); - - }]; -} - -- (void)testCGDataProviderComparisonSame { - [self measureBlock:^{ - XCTAssertFalse(imagesDifferByCGDataProviderComparison(self->cgImageA, self->cgImageAA)); - }]; -} - -- (void)testBitmapContextComparisonDifferent { - [self measureBlock:^{ - XCTAssertTrue(imagesDifferByCachedBitmapContextComparison(self.imageA, self.imageB)); - }]; -} - -- (void)testBitmapContextComparisonSame { - [self measureBlock:^{ - XCTAssertFalse(imagesDifferByCachedBitmapContextComparison(self.imageA, self.imageAA)); - }]; -} - -@end diff --git a/SwitchTests/SWIsolatorTests.m b/SwitchTests/SWIsolatorTests.m deleted file mode 100644 index 092b1741..00000000 --- a/SwitchTests/SWIsolatorTests.m +++ /dev/null @@ -1,52 +0,0 @@ -// -// SWIsolatorTests.m -// Switch -// -// Created by Scott Perry on 04/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWIsolatorTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWIsolatorTests - -- (void)testIsolatorShieldFiltering; -{ - NSDictionary *windowDescription = @{ - NNWindowLayer : @0, - NNWindowName : @"", - NNWindowMemoryUsage : @5186812, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @428, - NNWindowNumber : @121, - NNWindowOwnerName : @"Isolator", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @900, - @"Width" : @1440, - @"X" : @0, - @"Y" : @0, - }, - NNWindowAlpha : @0.731156, - }; - NSArray *infoList = @[windowDescription]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 0, @""); -} - -@end diff --git a/SwitchTests/SWMacVimTests.m b/SwitchTests/SWMacVimTests.m deleted file mode 100644 index c103b595..00000000 --- a/SwitchTests/SWMacVimTests.m +++ /dev/null @@ -1,128 +0,0 @@ -// -// SWMacVimTests.m -// Switch -// -// Created by Scott Perry on 01/11/14. -// Copyright © 2016 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWMacVimTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWMacVimTests - -// No issue #, found during development -- (void)testInsetWindows -{ - NSDictionary *window2Description = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 675, - .size.width = 584, - .origin.x = 50, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1752852, - NNWindowName : @"[No Name] - VIM1", - NNWindowNumber : @84626, - NNWindowOwnerName : @"MacVim", - NNWindowOwnerPID : @47503, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - - NSDictionary *window1Description = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - // This was gerrymandered a little bit, but it's close to the actual conditions—the window is slightly smaller than the one beneath it. - .size.height = 673, - .size.width = 582, - .origin.x = 51, - .origin.y = 23 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1643220, - NNWindowNumber : @84625, - NNWindowOwnerName : @"MacVim", - NNWindowOwnerPID : @47503, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - - NSArray *infoList = @[window1Description, window2Description]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 2, @""); - if (self.listService.windows.count == 2) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, window1Description, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, window2Description, @""); - } -} - -// No issue #, found during development -- (void)testOverlappingWindows; -{ - NSDictionary *window1Description = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 675, - .size.width = 584, - .origin.x = 50, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1752852, - NNWindowNumber : @84913, - NNWindowOwnerName : @"MacVim", - NNWindowOwnerPID : @47503, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - - NSDictionary *window2Description = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 675, - .size.width = 584, - .origin.x = 739, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1643220, - NNWindowName : @"[No Name] - VIM1", - NNWindowNumber : @84626, - NNWindowOwnerName : @"MacVim", - NNWindowOwnerPID : @47503, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - - NSArray *infoList = @[window1Description, window2Description]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 2, @""); - if (self.listService.windows.count == 2) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, window1Description, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, window2Description, @""); - } -} - -@end diff --git a/SwitchTests/SWPowerboxTests.m b/SwitchTests/SWPowerboxTests.m deleted file mode 100644 index 23ed179d..00000000 --- a/SwitchTests/SWPowerboxTests.m +++ /dev/null @@ -1,281 +0,0 @@ -// -// SWPowerboxTests.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWPowerboxTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWPowerboxTests - -// https://github.com/numist/Switch/issues/10 -- (void)testPowerboxSaveDialog_10_9_Seed; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 412, - .size.width = 640, - .origin.x = 147, - .origin.y = 47 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1110708, - NNWindowName : @"Untitled.txt", - NNWindowNumber : @33261, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @75652, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 58, - .size.width = 423, - .origin.x = 256, - .origin.y = 255 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @21788, - NNWindowName : @"", - NNWindowNumber : @33264, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @75652, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - },@{ - NNWindowAlpha : @0.8500000238418579, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 478, - .origin.x = 229, - .origin.y = 69 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @5404, - NNWindowNumber : @33270, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @75652, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - },@{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 293, - .size.width = 465, - .origin.x = 235, - .origin.y = 69 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @220020, - NNWindowName : @"Save", - NNWindowNumber : @33265, - NNWindowOwnerName : @"com.apple.security.pboxd", - NNWindowOwnerPID : @75654, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - windowDescription - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 1, @"Powerbox save dialog wasn't grouped correctly"); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @"Main window for group was not identified correctly"); - } -} - -- (void)testPowerboxSaveDialog_10_9_0; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 412, - .size.width = 640, - .origin.x = 259, - .origin.y = 79 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1101076, - NNWindowName : @"Untitled.txt", - NNWindowNumber : @48043, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - - NSArray *infoList = @[ - @{ - NNWindowAlpha : @0.8500000238418579, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 503, - .origin.x = 328, - .origin.y = 101 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @21756, - NNWindowNumber : @48051, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 58, - .size.width = 463, - .origin.x = 347, - .origin.y = 313 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @111868, - NNWindowName : @"", - NNWindowNumber : @48050, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 489, - .origin.x = 334, - .origin.y = 101 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @636156, - NNWindowName : @"Save", - NNWindowNumber : @48045, - NNWindowOwnerName : @"com.apple.appkit.xpc.openAndSav", - NNWindowOwnerPID : @82769, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 490, - .origin.x = 334, - .origin.y = 101 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @806740, - NNWindowName : @"Save", - NNWindowNumber : @48046, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - windowDescription - ]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @"Powerbox save dialog wasn't grouped correctly"); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @"Main window for group was not identified correctly"); - } -} - -- (void)testMacVimSaveDialog; -{ - NSDictionary *windowDescription = @{ - NNWindowLayer : @0, - NNWindowName : @"[No Name] + - VIM", - NNWindowMemoryUsage : @1752852, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @47503, - NNWindowNumber : @90316, - NNWindowOwnerName : @"MacVim", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @675, - @"Width" : @584, - @"X" : @50, - @"Y" : @22, - }, - NNWindowAlpha : @1.000000, - }; - NSArray *infoList = @[ - @{ - NNWindowLayer : @0, - NNWindowMemoryUsage : @21756, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @47503, - NNWindowNumber : @90319, - NNWindowOwnerName : @"MacVim", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @10, - @"Width" : @437, - @"X" : @124, - @"Y" : @44, - }, - NNWindowAlpha : @0.850000, - }, - @{ - NNWindowLayer : @0, - NNWindowMemoryUsage : @305172, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @47503, - NNWindowNumber : @90318, - NNWindowOwnerName : @"MacVim", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @166, - @"Width" : @424, - @"X" : @130, - @"Y" : @44, - }, - NNWindowAlpha : @1.000000, - }, - windowDescription, - ]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @"Save dialog wasn't grouped correctly"); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @"Main window for group was not identified correctly"); - } -} - -@end diff --git a/SwitchTests/SWSafariTests.m b/SwitchTests/SWSafariTests.m deleted file mode 100644 index dc02c0dc..00000000 --- a/SwitchTests/SWSafariTests.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// SWSafariTests.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWSafariTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWSafariTests - -// https://github.com/numist/Switch/issues/8 -- (void)testSearchMatches; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 706, - .size.width = 1366, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @316020, - NNWindowName : @"mikeash.com: Performance Comparisons of Common Operations", - NNWindowNumber : @31886, - NNWindowOwnerName : @"Safari", - NNWindowOwnerPID : @164, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 26, - .size.width = 224, - .origin.x = 50, - .origin.y = 413 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @29980, - NNWindowName : @"", - NNWindowNumber : @32161, - NNWindowOwnerName : @"Safari", - NNWindowOwnerPID : @164, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - windowDescription - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 1, @"Safari was incorrectly grouped"); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); - } -} - -@end diff --git a/SwitchTests/SWSanityTests.m b/SwitchTests/SWSanityTests.m deleted file mode 100644 index 42f12130..00000000 --- a/SwitchTests/SWSanityTests.m +++ /dev/null @@ -1,502 +0,0 @@ -// -// SWSanityTests.m -// Switch -// -// Created by Scott Perry on 05/13/16. -// Copyright © 2016 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWSanityTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWSanityTests - -- (void)test1463163574; -{ - // Raw list: - NSArray *infoList = @[ - @{ - NNWindowLayer : @3000, - NNWindowAlpha : @0.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @276, - NNWindowNumber : @722, - NNWindowOwnerName : @"universalaccessd", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @90, - @"Width" : @40, - @"X" : @1310, - @"Y" : @58, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @5280, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @41791, - NNWindowNumber : @16565, - NNWindowOwnerName : @"Switch", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @30, - @"X" : @1000, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @5280, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @280, - NNWindowNumber : @12, - NNWindowOwnerName : @"Little Snitch Agent", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @28, - @"X" : @947, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @5280, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @636, - NNWindowNumber : @107, - NNWindowOwnerName : @"Cloak", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @25, - @"X" : @975, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1184, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @635, - NNWindowNumber : @112, - NNWindowOwnerName : @"Alfred 2", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @0, - @"Width" : @0, - @"X" : @1000, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1184, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @58258, - NNWindowNumber : @8582, - NNWindowOwnerName : @"CrashPlan menu bar", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @0, - @"Width" : @0, - @"X" : @1030, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1184, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @41622, - NNWindowNumber : @5887, - NNWindowOwnerName : @"Dropbox", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @0, - @"Width" : @0, - @"X" : @1030, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @25760, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @336, - NNWindowNumber : @33, - NNWindowOwnerName : @"SystemUIServer", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @230, - @"X" : @1030, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @5280, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @632, - NNWindowNumber : @103, - NNWindowOwnerName : @"Bartender", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @24, - @"X" : @1260, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @9376, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @343, - NNWindowNumber : @43, - NNWindowOwnerName : @"Spotlight", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @36, - @"X" : @1284, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @25, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @9376, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @336, - NNWindowNumber : @29, - NNWindowOwnerName : @"SystemUIServer", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @46, - @"X" : @1320, - @"Y" : @0, - }, - NNWindowName : @"", - }, - @{ - NNWindowOwnerPID : @197, - NNWindowOwnerName : @"Window Server", - NNWindowAlpha : @1.000000, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - NNWindowNumber : @19, - NNWindowName : @"Menubar", - NNWindowMemoryUsage : @124064, - NNWindowBounds : @{ - @"Height" : @22, - @"Width" : @1366, - @"X" : @0, - @"Y" : @0, - }, - NNWindowLayer : @24, - NNWindowIsOnscreen : @1, -// NNWindowBackingLocationVideoMemory : @1, - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @2360480, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @46473, - NNWindowNumber : @7814, - NNWindowOwnerName : @"iTerm2", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @647, - @"X" : @721, - @"Y" : @23, - }, - NNWindowName : @"1. ~/C/Switch (fish)", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @314, - NNWindowNumber : @16272, - NNWindowOwnerName : @"Safari", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @740, - @"Width" : @1165, - @"X" : @145, - @"Y" : @23, - }, - NNWindowName : @"man7.org: How much do __builtin_expect(), likely(), and unlikely() improve performance?", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @314, - NNWindowNumber : @10096, - NNWindowOwnerName : @"Safari", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1295, - @"X" : @42, - @"Y" : @23, - }, - NNWindowName : @"numist/Switch - Travis CI", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @58213, - NNWindowNumber : @14164, - NNWindowOwnerName : @"Finder", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @336, - @"Width" : @588, - @"X" : @730, - @"Y" : @216, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @3036320, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @774, - NNWindowNumber : @14030, - NNWindowOwnerName : @"TextMate", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1009, - @"X" : @61, - @"Y" : @23, - }, - NNWindowName : @"untitled (stdin)", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @2360480, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @73779, - NNWindowNumber : @9616, - NNWindowOwnerName : @"Activity Monitor", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @737, - @"Width" : @720, - @"X" : @1, - @"Y" : @23, - }, - NNWindowName : @"Activity Monitor (All Processes)", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @3036320, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @774, - NNWindowNumber : @9425, - NNWindowOwnerName : @"TextMate", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1009, - @"X" : @355, - @"Y" : @23, - }, - NNWindowName : @"notes.txt — keyboardio", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @4080800, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @17923, - NNWindowNumber : @11797, - NNWindowOwnerName : @"Sublime Text 2", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1366, - @"X" : @0, - @"Y" : @23, - }, - NNWindowName : @"Rakefile — Switch", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @3933344, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @50667, - NNWindowNumber : @7772, - NNWindowOwnerName : @"Sublime Text", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1067, - @"X" : @1, - @"Y" : @23, - }, - NNWindowName : @"Makefile — model01_keyscanner_firmware/firmware — keyboardio", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @60716, - NNWindowNumber : @9484, - NNWindowOwnerName : @"App Store", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @688, - @"Width" : @1000, - @"X" : @14, - @"Y" : @36, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @50514, - NNWindowNumber : @7412, - NNWindowOwnerName : @"Preview", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @702, - @"Width" : @1330, - @"X" : @25, - @"Y" : @34, - }, - NNWindowName : @"model01-left-hand.pdf (1 page)", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @50514, - NNWindowNumber : @7416, - NNWindowOwnerName : @"Preview", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @702, - @"Width" : @1330, - @"X" : @10, - @"Y" : @57, - }, - NNWindowName : @"model01-right-hand.pdf (1 page)", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @3986592, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @54478, - NNWindowNumber : @7980, - NNWindowOwnerName : @"GitHub Desktop", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @741, - @"Width" : @1341, - @"X" : @25, - @"Y" : @23, - }, - NNWindowName : @"keyboardio/HID", - }, - ]; - - /* Sorted window group list: - {( - <7814 (1. ~/C/Switch (fish)), 1 windows>, - <16272 (man7.org: How much do __builtin_expect(), likely(), and unlikely() improve performance?), 1 windows>, - <10096 (numist/Switch - Travis CI), 1 windows>, - <14030 (untitled (stdin)), 1 windows>, - <9616 (Activity Monitor (All Processes)), 1 windows>, - <9425 (notes.txt — keyboardio), 1 windows>, - <11797 (Rakefile — Switch), 1 windows>, - <7772 (Makefile — model01_keyscanner_firmware/firmware — keyboardio), 1 windows>, - <9484 (App Store), 1 windows>, - <7412 (model01-left-hand.pdf (1 page)), 1 windows>, - <7416 (model01-right-hand.pdf (1 page)), 1 windows>, - <7980 (keyboardio/HID), 1 windows> - )} */ - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 12, @""); -} - -@end diff --git a/SwitchTests/SWScrollControlTests.m b/SwitchTests/SWScrollControlTests.m deleted file mode 100644 index 6db1a109..00000000 --- a/SwitchTests/SWScrollControlTests.m +++ /dev/null @@ -1,155 +0,0 @@ -// -// SWScrollControlTests.m -// Switch -// -// Created by Scott Perry on 09/30/14. -// Copyright © 2014 Scott Perry. -// -// 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 -#import -#import "SWScrollControl.h" - - -@interface SWScrollControlTests : XCTestCase -@end - - -@implementation SWScrollControlTests - -- (void)testBasic { - NSInteger const threshold = 50; - __block NSInteger units = 0; - SWScrollControl *scroller = [[SWScrollControl alloc] initWithThreshold:threshold incHandler:^{ - ++units; - } decHandler:^{ - --units; - }]; - - void (^verifyFeed)(NSInteger feed) = ^(NSInteger feed){ - units = 0; - [scroller feed:feed]; - XCTAssertEqual(feed / threshold, units); - }; - - verifyFeed(threshold); - verifyFeed(-threshold); - verifyFeed(threshold * 20); - verifyFeed(-threshold * 20); -} - -- (void)testNegativeThreshold; -{ - NSInteger const threshold = 50; - __block NSInteger units = 0; - SWScrollControl *scroller = [[SWScrollControl alloc] initWithThreshold:-threshold incHandler:^{ - ++units; - } decHandler:^{ - --units; - }]; - - void (^verifyFeed)(NSInteger feed) = ^(NSInteger feed){ - units = 0; - [scroller reset]; - [scroller feed:feed]; - XCTAssertEqual(feed / threshold, units); - }; - - verifyFeed(threshold); - verifyFeed(-threshold); - verifyFeed(threshold * 20); - verifyFeed(-threshold * 20); -} - -- (void)testMaximumThreshold; -{ - NSInteger const threshold = NSIntegerMax / 2; - __block NSInteger units = 0; - SWScrollControl *scroller = [[SWScrollControl alloc] initWithThreshold:threshold incHandler:^{ - ++units; - } decHandler:^{ - --units; - }]; - - units = 0; - [scroller reset]; - [scroller feed:threshold]; - XCTAssertEqual(1, units); - - units = 0; - [scroller reset]; - [scroller feed:(threshold / 2)]; - // Attempt to force an overflow - [scroller feed:(threshold / 3) * 2]; - XCTAssertEqual(1, units); - - units = 0; - [scroller reset]; - [scroller feed:-(threshold / 2)]; - [scroller feed:-(threshold % 2)]; - // Attempt to force an underflow - [scroller feed:-(threshold / 3) * 2]; - XCTAssertEqual(-1, units); - - units = 0; - [scroller reset]; - // Attempt to force an overflow - [scroller feed:threshold - 1]; - [scroller feed:NSIntegerMax]; - XCTAssertEqual((NSIntegerMax / threshold) + 1, units); - - units = 0; - [scroller reset]; - // Attempt to force an underflow - [scroller feed:-threshold - 1]; - [scroller feed:NSIntegerMin]; - XCTAssertEqual((NSIntegerMin / threshold) - 1, units); - -} - -- (void)testUnrealisticThreshold; -{ - NSInteger const threshold = NSIntegerMax; - __block NSInteger units = 0; - SWScrollControl *scroller = [[SWScrollControl alloc] initWithThreshold:threshold incHandler:^{ - ++units; - } decHandler:^{ - --units; - }]; - - // For now, until it matters enough that this work at that kind of scale—it's not worth writing all that code :/ - XCTAssertNil(scroller); -} - -- (void)testAccumulatorOverflowIncrementing; -{ - NSInteger const threshold = 50; - __block NSInteger units = 0; - SWScrollControl *scroller = [[SWScrollControl alloc] initWithThreshold:threshold incHandler:^{ - ++units; - } decHandler:^{ - --units; - }]; - - units = 0; - [scroller reset]; - for (int i = 0; i < 10; i++) { - [scroller feed:15]; - } - XCTAssertEqual(3, units); - - units = 0; - [scroller reset]; - for (int i = 0; i < 10; i++) { - [scroller feed:-15]; - } - XCTAssertEqual(-3, units); -} - -@end diff --git a/SwitchTests/SWSelectorTests.m b/SwitchTests/SWSelectorTests.m deleted file mode 100644 index bf55299e..00000000 --- a/SwitchTests/SWSelectorTests.m +++ /dev/null @@ -1,392 +0,0 @@ -// -// SWSelectorTests.m -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 -#import "SWSelector.h" - - -@interface SWSelectorTests : XCTestCase - -@property (nonatomic, strong, readonly) NSOrderedSet *list0; -@property (nonatomic, strong, readonly) NSOrderedSet *list0123; -@property (nonatomic, strong, readonly) NSOrderedSet *list321; - -@end - - -@implementation SWSelectorTests - -- (void)setUp -{ - [super setUp]; - - self->_list0 = [NSOrderedSet orderedSetWithArray:@[@(0)]]; - self->_list0123 = [NSOrderedSet orderedSetWithArray:@[@(0), @(1), @(2), @(3)]]; - self->_list321 = [NSOrderedSet orderedSetWithArray:@[@(3), @(2), @(1)]]; -} - -- (void)tearDown -{ - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - -#define updateWithEmptyAndCheck(_selector_) \ - _selector_ = [(_selector_) updateWithWindowList:[NSOrderedSet new]]; \ - XCTAssertNil((_selector_).selectedWindow); \ - XCTAssertEqual((_selector_).selectedIndex, NSNotFound); - -- (void)testEmptySet; -{ - SWSelector *selector = [SWSelector new]; - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptySetIncrement; -{ - SWSelector *selector = [[SWSelector new] increment]; - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptySetIncrementWithoutWrapping; -{ - SWSelector *selector = [[SWSelector new] incrementWithoutWrapping]; - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptySetDecrement; -{ - SWSelector *selector = [[SWSelector new] decrement]; - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptySetDecrementWithoutWrapping; -{ - SWSelector *selector = [[SWSelector new] decrementWithoutWrapping]; - updateWithEmptyAndCheck(selector); -} - -- (void)testEmpty; -{ - SWSelector *selector = [SWSelector new]; - XCTAssertNotNil(selector); - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, 0); - - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptyIncrement; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector increment]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, 1); - - selector = [selector increment]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, 2); - - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptyIncrementWithoutWrapping; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector incrementWithoutWrapping]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, 1); - - selector = [selector incrementWithoutWrapping]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, 2); - - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptyDecrement; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector decrement]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, -1); - - selector = [selector decrement]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, -2); - - updateWithEmptyAndCheck(selector); -} - -- (void)testEmptyDecrementWithoutWrapping; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector decrementWithoutWrapping]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, -1); - - selector = [selector decrementWithoutWrapping]; - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, -2); - - updateWithEmptyAndCheck(selector); -} - -- (void)testSet; -{ - SWSelector *selector = [[SWSelector new] updateWithWindowList:self.list0123]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); -} - -- (void)testIncrement; -{ - SWSelector *selector = [[SWSelector new] updateWithWindowList:self.list0123]; - - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[1]); - XCTAssertEqual(selector.selectedIndex, 1); - - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[2]); - XCTAssertEqual(selector.selectedIndex, 2); - - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); - - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); - - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[1]); - XCTAssertEqual(selector.selectedIndex, 1); - - updateWithEmptyAndCheck(selector); -} - -- (void)testIncrementWithoutWrapping; -{ - SWSelector *selector = [[SWSelector new] updateWithWindowList:self.list0123]; - - selector = [selector incrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[1]); - XCTAssertEqual(selector.selectedIndex, 1); - - selector = [selector incrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[2]); - XCTAssertEqual(selector.selectedIndex, 2); - - selector = [selector incrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); - - selector = [selector incrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); - - updateWithEmptyAndCheck(selector); -} - -- (void)testDecrement; -{ - SWSelector *selector = [[SWSelector new] updateWithWindowList:self.list0123]; - - selector = [selector decrement]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); - - selector = [selector decrement]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[2]); - XCTAssertEqual(selector.selectedIndex, 2); - - selector = [selector decrement]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[1]); - XCTAssertEqual(selector.selectedIndex, 1); - - selector = [selector decrement]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); - - updateWithEmptyAndCheck(selector); -} - -- (void)testDecrementWithoutWrapping; -{ - SWSelector *selector = [[SWSelector new] updateWithWindowList:self.list0123]; - - selector = [selector decrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); - - updateWithEmptyAndCheck(selector); -} - -- (void)testDecrementBeforeUpdate; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector decrement]; - selector = [selector updateWithWindowList:self.list0123]; - - XCTAssertEqualObjects(selector.selectedWindow, self.list0123.lastObject); - XCTAssertEqual(selector.selectedIndex, (self.list0123.count - 1)); - - updateWithEmptyAndCheck(selector); -} - -- (void)testIncrementBeforeUpdate; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector increment]; - selector = [selector increment]; - selector = [selector increment]; - selector = [selector increment]; - selector = [selector updateWithWindowList:self.list0123]; - - XCTAssertEqualObjects(selector.selectedWindow, self.list0123.firstObject); - XCTAssertEqual(selector.selectedIndex, 0); - - updateWithEmptyAndCheck(selector); -} - -- (void)testUpdateWithSmallerList; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector increment]; - selector = [selector increment]; - selector = [selector updateWithWindowList:self.list0123]; - selector = [selector updateWithWindowList:self.list0]; - - XCTAssertEqualObjects(selector.selectedWindow, self.list0123.firstObject); - XCTAssertEqual(selector.selectedIndex, 0); - - updateWithEmptyAndCheck(selector); -} - -- (void)testUpdateWithSharedSelectedObject; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector increment]; - selector = [selector increment]; - - selector = [selector updateWithWindowList:self.list0123]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[2]); - - selector = [selector updateWithWindowList:self.list321]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[2]); - - XCTAssertEqual(selector.selectedIndex, 1); - - updateWithEmptyAndCheck(selector); -} - -- (void)testDeselectionWithUpdateToNotFoundIndex; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:self.list0123]; - - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); - - selector = [selector selectIndex:NSNotFound]; - - XCTAssertNil(selector.selectedWindow); - XCTAssertEqual(selector.selectedIndex, NSNotFound); -} - -- (void)testIncrementAfterDeselection; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:self.list0123]; - selector = [selector selectIndex:NSNotFound]; - selector = [selector increment]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); -} - -- (void)testIncrementAfterDeselectionNoWrap; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:self.list0123]; - selector = [selector selectIndex:NSNotFound]; - selector = [selector incrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[0]); - XCTAssertEqual(selector.selectedIndex, 0); -} - -- (void)testDecrementAfterDeselection; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:self.list0123]; - selector = [selector selectIndex:NSNotFound]; - selector = [selector decrement]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); -} - -- (void)testDecrementAfterDeselectionNoWrap; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:self.list0123]; - selector = [selector selectIndex:NSNotFound]; - selector = [selector decrementWithoutWrapping]; - XCTAssertEqualObjects(selector.selectedWindow, self.list0123[3]); - XCTAssertEqual(selector.selectedIndex, 3); -} - -- (void)testNoSelectedIndex; -{ - SWSelector *selector = [SWSelector new]; - NSUInteger selectedUIndex = [selector selectedUIndex]; - NSInteger selectedIndex = [selector selectedIndex]; - XCTAssertEqual(selectedUIndex, 0); - XCTAssertEqual(selectedIndex, 0); -} - -- (void)testEmptyList; -{ - SWSelector *selector = [SWSelector new]; - selector = [selector updateWithWindowList:[NSOrderedSet orderedSet]]; - selector = [selector increment]; - XCTAssertEqual(selector.selectedIndex, NSNotFound); - selector = [selector decrement]; - XCTAssertEqual(selector.selectedIndex, NSNotFound); -} - -- (void)testSelectInvalidIndex; -{ - SWSelector *selector = [SWSelector new]; - - selector = [selector decrement]; - XCTAssertThrows(selector.selectedUIndex); - - selector = [selector updateWithWindowList:[NSOrderedSet orderedSet]]; - XCTAssertThrows([selector selectIndex:0]); - selector = [selector updateWithWindowList:self.list0123]; - XCTAssertThrows([selector selectIndex:-1]); - XCTAssertThrows([selector selectIndex:50]); - selector = [selector selectIndex:NSNotFound]; - XCTAssertEqual(selector.selectedIndex, NSNotFound); -} - -@end diff --git a/SwitchTests/SWSheetTests.m b/SwitchTests/SWSheetTests.m deleted file mode 100644 index a42d6bfd..00000000 --- a/SwitchTests/SWSheetTests.m +++ /dev/null @@ -1,197 +0,0 @@ -// -// SWSheetTests.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWSheetTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWSheetTests - -// https://github.com/numist/Switch/issues/9 -- (void)testSheetFiltering; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 706, - .size.width = 1000, - .origin.x = 181, - .origin.y = 58 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2941236, - NNWindowName : @"Instruments1", - NNWindowNumber : @33085, - NNWindowOwnerName : @"Instruments", - NNWindowOwnerPID : @75257, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[ - @{ - NNWindowAlpha : @(0.8500000238418579), - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 469, - .origin.x = 447, - .origin.y = 136 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @5404, - NNWindowNumber : @33143, - NNWindowOwnerName : @"Instruments", - NNWindowOwnerPID : @75257, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - },@{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 131, - .size.width = 456, - .origin.x = 453, - .origin.y = 136 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @61300, - NNWindowNumber : @33142, - NNWindowOwnerName : @"Instruments", - NNWindowOwnerPID : @75257, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - windowDescription - ]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @""); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); - } -} - -// https://github.com/numist/Switch/issues/55 -- (void)testTextEditSaveDialog; -{ - NSDictionary *windowDescription = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 412, - .size.width = 640, - .origin.x = 147, - .origin.y = 47 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1220180, - NNWindowName : @"Untitled.txt", - NNWindowNumber : @74761, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[ - @{ - NNWindowAlpha : @0.8500000238418579, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 503, - .origin.x = 216, - .origin.y = 69 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @21756, - NNWindowNumber : @74771, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 58, - .size.width = 463, - .origin.x = 235, - .origin.y = 281 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @111868, - NNWindowName : @"", - NNWindowNumber : @74770, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 489, - .origin.x = 222, - .origin.y = 69 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @636156, - NNWindowName : @"Save", - NNWindowNumber : @74764, - NNWindowOwnerName : @"com.apple.appkit.xpc.openAndSav", - NNWindowOwnerPID : @82769, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 490, - .origin.x = 222, - .origin.y = 69 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @806740, - NNWindowName : @"Save", - NNWindowNumber : @74765, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @82763, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - windowDescription, - ]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @""); - if (self.listService.windows.count == 1) { - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); - } -} - -@end diff --git a/SwitchTests/SWStateMachineTests.m b/SwitchTests/SWStateMachineTests.m deleted file mode 100644 index ae23578d..00000000 --- a/SwitchTests/SWStateMachineTests.m +++ /dev/null @@ -1,1652 +0,0 @@ -// -// SWStateMachineTests.m -// Switch -// -// Created by Scott Perry on 09/26/14. -// Copyright © 2014 Scott Perry. -// -// 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 -#import - -#import "SWStateMachine.h" -#import "SWWindowFilteringTests.h" -#import "SWWindow.h" -#import "SWWindowListService.h" -#import "SWTestApplication.h" - - -#ifndef MAX_TIME_PER_TEST -#define MAX_TIME_PER_TEST 1.0 -#endif - - -@interface SWStateMachineTests : XCTestCase - -+ (NSMutableOrderedSet *)windowList; - -@property (nonatomic, readonly, strong) SWStateMachine *stateMachineUnderTest; -@property (nonatomic, readonly, strong) id stateMachineDelegateMock; -@property (nonatomic, readwrite, assign, getter=isNiceMock) _Bool niceMock; - -@property (nonatomic, readonly, assign) unsigned monkeyCount; - -@end - - -static uint32_t (^die)(uint32_t sides) = ^(uint32_t sides){ - return arc4random_uniform(sides); -}; - -static _Bool (^coin)() = ^{ - return (_Bool)die(2); -}; - -static NSMutableOrderedSet *(^rwgs)() = ^{ - NSMutableOrderedSet *windowList = [SWStateMachineTests windowList]; - NSUInteger numWindows = coin(windowList.count + 1); - - if (numWindows == windowList.count) { return (NSMutableOrderedSet *)nil; } - - while (numWindows > windowList.count) { - [windowList removeObjectAtIndex:die((uint32_t)windowList.count)]; - } - - return (NSMutableOrderedSet *)windowList; -}; - -static SWWindow *(^rwg)() = ^{ - return [rwgs() objectAtIndex:0]; -}; - - -@implementation SWStateMachineTests - -- (void)setUp -{ - [super setUp]; - - self.niceMock = false; - self->_stateMachineDelegateMock = OCMStrictProtocolMock(@protocol(SWStateMachineDelegate)); - self->_stateMachineUnderTest = [SWStateMachine stateMachineWithDelegate:self.stateMachineDelegateMock]; - - self->_monkeyCount = 15; - - self.continueAfterFailure = NO; -} - -- (void)tearDown -{ - self->_stateMachineDelegateMock = nil; - self->_stateMachineUnderTest = nil; - - [super tearDown]; -} - -+ (NSMutableOrderedSet *)windowList; -{ - NSArray *windowInfoList = @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 768, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @101, - NNWindowMemoryUsage : @4228348, - NNWindowName : @"", - NNWindowNumber : @1357, - NNWindowOwnerName : @"Switch", - NNWindowOwnerPID : @8392, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 30, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @1317, - NNWindowOwnerName : @"Switch", - NNWindowOwnerPID : @8392, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @1286, - NNWindowOwnerName : @"Location Menu", - NNWindowOwnerPID : @8287, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @45, - NNWindowOwnerName : @"Dropbox", - NNWindowOwnerPID : @222, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @51, - NNWindowOwnerName : @"Alfred 2", - NNWindowOwnerPID : @226, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @62, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @50, - NNWindowOwnerName : @"CrashPlan menu bar", - NNWindowOwnerPID : @227, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 24, - .origin.x = 1342, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @44, - NNWindowOwnerName : @"Bartender", - NNWindowOwnerPID : @219, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @43, - NNWindowOwnerName : @"Flux", - NNWindowOwnerPID : @217, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @61, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @60, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @46, - NNWindowOwnerName : @"1Password mini", - NNWindowOwnerPID : @190, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @41, - NNWindowOwnerName : @"Satellite Eyes", - NNWindowOwnerPID : @242, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 69, - .origin.x = 1191, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @9468, - NNWindowName : @"", - NNWindowNumber : @59, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 30, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @58, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @57, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 52, - .origin.x = 1290, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @9468, - NNWindowName : @"", - NNWindowNumber : @56, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1342, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @25, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @39, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @0, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowNumber : @40, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1366, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @38, - NNWindowOwnerName : @"Notification Center", - NNWindowOwnerPID : @188, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @24, - NNWindowMemoryUsage : @124156, - NNWindowName : @"Menubar", - NNWindowNumber : @12, - NNWindowOwnerName : @"Window Server", - NNWindowOwnerPID : @87, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 43, - .size.width = 1261, - .origin.x = 53, - .origin.y = 798 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @19, - NNWindowMemoryUsage : @222460, - NNWindowName : @"Magic Mirror", - NNWindowNumber : @1351, - NNWindowOwnerName : @"Dock", - NNWindowOwnerPID : @162, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 742, - .size.width = 1366, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @4416340, - NNWindowName : @"SWWindowListService.m", - NNWindowNumber : @91, - NNWindowOwnerName : @"Xcode", - NNWindowOwnerPID : @415, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 317, - .size.width = 744, - .origin.x = 45, - .origin.y = 34 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @46916, - NNWindowName : @"Wreck-It Ralph", - NNWindowNumber : @1355, - NNWindowOwnerName : @"QuickTime Player", - NNWindowOwnerPID : @8438, - NNWindowSharingState : @1, - NNWindowStoreType : @1, - }, - @{ - NNWindowAlpha : @"0.8500000238418579", - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 503, - .origin.x = 534, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @21756, - NNWindowNumber : @1342, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 58, - .size.width = 463, - .origin.x = 553, - .origin.y = 272 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @111868, - NNWindowName : @"", - NNWindowNumber : @1341, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 489, - .origin.x = 540, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @636156, - NNWindowName : @"Save", - NNWindowNumber : @1336, - NNWindowOwnerName : @"com.apple.appkit.xpc.openAndSav", - NNWindowOwnerPID : @8436, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 490, - .origin.x = 540, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @806740, - NNWindowName : @"Save", - NNWindowNumber : @1337, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 702, - .size.width = 858, - .origin.x = 356, - .origin.y = 38 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2484948, - NNWindowName : @"Untitled.txt", - NNWindowNumber : @1334, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 698, - .size.width = 1279, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @3661844, - NNWindowName : @"untitled 16", - NNWindowNumber : @754, - NNWindowOwnerName : @"TextMate", - NNWindowOwnerPID : @5985, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 698, - .size.width = 1365, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @3932116, - NNWindowName : @"untitled 12", - NNWindowNumber : @755, - NNWindowOwnerName : @"TextMate", - NNWindowOwnerPID : @5985, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 422, - .size.width = 1012, - .origin.x = 73, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2167060, - NNWindowName : @"~/C/Switch — fish", - NNWindowNumber : @626, - NNWindowOwnerName : @"Terminal", - NNWindowOwnerPID : @3579, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - } - ]; - - NSOrderedSet *windowObjectList = [SWWindowListService filterInfoDictionariesToWindowObjects:windowInfoList]; - NSOrderedSet *windowList = [SWWindowListService filterWindowObjectsToWindowGroups:windowObjectList]; - - if (!windowList) { abort(); } - - NSMutableOrderedSet *mutableList = [windowList isKindOfClass:[NSMutableOrderedSet class]] ? windowList : [windowList mutableCopy]; - SWWindow *firstWindow = [windowList firstObject]; - [mutableList removeObjectAtIndex:0]; - [((SWTestApplication *)firstWindow.application) setActive:YES]; - [mutableList insertObject:firstWindow atIndex:0]; - - if(![((SWWindow *)[mutableList objectAtIndex:0]).application isActiveApplication]) { abort(); } - - return mutableList; -} - -- (void)setNiceMock:(_Bool)niceMock; -{ - if (niceMock == self.niceMock) { return; } - - self->_stateMachineDelegateMock = niceMock - ? OCMProtocolMock(@protocol(SWStateMachineDelegate)) - : OCMStrictProtocolMock(@protocol(SWStateMachineDelegate)); - self->_stateMachineUnderTest = [SWStateMachine stateMachineWithDelegate:self.stateMachineDelegateMock]; -} - -- (void)stateMachineInvokeWithDirection:(SWIncrementDirection)direction; -{ - if (self.stateMachineUnderTest.active) { - [self.stateMachineUnderTest incrementWithInvoke:true direction:direction isRepeating:false]; - return; - } - - // State machine is inactive, everything should be off. - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - - XCTAssertFalse(self.stateMachineUnderTest.interfaceVisible); - XCTAssertFalse(self.stateMachineUnderTest.windowListUpdates); - XCTAssertFalse(self.stateMachineUnderTest.displayTimer); - XCTAssertFalse(self.stateMachineUnderTest.pendingSwitch); - XCTAssertFalse(self.stateMachineUnderTest.active); - - [self stateMachineExpectDisplayTimerStart:^{ - [self stateMachineExpectWindowListUpdates:true block:^{ - [self.stateMachineUnderTest incrementWithInvoke:true direction:direction isRepeating:false]; - }]; - }]; - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - - XCTAssertTrue(self.stateMachineUnderTest.active); - XCTAssertTrue(self.stateMachineUnderTest.invoked); - XCTAssertTrue(self.stateMachineUnderTest.windowListUpdates); - XCTAssertTrue(self.stateMachineUnderTest.displayTimer); - - XCTAssertFalse(self.stateMachineUnderTest.pendingSwitch); -} - -- (void)stateMachineShowUIWithWindowList:(NSOrderedSet *)windowList; -{ - if (!self.stateMachineUnderTest.active) { - XCTFail(@"Call stateMachineInvokeWithDirection: before %@!", NSStringFromSelector(_cmd)); - } - - if (self.stateMachineUnderTest.interfaceVisible) { return; } - - [self _windowList:windowList]; - - [self stateMachineExpectShowInterface:true block:^{ - [self _timer]; - }]; -} - -- (void)stateMachineInRaiseStateMonkey:(unsigned)iterations; -{ - @autoreleasepool { - NSArray *monkeys = @[ - ^{ - // -> windowlist (with selectedwindow not first/active) -> - NSMutableOrderedSet *windowList = rwgs(); - // If selectedWindow is firstWindow, make inactiveWindow and updateWindows - if ([[windowList objectAtIndex:0] isEqual:self.stateMachineUnderTest.selectedWindow]) { - SWWindow *tmp = windowList[0]; - [windowList removeObjectAtIndex:0]; - ((SWTestApplication *)tmp.application).active = false; - [windowList insertObject:tmp atIndex:0]; - } - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:windowList]); - }, - ^{ - // -> hotkey(invoking or no, inc or dec)+ -> (windowlist(any) | hotkey(invoking or no, inc or dec))* -> keyEventModifierReleased (selectedwindow not first/active) -> raise -> - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]); - - for (unsigned i = die(self.monkeyCount); i < self.monkeyCount; ++i) { - if (coin()) { - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]); - } else { - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:rwgs()]); - } - } - - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:[SWStateMachineTests windowList]]); - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]); - - XCTAssertNotNil(self.stateMachineUnderTest.selectedWindow); - - // If selectedWindow is firstWindow, make inactiveWindow and updateWindows - _Bool selectedWindowIsFirst = [self.stateMachineUnderTest.windowList indexOfObject:self.stateMachineUnderTest.selectedWindow] == 0; - _Bool selectedWindowIsActive = [self.stateMachineUnderTest.selectedWindow.application isActiveApplication]; - if (selectedWindowIsActive && selectedWindowIsFirst) { - NSMutableOrderedSet *windowList = [SWStateMachineTests windowList]; - SWWindow *tmp = windowList[0]; - [windowList removeObjectAtIndex:0]; - ((SWTestApplication *)tmp.application).active = false; - [windowList insertObject:tmp atIndex:0]; - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:windowList]); - } - - OCMExpect([self.stateMachineDelegateMock stateMachine:self.stateMachineUnderTest wantsWindowRaised:self.stateMachineUnderTest.selectedWindow]); - XCTAssertNoThrow([self.stateMachineUnderTest endInvocation]); - [self mockVerify]; - } - ]; - - ((dispatch_block_t)[monkeys objectAtIndex:die((uint32_t)monkeys.count)])(); - - if (--iterations) { - [self stateMachineInRaiseStateMonkey:iterations]; - } - } -} - -- (void)stateMachineExpectDisplayTimerStart:(dispatch_block_t)block; -{ - OCMExpect([self.stateMachineDelegateMock stateMachineWantsDisplayTimerStarted:self.stateMachineUnderTest]); - - XCTAssertNoThrow(block()); - - [self mockVerify]; -} - -- (void)stateMachineExpectDisplayTimerInvalidate:(dispatch_block_t)block; -{ - OCMExpect([self.stateMachineDelegateMock stateMachineWantsDisplayTimerInvalidated:self.stateMachineUnderTest]); - - XCTAssertNoThrow(block()); - - [self mockVerify]; -} - -- (void)stateMachineExpectRaise:(dispatch_block_t)block; -{ - SWWindow *selectedWindow = self.stateMachineUnderTest.selectedWindow ?: [OCMArg any]; - - OCMExpect([self.stateMachineDelegateMock stateMachine:self.stateMachineUnderTest wantsWindowRaised:selectedWindow]); - - XCTAssertNoThrow(block()); - - [self mockVerify]; -} - -- (void)stateMachineExpectClose:(dispatch_block_t)block; -{ - OCMExpect([self.stateMachineDelegateMock stateMachine:self.stateMachineUnderTest wantsWindowClosed:self.stateMachineUnderTest.selectedWindow]); - - XCTAssertNoThrow(block()); - - [self mockVerify]; -} - -- (void)stateMachineExpectShowInterface:(_Bool)showInterface block:(dispatch_block_t)block; -{ - XCTAssertNoThrow(block()); - - XCTAssertEqual(self.stateMachineUnderTest.interfaceVisible, showInterface); - [self mockVerify]; -} - -- (void)stateMachineExpectWindowListUpdates:(_Bool)updateWindowList block:(dispatch_block_t)block; -{ - XCTAssertNoThrow(block()); - - XCTAssertEqual(self.stateMachineUnderTest.windowListUpdates, updateWindowList); - [self mockVerify]; -} - -- (void)stateMachineCompletePendingRaise; -{ - @autoreleasepool { - // What window group is selected? - SWWindow *selectedGroup = self.stateMachineUnderTest.selectedWindow; - XCTAssertNotNil(selectedGroup); - if (!selectedGroup) { return; } - - // Make a new set of windows with that window first and active - NSMutableOrderedSet *windowList = [NSMutableOrderedSet orderedSetWithOrderedSet:self.stateMachineUnderTest.windowList]; - [windowList removeObject:selectedGroup]; - ((SWTestApplication *)selectedGroup.application).active = YES; - [windowList insertObject:selectedGroup atIndex:0]; - - if (!self.stateMachineUnderTest.interfaceVisible) { - // if !ui, expect invalidatetimer - OCMExpect([self.stateMachineDelegateMock stateMachineWantsDisplayTimerInvalidated:self.stateMachineUnderTest]); - } - - // expect nowantwindowupdates - [self stateMachineExpectWindowListUpdates:false block:^{ - // updateWindowList: - [self _windowList:windowList]; - }]; - XCTAssertFalse(self.stateMachineUnderTest.interfaceVisible); - } -} - -- (void)mockVerify; -{ - OCMVerifyAll((OCMockObject *)self.stateMachineDelegateMock); -} - -- (void)recordFailureWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber expected:(BOOL)expected; -{ - [super recordFailureWithDescription:description inFile:filePath atLine:lineNumber expected:expected]; -} - -#pragma mark - Events that do not invoke the state machine should not perturb it - -- (void)testNonInvokingEventKeyEventIncreasingNonRepeating; -{ - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:false]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventIncreasingRepeating; -{ - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:true]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventDecreasingNonRepeating; -{ - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:false]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventDecreasingRepeating; -{ - XCTAssertNoThrow([self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:true]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventCancelInvocation; -{ - XCTAssertNoThrow([self.stateMachineUnderTest cancelInvocation]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventCloseWindow; -{ - XCTAssertNoThrow([self.stateMachineUnderTest closeWindow]); - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testNonInvokingEventKeyEventEndInvocation; -{ - [self.stateMachineUnderTest endInvocation]; - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testUnexpectedEventDisplayTimerCompleted; -{ - [self.stateMachineUnderTest displayTimerCompleted]; - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -- (void)testUnexpectedEventMouseSelection; -{ - [self.stateMachineUnderTest selectWindow:nil]; - - XCTAssertNil(self.stateMachineUnderTest.windowList); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - [self mockVerify]; - - // Verify that the state machine is ok by running a common-case invoking test. - [self testInvokeWindowListTimerKeyReleased]; -} - -#pragma mark Illegal commands to the state machine - -- (void)testIllegalEventMouseSelectedInvalidWindow; -{ - NSMutableOrderedSet *windowList = [[self class] windowList]; - SWWindow *window = [windowList lastObject]; - [windowList removeObjectAtIndex:(windowList.count - 1)]; - - [self stateMachineInvokeWithDirection:SWIncrementDirectionDecreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - XCTAssertNoThrow([self.stateMachineUnderTest selectWindow:window]); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:false]; - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, windowList[0]); -} - -- (void)testIllegalEventMouseActivatedInvalidWindow; -{ - NSMutableOrderedSet *windowList = [[self class] windowList]; - SWWindow *window = [windowList lastObject]; - [windowList removeObjectAtIndex:(windowList.count - 1)]; - - [self stateMachineInvokeWithDirection:SWIncrementDirectionDecreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - XCTAssertNoThrow([self.stateMachineUnderTest activateWindow:window]); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:false]; - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, [windowList lastObject]); -} - -#pragma mark Common real life edge cases - -- (void)testRaiseEmptyWindowList; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self.stateMachineUnderTest updateWindowList:[NSOrderedSet new]]; - XCTAssertEqualObjects(self.stateMachineUnderTest.windowList, [NSOrderedSet new]); - XCTAssertNil(self.stateMachineUnderTest.selectedWindow); - - [self stateMachineExpectWindowListUpdates:false block:^{ - [self stateMachineExpectDisplayTimerInvalidate:^{ - [self.stateMachineUnderTest endInvocation]; - }]; - }]; -} - -- (void)testRaiseFirstWindowAlreadyActive; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:false]; - - NSOrderedSet *windowList = [[self class] windowList]; - XCTAssertTrue([((SWWindow *)[windowList objectAtIndex:0]).application isActiveApplication]); - [self _windowList:windowList]; - - [self stateMachineExpectDisplayTimerInvalidate:^{ - [self stateMachineExpectWindowListUpdates:false block:^{ - [self _keyReleased]; - }]; - }]; -} - -- (void)testInvocationSelectsFirstWindowIfInactive; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - NSMutableOrderedSet *windowList = [[self class] windowList]; - SWWindow *firstWindow = [windowList objectAtIndex:0]; - [windowList removeObjectAtIndex:0]; - ((SWTestApplication *)firstWindow.application).active = NO; - [windowList insertObject:firstWindow atIndex:0]; - XCTAssertFalse([((SWWindow *)[windowList objectAtIndex:0]).application isActiveApplication]); - - [self _windowList:windowList]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, [windowList objectAtIndex:0]); - }]; -} - -- (void)testInvocationSelectsSecondWindowIfFirstIsActive; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - NSOrderedSet *windowList = [[self class] windowList]; - XCTAssertTrue([((SWWindow *)[windowList objectAtIndex:0]).application isActiveApplication]); - [self _windowList:windowList]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, [windowList objectAtIndex:1]); - }]; -} - -- (void)testLastWindowSelectedOnDecInvocation; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionDecreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, [windowList lastObject]); - }]; -} - -- (void)testNonAutorepeatIncrementingWraps; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - for (sw_unused id window in windowList) { - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:false]; - } - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, windowList[1]); -} - -- (void)testNonAutorepeatDecrementingWraps; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - for (sw_unused id window in windowList) { - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:false]; - } - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, windowList[1]); -} - -- (void)testAutorepeatIncrementingDoesNotWrap; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - for (sw_unused id window in windowList) { - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:true]; - } - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, [windowList lastObject]); -} - -- (void)testAutorepeatDecrementingDoesNotWrap; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionIncreasing isRepeating:true]; - - for (sw_unused id window in windowList) { - [self.stateMachineUnderTest incrementWithInvoke:false direction:SWIncrementDirectionDecreasing isRepeating:true]; - } - XCTAssertEqualObjects(self.stateMachineUnderTest.selectedWindow, windowList[0]); -} - -- (void)testNilFirstWindowListUpdate; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:nil]); - [self _windowList:nil]; - - [self stateMachineExpectShowInterface:true block:^{ - [self _timer]; - }]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - }]; -} - -#pragma mark Different paths to single-hotkey raise events - -- (void)_timer; -{ - XCTAssertNoThrow([self.stateMachineUnderTest displayTimerCompleted]); -}; - -- (void)_windowList:(NSOrderedSet *)windowList; -{ - @autoreleasepool { - if (windowList == nil) { - windowList = [[self class] windowList]; - } - - XCTAssertNoThrow([self.stateMachineUnderTest updateWindowList:windowList]); - } -} - -- (void)_keyReleased; -{ - XCTAssertNoThrow([self.stateMachineUnderTest endInvocation]); -} - -// invoke: -> timer -> windowlist -> wantsDisplay -> keyEventModifierReleased -> raise -> YYY -- (void)testInvokeTimerWindowListKeyReleased; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _timer]; - - // Remember: if (!showingUI) expectWantsDisplay - if (!self.stateMachineUnderTest.interfaceVisible) { - [self stateMachineExpectShowInterface:true block:^{ - [self _windowList:nil]; - }]; - } else { - [self _windowList:nil]; - } - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - }]; -} - -// invoke: -> windowlist -> timer -> wantsDisplay -> keyEventModifierReleased -> raise -> YYY -- (void)testInvokeWindowListTimerKeyReleased; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _windowList:nil]; - - // Remember: if (!showingUI) expectWantsDisplay - if (!self.stateMachineUnderTest.interfaceVisible) { - [self stateMachineExpectShowInterface:true block:^{ - [self _timer]; - }]; - } else { - [self _timer]; - } - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - }]; -} - -// invoke: -> windowlist -> keyEventModifierReleased -> raise -> timer -> wantsDisplay -> YYY -- (void)testInvokeWindowListKeyReleasedTimer; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _windowList:nil]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - }]; - - [self _timer]; -} - -// invoke: -> keyEventModifierReleased -> windowlist -> raise -> timer -> wantsDisplay -> YYY -- (void)testInvokeKeyReleasedWindowListTimer; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _keyReleased]; - [self stateMachineExpectRaise:^{ - [self _windowList:nil]; - }]; - - [self _timer]; -} - -// invoke: -> keyEventModifierReleased -> timer -> windowlist -> wantsDisplay/raise -> YYY -- (void)testInvokeKeyReleasedTimerWindowList; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _keyReleased]; - - [self _timer]; - - [self stateMachineExpectRaise:^{ - [self _windowList:nil]; - }]; -} - -// invoke: -> timer -> keyEventModifierReleased -> windowlist -> raise/wantsDisplay -> YYY -- (void)testInvokeTimerKeyReleasedWindowList; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _timer]; - - [self _keyReleased]; - - [self stateMachineExpectRaise:^{ - // Remember: if (!showingUI) expectWantsDisplay - if (!self.stateMachineUnderTest.interfaceVisible) { - [self stateMachineExpectShowInterface:true block:^{ - [self _windowList:nil]; - }]; - } else { - [self _windowList:nil]; - } - }]; -} - -// invoke: -> keyEventModifierReleased -> windowlist -> raise -> YYY -- (void)testInvokeKeyReleasedWindowList; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _keyReleased]; - - [self stateMachineExpectRaise:^{ - [self _windowList:nil]; - }]; -} - -// invoke: -> windowlist -> keyEventModifierReleased -> raise -> YYY -- (void)testInvokeWindowListKeyReleased; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self _windowList:nil]; - - [self stateMachineExpectRaise:^{ - [self _keyReleased]; - }]; -} - -#pragma mark Other paths to de-invocation - -- (void)testDismissalByMouseActivation; -{ - NSOrderedSet *windowList = [[self class] windowList]; - [self stateMachineInvokeWithDirection:SWIncrementDirectionDecreasing]; - [self stateMachineShowUIWithWindowList:windowList]; - - [self stateMachineExpectRaise:^{ - [self.stateMachineUnderTest activateWindow:[windowList lastObject]]; - }]; -} - -- (void)testUIDismissalByCancelHotkey; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - [self stateMachineShowUIWithWindowList:nil]; - [self stateMachineExpectShowInterface:false block:^{ - [self stateMachineExpectWindowListUpdates:false block:^{ - [self.stateMachineUnderTest cancelInvocation]; - }]; - }]; -} - -- (void)testDismissalByCancelHotkey; -{ - [self stateMachineInvokeWithDirection:SWIncrementDirectionIncreasing]; - - [self stateMachineExpectDisplayTimerInvalidate:^{ - [self stateMachineExpectWindowListUpdates:false block:^{ - [self.stateMachineUnderTest cancelInvocation]; - }]; - }]; -} - -#pragma mark Compound tests executing end-to-end usage - -- (void)testInvokeTimerWindowListKeyReleasedSuccessfulRaise; -{ - [self testInvokeTimerWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListTimerKeyReleasedSuccessfulRaise; -{ - [self testInvokeWindowListTimerKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedTimerSuccessfulRaise; -{ - [self testInvokeWindowListKeyReleasedTimer]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListTimerSuccessfulRaise; -{ - [self testInvokeKeyReleasedWindowListTimer]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedTimerWindowListSuccessfulRaise; -{ - [self testInvokeKeyReleasedTimerWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerKeyReleasedWindowListSuccessfulRaise; -{ - [self testInvokeTimerKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListSuccessfulRaise; -{ - [self testInvokeKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedSuccessfulRaise; -{ - [self testInvokeWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerWindowListKeyReleasedSuccessfulRaiseTwice; -{ - [self testInvokeTimerWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; - [self testInvokeTimerWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListTimerKeyReleasedSuccessfulRaiseTwice; -{ - [self testInvokeWindowListTimerKeyReleased]; - [self stateMachineCompletePendingRaise]; - [self testInvokeWindowListTimerKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedTimerSuccessfulRaiseTwice; -{ - [self testInvokeWindowListKeyReleasedTimer]; - [self stateMachineCompletePendingRaise]; - [self testInvokeWindowListKeyReleasedTimer]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListTimerSuccessfulRaiseTwice; -{ - [self testInvokeKeyReleasedWindowListTimer]; - [self stateMachineCompletePendingRaise]; - [self testInvokeKeyReleasedWindowListTimer]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedTimerWindowListSuccessfulRaiseTwice; -{ - [self testInvokeKeyReleasedTimerWindowList]; - [self stateMachineCompletePendingRaise]; - [self testInvokeKeyReleasedTimerWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerKeyReleasedWindowListSuccessfulRaiseTwice; -{ - [self testInvokeTimerKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; - [self testInvokeTimerKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListSuccessfulRaiseTwice; -{ - [self testInvokeKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; - [self testInvokeKeyReleasedWindowList]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedSuccessfulRaiseTwice; -{ - [self testInvokeWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; - [self testInvokeWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerWindowListKeyReleasedSuccessfulRaiseInterrupted; -{ - [self testInvokeTimerWindowListKeyReleased]; - [self testInvokeTimerWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListTimerKeyReleasedSuccessfulRaiseInterrupted; -{ - [self testInvokeWindowListTimerKeyReleased]; - [self testInvokeWindowListTimerKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedTimerSuccessfulRaiseInterrupted; -{ - [self testInvokeWindowListKeyReleasedTimer]; - [self testInvokeWindowListKeyReleasedTimer]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedSuccessfulRaiseInterrupted; -{ - [self testInvokeWindowListKeyReleased]; - [self testInvokeWindowListKeyReleased]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerWindowListKeyReleasedSuccessfulRaiseWithMonkey; -{ - [self testInvokeTimerWindowListKeyReleased]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListTimerKeyReleasedSuccessfulRaiseWithMonkey; -{ - [self testInvokeWindowListTimerKeyReleased]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedTimerSuccessfulRaiseWithMonkey; -{ - [self testInvokeWindowListKeyReleasedTimer]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListTimerSuccessfulRaiseWithMonkey; -{ - [self testInvokeKeyReleasedWindowListTimer]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedTimerWindowListSuccessfulRaiseWithMonkey; -{ - [self testInvokeKeyReleasedTimerWindowList]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeTimerKeyReleasedWindowListSuccessfulRaiseWithMonkey; -{ - [self testInvokeTimerKeyReleasedWindowList]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeKeyReleasedWindowListSuccessfulRaiseWithMonkey; -{ - [self testInvokeKeyReleasedWindowList]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testInvokeWindowListKeyReleasedSuccessfulRaiseWithMonkey; -{ - [self testInvokeWindowListKeyReleased]; - [self stateMachineInRaiseStateMonkey:self.monkeyCount]; - [self stateMachineCompletePendingRaise]; -} - -- (void)testAllSuccessfulRaiseTests; -{ - [self testInvokeTimerWindowListKeyReleasedSuccessfulRaise]; - [self testInvokeWindowListTimerKeyReleasedSuccessfulRaise]; - [self testInvokeWindowListKeyReleasedTimerSuccessfulRaise]; - [self testInvokeKeyReleasedWindowListTimerSuccessfulRaise]; - [self testInvokeKeyReleasedTimerWindowListSuccessfulRaise]; - [self testInvokeTimerKeyReleasedWindowListSuccessfulRaise]; - [self testInvokeKeyReleasedWindowListSuccessfulRaise]; - [self testInvokeWindowListKeyReleasedSuccessfulRaise]; - [self testInvokeTimerWindowListKeyReleasedSuccessfulRaiseTwice]; - [self testInvokeWindowListTimerKeyReleasedSuccessfulRaiseTwice]; - [self testInvokeWindowListKeyReleasedTimerSuccessfulRaiseTwice]; - [self testInvokeKeyReleasedWindowListTimerSuccessfulRaiseTwice]; - [self testInvokeKeyReleasedTimerWindowListSuccessfulRaiseTwice]; - [self testInvokeTimerKeyReleasedWindowListSuccessfulRaiseTwice]; - [self testInvokeKeyReleasedWindowListSuccessfulRaiseTwice]; - [self testInvokeWindowListKeyReleasedSuccessfulRaiseTwice]; - [self testInvokeTimerWindowListKeyReleasedSuccessfulRaiseInterrupted]; - [self testInvokeWindowListTimerKeyReleasedSuccessfulRaiseInterrupted]; - [self testInvokeWindowListKeyReleasedTimerSuccessfulRaiseInterrupted]; - [self testInvokeWindowListKeyReleasedSuccessfulRaiseInterrupted]; - [self testInvokeTimerWindowListKeyReleasedSuccessfulRaiseWithMonkey]; - [self testInvokeWindowListTimerKeyReleasedSuccessfulRaiseWithMonkey]; - [self testInvokeWindowListKeyReleasedTimerSuccessfulRaiseWithMonkey]; - [self testInvokeKeyReleasedWindowListTimerSuccessfulRaiseWithMonkey]; - [self testInvokeKeyReleasedTimerWindowListSuccessfulRaiseWithMonkey]; - [self testInvokeTimerKeyReleasedWindowListSuccessfulRaiseWithMonkey]; - [self testInvokeKeyReleasedWindowListSuccessfulRaiseWithMonkey]; - [self testInvokeWindowListKeyReleasedSuccessfulRaiseWithMonkey]; -} - -- (void)testAllAPIAtRandom; -{ - self.niceMock = true; - - NSArray *tests = @[ - ^{ (void)self.stateMachineUnderTest.windowList; }, - ^{ (void)self.stateMachineUnderTest.selectedWindow; }, - ^{ [self.stateMachineUnderTest displayTimerCompleted]; }, - ^{ [self.stateMachineUnderTest displayTimerCompleted]; }, - ^{ [self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]; }, - ^{ [self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]; }, - ^{ [self.stateMachineUnderTest incrementWithInvoke:coin() direction:(coin() ? SWIncrementDirectionIncreasing : SWIncrementDirectionDecreasing) isRepeating:coin()]; }, - ^{ [self.stateMachineUnderTest closeWindow]; }, - ^{ [self.stateMachineUnderTest cancelInvocation]; }, - ^{ [self.stateMachineUnderTest endInvocation]; }, - ^{ [self.stateMachineUnderTest selectWindow:rwg()]; }, - ^{ [self.stateMachineUnderTest activateWindow:rwg()]; }, - ^{ [self.stateMachineUnderTest updateWindowList:rwgs()]; }, - ^{ [self.stateMachineUnderTest updateWindowList:rwgs()]; }, - ^{ [self.stateMachineUnderTest updateWindowList:rwgs()]; }, - ]; - - unsigned iterations = 0; - NSDate *start = [NSDate date]; - while (-[start timeIntervalSinceNow] < MAX_TIME_PER_TEST) { - @autoreleasepool { - for (unsigned i = 0; i < tests.count; ++i) { - iterations++; - XCTAssertNoThrow(((dispatch_block_t)tests[die((uint32_t)tests.count)])()); - } - } - } - NSLog(@"Ran %u iterations of random API tests", iterations); -} - -@end diff --git a/SwitchTests/SWTestApplication.h b/SwitchTests/SWTestApplication.h deleted file mode 100644 index 5998505a..00000000 --- a/SwitchTests/SWTestApplication.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SWTestApplication.h -// Switch -// -// Created by Scott Perry on 09/29/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWApplication.h" - - -@interface SWTestApplication : SWApplication - -@property (nonatomic, assign, readwrite, getter=isActiveApplication) BOOL active; - -@end diff --git a/SwitchTests/SWTestApplication.m b/SwitchTests/SWTestApplication.m deleted file mode 100644 index 504f54b3..00000000 --- a/SwitchTests/SWTestApplication.m +++ /dev/null @@ -1,61 +0,0 @@ -// -// SWTestApplication.m -// Switch -// -// Created by Scott Perry on 09/29/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWTestApplication.h" - - -@interface SWTestApplication () - -@property (nonatomic, assign, readwrite) pid_t pid; -@property (nonatomic, copy, readwrite) NSImage *icon; -@property (nonatomic, strong, readwrite) NSString *name; -@property (nonatomic, assign, readwrite, getter=isLiveApplication) BOOL liveApplication; -@property (nonatomic, assign, readwrite) BOOL canBeActivated; - -@end - - -@implementation SWTestApplication - -@synthesize pid = _pid; -@synthesize icon = _icon; -@synthesize name = _name; -@synthesize liveApplication = _liveApplication; -@synthesize canBeActivated = _canBeActivated; - -- (instancetype)initWithPID:(pid_t)pid name:(NSString *)name; -{ - if (!(self = [super init])) { return nil; } - - self->_pid = pid; - self->_name = name; - self->_active = [name isEqualToString:@"Switch"]; - self->_liveApplication = YES; - self->_canBeActivated = ^{ - NSArray *applicationNamesThatCannotBeActivated = @[ - @"com.apple.security.pboxd", - @"com.apple.appkit.xpc.openAndSav", - ]; - return ![applicationNamesThatCannotBeActivated containsObject:self.name]; - }(); - - return self; -} - -- (NSRunningApplication *)runningApplication; -{ - __builtin_trap(); -} - -@end diff --git a/SwitchTests/SWTweetbotTests.m b/SwitchTests/SWTweetbotTests.m deleted file mode 100644 index c6896863..00000000 --- a/SwitchTests/SWTweetbotTests.m +++ /dev/null @@ -1,183 +0,0 @@ -// -// SWTweetbotTests.m -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWTweetbotTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWTweetbotTests - -- (NSDictionary *)mainWindowInfoDict; -{ - return @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 636, - .size.width = 480, - .origin.x = 293, - .origin.y = 74 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1710196, - NNWindowName : @"Main Window", - NNWindowNumber : @85208, - NNWindowOwnerName : @"Tweetbot", - NNWindowOwnerPID : @9258, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; -} - -- (NSArray *)mainWindowsInfoList; -{ - return @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 30, - .size.width = 412, - .origin.x = 360, - .origin.y = 110 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @62748, - NNWindowName : @"", - NNWindowNumber : @114910, - NNWindowOwnerName : @"Tweetbot", - NNWindowOwnerPID : @9258, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - [self mainWindowInfoDict] - ]; -} - -- (NSDictionary *)imageWindowInfoDict; -{ - return @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 379, - .size.width = 1002, - .origin.x = 124, - .origin.y = 300 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2541364, - NNWindowName : @"", - NNWindowNumber : @117515, - NNWindowOwnerName : @"Tweetbot", - NNWindowOwnerPID : @9258, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; -} - -// https://github.com/numist/Switch/issues/2 -- (void)testFilterUnreadBanner; -{ - NSDictionary *windowDescription = self.mainWindowInfoDict; - NSArray *infoList = self.mainWindowsInfoList; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @""); - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, infoList.count, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); -} - -// https://github.com/numist/Switch/issues/2 -- (void)testFilterWithImageWindow; -{ - NSMutableArray *infoList = [self.mainWindowsInfoList mutableCopy]; - [infoList addObject:self.imageWindowInfoDict]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 2, @""); - if (self.listService.windows.count == 2) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, self.mainWindowInfoDict, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, self.imageWindowInfoDict, @""); - } - - [infoList removeObjectAtIndex:(infoList.count - 1)]; - [infoList insertObject:self.imageWindowInfoDict atIndex:0]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 2, @""); - if (self.listService.windows.count == 2) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, self.imageWindowInfoDict, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, self.mainWindowInfoDict, @""); - } -} - -// No ticket, verify that the frame of an image window doesn't cause it to be coalesced into the main window (image windows are unnamed and could be positioned within the frame of the main window) -- (void)testImageInsideAndAboveMainWindow; -{ - NSDictionary *imageWindowInfo = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 244, - .size.width = 464, - .origin.x = 880, - .origin.y = 287 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @664916, - NNWindowName : @"", - NNWindowNumber : @54150, - NNWindowOwnerName : @"Tweetbot", - NNWindowOwnerPID : @90385, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSDictionary *mainWindowInfo = @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 636, - .size.width = 480, - .origin.x = 872, - .origin.y = 73 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @1226068, - NNWindowName : @"Main Window", - NNWindowNumber : @52387, - NNWindowOwnerName : @"Tweetbot", - NNWindowOwnerPID : @90385, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }; - NSArray *infoList = @[imageWindowInfo, mainWindowInfo]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 2, @""); - if (self.listService.windows.count == 2) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, imageWindowInfo, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:1]).mainWindow.windowDescription, mainWindowInfo, @""); - } -} - -@end diff --git a/SwitchTests/SWWindowFilteringTests.h b/SwitchTests/SWWindowFilteringTests.h deleted file mode 100644 index df115a47..00000000 --- a/SwitchTests/SWWindowFilteringTests.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// NNWindowFilteringTests.h -// Switch -// -// Created by Scott Perry on 10/11/13. -// Copyright © 2013 Scott Perry. -// -// 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. -// - -#ifndef Switch_NNWindowFilteringTests_h -#define Switch_NNWindowFilteringTests_h - - -#define DICT_FROM_RECT(rect) ((__bridge_transfer NSDictionary *)CGRectCreateDictionaryRepresentation(rect)) - - -static NSString *NNWindowAlpha; -static NSString *NNWindowBounds; -static NSString *NNWindowIsOnscreen; -static NSString *NNWindowLayer; -static NSString *NNWindowMemoryUsage; -static NSString *NNWindowName; -static NSString *NNWindowNumber; -static NSString *NNWindowOwnerName; -static NSString *NNWindowOwnerPID; -static NSString *NNWindowSharingState; -static NSString *NNWindowStoreType; - -__attribute__((constructor)) static void NNWindowKeyInit() { - NNWindowAlpha = (__bridge NSString *)kCGWindowAlpha; - NNWindowBounds = (__bridge NSString *)kCGWindowBounds; - NNWindowIsOnscreen = (__bridge NSString *)kCGWindowIsOnscreen; - NNWindowLayer = (__bridge NSString *)kCGWindowLayer; - NNWindowMemoryUsage = (__bridge NSString *)kCGWindowMemoryUsage; - NNWindowName = (__bridge NSString *)kCGWindowName; - NNWindowNumber = (__bridge NSString *)kCGWindowNumber; - NNWindowOwnerName = (__bridge NSString *)kCGWindowOwnerName; - NNWindowOwnerPID = (__bridge NSString *)kCGWindowOwnerPID; - NNWindowSharingState = (__bridge NSString *)kCGWindowSharingState; - NNWindowStoreType = (__bridge NSString *)kCGWindowStoreType; -} - -#endif diff --git a/SwitchTests/SWWindowListServiceTestSuperclass.h b/SwitchTests/SWWindowListServiceTestSuperclass.h deleted file mode 100644 index 6a669f71..00000000 --- a/SwitchTests/SWWindowListServiceTestSuperclass.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// SWWindowListServiceTestSuperclass.h -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 - -#import "SWWindowListService.h" -#import "SWWindowFilteringTests.h" -#import "SWWindowGroup.h" -#import "SWWindow.h" - - -@interface SWWindowListServiceTestSuperclass : XCTestCase - -@property (nonatomic, strong, readonly) SWWindowListService *listService; - -- (void)updateListServiceWithInfoList:(NSArray *)infoList; - -@end diff --git a/SwitchTests/SWWindowListServiceTestSuperclass.m b/SwitchTests/SWWindowListServiceTestSuperclass.m deleted file mode 100644 index 63d2aca7..00000000 --- a/SwitchTests/SWWindowListServiceTestSuperclass.m +++ /dev/null @@ -1,46 +0,0 @@ -// -// SWWindowListServiceTestSuperclass.m -// Switch -// -// Created by Scott Perry on 01/05/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWWindowListService (Internal) - -- (void)private_updateWindowList:(NSArray *)windowInfoList; - -@end - - -@implementation SWWindowListServiceTestSuperclass - -- (void)setUp -{ - [super setUp]; - - self->_listService = [SWWindowListService new]; -} - -- (void)tearDown -{ - self->_listService = nil; - - [super tearDown]; -} - -- (void)updateListServiceWithInfoList:(NSArray *)infoList; -{ - [self.listService private_updateWindowList:infoList]; -} - -@end diff --git a/SwitchTests/SWWindowListServiceTests.m b/SwitchTests/SWWindowListServiceTests.m deleted file mode 100644 index a935a53f..00000000 --- a/SwitchTests/SWWindowListServiceTests.m +++ /dev/null @@ -1,646 +0,0 @@ -// -// SWWindowListServiceTests.m -// Switch -// -// Created by Scott Perry on 09/27/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWWindowListService (Internal) - -- (void)private_updateWindowList:(NSArray *)windowInfoList; - -@end - - -@interface SWWindowListServiceTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWWindowListServiceTests - -- (void)testEmpty -{ - XCTAssertNil(self.listService.windows, @""); - - [self measureBlock:^{ - [self.listService private_updateWindowList:@[]]; - }]; - XCTAssertEqualObjects(self.listService.windows, [NSOrderedSet new], @""); -} - -- (void)testNormalDay -{ - NSArray *windowInfoList = @[ - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 768, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @101, - NNWindowMemoryUsage : @4228348, - NNWindowName : @"", - NNWindowNumber : @1357, - NNWindowOwnerName : @"Switch", - NNWindowOwnerPID : @8392, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 30, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @1317, - NNWindowOwnerName : @"Switch", - NNWindowOwnerPID : @8392, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @1286, - NNWindowOwnerName : @"Location Menu", - NNWindowOwnerPID : @8287, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @45, - NNWindowOwnerName : @"Dropbox", - NNWindowOwnerPID : @222, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @51, - NNWindowOwnerName : @"Alfred 2", - NNWindowOwnerPID : @226, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @62, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @50, - NNWindowOwnerName : @"CrashPlan menu bar", - NNWindowOwnerPID : @227, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 24, - .origin.x = 1342, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @44, - NNWindowOwnerName : @"Bartender", - NNWindowOwnerPID : @219, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @43, - NNWindowOwnerName : @"Flux", - NNWindowOwnerPID : @217, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @61, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @60, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @46, - NNWindowOwnerName : @"1Password mini", - NNWindowOwnerPID : @190, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @41, - NNWindowOwnerName : @"Satellite Eyes", - NNWindowOwnerPID : @242, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 69, - .origin.x = 1191, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @9468, - NNWindowName : @"", - NNWindowNumber : @59, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 30, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowName : @"", - NNWindowNumber : @58, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1260, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @57, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 52, - .origin.x = 1290, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @9468, - NNWindowName : @"", - NNWindowNumber : @56, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1342, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @25, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1161, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @39, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @0, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @5372, - NNWindowNumber : @40, - NNWindowOwnerName : @"SystemUIServer", - NNWindowOwnerPID : @165, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 0, - .size.width = 0, - .origin.x = 1366, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @25, - NNWindowMemoryUsage : @1276, - NNWindowName : @"", - NNWindowNumber : @38, - NNWindowOwnerName : @"Notification Center", - NNWindowOwnerPID : @188, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 22, - .size.width = 1366, - .origin.x = 0, - .origin.y = 0 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @24, - NNWindowMemoryUsage : @124156, - NNWindowName : @"Menubar", - NNWindowNumber : @12, - NNWindowOwnerName : @"Window Server", - NNWindowOwnerPID : @87, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 43, - .size.width = 1261, - .origin.x = 53, - .origin.y = 798 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @19, - NNWindowMemoryUsage : @222460, - NNWindowName : @"Magic Mirror", - NNWindowNumber : @1351, - NNWindowOwnerName : @"Dock", - NNWindowOwnerPID : @162, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 742, - .size.width = 1366, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @4416340, - NNWindowName : @"SWWindowListService.m", - NNWindowNumber : @91, - NNWindowOwnerName : @"Xcode", - NNWindowOwnerPID : @415, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 317, - .size.width = 744, - .origin.x = 45, - .origin.y = 34 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @46916, - NNWindowName : @"Wreck-It Ralph", - NNWindowNumber : @1355, - NNWindowOwnerName : @"QuickTime Player", - NNWindowOwnerPID : @8438, - NNWindowSharingState : @1, - NNWindowStoreType : @1, - }, - @{ - NNWindowAlpha : @"0.8500000238418579", - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 10, - .size.width = 503, - .origin.x = 534, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @21756, - NNWindowNumber : @1342, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 58, - .size.width = 463, - .origin.x = 553, - .origin.y = 272 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @111868, - NNWindowName : @"", - NNWindowNumber : @1341, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 489, - .origin.x = 540, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @636156, - NNWindowName : @"Save", - NNWindowNumber : @1336, - NNWindowOwnerName : @"com.apple.appkit.xpc.openAndSav", - NNWindowOwnerPID : @8436, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 319, - .size.width = 490, - .origin.x = 540, - .origin.y = 60 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @806740, - NNWindowName : @"Save", - NNWindowNumber : @1337, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 702, - .size.width = 858, - .origin.x = 356, - .origin.y = 38 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2484948, - NNWindowName : @"Untitled.txt", - NNWindowNumber : @1334, - NNWindowOwnerName : @"TextEdit", - NNWindowOwnerPID : @8428, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 698, - .size.width = 1279, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @3661844, - NNWindowName : @"untitled 16", - NNWindowNumber : @754, - NNWindowOwnerName : @"TextMate", - NNWindowOwnerPID : @5985, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 698, - .size.width = 1365, - .origin.x = 0, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @3932116, - NNWindowName : @"untitled 12", - NNWindowNumber : @755, - NNWindowOwnerName : @"TextMate", - NNWindowOwnerPID : @5985, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - }, - @{ - NNWindowAlpha : @1, - NNWindowBounds : DICT_FROM_RECT(((CGRect){ - .size.height = 422, - .size.width = 1012, - .origin.x = 73, - .origin.y = 22 - })), - NNWindowIsOnscreen : @1, - NNWindowLayer : @0, - NNWindowMemoryUsage : @2167060, - NNWindowName : @"~/C/Switch — fish", - NNWindowNumber : @626, - NNWindowOwnerName : @"Terminal", - NNWindowOwnerPID : @3579, - NNWindowSharingState : @1, - NNWindowStoreType : @2, - } - ]; - - [self measureBlock:^{ - [self.listService private_updateWindowList:windowInfoList]; - }]; - - XCTAssertEqual(6, self.listService.windows.count); -} - -@end diff --git a/SwitchTests/SWWordTests.m b/SwitchTests/SWWordTests.m deleted file mode 100644 index 11ae9084..00000000 --- a/SwitchTests/SWWordTests.m +++ /dev/null @@ -1,92 +0,0 @@ -// -// SWWordTests.m -// Switch -// -// Created by Scott Perry on 02/12/14. -// Copyright © 2014 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWWordTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWWordTests - -- (void)testGiantInvisibleWindow; -{ - NSDictionary *windowDescription = @{ - NNWindowLayer : @0, - NNWindowName : @"Document1", - NNWindowMemoryUsage : @5548372, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @65457, - NNWindowNumber : @17655, - NNWindowOwnerName : @"Microsoft Word", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @823, - @"Width" : @1264, - @"X" : @436, - @"Y" : @245, - }, - NNWindowAlpha : @1.000000, - }; - - NSArray *infoList = @[ - @{ - NNWindowLayer : @0, - NNWindowMemoryUsage : @3892476, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @65457, - NNWindowNumber : @17657, - NNWindowOwnerName : @"Microsoft Word", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @769, - @"Width" : @1264, - @"X" : @436, - @"Y" : @299, - }, - NNWindowAlpha : @1.000000, - }, - windowDescription, - @{ - NNWindowLayer : @0, - NNWindowName : @"Microsoft Word", - NNWindowMemoryUsage : @9020668, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @65457, - NNWindowNumber : @17635, - NNWindowOwnerName : @"Microsoft Word", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @1174, - @"Width" : @1920, - @"X" : @0, - @"Y" : @22, - }, - NNWindowAlpha : @1.000000, - } - ]; - - [self updateListServiceWithInfoList:infoList]; - - XCTAssertEqual(self.listService.windows.count, 1, @""); - XCTAssertEqual(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).windows.count, (NSUInteger)2, @""); - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.windowDescription, windowDescription, @""); -} - -@end diff --git a/SwitchTests/SWXcodeTests.m b/SwitchTests/SWXcodeTests.m deleted file mode 100644 index fb0c9549..00000000 --- a/SwitchTests/SWXcodeTests.m +++ /dev/null @@ -1,72 +0,0 @@ -// -// SWXcodeTests.m -// Switch -// -// Created by Scott Perry on 05/13/16. -// Copyright © 2016 Scott Perry. -// -// 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 "SWWindowListServiceTestSuperclass.h" - - -@interface SWXcodeTests : SWWindowListServiceTestSuperclass - -@end - - -@implementation SWXcodeTests - -- (void)testFullScreenWindow { - NSArray *infoList = @[ - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @1024, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @60683, - NNWindowNumber : @14131, - NNWindowOwnerName : @"Xcode", - NNWindowStoreType : @1, - NNWindowBounds : @{ - @"Height" : @74, - @"Width" : @1366, - @"X" : @0, - @"Y" : @22, - }, - NNWindowName : @"", - }, - @{ - NNWindowLayer : @0, - NNWindowAlpha : @1.000000, - NNWindowMemoryUsage : @4002976, - NNWindowIsOnscreen : @1, - NNWindowSharingState : @1, - NNWindowOwnerPID : @60683, - NNWindowNumber : @9487, - NNWindowOwnerName : @"Xcode", - NNWindowStoreType : @2, - NNWindowBounds : @{ - @"Height" : @727, - @"Width" : @1366, - @"X" : @0, - @"Y" : @41, - }, - NNWindowName : @"SWCoreWindowService.m", - }, - ]; - - [self updateListServiceWithInfoList:infoList]; - XCTAssertEqual(self.listService.windows.count, 1, @"Xcode was incorrectly filtered"); - if (self.listService.windows.count == 1) { - XCTAssertEqualObjects(((SWWindowGroup *)[self.listService.windows objectAtIndex:0]).mainWindow.name, @"SWCoreWindowService.m"); - } -} - -@end diff --git a/SwitchTests/en.lproj/1376.ar b/SwitchTests/en.lproj/1376.ar deleted file mode 100644 index aa76b052..00000000 Binary files a/SwitchTests/en.lproj/1376.ar and /dev/null differ diff --git a/SwitchTests/en.lproj/5891.ar b/SwitchTests/en.lproj/5891.ar deleted file mode 100644 index 7749f658..00000000 Binary files a/SwitchTests/en.lproj/5891.ar and /dev/null differ diff --git a/SwitchTests/en.lproj/5904.ar b/SwitchTests/en.lproj/5904.ar deleted file mode 100644 index bf47a231..00000000 Binary files a/SwitchTests/en.lproj/5904.ar and /dev/null differ diff --git a/SwitchTests/en.lproj/InfoPlist.strings b/SwitchTests/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff..00000000 --- a/SwitchTests/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/relaunch/relaunch-Prefix.pch b/relaunch/relaunch-Prefix.pch deleted file mode 100644 index f0f7e6fc..00000000 --- a/relaunch/relaunch-Prefix.pch +++ /dev/null @@ -1,9 +0,0 @@ -// -// Prefix header -// -// The contents of this file are implicitly included at the beginning of every source file. -// - -#ifdef __OBJC__ - #import -#endif