├── Assets
├── btn_release.svg
├── btn_sketchpacks.svg
├── btn_support.svg
└── img_cover.png
├── LICENSE
├── README.md
└── Source
├── Assets
├── Assets.xcassets
│ ├── Contents.json
│ ├── SketchAPITestFile.dataset
│ │ ├── Contents.json
│ │ └── SketchAPITestFile.sketch
│ ├── disclosure-horizontal.imageset
│ │ ├── Contents.json
│ │ └── disclosure-horizontal.pdf
│ ├── disclosure-vertical.imageset
│ │ ├── Contents.json
│ │ └── disclosure-vertical.pdf
│ ├── prokeys-bg.imageset
│ │ ├── Contents.json
│ │ └── Group 2.pdf
│ └── shortcut-menu.imageset
│ │ ├── Contents.json
│ │ └── shortcut-menu.pdf
├── KeysIcon.icns
└── SketchAPITestFile.sketch
├── Controller
├── ActionsMenu.swift
├── DonationButton.swift
├── Licensing Window
│ ├── LicensingBGImageView.swift
│ ├── LicensingInfoTextFieldCell.swift
│ ├── LicensingWindowController.swift
│ ├── LicensingWindowController.xib
│ └── UI
│ │ └── LicensingWindowController.swift
├── Outline View
│ ├── Cell
│ │ ├── ItemCellContextualMenu.swift
│ │ ├── ItemTableCellView.swift
│ │ └── Shortcut View
│ │ │ ├── MASShortcut+Additions.swift
│ │ │ ├── ShortcutButtonCell.swift
│ │ │ ├── ShortcutErrorAlert.swift
│ │ │ ├── ShortcutErrorHandler.swift
│ │ │ ├── ShortcutValidator.swift
│ │ │ └── ShortcutView.swift
│ ├── OutlineView.swift
│ ├── OutlineViewDataSource.swift
│ └── OutlineViewDelegate.swift
├── SearchField.swift
├── ViewController.swift
└── ViewController.xib
├── Info.plist
├── Keys.swift
├── KeysForSketch.h
├── KeysForSketch.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── exevil.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── exevil.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── KeysForSketch.xcscheme
│ └── xcschememanagement.plist
├── KeysForSketch.xcworkspace
├── contents.xcworkspacedata
├── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
└── xcuserdata
│ └── exevil.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ ├── WorkspaceSettings.xcsettings
│ └── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
├── KeysForSketch
├── Assets
│ └── Assets.xcassets
│ │ ├── disclosure-horizontal.imageset
│ │ ├── Contents.json
│ │ └── disclosure-horizontal.pdf
│ │ └── disclosure-vertical.imageset
│ │ ├── Contents.json
│ │ └── disclosure-vertical.pdf
├── KeysForSketch.bundle
│ └── Contents
│ │ ├── Frameworks
│ │ ├── MASShortcut.framework
│ │ │ ├── MASShortcut
│ │ │ ├── Resources
│ │ │ │ └── Info.plist
│ │ │ └── Versions
│ │ │ │ └── A
│ │ │ │ └── MASShortcut
│ │ ├── libswiftCoreFoundation.dylib
│ │ ├── libswiftCoreGraphics.dylib
│ │ ├── libswiftCoreImage.dylib
│ │ ├── libswiftFoundation.dylib
│ │ ├── libswiftObjectiveC.dylib
│ │ ├── libswiftQuartzCore.dylib
│ │ └── libswiftSwiftOnoneSupport.dylib
│ │ └── Resources
│ │ └── libswiftRemoteMirror.dylib
├── KeysForSketch.xcodeproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── KeysForSketch.xcworkspace
│ ├── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
│ └── xcuserdata
│ │ └── exevil.xcuserdatad
│ │ └── WorkspaceSettings.xcsettings
├── Pods
│ ├── Pods.xcodeproj
│ │ └── xcuserdata
│ │ │ └── exevil.xcuserdatad
│ │ │ └── xcschemes
│ │ │ ├── MASShortcut.xcscheme
│ │ │ ├── Pods-KeysForSketch.xcscheme
│ │ │ └── xcschememanagement.plist
│ └── Target Support Files
│ │ ├── MASShortcut
│ │ └── ResourceBundle-MASShortcut-Info.plist
│ │ ├── Pods-KeysForSketch
│ │ ├── Pods-KeysForSketch-acknowledgements.plist
│ │ ├── Pods-KeysForSketch-dummy.m
│ │ ├── Pods-KeysForSketch-frameworks.sh
│ │ ├── Pods-KeysForSketch-resources.sh
│ │ ├── Pods-KeysForSketch-umbrella.h
│ │ ├── Pods-KeysForSketch.debug.xcconfig
│ │ ├── Pods-KeysForSketch.modulemap
│ │ └── Pods-KeysForSketch.release.xcconfig
│ │ └── Pods-KeysForSketchUnitTests
│ │ └── Info.plist
├── Sketch API
│ └── Protocols
│ │ ├── VDKSketchMSPluginManagerWithActionsProtocol.h
│ │ └── VDKSketchMSPreferencesControllerProtocol.h
└── html
│ ├── Pods_Target Support Files_MASShortcut_MASShortcut-dummy.m.html
│ ├── Pods_Target Support Files_Pods-KeysForSketch_Pods-KeysForSketch-dummy.m.html
│ ├── Sketch API_Protocols_Updater_VDKSketchSUAppcastItemProtocol.h.html
│ ├── Sketch API_Protocols_Updater_VDKSketchSUHostProtocol.h.html
│ ├── Sketch API_Protocols_Updater_VDKSketchSUUpdaterDelegate.h.html
│ ├── Sketch API_Protocols_Updater_VDKSketchSUUpdaterProtocol.h.html
│ ├── Sketch API_Protocols_VDKSketchMSDocumentControllerProtocol.h.html
│ ├── Sketch API_Protocols_VDKSketchMSPluginBundleProtocol.h.html
│ ├── Sketch API_Protocols_VDKSketchMSPluginManagerWithActionsProtocol.h.html
│ └── Sketch API_Protocols_VDKSketchMSPreferencesControllerProtocol.h.html
├── KeysForSketchTests
├── Info.plist
└── KeysForSketchTests.swift
├── Menu
├── MenuObserver.swift
├── NSMenu+Additions.swift
├── NSMenuItem+Additions.swift
├── NSMenuItem+Private.h
└── ToolsManager.swift
├── Plugin
├── manifest.json
└── script.js
├── ProKeys
├── Keys
│ ├── ProKeyAspectRatioSwitcher.swift
│ └── ProKeyProtocol.swift
└── ProKeysManager.swift
├── Sketch API
├── Protocols
│ ├── Document
│ │ ├── Inspector
│ │ │ ├── VDKSketchMSInspectorControllerProtocol.h
│ │ │ ├── VDKSketchMSLayerInspectorViewControllerProtocol.h
│ │ │ ├── VDKSketchMSNormalInspectorProtocol.h
│ │ │ └── VDKSketchMSStandardInspectorViewControllersProtocol.h
│ │ ├── VDKSketchMSActionControllerProtocol.h
│ │ ├── VDKSketchMSDocumentControllerProtocol.h
│ │ ├── VDKSketchMSDocumentDataProtocol.h
│ │ └── VDKSketchMSDocumentProtocol.h
│ ├── Layers
│ │ ├── VDKSketchMSArtboardGroupProtocol.h
│ │ ├── VDKSketchMSLayerArrayProtocol.h
│ │ ├── VDKSketchMSLayerGroupProtocol.h
│ │ ├── VDKSketchMSLayerProtocol.h
│ │ └── VDKSketchMSPageProtocol.h
│ ├── VDKSketchAppControllerProtocol.h
│ ├── VDKSketchMSKeyBindingsProtocol.h
│ └── VDKSketchMSPreferencesControllerProtocol.h
├── SketchAPIIntegrationTest.swift
├── SketchAPIProtocolsTest.swift
├── SketchAPITools.swift
├── VDKSketchAPI.h
└── VDKSketchAPI.m
├── Support
├── Const.swift
├── FileWatch.swift
├── Helpers.swift
├── JRSwizzle
│ ├── VDK_JRSwizzle.h
│ └── VDK_JRSwizzle.m
├── KeyCodeTransformer.swift
├── Lorgnette
│ ├── lorgnette-structs.h
│ ├── lorgnette.c
│ └── lorgnette.h
├── MASShortcut.framework
│ ├── Headers
│ ├── MASShortcut
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── Headers
│ │ │ ├── MASDictionaryTransformer.h
│ │ │ ├── MASHotKey.h
│ │ │ ├── MASKeyCodes.h
│ │ │ ├── MASKeyMasks.h
│ │ │ ├── MASLocalization.h
│ │ │ ├── MASShortcut-umbrella.h
│ │ │ ├── MASShortcut.h
│ │ │ ├── MASShortcutBinder.h
│ │ │ ├── MASShortcutMonitor.h
│ │ │ ├── MASShortcutValidator.h
│ │ │ ├── MASShortcutView+Bindings.h
│ │ │ ├── MASShortcutView.h
│ │ │ └── Shortcut.h
│ │ ├── MASShortcut
│ │ ├── Modules
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ ├── Info.plist
│ │ │ └── MASShortcut.bundle
│ │ │ └── Contents
│ │ │ ├── Info.plist
│ │ │ ├── Resources
│ │ │ ├── cs.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── de.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── en.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── es.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── fr.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── it.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── ja.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── ko.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── nl.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── pl.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── ru.lproj
│ │ │ │ └── Localizable.strings
│ │ │ ├── zh-Hans.lproj
│ │ │ │ └── Localizable.strings
│ │ │ └── zh-Hant.lproj
│ │ │ │ └── Localizable.strings
│ │ │ └── _CodeSignature
│ │ │ ├── CodeDirectory
│ │ │ ├── CodeRequirements
│ │ │ ├── CodeRequirements-1
│ │ │ ├── CodeResources
│ │ │ └── CodeSignature
│ │ └── Current
├── NSAlert+Additions.swift
└── UIInjector.swift
├── Update
├── FileObserver.swift
└── UpdateCompletionAlert.swift
└── VDKBridgingHeader.h
/Assets/img_cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Assets/img_cover.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Vyacheslav Dubovitsky
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Attention! Plugin Support Has Been Discontinued
2 | Since I don't have much time for further plugin developement and new Sketch releases produce bugs pretty often I decided to discontinue the project support. Sorry.
3 |
4 | Update: All sources are now available in this repo. Enjoy!
5 |
6 | ---
7 |
8 | 
9 |
10 |
11 |
12 |
13 | # Keys For Sketch
14 |
15 |
16 | Keys is a full-featured shortcut manager for Sketch. Integrated directly to Preferences window it helps you to customize shortcuts easily.
17 |
18 |
19 |
20 | 1. [Installation](#installation)
21 | 1. [Features](#features)
22 | 1. [Usage](#usage)
23 | 1. [FAQ](#faq)
24 | 1. [Support Project](#support-project)
25 |
26 | ## Installation
27 | Keys installation is way the same as any other plugins. Just download latest release and double-click it.
28 |
29 | [
](https://github.com/exevil/Keys-For-Sketch/releases/latest) [
](https://sketchpacks.com/exevil/Keys-For-Sketch/install)
30 |
31 | ## Features
32 | * Easily menu shortcuts customization
33 | * Single-character shortcuts customization (like Pencil or Vector)
34 | * Shortcut conflicts resolving
35 | * Any third-party plugins support
36 | * Preferences window integration
37 | * Intuitive lightning-fast UI
38 | * Instant menu search
39 | * Modern Sketch v45 plugin updating system support
40 |
41 | ## Usage
42 | 1. Open Sketch Preferences (⌘+,)
43 | 1. Select `Keys` tab
44 | 1. Choose menu item for shortcut redefinition
45 | 1. Click on its shortcut or on `...` on the right side
46 | 1. Press new short
47 | 1. Resolve conflict if needed
48 | 1. Enjoy
49 |
50 | ## FAQ
51 | #### — There is a Keys-related bug in latest beta version of Sketch. What should I do?
52 | The only you can do is check [the issues list](https://github.com/exevil/Keys-For-Sketch/issues) and report your issue if no-one did it before. Right now Keys supports only the latest stable Sketch version, so you should expect an update right after its release.
53 |
54 | #### — Where can I see the changelog?
55 | On the [Releases](https://github.com/exevil/Keys-For-Sketch/releases) page.
56 |
57 | #### — How to restore default Sketch shortcuts?
58 | Use «Restore Default Shortcuts...» command from plugin menu. *It should remove any user shortcut data includinge one that defined directly in System Preferences.*
59 |
60 | #### — How Keys will affect custom shortcuts I defined earlier in System Preferences?
61 | Since Keys is using default system storage for shortcuts it shouldn't affect previously set shortcuts without additional user actions like «Restore Default Shortcuts...» command from plugin menu.
62 |
63 | #### — Can't see my Keys shortcuts in System Preferences. Is it something wrong?
64 | Since System Preferences caches shortcut values from storage once upon a startup you need to completely relaunch it to get updated shortcut data there.
65 |
66 | #### — What happens with my custom shortcuts if I remove Keys?
67 | Nothing, because all your shortcuts were defined in System Preferences.
68 |
69 | #### — I removed Keys and my custom shortcuts manually from System Preferences, but tools with single-character shortcuts (like Pencil or Vector) are still using previously defined values. How to reset it to defaults too?
70 | Since Sketch manages single character shortcuts by itself, you should delete `keyBindings.plist` from `~/Library/Application Support/com.bohemiancoding.sketch3/` folder and restart Sketch to return default tools shortcuts.
71 |
72 | #### — Some menu items aren't shown in Keys. How to fix?
73 | Internally Sketch may not properly draw some menu items prior to their first appearance in menu, that's why the only way to make these kind of menu items configurable with Keys is to manually access it through the app menu first.
74 |
75 | #### — Keys conflicts with other plugins that using earlier Swift versions. Is there any way to fix it?
76 | Unfortunately, there isn't. In simple terms, there's no capability between different Swift versions right now so it may produce an errors at runtime.
77 |
78 | But it's not really a big deal. Firstly, the Swift team has already announced that ABI capability feature is planned for the next major Swift release and this is the first priority right now. Secondly, plugins can't conflict each other while the latest available Swift version is used for all of them and requesting author for an update may be a good temporary solution for this problem.
79 |
80 | #### — Why I can't remove default shortcut from a menu item?
81 | Since Keys plugin is wrapping a default System Preferences shortcut assignment flow it follows the same rules. So any default shortcut are constant until you either replace it with your own or use the same combination for another menu item.
82 |
83 | #### — Why it's not possible to assign a single-character shortcut to menu item other than specific items under the insert menu?
84 | Since Sketch itself is using an internal predefined list of menu items that allowed a single-character shortcut to be set, Keys can customize single-character shortcuts only for these items.
85 |
86 | #### — My issue isn't listed here. What now?
87 | Please check the [open issues list](https://github.com/exevil/Keys-For-Sketch/issues) and feel free to create a new one if you don't see your problem there.
88 |
89 | ## Support Project
90 | I spent a lot of time to make this project real so any feedback or donation is really matters. It's wonderful to see that things you do are really making people's lives easier.
91 |
92 | [
](https://www.paypal.me/exevil/10)
93 |
94 | ###### Please note that if you're sending your donation from Russian Federation only Russian Roubles will be accepted.
95 |
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/SketchAPITestFile.dataset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "data" : [
7 | {
8 | "idiom" : "universal",
9 | "filename" : "SketchAPITestFile.sketch"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/SketchAPITestFile.dataset/SketchAPITestFile.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/Assets.xcassets/SketchAPITestFile.dataset/SketchAPITestFile.sketch
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/disclosure-horizontal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "filename" : "disclosure-horizontal.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/disclosure-horizontal.imageset/disclosure-horizontal.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/Assets.xcassets/disclosure-horizontal.imageset/disclosure-horizontal.pdf
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/disclosure-vertical.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "filename" : "disclosure-vertical.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/disclosure-vertical.imageset/disclosure-vertical.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/Assets.xcassets/disclosure-vertical.imageset/disclosure-vertical.pdf
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/prokeys-bg.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group 2.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/prokeys-bg.imageset/Group 2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/Assets.xcassets/prokeys-bg.imageset/Group 2.pdf
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/shortcut-menu.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "shortcut-menu.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Source/Assets/Assets.xcassets/shortcut-menu.imageset/shortcut-menu.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/Assets.xcassets/shortcut-menu.imageset/shortcut-menu.pdf
--------------------------------------------------------------------------------
/Source/Assets/KeysIcon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/KeysIcon.icns
--------------------------------------------------------------------------------
/Source/Assets/SketchAPITestFile.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Assets/SketchAPITestFile.sketch
--------------------------------------------------------------------------------
/Source/Controller/ActionsMenu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActionsMenu.swift
3 | //
4 | //
5 | // Created by Vyacheslav Dubovitsky on 21/03/2017.
6 | //
7 | //
8 |
9 | class ActionsMenu: NSMenu {
10 |
11 | /// Show warning alert and if its passed reset all shortcuts to default ones.
12 | @IBAction func resetButtonAction(_ sender: Any) {
13 |
14 | /// Action for "Yes" button.
15 | let yesAction: () -> Void = {
16 | // Remove all user shortcut data from System Preferences
17 | CFPreferencesSetAppValue(Const.Preferences.kUserKeyEquivalents, nil, kCFPreferencesCurrentApplication)
18 | CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
19 | // Clean local shortcut cache
20 | appKeyEquivalents.pointee = nil
21 |
22 | // Recursively update all Menu Items in main menu to apply default key equivalent values.
23 | Const.Menu.main.iterateAndExecute { $0._recacheUserKeyEquivalentOnlyIfStale(false) }
24 | // Reload Outline View if needed.
25 | ViewController.initialized?.outlineView.reloadData()
26 | }
27 |
28 | // Show confirmation alert
29 |
30 | let alertCompletionHandler = { (pressedButtonNumber: Int) in
31 | switch pressedButtonNumber {
32 | case 0:
33 | yesAction()
34 | default:
35 | break
36 | }
37 | }
38 | NSAlert.showAlert(over: ViewController.initialized?.view.window,
39 | messageText: "Reset All Shortcuts?",
40 | informativeText: "All Sketch user shortcuts including ones that defined manually in System Preferences will be immediately erased and default values will be restored.",
41 | orderedButtonTitles: ["Yes", "No"],
42 | completionHandler: alertCompletionHandler)
43 | }
44 |
45 | // ---
46 |
47 | @IBAction func proRegistrationInfoButtonAction(_ sender: Any) {
48 | LicensingWindowController.shared.showWindow(self)
49 | }
50 |
51 | // ---
52 |
53 | @IBAction func reportIssueButtonAction(_ sender: Any) {
54 | NSWorkspace.shared.open(URL(string: "https://github.com/exevil/Keys-For-Sketch/issues")!)
55 | }
56 |
57 | @IBAction func helpButtonAction(_ sender: Any) {
58 | NSWorkspace.shared.open(URL(string: "https://github.com/exevil/Keys-For-Sketch/blob/master/README.md#faq")!)
59 | }
60 |
61 | @IBAction func supportButtonAction(_ sender: Any) {
62 | NSWorkspace.shared.open(URL(string: "https://www.paypal.me/exevil/10d")!)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Source/Controller/DonationButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DonationButton.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class DonationButton: NSButton {
10 |
11 | override func awakeFromNib() {
12 | // Button Styling
13 | let paragraphStyle: NSParagraphStyle = {
14 | let style = NSMutableParagraphStyle()
15 | style.alignment = alignment
16 | return style
17 | }()
18 | var basicAttributes: [NSAttributedStringKey : Any] = [
19 | NSAttributedStringKey.font : font!,
20 | NSAttributedStringKey.paragraphStyle : paragraphStyle
21 | ]
22 |
23 | attributedTitle = NSAttributedString(string: title, attributes:{
24 | basicAttributes[.foregroundColor] = NSColor(calibratedRed: 74/255, green: 144/255, blue: 226/255, alpha: 1)
25 | return basicAttributes
26 | }())
27 |
28 | attributedAlternateTitle = NSAttributedString(string: title, attributes:{
29 | basicAttributes[.foregroundColor] = NSColor(calibratedRed: 74/255, green: 144/255, blue: 226/255, alpha: 0.5)
30 | return basicAttributes
31 | }())
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Source/Controller/Licensing Window/LicensingBGImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LicensingBGImageView.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 28/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class LicensingBGImageView: NSImageView {
12 |
13 | // Pass mouse events to superview to allow moving window by dragging it anywhere
14 | override var mouseDownCanMoveWindow: Bool {
15 | return true
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Source/Controller/Licensing Window/LicensingInfoTextFieldCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LicensingInfoTextFieldCell.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 27/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class LicensingInfoTextFieldCell: NSTextFieldCell {
12 |
13 | override func awakeFromNib() {
14 | bezelStyle = .roundedBezel
15 | focusRingType = .none
16 | }
17 |
18 | // Center text vert
19 | override func drawingRect(forBounds rect: NSRect) -> NSRect {
20 | let horizontalInset: CGFloat = -6 // For better appearance
21 | let textHeight: CGFloat = 24 // Default text height.
22 | let newRect = NSRect(x: horizontalInset,
23 | y: (rect.size.height - textHeight) / 2,
24 | width: rect.size.width - horizontalInset,
25 | height: textHeight)
26 | return super.drawingRect(forBounds: newRect)
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Source/Controller/Licensing Window/LicensingWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LicensingWindowController.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 23/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class LicensingWindowController: NSWindowController {
12 |
13 | // MARK: Properties
14 |
15 | static let shared = LicensingWindowController(windowNibName: NSNib.Name(rawValue: "LicensingWindowController"
16 | ))
17 |
18 | @IBOutlet weak var emailTextField: NSTextField!
19 | @IBOutlet weak var serialTextField: NSTextField!
20 | @IBOutlet weak var activateButton: NSButton!
21 |
22 | // MARK: Loading
23 |
24 | override func windowDidLoad() {
25 | super.windowDidLoad()
26 |
27 | // Initial setup
28 | window?.backgroundColor = NSColor(calibratedHue: 0, saturation: 0, brightness: 98.0/100.0, alpha: 1)
29 | window?.styleMask.remove(.resizable)
30 | window?.isMovableByWindowBackground = true
31 |
32 | // Hide title bar
33 | window?.titleVisibility = .hidden
34 | window?.titlebarAppearsTransparent = true
35 | window?.styleMask.insert(.fullSizeContentView)
36 |
37 | // Hide excessive buttons
38 | window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
39 | window?.standardWindowButton(.zoomButton)?.isHidden = true
40 | }
41 |
42 | // Actions
43 | }
44 |
--------------------------------------------------------------------------------
/Source/Controller/Licensing Window/UI/LicensingWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LicensingWindowController.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 23/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class LicensingWindowController: NSWindowController {
12 |
13 | // MARK: Properties
14 |
15 | static let shared = LicensingWindowController(windowNibName: NSNib.Name(rawValue: "LicensingWindowController"
16 | ))
17 |
18 | @IBOutlet weak var emailTextField: NSTextField!
19 | @IBOutlet weak var serialTextField: NSTextField!
20 | @IBOutlet weak var activateButton: NSButton!
21 | @IBOutlet weak var bgImageView: NSImageView!
22 |
23 | // MARK: Loading
24 |
25 | override func windowDidLoad() {
26 | super.windowDidLoad()
27 |
28 | // Initial setup
29 | window?.backgroundColor = NSColor(calibratedHue: 0, saturation: 0, brightness: 98.0/100.0, alpha: 1)
30 | window?.styleMask.remove(.resizable)
31 | window?.isMovableByWindowBackground = true
32 | bgImageView.mouseDownCanMoveWindow = true
33 |
34 | // Hide title bar
35 | window?.titleVisibility = .hidden
36 | window?.titlebarAppearsTransparent = true
37 | window?.styleMask.insert(.fullSizeContentView)
38 |
39 | // Hide excessive buttons
40 | window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
41 | window?.standardWindowButton(.zoomButton)?.isHidden = true
42 | }
43 |
44 | // Actions
45 | }
46 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/ItemCellContextualMenu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyItemContextMenu.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 20/05/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class ItemCellContextualMenu: NSMenu {
10 |
11 | // MARK: Init
12 |
13 | /// Associated KeyItem.
14 | let menuItem: NSMenuItem
15 |
16 | required init(menuItem: NSMenuItem) {
17 | self.menuItem = menuItem
18 | super.init(title: "")
19 | self.autoenablesItems = false
20 |
21 | addItem(withTitle: "Remove", action: #selector(restoreDefaultShortcut), keyEquivalent: "").target = self
22 | }
23 |
24 | // Since we can only init with keyItem, use fatalError on required initializer
25 | required init(coder decoder: NSCoder) {
26 | fatalError("ItemCellContextualMenu can only be initialized with init(keyItem:)")
27 | }
28 |
29 | // MARK: Actions
30 |
31 | /// Restore default shortcut for clicked item.
32 | @objc func restoreDefaultShortcut() {
33 | menuItem.shortcut = nil
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/ItemTableCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyItemTableCellView.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 12/02/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class ItemTableCellView: NSTableCellView {
10 |
11 | @IBOutlet weak var shortcutView: ShortcutView!
12 | @IBOutlet weak var titleLabel: NSTextField!
13 | @IBOutlet weak var menuButton: NSButton!
14 |
15 | // MARK: Init
16 |
17 | // Should be assigned during cell initialization process
18 | internal var menuItem: NSMenuItem! {
19 | didSet {
20 | shortcutView.menuItem = menuItem
21 | titleLabel.stringValue = menuItem.title
22 | // Hide shortcut view if now shortcut needed
23 | shortcutView.isHidden = !menuItem.needsShortcut
24 | // Hide menu button if there's no custom shortcut to manage
25 | menuButton.isHidden = !menuItem.hasCustomShortcut
26 | }
27 | }
28 |
29 | required init?(coder: NSCoder) {
30 | super.init(coder: coder)
31 |
32 | // Draw fake separator
33 | self.layer = {
34 | let layer = CALayer()
35 | let separatorLayer = CAShapeLayer()
36 | separatorLayer.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: Const.Cell.dividerHeight)
37 | separatorLayer.backgroundColor = NSColor(calibratedRed: 217/255, green: 217/255, blue: 217/255, alpha: 1).cgColor
38 | layer.addSublayer(separatorLayer)
39 | return layer
40 | }()
41 | }
42 |
43 | // MARK: Actions
44 |
45 | /// Popup context menu on button press.
46 | @IBAction func contextualMenuButtonAction(_ sender: NSButton) {
47 | let contextualMenu = ItemCellContextualMenu(menuItem: menuItem)
48 | contextualMenu.popUp(positioning: nil, at: NSPoint(x: sender.frame.minX, y: sender.frame.minY), in: self)
49 | }
50 |
51 | // MARK: Other
52 |
53 | /// Blink animation.
54 | func blink(with color: NSColor, forDuration duration: Float) {
55 | let transparentColor = NSColor(calibratedWhite: 1.0, alpha: 0.0).cgColor
56 | let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
57 | animation.values = [color.cgColor, transparentColor]
58 | animation.keyTimes = [0.1, 1.0]
59 | animation.timingFunctions = [
60 | CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut),
61 | CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
62 | ]
63 | animation.duration = CFTimeInterval(duration)
64 |
65 | layer?.add(animation, forKey: "animation")
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/Shortcut View/MASShortcut+Additions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MASShortcut+Additions.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/07/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | extension MASShortcut {
10 | /// A modifier flags string valid to store modifiers data in System Preferences.
11 | var modifierFlagsStringForSystemPreferences: String! {
12 | // Replace modifiers in modifierFlagsString to ones that suitable for System Preferences
13 | return String(modifierFlagsString.map {
14 | switch $0 {
15 | case "⌘":
16 | return "@"
17 | case "⌥":
18 | return "~"
19 | case "⌃":
20 | return "^"
21 | case "⇧":
22 | return "$"
23 | default:
24 | return "\0" // Null character
25 | }
26 | })
27 | }
28 |
29 | /// A key-code string used in key equivalent matching. This implementation should fix some problems that default `keyCodeStringForKeyEquivalent` has.
30 | var fixedKeyCodeStringForKeyEquivalent: String! {
31 | return KeyCodeTransformer.keyEquivalents(for: keyCode)?[0] ?? ""
32 | }
33 |
34 | /** Init with Key Equivalent string and modifier flags.
35 | — parameters:
36 | keyEquivalent: A string that represents character from ANSI keyboard.
37 | modifierFlags: Used modifier flags.
38 | */
39 | convenience init?(keyEquivalent: String, modifierFlags: NSEvent.ModifierFlags) {
40 | if let itemKeyCode = KeyCodeTransformer.keyCode(for: keyEquivalent) {
41 |
42 | var modifierFlagsWithShift: UInt = modifierFlags.rawValue
43 | // Since NSMenuItem can't capture Shift key as a Modifier Flag try to determine was Shift used during keyEquivalent setup and if so, add it to modifierFlags
44 | if itemKeyCode.wasShiftKeyUsed {
45 | modifierFlagsWithShift |= NSEvent.ModifierFlags.shift.rawValue
46 | }
47 |
48 | self.init(keyCode: itemKeyCode.value, modifierFlags: modifierFlagsWithShift)
49 | } else {
50 | return nil
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/Shortcut View/ShortcutButtonCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShortcutButtonCell.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class ShortcutButtonCell: NSButtonCell {
10 |
11 | override var alignment: NSTextAlignment {
12 | get {
13 | return super.alignment
14 | }
15 | set {
16 | switch newValue {
17 | case .center:
18 | // Make shortcut string aligned left since default implementation of Shortcut aligns it to center in ShortcutView
19 | super.alignment = .left
20 | case .right:
21 | // Disable display of default hinting buttons
22 | self.isEnabled = false
23 | default:
24 | super.alignment = newValue
25 | }
26 | }
27 | }
28 |
29 | override var title: String! {
30 | didSet {
31 | // Change "Record Shortcut" to set shortcut
32 | if title == "Record Shortcut" {
33 | attributedTitle = NSAttributedString(string: "…", attributes:[NSAttributedStringKey.foregroundColor : NSColor.lightGray])
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/Shortcut View/ShortcutErrorAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShortcutErrorAlert.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 05/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class ShortcutErrorAlert: NSAlert {
10 |
11 | var keysShortcutshortcut: MASShortcut?
12 |
13 | var setAnywayButton: NSButton?
14 | var toConflictButton: NSButton?
15 |
16 | convenience init(with shortcut: MASShortcut, explanation: String) {
17 | self.init()
18 | alertStyle = .critical
19 | messageText = "The key combination \(shortcut) cannot be used"
20 | informativeText = explanation
21 | // Add Cancel button and assign it Escape keyEquivalent
22 | addButton(withTitle: "Cancel").keyEquivalent = "\u{1b}"
23 |
24 | // If conflicted shortcut reserved by Menu Item not represented by KeyItem options below are not allowed
25 | if Const.Menu.main.findItem(by: shortcut) != nil {
26 | setAnywayButton = addButton(withTitle: "Set Anyway")
27 | toConflictButton = addButton(withTitle: "To Conflict")
28 | }
29 |
30 | window.initialFirstResponder = toConflictButton
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/Shortcut View/ShortcutErrorHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShortcutErrorHandler.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 25/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ShortcutErrorHandler: NSObject {
12 |
13 | static let shared = ShortcutErrorHandler()
14 |
15 | /// View Controller to display error alerts.
16 | /// - important: Explicitly unwrapped since you can produce an error alert only when controller is successfully initialized.
17 | let viewController: ViewController = ViewController.initialized!
18 |
19 | /// Currently presented Error Sheet.
20 | var presentedErrorSheet: NSAlert?
21 |
22 | /// Begin Error Sheet in viewController.
23 | func beginErrorSheet(with shortcut: MASShortcut, explanation: String, sender: ShortcutView) {
24 | endErrorSheetIfDisplayed()
25 |
26 | let errorAlert = ShortcutErrorAlert(with: shortcut, explanation: explanation)
27 | presentedErrorSheet = errorAlert
28 | errorAlert.beginSheetModal(for: viewController.view.window!, completionHandler: { (response) -> Void in
29 |
30 | // MARK: Cancel button
31 | if response == .alertFirstButtonReturn {
32 | errorAlert.window.sheetParent?.endSheet(errorAlert.window)
33 | }
34 |
35 | if let conflictKeyItem = Const.Menu.main.findItem(by: shortcut) {
36 | // MARK: Set Anyway button
37 | if response == .alertSecondButtonReturn {
38 | sender.menuItem.shortcut = shortcut
39 | conflictKeyItem.shortcut = nil
40 | }
41 |
42 | // MARK: To Conflict Button
43 | if response == .alertThirdButtonReturn {
44 | // If search is used, clear it to access a conflict item even if it's not currently shown in Outline View.
45 | if
46 | let dataSource = self.viewController.outlineView.dataSource as? OutlineViewDataSource,
47 | dataSource.searchPhrase != ""
48 | {
49 | dataSource.searchPhrase = ""
50 | self.viewController.outlineView.reloadData()
51 | }
52 |
53 | // Collect all parents hierarchy of conflicted item
54 | var items = [conflictKeyItem]
55 | if var parent = conflictKeyItem.parent {
56 | items.append(parent)
57 | while parent.parent != nil {
58 | let parentOfParent = parent.parent!
59 | items.append(parentOfParent)
60 | parent = parentOfParent
61 | }
62 | }
63 |
64 | // Reverse array to expand parents in right order
65 | items.reverse()
66 | // Expand items and blink a latest one
67 | for item in items {
68 | if item == items.last {
69 | let itemRow = self.viewController.outlineView.row(forItem: item)
70 | self.viewController.outlineView.scrollRowToVisible(itemRow)
71 | // Blink item cell
72 | if let itemCellView = self.viewController.outlineView.view(atColumn: 0, row: itemRow, makeIfNecessary: false) as? ItemTableCellView {
73 | itemCellView.blink(with: NSColor(calibratedRed: 250/255.0, green: 100/255.0, blue: 93/255.0, alpha: 0.4), forDuration: 0.75)
74 | }
75 | } else {
76 | self.viewController.outlineView.expandItem(item)
77 | }
78 | }
79 | }
80 | }
81 |
82 | // Anyway stop listening for shortcut updates when alert is closed
83 | sender.eventMonitoringIsActive = false
84 | })
85 | }
86 |
87 | /// End currently shown error sheet.
88 | func endErrorSheetIfDisplayed() {
89 | if let errorSheet = presentedErrorSheet {
90 | errorSheet.window.sheetParent?.endSheet(errorSheet.window)
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/Cell/Shortcut View/ShortcutValidator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShortcutValidator.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 02/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ShortcutValidator : MASShortcutValidator {
12 |
13 | static let sharedInstance = ShortcutValidator()
14 | // Return the same shared instance to override Obj-C superclass singleton method
15 | override static func shared() -> ShortcutValidator {
16 | return ShortcutValidator.sharedInstance
17 | }
18 |
19 | func isShortcutValid(_ shortcut: MASShortcut!, validWithoutModifiers: Bool) -> Bool {
20 | let keyCode = Int(shortcut.keyCode)
21 | let modifiers = shortcut.modifierFlags
22 |
23 | // Allow any function key with any combination of modifiers
24 | let includesFunctionKey: Bool = {
25 | let fKeyCodes = [kVK_F1, kVK_F2, kVK_F3, kVK_F4, kVK_F5, kVK_F6, kVK_F7, kVK_F8, kVK_F9, kVK_F10, kVK_F11, kVK_F12, kVK_F13, kVK_F14, kVK_F15, kVK_F16, kVK_F17, kVK_F18, kVK_F19, kVK_F20]
26 | for fKeyCode in fKeyCodes {
27 | if fKeyCode == keyCode { return true }
28 | }
29 | return false
30 | }()
31 | if includesFunctionKey { return true }
32 |
33 | // Check for modifiers
34 | let hasModifierFlags = modifiers > 0
35 | if !hasModifierFlags && validWithoutModifiers { return true }
36 |
37 | // Allow any hotkey containing Control or Command modifier
38 | let includesCmd = modifiers & NSEvent.ModifierFlags.command.rawValue > 0
39 | let includesCtrl = modifiers & NSEvent.ModifierFlags.control.rawValue > 0
40 | // Allow Option key only in selected cases
41 | let includesCorrectOption: Bool = {
42 | if modifiers & NSEvent.ModifierFlags.control.rawValue > 0 {
43 | // Always allow Option-Space and Option-Escape because they do not have any bind system commands
44 | if keyCode == kVK_Space || keyCode == kVK_Escape { return true }
45 |
46 | // Allow Option modifier with any key even if it will break the system binding
47 | if self.allowAnyShortcutWithOptionModifier { return true }
48 | }
49 | return false
50 | }()
51 | if includesCmd || includesCtrl || includesCorrectOption {
52 | return true
53 | }
54 |
55 | // The hotkey violates system bindings
56 | return false
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/OutlineView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineView.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 02/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class OutlineView: NSOutlineView {
10 |
11 | override func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView? {
12 | let view = super.makeView(withIdentifier: identifier, owner: owner)
13 |
14 | // Custom disclosure icons
15 | if identifier == NSOutlineView.disclosureButtonIdentifier {
16 | (view as! NSButton).image = image(Const.Image.disclosureVertiacal)
17 | (view as! NSButton).alternateImage = image(Const.Image.disclosureHorizontal)
18 | }
19 |
20 | return view
21 | }
22 |
23 | // Remove default cell separator
24 | override var intercellSpacing: NSSize {
25 | get {
26 | return NSSize(width: 0, height: 0)
27 | }
28 | set {}
29 | }
30 |
31 | // MARK: Double Action
32 |
33 | override var doubleAction: Selector? {
34 | get {
35 | return #selector(doubleActionHandler)
36 | }
37 | set {}
38 | }
39 |
40 | @objc func doubleActionHandler() {
41 | if let item = item(atRow: clickedRow), let view = view(atColumn: 0, row: clickedRow, makeIfNecessary: false) as? ItemTableCellView {
42 | let optionPressed = (NSApp.currentEvent!.modifierFlags.rawValue & NSEvent.ModifierFlags.option.rawValue) == NSEvent.ModifierFlags.option.rawValue
43 |
44 | // Play blink animation
45 | view.blink(with: NSColor.lightGray.withAlphaComponent(0.25), forDuration: 0.2)
46 |
47 | // Expand/Collapse item based on its current state
48 | isItemExpanded(item) ?
49 | animator().collapseItem(item, collapseChildren: optionPressed) :
50 | animator().expandItem(item, expandChildren: optionPressed)
51 | }
52 | }
53 | }
54 |
55 | extension NSOutlineView {
56 | /// Make function that automatically assigns given Key Item.
57 | func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?, menuItem: NSMenuItem) -> ItemTableCellView? {
58 | let view = super.makeView(withIdentifier: identifier, owner: owner) as? ItemTableCellView
59 | view?.menuItem = menuItem
60 | return view
61 | }
62 |
63 | /// A KeyItem at clicked row or nil if no Key Item found.
64 | var clickedMenuItem: NSMenuItem? {
65 | return item(atRow: clickedRow) as? NSMenuItem
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/OutlineViewDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineViewDataSource.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 24/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class OutlineViewDataSource: NSObject, NSOutlineViewDataSource {
10 |
11 | /// String that represents a search phrase that menu item titles should correspond
12 | var searchPhrase: String = ""
13 |
14 | /// Get the only menu items that need to be displayed by Outline View
15 | func itemsToDisplay(of menu: NSMenu) -> [NSMenuItem] {
16 | // Filter restricted items
17 | var result = menu.items.filter{ $0.shouldBeDisplayedInKeys }
18 | // Additionaly filter items that not containing searchPhrase if needed
19 | if !searchPhrase.isEmpty {
20 | result = result.filter{ $0.title(contains: searchPhrase) }
21 | }
22 |
23 | return result
24 | }
25 |
26 | func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
27 | // Get customizable Menu Items count
28 | if let submenu = (item as? NSMenuItem)?.submenu {
29 | return itemsToDisplay(of: submenu).count
30 | }
31 | return itemsToDisplay(of: Const.Menu.main).count
32 | }
33 |
34 | func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
35 | // Get customizable Menu Item by index
36 | if let submenu = (item as? NSMenuItem)?.submenu {
37 | return itemsToDisplay(of: submenu)[index] as Any
38 | }
39 | return itemsToDisplay(of: Const.Menu.main)[index] as Any
40 | }
41 |
42 | func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
43 | if let submenu = (item as? NSMenuItem)?.submenu {
44 | // Stop menu observer to avoid infinite loop and invoke menuNeedsUpdate method of submenu delegate to let Sketch provide actial menu items data if needed
45 | MenuObserver.shared.stopObserving()
46 | submenu.delegate?.menuNeedsUpdate?(submenu)
47 | MenuObserver.shared.startObserving()
48 |
49 | return itemsToDisplay(of: submenu).count > 0
50 | }
51 | return false
52 | }
53 | }
54 |
55 | fileprivate extension NSMenuItem {
56 | /// Recursively check if menu item and its children titles contains given search phrase.
57 | func title(contains searchPhrase: String) -> Bool {
58 | switch self {
59 | case let val where val.title.localizedCaseInsensitiveContains(searchPhrase):
60 | return true
61 | case let val where val.hasSubmenu:
62 | return val.submenu!.items.contains{ $0.title(contains: searchPhrase) }
63 | default:
64 | return false
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Source/Controller/Outline View/OutlineViewDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineViewDelegate.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 24/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class OutlineViewDelegate: NSObject, NSOutlineViewDelegate {
10 |
11 | @IBOutlet weak var errorHandler: ShortcutErrorHandler!
12 |
13 | func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
14 | var height: CGFloat = 0 // Should crash if no value is set
15 | if let menuItem = item as? NSMenuItem {
16 | if menuItem.isSeparatorItem {
17 | height = Const.Cell.separatorHeight
18 | } else {
19 | height = Const.Cell.height
20 | }
21 | }
22 | return height
23 | }
24 |
25 | func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
26 | var cell: NSTableCellView?
27 | if let menuItem = item as? NSMenuItem {
28 | if menuItem.isSeparatorItem {
29 | cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "separatorCell"), owner: self) as? NSTableCellView
30 | } else {
31 | cell = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "menuCell"), owner: self, menuItem: menuItem)
32 | }
33 | }
34 | return cell
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/Controller/SearchField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchField.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 13/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class SearchField: NSSearchField {
12 |
13 | @IBOutlet weak var outlineView: NSOutlineView!
14 | @IBOutlet weak var outlineViewDataSource: OutlineViewDataSource!
15 |
16 | /// Send search phrase to Data source of Outline view and reload its data to show the results
17 | @IBAction func filterResults(_ sender: NSSearchFieldCell) {
18 | outlineViewDataSource.searchPhrase = sender.stringValue
19 | outlineView.reloadData()
20 |
21 | // Expand items when searching or collapse it back on searching ends
22 | if !sender.stringValue.isEmpty {
23 | outlineView.expandItem(nil, expandChildren: true)
24 | } else {
25 | outlineView.collapseItem(nil, collapseChildren: true)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/Controller/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 12/02/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public class ViewController: NSViewController {
10 |
11 | /// - important: Should return instance only after plugin initialization.
12 | static var initialized: ViewController?
13 |
14 | // MARK: Sketch's MSPreferencePane conformation
15 |
16 | @objc static var toolbarIcon = image(Const.Image.keysIcon)
17 | @objc static var title = "Keys"
18 | @objc static var identifier = Const.Preferences.keysIdentifier
19 | @objc static var nibName = NSNib.Name(rawValue: "ViewController")
20 | @objc var preferencesController: AnyObject?
21 |
22 | @objc public convenience init(preferencesController: AnyObject) {
23 | self.init(nibName: type(of: self).nibName, bundle: Keys.bundle)
24 | self.preferencesController = preferencesController
25 | ViewController.initialized = self
26 | }
27 |
28 | // MARK: Variables
29 |
30 | @IBOutlet weak var outlineScrollView: NSScrollView!
31 | @IBOutlet weak var outlineView: OutlineView!
32 | @IBOutlet weak var actionMenu: ActionsMenu!
33 |
34 | // MARK: Actions
35 |
36 | // Main Settings button action
37 | @IBAction func settingsButtonAction(_ sender: NSButton) {
38 | actionMenu.popUp(positioning: nil, at: NSPoint(x: sender.frame.minX, y: sender.frame.minY), in: view)
39 | }
40 |
41 | // MARK: View Preparations
42 |
43 | override public func viewWillAppear() {
44 | // Style Outline View
45 | outlineScrollView.layer = {
46 | let layer = CALayer()
47 | layer.borderWidth = 0.5
48 | layer.borderColor = NSColor.lightGray.cgColor
49 | return layer
50 | }()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Source/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.8.9
19 | CFBundleVersion
20 | 1924
21 | NSHumanReadableCopyright
22 | Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
23 | NSPrincipalClass
24 | KeysForSketch.Keys
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Source/Keys.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Keys.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 11/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class Keys: NSObject {
10 |
11 | /// Shared instance
12 | @objc static var shared = Keys()
13 | /// `true` if plugin loading process is finished succesfuly.
14 | @objc var isLoaded: Bool = false
15 |
16 | // MARK: Properties
17 |
18 | /// Keys bundle shortcut.
19 | static var bundle: Bundle {
20 | return Bundle(for:Keys.self)
21 | }
22 |
23 | /// URL of `KeysForSketch.sketchplugin` bundle.
24 | static var pluginPath: String {
25 | let frameworkPath = bundle.bundleURL.path
26 | let endPathIndex = frameworkPath.range(of: ".sketchplugin", options: .backwards)!.upperBound
27 | let endPath = String(frameworkPath.prefix(upTo: endPathIndex))
28 | return endPath
29 | }
30 |
31 | // CFBundleShortVersionString shortcut.
32 | static var shortVersion: String {
33 | return bundle.infoDictionary?["CFBundleShortVersionString"] as! String
34 | }
35 |
36 | // MARK: Start
37 |
38 | /// Plugin loading preparations.
39 | @objc class func start() {
40 |
41 | // Try to test API protocols and inject it to its Sketch classes for easier casting in Swift. If fails, Keys shouldn't run further
42 | do {
43 | try SketchAPIProtocolsTest.testProtocols()
44 | try SketchAPITools.injectAPIProtocols()
45 | } catch {
46 | NSAlert.showKeysErrorAlert(error: error)
47 | return
48 | }
49 |
50 | // Keys should start loading only after Sketch isFinishedLaunching
51 | if NSRunningApplication.current.isFinishedLaunching {
52 | shared.loadKeys()
53 | } else {
54 | NotificationCenter.default.addObserver(shared,
55 | selector: #selector(shared.loadKeys),
56 | name: NSApplication.didFinishLaunchingNotification,
57 | object: NSApp)
58 | }
59 | }
60 |
61 | /// Load plugin.
62 | @objc func loadKeys() {
63 | do {
64 | try UIInjector.injectKeysPanel()
65 | // TODO: Don't forget to execute only after license verification.
66 | // try ProKeysManager.injectProKeys()
67 | } catch {
68 | NSAlert.showKeysErrorAlert(error: error)
69 | }
70 |
71 |
72 | // Invoke Observers
73 | MenuObserver.shared.startObserving()
74 | FileObserver.shared.startObserving()
75 | // TODO: Add license management
76 |
77 | // Update loading status
78 | isLoaded = true
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.h:
--------------------------------------------------------------------------------
1 | //
2 | // KeysForSketch.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 15/07/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "VDKSketchAPI.h"
11 | #import "VDK_JRSwizzle.h"
12 | #import "NSMenuItem+Private.h"
13 | #import "lorgnette.h"
14 | #import "lorgnette-structs.h"
15 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcodeproj/project.xcworkspace/xcuserdata/exevil.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch.xcodeproj/project.xcworkspace/xcuserdata/exevil.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcodeproj/xcuserdata/exevil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcodeproj/xcuserdata/exevil.xcuserdatad/xcschemes/KeysForSketch.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
68 |
69 |
70 |
76 |
77 |
78 |
79 |
80 |
81 |
87 |
91 |
92 |
93 |
99 |
100 |
101 |
102 |
104 |
105 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcodeproj/xcuserdata/exevil.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | KeysForSketch.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | KeysForSketchTests.xcscheme
13 |
14 | orderHint
15 | 1
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | EAC3F8041F1AC2EC00207699
21 |
22 | primary
23 |
24 |
25 | EAC3F80D1F1AC2ED00207699
26 |
27 | primary
28 |
29 |
30 | EACE68431F20181500518CAF
31 |
32 | primary
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/xcuserdata/exevil.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch.xcworkspace/xcuserdata/exevil.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/xcuserdata/exevil.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | EnabledFullIndexStoreVisibility
12 |
13 | IssueFilterStyle
14 | ShowActiveSchemeOnly
15 | LiveSourceIssuesEnabled
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Source/KeysForSketch.xcworkspace/xcuserdata/exevil.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-horizontal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "filename" : "disclosure-horizontal.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-horizontal.imageset/disclosure-horizontal.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-horizontal.imageset/disclosure-horizontal.pdf
--------------------------------------------------------------------------------
/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-vertical.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "filename" : "disclosure-vertical.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-vertical.imageset/disclosure-vertical.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/Assets/Assets.xcassets/disclosure-vertical.imageset/disclosure-vertical.pdf
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/MASShortcut.framework/MASShortcut:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/MASShortcut.framework/MASShortcut
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/MASShortcut.framework/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 16F73
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | MASShortcut
11 | CFBundleIdentifier
12 | org.cocoapods.MASShortcut
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | MASShortcut
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 2.3.6
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 9M174d
33 | DTPlatformVersion
34 | GM
35 | DTSDKBuild
36 | 17A306e
37 | DTSDKName
38 | macosx10.13
39 | DTXcode
40 | 0900
41 | DTXcodeBuild
42 | 9M174d
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/MASShortcut.framework/Versions/A/MASShortcut:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/MASShortcut.framework/Versions/A/MASShortcut
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreFoundation.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreFoundation.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreGraphics.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreGraphics.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreImage.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftCoreImage.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftFoundation.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftFoundation.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftObjectiveC.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftObjectiveC.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftQuartzCore.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftQuartzCore.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftSwiftOnoneSupport.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Frameworks/libswiftSwiftOnoneSupport.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.bundle/Contents/Resources/libswiftRemoteMirror.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/KeysForSketch/KeysForSketch.bundle/Contents/Resources/libswiftRemoteMirror.dylib
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Latest
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/KeysForSketch.xcworkspace/xcuserdata/exevil.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | EnabledFullIndexStoreVisibility
12 |
13 | IssueFilterStyle
14 | ShowActiveSchemeOnly
15 | LiveSourceIssuesEnabled
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Pods.xcodeproj/xcuserdata/exevil.xcuserdatad/xcschemes/MASShortcut.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Pods.xcodeproj/xcuserdata/exevil.xcuserdatad/xcschemes/Pods-KeysForSketch.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
68 |
69 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Pods.xcodeproj/xcuserdata/exevil.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MASShortcut-MASShortcut.xcscheme
8 |
9 | isShown
10 |
11 |
12 | MASShortcut.xcscheme
13 |
14 | isShown
15 |
16 |
17 | Pods-KeysForSketch.xcscheme
18 |
19 | isShown
20 |
21 |
22 | Pods-KeysForSketchUnitTests.xcscheme
23 |
24 | isShown
25 |
26 |
27 |
28 | SuppressBuildableAutocreation
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/MASShortcut/ResourceBundle-MASShortcut-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleIdentifier
8 | ${PRODUCT_BUNDLE_IDENTIFIER}
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundleName
12 | ${PRODUCT_NAME}
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 2.3.6
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2012-2013, Vadim Shpakovski
18 | All rights reserved.
19 |
20 | Redistribution and use in source and binary forms, with or without
21 | modification, are permitted provided that the following conditions are met:
22 |
23 | 1. Redistributions of source code must retain the above copyright notice, this
24 | list of conditions and the following disclaimer.
25 | 2. Redistributions in binary form must reproduce the above copyright notice,
26 | this list of conditions and the following disclaimer in the documentation
27 | and/or other materials provided with the distribution.
28 |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
30 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
33 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 |
40 | License
41 | BSD 2-clause
42 | Title
43 | MASShortcut
44 | Type
45 | PSGroupSpecifier
46 |
47 |
48 | FooterText
49 | Generated by CocoaPods - https://cocoapods.org
50 | Title
51 |
52 | Type
53 | PSGroupSpecifier
54 |
55 |
56 | StringsTable
57 | Acknowledgements
58 | Title
59 | Acknowledgements
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_KeysForSketch : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_KeysForSketch
5 | @end
6 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | install_framework()
10 | {
11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
12 | local source="${BUILT_PRODUCTS_DIR}/$1"
13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
15 | elif [ -r "$1" ]; then
16 | local source="$1"
17 | fi
18 |
19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
20 |
21 | if [ -L "${source}" ]; then
22 | echo "Symlinked..."
23 | source="$(readlink "${source}")"
24 | fi
25 |
26 | # use filter instead of exclude so missing patterns dont' throw errors
27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
29 |
30 | local basename
31 | basename="$(basename -s .framework "$1")"
32 | binary="${destination}/${basename}.framework/${basename}"
33 | if ! [ -r "$binary" ]; then
34 | binary="${destination}/${basename}"
35 | fi
36 |
37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
39 | strip_invalid_archs "$binary"
40 | fi
41 |
42 | # Resign the code if required by the build settings to avoid unstable apps
43 | code_sign_if_enabled "${destination}/$(basename "$1")"
44 |
45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
47 | local swift_runtime_libs
48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
49 | for lib in $swift_runtime_libs; do
50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
52 | code_sign_if_enabled "${destination}/${lib}"
53 | done
54 | fi
55 | }
56 |
57 | # Signs a framework with the provided identity
58 | code_sign_if_enabled() {
59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
60 | # Use the current code_sign_identitiy
61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
63 |
64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
65 | code_sign_cmd="$code_sign_cmd &"
66 | fi
67 | echo "$code_sign_cmd"
68 | eval "$code_sign_cmd"
69 | fi
70 | }
71 |
72 | # Strip invalid architectures
73 | strip_invalid_archs() {
74 | binary="$1"
75 | # Get architectures for current file
76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
77 | stripped=""
78 | for arch in $archs; do
79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
80 | # Strip non-valid architectures in-place
81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
82 | stripped="$stripped $arch"
83 | fi
84 | done
85 | if [[ "$stripped" ]]; then
86 | echo "Stripped $binary of architectures:$stripped"
87 | fi
88 | }
89 |
90 |
91 | if [[ "$CONFIGURATION" == "Debug" ]]; then
92 | install_framework "$BUILT_PRODUCTS_DIR/MASShortcut/MASShortcut.framework"
93 | fi
94 | if [[ "$CONFIGURATION" == "Release" ]]; then
95 | install_framework "$BUILT_PRODUCTS_DIR/MASShortcut/MASShortcut.framework"
96 | fi
97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
98 | wait
99 | fi
100 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | 3)
22 | TARGET_DEVICE_ARGS="--target-device tv"
23 | ;;
24 | *)
25 | TARGET_DEVICE_ARGS="--target-device mac"
26 | ;;
27 | esac
28 |
29 | install_resource()
30 | {
31 | if [[ "$1" = /* ]] ; then
32 | RESOURCE_PATH="$1"
33 | else
34 | RESOURCE_PATH="${PODS_ROOT}/$1"
35 | fi
36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
37 | cat << EOM
38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
39 | EOM
40 | exit 1
41 | fi
42 | case $RESOURCE_PATH in
43 | *.storyboard)
44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
46 | ;;
47 | *.xib)
48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
50 | ;;
51 | *.framework)
52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | ;;
57 | *.xcdatamodel)
58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
60 | ;;
61 | *.xcdatamodeld)
62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
64 | ;;
65 | *.xcmappingmodel)
66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
68 | ;;
69 | *.xcassets)
70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
72 | ;;
73 | *)
74 | echo "$RESOURCE_PATH"
75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
76 | ;;
77 | esac
78 | }
79 |
80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | fi
86 | rm -f "$RESOURCES_TO_COPY"
87 |
88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
89 | then
90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
92 | while read line; do
93 | if [[ $line != "${PODS_ROOT}*" ]]; then
94 | XCASSET_FILES+=("$line")
95 | fi
96 | done <<<"$OTHER_XCASSETS"
97 |
98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
99 | fi
100 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_KeysForSketchVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_KeysForSketchVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch.debug.xcconfig:
--------------------------------------------------------------------------------
1 | CODE_SIGN_IDENTITY =
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/MASShortcut"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/MASShortcut/MASShortcut.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "MASShortcut"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_KeysForSketch {
2 | umbrella header "Pods-KeysForSketch-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketch/Pods-KeysForSketch.release.xcconfig:
--------------------------------------------------------------------------------
1 | CODE_SIGN_IDENTITY =
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/MASShortcut"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/MASShortcut/MASShortcut.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "MASShortcut"
7 | PODS_BUILD_DIR = $BUILD_DIR
8 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Pods/Target Support Files/Pods-KeysForSketchUnitTests/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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Sketch API/Protocols/VDKSketchMSPluginManagerWithActionsProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSPluginManagerWithActionsProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 22/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSPluginManagerWithActionsProtocol
10 |
11 | + (NSURL *)mainPluginsFolderURL;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/KeysForSketch/Sketch API/Protocols/VDKSketchMSPreferencesControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDPreferencesControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 27/02/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | // Silence clang warnings
10 | #pragma clang diagnostic push
11 | #pragma clang diagnostic ignored "-Wnullability-completeness"
12 |
13 | @protocol VDKSketchMSPreferencesControllerProtocol
14 |
15 | @property (getter=isWindowLoaded, readonly) BOOL windowLoaded;
16 | @property (nullable, strong) NSWindow *window;
17 | @property(nonatomic) __weak NSToolbar *toolbar;
18 | @property(retain, nonatomic, nullable) NSCache *preferencePanes;
19 | @property(copy, nonatomic) NSDictionary *preferencePaneClasses;
20 | @property(copy, nonatomic) NSArray *toolbarItemIdentifiers;
21 | @property(retain, nonatomic) NSViewController *currentPreferencePane;
22 | @property(nonatomic) unsigned long long selectedTabIndex;
23 |
24 | + (instancetype)sharedController;
25 |
26 | - (BOOL)validateToolbarItem:(id)arg1;
27 | - (id)toolbar:(id)arg1 itemForItemIdentifier:(id)arg2 willBeInsertedIntoToolbar:(BOOL)arg3;
28 | - (id)toolbarSelectableItemIdentifiers:(id)arg1;
29 | - (id)toolbarDefaultItemIdentifiers:(id)arg1;
30 | - (id)toolbarAllowedItemIdentifiers:(id)arg1;
31 | - (void)updateWindowFrame;
32 | - (void)switchToPaneWithIdentifier:(id)arg1;
33 | - (void)switchPanes:(id)arg1;
34 | - (void)adjustColorsAction:(id)arg1;
35 | - (void)awakeFromNib;
36 |
37 | - (IBAction)showWindow:(id)sender;
38 | - (void)close;
39 |
40 | // Remaining properties
41 | @property(readonly, copy) NSString *debugDescription;
42 | @property(readonly, copy) NSString *description;
43 | @property(readonly) Class superclass;
44 |
45 | @end
46 |
47 | #pragma clang diagnostic pop
48 |
--------------------------------------------------------------------------------
/Source/KeysForSketchTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Source/KeysForSketchTests/KeysForSketchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeysForSketchTests.swift
3 | // KeysForSketchTests
4 | //
5 | // Created by Vyacheslav Dubovitsky on 08/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class KeysForSketchTests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | // Use XCTAssert and related functions to verify your tests produce the correct results.
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Source/Menu/MenuObserver.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuObserver.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public class MenuObserver : NSObject {
10 |
11 | @objc public static let shared = MenuObserver()
12 | let notificationCenter = NotificationCenter.default
13 |
14 | @objc public func startObserving() {
15 | notificationCenter.addObserver(self, selector: #selector(received(notification:)), name: NSMenu.didAddItemNotification, object: nil)
16 | notificationCenter.addObserver(self, selector: #selector(received(notification:)), name: NSMenu.didChangeItemNotification, object: nil)
17 | notificationCenter.addObserver(self, selector: #selector(received(notification:)), name: NSMenu.didChangeItemNotification, object: nil)
18 | }
19 |
20 | @objc public func stopObserving() {
21 | notificationCenter.removeObserver(self, name: NSMenu.didAddItemNotification, object: nil)
22 | notificationCenter.removeObserver(self, name: NSMenu.didChangeItemNotification, object: nil)
23 | notificationCenter.removeObserver(self, name: NSMenu.didChangeItemNotification, object: nil)
24 | }
25 |
26 | /// Handle received notifications.
27 | @objc func received(notification: NSNotification) {
28 | let menu = notification.object as? NSMenu
29 | // let itemIndex = notification.userInfo?["NSMenuItemIndex"] as? Int ?? -1
30 | // let menuItem = menu?.item(at: itemIndex)
31 |
32 | if menu?.isChildOf(Const.Menu.main) ?? false {
33 |
34 | // Reload outlineView for superitem if needed.
35 | if ViewController.initialized != nil {
36 | self.cancelPreviousPerformRequiestsAndPerform(#selector(self.reloadOutlineViewItem(for:)), with: menu, afterDelay: 0.5)
37 | }
38 |
39 | // Provide debug info based on notification type
40 | switch notification.name {
41 | case NSMenu.didAddItemNotification:
42 | // inDebug { print("Did add menuItem: '\(menu?.title ?? "") -> \(menuItem?.title ?? "")' (index: \(itemIndex))") }
43 | break
44 | case NSMenu.didChangeItemNotification:
45 | // inDebug { print("Did change menuItem: '\(menu?.title ?? "") -> \(menuItem?.title ?? "")' (index: \(itemIndex))") }
46 | break
47 | case NSMenu.didRemoveItemNotification:
48 | // inDebug { print("Did remove menuItem from Menu '\(menu?.title ?? "")' at index \(itemIndex)") }
49 | break
50 | default:
51 | assertionFailure("Unexpected notification type.")
52 | }
53 | }
54 | }
55 |
56 | /// Reload ViewController's Outline View item for given menu if needed.
57 | @objc func reloadOutlineViewItem(for menu: NSMenu?) {
58 | if let superItem = menu?.superitem {
59 | ViewController.initialized?.outlineView.reloadItem(superItem, reloadChildren: true)
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Source/Menu/NSMenu+Additions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSMenu+Additions.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 05/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public extension NSMenu {
10 |
11 | // MARK: Common
12 |
13 | /// True if NSMenu is a child of Application's mainMenu.
14 | func isChildOf(_ menu: NSMenu) -> Bool {
15 | var currentSupermenu = supermenu
16 | while currentSupermenu != nil {
17 | if currentSupermenu == menu { return true }
18 | currentSupermenu = currentSupermenu?.supermenu
19 | }
20 | return false
21 | }
22 |
23 | /// Enclosing Menu Item.
24 | var superitem: NSMenuItem? {
25 | return supermenu?.items.filter{ $0.submenu == self }[0]
26 | }
27 |
28 | // MARK: Iterate
29 |
30 | /// Iterate over all KeyItems placed deeper in hierarchy and execute given closure for each of it.
31 | func iterateAndExecute(_ closure: (_ menuItem: NSMenuItem) -> Void) {
32 | items.forEach { item in
33 | closure(item)
34 | item.submenu?.iterateAndExecute(closure)
35 | }
36 | }
37 |
38 | /// Find KeyItem placed deeper in hierarchy by its shortcut.
39 | func findItem(by shortcut: MASShortcut) -> NSMenuItem? {
40 | var resulItem: NSMenuItem? = nil
41 | iterateAndExecute { item in
42 | if item.shortcut == shortcut {
43 | resulItem = item
44 | }
45 | }
46 | return resulItem
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Source/Menu/NSMenuItem+Additions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSMenuItem+Additions.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/02/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public extension NSMenuItem {
10 |
11 | // MARK: Keys Checks
12 |
13 | /// Decide which Menu Item should be presented to user.
14 | var shouldBeDisplayedInKeys: Bool {
15 | // Alternate menuItems shouldn't have unique shortcut
16 | if isAlternate { return false }
17 | // If menuItem matches any of the manually defined exceptions, return false as well
18 | if Const.MenuItem.customizableShortcutExceptions.contains(where: { $0 == (title, parent?.title ?? "") }) { return false }
19 |
20 | return true
21 | }
22 |
23 | /// Determines should Key Item have a shortcut or not.
24 | var needsShortcut: Bool {
25 | if hasSubmenu || isSeparatorItem {
26 | return false
27 | }
28 | return true
29 | }
30 |
31 | // MARK: Shortcut
32 |
33 | /// Calculate individual key string to store shortcut value in System Preferences.
34 | var defaultsKey: String {
35 | var parentsTitles = [String]()
36 |
37 | var itemToLoop: NSMenuItem? = self
38 | while itemToLoop != nil {
39 | parentsTitles.append(itemToLoop!.title)
40 | itemToLoop = itemToLoop!.parent
41 | }
42 |
43 | return "\u{1B}" + parentsTitles.reversed().joined(separator: "\u{1B}")
44 | }
45 |
46 | /// Tools can have single a character shortcut and special binding after shortcut is set.
47 | var isTool: Bool {
48 | // Сheck if menu item is part of Insert menu
49 | var parentToCheck = parent
50 | while parentToCheck != nil {
51 | if parentToCheck?.title == "Insert" {
52 | return true
53 | }
54 | parentToCheck = parentToCheck?.parent
55 | }
56 |
57 | // TODO: Possibly add target class matching check
58 |
59 | return false
60 | }
61 |
62 | /// Check if menu item has custom shortcut assigned.
63 | var hasCustomShortcut: Bool {
64 | guard let currentShortcuts = CFPreferencesCopyAppValue(Const.Preferences.kUserKeyEquivalents, kCFPreferencesCurrentApplication) as? [String : String] else {
65 | assertionFailure("Can't get App's User Key Equivalents dict.")
66 | return false
67 | }
68 | return currentShortcuts[defaultsKey] != nil
69 | }
70 |
71 | /// Computed property that represents shortcut that currently associated with KeyItem.
72 | @objc var shortcut: MASShortcut? {
73 | get {
74 | return MASShortcut(keyEquivalent: keyEquivalent, modifierFlags: keyEquivalentModifierMask)
75 | }
76 | set {
77 | if needsShortcut {
78 | // Write new user key equivalent value to System Preferences
79 | writeCFPreferences(for: newValue)
80 |
81 | // Clean up AppKit's local user key equivalents cache
82 | appKeyEquivalents.pointee = nil
83 | // Recache updated user key equivalents cache from System Preferences and apply the changes
84 | _recacheUserKeyEquivalentOnlyIfStale(false)
85 |
86 | inDebug {
87 | print("Shortcut set:\n \(CFPreferencesCopyAppValue( "NSUserKeyEquivalents" as CFString, kCFPreferencesCurrentApplication) ?? [:] as CFPropertyList)")
88 | }
89 |
90 | // Final setup
91 | ToolsManager.setToolShortcutIfNeeded(for: self)
92 | ViewController.initialized?.outlineView.reloadItem(self)
93 | }
94 | }
95 | }
96 |
97 | // MARK: Other
98 |
99 | /// An index of the menu item in enclosing menu.
100 | /// - important: Returns `nil` if item has no enclosing menu or haven't been included into it for some reason.
101 | var index: Int? {
102 | return menu?.index(of: self) != -1 ? menu?.index(of: self) : nil
103 | }
104 |
105 | /// Target object type name
106 | var targetTypeName: String! {
107 | if target != nil {
108 | return String(describing: type(of:target!))
109 | } else {
110 | // TODO: Handle Error
111 | assertionFailure("Can't get menu item target to determine its class.")
112 | return nil
113 | }
114 | }
115 |
116 | /// Save given Shortcut to System Preferences
117 | func writeCFPreferences(for shortcut: MASShortcut?) {
118 |
119 | // Copy current values from System Preferences, add a new value and save it back
120 |
121 | var currentShortcuts = CFPreferencesCopyAppValue(Const.Preferences.kUserKeyEquivalents, kCFPreferencesCurrentApplication) as? [String : String] ?? [String : String]()
122 |
123 | if shortcut != nil {
124 | let sysPrefsShortcutString = "\(shortcut?.modifierFlagsStringForSystemPreferences ?? "")\(shortcut?.fixedKeyCodeStringForKeyEquivalent ?? "")"
125 | currentShortcuts[defaultsKey] = sysPrefsShortcutString
126 | } else {
127 | currentShortcuts[defaultsKey] = nil
128 | }
129 |
130 | CFPreferencesSetAppValue(Const.Preferences.kUserKeyEquivalents, currentShortcuts as CFPropertyList, kCFPreferencesCurrentApplication)
131 | CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
132 |
133 | // Register application in `com.apple.universalaccess` preference file if needed to get shortcuts shown in System Preferences
134 |
135 | guard var custommenuApps = CFPreferencesCopyAppValue(Const.Preferences.kCustomMenuApps, Const.Preferences.kUniversalAccess) as? [String] else {
136 | assertionFailure("Can't determine \(Const.Preferences.kCustomMenuApps) array from \(Const.Preferences.kUniversalAccess) preferences file")
137 | return
138 | }
139 |
140 | if !currentShortcuts.isEmpty && !custommenuApps.contains(Bundle.main.bundleIdentifier!) {
141 | custommenuApps += [Bundle.main.bundleIdentifier!]
142 | CFPreferencesSetAppValue(Const.Preferences.kCustomMenuApps, custommenuApps as CFPropertyList, Const.Preferences.kUniversalAccess)
143 | CFPreferencesAppSynchronize(Const.Preferences.kUniversalAccess)
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Source/Menu/NSMenuItem+Private.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSMenuItem+Private.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 27/07/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /// A category used to get access to NSMenuItem's private methods.
12 | @interface NSMenuItem (Private)
13 |
14 | /// When NO is passed, this method will recache user key equivalents immediately.
15 | - (void)_recacheUserKeyEquivalentOnlyIfStale:(BOOL)arg1;
16 | @end
17 |
--------------------------------------------------------------------------------
/Source/Menu/ToolsManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ToolsManager.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 28/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public class ToolsManager : NSObject {
10 |
11 | /// Sketch KeyBindings ShortcutMap dictionary contains mapping of tools action class names to its key equivalents.
12 | static var sketchShortcutMap: [String : String] {
13 | get {
14 | return VDKSketchAPI.keyBindingsController().shortcutMap as! [String : String]
15 | }
16 | set {
17 | // Update Sketch's KeyBindingsController shortcutMap current value
18 | VDKSketchAPI.keyBindingsController().shortcutMap.removeAllObjects()
19 | VDKSketchAPI.keyBindingsController().shortcutMap.setDictionary(newValue)
20 |
21 | // Update singleKeysShortcuts dict of action controllers of all opened documents which is storing an original KeyBindings shortcutMap value during initialization and won't updating it automatically.
22 | for case let doc as VDKSketchMSDocumentProtocol in NSApp.orderedDocuments {
23 | doc.actionsController.singleKeyShortcuts = newValue
24 | }
25 |
26 | // Store new value in User Key Bindings plist file to load it automatically when Sketch will start next time
27 | NSDictionary(dictionary: newValue).write(toFile: VDKSketchAPI.keyBindingsController().userKeyBindingsPath(), atomically: true)
28 | }
29 | }
30 |
31 | /// Default Sketch tools shortcut mapping.
32 | static var sketchDefaultKeyBindings: [String : String] {
33 | // TODO: Need to test
34 | return NSDictionary(contentsOfFile: VDKSketchAPI.keyBindingsController().defaultKeyBindingsPath()) as! [String : String]
35 | }
36 |
37 | // MARK: Shortcut
38 |
39 | /// Bind Shortcut for given Tool KeyItem via Sketch Key Bindings if needed.
40 | class func setToolShortcutIfNeeded(for menuItem: NSMenuItem) {
41 | if !menuItem.isTool { return }
42 |
43 | inDebug { print("Set tool shortcut for: ", menuItem.title) }
44 |
45 | // If the new shortcut is empty, remove its bindning
46 | guard let menuItemShortcut = menuItem.shortcut else {
47 | restoreDefaultToolShortcut(for: menuItem)
48 | return
49 | }
50 |
51 | // If given shortcut is a multi-character one remove previous single-character shortcut for tool from KeyBindings if exist without setting a new one because now app menu itself can manage the shortcut handling
52 | if menuItemShortcut.modifierFlags != 0 {
53 | restoreDefaultToolShortcut(for: menuItem)
54 | return
55 | }
56 |
57 | // Make sure that key has proper Key Equivalent and associated class
58 | if let keyEquivalents = KeyCodeTransformer.keyEquivalents(for: menuItemShortcut.keyCode) {
59 | // Get target class name and use it as a key to set new value to Sketch shortcutMap
60 | sketchShortcutMap[menuItem.targetTypeName] = keyEquivalents.joined()
61 | }
62 | }
63 |
64 | /// Remove shortcut of given tool KeyItem in Sketch Key Bindings.
65 | class func restoreDefaultToolShortcut(for menuItem: NSMenuItem) {
66 | if let defaultValue = sketchDefaultKeyBindings[menuItem.targetTypeName] {
67 | sketchShortcutMap[menuItem.targetTypeName] = defaultValue
68 | } else {
69 | sketchShortcutMap[menuItem.targetTypeName] = nil
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Source/Plugin/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "identifier" : "com.vyacheslav-dubovitsky.KeysForSketch",
3 | "name" : "Keys For Sketch",
4 | "author" : "Vyacheslav Dubovitsky",
5 | "homepage" : "https://github.com/exevil/Keys-For-Sketch/releases",
6 | "appcast" : "https://api.sketchpacks.com/v1/plugins/com.vyacheslav-dubovitsky.KeysForSketch/appcast",
7 | "version" : "0.8.9",
8 | "description" : "Advanced shortcut manager for Sketch",
9 | "authorEmail" : "m@dbv.ae",
10 | "commands" : [
11 | {
12 | "script" : "script.js",
13 | "handlers" : {
14 | "actions" : {
15 | "Startup" : "startKeys"
16 | }
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/Source/Plugin/script.js:
--------------------------------------------------------------------------------
1 | //
2 | // KeysForSketch
3 | //
4 | // Created by Vyacheslav Dubovitsky on 12/04/2017.
5 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
6 | //
7 |
8 | // Return principal class used to start an initialization process.
9 | function principalClass() {
10 | return NSClassFromString("KeysForSketch.Keys")
11 | }
12 |
13 | // Load framework and start initialization process if needed.
14 | function startKeys(context) {
15 | if (principalClass() == null || principalClass().isLoaded == false) {
16 | var mocha = Mocha.sharedRuntime()
17 | var resourcesFolder = context.scriptPath.stringByDeletingLastPathComponent().stringByDeletingLastPathComponent() + "/Resources"
18 | if (mocha.loadFrameworkWithName_inDirectory("KeysForSketch", resourcesFolder)) {
19 | principalClass().start()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/ProKeys/Keys/ProKeyAspectRatioSwitcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProKeyAspectRatioSwitcher.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 31/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | /// Aspect ratio (Constrain Proportions) lock switching menu item.
10 | final class ProKeyAspectRatioSwitcher: NSObject, ProKeyProtocol {
11 |
12 | static let shared: ProKeyProtocol = ProKeyAspectRatioSwitcher()
13 |
14 | let menuItems: [NSMenuItem]
15 | let parentMenu: NSMenu? = Const.Menu.main.item(withTitle: "Layer")?.submenu?.item(withTitle: "Transform")?.submenu
16 | let previousMenuItemTitle: String? = "Rotate"
17 |
18 | /// Button for switching aspect ratio lock in Sketch Inspector which our menu should correspond to.
19 | var sketchInspectorButton: NSButton? {
20 | return VDKSketchAPI.currentDocument()?.inspectorController.normalInspector.standardInspectors.layerViewController.lockProportionsButton
21 | }
22 |
23 | override init() {
24 | let actionMenuItem = NSMenuItem(title: "Constrain Proportions", action: #selector(action), keyEquivalent: "")
25 | menuItems = [
26 | NSMenuItem.separator(),
27 | actionMenuItem,
28 | NSMenuItem.separator()
29 | ]
30 | super.init()
31 | actionMenuItem.target = self
32 | }
33 |
34 | /// Simply click on Sketch Inspector button.
35 | @objc func action() {
36 | if let inspectorButton = sketchInspectorButton {
37 | inspectorButton.performClick(self)
38 | }
39 | }
40 |
41 | override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
42 | if let inspectorButton = sketchInspectorButton {
43 | menuItem.state = inspectorButton.state
44 | return inspectorButton.isEnabled
45 | } else {
46 | menuItem.state = .off
47 | return false
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Source/ProKeys/Keys/ProKeyProtocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProKeyProtocol.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 30/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | protocol ProKeyProtocol {
10 |
11 | /// Shared instance.
12 | static var shared: ProKeyProtocol { get }
13 |
14 | /// Associated menu items that will be injected into Sketch menu.
15 | var menuItems: [NSMenuItem] { get }
16 |
17 | /// Sketch menu where ProKey's menu items should be placed.
18 | /// - note: Since there's no guarantee that given menu exists, value type is set to optional and should be implicitly unwrapped during ProKeys injection process.
19 | /// - important: If no item will be found, error will be thrown.
20 | var parentMenu: NSMenu? { get }
21 |
22 | /// Title of Sketch menu item placed in parent menu after which ProKey menu items should be placed after.
23 | /// - important: Make sure that entered title is correct and refers to existing Sketch menu item. Otherwise, error will be thrown during ProKeys injection process.
24 | var previousMenuItemTitle: String? { get }
25 |
26 | /// Menu item validation. See `NSMenuValidation` for help.
27 | /// - important: Make sure that menuItem's target set to self during initialization to get `NSMenuValidation` work.
28 | func validateMenuItem(_ menuItem: NSMenuItem) -> Bool
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Source/ProKeys/ProKeysManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProKeysManager.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 21/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public class ProKeysManager: NSObject {
10 |
11 | /// Array of ProKeys
12 | static var proKeys: [ProKeyProtocol] = [ProKeyAspectRatioSwitcher.shared]
13 |
14 | /// Insert ProKeys menu items onto its positions.
15 | @objc public class func injectProKeys() throws {
16 | for pkey in proKeys {
17 |
18 | guard let parentMenu = pkey.parentMenu else { throw Error.cantGetParentMenuOf(proKey: pkey) }
19 |
20 | // If there's no specified previous menu item, ProKey's menu items should be inserted at index 0.
21 | var previousItemIndex = -1
22 | // If previous item title was set for ProKey, make sure that corresponding menu item exists or throw the error to avoid menu items misplacement.
23 | if let previousMenuItemTitle = pkey.previousMenuItemTitle {
24 | guard let prevMenuItem = parentMenu.item(withTitle: previousMenuItemTitle) else { throw Error.cantGetPreviousMenuItemOf(proKey: pkey) }
25 | previousItemIndex = parentMenu.index(of: prevMenuItem)
26 | }
27 |
28 | for menuItem in pkey.menuItems {
29 | parentMenu.insertItem(menuItem, at: previousItemIndex + 1)
30 | previousItemIndex += 1
31 | }
32 | }
33 | }
34 |
35 | enum Error: LocalizedError {
36 |
37 | case cantGetPreviousMenuItemOf(proKey: ProKeyProtocol)
38 | case cantGetParentMenuOf(proKey: ProKeyProtocol)
39 |
40 | var errorDescription: String? {
41 | switch self {
42 | case .cantGetPreviousMenuItemOf(let proKey):
43 | return "Can't get previous menu item of ProKey: \"\(type(of:proKey))\"."
44 | case .cantGetParentMenuOf(let proKey):
45 | return "Can't get parent menu of ProKey: \"\(type(of:proKey))\"."
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/Inspector/VDKSketchMSInspectorControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSInspectorControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSNormalInspectorProtocol;
10 |
11 | @protocol VDKSketchMSInspectorControllerProtocol
12 |
13 | @property(nonnull, retain, nonatomic) NSViewController *normalInspector;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/Inspector/VDKSketchMSLayerInspectorViewControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSLayerInspectorViewControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerInspectorViewControllerProtocol
10 |
11 | @property(nullable, nonatomic) __weak NSButton *lockProportionsButton;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/Inspector/VDKSketchMSNormalInspectorProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSNormalInspectorProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 | @protocol VDKSketchMSStandardInspectorViewControllersProtocol;
9 |
10 | @protocol VDKSketchMSNormalInspectorProtocol
11 |
12 | @property(nonnull, readonly, nonatomic) NSObject *standardInspectors;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/Inspector/VDKSketchMSStandardInspectorViewControllersProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSStandardInspectorViewControllersProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 17/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerInspectorViewControllerProtocol;
10 |
11 | @protocol VDKSketchMSStandardInspectorViewControllersProtocol
12 |
13 | @property(nonnull, readonly, nonatomic) NSViewController *layerViewController;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/VDKSketchMSActionControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSActionControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 22/06/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSActionControllerProtocol
10 |
11 | @property(nonnull, nonatomic, copy) NSDictionary *singleKeyShortcuts;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/VDKSketchMSDocumentControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSDocumentControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 27/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSDocumentProtocol;
10 |
11 | @protocol VDKSketchMSDocumentControllerProtocol
12 |
13 | + (nonnull instancetype)sharedDocumentController;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/VDKSketchMSDocumentDataProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSDocumentDataProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 03/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSPageProtocol;
10 |
11 | @protocol VDKSketchMSDocumentDataProtocol
12 |
13 | @property(nonnull, readonly, nonatomic) NSArray*> *pages;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Document/VDKSketchMSDocumentProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKMSDocumentProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 20/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSActionControllerProtocol, VDKSketchMSLayerArrayProtocol, VDKSketchMSDocumentDataProtocol, VDKSketchMSInspectorControllerProtocol;
10 |
11 | @protocol VDKSketchMSDocumentProtocol
12 |
13 | @property(nonnull, retain, nonatomic) NSObject *documentData;
14 | @property(nonnull, retain, nonatomic) NSObject *actionsController;
15 | @property(nonnull, retain, nonatomic) NSViewController *inspectorController;
16 |
17 | + (nullable instancetype)currentDocument;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Layers/VDKSketchMSArtboardGroupProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSArtboardGroupProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerGroupProtocol;
10 |
11 | @protocol VDKSketchMSArtboardGroupProtocol
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Layers/VDKSketchMSLayerArrayProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSLayerArrayProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 02/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKMSLayerProtocol;
10 |
11 | @protocol VDKSketchMSLayerArrayProtocol
12 |
13 | @property(nonnull, copy, nonatomic) NSArray*> *layers;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Layers/VDKSketchMSLayerGroupProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSLayerGroupProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerProtocol;
10 |
11 | @protocol VDKSketchMSLayerGroupProtocol
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Layers/VDKSketchMSLayerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSLayerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerProtocol
10 |
11 | @property(nonnull, retain, nonatomic) NSString *nodeName;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/Layers/VDKSketchMSPageProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSPageProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 04/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSLayerGroupProtocol, VDKSketchMSArtboardGroupProtocol;
10 |
11 | @protocol VDKSketchMSPageProtocol
12 |
13 | @property(weak, readonly, nonatomic) NSArray*> *artboards;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/VDKSketchAppControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchAppControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 22/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchAppControllerProtocol
10 |
11 | + (nonnull instancetype)sharedInstance;
12 | - (void)refreshCurrentDocument;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/VDKSketchMSKeyBindingsProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchMSKeyBindingsProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 24/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSKeyBindingsProtocol
10 |
11 | @property(nonnull, retain, nonatomic, readonly) NSMutableDictionary *shortcutMap;
12 |
13 | - (nonnull NSString *)defaultKeyBindingsPath;
14 | - (nonnull NSString *)userKeyBindingsPath;
15 | + (nonnull instancetype)sharedInstance;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/Source/Sketch API/Protocols/VDKSketchMSPreferencesControllerProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDPreferencesControllerProtocol.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 27/02/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @protocol VDKSketchMSPreferencesControllerProtocol
10 |
11 | @property(nullable, nonatomic) __weak NSToolbar *toolbar;
12 | @property(nullable, copy, nonatomic) NSArray *toolbarItemIdentifiers;
13 | @property(nonnull, copy, nonatomic) NSDictionary *preferencePaneClasses;
14 | @property(nonatomic) unsigned long long selectedTabIndex;
15 |
16 | + (nonnull id)sharedController;
17 | - (void)switchToPaneWithIdentifier:(nonnull NSToolbarItemIdentifier)arg1;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Source/Sketch API/SketchAPIIntegrationTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SketchAPIIntegrationTest.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 18/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | /// Runtime Big-Bang Integration test of all
10 | class SketchAPIIntegrationTest: NSObject {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/Source/Sketch API/SketchAPIProtocolsTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SketchAPI.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 06/08/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | /// Runtime test Sketch classes for conformance to corresponding Keys API protocols.
10 | public class SketchAPIProtocolsTest: NSObject {
11 |
12 | /// Array of protocols that represents Sketch Classes.
13 | static let protocols: [Protocol] = VDKSketchAPI.protocols
14 |
15 | /// Runtime test Sketch classes for conformance to corresponding Keys API protocols.
16 | @objc public static func testProtocols() throws {
17 | for prt in protocols {
18 | let cls: AnyClass
19 | do { cls = try SketchAPITools.sketchClass(from: prt) }
20 | catch { throw error }
21 |
22 | // Test that class responds to protocol instance methods
23 | var methodsCount: UInt32 = 0;
24 | if let instanceMethods = protocol_copyMethodDescriptionList(prt, true, true, &methodsCount) {
25 | for i in 0.. AnyClass {
52 | let prtName = String(cString: protocol_getName(prt))
53 |
54 | // Check and remove suffix and prefix of protocol name to define a class name
55 | let prtNamePrefix = "VDKSketch"
56 | let prtNameSuffix = "Protocol"
57 | assert(prtName.hasPrefix(prtNamePrefix) && prtName.hasSuffix(prtNameSuffix), "")
58 | let clsName = String(prtName.dropFirst(prtNamePrefix.count).dropLast(prtNameSuffix.count))
59 |
60 | // Define and return a class itself
61 | guard let cls = NSClassFromString(clsName) else {
62 | throw Error.cantDetermineClassFrom(protocol: prt)
63 | }
64 | return cls
65 | }
66 |
67 | /// Sketch API errors.
68 | enum Error: LocalizedError {
69 |
70 | case cantDetermineClassFrom(protocol: Protocol)
71 | case cantAdd(protocol: Protocol, toClass: AnyClass)
72 |
73 | var errorDescription: String? {
74 | switch self {
75 | case .cantDetermineClassFrom(let prt):
76 | return "Can't determine class from protocol: \"\(prt)\"."
77 | case .cantAdd(let prt, let cls):
78 | return "Can't add API protocol: \"\(prt)\" to its corresponding class: \"\(cls)\"."
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Source/Sketch API/VDKSketchAPI.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKSketchAPI.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 18/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 | #import
9 |
10 | #import "VDKSketchAppControllerProtocol.h"
11 | #import "VDKSketchMSPreferencesControllerProtocol.h"
12 | #import "VDKSketchMSKeyBindingsProtocol.h"
13 | // Document
14 | #import "VDKSketchMSDocumentControllerProtocol.h"
15 | #import "VDKSketchMSDocumentProtocol.h"
16 | #import "VDKSketchMSDocumentDataProtocol.h"
17 | #import "VDKSketchMSActionControllerProtocol.h"
18 | // - Inspector
19 | #import "VDKSketchMSInspectorControllerProtocol.h"
20 | #import "VDKSketchMSNormalInspectorProtocol.h"
21 | #import "VDKSketchMSStandardInspectorViewControllersProtocol.h"
22 | #import "VDKSketchMSLayerInspectorViewControllerProtocol.h"
23 | // Layers
24 | #import "VDKSketchMSLayerArrayProtocol.h"
25 | #import "VDKSketchMSPageProtocol.h"
26 | #import "VDKSketchMSArtboardGroupProtocol.h"
27 | #import "VDKSketchMSLayerGroupProtocol.h"
28 | #import "VDKSketchMSLayerProtocol.h"
29 |
30 | @interface VDKSketchAPI : NSObject
31 |
32 | /// Array of API protocols that represents selected methods from its corresponding Sketch Classes.
33 | @property (nonatomic, readonly, class) NSArray * _Nonnull protocols;
34 |
35 | // MARK: General
36 |
37 | + (NSWindowController *_Nonnull)preferencesController;
38 | + (NSObject *_Nonnull)appController;
39 | + (NSObject *_Nonnull)keyBindingsController;
40 |
41 | // MARK: Document
42 |
43 | + (NSDocumentController *_Nonnull)sharedDocumentController;
44 | + (NSDocument *_Nullable)currentDocument;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/Source/Sketch API/VDKSketchAPI.m:
--------------------------------------------------------------------------------
1 | //
2 | // SketchAPI.m
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 18/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | #import "VDKSketchAPI.h"
10 | #import
11 | #import
12 |
13 | @implementation VDKSketchAPI
14 |
15 | + (NSArray *)protocols {
16 | return SketchAPITools.protocols;
17 | }
18 |
19 | // MARK: General
20 |
21 | + (NSWindowController *)preferencesController {
22 | NSError *error;
23 | Class sketchClass = [SketchAPITools
24 | sketchClassFrom:@protocol(VDKSketchMSPreferencesControllerProtocol)
25 | error:&error];
26 | return [sketchClass sharedController];
27 | // TODO: Handle Error
28 | }
29 |
30 | + (nonnull NSObject *)appController {
31 | return [NSClassFromString(@"AppController") sharedInstance];
32 | }
33 |
34 | + (nonnull NSObject *)keyBindingsController {
35 | return [NSClassFromString(@"MSKeyBindings") sharedInstance];
36 | }
37 |
38 | // MARK: Document
39 |
40 | + (nonnull NSDocumentController *)sharedDocumentController {
41 | return [NSClassFromString(@"MSDocumentController") sharedDocumentController];
42 | }
43 |
44 | + (nullable NSDocument *)currentDocument {
45 | return [NSClassFromString(@"MSDocument") currentDocument];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Source/Support/Const.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 19/04/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public struct Const {
10 | /// Image names.
11 | public struct Image {
12 | public static let keysIcon = "KeysIcon.icns"
13 | static let disclosureVertiacal = "disclosure-vertical"
14 | static let disclosureHorizontal = "disclosure-horizontal"
15 | }
16 |
17 | struct Menu {
18 |
19 | /// Sketch's mainMenu.
20 | static let main = NSApplication.shared.mainMenu!
21 |
22 | /// A dictionary where keys is titles of Tool Menu Items and the objects is a Sketch action class names associated with it.
23 | static let toolsClassNames = [
24 | "Vector" : "MSInsertVectorAction",
25 | "Pencil" : "MSPencilAction",
26 | "Text" : "MSInsertTextLayerAction",
27 | "Artboard" : "MSInsertArtboardAction",
28 | "Slice" : "MSInsertSliceAction",
29 | // Shape Submenu items
30 | "Line" : "MSInsertLineAction",
31 | "Arrow" : "MSInsertArrowAction",
32 | "Rectangle" : "MSRectangleShapeAction",
33 | "Oval" : "MSOvalShapeAction",
34 | "Rounded" : "MSRoundedRectangleShapeAction",
35 | "Star" : "MSStarShapeAction",
36 | "Polygon" : "MSPolygonShapeAction",
37 | "Triangle": "MSTriangleShapeAction"
38 | ]
39 | }
40 |
41 | struct MenuItem {
42 | /// An array of manually defined tuples describing a menuItems that shouldn't be customized by Keys.
43 | static let customizableShortcutExceptions: [(title: String, parentItem: String)] = [
44 | // Sketch
45 | // Dynamic system menu. Better to manage through System Preferences
46 | ("Services", Const.Menu.main.items[0].title),
47 | // File
48 | // A group of dynamic items
49 | ("New from Template", "File"),
50 | // Hidden by Sketch from the main menu
51 | ("New from Template…", "File"),
52 | // A group of dynamic items
53 | ("Open Recent", "File"),
54 | // A group of dynamic items
55 | ("Revert To", "File"),
56 | // A group of dynamic items
57 | ("Print", "File"),
58 | // Edit
59 | // Since "Start Dictation" has`fn fn` shortcut by default which we cant easily reassign, skip it too.
60 | ("Start Dictation", "Edit"),
61 | // Insert
62 | // Layer
63 | // A group of dynamic items
64 | ("Replace With", "Layer")
65 | // Text
66 | // Arrange
67 | // Share
68 | // Plugins
69 | // View
70 | // Window
71 | // Help
72 | ]
73 | }
74 |
75 | struct Cell {
76 | // OutlineView cells
77 | static let height: CGFloat = 33.0
78 | static let separatorHeight: CGFloat = 8.0
79 | static let dividerHeight: CGFloat = 0.5
80 | }
81 |
82 | struct KeyCodeTransformer {
83 | /// A map of non-printed characters Key Codes to its String representations. Values presented in array to match KeyCodeTransformer mapping dictionary data representation.
84 | static let specialKeyCodesMap : [UInt : [String]] = [
85 | UInt(kVK_F1) : [String(unicodeInt: NSF1FunctionKey)],
86 | UInt(kVK_F2) : [String(unicodeInt: NSF2FunctionKey)],
87 | UInt(kVK_F3) : [String(unicodeInt: NSF3FunctionKey)],
88 | UInt(kVK_F4) : [String(unicodeInt: NSF4FunctionKey)],
89 | UInt(kVK_F5) : [String(unicodeInt: NSF5FunctionKey)],
90 | UInt(kVK_F6) : [String(unicodeInt: NSF6FunctionKey)],
91 | UInt(kVK_F7) : [String(unicodeInt: NSF7FunctionKey)],
92 | UInt(kVK_F8) : [String(unicodeInt: NSF8FunctionKey)],
93 | UInt(kVK_F9) : [String(unicodeInt: NSF9FunctionKey)],
94 | UInt(kVK_F10) : [String(unicodeInt: NSF10FunctionKey)],
95 | UInt(kVK_F11) : [String(unicodeInt: NSF11FunctionKey)],
96 | UInt(kVK_F12) : [String(unicodeInt: NSF12FunctionKey)],
97 | UInt(kVK_F13) : [String(unicodeInt: NSF13FunctionKey)],
98 | UInt(kVK_F14) : [String(unicodeInt: NSF14FunctionKey)],
99 | UInt(kVK_F15) : [String(unicodeInt: NSF15FunctionKey)],
100 | UInt(kVK_F16) : [String(unicodeInt: NSF16FunctionKey)],
101 | UInt(kVK_F17) : [String(unicodeInt: NSF17FunctionKey)],
102 | UInt(kVK_F18) : [String(unicodeInt: NSF18FunctionKey)],
103 | UInt(kVK_F19) : [String(unicodeInt: NSF19FunctionKey)],
104 | UInt(kVK_F20) : [String(unicodeInt: NSF20FunctionKey)],
105 | UInt(kVK_Space) : ["\u{20}"],
106 | UInt(kVK_Delete) : [String(unicodeInt: NSBackspaceCharacter)],
107 | UInt(kVK_ForwardDelete) : [String(unicodeInt: NSDeleteCharacter)],
108 | UInt(kVK_ANSI_KeypadClear) : [String(unicodeInt: NSClearLineFunctionKey)],
109 | UInt(kVK_LeftArrow) : [String(unicodeInt: NSLeftArrowFunctionKey), "\u{2190}"],
110 | UInt(kVK_RightArrow) : [String(unicodeInt: NSRightArrowFunctionKey), "\u{2192}"],
111 | UInt(kVK_UpArrow) : [String(unicodeInt: NSUpArrowFunctionKey), "\u{2191}"],
112 | UInt(kVK_DownArrow) : [String(unicodeInt: NSDownArrowFunctionKey), "\u{2193}"],
113 | UInt(kVK_End) : [String(unicodeInt: NSEndFunctionKey)],
114 | UInt(kVK_Home) : [String(unicodeInt: NSHomeFunctionKey)],
115 | UInt(kVK_Escape) : ["\u{1b}"],
116 | UInt(kVK_PageDown) : [String(unicodeInt: NSPageDownFunctionKey)],
117 | UInt(kVK_PageUp) : [String(unicodeInt: NSPageUpFunctionKey)],
118 | UInt(kVK_Return) : [String(unicodeInt: NSCarriageReturnCharacter)],
119 | UInt(kVK_ANSI_KeypadEnter) : [String(unicodeInt: NSEnterCharacter)],
120 | UInt(kVK_Tab) : [String(unicodeInt: NSTabCharacter)],
121 | UInt(kVK_Help) : [String(unicodeInt: NSHelpFunctionKey)],
122 | UInt(kVK_Function) : ["\u{F747}"]
123 | ]
124 | }
125 |
126 | struct Preferences {
127 | // Identifier used to present Keys in Preferences window.
128 | static let keysIdentifier = NSToolbarItem.Identifier(rawValue: "keysForSketch")
129 | // Keys used to store settings in Preferences
130 | static let kUserKeyEquivalents = "NSUserKeyEquivalents" as CFString
131 | static let kCustomMenuApps = "com.apple.custommenu.apps" as CFString
132 | static let kUniversalAccess = "com.apple.universalaccess" as CFString
133 | }
134 |
135 | struct Licensing {
136 | /// Key for local storing of Licensing Info in UserDefaults.
137 | static let kLicensingInfoDefaultsDict = "VDKLicensingInfo"
138 | }
139 | }
140 |
141 | fileprivate extension String {
142 | /// Init with unicode int value.
143 | init(unicodeInt: Int) {
144 | self = String(format: "%C", unicodeInt)
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Source/Support/Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helpers.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 06/07/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | /// Execute given closure in `#DEBUG` configuration.
10 | func inDebug(_ closure: () -> () ) {
11 | #if DEBUG
12 | closure()
13 | #endif
14 | }
15 |
16 | // MARK: Assertions
17 |
18 | /// Fast `non-nil` assert.
19 | func assertNonNil(_ object: AnyObject?) {
20 | assert(object != nil, "Object is nil.")
21 | }
22 |
23 | /// Fast `respondsToSelector` assert.
24 | func assert(_ object: AnyObject, respondsTo selector: Selector) {
25 | assert(object.responds(to: selector), "Object is not responding to selector.")
26 | }
27 |
28 | /// Get a resource from asset catalog by name
29 | public func image(_ imageName: String) -> NSImage? {
30 | return Bundle(for:Keys.self).image(forResource: NSImage.Name(rawValue: imageName))
31 | }
32 |
33 | // MARK: Other
34 |
35 | /// Pointer to internal static AppKit dictionary with cached user Key Equivalents.
36 | let appKeyEquivalents: UnsafeMutablePointer = {
37 | let addr = UInt(lorgnette_lookup(mach_task_self_, "sAppKeyEquivalents"))
38 | return UnsafeMutablePointer(bitPattern:addr)!
39 | }()
40 |
41 |
--------------------------------------------------------------------------------
/Source/Support/JRSwizzle/VDK_JRSwizzle.h:
--------------------------------------------------------------------------------
1 | // JRSwizzle.h semver:1.1.0
2 | // Copyright (c) 2007-2016 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
3 | // Some rights reserved: http://opensource.org/licenses/mit
4 | // https://github.com/rentzsch/jrswizzle
5 |
6 | #import
7 |
8 | @interface NSObject (VDK_JRSwizzle)
9 |
10 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_;
11 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_;
12 |
13 |
14 | /**
15 | ```
16 | __block NSInvocation *invocation = nil;
17 | invocation = [self jr_swizzleMethod:@selector(initWithCoder:) withBlock:^(id obj, NSCoder *coder) {
18 | NSLog(@"before %@, coder %@", obj, coder);
19 |
20 | [invocation setArgument:&coder atIndex:2];
21 | [invocation invokeWithTarget:obj];
22 |
23 | id ret = nil;
24 | [invocation getReturnValue:&ret];
25 |
26 | NSLog(@"after %@, coder %@", obj, coder);
27 |
28 | return ret;
29 | } error:nil];
30 | ```
31 | */
32 | + (NSInvocation*)jr_swizzleMethod:(SEL)origSel withBlock:(id)block error:(NSError**)error;
33 |
34 | /**
35 | ```
36 | __block NSInvocation *classInvocation = nil;
37 | classInvocation = [self jr_swizzleClassMethod:@selector(test) withBlock:^() {
38 | NSLog(@"before");
39 |
40 | [classInvocation invoke];
41 |
42 | NSLog(@"after");
43 | } error:nil];
44 | ```
45 | */
46 | + (NSInvocation*)jr_swizzleClassMethod:(SEL)origSel withBlock:(id)block error:(NSError**)error;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Source/Support/Lorgnette/lorgnette-structs.h:
--------------------------------------------------------------------------------
1 | //
2 | // lorgnette-structs.h
3 | // liblorgnette
4 | //
5 | // Created by Dmitry Rodionov on 9/26/14.
6 | // Copyright (c) 2014 rodionovd. All rights reserved.
7 | //
8 |
9 | #pragma once
10 |
11 | struct dyld_image_info_32 {
12 | uint32_t imageLoadAddress;
13 | uint32_t imageFilePath;
14 | uint32_t imageFileModDate;
15 | };
16 |
17 | struct load_command_with_segname {
18 | uint32_t cmd;
19 | uint32_t cmdsize;
20 | uint32_t segname;
21 | };
22 |
23 | struct dyld_image_info_64 {
24 | uint64_t imageLoadAddress;
25 | uint64_t imageFilePath;
26 | uint64_t imageFileModDate;
27 | };
28 |
29 | struct dyld_all_image_infos_32 {
30 | uint32_t version;
31 | uint32_t infoArrayCount;
32 | uint32_t infoArray;
33 | uint32_t notification;
34 | bool processDetachedFromSharedRegion;
35 | bool libSystemInitialized;
36 | uint32_t dyldImageLoadAddress;
37 | uint32_t jitInfo;
38 | uint32_t dyldVersion;
39 | uint32_t errorMessage;
40 | uint32_t terminationFlags;
41 | uint32_t coreSymbolicationShmPage;
42 | uint32_t systemOrderFlag;
43 | uint32_t uuidArrayCount;
44 | uint32_t uuidArray;
45 | uint32_t dyldAllImageInfosAddress;
46 | uint32_t initialImageCount;
47 | uint32_t errorKind;
48 | uint32_t errorClientOfDylibPath;
49 | uint32_t errorTargetDylibPath;
50 | uint32_t errorSymbol;
51 | uint32_t sharedCacheSlide;
52 | uint8_t sharedCacheUUID[16];
53 | uint32_t sharedCacheBaseAddress;
54 | uint64_t infoArrayChangeTimestamp;
55 | uint32_t dyldPath;
56 | uint32_t notifyMachPorts[8];
57 | uint32_t reserved[5];
58 | uint32_t compact_dyld_image_info_addr;
59 | uint32_t compact_dyld_image_info_size;
60 | };
61 |
62 | struct dyld_all_image_infos_64 {
63 | uint32_t version;
64 | uint32_t infoArrayCount;
65 | uint64_t infoArray;
66 | uint64_t notification;
67 | bool processDetachedFromSharedRegion;
68 | bool libSystemInitialized;
69 | uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
70 | uint64_t dyldImageLoadAddress;
71 | uint64_t jitInfo;
72 | uint64_t dyldVersion;
73 | uint64_t errorMessage;
74 | uint64_t terminationFlags;
75 | uint64_t coreSymbolicationShmPage;
76 | uint64_t systemOrderFlag;
77 | uint64_t uuidArrayCount;
78 | uint64_t uuidArray;
79 | uint64_t dyldAllImageInfosAddress;
80 | uint64_t initialImageCount;
81 | uint64_t errorKind;
82 | uint64_t errorClientOfDylibPath;
83 | uint64_t errorTargetDylibPath;
84 | uint64_t errorSymbol;
85 | uint64_t sharedCacheSlide;
86 | uint8_t sharedCacheUUID[16];
87 | uint64_t sharedCacheBaseAddress;
88 | uint64_t infoArrayChangeTimestamp;
89 | uint64_t dyldPath;
90 | uint32_t notifyMachPorts[8];
91 | uint64_t reserved[9];
92 | uint64_t compact_dyld_image_info_addr;
93 | uint64_t compact_dyld_image_info_size;
94 | };
95 |
--------------------------------------------------------------------------------
/Source/Support/Lorgnette/lorgnette.h:
--------------------------------------------------------------------------------
1 | //
2 | // lorgnette.h
3 | // liblorgnette
4 | //
5 | // Created by Dmitry Rodionov on 9/24/14.
6 | // Copyright (c) 2014 rodionovd. All rights reserved.
7 | //
8 | #include
9 | #pragma once
10 | /**
11 | * @abstract
12 | * Locate a symbol inside an arbitrary process' address space.
13 | *
14 | * @note
15 | * This function iterates local symbols first and only then it looks for symbols
16 | * in linked libraries.
17 | *
18 | * @param target
19 | * The target process to inspect.
20 | * @param symbol_name
21 | * The name of the symbol to find. This parameter must not be NULL.
22 | *
23 | * @return
24 | * An address of the given symbol within the given process, or 0 (zero) if this symbol
25 | * could not be found.
26 | *
27 | * @b Examples
28 | *
29 | * Find a @p dlopen symbol address within the current task memory space
30 | * @code
31 | * addr = lorgnette_lookup(mach_task_self(), "dlopen");
32 | * @endcode
33 | * Find a @p glob_var0 symbol address inside a remote process
34 | * @code
35 | * addr = lorgnette_lookup(some_task, "glob_var0");
36 | * @endcode
37 | */
38 | mach_vm_address_t lorgnette_lookup(task_t target, const char *symbol_name);
39 |
40 | /**
41 | * @abstract
42 | * Locate a symbol within a particular image inside an alien process.
43 | *
44 | * @param target
45 | * The target process to inspect.
46 | * @param symbol_name
47 | * The name of the symbol to find. This parameter must not be NULL.
48 | * @param image_name
49 | * The name of the host image of the given symbol. This may be NULL.
50 | * The image name should be either a full file path or just a file base name.
51 | *
52 | * @return
53 | * An address of the given symbol within the given process, or 0 (zero) if this symbol
54 | * could not be found [within the given image, if @p image_name is not NULL].
55 | *
56 | * @see lorgnette_lookup()
57 | */
58 | mach_vm_address_t lorgnette_lookup_image(task_t target, const char *symbol_name, const char *image_name);
59 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/MASShortcut:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/MASShortcut
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASDictionaryTransformer.h:
--------------------------------------------------------------------------------
1 | extern NSString *const MASDictionaryTransformerName;
2 |
3 | /**
4 | Converts shortcuts for storage in user defaults.
5 |
6 | User defaults can’t stored custom types directly, they have to
7 | be serialized to `NSData` or some other supported type like an
8 | `NSDictionary`. In Cocoa Bindings, the conversion can be done
9 | using value transformers like this one.
10 |
11 | There’s a built-in transformer (`NSKeyedUnarchiveFromDataTransformerName`)
12 | that converts any `NSCoding` types to `NSData`, but with shortcuts
13 | it makes sense to use a dictionary instead – the defaults look better
14 | when inspected with the `defaults` command-line utility and the
15 | format is compatible with an older sortcut library called Shortcut
16 | Recorder.
17 | */
18 | @interface MASDictionaryTransformer : NSValueTransformer
19 | @end
20 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASHotKey.h:
--------------------------------------------------------------------------------
1 | #import "MASShortcut.h"
2 |
3 | extern FourCharCode const MASHotKeySignature;
4 |
5 | @interface MASHotKey : NSObject
6 |
7 | @property(readonly) UInt32 carbonID;
8 | @property(copy) dispatch_block_t action;
9 |
10 | + (instancetype) registeredHotKeyWithShortcut: (MASShortcut*) shortcut;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASKeyCodes.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "MASKeyMasks.h"
4 |
5 | // These glyphs are missed in Carbon.h
6 | enum {
7 | kMASShortcutGlyphEject = 0x23CF,
8 | kMASShortcutGlyphClear = 0x2715,
9 | kMASShortcutGlyphDeleteLeft = 0x232B,
10 | kMASShortcutGlyphDeleteRight = 0x2326,
11 | kMASShortcutGlyphLeftArrow = 0x2190,
12 | kMASShortcutGlyphRightArrow = 0x2192,
13 | kMASShortcutGlyphUpArrow = 0x2191,
14 | kMASShortcutGlyphDownArrow = 0x2193,
15 | kMASShortcutGlyphEscape = 0x238B,
16 | kMASShortcutGlyphHelp = 0x003F,
17 | kMASShortcutGlyphPageDown = 0x21DF,
18 | kMASShortcutGlyphPageUp = 0x21DE,
19 | kMASShortcutGlyphTabRight = 0x21E5,
20 | kMASShortcutGlyphReturn = 0x2305,
21 | kMASShortcutGlyphReturnR2L = 0x21A9,
22 | kMASShortcutGlyphPadClear = 0x2327,
23 | kMASShortcutGlyphNorthwestArrow = 0x2196,
24 | kMASShortcutGlyphSoutheastArrow = 0x2198,
25 | };
26 |
27 | NS_INLINE NSString* NSStringFromMASKeyCode(unsigned short ch)
28 | {
29 | return [NSString stringWithFormat:@"%C", ch];
30 | }
31 |
32 | NS_INLINE NSUInteger MASPickCocoaModifiers(NSUInteger flags)
33 | {
34 | return (flags & (NSEventModifierFlagControl | NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagCommand));
35 | }
36 |
37 | NS_INLINE UInt32 MASCarbonModifiersFromCocoaModifiers(NSUInteger cocoaFlags)
38 | {
39 | return
40 | (cocoaFlags & NSEventModifierFlagCommand ? cmdKey : 0)
41 | | (cocoaFlags & NSEventModifierFlagOption ? optionKey : 0)
42 | | (cocoaFlags & NSEventModifierFlagControl ? controlKey : 0)
43 | | (cocoaFlags & NSEventModifierFlagShift ? shiftKey : 0);
44 | }
45 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASKeyMasks.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | // https://github.com/shpakovski/MASShortcut/issues/99
4 | //
5 | // Long story short: NSControlKeyMask and friends were replaced with NSEventModifierFlagControl
6 | // and similar in macOS Sierra. The project builds fine & clean, but including MASShortcut in
7 | // a project with deployment target set to 10.12 results in several deprecation warnings because
8 | // of the control masks. Simply replacing the old symbols with the new ones isn’t an option,
9 | // since it breaks the build on older SDKs – in Travis, for example.
10 | //
11 | // It should be safe to remove this whole thing once the 10.12 SDK is ubiquitous.
12 |
13 | #if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
14 | #define NSEventModifierFlagCommand NSCommandKeyMask
15 | #define NSEventModifierFlagControl NSControlKeyMask
16 | #define NSEventModifierFlagOption NSAlternateKeyMask
17 | #define NSEventModifierFlagShift NSShiftKeyMask
18 | #endif
19 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASLocalization.h:
--------------------------------------------------------------------------------
1 | /**
2 | Reads a localized string from the framework’s bundle.
3 |
4 | Normally you would use NSLocalizedString to read the localized
5 | strings, but that’s just a shortcut for loading the strings from
6 | the main bundle. And once the framework ends up in an app, the
7 | main bundle will be the app’s bundle and won’t contain our strings.
8 | So we introduced this helper function that makes sure to load the
9 | strings from the framework’s bundle. Please avoid using
10 | NSLocalizedString throughout the framework, it wouldn’t work
11 | properly.
12 | */
13 | NSString *MASLocalizedString(NSString *key, NSString *comment);
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcut-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | #import "MASDictionaryTransformer.h"
14 | #import "MASHotKey.h"
15 | #import "MASKeyCodes.h"
16 | #import "MASKeyMasks.h"
17 | #import "MASLocalization.h"
18 | #import "MASShortcut.h"
19 | #import "MASShortcutBinder.h"
20 | #import "MASShortcutMonitor.h"
21 | #import "MASShortcutValidator.h"
22 | #import "MASShortcutView+Bindings.h"
23 | #import "MASShortcutView.h"
24 | #import "Shortcut.h"
25 |
26 | FOUNDATION_EXPORT double MASShortcutVersionNumber;
27 | FOUNDATION_EXPORT const unsigned char MASShortcutVersionString[];
28 |
29 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcut.h:
--------------------------------------------------------------------------------
1 | #import "MASKeyCodes.h"
2 |
3 | /**
4 | A model class to hold a key combination.
5 |
6 | This class just represents a combination of keys. It does not care if
7 | the combination is valid or can be used as a hotkey, it doesn’t watch
8 | the input system for the shortcut appearance, nor it does access user
9 | defaults.
10 | */
11 | @interface MASShortcut : NSObject
12 |
13 | /**
14 | The virtual key code for the keyboard key.
15 |
16 | Hardware independent, same as in `NSEvent`. See `Events.h` in the HIToolbox
17 | framework for a complete list, or Command-click this symbol: `kVK_ANSI_A`.
18 | */
19 | @property (nonatomic, readonly) NSUInteger keyCode;
20 |
21 | /**
22 | Cocoa keyboard modifier flags.
23 |
24 | Same as in `NSEvent`: `NSCommandKeyMask`, `NSAlternateKeyMask`, etc.
25 | */
26 | @property (nonatomic, readonly) NSUInteger modifierFlags;
27 |
28 | /**
29 | Same as `keyCode`, just a different type.
30 | */
31 | @property (nonatomic, readonly) UInt32 carbonKeyCode;
32 |
33 | /**
34 | Carbon modifier flags.
35 |
36 | A bit sum of `cmdKey`, `optionKey`, etc.
37 | */
38 | @property (nonatomic, readonly) UInt32 carbonFlags;
39 |
40 | /**
41 | A string representing the “key” part of a shortcut, like the `5` in `⌘5`.
42 |
43 | @warning The value may change depending on the active keyboard layout.
44 | For example for the `^2` keyboard shortcut (`kVK_ANSI_2+NSControlKeyMask`
45 | to be precise) the `keyCodeString` is `2` on the US keyboard, but `ě` when
46 | the Czech keyboard layout is active. See the spec for details.
47 | */
48 | @property (nonatomic, readonly) NSString *keyCodeString;
49 |
50 | /**
51 | A key-code string used in key equivalent matching.
52 |
53 | For precise meaning of “key equivalents” see the `keyEquivalent`
54 | property of `NSMenuItem`. Here the string is used to support shortcut
55 | validation (“is the shortcut already taken in this menu?”) and
56 | for display in `NSMenu`.
57 |
58 | The value of this property may differ from `keyCodeString`. For example
59 | the Russian keyboard has a `Г` (Ge) Cyrillic character in place of the
60 | latin `U` key. This means you can create a `^Г` shortcut, but in menus
61 | that’s always displayed as `^U`. So the `keyCodeString` returns `Г`
62 | and `keyCodeStringForKeyEquivalent` returns `U`.
63 | */
64 | @property (nonatomic, readonly) NSString *keyCodeStringForKeyEquivalent;
65 |
66 | /**
67 | A string representing the shortcut modifiers, like the `⌘` in `⌘5`.
68 | */
69 | @property (nonatomic, readonly) NSString *modifierFlagsString;
70 |
71 | - (instancetype)initWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags;
72 | + (instancetype)shortcutWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags;
73 |
74 | /**
75 | Creates a new shortcut from an `NSEvent` object.
76 |
77 | This is just a convenience initializer that reads the key code and modifiers from an `NSEvent`.
78 | */
79 | + (instancetype)shortcutWithEvent:(NSEvent *)anEvent;
80 |
81 | @end
82 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcutBinder.h:
--------------------------------------------------------------------------------
1 | #import "MASShortcutMonitor.h"
2 |
3 | /**
4 | Binds actions to user defaults keys.
5 |
6 | If you store shortcuts in user defaults (for example by binding
7 | a `MASShortcutView` to user defaults), you can use this class to
8 | connect an action directly to a user defaults key. If the shortcut
9 | stored under the key changes, the action will get automatically
10 | updated to the new one.
11 |
12 | This class is mostly a wrapper around a `MASShortcutMonitor`. It
13 | watches the changes in user defaults and updates the shortcut monitor
14 | accordingly with the new shortcuts.
15 | */
16 | @interface MASShortcutBinder : NSObject
17 |
18 | /**
19 | A convenience shared instance.
20 |
21 | You may use it so that you don’t have to manage an instance by hand,
22 | but it’s perfectly fine to allocate and use a separate instance instead.
23 | */
24 | + (instancetype) sharedBinder;
25 |
26 | /**
27 | The underlying shortcut monitor.
28 | */
29 | @property(strong) MASShortcutMonitor *shortcutMonitor;
30 |
31 | /**
32 | Binding options customizing the access to user defaults.
33 |
34 | As an example, you can use `NSValueTransformerNameBindingOption` to customize
35 | the storage format used for the shortcuts. By default the shortcuts are converted
36 | from `NSData` (`NSKeyedUnarchiveFromDataTransformerName`). Note that if the
37 | binder is to work with `MASShortcutView`, both object have to use the same storage
38 | format.
39 | */
40 | @property(copy) NSDictionary *bindingOptions;
41 |
42 | /**
43 | Binds given action to a shortcut stored under the given defaults key.
44 |
45 | In other words, no matter what shortcut you store under the given key,
46 | pressing it will always trigger the given action.
47 | */
48 | - (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action;
49 |
50 | /**
51 | Disconnect the binding between user defaults and action.
52 |
53 | In other words, the shortcut stored under the given key will no longer trigger an action.
54 | */
55 | - (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName;
56 |
57 | /**
58 | Register default shortcuts in user defaults.
59 |
60 | This is a convenience frontent to `[NSUserDefaults registerDefaults]`.
61 | The dictionary should contain a map of user defaults’ keys to appropriate
62 | keyboard shortcuts. The shortcuts will be transformed according to
63 | `bindingOptions` and registered using `registerDefaults`.
64 | */
65 | - (void) registerDefaultShortcuts: (NSDictionary*) defaultShortcuts;
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcutMonitor.h:
--------------------------------------------------------------------------------
1 | #import "MASShortcut.h"
2 |
3 | /**
4 | Executes action when a shortcut is pressed.
5 |
6 | There can only be one instance of this class, otherwise things
7 | will probably not work. (There’s a Carbon event handler inside
8 | and there can only be one Carbon event handler of a given type.)
9 | */
10 | @interface MASShortcutMonitor : NSObject
11 |
12 | - (instancetype) init __unavailable;
13 | + (instancetype) sharedMonitor;
14 |
15 | /**
16 | Register a shortcut along with an action.
17 |
18 | Attempting to insert an already registered shortcut probably won’t work.
19 | It may burn your house or cut your fingers. You have been warned.
20 | */
21 | - (BOOL) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action;
22 | - (BOOL) isShortcutRegistered: (MASShortcut*) shortcut;
23 |
24 | - (void) unregisterShortcut: (MASShortcut*) shortcut;
25 | - (void) unregisterAllShortcuts;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcutValidator.h:
--------------------------------------------------------------------------------
1 | #import "MASShortcut.h"
2 |
3 | /**
4 | This class is used by the recording control to tell which shortcuts are acceptable.
5 |
6 | There are two kinds of shortcuts that are not considered acceptable: shortcuts that
7 | are too simple (like single letter keys) and shortcuts that are already used by the
8 | operating system.
9 | */
10 | @interface MASShortcutValidator : NSObject
11 |
12 | /**
13 | Set to `YES` if you want to accept Option-something shortcuts.
14 |
15 | `NO` by default, since Option-something shortcuts are often used by system,
16 | for example Option-G will type the © sign. This also applies to Option-Shift
17 | shortcuts – in other words, shortcut recorder will not accept shortcuts like
18 | Option-Shift-K by default. (Again, since Option-Shift-K inserts the Apple
19 | logo sign by default.)
20 | */
21 | @property(assign) BOOL allowAnyShortcutWithOptionModifier;
22 |
23 | + (instancetype) sharedValidator;
24 |
25 | - (BOOL) isShortcutValid: (MASShortcut*) shortcut;
26 | - (BOOL) isShortcut: (MASShortcut*) shortcut alreadyTakenInMenu: (NSMenu*) menu explanation: (NSString**) explanation;
27 | - (BOOL) isShortcutAlreadyTakenBySystem: (MASShortcut*) shortcut explanation: (NSString**) explanation;
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcutView+Bindings.h:
--------------------------------------------------------------------------------
1 | #import "MASShortcutView.h"
2 |
3 | /**
4 | A simplified interface to bind the recorder value to user defaults.
5 |
6 | You can bind the `shortcutValue` to user defaults using the standard
7 | `bind:toObject:withKeyPath:options:` call, but since that’s a lot to type
8 | and read, here’s a simpler option.
9 |
10 | Setting the `associatedUserDefaultsKey` binds the view’s shortcut value
11 | to the given user defaults key. You can supply a value transformer to convert
12 | values between user defaults and `MASShortcut`. If you don’t supply
13 | a transformer, the `NSUnarchiveFromDataTransformerName` will be used
14 | automatically.
15 |
16 | Set `associatedUserDefaultsKey` to `nil` to disconnect the binding.
17 | */
18 | @interface MASShortcutView (Bindings)
19 |
20 | @property(copy) NSString *associatedUserDefaultsKey;
21 |
22 | - (void) setAssociatedUserDefaultsKey: (NSString*) newKey withTransformer: (NSValueTransformer*) transformer;
23 | - (void) setAssociatedUserDefaultsKey: (NSString*) newKey withTransformerName: (NSString*) transformerName;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/MASShortcutView.h:
--------------------------------------------------------------------------------
1 | @class MASShortcut, MASShortcutValidator;
2 |
3 | extern NSString *const MASShortcutBinding;
4 |
5 | typedef enum {
6 | MASShortcutViewStyleDefault = 0, // Height = 19 px
7 | MASShortcutViewStyleTexturedRect, // Height = 25 px
8 | MASShortcutViewStyleRounded, // Height = 43 px
9 | MASShortcutViewStyleFlat
10 | } MASShortcutViewStyle;
11 |
12 | @interface MASShortcutView : NSView
13 |
14 | @property (nonatomic, strong) MASShortcut *shortcutValue;
15 | @property (nonatomic, strong) MASShortcutValidator *shortcutValidator;
16 | @property (nonatomic, getter = isRecording) BOOL recording;
17 | @property (nonatomic, getter = isEnabled) BOOL enabled;
18 | @property (nonatomic, copy) void (^shortcutValueChange)(MASShortcutView *sender);
19 | @property (nonatomic, assign) MASShortcutViewStyle style;
20 |
21 | /// Returns custom class for drawing control.
22 | + (Class)shortcutCellClass;
23 |
24 | - (void)setAcceptsFirstResponder:(BOOL)value;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Headers/Shortcut.h:
--------------------------------------------------------------------------------
1 | #import "MASKeyMasks.h"
2 | #import "MASShortcut.h"
3 | #import "MASShortcutValidator.h"
4 | #import "MASShortcutMonitor.h"
5 | #import "MASShortcutBinder.h"
6 | #import "MASDictionaryTransformer.h"
7 | #import "MASShortcutView.h"
8 | #import "MASShortcutView+Bindings.h"
9 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/MASShortcut:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/Versions/A/MASShortcut
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module MASShortcut {
2 | umbrella header "MASShortcut-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 17A405
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | MASShortcut
11 | CFBundleIdentifier
12 | org.cocoapods.MASShortcut
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | MASShortcut
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 2.3.6
21 | CFBundleSignature
22 | ????
23 | CFBundleSupportedPlatforms
24 |
25 | MacOSX
26 |
27 | CFBundleVersion
28 | 1
29 | DTCompiler
30 | com.apple.compilers.llvm.clang.1_0
31 | DTPlatformBuild
32 | 9A235
33 | DTPlatformVersion
34 | GM
35 | DTSDKBuild
36 | 17A360
37 | DTSDKName
38 | macosx10.13
39 | DTXcode
40 | 0900
41 | DTXcodeBuild
42 | 9A235
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 17A405
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleIdentifier
10 | org.cocoapods.MASShortcut
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | MASShortcut
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 2.3.6
19 | CFBundleSignature
20 | ????
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 9A235
31 | DTPlatformVersion
32 | GM
33 | DTSDKBuild
34 | 17A360
35 | DTSDKName
36 | macosx10.13
37 | DTXcode
38 | 0900
39 | DTXcodeBuild
40 | 9A235
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/cs.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Zrušit";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Kliknutím nahrajete novou zkratku";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Smazat zkratku";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "klávesová zkratka";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Nahrát zkratku";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "zkratka smazána";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "zkratka nastavena";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Mezerník";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "Kombinace %@ se nedá použít";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Tato zkratka se nedá použít, protože už ji obsadil systém.\nKdybyste na ní trvali, většina systémových zkratek se dá přenastavit v Předvolbách systému.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Tato zkratka se nedá použít, protože už je použita pro menu (%@).";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Pokud chcete nahrát novou zkratku, stiskněte toto tlačítko a následně vybranou zkratku. Stisknutím Delete vymažete stávající zkratku.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Stiskněte zkratku";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Stiskněte zkratku";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Vrátit se k původní";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/de.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Abbrechen";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Klicken um neuen Kurzbefehl aufzunehmen";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Kurzbefehl Löschen";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "Kurzbefehl";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Kurzbefehl aufnehmen";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Kurzbefehl gelöscht";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Kurzbefehl gesetzt";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Leertaste";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "Die Tastenkombination %@ kann nicht genutzt werden";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Diese Kombination kann nicht genutzt werden, weil sie bereits als systemweiter Kurzbefehl genutzt wird.\nFalls du diese Tastenkombination wirklich benutzen willst, können die meisten Kurzbefehle in den Tastatur Systemeinstellungen geändert werden.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Dieser Kurzbefehl kann nicht genutzt werden, weil er bereits vom Menüpunkt „%@“ genutzt wird.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Drücke diesen Button, um einen neuen Kurzbefehl aufzunehmen. Tippe dann den neuen Kurzbefehl oder drücke Löschen, um den aktuellen Kurzbefehl zu löschen.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Neuen eingeben";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Kurzbefehl eingeben";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Alten nutzen";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Cancel";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Click to record new shortcut";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Delete shortcut";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "keyboard shortcut";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Record Shortcut";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Shortcut cleared";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Shortcut set";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Space";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "The key combination %@ cannot be used";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "This shortcut cannot be used because it is already used by the menu item ‘%@’.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Type New Shortcut";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Type Shortcut";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Use Old Shortcut";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/es.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Cancelar";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Haga clic para grabar nuevo atajo";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Borrar atajo";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "atajo de teklado";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Grabar atajo";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Atajo borrado";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Atajo creado";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Espacio";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "La combinación de claves %@ no se puede utilizada";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Esta combinación no se puede utilizar debido a que ya es en us como atajo del sistema.\nSi realmente desea utilizar esta combinación de teclas, la mayoría de los atajos se puede cambiar en el panel de Teclado y Ratón de Preferencias del Sistema.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Este atajo no se puede utilizar debido a que ya es utilizado por el elemento de menú '%@'.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Para grabar un nuevo atajo, haga clic en este botón, a continuar, escriba el nuevo atajo, o pulse borrar para qutar un atajo existente.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Escribir atajo";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Escribir atajo";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Usa atajo anterior";
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/fr.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Annuler";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Cliquez pour enregistrer le raccourci";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Supprimer le raccourci";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "raccourci clavier";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Enregistrer le raccourci";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Raccourci supprimé";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Raccourci configuré";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Espace";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "La combinaison %@ ne peut être utilisée";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Cette combinaison de touches ne peut être utilisée parce qu’elle est réservée pour un raccourci du système.\nSi vous désirez l’utiliser, la plupart des raccourcis peuvent être modifiés dans l’onglet Clavier, dans Préférences Système.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Ce raccourci ne peut être utilisé parce qu’il est déjà utilisé par le point de menu «%@».";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Pour enregistrer un nouveau raccourci, cliquez sur ce bouton et tapez le nouveau raccourci, ou bien, tapez sur «Supprimer» pour supprimer le raccourci configuré.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Saisir un raccourci";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Saisir un raccourci";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Revenir au raccourci précédent";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/it.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Annulla";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Cliccare per registrare una nuova combinazione";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Cancella scorciatoia";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "Scorciatoia da tastiera";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Registra scorciatoia";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Scorciatoia rimossa";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Scorciatoia impostata";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Spazio";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "Questa combinazione %@ di tasti non può essere usata";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Questa combinazione di tasti non può essere usata perchè è già usata da una scorciatoia da tastiera a livello di Sistema.\nSe volete davvero usare questa combinazione di tasti, la maggior parte delle scorciatoie possono essere cambiate nel pannello Tastiera e Mouse delle Preferenze di Sistema.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Questa combinazione di tasti non può essere usata perchè è già usata dalla voce di menù ‘%@’.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Per registrare una nuova scorciatoia, cliccare su questo pulsante e poi inserire la muova scorciatoia o premere cancella per resettare una scorciatoia esistente.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Digita nuova";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Digita scorciatoia";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Usare vecchia";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/ja.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "キャンセルする";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "クリックしてショートカットを入力";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "ショートカットを削除";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "キーボードショートカット";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "ショートカットを入力";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "ショートカットが削除されました";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "ショートカットが設定されました";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "スペース";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "%@ はショートカットに設定できません";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "このショートカットは、システム全体で使用されているショートカットのため、設定することができません。\nもしこのショートカットを使用したい場合、「システム環境設定」の「キーボード」、「マウス」のパネルから既に設定されているショートカットを変更してください。";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "このショートカットは、メニュー操作の「%@」で使われているため、設定できません。";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "このボタンをクリックし、ショートカットを入力すると、新しいショートカットが設定されます。また、削除ボタンをおすと、ショートカットが削除されます。";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "ショートカットを入力";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "ショートカットを入力";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "古いショートカットを使用";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/ko.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "취소";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "클릭해 단축 키를 입력";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "단축키 삭제";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "키보드 단축키";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "좋아";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "단축키 입력";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "단축키 삭제됨";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "단축키 설정됨";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "스페이스 바";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "%@ 단축키로 설정할 수 없습니다";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "이 결합은 시스템 전체에서 사용 때문에 단축키로 설정 할 수 없습니다.\n단축키를 사용하고 싶으면 시스템 환경 설정의 키보드, 마우스 패널에서 이미 설정되어있는 단축키를 변경하십시오.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "이 단축키는 ‘%@’ 메뉴 아이템에 사용되고 있기 때문에 설정할 수 없습니다.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "이 버튼을 클릭하고 단축키를 입력하면 새로운 단축키가 설정됩니다. 또한 삭제 버튼을 누르면 단축키가 삭제됩니다.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "새 단축키 입력";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "단축키 입력";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "오래된 단축키를 사용";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/nl.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Verbreken";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Druk om een nieuwe sneltoets in te voeren";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Verwijder sneltoets";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "sneltoets";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Sneltoets opnemen";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Sneltoets verwijderd";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Sneltoets zetten";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Spatie";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "De sneltoetsencombinatie kan niet worden gebruikt";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Deze combinatie kan niet worden gebruikt want hij wordt al gebruikt door een systeembreed sneltoets.\nAls je deze combinatie echt wilt gebruiken, kun je de meeste sneltoetsen binnen Toetsenbordinstellingen veranderen.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Deze sneltoets kan niet worden gebruikt want hij wordt al gebruikt door het menu item ‘%@’.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Om nieuwe sneltoets op te nemen, druk op deze knop, en voer een nieuwe sneltoets in, of druk op verwijder om bestaande sneltoets te verwijderen.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Voer Nieuwe Sneltoets in";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Voer Sneltoets in";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Gebruik Oude Sneltoets";
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/pl.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Anuluj";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Kliknij, by ustawić nowy skrót";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Usuń skrót";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "skrót klawiszowy";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "OK";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Utwórz skrót";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Skrót usunięty";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Skrót ustawiony";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Spacja";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "Nie można użyć kombinacji klawiszy %@";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Nie można użyć tej kombinacji, ponieważ jest już zajęta przez skrót systemowy.\nMożesz to zmienić w panelu Klawiatura w Preferencjach systemowych.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Ten skrót nie może być użyty, ponieważ w menu ma już przypisaną funkcję ‘%@’.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Aby ustawić nowy skrót, użyj tego przycisku, a następnie wpisz nowy skrót albo naciśnij klawisz delete, by usunąć istniejący skrót";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Wpisz nowy skrót";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Wpisz skrót";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Użyj starego skrótu";
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/ru.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "Отмена";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "Нажмите для записи сочетания клавиш";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "Удалить горячую клавишу";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "сочетание клавиш";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "ОК";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "Ввести сочетание";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "Сочетание клавиш удалено";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "Сочетание клавиш назначено";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "Пробел";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "Нельзя использовать сочетание клавиш %@";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "Нельзя использовать это сочетание клавиш, потому что оно уже используется в системе.\n Если вы хотите использовать это сочетание, измените существующее системное сочетание клавиш через панель Клавиатура в Cистемных настройках.";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "Нельзя использовать это сочетание, потому что оно уже связано с элементом ‘%@’.";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "Чтобы назначить новое сочетание клавиш, нажмите эту кнопку и введите новое сочетание, или нажмите \"Удалить\", чтобы удалить действующее сочетание клавиш.";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "Введите сочетание";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "Введите сочетание";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "Вернуть старое";
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "取消";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "点击以记录新快捷键";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "删除快捷键";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "键盘快捷键";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "好";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "记录快捷键";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "快捷键已清除";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "快捷键已设置";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "空格键";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "按键组合“%@”无法使用";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "当前按键组合无法使用,因为它已经用作其他系统全局快捷键。\n如果您真的想使用这个按键组合,大部分系统全局快捷键能在“系统偏好设置”里的“键盘”和“鼠标”面板中重设。";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "当前快捷键无法使用,因为它已用作菜单项“%@”的快捷键。";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "若要记录新的快捷键,单击此按钮,然后键入新的快捷键,或者按“delete键”删除已经存在的快捷键。";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "键入新快捷键";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "键入快捷键";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "还原快捷键";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/Resources/zh-Hant.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* Cancel action button in recording state */
2 | "Cancel" = "取消";
3 |
4 | /* Tooltip for non-empty shortcut button */
5 | "Click to record new shortcut" = "點選以記錄新快捷鍵";
6 |
7 | /* Tooltip for hint button near the non-empty shortcut */
8 | "Delete shortcut" = "刪除快捷鍵";
9 |
10 | /* VoiceOver title */
11 | "keyboard shortcut" = "鍵盤快捷鍵";
12 |
13 | /* Alert button when shortcut is already used */
14 | "OK" = "好";
15 |
16 | /* Empty shortcut button in normal state */
17 | "Record Shortcut" = "記錄快捷鍵";
18 |
19 | /* VoiceOver: Shortcut cleared */
20 | "Shortcut cleared" = "快捷鍵已清除";
21 |
22 | /* VoiceOver: Shortcut set */
23 | "Shortcut set" = "快捷鍵已設定";
24 |
25 | /* Shortcut glyph name for SPACE key */
26 | "Space" = "空格鍵";
27 |
28 | /* Title for alert when shortcut is already used */
29 | "The key combination %@ cannot be used" = "按鍵組合“%@”無法使用";
30 |
31 | /* Message for alert when shortcut is already used by the system */
32 | "This combination cannot be used because it is already used by a system-wide keyboard shortcut.\nIf you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences." = "當前按鍵組合無法使用,因為它已經用作其他系統全局快捷鍵。\n如果您真的想使用這個按鍵組合,大部分系統全局快捷鍵能在“系統偏好設定”裡的“鍵盤”和“滑鼠”面板中重設。";
33 |
34 | /* Message for alert when shortcut is already used */
35 | "This shortcut cannot be used because it is already used by the menu item ‘%@’." = "當前快捷鍵無法使用,因為它已用作選單項“%@”的快捷鍵。";
36 |
37 | /* VoiceOver shortcut help */
38 | "To record a new shortcut, click this button, and then type the new shortcut, or press delete to clear an existing shortcut." = "若要記錄新的快捷鍵,單擊此按鈕,然後鍵入新的快捷鍵,或者按“delete鍵”刪除已經存在的快捷鍵。";
39 |
40 | /* Non-empty shortcut button in recording state */
41 | "Type New Shortcut" = "鍵入新快捷鍵";
42 |
43 | /* Empty shortcut button in recording state */
44 | "Type Shortcut" = "鍵入快捷鍵";
45 |
46 | /* Cancel action button for non-empty shortcut in recording state */
47 | "Use Old Shortcut" = "還原快捷鍵";
48 |
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeDirectory:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeDirectory
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeRequirements:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeRequirements
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeRequirements-1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeRequirements-1
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeSignature:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exevil/Keys-For-Sketch/dfbc9ac3d67579802516b2a27da39dcec12e7306/Source/Support/MASShortcut.framework/Versions/A/Resources/MASShortcut.bundle/Contents/_CodeSignature/CodeSignature
--------------------------------------------------------------------------------
/Source/Support/MASShortcut.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/Source/Support/NSAlert+Additions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSAlert+additions.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 09/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | extension NSAlert {
10 | /**
11 | Display an alert sheet with given parameters and execute closure associated with pressed button. Buttons will shown on `NSAlert` in the same order in order that provides orderedButtonTitles array
12 |
13 | - parameters:
14 | - window: An NSWindow instance new sheet should be attached to. While nil value is passed method will firstly try to attach alert to NSApplication's mainWindow but if it's nil too alert will be shown separately using `runModal()` method.
15 | - style: 'NSAlert''s alertSytle.
16 | - icon: 'NSAlert''s icon.
17 | - messageText: `NSAlert's` messageText.
18 | - informativeText: `NSAlert's` informativeText.
19 | - orderedButtonTitles: Titles of alert buttons ordered from first to last one.
20 | - completionHandler: Closure with pressed button number as a parameter. Button number should correspond the button order in `orderedButtonTitles` parameter's array.
21 | */
22 | class func showAlert(over window: NSWindow? = nil,
23 | style: NSAlert.Style = .informational,
24 | icon: NSImage? = image(Const.Image.keysIcon),
25 | messageText: String,
26 | informativeText: String,
27 | orderedButtonTitles: [String],
28 | completionHandler: ((_ pressedButtonNumber: Int) -> Void)?
29 | ) {
30 |
31 | // Make alert
32 | let alert = NSAlert()
33 | alert.alertStyle = style
34 | alert.icon = icon ?? alert.icon
35 | alert.messageText = messageText
36 | alert.informativeText = informativeText
37 | orderedButtonTitles.forEach {
38 | alert.addButton(withTitle: $0)
39 | }
40 |
41 | // Handle completion handler
42 | let alertCompletionHandler: (NSApplication.ModalResponse) -> () = { response in
43 | // Responeded buttons have incremental (by 1) indexes started from 1000
44 | let buttonNumber = response.rawValue - 1000
45 | completionHandler?(buttonNumber)
46 | }
47 |
48 | // Attach to window if possible and needed
49 | if let win = window {
50 | alert.beginSheetModal(for: win, completionHandler: alertCompletionHandler)
51 | } else {
52 | alertCompletionHandler(alert.runModal())
53 | }
54 | }
55 |
56 | /**
57 | `showAlert` method shorthand for quick presentation of error `localizedDescription` as part of message text.
58 |
59 | - important: Descriptions of other parameters can be found in `showAlert` method.
60 |
61 | - parameters:
62 | - error: Error instance which `localizedDescription` should be used in alert.
63 | - underErrorText: Additional text that should be placed right after an error message.
64 | */
65 | class func showKeysErrorAlert(over window: NSWindow? = nil,
66 | style: NSAlert.Style = NSAlert.Style.critical,
67 | icon: NSImage? = image(Const.Image.keysIcon),
68 | messageText: String = "Keys For Sketch Error Occurred",
69 | error: Error,
70 | underErrorText: String = "Make sure you running latest versions of Keys and Sketch and contact developer if problem still occurs.",
71 | orderedButtonTitles: [String] = ["OK"],
72 | completionHandler: ((_ pressedButtonNumber: Int) -> Void)? = nil) {
73 |
74 | showAlert(over: window,
75 | style: style,
76 | icon: icon,
77 | messageText: messageText,
78 | informativeText: "\(error.localizedDescription)\n· · ·\n\(underErrorText)",
79 | orderedButtonTitles: orderedButtonTitles,
80 | completionHandler: completionHandler)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Source/Support/UIInjector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIInjector.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 15/09/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | class UIInjector: NSObject {
10 |
11 | /// Shared instance of MSPreferencesController.
12 | @objc static let pc = VDKSketchAPI.preferencesController()
13 |
14 | /// Add Keys Panel to Sketch Preferences.
15 | class func injectKeysPanel() throws {
16 | pc.loadWindow()
17 |
18 | // Make sure that all used properties are exists.
19 | guard let toolbar = pc.toolbar else {
20 | throw Error.prefsControllerPropertyIsNil(propertyKeyPath: #keyPath(pc.toolbar))
21 | }
22 | guard var toolbarItemIdentifiers = pc.toolbarItemIdentifiers else {
23 | throw Error.prefsControllerPropertyIsNil(propertyKeyPath: #keyPath(pc.toolbarItemIdentifiers))
24 | }
25 |
26 | // Register and insert Keys toolbar item into Preferences window toolbar.
27 | pc.preferencePaneClasses[Const.Preferences.keysIdentifier] = ViewController.self
28 |
29 | toolbarItemIdentifiers.append(Const.Preferences.keysIdentifier)
30 | pc.toolbarItemIdentifiers = toolbarItemIdentifiers
31 |
32 | toolbar.insertItem(withItemIdentifier: Const.Preferences.keysIdentifier, at: toolbar.items.count)
33 |
34 | // Make sure that last closed tab is selected again
35 | if pc.toolbar?.selectedItemIdentifier == nil {
36 | pc.switchToPane(withIdentifier: toolbarItemIdentifiers[Int(pc.selectedTabIndex)])
37 | }
38 |
39 | // Hide Preferences window.
40 | pc.close()
41 | }
42 |
43 | enum Error: LocalizedError {
44 |
45 | case prefsControllerPropertyIsNil(propertyKeyPath: String)
46 |
47 | var errorDescription: String? {
48 | switch self {
49 | case .prefsControllerPropertyIsNil(let propertyKeyPath):
50 | return "MSPreferencesController property \"\(propertyKeyPath)\" is unexpectedly nil."
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Source/Update/FileObserver.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileObserver.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 29/06/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | // FileObserver observes changes made in plugin file. If user or Sketch modifying it (on update or reinstall, for instance) an alert asking to Restart Sketch should appear.
10 | public class FileObserver: NSObject {
11 |
12 | @objc public static let shared = FileObserver()
13 | /// https://github.com/soh335/FileWatch
14 | var fileWatch: FileWatch?
15 |
16 | @objc public func startObserving() {
17 | fileWatch = try! FileWatch(paths: [Keys.pluginPath],
18 | createFlag: [.UseCFTypes, .FileEvents],
19 | runLoop: RunLoop.current,
20 | latency: 0.5,
21 | eventHandler: { event in
22 |
23 | inDebug {
24 | print("""
25 |
26 | \(event.path)
27 |
28 | == pluginURL: \(event.path == Keys.pluginPath)
29 | .ItemCreated: \(event.flag.contains(.ItemCreated))
30 | .ItemRemoved: \(event.flag.contains(.ItemRemoved))
31 | .ItemInodeMetaMod: \(event.flag.contains(.ItemInodeMetaMod))
32 | .ItemRenamed: \(event.flag.contains(.ItemRenamed))
33 | .ItemModified: \(event.flag.contains(.ItemModified))
34 | .ItemFinderInfoMod: \(event.flag.contains(.ItemFinderInfoMod))
35 | .ItemChangeOwner: \(event.flag.contains(.ItemChangeOwner))
36 | .ItemXattrMod: \(event.flag.contains(.ItemXattrMod))
37 |
38 | """)
39 | }
40 |
41 | if
42 | event.flag.contains(.ItemModified) // Manual Plugin Installation
43 | || event.path == Keys.pluginPath && event.flag.contains(.ItemRenamed) // Automatic update by Sketch
44 |
45 | {
46 | // Most of changes should appear through latency period but to make sure that all files passed, delay displaying of update alert until all files will be copied.
47 | self.cancelPreviousPerformRequiestsAndPerform(#selector(self.fileChanged), with: nil, afterDelay: 0.75)
48 | }
49 | })
50 | }
51 |
52 | @objc func fileChanged() {
53 | let alert = UpdateCompletionAlert()
54 | alert.completionHandler(with: alert.runModal())
55 | }
56 | }
57 |
58 | extension NSObject {
59 | /// Cancel previous perform requests with given arguments for object and register new one
60 | /// - note: Useful when you need to run a selector after a bunch of repetitive actions. Each action should cancel previous perform request and register a new delayed request afterwards. Selector will perform after the last action registers its perform request and its delay time will expire.
61 | func cancelPreviousPerformRequiestsAndPerform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval) {
62 | NSObject.cancelPreviousPerformRequests(withTarget: self, selector: aSelector, object: anArgument)
63 | self.perform(aSelector, with: anArgument, afterDelay: delay)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Source/Update/UpdateCompletionAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UpdateCompleteAlert.swift
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 19/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | public class UpdateCompletionAlert: NSAlert {
10 |
11 | override init() {
12 | super.init()
13 | messageText = "Keys Were Installed Sucessfully"
14 | informativeText = "Please restart Sketch to finish the instalation process."
15 | icon = image(Const.Image.keysIcon)
16 |
17 | addButton(withTitle: "Restart Sketch")
18 | addButton(withTitle: "Later")
19 | }
20 |
21 | /// Default completion handler for alert.
22 | @objc public func completionHandler(with response: NSApplication.ModalResponse) {
23 | if response == .alertFirstButtonReturn {
24 | // Relaunch Sketch
25 | let task = Process()
26 | task.launchPath = "/bin/sh"
27 | task.arguments = ["-c", "sleep \(0.5); open \"\(Bundle.main.bundlePath)\""]
28 | task.launch()
29 | NSApp.terminate(nil)
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Source/VDKBridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // VDKeysBridgingHeader.h
3 | // KeysForSketch
4 | //
5 | // Created by Vyacheslav Dubovitsky on 15/03/2017.
6 | // Copyright © 2017 Vyacheslav Dubovitsky. All rights reserved.
7 | //
8 |
9 | @import MASShortcut;
10 | #import "VDKeys.h"
11 | #import "VDKSketchAPI.h"
12 | #import "VDK_JRSwizzle.h"
13 | #import "NSMenuItem+Private.h"
14 |
--------------------------------------------------------------------------------