├── Spectacle ├── Resources │ ├── Images.xcassets │ │ ├── Contents.json │ │ ├── 10.9 Crash Fix.imageset │ │ │ ├── Spectacle-1.png │ │ │ ├── Spectacle-2.png │ │ │ ├── Spectacle-3.png │ │ │ ├── Spectacle-4.png │ │ │ ├── Spectacle-5.png │ │ │ ├── Spectacle-6.png │ │ │ ├── Spectacle-7.png │ │ │ ├── Spectacle-8.png │ │ │ ├── Spectacle-9.png │ │ │ ├── Spectacle-10.png │ │ │ ├── Spectacle-11.png │ │ │ ├── Spectacle-12.png │ │ │ ├── Spectacle-13.png │ │ │ ├── Spectacle-14.png │ │ │ ├── Spectacle-15.png │ │ │ ├── Spectacle-16.png │ │ │ ├── Spectacle-17.png │ │ │ ├── Spectacle-18.png │ │ │ ├── Spectacle-19.png │ │ │ ├── Spectacle-20.png │ │ │ ├── Spectacle-21.png │ │ │ ├── Spectacle-22.png │ │ │ ├── Spectacle-23.png │ │ │ ├── Spectacle-24.png │ │ │ ├── Spectacle-25.png │ │ │ ├── Spectacle-26.png │ │ │ ├── Spectacle-27.png │ │ │ ├── Spectacle-28.png │ │ │ ├── Spectacle-29.png │ │ │ ├── Spectacle-30.png │ │ │ ├── Spectacle-31.png │ │ │ ├── Spectacle-32.png │ │ │ ├── Spectacle-33.png │ │ │ ├── Spectacle-34.png │ │ │ ├── Spectacle-35.png │ │ │ ├── Spectacle-36.png │ │ │ ├── Spectacle-37.png │ │ │ ├── Spectacle-38.png │ │ │ ├── Spectacle-39.png │ │ │ ├── Spectacle-40.png │ │ │ ├── Spectacle-41.png │ │ │ ├── Spectacle-42.png │ │ │ ├── Spectacle-43.png │ │ │ ├── Spectacle-44.png │ │ │ ├── Spectacle-45.png │ │ │ ├── Spectacle-46.png │ │ │ ├── Spectacle-47.png │ │ │ └── Spectacle-48.png │ │ ├── Spectacle.appiconset │ │ │ ├── Spectacle 128.png │ │ │ ├── Spectacle 16.png │ │ │ ├── Spectacle 256.png │ │ │ ├── Spectacle 32.png │ │ │ ├── Spectacle 512.png │ │ │ ├── Spectacle 128@2x.png │ │ │ ├── Spectacle 16@2x.png │ │ │ ├── Spectacle 256@2x.png │ │ │ ├── Spectacle 32@2x.png │ │ │ ├── Spectacle 512@2x.png │ │ │ └── Contents.json │ │ ├── Security & Privacy.imageset │ │ │ ├── Security & Privacy.png │ │ │ ├── Security & Privacy@2x.png │ │ │ └── Contents.json │ │ └── Spectacle Status Item.imageset │ │ │ ├── Spectacle Status Item.png │ │ │ ├── Spectacle Status Item@2x.png │ │ │ └── Contents.json │ ├── Localizations │ │ ├── en.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ └── Credits.rtf │ │ ├── es.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ ├── Credits.rtf │ │ │ ├── Spectacle.strings │ │ │ └── SpectaclePreferencesWindow.strings │ │ ├── fi.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ ├── Credits.rtf │ │ │ ├── Spectacle.strings │ │ │ └── SpectaclePreferencesWindow.strings │ │ ├── fr.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ ├── Credits.rtf │ │ │ ├── Spectacle.strings │ │ │ └── SpectaclePreferencesWindow.strings │ │ ├── it.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ ├── Credits.rtf │ │ │ ├── Spectacle.strings │ │ │ └── SpectaclePreferencesWindow.strings │ │ └── pt.lproj │ │ │ ├── InfoPlist.strings │ │ │ ├── Localizable.strings │ │ │ ├── Credits.rtf │ │ │ ├── Spectacle.strings │ │ │ └── SpectaclePreferencesWindow.strings │ ├── Scripts │ │ ├── Security & Privacy System Preferences.scpt │ │ └── Security & Privacy System Preferences Original.scpt │ ├── Window Position Calculations │ │ ├── SpectacleFullscreenWindowCalculation.js │ │ ├── SpectacleLargerWindowCalculation.js │ │ ├── SpectacleNextThirdWindowCalculation.js │ │ ├── SpectacleSmallerWindowCalculation.js │ │ ├── SpectaclePreviousThirdWindowCalculation.js │ │ ├── SpectacleNextDisplayWindowCalculation.js │ │ ├── SpectaclePreviousDisplayWindowCalculation.js │ │ ├── SpectacleNextOrPreviousDisplay.js │ │ ├── SpectacleWindowCalculationHelpers.js │ │ ├── SpectacleCenterWindowCalculation.js │ │ ├── SpectacleLeftHalfWindowCalculation.js │ │ ├── SpectacleBottomHalfWindowCalculation.js │ │ ├── SpectacleLowerLeftWindowCalculation.js │ │ ├── SpectacleRightHalfWindowCalculation.js │ │ ├── SpectacleUpperLeftWindowCalculation.js │ │ ├── SpectacleTopHalfWindowCalculation.js │ │ ├── SpectacleLowerRightWindowCalculation.js │ │ ├── SpectacleUpperRightWindowCalculation.js │ │ └── SpectacleNextOrPreviousThirds.js │ ├── Property Lists │ │ └── Defaults.plist │ └── Certificates │ │ └── dsa_public.pem ├── Supporting Files │ ├── main.m │ └── Info.plist └── Sources │ ├── SpectacleDefaultShortcutHelpers.h │ ├── SpectacleRegisteredShortcutValidator.h │ ├── SpectacleShortcutUserDefaultsStorage.h │ ├── SpectacleShortcutJSONStorage.h │ ├── SpectacleMacros.h │ ├── SpectacleLoginItemHelper.h │ ├── SpectacleQuantizedWindowMover.h │ ├── SpectacleStandardWindowMover.h │ ├── SpectacleBestEffortWindowMover.h │ ├── SpectacleShortcutStorageMigrationDestination.h │ ├── SpectacleShortcutValidator.h │ ├── SpectacleShortcutKeyBindings.h │ ├── SpectacleShortcutStorage.h │ ├── SpectacleHistory.h │ ├── SpectacleRegisteredShortcutValidator.m │ ├── SpectacleUtilities.h │ ├── SpectacleJavaScriptEnvironment.h │ ├── SpectacleShortcutRecorderDelegate.h │ ├── SpectacleShortcutRecorder.h │ ├── SpectacleMigratingShortcutStorage.h │ ├── SpectacleShortcutTranslations.h │ ├── SpectacleScreenDetector.h │ ├── SpectacleShortcutValidation.h │ ├── SpectacleWindowPositionCalculationResult.h │ ├── SpectacleWindowMover.h │ ├── SpectacleScreenDetectionResult.h │ ├── SpectacleWindowPositionCalculationResult.m │ ├── SpectacleHistoryItem.m │ ├── SpectacleAccessibilityElement.h │ ├── SpectacleScreenDetectionResult.m │ ├── SpectacleWindowPositionCalculator.h │ ├── SpectacleWindowPositionCalculationRegistry.h │ ├── SpectacleHistoryItem.h │ ├── SpectacleShortcutManager.h │ ├── SpectacleShortcutHolder.h │ ├── SpectacleWindowPositionCalculationRegistry.m │ ├── SpectacleMigratingShortcutStorage.m │ ├── SpectacleJavaScriptEnvironment.m │ ├── SpectacleStandardWindowMover.m │ ├── SpectacleHistory.m │ ├── SpectacleWindowPositionManager.h │ ├── SpectacleWindowAction.h │ ├── SpectacleShortcut.h │ ├── SpectacleShortcutHolder.m │ ├── SpectacleQuantizedWindowMover.m │ ├── SpectacleWindowAction.m │ ├── SpectacleBestEffortWindowMover.m │ ├── SpectacleShortcutUserDefaultsStorage.m │ ├── SpectacleDefaultShortcutHelpers.m │ ├── SpectaclePreferencesController.h │ ├── SpectacleAppDelegate.h │ ├── SpectacleWindowPositionCalculator.m │ ├── SpectacleLoginItemHelper.m │ ├── SpectacleUtilities.m │ ├── SpectacleShortcutValidation.m │ └── SpectacleShortcutJSONStorage.m ├── Cartfile ├── Cartfile.resolved ├── Spectacle.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── Spectacle.xcscheme ├── .gitignore ├── .travis.yml ├── SpectacleSpecs ├── Supporting Files │ └── Info.plist └── Sources │ ├── SpectacleCenterWindowCalculationSpec.m │ ├── SpectacleFullscreenWindowCalculationSpec.m │ ├── SpectacleLowerLeftWindowCalculationSpec.m │ ├── SpectacleNextDisplayWindowCalculationSpec.m │ ├── SpectacleLowerRightWindowCalculationSpec.m │ ├── SpectaclePreviousDisplayWindowCalculationSpec.m │ ├── SpectacleLeftHalfWindowCalculationSpec.m │ ├── SpectacleTopHalfWindowCalculationSpec.m │ ├── SpectacleBottomHalfWindowCalculationSpec.m │ ├── SpectacleRightHalfWindowCalculationSpec.m │ ├── SpectacleUpperLeftWindowCalculationSpec.m │ └── SpectacleUpperRightWindowCalculationSpec.m ├── LICENSE.md └── CONTRIBUTING.md /Spectacle/Resources/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/en.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/es.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/es.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fi.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/fi.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fr.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/fr.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/it.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/it.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/pt.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/pt.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/es.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fi.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/fi.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/fr.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/it.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/it.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/pt.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Localizations/pt.lproj/Localizable.strings -------------------------------------------------------------------------------- /Spectacle/Supporting Files/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | return NSApplicationMain(argc, (const char **)argv); 6 | } 7 | -------------------------------------------------------------------------------- /Spectacle/Resources/Scripts/Security & Privacy System Preferences.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Scripts/Security & Privacy System Preferences.scpt -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "hamcrest/OCHamcrest" ~> 7.1 2 | github "jonreid/OCMockito" ~> 5.1 3 | github "sparkle-project/Sparkle" ~> 1.22 4 | github "specta/expecta" ~> 1.0 5 | github "specta/specta" ~> 1.0 6 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-1.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-2.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-3.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-4.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-5.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-6.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-7.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-8.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-9.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 128.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 16.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 256.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 32.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 512.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-10.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-11.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-12.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-13.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-14.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-15.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-16.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-17.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-18.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-19.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-20.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-21.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-22.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-23.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-24.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-25.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-26.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-27.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-28.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-29.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-30.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-31.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-32.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-33.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-34.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-35.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-36.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-37.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-38.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-39.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-40.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-41.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-42.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-43.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-44.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-45.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-46.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-47.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/10.9 Crash Fix.imageset/Spectacle-48.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 128@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 16@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 256@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 32@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Spectacle 512@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Scripts/Security & Privacy System Preferences Original.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Scripts/Security & Privacy System Preferences Original.scpt -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "hamcrest/OCHamcrest" "v7.1.2" 2 | github "jonreid/OCMockito" "v5.1.2" 3 | github "sparkle-project/Sparkle" "1.22.0" 4 | github "specta/expecta" "v1.0.6" 5 | github "specta/specta" "v1.0.7" 6 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Security & Privacy.imageset/Security & Privacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Security & Privacy.imageset/Security & Privacy.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Security & Privacy.imageset/Security & Privacy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Security & Privacy.imageset/Security & Privacy@2x.png -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle Status Item.imageset/Spectacle Status Item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle Status Item.imageset/Spectacle Status Item.png -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleDefaultShortcutHelpers.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcut.h" 4 | 5 | NSArray *SpectacleDefaultShortcutsWithAction(SpectacleShortcutAction action); 6 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle Status Item.imageset/Spectacle Status Item@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eczarny/spectacle/HEAD/Spectacle/Resources/Images.xcassets/Spectacle Status Item.imageset/Spectacle Status Item@2x.png -------------------------------------------------------------------------------- /Spectacle.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleRegisteredShortcutValidator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcutValidator.h" 4 | 5 | @interface SpectacleRegisteredShortcutValidator : NSObject 6 | @end 7 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutUserDefaultsStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcutStorage.h" 4 | 5 | @interface SpectacleShortcutUserDefaultsStorage : NSObject 6 | @end 7 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutJSONStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcutStorageMigrationDestination.h" 4 | 5 | @interface SpectacleShortcutJSONStorage : NSObject 6 | @end 7 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleMacros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef SPECTACLE_INIT_AND_NEW_UNAVAILABLE 4 | #define SPECTACLE_INIT_AND_NEW_UNAVAILABLE \ 5 | - (instancetype)init NS_UNAVAILABLE; \ 6 | + (instancetype)new NS_UNAVAILABLE; 7 | #endif // SPECTACLE_INIT_AND_NEW_UNAVAILABLE 8 | -------------------------------------------------------------------------------- /Spectacle.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleFullscreenWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return visibleFrameOfDestinationScreen; 3 | }, "SpectacleWindowActionFullscreen"); 4 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleLoginItemHelper.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SpectacleLoginItemHelper : NSObject 4 | 5 | + (BOOL)isLoginItemEnabledForBundle:(NSBundle *)bundle; 6 | 7 | + (void)enableLoginItemForBundle:(NSBundle *)bundle; 8 | + (void)disableLoginItemForBundle:(NSBundle *)bundle; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleQuantizedWindowMover.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleWindowMover.h" 4 | 5 | @interface SpectacleQuantizedWindowMover : NSObject 6 | 7 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover NS_DESIGNATED_INITIALIZER; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleStandardWindowMover.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleWindowMover.h" 4 | 5 | @interface SpectacleStandardWindowMover : NSObject 6 | 7 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover NS_DESIGNATED_INITIALIZER; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleBestEffortWindowMover.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleWindowMover.h" 4 | 5 | @interface SpectacleBestEffortWindowMover : NSObject 6 | 7 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover NS_DESIGNATED_INITIALIZER; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutStorageMigrationDestination.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcutStorage.h" 4 | 5 | @protocol SpectacleShortcutStorageMigrationDestination 6 | 7 | @property (nonatomic, readonly, assign, getter=isMigrationNeeded) BOOL migrationNeeded; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutValidator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleShortcut; 4 | @class SpectacleShortcutManager; 5 | 6 | @protocol SpectacleShortcutValidator 7 | 8 | - (BOOL)isShortcutValid:(SpectacleShortcut *)shortcut shortcutManager:(SpectacleShortcutManager *)shortcutManager; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | Carthage/Build 21 | Carthage/Checkouts 22 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutKeyBindings.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleShortcut; 4 | 5 | NSNumber *SpectacleConvertShortcutKeyBindingToKeyCode(NSString *keyBinding); 6 | NSNumber *SpectacleConvertShortcutKeyBindingToModifiers(NSString *keyBinding); 7 | 8 | NSString *SpectacleConvertShortcutToKeyBinding(SpectacleShortcut *shortcut); 9 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcut.h" 4 | 5 | @protocol SpectacleShortcutStorage 6 | 7 | - (NSArray *)loadShortcutsWithAction:(SpectacleShortcutAction)action; 8 | 9 | - (void)storeShortcuts:(NSArray *)shortcuts; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleHistory.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleHistoryItem; 4 | 5 | @interface SpectacleHistory : NSObject 6 | 7 | - (void)addHistoryItem:(SpectacleHistoryItem *)historyItem; 8 | 9 | - (SpectacleHistoryItem *)nextHistoryItem; 10 | - (SpectacleHistoryItem *)previousHistoryItem; 11 | 12 | - (BOOL)isEmpty; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.2 2 | language: objective-c 3 | xcode_project: Spectacle.xcodeproj 4 | xcode_scheme: Spectacle 5 | 6 | before_install: 7 | - brew update 8 | - brew outdated carthage || brew upgrade carthage 9 | - carthage bootstrap --platform Mac 10 | 11 | script: 12 | - set -o pipefail && xcodebuild -project Spectacle.xcodeproj -scheme Spectacle test | xcpretty 13 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleLargerWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleWindowSizeAdjuster.resizeWindowRect(windowRect, visibleFrameOfDestinationScreen, 30.0); 3 | }, "SpectacleWindowActionLarger"); 4 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleNextThirdWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleNextOrPreviousThirds.findNextThird(windowRect, visibleFrameOfDestinationScreen); 3 | }, "SpectacleWindowActionNextThird"); 4 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleSmallerWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleWindowSizeAdjuster.resizeWindowRect(windowRect, visibleFrameOfDestinationScreen, -30.0); 3 | }, "SpectacleWindowActionSmaller"); 4 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectaclePreviousThirdWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleNextOrPreviousThirds.findPreviousThird(windowRect, visibleFrameOfDestinationScreen); 3 | }, "SpectacleWindowActionPreviousThird"); 4 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleRegisteredShortcutValidator.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleRegisteredShortcutValidator.h" 2 | 3 | #import "SpectacleShortcutManager.h" 4 | 5 | @implementation SpectacleRegisteredShortcutValidator 6 | 7 | - (BOOL)isShortcutValid:(SpectacleShortcut *)shortcut shortcutManager:(SpectacleShortcutManager *)shortcutManager 8 | { 9 | return ![shortcutManager doesShortcutExist:shortcut]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleNextDisplayWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleNextOrPreviousDisplay.moveWindowRectToDisplay(windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen); 3 | }, "SpectacleWindowActionNextDisplay"); 4 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Security & Privacy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "filename" : "Security & Privacy.png" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "filename" : "Security & Privacy@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectaclePreviousDisplayWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | return SpectacleNextOrPreviousDisplay.moveWindowRectToDisplay(windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen); 3 | }, "SpectacleWindowActionPreviousDisplay"); 4 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle Status Item.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "filename" : "Spectacle Status Item.png" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "filename" : "Spectacle Status Item@2x.png" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleUtilities.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SpectacleUtilities : NSObject 4 | 5 | + (NSString *)applicationVersion; 6 | 7 | + (void)registerDefaultsForBundle:(NSBundle *)bundle; 8 | 9 | + (void)displayRunningInBackgroundAlertWithCallback:(void (^)(BOOL, BOOL))callback; 10 | + (void)displayRestoreDefaultsAlertWithConfirmationCallback:(void (^)(void))callback; 11 | 12 | + (NSString *)pathForPreferencePaneNamed:(NSString *)preferencePaneName; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleJavaScriptEnvironment.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class JSContext; 6 | @class JSValue; 7 | @class SpectacleWindowPositionCalculationResult; 8 | 9 | @interface SpectacleJavaScriptEnvironment : NSObject 10 | 11 | - (instancetype)initWithContextBuilder:(void(^)(JSContext *))contextBuilder NS_DESIGNATED_INITIALIZER; 12 | 13 | - (JSValue *)valueWithRect:(CGRect)rect; 14 | 15 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutRecorderDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleShortcut; 4 | @class SpectacleShortcutRecorder; 5 | 6 | @protocol SpectacleShortcutRecorderDelegate 7 | 8 | - (void)shortcutRecorder:(SpectacleShortcutRecorder *)shortcutRecorder 9 | didReceiveNewShortcut:(SpectacleShortcut *)shortcut; 10 | 11 | - (void)shortcutRecorder:(SpectacleShortcutRecorder *)shortcutRecorder 12 | didClearExistingShortcut:(SpectacleShortcut *)shortcut; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutRecorder.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleShortcut; 4 | @class SpectacleShortcutValidation; 5 | 6 | @protocol SpectacleShortcutRecorderDelegate; 7 | 8 | @interface SpectacleShortcutRecorder : NSView 9 | 10 | @property (nonatomic, copy) NSString *shortcutName; 11 | @property (nonatomic, strong) SpectacleShortcut *shortcut; 12 | @property (nonatomic, strong) SpectacleShortcutValidation *shortcutValidation; 13 | @property (nonatomic, weak) id delegate; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleMigratingShortcutStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | #import "SpectacleShortcutStorage.h" 5 | 6 | @protocol SpectacleShortcutStorageMigrationDestination; 7 | 8 | @interface SpectacleMigratingShortcutStorage : NSObject 9 | 10 | - (instancetype)initWithShortcutStorage:(id)shortcutStorage 11 | migrationDestination:(id)migrationDestination NS_DESIGNATED_INITIALIZER; 12 | 13 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutTranslations.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectacleShortcut; 4 | 5 | NSString *SpectacleTranslateKeyCode(NSInteger keyCode); 6 | NSString *SpectacleTranslateModifiers(NSUInteger modifiers); 7 | NSString *SpectacleTranslateShortcut(SpectacleShortcut *shortcut); 8 | 9 | NSUInteger SpectacleConvertCocoaModifiersToCarbon(NSUInteger modifiers); 10 | NSUInteger SpectacleConvertCarbonModifiersToCocoa(NSUInteger modifiers); 11 | NSUInteger SpectacleConvertModifiersToCarbonIfNecessary(NSUInteger modifiers); 12 | NSUInteger SpectacleConvertModifiersToCocoaIfNecessary(NSUInteger modifiers); 13 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleScreenDetector.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleWindowAction.h" 4 | 5 | @class SpectacleAccessibilityElement; 6 | @class SpectacleScreenDetectionResult; 7 | 8 | @interface SpectacleScreenDetector : NSObject 9 | 10 | - (SpectacleScreenDetectionResult *)screenWithAction:(SpectacleWindowAction *)action 11 | frontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 12 | screens:(NSArray *)screens 13 | mainScreen:(NSScreen *)mainScreen; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutValidation.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class SpectacleShortcut; 6 | @class SpectacleShortcutManager; 7 | 8 | @protocol SpectacleShortcutValidator; 9 | 10 | @interface SpectacleShortcutValidation : NSObject 11 | 12 | - (instancetype)initWithShortcutManager:(SpectacleShortcutManager *)shortcutManager 13 | validators:(NSArray> *)validators NS_DESIGNATED_INITIALIZER; 14 | 15 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 16 | 17 | - (BOOL)isShortcutValid:(SpectacleShortcut *)shortcut error:(NSError **)error; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculationResult.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | #import "SpectacleWindowAction.h" 5 | 6 | @interface SpectacleWindowPositionCalculationResult : NSObject 7 | 8 | @property (nonatomic, readonly, strong) SpectacleWindowAction *action; 9 | @property (nonatomic, readonly, assign) CGRect windowRect; 10 | 11 | - (instancetype)initWithAction:(SpectacleWindowAction *)action windowRect:(CGRect)windowRect NS_DESIGNATED_INITIALIZER; 12 | 13 | + (instancetype)resultWithAction:(SpectacleWindowAction *)action windowRect:(CGRect)windowRect; 14 | 15 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Spectacle/Resources/Property Lists/Defaults.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AutomaticUpdateCheckEnabled 6 | 7 | BackgroundAlertSuppressed 8 | 9 | BlacklistedApplications 10 | 11 | com.adobe.Photoshop 12 | com.divisiblebyzero.Spectacle 13 | com.valvesoftware.steam 14 | 15 | DisabledApplications 16 | 17 | StatusItemEnabled 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleNextOrPreviousDisplay.js: -------------------------------------------------------------------------------- 1 | var SpectacleNextOrPreviousDisplay = (function () { 2 | var moveWindowRectToDisplay = function(windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 3 | if (SpectacleCalculationHelpers.rectFitsWithinRect(windowRect, visibleFrameOfDestinationScreen)) { 4 | return SpectacleCenterWindowCalculation.centerWindowRectInVisibleFrameOfScreen(windowRect, visibleFrameOfDestinationScreen); 5 | } else { 6 | return visibleFrameOfDestinationScreen; 7 | } 8 | }; 9 | return { 10 | moveWindowRectToDisplay: moveWindowRectToDisplay, 11 | }; 12 | })(); 13 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowMover.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleWindowAction.h" 4 | 5 | @class SpectacleAccessibilityElement; 6 | 7 | @protocol SpectacleWindowMover 8 | 9 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover; 10 | 11 | + (instancetype)newWithInnerWindowMover:(id)innerWindowMover; 12 | 13 | - (void)moveWindowRect:(CGRect)windowRect 14 | frameOfScreen:(CGRect)frameOfScreen 15 | visibleFrameOfScreen:(CGRect)visibleFrameOfScreen 16 | frontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 17 | action:(SpectacleWindowAction *)action; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleScreenDetectionResult.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class NSScreen; 6 | 7 | @interface SpectacleScreenDetectionResult : NSObject 8 | 9 | @property (nonatomic, readonly, strong) NSScreen *sourceScreen; 10 | @property (nonatomic, readonly, strong) NSScreen *destinationScreen; 11 | 12 | - (instancetype)initWithSourceScreen:(NSScreen *)sourceScreen 13 | destinationScreen:(NSScreen *)destinationScreen NS_DESIGNATED_INITIALIZER; 14 | 15 | + (instancetype)resultWithSourceScreen:(NSScreen *)sourceScreen 16 | destinationScreen:(NSScreen *)destinationScreen; 17 | 18 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculationResult.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleWindowPositionCalculationResult.h" 2 | 3 | @implementation SpectacleWindowPositionCalculationResult 4 | 5 | @synthesize action = _action; 6 | @synthesize windowRect = _windowRect; 7 | 8 | - (instancetype)initWithAction:(SpectacleWindowAction *)action windowRect:(CGRect)windowRect 9 | { 10 | if (self = [super init]) { 11 | _action = action; 12 | _windowRect = windowRect; 13 | } 14 | return self; 15 | } 16 | 17 | + (instancetype)resultWithAction:(SpectacleWindowAction *)action windowRect:(CGRect)windowRect 18 | { 19 | return [[SpectacleWindowPositionCalculationResult alloc] initWithAction:action windowRect:windowRect]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleHistoryItem.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleHistoryItem.h" 2 | 3 | @implementation SpectacleHistoryItem 4 | 5 | - (instancetype)initWithAccessibilityElement:(SpectacleAccessibilityElement *)accessibilityElement windowRect:(CGRect)windowRect 6 | { 7 | if (self = [super init]) { 8 | _accessibilityElement = accessibilityElement; 9 | _windowRect = windowRect; 10 | } 11 | return self; 12 | } 13 | 14 | + (SpectacleHistoryItem *)historyItemFromAccessibilityElement:(SpectacleAccessibilityElement *)accessibilityElement 15 | windowRect:(CGRect)windowRect 16 | { 17 | return [[SpectacleHistoryItem alloc] initWithAccessibilityElement:accessibilityElement windowRect:windowRect]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleAccessibilityElement.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface SpectacleAccessibilityElement : NSObject 4 | 5 | + (SpectacleAccessibilityElement *)frontmostApplicationElement; 6 | + (SpectacleAccessibilityElement *)frontmostWindowElement; 7 | 8 | - (SpectacleAccessibilityElement *)elementWithAttribute:(CFStringRef)attribute; 9 | 10 | - (NSString *)stringValueOfAttribute:(CFStringRef)attribute; 11 | 12 | - (AXValueRef)valueOfAttribute:(CFStringRef)attribute type:(AXValueType)type; 13 | - (void)setValue:(AXValueRef)value forAttribute:(CFStringRef)attribute; 14 | 15 | - (CGRect)rectOfElement; 16 | - (void)setRectOfElement:(CGRect)rect; 17 | 18 | + (CGRect)normalizeCoordinatesOfRect:(CGRect)rect frameOfScreen:(CGRect)frameOfScreen; 19 | 20 | - (BOOL)isSheet; 21 | - (BOOL)isSystemDialog; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleScreenDetectionResult.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleScreenDetectionResult.h" 2 | 3 | @implementation SpectacleScreenDetectionResult 4 | 5 | @synthesize sourceScreen = _sourceScreen; 6 | @synthesize destinationScreen = _destinationScreen; 7 | 8 | - (instancetype)initWithSourceScreen:(NSScreen *)sourceScreen 9 | destinationScreen:(NSScreen *)destinationScreen 10 | { 11 | if (self = [super init]) { 12 | _sourceScreen = sourceScreen; 13 | _destinationScreen = destinationScreen; 14 | } 15 | return self; 16 | } 17 | 18 | + (instancetype)resultWithSourceScreen:(NSScreen *)sourceScreen 19 | destinationScreen:(NSScreen *)destinationScreen 20 | { 21 | return [[SpectacleScreenDetectionResult alloc] initWithSourceScreen:sourceScreen destinationScreen:destinationScreen]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | #import "SpectacleWindowAction.h" 5 | 6 | @class SpectacleWindowPositionCalculationResult; 7 | 8 | @interface SpectacleWindowPositionCalculator : NSObject 9 | 10 | - (instancetype)initWithErrorHandler:(void(^)(NSString *message))errorHandler NS_DESIGNATED_INITIALIZER; 11 | 12 | - (SpectacleWindowPositionCalculationResult *)calculateWindowRect:(CGRect)windowRect 13 | visibleFrameOfSourceScreen:(CGRect)visibleFrameOfSourceScreen 14 | visibleFrameOfDestinationScreen:(CGRect)visibleFrameOfDestinationScreen 15 | action:(SpectacleWindowAction *)action; 16 | 17 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SpectacleSpecs/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculationRegistry.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowAction.h" 5 | 6 | @class SpectacleWindowPositionCalculationResult; 7 | 8 | typedef CGRect *(^SpectacleWindowPositionCalculation)(CGRect windowRect, 9 | CGRect visibleFrameOfSourceScreen, 10 | CGRect visibleFrameOfDestinationScreen); 11 | 12 | @protocol SpectacleWindowPositionCalculationRegistryExports 13 | 14 | - (void)registerWindowPositionCalculation:(JSValue *)windowPositionCalculation withAction:(NSString *)action; 15 | 16 | @end 17 | 18 | @interface SpectacleWindowPositionCalculationRegistry : NSObject 19 | 20 | - (JSValue *)windowPositionCalculationWithAction:(SpectacleWindowAction *)action; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleHistoryItem.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class SpectacleAccessibilityElement; 6 | 7 | @interface SpectacleHistoryItem : NSObject 8 | 9 | @property (nonatomic, strong) SpectacleAccessibilityElement *accessibilityElement; 10 | @property (nonatomic, assign) CGRect windowRect; 11 | @property (nonatomic, strong) SpectacleHistoryItem *nextHistoryItem; 12 | @property (nonatomic, weak) SpectacleHistoryItem *previousHistoryItem; 13 | 14 | - (instancetype)initWithAccessibilityElement:(SpectacleAccessibilityElement *)accessibilityElement 15 | windowRect:(CGRect)windowRect NS_DESIGNATED_INITIALIZER; 16 | 17 | + (SpectacleHistoryItem *)historyItemFromAccessibilityElement:(SpectacleAccessibilityElement *)accessibilityElement 18 | windowRect:(CGRect)windowRect; 19 | 20 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class SpectacleShortcut; 6 | 7 | @protocol SpectacleShortcutStorage; 8 | 9 | @interface SpectacleShortcutManager : NSObject 10 | 11 | - (instancetype)initWithShortcutStorage:(id)shortcutStorage NS_DESIGNATED_INITIALIZER; 12 | 13 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 14 | 15 | - (void)manageShortcuts:(NSArray *)shortcuts; 16 | 17 | - (void)updateShortcut:(SpectacleShortcut *)shortcut; 18 | - (void)updateShortcuts:(NSArray *)shortcuts; 19 | 20 | - (void)clearShortcut:(SpectacleShortcut *)shortcut; 21 | 22 | - (NSArray *)shortcuts; 23 | - (SpectacleShortcut *)shortcutForShortcutName:(NSString *)shortcutName; 24 | 25 | - (BOOL)doesShortcutExist:(SpectacleShortcut *)shortcut; 26 | 27 | - (void)registerShortcuts; 28 | - (void)unregisterShortcuts; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleWindowCalculationHelpers.js: -------------------------------------------------------------------------------- 1 | var SpectacleCalculationHelpers = (function () { 2 | var copyRect = function(rect) { 3 | return { 4 | x: rect.x, 5 | y: rect.y, 6 | width: rect.width, 7 | height: rect.height, 8 | }; 9 | }; 10 | var rectCenteredWithinRect = function(rect1, rect2) { 11 | var centeredMidX = Math.abs(CGRectGetMidX(rect2) - CGRectGetMidX(rect1)) <= 1.0; 12 | var centeredMidY = Math.abs(CGRectGetMidY(rect2) - CGRectGetMidY(rect1)) <= 1.0; 13 | return CGRectContainsRect(rect1, rect2) && centeredMidX && centeredMidY; 14 | }; 15 | var rectFitsWithinRect = function(rect1, rect2) { 16 | return (rect1.width <= rect2.width) && (rect1.height <= rect2.height); 17 | }; 18 | return { 19 | copyRect: copyRect, 20 | rectCenteredWithinRect: rectCenteredWithinRect, 21 | rectFitsWithinRect: rectFitsWithinRect, 22 | }; 23 | })(); 24 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleCenterWindowCalculation.js: -------------------------------------------------------------------------------- 1 | var SpectacleCenterWindowCalculation = (function () { 2 | var centerWindowRectInVisibleFrameOfScreen = function(windowRect, visibleFrameOfScreen) { 3 | var calculatedWindowRect = SpectacleCalculationHelpers.copyRect(windowRect); 4 | calculatedWindowRect.x = Math.round((visibleFrameOfScreen.width - windowRect.width) / 2.0) + visibleFrameOfScreen.x; 5 | calculatedWindowRect.y = Math.round((visibleFrameOfScreen.height - windowRect.height) / 2.0) + visibleFrameOfScreen.y; 6 | return calculatedWindowRect; 7 | }; 8 | return { 9 | centerWindowRectInVisibleFrameOfScreen: centerWindowRectInVisibleFrameOfScreen, 10 | }; 11 | })(); 12 | 13 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 14 | return SpectacleCenterWindowCalculation.centerWindowRectInVisibleFrameOfScreen(windowRect, visibleFrameOfDestinationScreen); 15 | }, "SpectacleWindowActionCenter"); 16 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutHolder.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | 5 | @class SpectacleShortcut; 6 | 7 | @interface SpectacleShortcutHolder : NSObject 8 | 9 | @property (nonatomic, readonly, assign) EventHotKeyID shortcutID; 10 | @property (nonatomic, readonly, strong) SpectacleShortcut *shortcut; 11 | @property (nonatomic, readonly, assign) EventHotKeyRef shortcutRef; 12 | 13 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID; 14 | 15 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID 16 | shortcut:(SpectacleShortcut *)shortcut; 17 | 18 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID 19 | shortcut:(SpectacleShortcut *)shortcut 20 | shortcutRef:(EventHotKeyRef)shortcutRef NS_DESIGNATED_INITIALIZER; 21 | 22 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 23 | 24 | - (instancetype)copyWithShortcut:(SpectacleShortcut *)shortcut; 25 | - (instancetype)copyWithShortcutRef:(EventHotKeyRef)shortcutRef; 26 | - (instancetype)copyWithClearedShortcut; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc 6 | 7 | \f0\b\fs24 \cf0 Credits 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 10 | \cf0 Icons and other artwork: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 17 | \cf0 Source code available on {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHub}} 19 | \b \ 20 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt @spectacleapp}}\ 21 | } -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculationRegistry.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleWindowPositionCalculationRegistry.h" 2 | 3 | #import "SpectacleJavaScriptEnvironment.h" 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | 6 | @interface SpectacleWindowPositionCalculationRegistry () 7 | 8 | @property (nonatomic, readonly, strong) SpectacleJavaScriptEnvironment *javaScriptEnvironment; 9 | 10 | @end 11 | 12 | @implementation SpectacleWindowPositionCalculationRegistry 13 | { 14 | NSMutableDictionary *_windowPositionCalculations; 15 | } 16 | 17 | - (instancetype)init 18 | { 19 | if (self = [super init]) { 20 | _windowPositionCalculations = [NSMutableDictionary new]; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)registerWindowPositionCalculation:(JSValue *)windowPositionCalculation withAction:(SpectacleWindowAction *)action 26 | { 27 | _windowPositionCalculations[action] = windowPositionCalculation; 28 | } 29 | 30 | - (JSValue *)windowPositionCalculationWithAction:(SpectacleWindowAction *)action 31 | { 32 | return _windowPositionCalculations[action]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleMigratingShortcutStorage.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleMigratingShortcutStorage.h" 2 | 3 | #import "SpectacleShortcutStorageMigrationDestination.h" 4 | 5 | @implementation SpectacleMigratingShortcutStorage 6 | { 7 | id _shortcutStorage; 8 | id _migrationDestination; 9 | } 10 | 11 | - (instancetype)initWithShortcutStorage:(id)shortcutStorage 12 | migrationDestination:(id)migrationDestination 13 | { 14 | if (self = [super init]) { 15 | _shortcutStorage = shortcutStorage; 16 | _migrationDestination = migrationDestination; 17 | } 18 | return self; 19 | } 20 | 21 | - (NSArray *)loadShortcutsWithAction:(SpectacleShortcutAction)action 22 | { 23 | return _migrationDestination.isMigrationNeeded ? [_shortcutStorage loadShortcutsWithAction:action] : [_migrationDestination loadShortcutsWithAction:action]; 24 | } 25 | 26 | - (void)storeShortcuts:(NSArray *)shortcuts 27 | { 28 | [_migrationDestination storeShortcuts:shortcuts]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleLeftHalfWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneHalfRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneHalfRect.width = Math.floor(oneHalfRect.width / 2.0); 4 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneHalfRect)) <= 1.0) { 5 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 6 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 7 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneHalfRect, windowRect)) { 8 | return twoThirdRect; 9 | } 10 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 11 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 12 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 13 | return oneThirdsRect; 14 | } 15 | } 16 | return oneHalfRect; 17 | }, "SpectacleWindowActionLeftHalf"); 18 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleBottomHalfWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneHalfRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneHalfRect.height = Math.floor(oneHalfRect.height / 2.0); 4 | if (Math.abs(CGRectGetMidX(windowRect) - CGRectGetMidX(oneHalfRect)) <= 1.0) { 5 | var twoThirdsRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 6 | twoThirdsRect.height = Math.floor(visibleFrameOfDestinationScreen.height * 2 / 3.0); 7 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneHalfRect, windowRect)) { 8 | return twoThirdsRect; 9 | } 10 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdsRect, windowRect)) { 11 | var oneThirdRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 12 | oneThirdRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 3.0); 13 | return oneThirdRect; 14 | } 15 | } 16 | return oneHalfRect; 17 | }, "SpectacleWindowActionBottomHalf"); 18 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fr.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc 6 | 7 | \f0\b\fs24 \cf0 Cr\'e9dits 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 10 | \cf0 Ic\'f4nes et autres \'9cuvres: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 17 | \cf0 Code source disponible sur {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHub}} 19 | \b \ 20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 21 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt \cf0 @spectacleapp}}\ 22 | } -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/it.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc 6 | 7 | \f0\b\fs24 \cf0 Riconoscimenti 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 10 | \cf0 Icone ed altri elementi grafici: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 17 | \cf0 Codice sorgente disponibile su {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHub}} 19 | \b \ 20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 21 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt \cf0 @spectacleapp}}\ 22 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | ## Spectacle is distributed under the MIT License: 4 | 5 | Copyright (c) 2017 Eric Czarny 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /Spectacle/Resources/Certificates/dsa_public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIDOjCCAi0GByqGSM44BAEwggIgAoIBAQCY2D8N/yE2yx4+fpl4m4M2FzaBOcY8 3 | B/bFN3bExJcapUAn0IpTvUDO0SZCw28WPQuEnu+RAq00UULTtGtO1H6J975csVBC 4 | KSx5YbSlcui8MzUASHC99+GfccPCSYXNgqzdq72gdQI0/QRZcBA2MrFDFtNOneCZ 5 | 0hCOrROwKB5B2iZ6duITRmcOBkcgx+/3sWPmi3860yCbUymhfIPsvyefJCTwDJbc 6 | ulQm4SxPykseLauJluhbSyU5o82CiN2bohv8qzPGq5X29hO77u10QLuyNdpE6pHc 7 | I+CmAxY87Z/vgVxf63RbFqZ9HSi3VI3sH9X+imzP8eCrI44I4NS/6o7zAhUArcVK 8 | GobiXm0fqWZpD7DrcgnEOF8CggEAJLczjeNtMNTRSrhh5SR06FG2sUiCIe1hHPCz 9 | +3WPK+rPDnoC4gft6QxO9zRlnuvISeqNhv1oge5phdVQDWLhUojAQwAi2DU+/hjn 10 | 7HjZx0PHs+oE568dH9qyB9F9xKLPpzHld2PJB9t1OMQjrbXuqFSjxecx7Y28f5D6 11 | IaMCS8EwgifS2vMyoPXW8MGpZ6iB5uPHFUZFEvGp6k0+T2V32F5JMgt37DlHA/Ok 12 | kmEMWU2JF14e3O1zU3h6ERhXdmNeRA9rarETO9mW8qLg95qRCR73I7CdhMSrY7/S 13 | 6s7bPbPR4VKwORP8LJTKbb7bXQpXrso34hZIOPhL/RUuuJqrGQOCAQUAAoIBAFWc 14 | 5KE8FFG8IrA+MG6G1gg0aDOPlrpE4Br/YrivsKlhP+9/jaCW+ESeukJIrNI8uw48 15 | 9e9s/WrqH8N5EH1AhTUd3yOAu9zyvj/FLyMnnfFCwHe36uyRUFE9k0MR4jS9NOA6 16 | MEGTbPevEyZFwsioJCQOWGmr94LlTfRP1FXkSN7F7xvPqieUG3lMzD5tq+rqKS0R 17 | G56LWDh2ccbR+HZXtC513HHz4PUUCy/ukcThdND2ca90qCWtqDIw46GxRyROt1wM 18 | qhACEQqijf1GCzEWxSMibVRtTIF2/AWOOOdi8FMOloLnln1R5o3X6XY5nSQ3bj4m 19 | ZvUkmWLQi6tvBv98A6Q= 20 | -----END PUBLIC KEY----- 21 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/pt.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1348\cocoasubrtf170 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 6 | 7 | \f0\b\fs24 \cf0 Cr\'e9ditos 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 10 | \cf0 \'cdcones e outras ilustra\'e7\'f5es: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 17 | \cf0 C\'f3digo fonte dispon\'edvel no {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHub}} 19 | \b \ 20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc 21 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt \cf0 @spectacleapp}}\ 22 | } -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fi.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf420 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 6 | 7 | \f0\b\fs24 \cf0 Tekij\'e4t 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 10 | \cf0 Kuvakkeet ja muut grafiikat: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 17 | \cf0 L\'e4hdekoodi saatavilla {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHubissa}} 19 | \b \ 20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 21 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt \cf0 @spectacleapp}}\ 22 | } -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleLowerLeftWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneQuarterRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneQuarterRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 2.0); 4 | oneQuarterRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 2.0); 5 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneQuarterRect)) <= 1.0) { 6 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 7 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 8 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneQuarterRect, windowRect)) { 9 | return twoThirdRect; 10 | } 11 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 12 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 13 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 14 | return oneThirdsRect; 15 | } 16 | } 17 | return oneQuarterRect; 18 | }, "SpectacleWindowActionLowerLeft"); 19 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/es.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \paperw12240\paperh15840\margl1440\margr1440\vieww11300\viewh8400\viewkind0 5 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0 6 | 7 | \f0\b\fs24 \cf0 Credits 8 | \b0 \ 9 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 10 | \cf0 Iconos y dise\'f1o gr\'e1fico: {\field{\*\fldinst{HYPERLINK "http://iiro.jappinen.fi"}}{\fldrslt 11 | \b Iiro J\'e4ppinen}} 12 | \b \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 14 | 15 | \b0 \cf0 \ 16 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 17 | \cf0 C\'f3digo fuente disponible en {\field{\*\fldinst{HYPERLINK "https://github.com/eczarny/spectacle"}}{\fldrslt 18 | \b GitHub}} 19 | \b \ 20 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 21 | {\field{\*\fldinst{HYPERLINK "https://twitter.com/spectacleapp"}}{\fldrslt \cf0 @spectacleapp}}\ 22 | } -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleCenterWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleCenterWindowCalculation) 8 | describe(@"SpectacleCenterWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a centered window's CGRect", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionCenter]; 20 | expect(result.windowRect).to.equal(CGRectMake(438.0f, 249.0f, 564.0f, 384.0f)); 21 | }); 22 | }); 23 | SpecEnd 24 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleFullscreenWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleFullscreenWindowCalculation) 8 | describe(@"SpectacleFullscreenWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a fullscreen window's CGRect", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionFullscreen]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f)); 21 | }); 22 | }); 23 | SpecEnd 24 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleLowerLeftWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleLowerLeftWindowCalculation) 8 | describe(@"SpectacleLowerLeftWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the lower left corner of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionLowerLeft]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 720.0f, 436.0f)); 21 | }); 22 | }); 23 | SpecEnd 24 | -------------------------------------------------------------------------------- /Spectacle/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | Spectacle 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Spectacle 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.2 19 | CFBundleSignature 20 | ZERO 21 | CFBundleVersion 22 | 0 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | 10.9 27 | LSUIElement 28 | 29 | NSMainNibFile 30 | Spectacle 31 | NSPrincipalClass 32 | NSApplication 33 | SUAllowsAutomaticUpdates 34 | 35 | SUEnableAutomaticChecks 36 | 37 | SUFeedURL 38 | https://spectacleapp.com/updates/appcast.xml 39 | SUPublicDSAKeyFile 40 | dsa_public.pem 41 | 42 | 43 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleNextDisplayWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleNextDisplayWindowCalculation) 8 | describe(@"SpectacleNextDisplayWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(1440.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a centered window's CGRect in the next display", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionNextDisplay]; 20 | expect(result.windowRect).to.equal(CGRectMake(1878.0f, 249.0f, 564.0f, 384.0f)); 21 | }); 22 | }); 23 | SpecEnd 24 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleLowerRightWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleLowerRightWindowCalculation) 8 | describe(@"SpectacleLowerRightWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the lower right corner of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionLowerRight]; 20 | 21 | expect(result.windowRect).to.equal(CGRectMake(720.0f, 4.0f, 720.0f, 436.0f)); 22 | }); 23 | }); 24 | SpecEnd 25 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectaclePreviousDisplayWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectaclePreviousDisplayWindowCalculation) 8 | describe(@"SpectaclePreviousDisplayWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(1440.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a centered window's CGRect in the previous display", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionPreviousDisplay]; 20 | expect(result.windowRect).to.equal(CGRectMake(1878.0f, 249.0f, 564.0f, 384.0f)); 21 | }); 22 | }); 23 | SpecEnd 24 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleRightHalfWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneHalfRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneHalfRect.width = Math.floor(oneHalfRect.width / 2.0); 4 | oneHalfRect.x += oneHalfRect.width; 5 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneHalfRect)) <= 1.0) { 6 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 7 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 8 | twoThirdRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - twoThirdRect.width; 9 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneHalfRect, windowRect)) { 10 | return twoThirdRect; 11 | } 12 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 13 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 14 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 15 | oneThirdsRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - oneThirdsRect.width; 16 | return oneThirdsRect; 17 | } 18 | } 19 | return oneHalfRect; 20 | }, "SpectacleWindowActionRightHalf"); 21 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleUpperLeftWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneQuarterRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneQuarterRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 2.0); 4 | oneQuarterRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 2.0); 5 | oneQuarterRect.y = visibleFrameOfDestinationScreen.y + Math.floor(visibleFrameOfDestinationScreen.height / 2.0) + (visibleFrameOfDestinationScreen.height % 2.0); 6 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneQuarterRect)) <= 1.0) { 7 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 8 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 9 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneQuarterRect, windowRect)) { 10 | return twoThirdRect; 11 | } 12 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 13 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 14 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 15 | return oneThirdsRect; 16 | } 17 | } 18 | return oneQuarterRect; 19 | }, "SpectacleWindowActionUpperLeft"); 20 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleJavaScriptEnvironment.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleJavaScriptEnvironment.h" 2 | 3 | #import 4 | 5 | @implementation SpectacleJavaScriptEnvironment 6 | { 7 | JSContext *_context; 8 | } 9 | 10 | - (instancetype)initWithContextBuilder:(void(^)(JSContext *))contextBuilder 11 | { 12 | if (self = [super init]) { 13 | _context = [JSContext new]; 14 | contextBuilder(_context); 15 | [self evaluateBundledScripts]; 16 | } 17 | return self; 18 | } 19 | 20 | - (JSValue *)valueWithRect:(CGRect)rect 21 | { 22 | return [JSValue valueWithRect:rect inContext:_context]; 23 | } 24 | 25 | - (void)evaluateBundledScripts 26 | { 27 | NSArray *scriptPaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"js" 28 | inDirectory:@"Window Position Calculations"]; 29 | [scriptPaths enumerateObjectsUsingBlock:^(NSString *scriptPath, NSUInteger index, BOOL *stop) { 30 | NSString *script = loadScriptAtPath(scriptPath); 31 | if (script) { 32 | [self->_context evaluateScript:script]; 33 | } 34 | }]; 35 | } 36 | 37 | static NSString *loadScriptAtPath(NSString *scriptPath) 38 | { 39 | NSError *error; 40 | NSString *script = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:&error]; 41 | if (!script) { 42 | NSLog(@"Unable to load script at path %@; reason %@", scriptPath, error.localizedDescription); 43 | } 44 | return script; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleStandardWindowMover.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleStandardWindowMover.h" 2 | 3 | #import "SpectacleAccessibilityElement.h" 4 | 5 | @implementation SpectacleStandardWindowMover 6 | { 7 | id _innerWindowMover; 8 | } 9 | 10 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover 11 | { 12 | if (self = [super init]) { 13 | _innerWindowMover = innerWindowMover; 14 | } 15 | return self; 16 | } 17 | 18 | - (instancetype)init 19 | { 20 | return [self initWithInnerWindowMover:nil]; 21 | } 22 | 23 | + (instancetype)newWithInnerWindowMover:(id)innerWindowMover 24 | { 25 | return [[self alloc] initWithInnerWindowMover:innerWindowMover]; 26 | } 27 | 28 | - (void)moveWindowRect:(CGRect)windowRect 29 | frameOfScreen:(CGRect)frameOfScreen 30 | visibleFrameOfScreen:(CGRect)visibleFrameOfScreen 31 | frontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 32 | action:(SpectacleWindowAction *)action 33 | { 34 | CGRect previousWindowRect = [frontmostWindowElement rectOfElement]; 35 | if (CGRectIsNull(previousWindowRect)) { 36 | return; 37 | } 38 | [frontmostWindowElement setRectOfElement:windowRect]; 39 | [_innerWindowMover moveWindowRect:windowRect 40 | frameOfScreen:frameOfScreen 41 | visibleFrameOfScreen:visibleFrameOfScreen 42 | frontmostWindowElement:frontmostWindowElement 43 | action:action]; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleTopHalfWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneHalfRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneHalfRect.height = Math.floor(oneHalfRect.height / 2.0); 4 | oneHalfRect.y += oneHalfRect.height + (visibleFrameOfDestinationScreen.height % 2.0); 5 | if (Math.abs(CGRectGetMidX(windowRect) - CGRectGetMidX(oneHalfRect)) <= 1.0) { 6 | var twoThirdsRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 7 | twoThirdsRect.height = Math.floor(visibleFrameOfDestinationScreen.height * 2 / 3.0); 8 | twoThirdsRect.y = visibleFrameOfDestinationScreen.y + visibleFrameOfDestinationScreen.height - twoThirdsRect.height; 9 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneHalfRect, windowRect)) { 10 | return twoThirdsRect; 11 | } 12 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdsRect, windowRect)) { 13 | var oneThirdRect = SpectacleCalculationHelpers.copyRect(oneHalfRect); 14 | oneThirdRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 3.0); 15 | oneThirdRect.y = visibleFrameOfDestinationScreen.y + visibleFrameOfDestinationScreen.height - oneThirdRect.height; 16 | return oneThirdRect; 17 | } 18 | } 19 | return oneHalfRect; 20 | }, "SpectacleWindowActionTopHalf"); 21 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleLowerRightWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneQuarterRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneQuarterRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 2.0); 4 | oneQuarterRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 2.0); 5 | oneQuarterRect.x += oneQuarterRect.width; 6 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneQuarterRect)) <= 1.0) { 7 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 8 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 9 | twoThirdRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - twoThirdRect.width; 10 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneQuarterRect, windowRect)) { 11 | return twoThirdRect; 12 | } 13 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 14 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 15 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 16 | oneThirdsRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - oneThirdsRect.width; 17 | return oneThirdsRect; 18 | } 19 | } 20 | return oneQuarterRect; 21 | }, "SpectacleWindowActionLowerRight"); 22 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleHistory.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleHistory.h" 2 | 3 | #import "SpectacleHistoryItem.h" 4 | 5 | @implementation SpectacleHistory 6 | { 7 | SpectacleHistoryItem *_lastHistoryItem; 8 | SpectacleHistoryItem *_currentHistoryItem; 9 | NSInteger _size; 10 | } 11 | 12 | - (void)addHistoryItem:(SpectacleHistoryItem *)historyItem 13 | { 14 | if ([self isEmpty]) { 15 | _currentHistoryItem = historyItem; 16 | _lastHistoryItem = _currentHistoryItem; 17 | } else { 18 | historyItem.previousHistoryItem = _currentHistoryItem; 19 | _currentHistoryItem.nextHistoryItem.previousHistoryItem = historyItem; 20 | _currentHistoryItem.nextHistoryItem = historyItem; 21 | if (![historyItem previousHistoryItem]) { 22 | _lastHistoryItem = historyItem; 23 | } 24 | _currentHistoryItem = historyItem; 25 | } 26 | if (++_size > 50) { 27 | _lastHistoryItem.nextHistoryItem.previousHistoryItem = nil; 28 | _lastHistoryItem = _lastHistoryItem.nextHistoryItem; 29 | _size--; 30 | } 31 | } 32 | 33 | - (SpectacleHistoryItem *)nextHistoryItem 34 | { 35 | SpectacleHistoryItem *historyItem = _currentHistoryItem.nextHistoryItem; 36 | if (historyItem) { 37 | _currentHistoryItem = historyItem; 38 | _size++; 39 | } 40 | return historyItem; 41 | } 42 | 43 | - (SpectacleHistoryItem *)previousHistoryItem 44 | { 45 | SpectacleHistoryItem *historyItem = _currentHistoryItem.previousHistoryItem; 46 | if (historyItem) { 47 | _currentHistoryItem = historyItem; 48 | _size--; 49 | } 50 | return historyItem; 51 | } 52 | 53 | - (BOOL)isEmpty 54 | { 55 | return _size == 0; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Spectacle/Resources/Images.xcassets/Spectacle.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "Spectacle 16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "Spectacle 16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "Spectacle 32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "Spectacle 32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "Spectacle 128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "Spectacle 128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "Spectacle 256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "Spectacle 256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "Spectacle 512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "Spectacle 512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | #import "SpectacleWindowAction.h" 5 | 6 | typedef void (^SpectacleFailureFeedback)(void); 7 | 8 | @class SpectacleAccessibilityElement; 9 | @class SpectacleScreenDetector; 10 | @class SpectacleShortcut; 11 | @class SpectacleWindowPositionCalculator; 12 | 13 | @protocol SpectacleWindowMover; 14 | 15 | @interface SpectacleWindowPositionManager : NSObject 16 | 17 | - (instancetype)initWithScreenDetector:(SpectacleScreenDetector *)screenDetector 18 | windowPositionCalculator:(SpectacleWindowPositionCalculator *)windowPositionCalculator 19 | sharedWorkspace:(NSWorkspace *)sharedWorkspace 20 | failureFeedback:(SpectacleFailureFeedback)failureFeedback 21 | windowMover:(id)windowMover NS_DESIGNATED_INITIALIZER; 22 | 23 | - (instancetype)initWithScreenDetector:(SpectacleScreenDetector *)screenDetector 24 | windowPositionCalculator:(SpectacleWindowPositionCalculator *)windowPositionCalculator 25 | sharedWorkspace:(NSWorkspace *)sharedWorkspace; 26 | 27 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 28 | 29 | - (void)moveFrontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 30 | action:(SpectacleWindowAction *)action 31 | screens:(NSArray *)screens 32 | mainScreen:(NSScreen *)mainScreen; 33 | 34 | - (void)moveFrontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 35 | action:(SpectacleWindowAction *)action; 36 | 37 | - (void)undoLastWindowAction; 38 | - (void)redoLastWindowAction; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleUpperRightWindowCalculation.js: -------------------------------------------------------------------------------- 1 | windowPositionCalculationRegistry.registerWindowPositionCalculationWithAction(function (windowRect, visibleFrameOfSourceScreen, visibleFrameOfDestinationScreen) { 2 | var oneQuarterRect = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 3 | oneQuarterRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 2.0); 4 | oneQuarterRect.height = Math.floor(visibleFrameOfDestinationScreen.height / 2.0); 5 | oneQuarterRect.x += oneQuarterRect.width; 6 | oneQuarterRect.y = visibleFrameOfDestinationScreen.y + Math.floor(visibleFrameOfDestinationScreen.height / 2.0) + (visibleFrameOfDestinationScreen.height % 2.0); 7 | if (Math.abs(CGRectGetMidY(windowRect) - CGRectGetMidY(oneQuarterRect)) <= 1.0) { 8 | var twoThirdRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 9 | twoThirdRect.width = Math.floor(visibleFrameOfDestinationScreen.width * 2 / 3.0); 10 | twoThirdRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - twoThirdRect.width; 11 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(oneQuarterRect, windowRect)) { 12 | return twoThirdRect; 13 | } 14 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(twoThirdRect, windowRect)) { 15 | var oneThirdsRect = SpectacleCalculationHelpers.copyRect(oneQuarterRect); 16 | oneThirdsRect.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 17 | oneThirdsRect.x = visibleFrameOfDestinationScreen.x + visibleFrameOfDestinationScreen.width - oneThirdsRect.width; 18 | return oneThirdsRect; 19 | } 20 | } 21 | return oneQuarterRect; 22 | }, "SpectacleWindowActionUpperRight"); 23 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowAction.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef NSString SpectacleWindowAction; 4 | 5 | extern SpectacleWindowAction *const kSpectacleWindowActionUndo; 6 | extern SpectacleWindowAction *const kSpectacleWindowActionRedo; 7 | extern SpectacleWindowAction *const kSpectacleWindowActionLarger; 8 | extern SpectacleWindowAction *const kSpectacleWindowActionSmaller; 9 | extern SpectacleWindowAction *const kSpectacleWindowActionNone; 10 | extern SpectacleWindowAction *const kSpectacleWindowActionCenter; 11 | extern SpectacleWindowAction *const kSpectacleWindowActionFullscreen; 12 | extern SpectacleWindowAction *const kSpectacleWindowActionLeftHalf; 13 | extern SpectacleWindowAction *const kSpectacleWindowActionUpperLeft; 14 | extern SpectacleWindowAction *const kSpectacleWindowActionLowerLeft; 15 | extern SpectacleWindowAction *const kSpectacleWindowActionRightHalf; 16 | extern SpectacleWindowAction *const kSpectacleWindowActionUpperRight; 17 | extern SpectacleWindowAction *const kSpectacleWindowActionLowerRight; 18 | extern SpectacleWindowAction *const kSpectacleWindowActionTopHalf; 19 | extern SpectacleWindowAction *const kSpectacleWindowActionBottomHalf; 20 | extern SpectacleWindowAction *const kSpectacleWindowActionNextDisplay; 21 | extern SpectacleWindowAction *const kSpectacleWindowActionPreviousDisplay; 22 | extern SpectacleWindowAction *const kSpectacleWindowActionNextThird; 23 | extern SpectacleWindowAction *const kSpectacleWindowActionPreviousThird; 24 | 25 | BOOL SpectacleIsUndoWindowAction(SpectacleWindowAction *action); 26 | BOOL SpectacleIsRedoWindowAction(SpectacleWindowAction *action); 27 | 28 | BOOL SpectacleIsNextDisplayWindowAction(SpectacleWindowAction *action); 29 | BOOL SpectacleIsPreviousDisplayWindowAction(SpectacleWindowAction *action); 30 | BOOL SpectacleIsMovingToDisplayWindowAction(SpectacleWindowAction *action); 31 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcut.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleMacros.h" 4 | #import "SpectacleWindowAction.h" 5 | 6 | @class SpectacleShortcut; 7 | 8 | typedef void(^SpectacleShortcutAction)(SpectacleShortcut *); 9 | 10 | @interface SpectacleShortcut : NSObject 11 | 12 | @property (nonatomic, readonly, copy) NSString *shortcutName; 13 | @property (nonatomic, readonly, assign) NSInteger shortcutKeyCode; 14 | @property (nonatomic, readonly, assign) NSUInteger shortcutModifiers; 15 | @property (nonatomic, readonly, strong) SpectacleShortcutAction shortcutAction; 16 | @property (nonatomic, readonly, copy) NSString *shortcutKeyBinding; 17 | @property (nonatomic, readonly, strong) SpectacleWindowAction *windowAction; 18 | 19 | - (instancetype)initWithShortcutName:(NSString *)shortcutName shortcutKeyBinding:(NSString *)shortcutKeyBinding; 20 | 21 | - (instancetype)initWithShortcutName:(NSString *)shortcutName 22 | shortcutKeyBinding:(NSString *)shortcutKeyBinding 23 | shortcutAction:(SpectacleShortcutAction)shortcutAction; 24 | 25 | - (instancetype)initWithShortcutName:(NSString *)shortcutName 26 | shortcutKeyCode:(NSInteger)shortcutKeyCode 27 | shortcutModifiers:(NSUInteger)shortcutModifiers; 28 | 29 | - (instancetype)initWithShortcutName:(NSString *)shortcutName 30 | shortcutKeyCode:(NSInteger)shortcutKeyCode 31 | shortcutModifiers:(NSUInteger)shortcutModifiers 32 | shortcutAction:(SpectacleShortcutAction)shortcutAction NS_DESIGNATED_INITIALIZER; 33 | 34 | SPECTACLE_INIT_AND_NEW_UNAVAILABLE 35 | 36 | - (instancetype)copyWithShortcutAction:(SpectacleShortcutAction)shortcutAction; 37 | 38 | - (void)triggerShortcutAction; 39 | 40 | - (BOOL)isClearedShortcut; 41 | 42 | - (NSString *)displayString; 43 | 44 | - (BOOL)isEqual:(id)object; 45 | - (BOOL)isEqualToShortcut:(SpectacleShortcut *)shortcut; 46 | 47 | - (BOOL)containsModifiers:(NSUInteger)modifiers; 48 | 49 | + (BOOL)validCocoaModifiers:(NSUInteger)modifiers; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions come in many forms. Creating an issue that documents a previously unreported defect is just as important as sending a pull request. Spectacle can only be great when the folks that use it are passionate about its quality. 4 | 5 | Before you start working on your contribution to Spectacle please keep reading. For even more information read [Contributing to Open Source on GitHub][1]. 6 | 7 | ## Issues 8 | 9 | Before creating a new issue check and make sure somebody hasn't already beat you to it. _Please take this time to also review Spectacle's [common issues][2]_. If your defect or feature request doesn't already exist feel free to create a new issue. When creating a new issue take the time to be as clear and descriptive as possible. 10 | 11 | Here are a few things to keep in mind when creating an issue for a potential defect: 12 | 13 | * What version of Spectacle did you use when encountering the defect? 14 | * What version of OS X did you use when encountering the defect? 15 | * Was there any relevant Spectacle output in the [Console][3]? 16 | 17 | Including screenshots or videos are extremely helpful so please include them if you can. Diagrams can also be highly valuable when describing a new feature in a feature request. 18 | 19 | ## Pull Requests 20 | 21 | 1. Fork the repository and create your branch from `master` 22 | 2. Add tests if your changes can and should be tested 23 | 3. Run the test suite and confirm that all tests pass 24 | 4. Rebase your changes on top of the most recent changes from `master` 25 | 26 | ## Coding Style 27 | 28 | To help make it easier maintain Spectacle please match the existing coding style as much as possible. This make it easier to both understand and merge your contributions. Only use two __spaces__ for indentation. 29 | 30 | ## License 31 | 32 | By contributing to Spectacle you agree that your contributions will be licensed under its MIT license. 33 | 34 | [1]: https://guides.github.com/activities/contributing-to-open-source/#contributing 35 | [2]: https://github.com/eczarny/spectacle#common-issues 36 | [3]: https://en.wikipedia.org/wiki/Console_(OS_X) 37 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutHolder.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleShortcutHolder.h" 2 | 3 | #import "SpectacleShortcut.h" 4 | 5 | @implementation SpectacleShortcutHolder 6 | 7 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID 8 | { 9 | return [self initWithShortcutID:shortcutID shortcut:nil shortcutRef:nil]; 10 | } 11 | 12 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID 13 | shortcut:(SpectacleShortcut *)shortcut 14 | { 15 | return [self initWithShortcutID:shortcutID shortcut:shortcut shortcutRef:nil]; 16 | } 17 | 18 | - (instancetype)initWithShortcutID:(EventHotKeyID)shortcutID 19 | shortcut:(SpectacleShortcut *)shortcut 20 | shortcutRef:(EventHotKeyRef)shortcutRef 21 | { 22 | if (self = [super init]) { 23 | _shortcutID = shortcutID; 24 | _shortcut = shortcut; 25 | _shortcutRef = shortcutRef; 26 | } 27 | return self; 28 | } 29 | 30 | - (instancetype)copyWithShortcut:(SpectacleShortcut *)shortcut 31 | { 32 | return [[SpectacleShortcutHolder alloc] initWithShortcutID:_shortcutID 33 | shortcut:shortcut 34 | shortcutRef:_shortcutRef]; 35 | } 36 | 37 | - (instancetype)copyWithShortcutRef:(EventHotKeyRef)shortcutRef 38 | { 39 | return [[SpectacleShortcutHolder alloc] initWithShortcutID:_shortcutID 40 | shortcut:_shortcut 41 | shortcutRef:shortcutRef]; 42 | } 43 | 44 | - (instancetype)copyWithClearedShortcut 45 | { 46 | SpectacleShortcut *clearedShortcut = [[SpectacleShortcut alloc] initWithShortcutName:_shortcut.shortcutName 47 | shortcutKeyCode:-1 48 | shortcutModifiers:0]; 49 | return [[SpectacleShortcutHolder alloc] initWithShortcutID:_shortcutID 50 | shortcut:clearedShortcut 51 | shortcutRef:nil]; 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleQuantizedWindowMover.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleQuantizedWindowMover.h" 2 | 3 | #import "SpectacleAccessibilityElement.h" 4 | 5 | @implementation SpectacleQuantizedWindowMover 6 | { 7 | id _innerWindowMover; 8 | } 9 | 10 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover 11 | { 12 | if (self = [super init]) { 13 | _innerWindowMover = innerWindowMover; 14 | } 15 | return self; 16 | } 17 | 18 | - (instancetype)init 19 | { 20 | return [self initWithInnerWindowMover:nil]; 21 | } 22 | 23 | + (instancetype)newWithInnerWindowMover:(id)innerWindowMover 24 | { 25 | return [[self alloc] initWithInnerWindowMover:innerWindowMover]; 26 | } 27 | 28 | - (void)moveWindowRect:(CGRect)windowRect 29 | frameOfScreen:(CGRect)frameOfScreen 30 | visibleFrameOfScreen:(CGRect)visibleFrameOfScreen 31 | frontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 32 | action:(SpectacleWindowAction *)action 33 | { 34 | CGRect movedWindowRect = [frontmostWindowElement rectOfElement]; 35 | if (!CGRectEqualToRect(movedWindowRect, windowRect)) { 36 | CGRect adjustedWindowRect = windowRect; 37 | while (movedWindowRect.size.width > windowRect.size.width || movedWindowRect.size.height > windowRect.size.height) { 38 | if (movedWindowRect.size.width > windowRect.size.width) { 39 | adjustedWindowRect.size.width -= 2; 40 | } 41 | if (movedWindowRect.size.height > windowRect.size.height) { 42 | adjustedWindowRect.size.height -= 2; 43 | } 44 | if (adjustedWindowRect.size.width < windowRect.size.width * 0.85f || adjustedWindowRect.size.height < windowRect.size.height * 0.85f) { 45 | break; 46 | } 47 | [frontmostWindowElement setRectOfElement:adjustedWindowRect]; 48 | movedWindowRect = [frontmostWindowElement rectOfElement]; 49 | } 50 | adjustedWindowRect.origin.x += floor((windowRect.size.width - movedWindowRect.size.width) / 2.0f); 51 | adjustedWindowRect.origin.y += floor((windowRect.size.height - movedWindowRect.size.height) / 2.0f); 52 | [frontmostWindowElement setRectOfElement:adjustedWindowRect]; 53 | } 54 | [_innerWindowMover moveWindowRect:windowRect 55 | frameOfScreen:frameOfScreen 56 | visibleFrameOfScreen:visibleFrameOfScreen 57 | frontmostWindowElement:frontmostWindowElement 58 | action:action]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Spectacle/Resources/Window Position Calculations/SpectacleNextOrPreviousThirds.js: -------------------------------------------------------------------------------- 1 | var SpectacleNextOrPreviousThirds = (function () { 2 | var findNextThird = function(windowRect, visibleFrameOfDestinationScreen) { 3 | var thirds = thirdsFromVisibleFrameOfScreen(visibleFrameOfDestinationScreen); 4 | var result = thirds[0]; 5 | for (var i = 0; i < thirds.length; i++) { 6 | var third = thirds[i]; 7 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(third, windowRect)) { 8 | var j = i; 9 | if (++j >= thirds.length) { 10 | j = 0; 11 | } 12 | result = thirds[j]; 13 | break; 14 | } 15 | } 16 | return result; 17 | }; 18 | var findPreviousThird = function(windowRect, visibleFrameOfDestinationScreen) { 19 | var thirds = thirdsFromVisibleFrameOfScreen(visibleFrameOfDestinationScreen); 20 | var result = thirds[0]; 21 | for (var i = 0; i < thirds.length; i++) { 22 | var third = thirds[i]; 23 | if (SpectacleCalculationHelpers.rectCenteredWithinRect(third, windowRect)) { 24 | var j = i; 25 | if (--j < 0) { 26 | j = thirds.length - 1; 27 | } 28 | result = thirds[j]; 29 | break; 30 | } 31 | } 32 | return result; 33 | }; 34 | var thirdsFromVisibleFrameOfScreen = function(visibleFrameOfDestinationScreen) { 35 | var thirds = []; 36 | for (var i = 0; i < 3; i++) { 37 | var third = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 38 | third.x = visibleFrameOfDestinationScreen.x + (Math.floor(visibleFrameOfDestinationScreen.width / 3.0) * i); 39 | third.width = Math.floor(visibleFrameOfDestinationScreen.width / 3.0); 40 | thirds.push(third); 41 | } 42 | for (var i = 0; i < 3; i++) { 43 | var third = SpectacleCalculationHelpers.copyRect(visibleFrameOfDestinationScreen); 44 | third.y = visibleFrameOfDestinationScreen.y + visibleFrameOfDestinationScreen.height - (Math.floor(visibleFrameOfDestinationScreen.height / 3.0) * (i + 1)); 45 | third.height = Math.floor(visibleFrameOfDestinationScreen.height / 3.0); 46 | thirds.push(third); 47 | } 48 | return thirds; 49 | }; 50 | return { 51 | findNextThird: findNextThird, 52 | findPreviousThird: findPreviousThird, 53 | }; 54 | })(); 55 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowAction.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleWindowAction.h" 2 | 3 | SpectacleWindowAction *const kSpectacleWindowActionUndo = @"SpectacleWindowActionUndo"; 4 | SpectacleWindowAction *const kSpectacleWindowActionRedo = @"SpectacleWindowActionRedo"; 5 | SpectacleWindowAction *const kSpectacleWindowActionLarger = @"SpectacleWindowActionLarger"; 6 | SpectacleWindowAction *const kSpectacleWindowActionSmaller = @"SpectacleWindowActionSmaller"; 7 | SpectacleWindowAction *const kSpectacleWindowActionNone = @"SpectacleWindowActionNone"; 8 | SpectacleWindowAction *const kSpectacleWindowActionCenter = @"SpectacleWindowActionCenter"; 9 | SpectacleWindowAction *const kSpectacleWindowActionFullscreen = @"SpectacleWindowActionFullscreen"; 10 | SpectacleWindowAction *const kSpectacleWindowActionLeftHalf = @"SpectacleWindowActionLeftHalf"; 11 | SpectacleWindowAction *const kSpectacleWindowActionUpperLeft = @"SpectacleWindowActionUpperLeft"; 12 | SpectacleWindowAction *const kSpectacleWindowActionLowerLeft = @"SpectacleWindowActionLowerLeft"; 13 | SpectacleWindowAction *const kSpectacleWindowActionRightHalf = @"SpectacleWindowActionRightHalf"; 14 | SpectacleWindowAction *const kSpectacleWindowActionUpperRight = @"SpectacleWindowActionUpperRight"; 15 | SpectacleWindowAction *const kSpectacleWindowActionLowerRight = @"SpectacleWindowActionLowerRight"; 16 | SpectacleWindowAction *const kSpectacleWindowActionTopHalf = @"SpectacleWindowActionTopHalf"; 17 | SpectacleWindowAction *const kSpectacleWindowActionBottomHalf = @"SpectacleWindowActionBottomHalf"; 18 | SpectacleWindowAction *const kSpectacleWindowActionNextDisplay = @"SpectacleWindowActionNextDisplay"; 19 | SpectacleWindowAction *const kSpectacleWindowActionPreviousDisplay = @"SpectacleWindowActionPreviousDisplay"; 20 | SpectacleWindowAction *const kSpectacleWindowActionNextThird = @"SpectacleWindowActionNextThird"; 21 | SpectacleWindowAction *const kSpectacleWindowActionPreviousThird = @"SpectacleWindowActionPreviousThird"; 22 | 23 | BOOL SpectacleIsUndoWindowAction(SpectacleWindowAction *action) 24 | { 25 | return [action isEqualToString:kSpectacleWindowActionUndo]; 26 | } 27 | 28 | BOOL SpectacleIsRedoWindowAction(SpectacleWindowAction *action) 29 | { 30 | return [action isEqualToString:kSpectacleWindowActionRedo]; 31 | } 32 | 33 | BOOL SpectacleIsNextDisplayWindowAction(SpectacleWindowAction *action) 34 | { 35 | return [action isEqualToString:kSpectacleWindowActionNextDisplay]; 36 | } 37 | 38 | BOOL SpectacleIsPreviousDisplayWindowAction(SpectacleWindowAction *action) 39 | { 40 | return [action isEqualToString:kSpectacleWindowActionPreviousDisplay]; 41 | } 42 | 43 | BOOL SpectacleIsMovingToDisplayWindowAction(SpectacleWindowAction *action) 44 | { 45 | return SpectacleIsNextDisplayWindowAction(action) || SpectacleIsPreviousDisplayWindowAction(action); 46 | } 47 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleLeftHalfWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleLeftHalfWindowCalculation) 8 | describe(@"SpectacleLeftHalfWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the left half of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionLeftHalf]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 720.0f, 873.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the left 2/3 of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 4.0f, 720.0f, 873.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionLeftHalf]; 28 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 960.0f, 873.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the left 1/3 of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 4.0f, 960.0f, 873.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionLeftHalf]; 36 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 480.0f, 873.0f)); 37 | }); 38 | }); 39 | SpecEnd 40 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleTopHalfWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleTopHalfWindowCalculation) 8 | describe(@"SpectacleTopHalfWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the top half of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionTopHalf]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 441.0f, 1440.0f, 436.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the top 2/3 of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 441.0f, 1440.0f, 436.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionTopHalf]; 28 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 295.0f, 1440.0f, 582.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the top 1/3 of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 295.0f, 1440.0f, 582.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionTopHalf]; 36 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 586.0f, 1440.0f, 291.0f)); 37 | }); 38 | }); 39 | SpecEnd 40 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleBestEffortWindowMover.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleBestEffortWindowMover.h" 2 | 3 | #import "SpectacleAccessibilityElement.h" 4 | #import "SpectacleWindowPositionManager.h" 5 | 6 | @implementation SpectacleBestEffortWindowMover 7 | { 8 | id _innerWindowMover; 9 | } 10 | 11 | - (instancetype)initWithInnerWindowMover:(id)innerWindowMover 12 | { 13 | if (self = [super init]) { 14 | _innerWindowMover = innerWindowMover; 15 | } 16 | return self; 17 | } 18 | 19 | - (instancetype)init 20 | { 21 | return [self initWithInnerWindowMover:nil]; 22 | } 23 | 24 | + (instancetype)newWithInnerWindowMover:(id)innerWindowMover 25 | { 26 | return [[self alloc] initWithInnerWindowMover:innerWindowMover]; 27 | } 28 | 29 | - (void)moveWindowRect:(CGRect)windowRect 30 | frameOfScreen:(CGRect)frameOfScreen 31 | visibleFrameOfScreen:(CGRect)visibleFrameOfScreen 32 | frontmostWindowElement:(SpectacleAccessibilityElement *)frontmostWindowElement 33 | action:(SpectacleWindowAction *)action 34 | { 35 | [_innerWindowMover moveWindowRect:windowRect 36 | frameOfScreen:frameOfScreen 37 | visibleFrameOfScreen:visibleFrameOfScreen 38 | frontmostWindowElement:frontmostWindowElement 39 | action:action]; 40 | CGRect movedWindowRect = [frontmostWindowElement rectOfElement]; 41 | CGRect previouslyMovedWindowRect = movedWindowRect; 42 | if (movedWindowRect.origin.x < visibleFrameOfScreen.origin.x) { 43 | movedWindowRect.origin.x = visibleFrameOfScreen.origin.x; 44 | } else if ((movedWindowRect.origin.x + movedWindowRect.size.width) > (visibleFrameOfScreen.origin.x + visibleFrameOfScreen.size.width)) { 45 | movedWindowRect.origin.x = visibleFrameOfScreen.origin.x + visibleFrameOfScreen.size.width - movedWindowRect.size.width; 46 | } 47 | movedWindowRect = [SpectacleAccessibilityElement normalizeCoordinatesOfRect:movedWindowRect 48 | frameOfScreen:frameOfScreen]; 49 | if (movedWindowRect.origin.y < visibleFrameOfScreen.origin.y) { 50 | movedWindowRect.origin.y = visibleFrameOfScreen.origin.y; 51 | } else if ((movedWindowRect.origin.y + movedWindowRect.size.height) > (visibleFrameOfScreen.origin.y + visibleFrameOfScreen.size.height)) { 52 | movedWindowRect.origin.y = visibleFrameOfScreen.origin.y + visibleFrameOfScreen.size.height - movedWindowRect.size.height; 53 | } 54 | movedWindowRect = [SpectacleAccessibilityElement normalizeCoordinatesOfRect:movedWindowRect 55 | frameOfScreen:frameOfScreen]; 56 | if (!CGRectEqualToRect(movedWindowRect, previouslyMovedWindowRect)) { 57 | [frontmostWindowElement setRectOfElement:movedWindowRect]; 58 | } 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleBottomHalfWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleBottomHalfWindowCalculation) 8 | describe(@"SpectacleBottomHalfWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the bottom half of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionBottomHalf]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 1440.0f, 436.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the bottom 2/3 of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 4.0f, 1440.0f, 436.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionBottomHalf]; 28 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 1440.0f, 582.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the bottom 1/3 of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 4.0f, 1440.0f, 582.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionBottomHalf]; 36 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 4.0f, 1440.0f, 291.0f)); 37 | }); 38 | }); 39 | SpecEnd 40 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleRightHalfWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleRightHalfWindowCalculation) 8 | describe(@"SpectacleRightHalfWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the right half of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionRightHalf]; 20 | expect(result.windowRect).to.equal(CGRectMake(720.0f, 4.0f, 720.0f, 873.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the right 2/3 of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(720.0f, 4.0f, 720.0f, 873.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionRightHalf]; 28 | expect(result.windowRect).to.equal(CGRectMake(480.0f, 4.0f, 960.0f, 873.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the right 1/3 of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(480.0f, 4.0f, 960.0f, 873.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionRightHalf]; 36 | expect(result.windowRect).to.equal(CGRectMake(960.0f, 4.0f, 480.0f, 873.0f)); 37 | }); 38 | }); 39 | SpecEnd 40 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutUserDefaultsStorage.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleShortcutUserDefaultsStorage.h" 2 | 3 | @implementation SpectacleShortcutUserDefaultsStorage 4 | 5 | + (void)initialize 6 | { 7 | [NSKeyedUnarchiver setClass:[SpectacleShortcut class] forClassName:@"SpectacleHotKey"]; 8 | [NSKeyedUnarchiver setClass:[SpectacleShortcut class] forClassName:@"ZeroKitHotKey"]; 9 | [NSKeyedUnarchiver setClass:[SpectacleShortcut class] forClassName:@"ZKHotKey"]; 10 | } 11 | 12 | - (NSArray *)loadShortcutsWithAction:(SpectacleShortcutAction)action 13 | { 14 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 15 | NSMutableDictionary *shortcutsFromUserDefaults = [NSMutableDictionary new]; 16 | for (NSString *shortcutName in [self shortcutNames]) { 17 | shortcutsFromUserDefaults[shortcutName] = [userDefaults dataForKey:shortcutName]; 18 | } 19 | return [self shortcutsFromDictionary:shortcutsFromUserDefaults action:action]; 20 | } 21 | 22 | - (void)storeShortcuts:(NSArray *)shortcuts 23 | { 24 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 25 | for (SpectacleShortcut *shortcut in shortcuts) { 26 | NSData *shortcutData = [NSKeyedArchiver archivedDataWithRootObject:shortcut]; 27 | NSString *shortcutName = shortcut.shortcutName; 28 | if (![shortcutData isEqualToData:[userDefaults dataForKey:shortcutName]]) { 29 | [userDefaults setObject:shortcutData forKey:shortcutName]; 30 | } 31 | } 32 | } 33 | 34 | - (BOOL)isMigrationNeeded 35 | { 36 | return NO; 37 | } 38 | 39 | - (NSArray *)shortcutNames 40 | { 41 | return @[ 42 | @"MoveToCenter", 43 | @"MoveToFullscreen", 44 | @"MoveToLeftHalf", 45 | @"MoveToRightHalf", 46 | @"MoveToTopHalf", 47 | @"MoveToBottomHalf", 48 | @"MoveToUpperLeft", 49 | @"MoveToLowerLeft", 50 | @"MoveToUpperRight", 51 | @"MoveToLowerRight", 52 | @"MoveToNextDisplay", 53 | @"MoveToPreviousDisplay", 54 | @"MoveToNextThird", 55 | @"MoveToPreviousThird", 56 | @"MakeLarger", 57 | @"MakeSmaller", 58 | @"UndoLastMove", 59 | @"RedoLastMove", 60 | ]; 61 | } 62 | 63 | - (NSArray *)shortcutsFromDictionary:(NSDictionary *)dictionary 64 | action:(SpectacleShortcutAction)action 65 | { 66 | NSMutableArray *shortcuts = [NSMutableArray new]; 67 | for (NSData *shortcutData in dictionary.allValues) { 68 | SpectacleShortcut *shortcut = [NSKeyedUnarchiver unarchiveObjectWithData:shortcutData]; 69 | [shortcuts addObject:[shortcut copyWithShortcutAction:action]]; 70 | } 71 | return shortcuts; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleUpperLeftWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleUpperLeftWindowCalculation) 8 | describe(@"SpectacleUpperLeftWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the upper left corner of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionUpperLeft]; 20 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 441.0f, 720.0f, 436.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the left 2/3 of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 441.0f, 720.0f, 436.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionUpperLeft]; 28 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 441.0f, 960.0f, 436.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the left 1/3 of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(0.0f, 441.0f, 960.0f, 436.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionUpperLeft]; 36 | expect(result.windowRect).to.equal(CGRectMake(0.0f, 441.0f, 480.0f, 436.0f)); 37 | }); 38 | 39 | 40 | }); 41 | SpecEnd 42 | -------------------------------------------------------------------------------- /SpectacleSpecs/Sources/SpectacleUpperRightWindowCalculationSpec.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "SpectacleWindowPositionCalculationResult.h" 5 | #import "SpectacleWindowPositionCalculator.h" 6 | 7 | SpecBegin(SpectacleUpperRightWindowCalculation) 8 | describe(@"SpectacleUpperRightWindowCalculation", ^{ 9 | CGRect visibleFrameSourceScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 10 | CGRect visibleFrameDestinationScreen = CGRectMake(0.0f, 4.0f, 1440.0f, 873.0f); 11 | SpectacleWindowPositionCalculator *windowPositionCalculator = [[SpectacleWindowPositionCalculator alloc] initWithErrorHandler:^(NSString *message) { 12 | EXP_failure(message); 13 | }]; 14 | 15 | it(@"should calculate a window's CGRect in the upper right corner of the screen", ^{ 16 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(165.0f, 245.0f, 564.0f, 384.0f) 17 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 18 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 19 | action:kSpectacleWindowActionUpperRight]; 20 | expect(result.windowRect).to.equal(CGRectMake(720.0f, 441.0f, 720.0f, 436.0f)); 21 | }); 22 | 23 | it(@"should calculate a window's CGRect in the upper right 2/3 corner of the screen", ^{ 24 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(720.0f, 441.0f, 720.0f, 436.0f) 25 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 26 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 27 | action:kSpectacleWindowActionUpperRight]; 28 | expect(result.windowRect).to.equal(CGRectMake(480.0f, 441.0f, 960.0f, 436.0f)); 29 | }); 30 | 31 | it(@"should calculate a window's CGRect in the upper right 1/3 corner of the screen", ^{ 32 | SpectacleWindowPositionCalculationResult *result = [windowPositionCalculator calculateWindowRect:CGRectMake(480.0f, 441.0f, 960.0f, 436.0f) 33 | visibleFrameOfSourceScreen:visibleFrameSourceScreen 34 | visibleFrameOfDestinationScreen:visibleFrameDestinationScreen 35 | action:kSpectacleWindowActionUpperRight]; 36 | expect(result.windowRect).to.equal(CGRectMake(960.0f, 441.0f, 480.0f, 436.0f)); 37 | }); 38 | 39 | }); 40 | SpecEnd 41 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleDefaultShortcutHelpers.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleDefaultShortcutHelpers.h" 2 | 3 | #import 4 | #import 5 | 6 | static NSArray *builtinDefaultShortcuts(void); 7 | 8 | NSArray *SpectacleDefaultShortcutsWithAction(SpectacleShortcutAction action) 9 | { 10 | NSMutableArray *defaultShortcuts = [NSMutableArray new]; 11 | for (SpectacleShortcut *defaultShortcut in builtinDefaultShortcuts()) { 12 | [defaultShortcuts addObject:[defaultShortcut copyWithShortcutAction:action]]; 13 | } 14 | return defaultShortcuts; 15 | } 16 | 17 | static NSArray *builtinDefaultShortcuts(void) 18 | { 19 | return @[ 20 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToCenter" shortcutKeyBinding:@"alt+cmd+c"], 21 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToFullscreen" shortcutKeyBinding:@"alt+cmd+f"], 22 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToLeftHalf" shortcutKeyBinding:@"alt+cmd+left"], 23 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToRightHalf" shortcutKeyBinding:@"alt+cmd+right"], 24 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToTopHalf" shortcutKeyBinding:@"alt+cmd+up"], 25 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToBottomHalf" shortcutKeyBinding:@"alt+cmd+down"], 26 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToUpperLeft" shortcutKeyBinding:@"ctrl+cmd+left"], 27 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToLowerLeft" shortcutKeyBinding:@"ctrl+shift+cmd+left"], 28 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToUpperRight" shortcutKeyBinding:@"ctrl+cmd+right"], 29 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToLowerRight" shortcutKeyBinding:@"ctrl+shift+cmd+right"], 30 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToNextDisplay" shortcutKeyBinding:@"ctrl+alt+cmd+right"], 31 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToPreviousDisplay" shortcutKeyBinding:@"ctrl+alt+cmd+left"], 32 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToNextThird" shortcutKeyBinding:@"ctrl+alt+right"], 33 | [[SpectacleShortcut alloc] initWithShortcutName:@"MoveToPreviousThird" shortcutKeyBinding:@"ctrl+alt+left"], 34 | [[SpectacleShortcut alloc] initWithShortcutName:@"MakeLarger" shortcutKeyBinding:@"ctrl+alt+shift+right"], 35 | [[SpectacleShortcut alloc] initWithShortcutName:@"MakeSmaller" shortcutKeyBinding:@"ctrl+alt+shift+left"], 36 | [[SpectacleShortcut alloc] initWithShortcutName:@"UndoLastMove" shortcutKeyBinding:@"alt+cmd+z"], 37 | [[SpectacleShortcut alloc] initWithShortcutName:@"RedoLastMove" shortcutKeyBinding:@"alt+shift+cmd+z"], 38 | ]; 39 | } 40 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectaclePreferencesController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "SpectacleShortcutRecorderDelegate.h" 4 | 5 | @class SpectacleAppDelegate; 6 | @class SpectacleShortcutManager; 7 | @class SpectacleShortcutRecorder; 8 | @class SpectacleWindowPositionManager; 9 | 10 | @protocol SpectacleShortcutStorage; 11 | 12 | @interface SpectaclePreferencesController : NSWindowController 13 | 14 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToCenterShortcutRecorder; 15 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToFullscreenShortcutRecorder; 16 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToLeftShortcutRecorder; 17 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToRightShortcutRecorder; 18 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToTopShortcutRecorder; 19 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToBottomShortcutRecorder; 20 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToUpperLeftShortcutRecorder; 21 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToLowerLeftShortcutRecorder; 22 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToUpperRightShortcutRecorder; 23 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToLowerRightShortcutRecorder; 24 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToNextDisplayShortcutRecorder; 25 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToPreviousDisplayShortcutRecorder; 26 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToNextThirdShortcutRecorder; 27 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *moveToPreviousThirdShortcutRecorder; 28 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *makeLargerShortcutRecorder; 29 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *makeSmallerShortcutRecorder; 30 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *undoLastMoveShortcutRecorder; 31 | @property (nonatomic, strong) IBOutlet SpectacleShortcutRecorder *redoLastMoveShortcutRecorder; 32 | 33 | @property (nonatomic, strong) IBOutlet NSView *footerView; 34 | @property (nonatomic, strong) IBOutlet NSView *shortcutModifierLegendFooterView; 35 | @property (nonatomic, strong) IBOutlet NSView *optionsFooterView; 36 | 37 | @property (nonatomic, strong) IBOutlet NSButton *loginItemEnabled; 38 | @property (nonatomic, strong) IBOutlet NSPopUpButton *statusItemEnabled; 39 | 40 | - (instancetype)initWithShortcutManager:(SpectacleShortcutManager *)shortcutManager 41 | windowPositionManager:(SpectacleWindowPositionManager *)windowPositionManager 42 | shortcutStorage:(id)shortcutStorage; 43 | 44 | - (void)loadRegisteredShortcuts; 45 | 46 | - (IBAction)swapFooterViews:(id)sender; 47 | 48 | - (IBAction)restoreDefaults:(id)sender; 49 | 50 | - (IBAction)toggleLoginItem:(id)sender; 51 | - (IBAction)toggleStatusItem:(id)sender; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SpectaclePreferencesController; 4 | 5 | @interface SpectacleAppDelegate : NSObject 6 | 7 | @property (nonatomic, strong) IBOutlet NSMenu *statusItemMenu; 8 | @property (nonatomic, strong) IBOutlet NSWindow *accessiblityAccessDialogWindow; 9 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToCenterShortcutMenuItem; 10 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToFullscreenShortcutMenuItem; 11 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToLeftShortcutMenuItem; 12 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToRightShortcutMenuItem; 13 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToTopShortcutMenuItem; 14 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToBottomShortcutMenuItem; 15 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToUpperLeftShortcutMenuItem; 16 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToLowerLeftShortcutMenuItem; 17 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToUpperRightShortcutMenuItem; 18 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToLowerRightShortcutMenuItem; 19 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToNextDisplayShortcutMenuItem; 20 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToPreviousDisplayShortcutMenuItem; 21 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToNextThirdShortcutMenuItem; 22 | @property (nonatomic, strong) IBOutlet NSMenuItem *moveToPreviousThirdShortcutMenuItem; 23 | @property (nonatomic, strong) IBOutlet NSMenuItem *makeLargerShortcutMenuItem; 24 | @property (nonatomic, strong) IBOutlet NSMenuItem *makeSmallerShortcutMenuItem; 25 | @property (nonatomic, strong) IBOutlet NSMenuItem *undoLastMoveShortcutMenuItem; 26 | @property (nonatomic, strong) IBOutlet NSMenuItem *redoLastMoveShortcutMenuItem; 27 | @property (nonatomic, strong) IBOutlet NSMenuItem *disableShortcutsForAnHourMenuItem; 28 | @property (nonatomic, strong) IBOutlet NSMenuItem *disableShortcutsForApplicationMenuItem; 29 | 30 | - (IBAction)showPreferencesWindow:(id)sender; 31 | 32 | - (IBAction)moveFrontmostWindowToFullscreen:(id)sender; 33 | - (IBAction)moveFrontmostWindowToCenter:(id)sender; 34 | - (IBAction)moveFrontmostWindowToLeftHalf:(id)sender; 35 | - (IBAction)moveFrontmostWindowToRightHalf:(id)sender; 36 | - (IBAction)moveFrontmostWindowToTopHalf:(id)sender; 37 | - (IBAction)moveFrontmostWindowToBottomHalf:(id)sender; 38 | - (IBAction)moveFrontmostWindowToUpperLeft:(id)sender; 39 | - (IBAction)moveFrontmostWindowToLowerLeft:(id)sender; 40 | - (IBAction)moveFrontmostWindowToUpperRight:(id)sender; 41 | - (IBAction)moveFrontmostWindowToLowerRight:(id)sender; 42 | - (IBAction)moveFrontmostWindowToNextDisplay:(id)sender; 43 | - (IBAction)moveFrontmostWindowToPreviousDisplay:(id)sender; 44 | - (IBAction)moveFrontmostWindowToNextThird:(id)sender; 45 | - (IBAction)moveFrontmostWindowToPreviousThird:(id)sender; 46 | - (IBAction)makeFrontmostWindowLarger:(id)sender; 47 | - (IBAction)makeFrontmostWindowSmaller:(id)sender; 48 | - (IBAction)undoLastWindowAction:(id)sender; 49 | - (IBAction)redoLastWindowAction:(id)sender; 50 | - (IBAction)disableOrEnableShortcutsForAnHour:(id)sender; 51 | - (IBAction)disableOrEnableShortcutsForApplication:(id)sender; 52 | 53 | - (IBAction)openSystemPreferences:(id)sender; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleWindowPositionCalculator.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleWindowPositionCalculator.h" 2 | 3 | #import 4 | 5 | #import "SpectacleJavaScriptEnvironment.h" 6 | #import "SpectacleWindowPositionCalculationRegistry.h" 7 | #import "SpectacleWindowPositionCalculationResult.h" 8 | 9 | @implementation SpectacleWindowPositionCalculator 10 | { 11 | SpectacleWindowPositionCalculationRegistry *_windowPositionCalculationRegistry; 12 | SpectacleJavaScriptEnvironment *_javaScriptEnvironment; 13 | } 14 | 15 | - (instancetype)initWithErrorHandler:(void(^)(NSString *message))errorHandler 16 | { 17 | if (self = [super init]) { 18 | _windowPositionCalculationRegistry = [SpectacleWindowPositionCalculationRegistry new]; 19 | _javaScriptEnvironment = [[SpectacleJavaScriptEnvironment alloc] initWithContextBuilder:^(JSContext *context) { 20 | context.exceptionHandler = ^(JSContext *context, JSValue *exception) { 21 | NSString *errorName = [exception[@"name"] toString]; 22 | NSString *errorMessage = [exception[@"message"] toString]; 23 | errorHandler([NSString stringWithFormat:@"%@\n%@", errorName, errorMessage]); 24 | }; 25 | context[@"windowPositionCalculationRegistry"] = self->_windowPositionCalculationRegistry; 26 | context[@"CGRectContainsRect"] = ^BOOL(CGRect rect1, CGRect rect2) { 27 | return CGRectContainsRect(rect1, rect2); 28 | }; 29 | context[@"CGRectEqualToRect"] = ^BOOL(CGRect rect1, CGRect rect2) { 30 | return CGRectEqualToRect(rect1, rect2); 31 | }; 32 | context[@"CGRectGetMinX"] = ^CGFloat(CGRect rect) { 33 | return CGRectGetMinX(rect); 34 | }; 35 | context[@"CGRectGetMinY"] = ^CGFloat(CGRect rect) { 36 | return CGRectGetMinY(rect); 37 | }; 38 | context[@"CGRectGetMidX"] = ^CGFloat(CGRect rect) { 39 | return CGRectGetMidX(rect); 40 | }; 41 | context[@"CGRectGetMidY"] = ^CGFloat(CGRect rect) { 42 | return CGRectGetMidY(rect); 43 | }; 44 | context[@"CGRectGetMaxX"] = ^CGFloat(CGRect rect) { 45 | return CGRectGetMaxX(rect); 46 | }; 47 | context[@"CGRectGetMaxY"] = ^CGFloat(CGRect rect) { 48 | return CGRectGetMaxY(rect); 49 | }; 50 | }]; 51 | } 52 | return self; 53 | } 54 | 55 | - (SpectacleWindowPositionCalculationResult *)calculateWindowRect:(CGRect)windowRect 56 | visibleFrameOfSourceScreen:(CGRect)visibleFrameOfSourceScreen 57 | visibleFrameOfDestinationScreen:(CGRect)visibleFrameOfDestinationScreen 58 | action:(SpectacleWindowAction *)action 59 | { 60 | JSValue *windowPositionCalculation = [_windowPositionCalculationRegistry windowPositionCalculationWithAction:action]; 61 | if (!windowPositionCalculation) { 62 | return nil; 63 | } 64 | JSValue *result = [windowPositionCalculation callWithArguments:@[ 65 | [_javaScriptEnvironment valueWithRect:windowRect], 66 | [_javaScriptEnvironment valueWithRect:visibleFrameOfSourceScreen], 67 | [_javaScriptEnvironment valueWithRect:visibleFrameOfDestinationScreen], 68 | ]]; 69 | return [SpectacleWindowPositionCalculationResult resultWithAction:action windowRect:[result toRect]]; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleLoginItemHelper.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleLoginItemHelper.h" 2 | 3 | @implementation SpectacleLoginItemHelper 4 | 5 | + (BOOL)isLoginItemEnabledForBundle:(NSBundle *)bundle 6 | { 7 | LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); 8 | NSString *applicationPath = bundle.bundlePath; 9 | BOOL result = NO; 10 | if (sharedFileList) { 11 | UInt32 seedValue; 12 | NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue)); 13 | for (id sharedFile in sharedFileListArray) { 14 | LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile; 15 | CFURLRef applicationPathURL = NULL; 16 | LSSharedFileListItemResolve(sharedFileListItem, 0, (CFURLRef *)&applicationPathURL, NULL); 17 | if (applicationPathURL != NULL) { 18 | NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path]; 19 | CFRelease(applicationPathURL); 20 | if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) { 21 | result = YES; 22 | break; 23 | } 24 | } 25 | } 26 | CFRelease(sharedFileList); 27 | } else { 28 | NSLog(@"Unable to create the shared file list."); 29 | } 30 | return result; 31 | } 32 | 33 | + (void)enableLoginItemForBundle:(NSBundle *)bundle 34 | { 35 | LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); 36 | NSString *applicationPath = bundle.bundlePath; 37 | NSURL *applicationPathURL = [NSURL fileURLWithPath:applicationPath]; 38 | if (sharedFileList) { 39 | LSSharedFileListItemRef sharedFileListItem = LSSharedFileListInsertItemURL(sharedFileList, 40 | kLSSharedFileListItemLast, 41 | NULL, 42 | NULL, 43 | (__bridge CFURLRef)applicationPathURL, 44 | NULL, 45 | NULL); 46 | if (sharedFileListItem) { 47 | CFRelease(sharedFileListItem); 48 | } 49 | CFRelease(sharedFileList); 50 | } else { 51 | NSLog(@"Unable to create the shared file list."); 52 | } 53 | } 54 | 55 | + (void)disableLoginItemForBundle:(NSBundle *)bundle 56 | { 57 | LSSharedFileListRef sharedFileList = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); 58 | NSString *applicationPath = bundle.bundlePath; 59 | if (sharedFileList) { 60 | UInt32 seedValue; 61 | NSArray *sharedFileListArray = CFBridgingRelease(LSSharedFileListCopySnapshot(sharedFileList, &seedValue)); 62 | for (id sharedFile in sharedFileListArray) { 63 | LSSharedFileListItemRef sharedFileListItem = (__bridge LSSharedFileListItemRef)sharedFile; 64 | CFURLRef applicationPathURL; 65 | if (LSSharedFileListItemResolve(sharedFileListItem, 0, &applicationPathURL, NULL) == noErr) { 66 | NSString *resolvedApplicationPath = [(__bridge NSURL *)applicationPathURL path]; 67 | if ([resolvedApplicationPath compare:applicationPath] == NSOrderedSame) { 68 | LSSharedFileListItemRemove(sharedFileList, sharedFileListItem); 69 | } 70 | CFRelease(applicationPathURL); 71 | } 72 | } 73 | CFRelease(sharedFileList); 74 | } else { 75 | NSLog(@"Unable to create the shared file list."); 76 | } 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleUtilities.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleUtilities.h" 2 | 3 | @implementation SpectacleUtilities 4 | 5 | + (NSString *)applicationVersion 6 | { 7 | NSBundle *mainBundle = NSBundle.mainBundle; 8 | NSString *bundleVersion = mainBundle.infoDictionary[@"CFBundleShortVersionString"]; 9 | if (!bundleVersion) { 10 | bundleVersion = mainBundle.infoDictionary[@"CFBundleVersion"]; 11 | } 12 | return bundleVersion; 13 | } 14 | 15 | + (void)registerDefaultsForBundle:(NSBundle *)bundle 16 | { 17 | NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults; 18 | NSString *path = [bundle pathForResource:@"Defaults" ofType:@"plist"]; 19 | [defaults registerDefaults:[[NSDictionary alloc] initWithContentsOfFile:path]]; 20 | } 21 | 22 | + (void)displayRunningInBackgroundAlertWithCallback:(void (^)(BOOL, BOOL))callback 23 | { 24 | NSAlert *alert = [NSAlert new]; 25 | alert.alertStyle = NSInformationalAlertStyle; 26 | alert.showsSuppressionButton = YES; 27 | alert.messageText = NSLocalizedString(@"AlertMessageTextRunningInBackground", @"The message text of the alert displayed when prompting to run Spectacle in the background"); 28 | alert.informativeText = NSLocalizedString(@"AlertInformativeTextRunningInBackground", @"The informative text of the alert displayed when prompting to run Spectacle in the background"); 29 | [alert addButtonWithTitle:NSLocalizedString(@"ButtonLabelAffirmative", @"The button label used in the affirmative")]; 30 | [alert addButtonWithTitle:NSLocalizedString(@"ButtonLabelNegative", @"The button label used in the negative")]; 31 | NSInteger response = [alert runModal]; 32 | BOOL isAlertSuppressed = [alert.suppressionButton state] == NSOnState; 33 | switch (response) { 34 | case NSAlertFirstButtonReturn: 35 | callback(YES, isAlertSuppressed); 36 | break; 37 | case NSAlertSecondButtonReturn: 38 | callback(NO, isAlertSuppressed); 39 | break; 40 | default: 41 | break; 42 | } 43 | } 44 | 45 | + (void)displayRestoreDefaultsAlertWithConfirmationCallback:(void (^)(void))callback 46 | { 47 | NSAlert *alert = [NSAlert new]; 48 | alert.messageText = NSLocalizedString(@"AlertMessageTextRestoreDefaults", @"The message text of the alert displayed when prompting to restore Spectacle's default shortcuts"); 49 | alert.informativeText = NSLocalizedString(@"AlertInformativeTextRestoreDefaults", @"The informative text of the alert displayed when prompting to restore Spectacle's default shortcuts"); 50 | [alert addButtonWithTitle:NSLocalizedString(@"ButtonLabelAffirmative", @"The button label used in the affirmative")]; 51 | [alert addButtonWithTitle:NSLocalizedString(@"ButtonLabelNegative", @"The button label used in the negative")]; 52 | NSInteger response = [alert runModal]; 53 | switch (response) { 54 | case NSAlertFirstButtonReturn: 55 | callback(); 56 | break; 57 | case NSAlertSecondButtonReturn: 58 | break; 59 | default: 60 | break; 61 | } 62 | } 63 | 64 | + (NSString *)pathForPreferencePaneNamed:(NSString *)preferencePaneName 65 | { 66 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSAllDomainsMask, YES); 67 | NSFileManager *fileManager = NSFileManager.defaultManager; 68 | NSString *preferencePanePath = nil; 69 | if (preferencePaneName) { 70 | preferencePaneName = [preferencePaneName stringByAppendingString:@".prefPane"]; 71 | for (__strong NSString *path in paths) { 72 | path = [path stringByAppendingPathComponent:preferencePaneName]; 73 | if (path && [fileManager fileExistsAtPath:path isDirectory:nil]) { 74 | preferencePanePath = path; 75 | break; 76 | } 77 | } 78 | if (!preferencePanePath) { 79 | NSLog(@"There was a problem obtaining the path for the specified preference pane: %@", preferencePaneName); 80 | } 81 | } 82 | return preferencePanePath; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fi.lproj/Spectacle.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "187-7k-0Ct"; */ 3 | "187-7k-0Ct.title" = "Tee uudelleen"; 4 | 5 | /* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "2sj-vF-eSL"; */ 6 | "2sj-vF-eSL.title" = "Tarkista päivitykset..."; 7 | 8 | /* Class = "NSMenuItem"; title = "Next Third"; ObjectID = "7HO-L3-O6Q"; */ 9 | "7HO-L3-O6Q.title" = "Seuraava kolmasosa"; 10 | 11 | /* Class = "NSMenuItem"; title = "Upper Right"; ObjectID = "C7t-Sf-kOI"; */ 12 | "C7t-Sf-kOI.title" = "Yläoikea"; 13 | 14 | /* Class = "NSMenuItem"; title = "About Spectacle"; ObjectID = "EA9-dx-Qfg"; */ 15 | "EA9-dx-Qfg.title" = "Tietoja Spectaclesta"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Spectacle would like to control your windows using accessibility features."; ObjectID = "HRo-tn-M1V"; */ 18 | "HRo-tn-M1V.title" = "Spectacle haluaisi hallita ikkunoita käyttöaputoimintojen avulla."; 19 | 20 | /* Class = "NSMenuItem"; title = "Next Display"; ObjectID = "I4U-jL-hXq"; */ 21 | "I4U-jL-hXq.title" = "Seuraava ikkuna"; 22 | 23 | /* Class = "NSMenuItem"; title = "Quit Spectacle"; ObjectID = "Isn-Og-AEv"; */ 24 | "Isn-Og-AEv.title" = "Sulje Spectacle"; 25 | 26 | /* Class = "NSMenuItem"; title = "Left Half"; ObjectID = "Jae-vx-7ZC"; */ 27 | "Jae-vx-7ZC.title" = "Vasen puoli"; 28 | 29 | /* Class = "NSMenu"; title = "Spectacle Status Item"; ObjectID = "KsA-8w-wcl"; */ 30 | "KsA-8w-wcl.title" = "Spectacle Status Item"; 31 | 32 | /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "ME4-Br-HLT"; */ 33 | "ME4-Br-HLT.title" = "Asetukset..."; 34 | 35 | /* Class = "NSMenuItem"; title = "Make Larger"; ObjectID = "Yun-AA-OVD"; */ 36 | "Yun-AA-OVD.title" = "Suurenna"; 37 | 38 | /* Class = "NSMenuItem"; title = "Previous Display"; ObjectID = "aPo-Rp-A1X"; */ 39 | "aPo-Rp-A1X.title" = "Edellinen näyttö"; 40 | 41 | /* Class = "NSMenuItem"; title = "Previous Third"; ObjectID = "bV9-Ga-jDG"; */ 42 | "bV9-Ga-jDG.title" = "Edellinen kolmasosa"; 43 | 44 | /* Class = "NSMenuItem"; title = "Right Half"; ObjectID = "cae-26-Oab"; */ 45 | "cae-26-Oab.title" = "Oikea puoli"; 46 | 47 | /* Class = "NSMenuItem"; title = "Fullscreen"; ObjectID = "dwQ-Bl-nEL"; */ 48 | "dwQ-Bl-nEL.title" = "Koko näyttö"; 49 | 50 | /* Class = "NSMenuItem"; title = "Lower Left"; ObjectID = "eN5-A6-iho"; */ 51 | "eN5-A6-iho.title" = "Alavasen"; 52 | 53 | /* Class = "NSMenuItem"; title = "Disable"; ObjectID = "eZT-Mm-qSc"; */ 54 | "eZT-Mm-qSc.title" = "Poista käytöstä"; 55 | 56 | /* Class = "NSMenu"; title = "Disable"; ObjectID = "flL-B2-XIP"; */ 57 | "flL-B2-XIP.title" = "Poista käytöstä"; 58 | 59 | /* Class = "NSButtonCell"; title = "Quit Spectacle"; ObjectID = "gMV-vD-CUe"; */ 60 | "gMV-vD-CUe.title" = "Sulje Spectacle"; 61 | 62 | /* Class = "NSMenuItem"; title = "Make Smaller"; ObjectID = "gTe-ym-DB0"; */ 63 | "gTe-ym-DB0.title" = "Pienennä"; 64 | 65 | /* Class = "NSMenuItem"; title = "Bottom Half"; ObjectID = "huV-Gw-lbX"; */ 66 | "huV-Gw-lbX.title" = "Alapuoli"; 67 | 68 | /* Class = "NSButtonCell"; title = "Open System Preferences"; ObjectID = "iPl-AT-Pei"; */ 69 | "iPl-AT-Pei.title" = "Avaa järjestelmäasetukset"; 70 | 71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "l9O-0B-fjp"; */ 72 | "l9O-0B-fjp.title" = "Kumoa"; 73 | 74 | /* Class = "NSMenuItem"; title = "Upper Left"; ObjectID = "ldc-QO-Cq6"; */ 75 | "ldc-QO-Cq6.title" = "Ylävasen"; 76 | 77 | /* Class = "NSMenuItem"; title = "for APPLICATION"; ObjectID = "msZ-id-Pvw"; */ 78 | "msZ-id-Pvw.title" = "ohjelmassa APPLICATION"; 79 | 80 | /* Class = "NSMenuItem"; title = "Lower Right"; ObjectID = "oDU-J0-NkJ"; */ 81 | "oDU-J0-NkJ.title" = "Alaoikea"; 82 | 83 | /* Class = "NSMenuItem"; title = "for an hour"; ObjectID = "qpo-5w-ZIY"; */ 84 | "qpo-5w-ZIY.title" = "tunniksi"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Grant access to this application in System Preferences."; ObjectID = "ruu-yJ-Q1F"; */ 87 | "ruu-yJ-Q1F.title" = "Salli pääsy tälle ohjelmalle järjestelmäasetuksissa."; 88 | 89 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "xJu-Uo-nZw"; */ 90 | "xJu-Uo-nZw.title" = "Keskitä"; 91 | 92 | /* Class = "NSMenuItem"; title = "Top Half"; ObjectID = "yh0-ik-weD"; */ 93 | "yh0-ik-weD.title" = "Yläpuoli"; 94 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fr.lproj/Spectacle.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "187-7k-0Ct"; */ 3 | "187-7k-0Ct.title" = "Rétablir"; 4 | 5 | /* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "2sj-vF-eSL"; */ 6 | "2sj-vF-eSL.title" = "Vérifier les mises à jour..."; 7 | 8 | /* Class = "NSMenuItem"; title = "Next Third"; ObjectID = "7HO-L3-O6Q"; */ 9 | "7HO-L3-O6Q.title" = "Tiers suivant"; 10 | 11 | /* Class = "NSMenuItem"; title = "Upper Right"; ObjectID = "C7t-Sf-kOI"; */ 12 | "C7t-Sf-kOI.title" = "En haut à droite"; 13 | 14 | /* Class = "NSMenuItem"; title = "About Spectacle"; ObjectID = "EA9-dx-Qfg"; */ 15 | "EA9-dx-Qfg.title" = "À propos de Spectacle"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Spectacle would like to control your windows using accessibility features."; ObjectID = "HRo-tn-M1V"; */ 18 | "HRo-tn-M1V.title" = "Spectacle souhaite contrôler vos fenêtres à l'aide des fonctionnalités d'accessibilité."; 19 | 20 | /* Class = "NSMenuItem"; title = "Next Display"; ObjectID = "I4U-jL-hXq"; */ 21 | "I4U-jL-hXq.title" = "Écran suivant"; 22 | 23 | /* Class = "NSMenuItem"; title = "Quit Spectacle"; ObjectID = "Isn-Og-AEv"; */ 24 | "Isn-Og-AEv.title" = "Quitter Spectacle"; 25 | 26 | /* Class = "NSMenuItem"; title = "Left Half"; ObjectID = "Jae-vx-7ZC"; */ 27 | "Jae-vx-7ZC.title" = "Moitié gauche"; 28 | 29 | /* Class = "NSMenu"; title = "Spectacle Status Item"; ObjectID = "KsA-8w-wcl"; */ 30 | "KsA-8w-wcl.title" = "Spectacle Status Item"; 31 | 32 | /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "ME4-Br-HLT"; */ 33 | "ME4-Br-HLT.title" = "Préférences..."; 34 | 35 | /* Class = "NSMenuItem"; title = "Make Larger"; ObjectID = "Yun-AA-OVD"; */ 36 | "Yun-AA-OVD.title" = "Agrandir"; 37 | 38 | /* Class = "NSMenuItem"; title = "Previous Display"; ObjectID = "aPo-Rp-A1X"; */ 39 | "aPo-Rp-A1X.title" = "Écran précédent"; 40 | 41 | /* Class = "NSMenuItem"; title = "Previous Third"; ObjectID = "bV9-Ga-jDG"; */ 42 | "bV9-Ga-jDG.title" = "Tiers précédent"; 43 | 44 | /* Class = "NSMenuItem"; title = "Right Half"; ObjectID = "cae-26-Oab"; */ 45 | "cae-26-Oab.title" = "Moitié droit"; 46 | 47 | /* Class = "NSMenuItem"; title = "Fullscreen"; ObjectID = "dwQ-Bl-nEL"; */ 48 | "dwQ-Bl-nEL.title" = "Plein écran"; 49 | 50 | /* Class = "NSMenuItem"; title = "Lower Left"; ObjectID = "eN5-A6-iho"; */ 51 | "eN5-A6-iho.title" = "En bas à gauche"; 52 | 53 | /* Class = "NSMenuItem"; title = "Disable"; ObjectID = "eZT-Mm-qSc"; */ 54 | "eZT-Mm-qSc.title" = "Désactiver"; 55 | 56 | /* Class = "NSMenu"; title = "Disable"; ObjectID = "flL-B2-XIP"; */ 57 | "flL-B2-XIP.title" = "Désactiver"; 58 | 59 | /* Class = "NSButtonCell"; title = "Quit Spectacle"; ObjectID = "gMV-vD-CUe"; */ 60 | "gMV-vD-CUe.title" = "Quitter Spectacle"; 61 | 62 | /* Class = "NSMenuItem"; title = "Make Smaller"; ObjectID = "gTe-ym-DB0"; */ 63 | "gTe-ym-DB0.title" = "Réduire"; 64 | 65 | /* Class = "NSMenuItem"; title = "Bottom Half"; ObjectID = "huV-Gw-lbX"; */ 66 | "huV-Gw-lbX.title" = "Moitié bas"; 67 | 68 | /* Class = "NSButtonCell"; title = "Open System Preferences"; ObjectID = "iPl-AT-Pei"; */ 69 | "iPl-AT-Pei.title" = "Ouvrir Préférences Système"; 70 | 71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "l9O-0B-fjp"; */ 72 | "l9O-0B-fjp.title" = "Annuler"; 73 | 74 | /* Class = "NSMenuItem"; title = "Upper Left"; ObjectID = "ldc-QO-Cq6"; */ 75 | "ldc-QO-Cq6.title" = "En haut à gauche"; 76 | 77 | /* Class = "NSMenuItem"; title = "for APPLICATION"; ObjectID = "msZ-id-Pvw"; */ 78 | "msZ-id-Pvw.title" = "for APPLICATION"; 79 | 80 | /* Class = "NSMenuItem"; title = "Lower Right"; ObjectID = "oDU-J0-NkJ"; */ 81 | "oDU-J0-NkJ.title" = "En bas à droite"; 82 | 83 | /* Class = "NSMenuItem"; title = "for an hour"; ObjectID = "qpo-5w-ZIY"; */ 84 | "qpo-5w-ZIY.title" = "pour 1 heure"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Grant access to this application in System Preferences."; ObjectID = "ruu-yJ-Q1F"; */ 87 | "ruu-yJ-Q1F.title" = "Accorder l'accès à cette application dans les Préférences Système."; 88 | 89 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "xJu-Uo-nZw"; */ 90 | "xJu-Uo-nZw.title" = "Centrer"; 91 | 92 | /* Class = "NSMenuItem"; title = "Top Half"; ObjectID = "yh0-ik-weD"; */ 93 | "yh0-ik-weD.title" = "Moitié haut"; 94 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/pt.lproj/Spectacle.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "187-7k-0Ct"; */ 3 | "187-7k-0Ct.title" = "Refazer"; 4 | 5 | /* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "2sj-vF-eSL"; */ 6 | "2sj-vF-eSL.title" = "Buscar Atualizações..."; 7 | 8 | /* Class = "NSMenuItem"; title = "Next Third"; ObjectID = "7HO-L3-O6Q"; */ 9 | "7HO-L3-O6Q.title" = "Terço Seguinte"; 10 | 11 | /* Class = "NSMenuItem"; title = "Upper Right"; ObjectID = "C7t-Sf-kOI"; */ 12 | "C7t-Sf-kOI.title" = "Direita Superior"; 13 | 14 | /* Class = "NSMenuItem"; title = "About Spectacle"; ObjectID = "EA9-dx-Qfg"; */ 15 | "EA9-dx-Qfg.title" = "Sobre o Spectacle"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Spectacle would like to control your windows using accessibility features."; ObjectID = "HRo-tn-M1V"; */ 18 | "HRo-tn-M1V.title" = "O Spectacle deseja controlar as janelas usando recursos de acessibilidade."; 19 | 20 | /* Class = "NSMenuItem"; title = "Next Display"; ObjectID = "I4U-jL-hXq"; */ 21 | "I4U-jL-hXq.title" = "Monitor Seguinte"; 22 | 23 | /* Class = "NSMenuItem"; title = "Quit Spectacle"; ObjectID = "Isn-Og-AEv"; */ 24 | "Isn-Og-AEv.title" = "Encerrar Spectacle"; 25 | 26 | /* Class = "NSMenuItem"; title = "Left Half"; ObjectID = "Jae-vx-7ZC"; */ 27 | "Jae-vx-7ZC.title" = "Metade Esquerda"; 28 | 29 | /* Class = "NSMenu"; title = "Spectacle Status Item"; ObjectID = "KsA-8w-wcl"; */ 30 | "KsA-8w-wcl.title" = "Spectacle Status Item"; 31 | 32 | /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "ME4-Br-HLT"; */ 33 | "ME4-Br-HLT.title" = "Preferências..."; 34 | 35 | /* Class = "NSMenuItem"; title = "Make Larger"; ObjectID = "Yun-AA-OVD"; */ 36 | "Yun-AA-OVD.title" = "Aumentar"; 37 | 38 | /* Class = "NSMenuItem"; title = "Previous Display"; ObjectID = "aPo-Rp-A1X"; */ 39 | "aPo-Rp-A1X.title" = "Monitor Anterior"; 40 | 41 | /* Class = "NSMenuItem"; title = "Previous Third"; ObjectID = "bV9-Ga-jDG"; */ 42 | "bV9-Ga-jDG.title" = "Terço Anterior"; 43 | 44 | /* Class = "NSMenuItem"; title = "Right Half"; ObjectID = "cae-26-Oab"; */ 45 | "cae-26-Oab.title" = "Metade Direita"; 46 | 47 | /* Class = "NSMenuItem"; title = "Fullscreen"; ObjectID = "dwQ-Bl-nEL"; */ 48 | "dwQ-Bl-nEL.title" = "Preencher Tela"; 49 | 50 | /* Class = "NSMenuItem"; title = "Lower Left"; ObjectID = "eN5-A6-iho"; */ 51 | "eN5-A6-iho.title" = "Esquerda Inferior"; 52 | 53 | /* Class = "NSMenuItem"; title = "Disable"; ObjectID = "eZT-Mm-qSc"; */ 54 | "eZT-Mm-qSc.title" = "Desativar"; 55 | 56 | /* Class = "NSMenu"; title = "Disable"; ObjectID = "flL-B2-XIP"; */ 57 | "flL-B2-XIP.title" = "Desativar"; 58 | 59 | /* Class = "NSButtonCell"; title = "Quit Spectacle"; ObjectID = "gMV-vD-CUe"; */ 60 | "gMV-vD-CUe.title" = "Encerrar Spectacle"; 61 | 62 | /* Class = "NSMenuItem"; title = "Make Smaller"; ObjectID = "gTe-ym-DB0"; */ 63 | "gTe-ym-DB0.title" = "Diminuir"; 64 | 65 | /* Class = "NSMenuItem"; title = "Bottom Half"; ObjectID = "huV-Gw-lbX"; */ 66 | "huV-Gw-lbX.title" = "Metade Inferior"; 67 | 68 | /* Class = "NSButtonCell"; title = "Open System Preferences"; ObjectID = "iPl-AT-Pei"; */ 69 | "iPl-AT-Pei.title" = "Abrir Preferências do Sistema"; 70 | 71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "l9O-0B-fjp"; */ 72 | "l9O-0B-fjp.title" = "Desfazer"; 73 | 74 | /* Class = "NSMenuItem"; title = "Upper Left"; ObjectID = "ldc-QO-Cq6"; */ 75 | "ldc-QO-Cq6.title" = "Esquerda Superior"; 76 | 77 | /* Class = "NSMenuItem"; title = "for APPLICATION"; ObjectID = "msZ-id-Pvw"; */ 78 | "msZ-id-Pvw.title" = "no APPLICATION"; 79 | 80 | /* Class = "NSMenuItem"; title = "Lower Right"; ObjectID = "oDU-J0-NkJ"; */ 81 | "oDU-J0-NkJ.title" = "Direita Inferior"; 82 | 83 | /* Class = "NSMenuItem"; title = "for an hour"; ObjectID = "qpo-5w-ZIY"; */ 84 | "qpo-5w-ZIY.title" = "por uma hora"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Grant access to this application in System Preferences."; ObjectID = "ruu-yJ-Q1F"; */ 87 | "ruu-yJ-Q1F.title" = "Conceda acesso a este aplicativo nas Preferências do Sistema."; 88 | 89 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "xJu-Uo-nZw"; */ 90 | "xJu-Uo-nZw.title" = "Centralizar"; 91 | 92 | /* Class = "NSMenuItem"; title = "Top Half"; ObjectID = "yh0-ik-weD"; */ 93 | "yh0-ik-weD.title" = "Metade Superior"; 94 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/it.lproj/Spectacle.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "187-7k-0Ct"; */ 3 | "187-7k-0Ct.title" = "Ripristina"; 4 | 5 | /* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "2sj-vF-eSL"; */ 6 | "2sj-vF-eSL.title" = "Verifica aggiornamenti…"; 7 | 8 | /* Class = "NSMenuItem"; title = "Next Third"; ObjectID = "7HO-L3-O6Q"; */ 9 | "7HO-L3-O6Q.title" = "Terza successiva"; 10 | 11 | /* Class = "NSMenuItem"; title = "Upper Right"; ObjectID = "C7t-Sf-kOI"; */ 12 | "C7t-Sf-kOI.title" = "In alto a destra"; 13 | 14 | /* Class = "NSMenuItem"; title = "About Spectacle"; ObjectID = "EA9-dx-Qfg"; */ 15 | "EA9-dx-Qfg.title" = "Informazioni su Spectacle"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Spectacle would like to control your windows using accessibility features."; ObjectID = "HRo-tn-M1V"; */ 18 | "HRo-tn-M1V.title" = "Spectacle vorrebbe controllare le tue finestre usando le funzioni di accessibilità."; 19 | 20 | /* Class = "NSMenuItem"; title = "Next Display"; ObjectID = "I4U-jL-hXq"; */ 21 | "I4U-jL-hXq.title" = "Monitor successivo"; 22 | 23 | /* Class = "NSMenuItem"; title = "Quit Spectacle"; ObjectID = "Isn-Og-AEv"; */ 24 | "Isn-Og-AEv.title" = "Esci da Spectacle"; 25 | 26 | /* Class = "NSMenuItem"; title = "Left Half"; ObjectID = "Jae-vx-7ZC"; */ 27 | "Jae-vx-7ZC.title" = "Metà sinistra"; 28 | 29 | /* Class = "NSMenu"; title = "Spectacle Status Item"; ObjectID = "KsA-8w-wcl"; */ 30 | "KsA-8w-wcl.title" = "Spectacle Status Item"; 31 | 32 | /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "ME4-Br-HLT"; */ 33 | "ME4-Br-HLT.title" = "Preferenze…"; 34 | 35 | /* Class = "NSMenuItem"; title = "Make Larger"; ObjectID = "Yun-AA-OVD"; */ 36 | "Yun-AA-OVD.title" = "Ingrandisci"; 37 | 38 | /* Class = "NSMenuItem"; title = "Previous Display"; ObjectID = "aPo-Rp-A1X"; */ 39 | "aPo-Rp-A1X.title" = "Monitor precedente"; 40 | 41 | /* Class = "NSMenuItem"; title = "Previous Third"; ObjectID = "bV9-Ga-jDG"; */ 42 | "bV9-Ga-jDG.title" = "Terza precedente"; 43 | 44 | /* Class = "NSMenuItem"; title = "Right Half"; ObjectID = "cae-26-Oab"; */ 45 | "cae-26-Oab.title" = "Metà destra"; 46 | 47 | /* Class = "NSMenuItem"; title = "Fullscreen"; ObjectID = "dwQ-Bl-nEL"; */ 48 | "dwQ-Bl-nEL.title" = "A tutto schermo"; 49 | 50 | /* Class = "NSMenuItem"; title = "Lower Left"; ObjectID = "eN5-A6-iho"; */ 51 | "eN5-A6-iho.title" = "In basso a sinistra"; 52 | 53 | /* Class = "NSMenuItem"; title = "Disable"; ObjectID = "eZT-Mm-qSc"; */ 54 | "eZT-Mm-qSc.title" = "Disattiva"; 55 | 56 | /* Class = "NSMenu"; title = "Disable"; ObjectID = "flL-B2-XIP"; */ 57 | "flL-B2-XIP.title" = "Disattiva"; 58 | 59 | /* Class = "NSButtonCell"; title = "Quit Spectacle"; ObjectID = "gMV-vD-CUe"; */ 60 | "gMV-vD-CUe.title" = "Esci da Spectacle"; 61 | 62 | /* Class = "NSMenuItem"; title = "Make Smaller"; ObjectID = "gTe-ym-DB0"; */ 63 | "gTe-ym-DB0.title" = "Riduci"; 64 | 65 | /* Class = "NSMenuItem"; title = "Bottom Half"; ObjectID = "huV-Gw-lbX"; */ 66 | "huV-Gw-lbX.title" = "Metà bassa"; 67 | 68 | /* Class = "NSButtonCell"; title = "Open System Preferences"; ObjectID = "iPl-AT-Pei"; */ 69 | "iPl-AT-Pei.title" = "Apri Preferenze di Sistema"; 70 | 71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "l9O-0B-fjp"; */ 72 | "l9O-0B-fjp.title" = "Annulla"; 73 | 74 | /* Class = "NSMenuItem"; title = "Upper Left"; ObjectID = "ldc-QO-Cq6"; */ 75 | "ldc-QO-Cq6.title" = "In alto a sinistra"; 76 | 77 | /* Class = "NSMenuItem"; title = "for APPLICATION"; ObjectID = "msZ-id-Pvw"; */ 78 | "msZ-id-Pvw.title" = "per APPLICATION"; 79 | 80 | /* Class = "NSMenuItem"; title = "Lower Right"; ObjectID = "oDU-J0-NkJ"; */ 81 | "oDU-J0-NkJ.title" = "In basso a destra"; 82 | 83 | /* Class = "NSMenuItem"; title = "for an hour"; ObjectID = "qpo-5w-ZIY"; */ 84 | "qpo-5w-ZIY.title" = "per un'ora"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Grant access to this application in System Preferences."; ObjectID = "ruu-yJ-Q1F"; */ 87 | "ruu-yJ-Q1F.title" = "Concedere l'accesso a questa applicazione nelle Preferenze di Sistema."; 88 | 89 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "xJu-Uo-nZw"; */ 90 | "xJu-Uo-nZw.title" = "Centra"; 91 | 92 | /* Class = "NSMenuItem"; title = "Top Half"; ObjectID = "yh0-ik-weD"; */ 93 | "yh0-ik-weD.title" = "Metà alta"; 94 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/es.lproj/Spectacle.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "187-7k-0Ct"; */ 3 | "187-7k-0Ct.title" = "Rehacer"; 4 | 5 | /* Class = "NSMenuItem"; title = "Check for Updates..."; ObjectID = "2sj-vF-eSL"; */ 6 | "2sj-vF-eSL.title" = "Chequear actualizaciones..."; 7 | 8 | /* Class = "NSMenuItem"; title = "Next Third"; ObjectID = "7HO-L3-O6Q"; */ 9 | "7HO-L3-O6Q.title" = "Tercer próximo"; 10 | 11 | /* Class = "NSMenuItem"; title = "Upper Right"; ObjectID = "C7t-Sf-kOI"; */ 12 | "C7t-Sf-kOI.title" = "Superior derecha"; 13 | 14 | /* Class = "NSMenuItem"; title = "About Spectacle"; ObjectID = "EA9-dx-Qfg"; */ 15 | "EA9-dx-Qfg.title" = "Acerca de Spectacle"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Spectacle would like to control your windows using accessibility features."; ObjectID = "HRo-tn-M1V"; */ 18 | "HRo-tn-M1V.title" = "Spectacle necesita tener control de las funcionalidades de accesibilidad."; 19 | 20 | /* Class = "NSMenuItem"; title = "Next Display"; ObjectID = "I4U-jL-hXq"; */ 21 | "I4U-jL-hXq.title" = "Siguiente monitor"; 22 | 23 | /* Class = "NSMenuItem"; title = "Quit Spectacle"; ObjectID = "Isn-Og-AEv"; */ 24 | "Isn-Og-AEv.title" = "Salir Spectacle"; 25 | 26 | /* Class = "NSMenuItem"; title = "Left Half"; ObjectID = "Jae-vx-7ZC"; */ 27 | "Jae-vx-7ZC.title" = "La mitad izquierda"; 28 | 29 | /* Class = "NSMenu"; title = "Spectacle Status Item"; ObjectID = "KsA-8w-wcl"; */ 30 | "KsA-8w-wcl.title" = "Spectacle estado del elemento"; 31 | 32 | /* Class = "NSMenuItem"; title = "Preferences..."; ObjectID = "ME4-Br-HLT"; */ 33 | "ME4-Br-HLT.title" = "Configuración..."; 34 | 35 | /* Class = "NSMenuItem"; title = "Make Larger"; ObjectID = "Yun-AA-OVD"; */ 36 | "Yun-AA-OVD.title" = "Hacer más grande"; 37 | 38 | /* Class = "NSMenuItem"; title = "Previous Display"; ObjectID = "aPo-Rp-A1X"; */ 39 | "aPo-Rp-A1X.title" = "Monitor anterior"; 40 | 41 | /* Class = "NSMenuItem"; title = "Previous Third"; ObjectID = "bV9-Ga-jDG"; */ 42 | "bV9-Ga-jDG.title" = "Tercer anterior"; 43 | 44 | /* Class = "NSMenuItem"; title = "Right Half"; ObjectID = "cae-26-Oab"; */ 45 | "cae-26-Oab.title" = "Mitad derecha"; 46 | 47 | /* Class = "NSMenuItem"; title = "Fullscreen"; ObjectID = "dwQ-Bl-nEL"; */ 48 | "dwQ-Bl-nEL.title" = "Pantalla completa"; 49 | 50 | /* Class = "NSMenuItem"; title = "Lower Left"; ObjectID = "eN5-A6-iho"; */ 51 | "eN5-A6-iho.title" = "Abajo a la izquierda"; 52 | 53 | /* Class = "NSMenuItem"; title = "Disable"; ObjectID = "eZT-Mm-qSc"; */ 54 | "eZT-Mm-qSc.title" = "Deshabilitado"; 55 | 56 | /* Class = "NSMenu"; title = "Disable"; ObjectID = "flL-B2-XIP"; */ 57 | "flL-B2-XIP.title" = "Deshabilitado"; 58 | 59 | /* Class = "NSButtonCell"; title = "Quit Spectacle"; ObjectID = "gMV-vD-CUe"; */ 60 | "gMV-vD-CUe.title" = "Salir Spectacle"; 61 | 62 | /* Class = "NSMenuItem"; title = "Make Smaller"; ObjectID = "gTe-ym-DB0"; */ 63 | "gTe-ym-DB0.title" = "Hacer mas pequeño"; 64 | 65 | /* Class = "NSMenuItem"; title = "Bottom Half"; ObjectID = "huV-Gw-lbX"; */ 66 | "huV-Gw-lbX.title" = "Mitad inferior"; 67 | 68 | /* Class = "NSButtonCell"; title = "Open System Preferences"; ObjectID = "iPl-AT-Pei"; */ 69 | "iPl-AT-Pei.title" = "Abrir preferencias de sistema"; 70 | 71 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "l9O-0B-fjp"; */ 72 | "l9O-0B-fjp.title" = "Deshacer"; 73 | 74 | /* Class = "NSMenuItem"; title = "Upper Left"; ObjectID = "ldc-QO-Cq6"; */ 75 | "ldc-QO-Cq6.title" = "Arriba a la izquierda"; 76 | 77 | /* Class = "NSMenuItem"; title = "for APPLICATION"; ObjectID = "msZ-id-Pvw"; */ 78 | "msZ-id-Pvw.title" = "para la aplicación"; 79 | 80 | /* Class = "NSMenuItem"; title = "Lower Right"; ObjectID = "oDU-J0-NkJ"; */ 81 | "oDU-J0-NkJ.title" = "Inferior derecha"; 82 | 83 | /* Class = "NSMenuItem"; title = "for an hour"; ObjectID = "qpo-5w-ZIY"; */ 84 | "qpo-5w-ZIY.title" = "Por una hora"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Grant access to this application in System Preferences."; ObjectID = "ruu-yJ-Q1F"; */ 87 | "ruu-yJ-Q1F.title" = "Permitir el acceso a esta aplicación en Preferencias del sistema."; 88 | 89 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "xJu-Uo-nZw"; */ 90 | "xJu-Uo-nZw.title" = "Centro"; 91 | 92 | /* Class = "NSMenuItem"; title = "Top Half"; ObjectID = "yh0-ik-weD"; */ 93 | "yh0-ik-weD.title" = "Mitad superior"; 94 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fi.lproj/SpectaclePreferencesWindow.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Make Larger:"; ObjectID = "0f9-kj-bro"; */ 3 | "0f9-kj-bro.title" = "Suurenna:"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Control"; ObjectID = "7Fe-Da-fYB"; */ 6 | "7Fe-Da-fYB.title" = "Ctrl"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "⌃"; ObjectID = "806-S1-nYH"; */ 9 | "806-S1-nYH.title" = "⌃"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Top Half:"; ObjectID = "AnP-Lv-Hbd"; */ 12 | "AnP-Lv-Hbd.title" = "Yläpuoli:"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Bottom Half:"; ObjectID = "Awn-mO-sht"; */ 15 | "Awn-mO-sht.title" = "Alapuoli:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Undo:"; ObjectID = "CJs-az-eG7"; */ 18 | "CJs-az-eG7.title" = "Kumoa:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Left Half:"; ObjectID = "IWj-rc-Kvi"; */ 21 | "IWj-rc-Kvi.title" = "Vasen puoli:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "⇧"; ObjectID = "Ir0-AJ-ldj"; */ 24 | "Ir0-AJ-ldj.title" = "⇧"; 25 | 26 | /* Class = "NSWindow"; title = "Spectacle X.Y.Z"; ObjectID = "JW4-q9-L7q"; */ 27 | "JW4-q9-L7q.title" = "Spectacle X.Y.Z"; 28 | 29 | /* Class = "NSButtonCell"; title = "Restore Defaults..."; ObjectID = "NT7-Sd-Ofw"; */ 30 | "NT7-Sd-Ofw.title" = "Palauta oletukset..."; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Make Smaller:"; ObjectID = "SDM-U5-yUR"; */ 33 | "SDM-U5-yUR.title" = "Pienennä:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "⌘"; ObjectID = "SQM-4e-yFd"; */ 36 | "SQM-4e-yFd.title" = "⌘"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Next Third:"; ObjectID = "Tze-mR-IV4"; */ 39 | "Tze-mR-IV4.title" = "Seuraava kolmasosa:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Option"; ObjectID = "U0H-hG-KAQ"; */ 42 | "U0H-hG-KAQ.title" = "Optio"; 43 | 44 | /* Class = "NSTextFieldCell"; title = "Lower Right:"; ObjectID = "Vul-FT-BbB"; */ 45 | "Vul-FT-BbB.title" = "Alaoikea:"; 46 | 47 | /* Class = "NSMenu"; title = "OtherViews"; ObjectID = "W2t-KP-bYJ"; */ 48 | "W2t-KP-bYJ.title" = "MuutNäkymät"; 49 | 50 | /* Class = "NSTextFieldCell"; title = "Right Half:"; ObjectID = "WeK-fS-mRh"; */ 51 | "WeK-fS-mRh.title" = "Oikea puoli:"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "⌥"; ObjectID = "YQm-Kc-fYF"; */ 54 | "YQm-Kc-fYF.title" = "⌥"; 55 | 56 | /* Class = "NSMenuItem"; title = "as a background application"; ObjectID = "aWf-FC-J1j"; */ 57 | "aWf-FC-J1j.title" = "taustaohjelmana"; 58 | 59 | /* Class = "NSButtonCell"; title = "Launch Spectacle at login"; ObjectID = "cEj-mH-kie"; */ 60 | "cEj-mH-kie.title" = "Käynnistä Spectacle kirjauduttaessa"; 61 | 62 | /* Class = "NSTextFieldCell"; title = "Lower Left:"; ObjectID = "dQP-TR-GMx"; */ 63 | "dQP-TR-GMx.title" = "Alavasen:"; 64 | 65 | /* Class = "NSTextFieldCell"; title = "Shift"; ObjectID = "dww-8Q-sa1"; */ 66 | "dww-8Q-sa1.title" = "Vaihto"; 67 | 68 | /* Class = "NSTextFieldCell"; title = "Run..."; ObjectID = "eYU-TH-wcO"; */ 69 | "eYU-TH-wcO.title" = "Suorita..."; 70 | 71 | /* Class = "NSTextFieldCell"; title = "Upper Right:"; ObjectID = "gSY-Au-xOH"; */ 72 | "gSY-Au-xOH.title" = "Yläoikea:"; 73 | 74 | /* Class = "NSTextFieldCell"; title = "Previous Display:"; ObjectID = "hmL-EC-pro"; */ 75 | "hmL-EC-pro.title" = "Edellinen näyttö:"; 76 | 77 | /* Class = "NSMenuItem"; title = "in the status menu"; ObjectID = "in4-v8-tIm"; */ 78 | "in4-v8-tIm.title" = "valikkopalkissa"; 79 | 80 | /* Class = "NSTextFieldCell"; title = "Command"; ObjectID = "jdf-Yu-zrq"; */ 81 | "jdf-Yu-zrq.title" = "Komento"; 82 | 83 | /* Class = "NSTextFieldCell"; title = "Upper Left:"; ObjectID = "ldH-1E-mKW"; */ 84 | "ldH-1E-mKW.title" = "Ylävasen:"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Next Display:"; ObjectID = "lw7-Ap-kJO"; */ 87 | "lw7-Ap-kJO.title" = "Seuraava näyttö:"; 88 | 89 | /* Class = "NSTextFieldCell"; title = "Center:"; ObjectID = "oqf-7z-BFa"; */ 90 | "oqf-7z-BFa.title" = "Keskitys:"; 91 | 92 | /* Class = "NSTextFieldCell"; title = "Previous Third:"; ObjectID = "s3E-Lp-mJ4"; */ 93 | "s3E-Lp-mJ4.title" = "Edellinen kolmasosa:"; 94 | 95 | /* Class = "NSTextFieldCell"; title = "Fullscreen:"; ObjectID = "tKr-XE-DEU"; */ 96 | "tKr-XE-DEU.title" = "Koko näyttö:"; 97 | 98 | /* Class = "NSBox"; title = "Box"; ObjectID = "vWw-ws-BgW"; */ 99 | "vWw-ws-BgW.title" = "Box"; 100 | 101 | /* Class = "NSTextFieldCell"; title = "Redo:"; ObjectID = "y7M-cR-DF2"; */ 102 | "y7M-cR-DF2.title" = "Tee uudelleen:"; 103 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/pt.lproj/SpectaclePreferencesWindow.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Make Larger:"; ObjectID = "0f9-kj-bro"; */ 3 | "0f9-kj-bro.title" = "Aumentar:"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Control"; ObjectID = "7Fe-Da-fYB"; */ 6 | "7Fe-Da-fYB.title" = "Controle"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "⌃"; ObjectID = "806-S1-nYH"; */ 9 | "806-S1-nYH.title" = "⌃"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Top Half:"; ObjectID = "AnP-Lv-Hbd"; */ 12 | "AnP-Lv-Hbd.title" = "Metade Superior:"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Bottom Half:"; ObjectID = "Awn-mO-sht"; */ 15 | "Awn-mO-sht.title" = "Metade Inferior:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Undo:"; ObjectID = "CJs-az-eG7"; */ 18 | "CJs-az-eG7.title" = "Desfazer:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Left Half:"; ObjectID = "IWj-rc-Kvi"; */ 21 | "IWj-rc-Kvi.title" = "Metade Esquerda:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "⇧"; ObjectID = "Ir0-AJ-ldj"; */ 24 | "Ir0-AJ-ldj.title" = "⇧"; 25 | 26 | /* Class = "NSWindow"; title = "Spectacle X.Y.Z"; ObjectID = "JW4-q9-L7q"; */ 27 | "JW4-q9-L7q.title" = "Spectacle X.Y.Z"; 28 | 29 | /* Class = "NSButtonCell"; title = "Restore Defaults..."; ObjectID = "NT7-Sd-Ofw"; */ 30 | "NT7-Sd-Ofw.title" = "Restaurar Padrões..."; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Make Smaller:"; ObjectID = "SDM-U5-yUR"; */ 33 | "SDM-U5-yUR.title" = "Diminuir:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "⌘"; ObjectID = "SQM-4e-yFd"; */ 36 | "SQM-4e-yFd.title" = "⌘"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Next Third:"; ObjectID = "Tze-mR-IV4"; */ 39 | "Tze-mR-IV4.title" = "Terço Seguinte:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Option"; ObjectID = "U0H-hG-KAQ"; */ 42 | "U0H-hG-KAQ.title" = "Opção"; 43 | 44 | /* Class = "NSTextFieldCell"; title = "Lower Right:"; ObjectID = "Vul-FT-BbB"; */ 45 | "Vul-FT-BbB.title" = "Direita Inferior:"; 46 | 47 | /* Class = "NSMenu"; title = "OtherViews"; ObjectID = "W2t-KP-bYJ"; */ 48 | "W2t-KP-bYJ.title" = "OtherViews"; 49 | 50 | /* Class = "NSTextFieldCell"; title = "Right Half:"; ObjectID = "WeK-fS-mRh"; */ 51 | "WeK-fS-mRh.title" = "Metade Direita:"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "⌥"; ObjectID = "YQm-Kc-fYF"; */ 54 | "YQm-Kc-fYF.title" = "⌥"; 55 | 56 | /* Class = "NSMenuItem"; title = "as a background application"; ObjectID = "aWf-FC-J1j"; */ 57 | "aWf-FC-J1j.title" = "em 2º plano"; 58 | 59 | /* Class = "NSButtonCell"; title = "Launch Spectacle at login"; ObjectID = "cEj-mH-kie"; */ 60 | "cEj-mH-kie.title" = "Abrir ao iniciar a sessão"; 61 | 62 | /* Class = "NSTextFieldCell"; title = "Lower Left:"; ObjectID = "dQP-TR-GMx"; */ 63 | "dQP-TR-GMx.title" = "Esquerda Inferior:"; 64 | 65 | /* Class = "NSTextFieldCell"; title = "Shift"; ObjectID = "dww-8Q-sa1"; */ 66 | "dww-8Q-sa1.title" = "Maiúsculas"; 67 | 68 | /* Class = "NSTextFieldCell"; title = "Run..."; ObjectID = "eYU-TH-wcO"; */ 69 | "eYU-TH-wcO.title" = "Manter:"; 70 | 71 | /* Class = "NSTextFieldCell"; title = "Upper Right:"; ObjectID = "gSY-Au-xOH"; */ 72 | "gSY-Au-xOH.title" = "Direita Superior:"; 73 | 74 | /* Class = "NSTextFieldCell"; title = "Previous Display:"; ObjectID = "hmL-EC-pro"; */ 75 | "hmL-EC-pro.title" = "Monitor Anterior:"; 76 | 77 | /* Class = "NSMenuItem"; title = "in the status menu"; ObjectID = "in4-v8-tIm"; */ 78 | "in4-v8-tIm.title" = "na barra de menus"; 79 | 80 | /* Class = "NSTextFieldCell"; title = "Command"; ObjectID = "jdf-Yu-zrq"; */ 81 | "jdf-Yu-zrq.title" = "Comando"; 82 | 83 | /* Class = "NSTextFieldCell"; title = "Upper Left:"; ObjectID = "ldH-1E-mKW"; */ 84 | "ldH-1E-mKW.title" = "Esquerda Superior:"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Next Display:"; ObjectID = "lw7-Ap-kJO"; */ 87 | "lw7-Ap-kJO.title" = "Monitor Seguinte:"; 88 | 89 | /* Class = "NSTextFieldCell"; title = "Center:"; ObjectID = "oqf-7z-BFa"; */ 90 | "oqf-7z-BFa.title" = "Centralizar:"; 91 | 92 | /* Class = "NSTextFieldCell"; title = "Previous Third:"; ObjectID = "s3E-Lp-mJ4"; */ 93 | "s3E-Lp-mJ4.title" = "Terço Anterior:"; 94 | 95 | /* Class = "NSTextFieldCell"; title = "Fullscreen:"; ObjectID = "tKr-XE-DEU"; */ 96 | "tKr-XE-DEU.title" = "Preencher Tela:"; 97 | 98 | /* Class = "NSBox"; title = "Box"; ObjectID = "vWw-ws-BgW"; */ 99 | "vWw-ws-BgW.title" = "Box"; 100 | 101 | /* Class = "NSTextFieldCell"; title = "Redo:"; ObjectID = "y7M-cR-DF2"; */ 102 | "y7M-cR-DF2.title" = "Refazer:"; 103 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/fr.lproj/SpectaclePreferencesWindow.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Make Larger:"; ObjectID = "0f9-kj-bro"; */ 3 | "0f9-kj-bro.title" = "Agrandir:"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Control"; ObjectID = "7Fe-Da-fYB"; */ 6 | "7Fe-Da-fYB.title" = "Contrôle"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "⌃"; ObjectID = "806-S1-nYH"; */ 9 | "806-S1-nYH.title" = "⌃"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Top Half:"; ObjectID = "AnP-Lv-Hbd"; */ 12 | "AnP-Lv-Hbd.title" = "Moitié haut:"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Bottom Half:"; ObjectID = "Awn-mO-sht"; */ 15 | "Awn-mO-sht.title" = "Moitié bas:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Undo:"; ObjectID = "CJs-az-eG7"; */ 18 | "CJs-az-eG7.title" = "Annuler:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Left Half:"; ObjectID = "IWj-rc-Kvi"; */ 21 | "IWj-rc-Kvi.title" = "Moitié gauche:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "⇧"; ObjectID = "Ir0-AJ-ldj"; */ 24 | "Ir0-AJ-ldj.title" = "⇧"; 25 | 26 | /* Class = "NSWindow"; title = "Spectacle X.Y.Z"; ObjectID = "JW4-q9-L7q"; */ 27 | "JW4-q9-L7q.title" = "Spectacle X.Y.Z"; 28 | 29 | /* Class = "NSButtonCell"; title = "Restore Defaults..."; ObjectID = "NT7-Sd-Ofw"; */ 30 | "NT7-Sd-Ofw.title" = "Rétablir par défaut..."; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Make Smaller:"; ObjectID = "SDM-U5-yUR"; */ 33 | "SDM-U5-yUR.title" = "Réduire:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "⌘"; ObjectID = "SQM-4e-yFd"; */ 36 | "SQM-4e-yFd.title" = "⌘"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Next Third:"; ObjectID = "Tze-mR-IV4"; */ 39 | "Tze-mR-IV4.title" = "Tiers suivant:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Option"; ObjectID = "U0H-hG-KAQ"; */ 42 | "U0H-hG-KAQ.title" = "Option"; 43 | 44 | /* Class = "NSTextFieldCell"; title = "Lower Right:"; ObjectID = "Vul-FT-BbB"; */ 45 | "Vul-FT-BbB.title" = "En bas à droite:"; 46 | 47 | /* Class = "NSMenu"; title = "OtherViews"; ObjectID = "W2t-KP-bYJ"; */ 48 | "W2t-KP-bYJ.title" = "OtherViews"; 49 | 50 | /* Class = "NSTextFieldCell"; title = "Right Half:"; ObjectID = "WeK-fS-mRh"; */ 51 | "WeK-fS-mRh.title" = "Moitié droit:"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "⌥"; ObjectID = "YQm-Kc-fYF"; */ 54 | "YQm-Kc-fYF.title" = "⌥"; 55 | 56 | /* Class = "NSMenuItem"; title = "as a background application"; ObjectID = "aWf-FC-J1j"; */ 57 | "aWf-FC-J1j.title" = "comme une application d'arrière-plan"; 58 | 59 | /* Class = "NSButtonCell"; title = "Launch Spectacle at login"; ObjectID = "cEj-mH-kie"; */ 60 | "cEj-mH-kie.title" = "Ouvrir Spectacle au démarrage"; 61 | 62 | /* Class = "NSTextFieldCell"; title = "Lower Left:"; ObjectID = "dQP-TR-GMx"; */ 63 | "dQP-TR-GMx.title" = "En bas à gauche:"; 64 | 65 | /* Class = "NSTextFieldCell"; title = "Shift"; ObjectID = "dww-8Q-sa1"; */ 66 | "dww-8Q-sa1.title" = "Maj"; 67 | 68 | /* Class = "NSTextFieldCell"; title = "Run..."; ObjectID = "eYU-TH-wcO"; */ 69 | "eYU-TH-wcO.title" = "Lancer..."; 70 | 71 | /* Class = "NSTextFieldCell"; title = "Upper Right:"; ObjectID = "gSY-Au-xOH"; */ 72 | "gSY-Au-xOH.title" = "En haut à droite:"; 73 | 74 | /* Class = "NSTextFieldCell"; title = "Previous Display:"; ObjectID = "hmL-EC-pro"; */ 75 | "hmL-EC-pro.title" = "Écran précédent:"; 76 | 77 | /* Class = "NSMenuItem"; title = "in the status menu"; ObjectID = "in4-v8-tIm"; */ 78 | "in4-v8-tIm.title" = "dans la barre d'état"; 79 | 80 | /* Class = "NSTextFieldCell"; title = "Command"; ObjectID = "jdf-Yu-zrq"; */ 81 | "jdf-Yu-zrq.title" = "Commande"; 82 | 83 | /* Class = "NSTextFieldCell"; title = "Upper Left:"; ObjectID = "ldH-1E-mKW"; */ 84 | "ldH-1E-mKW.title" = "En haut à gauche:"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Next Display:"; ObjectID = "lw7-Ap-kJO"; */ 87 | "lw7-Ap-kJO.title" = "Écran suivant:"; 88 | 89 | /* Class = "NSTextFieldCell"; title = "Center:"; ObjectID = "oqf-7z-BFa"; */ 90 | "oqf-7z-BFa.title" = "Centrer:"; 91 | 92 | /* Class = "NSTextFieldCell"; title = "Previous Third:"; ObjectID = "s3E-Lp-mJ4"; */ 93 | "s3E-Lp-mJ4.title" = "Tiers précédent:"; 94 | 95 | /* Class = "NSTextFieldCell"; title = "Fullscreen:"; ObjectID = "tKr-XE-DEU"; */ 96 | "tKr-XE-DEU.title" = "Plein écran:"; 97 | 98 | /* Class = "NSBox"; title = "Box"; ObjectID = "vWw-ws-BgW"; */ 99 | "vWw-ws-BgW.title" = "Box"; 100 | 101 | /* Class = "NSTextFieldCell"; title = "Redo:"; ObjectID = "y7M-cR-DF2"; */ 102 | "y7M-cR-DF2.title" = "Rétablir:"; 103 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/it.lproj/SpectaclePreferencesWindow.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Make Larger:"; ObjectID = "0f9-kj-bro"; */ 3 | "0f9-kj-bro.title" = "Ingrandisci:"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Control"; ObjectID = "7Fe-Da-fYB"; */ 6 | "7Fe-Da-fYB.title" = "Control"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "⌃"; ObjectID = "806-S1-nYH"; */ 9 | "806-S1-nYH.title" = "⌃"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Top Half:"; ObjectID = "AnP-Lv-Hbd"; */ 12 | "AnP-Lv-Hbd.title" = "Metà alta:"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Bottom Half:"; ObjectID = "Awn-mO-sht"; */ 15 | "Awn-mO-sht.title" = "Metà bassa:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Undo:"; ObjectID = "CJs-az-eG7"; */ 18 | "CJs-az-eG7.title" = "Annulla:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Left Half:"; ObjectID = "IWj-rc-Kvi"; */ 21 | "IWj-rc-Kvi.title" = "Metà sinistra:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "⇧"; ObjectID = "Ir0-AJ-ldj"; */ 24 | "Ir0-AJ-ldj.title" = "⇧"; 25 | 26 | /* Class = "NSWindow"; title = "Spectacle X.Y.Z"; ObjectID = "JW4-q9-L7q"; */ 27 | "JW4-q9-L7q.title" = "Spectacle X.Y.Z"; 28 | 29 | /* Class = "NSButtonCell"; title = "Restore Defaults..."; ObjectID = "NT7-Sd-Ofw"; */ 30 | "NT7-Sd-Ofw.title" = "Ripristina predefiniti…"; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Make Smaller:"; ObjectID = "SDM-U5-yUR"; */ 33 | "SDM-U5-yUR.title" = "Riduci:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "⌘"; ObjectID = "SQM-4e-yFd"; */ 36 | "SQM-4e-yFd.title" = "⌘"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Next Third:"; ObjectID = "Tze-mR-IV4"; */ 39 | "Tze-mR-IV4.title" = "Terza successiva:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Option"; ObjectID = "U0H-hG-KAQ"; */ 42 | "U0H-hG-KAQ.title" = "Opzione"; 43 | 44 | /* Class = "NSTextFieldCell"; title = "Lower Right:"; ObjectID = "Vul-FT-BbB"; */ 45 | "Vul-FT-BbB.title" = "In basso a destra:"; 46 | 47 | /* Class = "NSMenu"; title = "OtherViews"; ObjectID = "W2t-KP-bYJ"; */ 48 | "W2t-KP-bYJ.title" = "OtherViews"; 49 | 50 | /* Class = "NSTextFieldCell"; title = "Right Half:"; ObjectID = "WeK-fS-mRh"; */ 51 | "WeK-fS-mRh.title" = "Metà destra:"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "⌥"; ObjectID = "YQm-Kc-fYF"; */ 54 | "YQm-Kc-fYF.title" = "⌥"; 55 | 56 | /* Class = "NSMenuItem"; title = "as a background application"; ObjectID = "aWf-FC-J1j"; */ 57 | "aWf-FC-J1j.title" = "come applicazione in background"; 58 | 59 | /* Class = "NSButtonCell"; title = "Launch Spectacle at login"; ObjectID = "cEj-mH-kie"; */ 60 | "cEj-mH-kie.title" = "Apri Spectacle al login"; 61 | 62 | /* Class = "NSTextFieldCell"; title = "Lower Left:"; ObjectID = "dQP-TR-GMx"; */ 63 | "dQP-TR-GMx.title" = "In basso a sinistra:"; 64 | 65 | /* Class = "NSTextFieldCell"; title = "Shift"; ObjectID = "dww-8Q-sa1"; */ 66 | "dww-8Q-sa1.title" = "Maiuscole"; 67 | 68 | /* Class = "NSTextFieldCell"; title = "Run..."; ObjectID = "eYU-TH-wcO"; */ 69 | "eYU-TH-wcO.title" = "Esegui…"; 70 | 71 | /* Class = "NSTextFieldCell"; title = "Upper Right:"; ObjectID = "gSY-Au-xOH"; */ 72 | "gSY-Au-xOH.title" = "In alto a destra:"; 73 | 74 | /* Class = "NSTextFieldCell"; title = "Previous Display:"; ObjectID = "hmL-EC-pro"; */ 75 | "hmL-EC-pro.title" = "Monitor precedente:"; 76 | 77 | /* Class = "NSMenuItem"; title = "in the status menu"; ObjectID = "in4-v8-tIm"; */ 78 | "in4-v8-tIm.title" = "nel menu di stato"; 79 | 80 | /* Class = "NSTextFieldCell"; title = "Command"; ObjectID = "jdf-Yu-zrq"; */ 81 | "jdf-Yu-zrq.title" = "Comando"; 82 | 83 | /* Class = "NSTextFieldCell"; title = "Upper Left:"; ObjectID = "ldH-1E-mKW"; */ 84 | "ldH-1E-mKW.title" = "In alto a sinistra:"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Next Display:"; ObjectID = "lw7-Ap-kJO"; */ 87 | "lw7-Ap-kJO.title" = "Monitor successivo:"; 88 | 89 | /* Class = "NSTextFieldCell"; title = "Center:"; ObjectID = "oqf-7z-BFa"; */ 90 | "oqf-7z-BFa.title" = "Centra:"; 91 | 92 | /* Class = "NSTextFieldCell"; title = "Previous Third:"; ObjectID = "s3E-Lp-mJ4"; */ 93 | "s3E-Lp-mJ4.title" = "Terza precedente:"; 94 | 95 | /* Class = "NSTextFieldCell"; title = "Fullscreen:"; ObjectID = "tKr-XE-DEU"; */ 96 | "tKr-XE-DEU.title" = "A tutto schermo:"; 97 | 98 | /* Class = "NSBox"; title = "Box"; ObjectID = "vWw-ws-BgW"; */ 99 | "vWw-ws-BgW.title" = "Box"; 100 | 101 | /* Class = "NSTextFieldCell"; title = "Redo:"; ObjectID = "y7M-cR-DF2"; */ 102 | "y7M-cR-DF2.title" = "Ripristina:"; 103 | -------------------------------------------------------------------------------- /Spectacle/Resources/Localizations/es.lproj/SpectaclePreferencesWindow.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSTextFieldCell"; title = "Make Larger:"; ObjectID = "0f9-kj-bro"; */ 3 | "0f9-kj-bro.title" = "Hacer más grande:"; 4 | 5 | /* Class = "NSTextFieldCell"; title = "Control"; ObjectID = "7Fe-Da-fYB"; */ 6 | "7Fe-Da-fYB.title" = "Control"; 7 | 8 | /* Class = "NSTextFieldCell"; title = "⌃"; ObjectID = "806-S1-nYH"; */ 9 | "806-S1-nYH.title" = "⌃"; 10 | 11 | /* Class = "NSTextFieldCell"; title = "Top Half:"; ObjectID = "AnP-Lv-Hbd"; */ 12 | "AnP-Lv-Hbd.title" = "Mitad superior:"; 13 | 14 | /* Class = "NSTextFieldCell"; title = "Bottom Half:"; ObjectID = "Awn-mO-sht"; */ 15 | "Awn-mO-sht.title" = "Mitad inferior:"; 16 | 17 | /* Class = "NSTextFieldCell"; title = "Undo:"; ObjectID = "CJs-az-eG7"; */ 18 | "CJs-az-eG7.title" = "Deshacer:"; 19 | 20 | /* Class = "NSTextFieldCell"; title = "Left Half:"; ObjectID = "IWj-rc-Kvi"; */ 21 | "IWj-rc-Kvi.title" = "Mitad izquierda:"; 22 | 23 | /* Class = "NSTextFieldCell"; title = "⇧"; ObjectID = "Ir0-AJ-ldj"; */ 24 | "Ir0-AJ-ldj.title" = "⇧"; 25 | 26 | /* Class = "NSWindow"; title = "Spectacle X.Y.Z"; ObjectID = "JW4-q9-L7q"; */ 27 | "JW4-q9-L7q.title" = "Spectacle X.Y.Z"; 28 | 29 | /* Class = "NSButtonCell"; title = "Restore Defaults..."; ObjectID = "NT7-Sd-Ofw"; */ 30 | "NT7-Sd-Ofw.title" = "Restaurar por defecto..."; 31 | 32 | /* Class = "NSTextFieldCell"; title = "Make Smaller:"; ObjectID = "SDM-U5-yUR"; */ 33 | "SDM-U5-yUR.title" = "Hacer más pequeño:"; 34 | 35 | /* Class = "NSTextFieldCell"; title = "⌘"; ObjectID = "SQM-4e-yFd"; */ 36 | "SQM-4e-yFd.title" = "⌘"; 37 | 38 | /* Class = "NSTextFieldCell"; title = "Next Third:"; ObjectID = "Tze-mR-IV4"; */ 39 | "Tze-mR-IV4.title" = "Siguiente tercero:"; 40 | 41 | /* Class = "NSTextFieldCell"; title = "Option"; ObjectID = "U0H-hG-KAQ"; */ 42 | "U0H-hG-KAQ.title" = "Opciones"; 43 | 44 | /* Class = "NSTextFieldCell"; title = "Lower Right:"; ObjectID = "Vul-FT-BbB"; */ 45 | "Vul-FT-BbB.title" = "Inferior derecho:"; 46 | 47 | /* Class = "NSMenu"; title = "OtherViews"; ObjectID = "W2t-KP-bYJ"; */ 48 | "W2t-KP-bYJ.title" = "Otras vistas"; 49 | 50 | /* Class = "NSTextFieldCell"; title = "Right Half:"; ObjectID = "WeK-fS-mRh"; */ 51 | "WeK-fS-mRh.title" = "Mitad derecha:"; 52 | 53 | /* Class = "NSTextFieldCell"; title = "⌥"; ObjectID = "YQm-Kc-fYF"; */ 54 | "YQm-Kc-fYF.title" = "⌥"; 55 | 56 | /* Class = "NSMenuItem"; title = "as a background application"; ObjectID = "aWf-FC-J1j"; */ 57 | "aWf-FC-J1j.title" = "aplicación en segundo plano"; 58 | 59 | /* Class = "NSButtonCell"; title = "Launch Spectacle at login"; ObjectID = "cEj-mH-kie"; */ 60 | "cEj-mH-kie.title" = "Ejecutar Spectacle al iniciar"; 61 | 62 | /* Class = "NSTextFieldCell"; title = "Lower Left:"; ObjectID = "dQP-TR-GMx"; */ 63 | "dQP-TR-GMx.title" = "Abajo a la izquierda:"; 64 | 65 | /* Class = "NSTextFieldCell"; title = "Shift"; ObjectID = "dww-8Q-sa1"; */ 66 | "dww-8Q-sa1.title" = "Shift"; 67 | 68 | /* Class = "NSTextFieldCell"; title = "Run..."; ObjectID = "eYU-TH-wcO"; */ 69 | "eYU-TH-wcO.title" = "Ejecutar..."; 70 | 71 | /* Class = "NSTextFieldCell"; title = "Upper Right:"; ObjectID = "gSY-Au-xOH"; */ 72 | "gSY-Au-xOH.title" = "Superior derecha:"; 73 | 74 | /* Class = "NSTextFieldCell"; title = "Previous Display:"; ObjectID = "hmL-EC-pro"; */ 75 | "hmL-EC-pro.title" = "Monitor anterior:"; 76 | 77 | /* Class = "NSMenuItem"; title = "in the status menu"; ObjectID = "in4-v8-tIm"; */ 78 | "in4-v8-tIm.title" = "en menu de estado"; 79 | 80 | /* Class = "NSTextFieldCell"; title = "Command"; ObjectID = "jdf-Yu-zrq"; */ 81 | "jdf-Yu-zrq.title" = "Command"; 82 | 83 | /* Class = "NSTextFieldCell"; title = "Upper Left:"; ObjectID = "ldH-1E-mKW"; */ 84 | "ldH-1E-mKW.title" = "Superior izquierdo:"; 85 | 86 | /* Class = "NSTextFieldCell"; title = "Next Display:"; ObjectID = "lw7-Ap-kJO"; */ 87 | "lw7-Ap-kJO.title" = "Monitor siguiente:"; 88 | 89 | /* Class = "NSTextFieldCell"; title = "Center:"; ObjectID = "oqf-7z-BFa"; */ 90 | "oqf-7z-BFa.title" = "Centro:"; 91 | 92 | /* Class = "NSTextFieldCell"; title = "Previous Third:"; ObjectID = "s3E-Lp-mJ4"; */ 93 | "s3E-Lp-mJ4.title" = "Tercer anterior:"; 94 | 95 | /* Class = "NSTextFieldCell"; title = "Fullscreen:"; ObjectID = "tKr-XE-DEU"; */ 96 | "tKr-XE-DEU.title" = "Pantalla completa:"; 97 | 98 | /* Class = "NSBox"; title = "Box"; ObjectID = "vWw-ws-BgW"; */ 99 | "vWw-ws-BgW.title" = "Caja"; 100 | 101 | /* Class = "NSTextFieldCell"; title = "Redo:"; ObjectID = "y7M-cR-DF2"; */ 102 | "y7M-cR-DF2.title" = "Rehacer:"; 103 | -------------------------------------------------------------------------------- /Spectacle.xcodeproj/xcshareddata/xcschemes/Spectacle.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutValidation.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleShortcutValidation.h" 2 | 3 | #import 4 | 5 | #import "SpectacleShortcut.h" 6 | #import "SpectacleShortcutTranslations.h" 7 | #import "SpectacleShortcutValidator.h" 8 | 9 | @implementation SpectacleShortcutValidation 10 | { 11 | SpectacleShortcutManager *_shortcutManager; 12 | NSArray> *_validators; 13 | } 14 | 15 | - (instancetype)initWithShortcutManager:(SpectacleShortcutManager *)shortcutManager 16 | validators:(NSArray> *)validators 17 | { 18 | if (self = [super init]) { 19 | _shortcutManager = shortcutManager; 20 | _validators = validators; 21 | } 22 | return self; 23 | } 24 | 25 | - (BOOL)isShortcutValid:(SpectacleShortcut *)shortcut error:(NSError **)error 26 | { 27 | CFArrayRef shortcuts = NULL; 28 | if (CopySymbolicHotKeys(&shortcuts)) { 29 | return YES; 30 | } 31 | for (CFIndex i = 0; i < CFArrayGetCount(shortcuts); i++) { 32 | CFDictionaryRef shortcutDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(shortcuts, i); 33 | if (!shortcutDictionary || (CFGetTypeID(shortcutDictionary) != CFDictionaryGetTypeID())) { 34 | continue; 35 | } 36 | if (kCFBooleanTrue != (CFBooleanRef)CFDictionaryGetValue(shortcutDictionary, kHISymbolicHotKeyEnabled)) { 37 | continue; 38 | } 39 | NSInteger keyCode = keyCodeFromDictionary(shortcutDictionary); 40 | NSUInteger modifiers = modifiersFromDictionary(shortcutDictionary); 41 | if (([shortcut shortcutKeyCode] == keyCode) && [shortcut containsModifiers:modifiers]) { 42 | if (error) { 43 | NSString *description = NSLocalizedString(@"AlertMessageTextShortcutValidationError", @"The message text of the alert displayed when a shortcut is invalid"); 44 | NSString *recoverySuggestion = NSLocalizedString(@"AlertInformativeTextSystemWideShortcutAlreadyUsed", @"The informative text of the alert displayed when a system-wide shortcut is already in use"); 45 | *error = [SpectacleShortcutValidation errorWithShortcut:shortcut 46 | description:description 47 | recoverySuggestion:recoverySuggestion]; 48 | } 49 | return NO; 50 | } 51 | } 52 | for (id validator in _validators) { 53 | if ([validator conformsToProtocol:@protocol(SpectacleShortcutValidator)] 54 | && ![validator isShortcutValid:shortcut shortcutManager:_shortcutManager]) { 55 | if (error) { 56 | NSString *description = NSLocalizedString(@"AlertMessageTextShortcutValidationError", @"The message text of the alert displayed when a shortcut is invalid"); 57 | NSString *recoverySuggestion = NSLocalizedString(@"AlertInformativeTextShortcutAlreadyInUse", @"The informative text of the alert displayed when a shortcut is already in use"); 58 | *error = [SpectacleShortcutValidation errorWithShortcut:shortcut 59 | description:description 60 | recoverySuggestion:recoverySuggestion]; 61 | } 62 | return NO; 63 | } 64 | } 65 | return YES; 66 | } 67 | 68 | + (NSError *)errorWithShortcut:(SpectacleShortcut *)shortcut 69 | description:(NSString *)description 70 | recoverySuggestion:(NSString *)recoverySuggestion 71 | { 72 | NSString *shortcutString = SpectacleTranslateShortcut(shortcut); 73 | return [NSError errorWithDomain:NSCocoaErrorDomain 74 | code:0 75 | userInfo:@{ 76 | NSLocalizedDescriptionKey: [NSString stringWithFormat:description, shortcutString], 77 | NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:recoverySuggestion, shortcutString], 78 | NSLocalizedRecoveryOptionsErrorKey: @[NSLocalizedString(@"ButtonLabelAffirmative", @"The button label used in the affirmative")] 79 | }]; 80 | } 81 | 82 | static NSInteger keyCodeFromDictionary(CFDictionaryRef dictionary) 83 | { 84 | CFNumberRef keyCodeFromDictionary = (CFNumberRef)CFDictionaryGetValue(dictionary, kHISymbolicHotKeyCode); 85 | NSInteger keyCode = 0; 86 | CFNumberGetValue(keyCodeFromDictionary, kCFNumberLongType, &keyCode); 87 | return keyCode; 88 | } 89 | 90 | static NSUInteger modifiersFromDictionary(CFDictionaryRef dictionary) 91 | { 92 | CFNumberRef modifiersFromDictionary = (CFNumberRef)CFDictionaryGetValue(dictionary, kHISymbolicHotKeyModifiers); 93 | NSUInteger modifiers = 0; 94 | CFNumberGetValue(modifiersFromDictionary, kCFNumberLongType, &modifiers); 95 | return modifiers; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /Spectacle/Sources/SpectacleShortcutJSONStorage.m: -------------------------------------------------------------------------------- 1 | #import "SpectacleShortcutJSONStorage.h" 2 | 3 | @implementation SpectacleShortcutJSONStorage 4 | 5 | - (NSArray *)loadShortcutsWithAction:(SpectacleShortcutAction)action 6 | { 7 | NSError *error; 8 | NSURL *shortcutsFileURL = findShortcutsFileURL(&error); 9 | if (!shortcutsFileURL) { 10 | NSLog(@"Unable to locate the shortcuts file: %@", error.localizedDescription); 11 | return nil; 12 | } 13 | NSData *content = [NSData dataWithContentsOfURL:shortcutsFileURL]; 14 | NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:content 15 | options:0 16 | error:&error]; 17 | if (!jsonArray) { 18 | NSLog(@"Deserializing shortcuts failed: %@", error.localizedDescription); 19 | return nil; 20 | } 21 | return shortcutsFromJsonObject(jsonArray, action); 22 | } 23 | 24 | - (void)storeShortcuts:(NSArray *)shortcuts 25 | { 26 | NSError *error; 27 | NSURL *shortcutsFileURL = findShortcutsFileURL(&error); 28 | if (!shortcutsFileURL) { 29 | NSLog(@"Unable to locate the shortcuts file: %@", error.localizedDescription); 30 | return; 31 | } 32 | NSData *contents = [NSJSONSerialization dataWithJSONObject:jsonObjectFromShortcuts(shortcuts) 33 | options:NSJSONWritingPrettyPrinted 34 | error:&error]; 35 | if (![contents writeToURL:shortcutsFileURL atomically:YES]) { 36 | NSLog(@"Unable to store shortcuts at location: %@", [shortcutsFileURL path]); 37 | } 38 | } 39 | 40 | - (BOOL)isMigrationNeeded 41 | { 42 | NSError *error; 43 | NSURL *shortcutsFileURL = findShortcutsFileURL(&error); 44 | if (!shortcutsFileURL) { 45 | NSLog(@"Unable to locate the shortcuts file: %@", error.localizedDescription); 46 | return YES; 47 | } 48 | return ![[NSFileManager defaultManager] fileExistsAtPath:[shortcutsFileURL path]]; 49 | } 50 | 51 | static NSURL *findShortcutsFileURL(NSError **error) 52 | { 53 | NSURL *shortcutsFileURL = [findOrCreateSpectacleDirectory(error) URLByAppendingPathComponent:@"Shortcuts.json"]; 54 | return shortcutsFileURL.URLByResolvingSymlinksInPath; 55 | } 56 | 57 | static NSURL *findOrCreateSpectacleDirectory(NSError **error) 58 | { 59 | NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory 60 | inDomain:NSUserDomainMask 61 | appropriateForURL:nil 62 | create:YES 63 | error:error]; 64 | if (!applicationSupportDirectory) { 65 | return nil; 66 | } 67 | NSURL *spectacleDirectory = [applicationSupportDirectory URLByAppendingPathComponent:@"Spectacle"]; 68 | BOOL success = [[NSFileManager defaultManager] createDirectoryAtURL:spectacleDirectory 69 | withIntermediateDirectories:YES 70 | attributes:nil 71 | error:error]; 72 | if (!success) { 73 | return nil; 74 | } 75 | return spectacleDirectory; 76 | } 77 | 78 | static NSArray *jsonObjectFromShortcuts(NSArray *shortcuts) 79 | { 80 | NSMutableArray *jsonArray = [NSMutableArray new]; 81 | for (SpectacleShortcut *shortcut in shortcuts) { 82 | [jsonArray addObject:@{ 83 | @"shortcut_name" : shortcut.shortcutName, 84 | @"shortcut_key_binding" : shortcut.shortcutKeyBinding ?: [NSNull null], 85 | }]; 86 | } 87 | return jsonArray; 88 | } 89 | 90 | static NSArray *shortcutsFromJsonObject(NSArray *jsonArray, SpectacleShortcutAction action) 91 | { 92 | NSMutableArray *shortcuts = [NSMutableArray new]; 93 | for (NSDictionary *jsonObject in jsonArray) { 94 | NSString *shortcutName = jsonObject[@"shortcut_name"]; 95 | NSString *shortcutKeyBinding = jsonObject[@"shortcut_key_binding"]; 96 | [shortcuts addObject:[[SpectacleShortcut alloc] initWithShortcutName:shortcutName 97 | shortcutKeyBinding:[shortcutKeyBinding isKindOfClass:[NSNull class]] ? nil : shortcutKeyBinding 98 | shortcutAction:action]]; 99 | } 100 | return shortcuts; 101 | } 102 | 103 | @end 104 | --------------------------------------------------------------------------------