├── Website ├── img │ └── icon.png ├── index.html └── style.css ├── Assets ├── q-blocker-icon.sketch ├── qblocker-icons │ ├── 128.png │ ├── 16.png │ ├── 256.png │ ├── 32.png │ ├── 512.png │ ├── 64.png │ ├── 1024.png │ └── github.png └── screenshots │ └── preferences.png ├── QBlocker ├── OpenPreferences.scpt ├── Assets.xcassets │ ├── Contents.json │ ├── Cog.imageset │ │ ├── Cog.pdf │ │ └── Contents.json │ ├── Rules.imageset │ │ ├── Rules.pdf │ │ └── Contents.json │ ├── Tick.imageset │ │ ├── Tick.png │ │ ├── Tick@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 512.png │ │ ├── 64.png │ │ ├── 1024.png │ │ ├── 256-1.png │ │ ├── 32-1.png │ │ ├── 512-1.png │ │ └── Contents.json │ ├── Menu Bar.imageset │ │ ├── Menu Bar.pdf │ │ └── Contents.json │ └── settings.imageset │ │ ├── Accessibility.png │ │ ├── Accessibility@2x.png │ │ └── Contents.json ├── Bridge.h ├── KeyListenerError.swift ├── ExcludeViewController + NSTableViewDataSource.swift ├── HUDView.swift ├── ExcludeWindowController.swift ├── App.swift ├── ListMode.swift ├── FirstRunViewController.swift ├── TabBarController.swift ├── AccessibilityWindowController.swift ├── ExcludeViewController + NSTableViewDelegate.swift ├── AccessibilityViewController.swift ├── KeyListener + Realm.swift ├── Info.plist ├── Delay.swift ├── StatusMenuController.swift ├── ExcludeViewController.swift ├── HUDAlert.swift ├── AtLogin.swift ├── AppDelegate.swift ├── KeyListener.swift └── Base.lproj │ └── Main.storyboard ├── Podfile ├── QBlocker.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── QBlocker.xcworkspace └── contents.xcworkspacedata ├── Podfile.lock ├── CHANGELOG.md ├── README.md └── .gitignore /Website/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Website/img/icon.png -------------------------------------------------------------------------------- /Assets/q-blocker-icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/q-blocker-icon.sketch -------------------------------------------------------------------------------- /Assets/qblocker-icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/128.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/16.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/256.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/32.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/512.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/64.png -------------------------------------------------------------------------------- /QBlocker/OpenPreferences.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/OpenPreferences.scpt -------------------------------------------------------------------------------- /Assets/qblocker-icons/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/1024.png -------------------------------------------------------------------------------- /Assets/qblocker-icons/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/qblocker-icons/github.png -------------------------------------------------------------------------------- /Assets/screenshots/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/Assets/screenshots/preferences.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Cog.imageset/Cog.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/Cog.imageset/Cog.pdf -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Rules.imageset/Rules.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/Rules.imageset/Rules.pdf -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Tick.imageset/Tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/Tick.imageset/Tick.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Tick.imageset/Tick@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/Tick.imageset/Tick@2x.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/256-1.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/32-1.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/AppIcon.appiconset/512-1.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Menu Bar.imageset/Menu Bar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/Menu Bar.imageset/Menu Bar.pdf -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.10' 2 | use_frameworks! 3 | 4 | target 'QBlocker' do 5 | pod 'SRTabBarController' 6 | pod 'DevMateKit' 7 | pod 'RealmSwift' 8 | end 9 | -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/settings.imageset/Accessibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/settings.imageset/Accessibility.png -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/settings.imageset/Accessibility@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve228uk/QBlocker/HEAD/QBlocker/Assets.xcassets/settings.imageset/Accessibility@2x.png -------------------------------------------------------------------------------- /QBlocker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Menu Bar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Menu Bar.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /QBlocker.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /QBlocker/Bridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // Bridge.h 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 04/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | #ifndef Bridge_h 10 | #define Bridge_h 11 | 12 | #import 13 | 14 | #endif /* Bridge_h */ 15 | -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Cog.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Cog.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Rules.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Rules.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /QBlocker/KeyListenerError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyListenerError.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 03/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum KeyListenerError: ErrorType { 12 | 13 | case AccessibilityPermissionDenied 14 | 15 | } -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/Tick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Tick.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "filename" : "Tick@2x.png", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "Accessibility.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "filename" : "Accessibility@2x.png", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /QBlocker/ExcludeViewController + NSTableViewDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExcludeViewController + NSTableViewDataSource.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension ExcludeViewController: NSTableViewDataSource { 12 | 13 | func numberOfRowsInTableView(tableView: NSTableView) -> Int { 14 | return KeyListener.sharedKeyListener.list?.count ?? 0 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /QBlocker/HUDView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HUDView.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 03/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class HUDView: NSView { 12 | 13 | override func drawRect(dirtyRect: NSRect) { 14 | super.drawRect(dirtyRect) 15 | 16 | wantsLayer = true 17 | layer?.backgroundColor = NSColor(deviceHue: 0, saturation: 0, brightness: 0, alpha: 0.5).CGColor 18 | layer?.cornerRadius = 12 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /QBlocker/ExcludeWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExcludeWindowController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ExcludeWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | window?.titlebarAppearsTransparent = true 17 | window?.titleVisibility = .Hidden 18 | window?.movableByWindowBackground = true 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /QBlocker/App.swift: -------------------------------------------------------------------------------- 1 | // 2 | // App.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | 11 | class App: Object { 12 | 13 | /// The name of the app that will be displayed as a label 14 | dynamic var name = "" 15 | 16 | /// The bundle ID of the app. e.g. uk.co.wearecocoon.QBlocker 17 | dynamic var bundleID = "" 18 | 19 | override static func primaryKey() -> String? { 20 | return "bundleID" 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /QBlocker/ListMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListMode.swift 3 | // QBlocker 4 | // 5 | // Created by Florian Schliep on 25.05.16. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | enum ListMode: Int { 10 | case Blacklist = 0 11 | case Whitelist = 1 12 | 13 | static var selectedMode: ListMode { 14 | return ListMode(rawValue: NSUserDefaults.standardUserDefaults().integerForKey("listMode")) ?? .Whitelist 15 | } 16 | 17 | func select() { 18 | NSUserDefaults.standardUserDefaults().setInteger(self.rawValue, forKey: "listMode") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DevMateKit (1.7.1) 3 | - Realm (0.101.0): 4 | - Realm/Headers (= 0.101.0) 5 | - Realm/Headers (0.101.0) 6 | - RealmSwift (0.101.0): 7 | - Realm (= 0.101.0) 8 | - SRTabBarController (0.1.0) 9 | 10 | DEPENDENCIES: 11 | - DevMateKit 12 | - RealmSwift 13 | - SRTabBarController 14 | 15 | SPEC CHECKSUMS: 16 | DevMateKit: 3ba77083f5faa40266e0d2d70776de2a82c71dd0 17 | Realm: b21596d6d7e1c054f6fae967d27263d2593ca309 18 | RealmSwift: 5247501bcd68b9db3a718cba808e1ce45ba67034 19 | SRTabBarController: aeb73a975a47aff9e14f3546da2394fce8271b09 20 | 21 | COCOAPODS: 0.39.0 22 | -------------------------------------------------------------------------------- /QBlocker/FirstRunViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstRunViewController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class FirstRunViewController: NSViewController { 12 | 13 | @IBAction func dismissWindow(sender: AnyObject) { 14 | view.window?.orderOut(self) 15 | } 16 | 17 | @IBAction func showExcludeApps(sender: AnyObject) { 18 | AppDelegate.sharedDelegate?.showPreferencesWindow() 19 | view.window?.orderOut(self) 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /QBlocker/TabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 29/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SRTabBarController 11 | 12 | class TabBarController: SRTabBarController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | tabBarLocation = .Top 18 | 19 | tabBar?.material = .Titlebar 20 | tabBar?.blendingMode = .WithinWindow 21 | tabBar?.translucent = true 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /QBlocker/AccessibilityWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccessibilityWindowController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 05/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AccessibilityWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | window?.level = Int(CGWindowLevelForKey(CGWindowLevelKey.PopUpMenuWindowLevelKey)) 17 | window?.titlebarAppearsTransparent = true 18 | window?.backgroundColor = NSColor(calibratedHue:0.00, saturation:0.00, brightness:0.90, alpha:1.00) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.2 4 | 5 | - Adds ability to change the length of delay before an app quits. 6 | 7 | ## 1.1 8 | 9 | - Adds ability to exclude apps or only use QBlocker with some apps. Thanks [@floschliep](https://github.com/floschliep)! 10 | - Fixes Exclude Apps window not appearing at front. Thanks [@floschliep](https://github.com/floschliep)! 11 | - Fixes QBlocker stopping the CMD + Shift + Q logout from working 12 | 13 | ## 1.0.2 14 | 15 | - Fixes an issue that caused QBlocker to stop working on non-QWERTY keyboards. 16 | 17 | ## 1.0.1 18 | 19 | - Fixes an issue where an excluded app behind a non-excluded app would also quit when holding CMD + Q 20 | 21 | ## 1.0 22 | 23 | - Initial release 24 | -------------------------------------------------------------------------------- /QBlocker/ExcludeViewController + NSTableViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExcludeViewController + NSTableViewDelegate.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension ExcludeViewController: NSTableViewDelegate { 12 | 13 | func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? { 14 | 15 | guard let cell = tableView.makeViewWithIdentifier("app name cell", owner: nil) as? NSTableCellView, 16 | let app = KeyListener.sharedKeyListener.list?[row] else { 17 | return nil 18 | } 19 | 20 | cell.textField?.stringValue = app.name 21 | return cell 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /QBlocker/AccessibilityViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccessibilityViewController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 05/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AccessibilityViewController: NSViewController { 12 | 13 | @IBAction func openPreferences(sender: AnyObject) { 14 | 15 | guard let scriptPath = NSBundle.mainBundle().pathForResource("OpenPreferences", ofType: "scpt") else { 16 | print("Could not find applescript") 17 | return 18 | } 19 | 20 | let task = NSTask() 21 | task.launchPath = "/usr/bin/osascript" 22 | task.arguments = [scriptPath] 23 | task.launch() 24 | 25 | // Quit the app 26 | NSApp.terminate(self) 27 | } 28 | 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /QBlocker/KeyListener + Realm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyListener + Realm.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | 11 | extension KeyListener { 12 | 13 | /** 14 | Add an excluded app to the Realm 15 | 16 | - parameter app: The app to be added 17 | */ 18 | func addExcludedApp(app: App) { 19 | do { 20 | try realm?.write { 21 | realm?.add(app, update: true) 22 | } 23 | } catch { 24 | print("Could not write excluded app") 25 | } 26 | } 27 | 28 | /** 29 | Remove an app from the Realm 30 | 31 | - parameter app: The app to be removed 32 | */ 33 | func removeExcludedApp(app: App) { 34 | do { 35 | try realm?.write { 36 | realm?.delete(app) 37 | } 38 | } catch { 39 | print("Could not remove excluded app") 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /QBlocker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 20 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2016 Stephen Radford. All rights reserved. 31 | NSMainStoryboardFile 32 | Main 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![QBlocker Icon](https://raw.githubusercontent.com/steve228uk/QBlocker/master/Assets/qblocker-icons/github.png) 2 | 3 | # QBlocker 4 | 5 | QBlocker stops you from accidentally quitting an app when you actually meant to close the window. It works by blocking the default CMD + Q keyboard shortcut and forcing you to hold it down to quit. 6 | 7 | ## Download 8 | 9 | Grab the [latest stable version here](https://dl.devmate.com/uk.co.wearecocoon.QBlocker/QBlocker.dmg) or download a version from the Github releases page. 10 | 11 | ## Contributing 12 | 13 | Any contribution is welcome whether it's reporting bugs, helping with design, fixing typos or getting stuck in with development. 14 | 15 | ## Screenshots 16 | 17 | ### Menu Bar Icon + Menu 18 | 19 | ![QBlocker Menu](http://i.imgur.com/DqbWTXN.png) 20 | 21 | ### Quit Message Demonstration 22 | 23 | ![Quitting](http://i.imgur.com/GDRx911.png) 24 | 25 | ### Preferences 26 | 27 | ![Preferences](https://raw.githubusercontent.com/steve228uk/QBlocker/master/Assets/screenshots/preferences.png) 28 | 29 | ## I love QBlocker, can I donate? 30 | 31 | I'm not currently accepting donations, instead please consider donating to one of the following charities: 32 | 33 | - **[Stonewall](http://www.stonewall.org.uk/support-stonewall)** 34 | - **[Shelter](http://www.shelter.org.uk)** 35 | - **[Epilepsy Action](https://www.epilepsy.org.uk/involved/donations)** 36 | - **[UNICEF](http://www.unicef.org.uk)** 37 | -------------------------------------------------------------------------------- /QBlocker/Delay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Delay.swift 3 | // QBlocker 4 | // 5 | // http://stackoverflow.com/questions/24034544/dispatch-after-gcd-in-swift 6 | // 7 | 8 | import Foundation 9 | 10 | typealias dispatch_cancelable_closure = (cancel : Bool) -> Void 11 | 12 | func delay(time:NSTimeInterval, closure:()->Void) -> dispatch_cancelable_closure? { 13 | 14 | func dispatch_later(clsr:()->Void) { 15 | dispatch_after( 16 | dispatch_time( 17 | DISPATCH_TIME_NOW, 18 | Int64(time * Double(NSEC_PER_SEC)) 19 | ), 20 | dispatch_get_main_queue(), clsr) 21 | } 22 | 23 | var closure:dispatch_block_t? = closure 24 | var cancelableClosure:dispatch_cancelable_closure? 25 | 26 | let delayedClosure:dispatch_cancelable_closure = { cancel in 27 | if closure != nil { 28 | if (cancel == false) { 29 | dispatch_async(dispatch_get_main_queue(), closure!); 30 | } 31 | } 32 | closure = nil 33 | cancelableClosure = nil 34 | } 35 | 36 | cancelableClosure = delayedClosure 37 | 38 | dispatch_later { 39 | if let delayedClosure = cancelableClosure { 40 | delayedClosure(cancel: false) 41 | } 42 | } 43 | 44 | return cancelableClosure; 45 | } 46 | 47 | func cancel_delay(closure:dispatch_cancelable_closure?) { 48 | 49 | if closure != nil { 50 | closure!(cancel: true) 51 | } 52 | } -------------------------------------------------------------------------------- /QBlocker/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32-1.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256-1.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | QBlocker | Block CMD+Q Quitting Apps 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

QBlocker

14 |

Never accidentally quit an app again

15 |

16 | QBlocker is a small, free menubar app for OS X that stops you from accidentally quitting an app. It works by blocking OS X's default CMD + Q keyboard shortcut and forcing you to hold to quit. You can also choose to exclude apps or let QBlocker work its magic on all apps. 17 |

18 | Github Download QBlocker 19 |
20 | 21 | 22 | 25 | 26 |
27 | 28 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | 22 | ## Other 23 | *.xccheckout 24 | *.moved-aside 25 | *.xcuserstate 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | 32 | ## Playgrounds 33 | timeline.xctimeline 34 | playground.xcworkspace 35 | 36 | # Swift Package Manager 37 | # 38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 39 | # Packages/ 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 63 | 64 | fastlane/report.xml 65 | fastlane/screenshots 66 | -------------------------------------------------------------------------------- /QBlocker/StatusMenuController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusMenuController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 03/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import CoreServices 11 | 12 | class StatusMenuController: NSObject, NSMenuDelegate { 13 | 14 | /// The icon added to the menu bar 15 | let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength) 16 | 17 | /// Reference to the storyboard 18 | @IBOutlet weak var statusMenu: NSMenu! 19 | 20 | override func awakeFromNib() { 21 | statusMenu.delegate = self 22 | statusItem.image = NSImage(named: "Menu Bar") 23 | statusItem.image?.template = true 24 | statusItem.menu = statusMenu 25 | } 26 | 27 | // MARK: - Actions 28 | 29 | @IBAction func quitItemClicked(sender: AnyObject) { 30 | NSApplication.sharedApplication().terminate(self) 31 | } 32 | 33 | /** 34 | Toggle open at login on/off 35 | 36 | - parameter sender: The menu item 37 | */ 38 | @IBAction func openAtLogin(sender: NSMenuItem) { 39 | AtLogin.toggle() 40 | } 41 | 42 | 43 | @IBAction func showPreferences(sender: AnyObject) { 44 | AppDelegate.sharedDelegate?.showPreferencesWindow() 45 | } 46 | 47 | // MARK: - NSMenuDelegate 48 | 49 | func menuWillOpen(menu: NSMenu) { 50 | statusMenu.itemAtIndex(0)?.title = String(format: "%d Quits Blocked", arguments: [KeyListener.sharedKeyListener.accidentalQuits]) 51 | statusMenu.itemAtIndex(4)?.state = (AtLogin.enabled) ? 1 : 0 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /QBlocker/ExcludeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExcludeViewController.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 07/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import RealmSwift 11 | 12 | class ExcludeViewController: NSViewController { 13 | 14 | @IBOutlet weak var tableView: NSTableView! 15 | 16 | // MARK: - Actions 17 | 18 | @IBAction func addClicked(sender: AnyObject) { 19 | let panel = NSOpenPanel() 20 | panel.title = "Choose a .app" 21 | panel.canChooseDirectories = false 22 | panel.canChooseFiles = true 23 | panel.canCreateDirectories = false 24 | panel.allowsMultipleSelection = true 25 | panel.allowedFileTypes = ["app"] 26 | panel.beginSheetModalForWindow(view.window!) { response in 27 | if (response == NSFileHandlingPanelOKButton) { 28 | for url in panel.URLs { 29 | guard let bundle = NSBundle(URL: url)?.bundleIdentifier, path = url.path else { 30 | continue 31 | } 32 | 33 | let name = NSFileManager.defaultManager().displayNameAtPath(path) 34 | 35 | let app = App() 36 | app.name = name 37 | app.bundleID = bundle 38 | KeyListener.sharedKeyListener.addExcludedApp(app) 39 | } 40 | self.tableView.reloadData() 41 | } 42 | } 43 | } 44 | 45 | @IBAction func removeClicked(sender: AnyObject) { 46 | guard tableView.selectedRowIndexes.count > 0, 47 | let apps = KeyListener.sharedKeyListener.list else { 48 | print("Nothing selected") 49 | return 50 | } 51 | 52 | var toRemove = [App]() 53 | tableView.selectedRowIndexes.enumerateIndexesUsingBlock { index, stop in 54 | toRemove.append(apps[index]) 55 | } 56 | 57 | for app in toRemove { 58 | KeyListener.sharedKeyListener.removeExcludedApp(app) 59 | } 60 | 61 | tableView.reloadData() 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /QBlocker/HUDAlert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HUDAlert.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 03/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class HUDAlert { 12 | 13 | /// Shared instance 14 | static let sharedHUDAlert = HUDAlert() 15 | 16 | /// The window that will be used to display the HUD alert 17 | var window: NSWindow? 18 | 19 | /// The dispatch delay that can be cancelled if the HUD is displayed again during that time 20 | var delayer: dispatch_cancelable_closure? 21 | 22 | init() { 23 | window = NSWindow(contentRect: NSMakeRect(0, 0, 426, 79), styleMask: NSBorderlessWindowMask, backing: .Buffered, defer: false) 24 | window?.level = Int(CGWindowLevelForKey(CGWindowLevelKey.AssistiveTechHighWindowLevelKey)) 25 | window?.opaque = false 26 | window?.backgroundColor = NSColor.clearColor() 27 | 28 | let vc = NSStoryboard(name: "Main", bundle: nil).instantiateControllerWithIdentifier("Alert") 29 | window?.contentView? = vc.view 30 | window?.contentView?.wantsLayer = true 31 | 32 | window?.makeKeyWindow() 33 | } 34 | 35 | /** 36 | Show the HUD window 37 | */ 38 | func showHUD(delayTime: NSTimeInterval? = nil) { 39 | 40 | guard let screenRect = NSScreen.mainScreen()?.visibleFrame else { 41 | print("Could not get screen frame") 42 | return 43 | } 44 | 45 | cancel_delay(delayer) 46 | 47 | let newRect = NSMakeRect((screenRect.size.width - 426) * 0.5, (screenRect.size.height - 79) * 0.5, 426, 79) 48 | window?.setFrame(newRect, display: true) 49 | window?.makeKeyAndOrderFront(self) 50 | 51 | if let delayTime = delayTime { 52 | delayer = delay(delayTime) { 53 | self.dismissHUD() 54 | } 55 | } 56 | } 57 | 58 | /** 59 | Dismiss the HUD window 60 | */ 61 | func dismissHUD(fade: Bool = true) { 62 | 63 | guard fade else { 64 | self.window?.orderOut(self) 65 | return 66 | } 67 | 68 | NSAnimationContext.runAnimationGroup({ context in 69 | context.duration = 0.4 70 | self.window?.contentView?.animator().alphaValue = 0 71 | }) { 72 | self.window?.orderOut(self) 73 | self.window?.contentView?.alphaValue = 1 74 | } 75 | 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /QBlocker/AtLogin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtLogin.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 04/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import CoreServices 10 | 11 | /** 12 | * Handle the launch at login settings for the app 13 | */ 14 | struct AtLogin { 15 | 16 | /// Whether launch at login is enabled or not 17 | static var enabled: Bool { 18 | 19 | if launchItem != nil { 20 | return true 21 | } 22 | 23 | return false 24 | 25 | } 26 | 27 | /// The launch item that's stored in LSSharedFileList 28 | private static var launchItem: LSSharedFileListItem? { 29 | let appUrl = NSURL(fileURLWithPath: NSBundle.mainBundle().bundlePath) 30 | 31 | guard let loginItemsRef = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil) else { 32 | return nil 33 | } 34 | 35 | let loginItems = LSSharedFileListCopySnapshot(loginItemsRef.takeRetainedValue(), nil).takeRetainedValue() 36 | for item in loginItems as NSArray { 37 | 38 | // Ensure that the item is a LSSharedFileListItem 39 | guard CFGetTypeID(item) == LSSharedFileListItemGetTypeID() else { 40 | continue 41 | } 42 | 43 | var error: Unmanaged? 44 | let itemUrl = LSSharedFileListItemCopyResolvedURL(item as! LSSharedFileListItem, 0, &error).takeRetainedValue() as NSURL 45 | 46 | if itemUrl == appUrl { 47 | return (item as! LSSharedFileListItem) 48 | } 49 | 50 | } 51 | 52 | return nil 53 | } 54 | 55 | /** 56 | Toggle launch at login 57 | */ 58 | static func toggle() { 59 | 60 | guard let loginItems = LSSharedFileListCreate(nil, kLSSharedFileListSessionLoginItems.takeRetainedValue(), nil) else { 61 | return 62 | } 63 | 64 | if enabled { // remove it from the startup 65 | LSSharedFileListItemRemove(loginItems.takeRetainedValue(), launchItem) 66 | } else { // add it to the startup 67 | let appUrl = NSURL(fileURLWithPath: NSBundle.mainBundle().bundlePath) 68 | LSSharedFileListInsertItemURL(loginItems.takeRetainedValue(), kLSSharedFileListItemBeforeFirst.takeUnretainedValue(), nil, nil, appUrl as CFURL, nil, nil) 69 | } 70 | 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /QBlocker/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 01/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | private var accessibilityWindowController: NSWindowController? 15 | private var firstRunWindowController: NSWindowController? 16 | private lazy var preferencesWindowController: NSWindowController = { 17 | return NSStoryboard(name: "Main", bundle: nil).instantiateControllerWithIdentifier("preferences window") as! NSWindowController 18 | }() 19 | 20 | class var sharedDelegate: AppDelegate? { 21 | return NSApplication.sharedApplication().delegate as? AppDelegate 22 | } 23 | 24 | // MARK: - Instantiation 25 | 26 | override init() { 27 | super.init() 28 | NSUserDefaults.standardUserDefaults().registerDefaults([ 29 | "accidentalQuits": 0, 30 | "firstRunComplete": false, 31 | "listMode": 0, 32 | "delay": 4 33 | ]) 34 | } 35 | 36 | // MARK: - NSApplicationDelegate 37 | 38 | func applicationDidFinishLaunching(aNotification: NSNotification) { 39 | 40 | setupDevMate() 41 | 42 | let promptFlag = kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString 43 | let myDict: CFDictionary = [promptFlag: false] 44 | if AXIsProcessTrustedWithOptions(myDict) { 45 | do { 46 | try KeyListener.sharedKeyListener.start() 47 | } catch { 48 | NSLog("Could not launch listener") 49 | } 50 | 51 | showFirstRunWindowIfRequired() 52 | 53 | } else { 54 | if let windowController = NSStoryboard(name: "Main", bundle: nil).instantiateControllerWithIdentifier("accessibility window") as? NSWindowController { 55 | accessibilityWindowController = windowController 56 | accessibilityWindowController?.showWindow(self) 57 | accessibilityWindowController?.window?.makeKeyAndOrderFront(self) 58 | } 59 | } 60 | 61 | } 62 | 63 | // MARK: - Actions 64 | 65 | /** 66 | Show the first run screen if the NSUserDefault stating it has already be run isn't set 67 | */ 68 | func showFirstRunWindowIfRequired() { 69 | guard !NSUserDefaults.standardUserDefaults().boolForKey("firstRunComplete") else { 70 | return 71 | } 72 | 73 | if let windowController = NSStoryboard(name: "Main", bundle: nil).instantiateControllerWithIdentifier("first run window") as? NSWindowController { 74 | firstRunWindowController = windowController 75 | firstRunWindowController?.showWindow(self) 76 | firstRunWindowController?.window?.makeKeyAndOrderFront(self) 77 | 78 | NSUserDefaults.standardUserDefaults().setBool(true, forKey: "firstRunComplete") 79 | } 80 | } 81 | 82 | /** 83 | Bring the app into foreground and show the preferences window 84 | */ 85 | func showPreferencesWindow() { 86 | NSApplication.sharedApplication().activateIgnoringOtherApps(true) 87 | self.preferencesWindowController.showWindow(nil) 88 | } 89 | 90 | /** 91 | Setup the devmate tracker, issues and updater 92 | */ 93 | func setupDevMate() { 94 | DevMateKit.sendTrackingReport(nil, delegate: nil) 95 | DevMateKit.setupIssuesController(nil, reportingUnhandledIssues: true) 96 | DM_SUUpdater.sharedUpdater().automaticallyChecksForUpdates = true 97 | DM_SUUpdater.sharedUpdater().automaticallyDownloadsUpdates = true 98 | } 99 | 100 | } 101 | 102 | -------------------------------------------------------------------------------- /QBlocker/KeyListener.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyListener.swift 3 | // QBlocker 4 | // 5 | // Created by Stephen Radford on 02/05/2016. 6 | // Copyright © 2016 Cocoon Development Ltd. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | 11 | private func keyDownCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, ptr: UnsafeMutablePointer) -> Unmanaged? { 12 | 13 | // If the command key wasn't used we can pass the event on 14 | let flags = CGEventGetFlags(event) 15 | guard (flags.rawValue & CGEventFlags.MaskCommand.rawValue) != 0 else { 16 | print("command not clicked") 17 | return Unmanaged.passUnretained(event) 18 | } 19 | 20 | // If the shift key was held down we should ignore the event as it breaks the systemwide logout shortcut 21 | guard (flags.rawValue & CGEventFlags.MaskShift.rawValue) == 0 else { 22 | print("shift clicked") 23 | return Unmanaged.passUnretained(event) 24 | } 25 | 26 | // If the q key wasn't clicked we can ignore the event too 27 | guard KeyListener.keyValueForEvent(event)?.lowercaseString == "q" else { 28 | print("q not clicked") 29 | return Unmanaged.passUnretained(event) 30 | } 31 | 32 | guard KeyListener.sharedKeyListener.canQuit else { 33 | print("not allowed to quit yet") 34 | return nil 35 | } 36 | 37 | // get the current active app 38 | guard let app = NSWorkspace.sharedWorkspace().menuBarOwningApplication else { 39 | print("could not get menubar owning app") 40 | return Unmanaged.passUnretained(event) 41 | } 42 | 43 | // Check if the current app is in the list 44 | if let bundleId = app.bundleIdentifier { 45 | let isIdentifierListed = KeyListener.sharedKeyListener.listedBundleIdentifiers.contains(bundleId) 46 | print(ListMode.selectedMode) 47 | if (ListMode.selectedMode == .Blacklist && isIdentifierListed) || (ListMode.selectedMode == .Whitelist && !isIdentifierListed) { 48 | print("App is excluded") 49 | return Unmanaged.passUnretained(event) 50 | } 51 | } 52 | 53 | // check that the app has CMD Q enabled 54 | guard KeyListener.cmdQActiveForApp(app) else { 55 | print("\(app.bundleIdentifier) does not use cmd+q") 56 | return nil 57 | } 58 | 59 | if KeyListener.sharedKeyListener.canQuit && KeyListener.sharedKeyListener.tries <= KeyListener.delay { 60 | print("showing HUD") 61 | HUDAlert.sharedHUDAlert.showHUD(1) 62 | } 63 | 64 | KeyListener.sharedKeyListener.tries += 1 65 | if KeyListener.sharedKeyListener.tries > KeyListener.delay { 66 | print("quit successful") 67 | KeyListener.sharedKeyListener.tries = 0 68 | KeyListener.sharedKeyListener.canQuit = false 69 | HUDAlert.sharedHUDAlert.dismissHUD(false) 70 | return Unmanaged.passUnretained(event) 71 | } 72 | 73 | return nil 74 | } 75 | 76 | private func keyUpCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, ptr: UnsafeMutablePointer) -> Unmanaged? { 77 | 78 | // If the command key wasn't used we can pass the event on 79 | let flags = CGEventGetFlags(event) 80 | guard (flags.rawValue & CGEventFlags.MaskCommand.rawValue) != 0 else { 81 | return Unmanaged.passUnretained(event) 82 | } 83 | 84 | // If the shift key was held down we should ignore the event as it breaks the systemwide logout shortcut 85 | guard (flags.rawValue & CGEventFlags.MaskShift.rawValue) == 0 else { 86 | print("shift clicked") 87 | return Unmanaged.passUnretained(event) 88 | } 89 | 90 | // If the q key wasn't clicked we can ignore the event too 91 | guard KeyListener.keyValueForEvent(event)?.lowercaseString == "q" else { 92 | return Unmanaged.passUnretained(event) 93 | } 94 | 95 | if KeyListener.sharedKeyListener.tries <= KeyListener.delay { 96 | KeyListener.sharedKeyListener.logAccidentalQuit() 97 | } else { 98 | HUDAlert.sharedHUDAlert.dismissHUD() 99 | } 100 | 101 | KeyListener.sharedKeyListener.tries = 0 102 | KeyListener.sharedKeyListener.canQuit = true 103 | 104 | return Unmanaged.passUnretained(event) 105 | } 106 | 107 | 108 | class KeyListener { 109 | 110 | /// Shared instance of the key listener 111 | static let sharedKeyListener = KeyListener() 112 | 113 | /// How long the Q key needs to be held before you can quit 114 | static var delay: Int { 115 | return NSUserDefaults.standardUserDefaults().integerForKey("delay") ?? 4 116 | } 117 | 118 | /// Reference to our default Realm 119 | var realm: Realm? 120 | 121 | /// The CGEvent for key down 122 | var keyDown: CFMachPort? 123 | 124 | /// The run loop for key down 125 | var keyDownRunLoopSource: CFRunLoopSource? 126 | 127 | /// The CG event for key up 128 | var keyUp: CFMachPort? 129 | 130 | /// The run loop for key up 131 | var keyUpRunLoopSource: CFRunLoopSource? 132 | 133 | /// The number of "tries" that CMD + Q have been hit. 134 | /// This is set when a user holds down the CMD + Q shortcut. 135 | var tries = 0 136 | 137 | /// Can quit is marked as false as soon as an app has just quit. 138 | /// If this is not checked then subsequent apps will continue to quit behind it. 139 | var canQuit = true 140 | 141 | /// The number of accidental quits that have been saved by QBlocker 142 | var accidentalQuits: Int { 143 | return NSUserDefaults.standardUserDefaults().integerForKey("accidentalQuits") 144 | } 145 | 146 | /// Array of apps to be ignored/allowed (depending on the setting) by QBlocker 147 | var list: Results? { 148 | return realm?.objects(App).sorted("name") 149 | } 150 | 151 | /// The bundle identifiers of all apps from list 152 | var listedBundleIdentifiers: Set { 153 | guard let apps = list else { 154 | return [] 155 | } 156 | 157 | return Set(apps.map { $0.bundleID }) 158 | } 159 | 160 | init() { 161 | do { 162 | realm = try Realm() 163 | } catch { 164 | print("Failed to load Realm") 165 | } 166 | } 167 | 168 | /** 169 | Start the keyDown and keyUp listeners. 170 | 171 | - throws: `KeyListenerError` 172 | */ 173 | func start() throws { 174 | 175 | keyDown = CGEventTapCreate(CGEventTapLocation.CGHIDEventTap, 176 | CGEventTapPlacement.HeadInsertEventTap, 177 | CGEventTapOptions.Default, 178 | CGEventMask((1 << CGEventType.KeyDown.rawValue)), 179 | keyDownCallback, 180 | UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())) 181 | 182 | keyUp = CGEventTapCreate(CGEventTapLocation.CGHIDEventTap, 183 | CGEventTapPlacement.HeadInsertEventTap, 184 | CGEventTapOptions.Default, 185 | CGEventMask((1 << CGEventType.KeyUp.rawValue)), 186 | keyUpCallback, 187 | UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())) 188 | 189 | guard keyDown != nil else { 190 | throw KeyListenerError.AccessibilityPermissionDenied 191 | } 192 | 193 | keyDownRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, keyDown, 0) 194 | CFRunLoopAddSource(CFRunLoopGetCurrent(), keyDownRunLoopSource, kCFRunLoopCommonModes) 195 | 196 | keyUpRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, keyUp, 0) 197 | CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSource, kCFRunLoopCommonModes) 198 | 199 | } 200 | 201 | /** 202 | Store accidental quits in the user defaults 203 | */ 204 | func logAccidentalQuit() { 205 | let quits = accidentalQuits + 1 206 | NSUserDefaults.standardUserDefaults().setInteger(quits, forKey: "accidentalQuits") 207 | } 208 | 209 | /** 210 | Checks if CMD+Q is in the menu bar for the current application 211 | 212 | - parameter app: The Current App 213 | */ 214 | class func cmdQActiveForApp(app: NSRunningApplication) -> Bool { 215 | 216 | let app = AXUIElementCreateApplication(app.processIdentifier).takeRetainedValue() 217 | var menuBar: AnyObject? 218 | AXUIElementCopyAttributeValue(app, kAXMenuBarAttribute, &menuBar) 219 | 220 | // If we can't get the menubar then exit 221 | guard menuBar != nil else { 222 | return false 223 | } 224 | 225 | // Get the toplevel menu items 226 | let menu = menuBar as! AXUIElement 227 | var children: AnyObject? 228 | AXUIElementCopyAttributeValue(menu, kAXChildrenAttribute, &children) 229 | 230 | guard let items = children as? NSArray where items.count > 0 else { 231 | return false 232 | } 233 | 234 | // Get the submenus of the first item 235 | var subMenus: AnyObject? 236 | let title = items[1] as! AXUIElement // subscript 0 is the apple menu 237 | AXUIElementCopyAttributeValue(title, kAXChildrenAttribute, &subMenus) 238 | 239 | guard let menus = subMenus as? NSArray where menus.count > 0 else { 240 | return false 241 | } 242 | 243 | // Get the entries of the submenu 244 | var entries: AnyObject? 245 | let submenu = menus[0] as! AXUIElement 246 | AXUIElementCopyAttributeValue(submenu, kAXChildrenAttribute, &entries) 247 | 248 | guard let menuItems = entries as? NSArray where menuItems.count > 0 else { 249 | return false 250 | } 251 | 252 | // Loop through the menu items and check if CMD + Q is the shortcut 253 | for item in menuItems { 254 | var cmdChar: AnyObject? 255 | AXUIElementCopyAttributeValue(item as! AXUIElement, kAXMenuItemCmdCharAttribute, &cmdChar) 256 | if let char = cmdChar as? String where char == "Q" { 257 | return true 258 | } 259 | } 260 | 261 | return false 262 | } 263 | 264 | /** 265 | Return the key character 266 | 267 | - parameter event: They keyboard event 268 | 269 | - returns: The characters clicked 270 | */ 271 | class func keyValueForEvent(event: CGEvent) -> String? { 272 | return NSEvent(CGEvent: event)?.charactersIgnoringModifiers 273 | } 274 | 275 | } -------------------------------------------------------------------------------- /Website/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 6 | */ 7 | 8 | html { 9 | font-family: sans-serif; /* 1 */ 10 | -ms-text-size-adjust: 100%; /* 2 */ 11 | -webkit-text-size-adjust: 100%; /* 2 */ 12 | } 13 | 14 | /** 15 | * Remove the margin in all browsers (opinionated). 16 | */ 17 | 18 | body { 19 | margin: 0; 20 | } 21 | 22 | /* HTML5 display definitions 23 | ========================================================================== */ 24 | 25 | /** 26 | * Add the correct display in IE 9-. 27 | * 1. Add the correct display in Edge, IE, and Firefox. 28 | * 2. Add the correct display in IE. 29 | */ 30 | 31 | article, 32 | aside, 33 | details, /* 1 */ 34 | figcaption, 35 | figure, 36 | footer, 37 | header, 38 | main, /* 2 */ 39 | menu, 40 | nav, 41 | section, 42 | summary { /* 1 */ 43 | display: block; 44 | } 45 | 46 | /** 47 | * Add the correct display in IE 9-. 48 | */ 49 | 50 | audio, 51 | canvas, 52 | progress, 53 | video { 54 | display: inline-block; 55 | } 56 | 57 | /** 58 | * Add the correct display in iOS 4-7. 59 | */ 60 | 61 | audio:not([controls]) { 62 | display: none; 63 | height: 0; 64 | } 65 | 66 | /** 67 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 68 | */ 69 | 70 | progress { 71 | vertical-align: baseline; 72 | } 73 | 74 | /** 75 | * Add the correct display in IE 10-. 76 | * 1. Add the correct display in IE. 77 | */ 78 | 79 | template, /* 1 */ 80 | [hidden] { 81 | display: none; 82 | } 83 | 84 | /* Links 85 | ========================================================================== */ 86 | 87 | /** 88 | * 1. Remove the gray background on active links in IE 10. 89 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 90 | */ 91 | 92 | a { 93 | background-color: transparent; /* 1 */ 94 | -webkit-text-decoration-skip: objects; /* 2 */ 95 | } 96 | 97 | /** 98 | * Remove the outline on focused links when they are also active or hovered 99 | * in all browsers (opinionated). 100 | */ 101 | 102 | a:active, 103 | a:hover { 104 | outline-width: 0; 105 | } 106 | 107 | /* Text-level semantics 108 | ========================================================================== */ 109 | 110 | /** 111 | * 1. Remove the bottom border in Firefox 39-. 112 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: none; /* 1 */ 117 | text-decoration: underline; /* 2 */ 118 | text-decoration: underline dotted; /* 2 */ 119 | } 120 | 121 | /** 122 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 123 | */ 124 | 125 | b, 126 | strong { 127 | font-weight: inherit; 128 | } 129 | 130 | /** 131 | * Add the correct font weight in Chrome, Edge, and Safari. 132 | */ 133 | 134 | b, 135 | strong { 136 | font-weight: bolder; 137 | } 138 | 139 | /** 140 | * Add the correct font style in Android 4.3-. 141 | */ 142 | 143 | dfn { 144 | font-style: italic; 145 | } 146 | 147 | /** 148 | * Correct the font size and margin on `h1` elements within `section` and 149 | * `article` contexts in Chrome, Firefox, and Safari. 150 | */ 151 | 152 | h1 { 153 | font-size: 2em; 154 | margin: 0.67em 0; 155 | } 156 | 157 | /** 158 | * Add the correct background and color in IE 9-. 159 | */ 160 | 161 | mark { 162 | background-color: #ff0; 163 | color: #000; 164 | } 165 | 166 | /** 167 | * Add the correct font size in all browsers. 168 | */ 169 | 170 | small { 171 | font-size: 80%; 172 | } 173 | 174 | /** 175 | * Prevent `sub` and `sup` elements from affecting the line height in 176 | * all browsers. 177 | */ 178 | 179 | sub, 180 | sup { 181 | font-size: 75%; 182 | line-height: 0; 183 | position: relative; 184 | vertical-align: baseline; 185 | } 186 | 187 | sub { 188 | bottom: -0.25em; 189 | } 190 | 191 | sup { 192 | top: -0.5em; 193 | } 194 | 195 | /* Embedded content 196 | ========================================================================== */ 197 | 198 | /** 199 | * Remove the border on images inside links in IE 10-. 200 | */ 201 | 202 | img { 203 | border-style: none; 204 | } 205 | 206 | /** 207 | * Hide the overflow in IE. 208 | */ 209 | 210 | svg:not(:root) { 211 | overflow: hidden; 212 | } 213 | 214 | /* Grouping content 215 | ========================================================================== */ 216 | 217 | /** 218 | * 1. Correct the inheritance and scaling of font size in all browsers. 219 | * 2. Correct the odd `em` font sizing in all browsers. 220 | */ 221 | 222 | code, 223 | kbd, 224 | pre, 225 | samp { 226 | font-family: monospace, monospace; /* 1 */ 227 | font-size: 1em; /* 2 */ 228 | } 229 | 230 | /** 231 | * Add the correct margin in IE 8. 232 | */ 233 | 234 | figure { 235 | margin: 1em 40px; 236 | } 237 | 238 | /** 239 | * 1. Add the correct box sizing in Firefox. 240 | * 2. Show the overflow in Edge and IE. 241 | */ 242 | 243 | hr { 244 | box-sizing: content-box; /* 1 */ 245 | height: 0; /* 1 */ 246 | overflow: visible; /* 2 */ 247 | } 248 | 249 | /* Forms 250 | ========================================================================== */ 251 | 252 | /** 253 | * 1. Change font properties to `inherit` in all browsers (opinionated). 254 | * 2. Remove the margin in Firefox and Safari. 255 | */ 256 | 257 | button, 258 | input, 259 | select, 260 | textarea { 261 | font: inherit; /* 1 */ 262 | margin: 0; /* 2 */ 263 | } 264 | 265 | /** 266 | * Restore the font weight unset by the previous rule. 267 | */ 268 | 269 | optgroup { 270 | font-weight: bold; 271 | } 272 | 273 | /** 274 | * Show the overflow in IE. 275 | * 1. Show the overflow in Edge. 276 | */ 277 | 278 | button, 279 | input { /* 1 */ 280 | overflow: visible; 281 | } 282 | 283 | /** 284 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 285 | * 1. Remove the inheritance of text transform in Firefox. 286 | */ 287 | 288 | button, 289 | select { /* 1 */ 290 | text-transform: none; 291 | } 292 | 293 | /** 294 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 295 | * controls in Android 4. 296 | * 2. Correct the inability to style clickable types in iOS and Safari. 297 | */ 298 | 299 | button, 300 | html [type="button"], /* 1 */ 301 | [type="reset"], 302 | [type="submit"] { 303 | -webkit-appearance: button; /* 2 */ 304 | } 305 | 306 | /** 307 | * Remove the inner border and padding in Firefox. 308 | */ 309 | 310 | button::-moz-focus-inner, 311 | [type="button"]::-moz-focus-inner, 312 | [type="reset"]::-moz-focus-inner, 313 | [type="submit"]::-moz-focus-inner { 314 | border-style: none; 315 | padding: 0; 316 | } 317 | 318 | /** 319 | * Restore the focus styles unset by the previous rule. 320 | */ 321 | 322 | button:-moz-focusring, 323 | [type="button"]:-moz-focusring, 324 | [type="reset"]:-moz-focusring, 325 | [type="submit"]:-moz-focusring { 326 | outline: 1px dotted ButtonText; 327 | } 328 | 329 | /** 330 | * Change the border, margin, and padding in all browsers (opinionated). 331 | */ 332 | 333 | fieldset { 334 | border: 1px solid #c0c0c0; 335 | margin: 0 2px; 336 | padding: 0.35em 0.625em 0.75em; 337 | } 338 | 339 | /** 340 | * 1. Correct the text wrapping in Edge and IE. 341 | * 2. Correct the color inheritance from `fieldset` elements in IE. 342 | * 3. Remove the padding so developers are not caught out when they zero out 343 | * `fieldset` elements in all browsers. 344 | */ 345 | 346 | legend { 347 | box-sizing: border-box; /* 1 */ 348 | color: inherit; /* 2 */ 349 | display: table; /* 1 */ 350 | max-width: 100%; /* 1 */ 351 | padding: 0; /* 3 */ 352 | white-space: normal; /* 1 */ 353 | } 354 | 355 | /** 356 | * Remove the default vertical scrollbar in IE. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; 361 | } 362 | 363 | /** 364 | * 1. Add the correct box sizing in IE 10-. 365 | * 2. Remove the padding in IE 10-. 366 | */ 367 | 368 | [type="checkbox"], 369 | [type="radio"] { 370 | box-sizing: border-box; /* 1 */ 371 | padding: 0; /* 2 */ 372 | } 373 | 374 | /** 375 | * Correct the cursor style of increment and decrement buttons in Chrome. 376 | */ 377 | 378 | [type="number"]::-webkit-inner-spin-button, 379 | [type="number"]::-webkit-outer-spin-button { 380 | height: auto; 381 | } 382 | 383 | /** 384 | * 1. Correct the odd appearance in Chrome and Safari. 385 | * 2. Correct the outline style in Safari. 386 | */ 387 | 388 | [type="search"] { 389 | -webkit-appearance: textfield; /* 1 */ 390 | outline-offset: -2px; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 395 | */ 396 | 397 | [type="search"]::-webkit-search-cancel-button, 398 | [type="search"]::-webkit-search-decoration { 399 | -webkit-appearance: none; 400 | } 401 | 402 | /** 403 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 404 | */ 405 | 406 | ::-webkit-input-placeholder { 407 | color: inherit; 408 | opacity: 0.54; 409 | } 410 | 411 | /** 412 | * 1. Correct the inability to style clickable types in iOS and Safari. 413 | * 2. Change font properties to `inherit` in Safari. 414 | */ 415 | 416 | ::-webkit-file-upload-button { 417 | -webkit-appearance: button; /* 1 */ 418 | font: inherit; /* 2 */ 419 | } 420 | 421 | .cf:after { 422 | content: ""; 423 | display: table; 424 | clear: both; 425 | } 426 | 427 | 428 | /******************************* 429 | * 430 | * QBLOCKER 431 | * 432 | ********************************/ 433 | 434 | html { 435 | height: 100%; 436 | } 437 | 438 | body { 439 | font-family: -apple-system, "Helvetica Neue", "Lucida Grande"; 440 | height: 100%; 441 | color: #999; 442 | } 443 | 444 | a { 445 | color: #999; 446 | } 447 | 448 | a:hover { 449 | text-decoration: none; 450 | } 451 | 452 | .container { 453 | width: 90%; 454 | max-width: 600px; 455 | margin: 0 auto; 456 | } 457 | 458 | .main { 459 | padding: 90px 0; 460 | } 461 | 462 | .main__icon { 463 | width: 20%; 464 | float: left; 465 | } 466 | 467 | .main__icon img { 468 | width: 100%; 469 | } 470 | 471 | .main__text { 472 | width: 70%; 473 | float: right; 474 | } 475 | 476 | h1 { 477 | font-weight: 300; 478 | font-size: 3em; 479 | margin: 0; 480 | letter-spacing: 0.5px; 481 | } 482 | 483 | h2 { 484 | font-weight: 300; 485 | margin: 0 0 25px; 486 | letter-spacing: 0.5px; 487 | } 488 | 489 | p { 490 | line-height: 1.5; 491 | margin: 0 0 35px; 492 | } 493 | 494 | .download-btn { 495 | background-image: linear-gradient(0, #A300FF 0%, #31E5F0 100%); 496 | letter-spacing: 0.5px; 497 | color: #fff; 498 | font-size: 1.5em; 499 | text-decoration: none; 500 | font-weight: 300; 501 | padding: 10px 15px; 502 | border-radius: 5px; 503 | display: inline-block; 504 | } 505 | 506 | .github-btn { 507 | background: #999; 508 | letter-spacing: 0.5px; 509 | color: #fff; 510 | font-size: 1.5em; 511 | text-decoration: none; 512 | font-weight: 300; 513 | padding: 10px 15px; 514 | border-radius: 5px; 515 | display: inline-block; 516 | margin-right: 10px; 517 | } 518 | 519 | footer { 520 | padding-bottom: 25px; 521 | } 522 | -------------------------------------------------------------------------------- /QBlocker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 13FA35AC1CF623C400A29767 /* ListMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13FA35AB1CF623C400A29767 /* ListMode.swift */; }; 11 | 280140151CDA9D5200BC2E78 /* AtLogin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280140141CDA9D5200BC2E78 /* AtLogin.swift */; }; 12 | 2832CA361CD688E9001D3373 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2832CA351CD688E9001D3373 /* AppDelegate.swift */; }; 13 | 2832CA3A1CD688E9001D3373 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2832CA391CD688E9001D3373 /* Assets.xcassets */; }; 14 | 2832CA3D1CD688E9001D3373 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2832CA3B1CD688E9001D3373 /* Main.storyboard */; }; 15 | 28437E4E1CD93DE4002E90EF /* KeyListenerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28437E4D1CD93DE4002E90EF /* KeyListenerError.swift */; }; 16 | 2853B63B1CD92D9700F186CE /* StatusMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2853B63A1CD92D9700F186CE /* StatusMenuController.swift */; }; 17 | 2880C4531CD7B3A000545E1F /* KeyListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2880C4521CD7B3A000545E1F /* KeyListener.swift */; }; 18 | 2883C1E31CDE30BF0007AC47 /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2883C1E21CDE30BF0007AC47 /* Delay.swift */; }; 19 | 2883C1E61CDE36B10007AC47 /* FirstRunViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2883C1E41CDE36B10007AC47 /* FirstRunViewController.swift */; }; 20 | 28A5185B1CDE416800BE7ED1 /* ExcludeWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5185A1CDE416800BE7ED1 /* ExcludeWindowController.swift */; }; 21 | 28A5185D1CDE49CD00BE7ED1 /* ExcludeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5185C1CDE49CD00BE7ED1 /* ExcludeViewController.swift */; }; 22 | 28A5185F1CDE87A300BE7ED1 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5185E1CDE87A300BE7ED1 /* App.swift */; }; 23 | 28A518611CDE8ADF00BE7ED1 /* KeyListener + Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A518601CDE8ADF00BE7ED1 /* KeyListener + Realm.swift */; }; 24 | 28A518631CDE91C200BE7ED1 /* ExcludeViewController + NSTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A518621CDE91C200BE7ED1 /* ExcludeViewController + NSTableViewDataSource.swift */; }; 25 | 28A518671CDE997400BE7ED1 /* ExcludeViewController + NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A518661CDE997400BE7ED1 /* ExcludeViewController + NSTableViewDelegate.swift */; }; 26 | 28BEB6831CDBE79D00E693D3 /* AccessibilityWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28BEB6811CDBE79D00E693D3 /* AccessibilityWindowController.swift */; }; 27 | 28BEB6861CDBEB6500E693D3 /* OpenPreferences.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 28BEB6851CDBEB4F00E693D3 /* OpenPreferences.scpt */; }; 28 | 28BEB6881CDBEB8200E693D3 /* AccessibilityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28BEB6871CDBEB8200E693D3 /* AccessibilityViewController.swift */; }; 29 | 28DEC8041CD950450012B5E3 /* HUDAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DEC8031CD950450012B5E3 /* HUDAlert.swift */; }; 30 | 28DEC8061CD954EA0012B5E3 /* HUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DEC8051CD954EA0012B5E3 /* HUDView.swift */; }; 31 | 28F40A711CFB61EB006719C8 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F40A701CFB61EB006719C8 /* TabBarController.swift */; }; 32 | 89B8C5D3D02D89463E1239E8 /* Pods_QBlocker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1CE05FBCE54008C441D3CA3 /* Pods_QBlocker.framework */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 13FA35AB1CF623C400A29767 /* ListMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListMode.swift; sourceTree = ""; }; 37 | 18D302062F0781EB95263F14 /* Pods-QBlocker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QBlocker.release.xcconfig"; path = "Pods/Target Support Files/Pods-QBlocker/Pods-QBlocker.release.xcconfig"; sourceTree = ""; }; 38 | 280140141CDA9D5200BC2E78 /* AtLogin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AtLogin.swift; sourceTree = ""; }; 39 | 2832CA321CD688E9001D3373 /* QBlocker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QBlocker.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 2832CA351CD688E9001D3373 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 2832CA391CD688E9001D3373 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 2832CA3C1CD688E9001D3373 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 2832CA3E1CD688E9001D3373 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 28437E4D1CD93DE4002E90EF /* KeyListenerError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyListenerError.swift; sourceTree = ""; }; 45 | 2853B63A1CD92D9700F186CE /* StatusMenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusMenuController.swift; sourceTree = ""; }; 46 | 2879D1171CDA529C00B64F00 /* Bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Bridge.h; sourceTree = ""; }; 47 | 2880C4521CD7B3A000545E1F /* KeyListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyListener.swift; sourceTree = ""; }; 48 | 2883C1E21CDE30BF0007AC47 /* Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = ""; }; 49 | 2883C1E41CDE36B10007AC47 /* FirstRunViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirstRunViewController.swift; sourceTree = ""; }; 50 | 28A5185A1CDE416800BE7ED1 /* ExcludeWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExcludeWindowController.swift; sourceTree = ""; }; 51 | 28A5185C1CDE49CD00BE7ED1 /* ExcludeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExcludeViewController.swift; sourceTree = ""; }; 52 | 28A5185E1CDE87A300BE7ED1 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; 53 | 28A518601CDE8ADF00BE7ED1 /* KeyListener + Realm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyListener + Realm.swift"; sourceTree = ""; }; 54 | 28A518621CDE91C200BE7ED1 /* ExcludeViewController + NSTableViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ExcludeViewController + NSTableViewDataSource.swift"; sourceTree = ""; }; 55 | 28A518661CDE997400BE7ED1 /* ExcludeViewController + NSTableViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ExcludeViewController + NSTableViewDelegate.swift"; sourceTree = ""; }; 56 | 28BEB6811CDBE79D00E693D3 /* AccessibilityWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibilityWindowController.swift; sourceTree = ""; }; 57 | 28BEB6851CDBEB4F00E693D3 /* OpenPreferences.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = OpenPreferences.scpt; sourceTree = ""; }; 58 | 28BEB6871CDBEB8200E693D3 /* AccessibilityViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessibilityViewController.swift; sourceTree = ""; }; 59 | 28DEC8031CD950450012B5E3 /* HUDAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUDAlert.swift; sourceTree = ""; }; 60 | 28DEC8051CD954EA0012B5E3 /* HUDView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUDView.swift; sourceTree = ""; }; 61 | 28F40A701CFB61EB006719C8 /* TabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 62 | C1CE05FBCE54008C441D3CA3 /* Pods_QBlocker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_QBlocker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | EAE265B701E85E6B909B8E2C /* Pods-QBlocker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-QBlocker.debug.xcconfig"; path = "Pods/Target Support Files/Pods-QBlocker/Pods-QBlocker.debug.xcconfig"; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 2832CA2F1CD688E9001D3373 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 89B8C5D3D02D89463E1239E8 /* Pods_QBlocker.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 280140131CDA9B4600BC2E78 /* First Launch */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 28BEB6811CDBE79D00E693D3 /* AccessibilityWindowController.swift */, 82 | 28BEB6871CDBEB8200E693D3 /* AccessibilityViewController.swift */, 83 | 2883C1E41CDE36B10007AC47 /* FirstRunViewController.swift */, 84 | 28BEB6851CDBEB4F00E693D3 /* OpenPreferences.scpt */, 85 | ); 86 | name = "First Launch"; 87 | sourceTree = ""; 88 | }; 89 | 280140161CDA9D9000BC2E78 /* Utility */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 280140141CDA9D5200BC2E78 /* AtLogin.swift */, 93 | ); 94 | name = Utility; 95 | sourceTree = ""; 96 | }; 97 | 2832CA291CD688E8001D3373 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 2832CA341CD688E9001D3373 /* QBlocker */, 101 | 2832CA331CD688E9001D3373 /* Products */, 102 | 38D42208756B276FCDCB321A /* Pods */, 103 | C44CD8B7A74F8347ECBD8138 /* Frameworks */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 2832CA331CD688E9001D3373 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 2832CA321CD688E9001D3373 /* QBlocker.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 2832CA341CD688E9001D3373 /* QBlocker */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 28A518591CDE415100BE7ED1 /* Preferences */, 119 | 280140161CDA9D9000BC2E78 /* Utility */, 120 | 280140131CDA9B4600BC2E78 /* First Launch */, 121 | 28437E4C1CD93DB0002E90EF /* Key Listener */, 122 | 2880C4511CD7B37C00545E1F /* Supporting Files */, 123 | 2832CA351CD688E9001D3373 /* AppDelegate.swift */, 124 | 2853B63A1CD92D9700F186CE /* StatusMenuController.swift */, 125 | 2832CA3B1CD688E9001D3373 /* Main.storyboard */, 126 | ); 127 | path = QBlocker; 128 | sourceTree = ""; 129 | }; 130 | 28437E4C1CD93DB0002E90EF /* Key Listener */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 2880C4521CD7B3A000545E1F /* KeyListener.swift */, 134 | 28A518601CDE8ADF00BE7ED1 /* KeyListener + Realm.swift */, 135 | 28437E4D1CD93DE4002E90EF /* KeyListenerError.swift */, 136 | 28DEC8031CD950450012B5E3 /* HUDAlert.swift */, 137 | 28DEC8051CD954EA0012B5E3 /* HUDView.swift */, 138 | 2883C1E21CDE30BF0007AC47 /* Delay.swift */, 139 | ); 140 | name = "Key Listener"; 141 | sourceTree = ""; 142 | }; 143 | 2880C4511CD7B37C00545E1F /* Supporting Files */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 2832CA391CD688E9001D3373 /* Assets.xcassets */, 147 | 2832CA3E1CD688E9001D3373 /* Info.plist */, 148 | 2879D1171CDA529C00B64F00 /* Bridge.h */, 149 | ); 150 | name = "Supporting Files"; 151 | sourceTree = ""; 152 | }; 153 | 28A518591CDE415100BE7ED1 /* Preferences */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 28A5185C1CDE49CD00BE7ED1 /* ExcludeViewController.swift */, 157 | 28A518621CDE91C200BE7ED1 /* ExcludeViewController + NSTableViewDataSource.swift */, 158 | 28A518661CDE997400BE7ED1 /* ExcludeViewController + NSTableViewDelegate.swift */, 159 | 28A5185A1CDE416800BE7ED1 /* ExcludeWindowController.swift */, 160 | 28A5185E1CDE87A300BE7ED1 /* App.swift */, 161 | 13FA35AB1CF623C400A29767 /* ListMode.swift */, 162 | 28F40A701CFB61EB006719C8 /* TabBarController.swift */, 163 | ); 164 | name = Preferences; 165 | sourceTree = ""; 166 | }; 167 | 38D42208756B276FCDCB321A /* Pods */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | EAE265B701E85E6B909B8E2C /* Pods-QBlocker.debug.xcconfig */, 171 | 18D302062F0781EB95263F14 /* Pods-QBlocker.release.xcconfig */, 172 | ); 173 | name = Pods; 174 | sourceTree = ""; 175 | }; 176 | C44CD8B7A74F8347ECBD8138 /* Frameworks */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | C1CE05FBCE54008C441D3CA3 /* Pods_QBlocker.framework */, 180 | ); 181 | name = Frameworks; 182 | sourceTree = ""; 183 | }; 184 | /* End PBXGroup section */ 185 | 186 | /* Begin PBXNativeTarget section */ 187 | 2832CA311CD688E9001D3373 /* QBlocker */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = 2832CA411CD688E9001D3373 /* Build configuration list for PBXNativeTarget "QBlocker" */; 190 | buildPhases = ( 191 | E382C6621D20D82AD3EE2371 /* Check Pods Manifest.lock */, 192 | 2832CA2E1CD688E9001D3373 /* Sources */, 193 | 2832CA2F1CD688E9001D3373 /* Frameworks */, 194 | 2832CA301CD688E9001D3373 /* Resources */, 195 | A5F3978DEF0056344662D1D8 /* Embed Pods Frameworks */, 196 | D83C7FD3BC174A7B11207BD0 /* Copy Pods Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | ); 202 | name = QBlocker; 203 | productName = QBlocker; 204 | productReference = 2832CA321CD688E9001D3373 /* QBlocker.app */; 205 | productType = "com.apple.product-type.application"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 2832CA2A1CD688E8001D3373 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastSwiftUpdateCheck = 0730; 214 | LastUpgradeCheck = 0730; 215 | ORGANIZATIONNAME = "Cocoon Development Ltd"; 216 | TargetAttributes = { 217 | 2832CA311CD688E9001D3373 = { 218 | CreatedOnToolsVersion = 7.3; 219 | DevelopmentTeam = B598AEF9ZM; 220 | SystemCapabilities = { 221 | com.apple.Sandbox = { 222 | enabled = 0; 223 | }; 224 | }; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = 2832CA2D1CD688E8001D3373 /* Build configuration list for PBXProject "QBlocker" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = English; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 2832CA291CD688E8001D3373; 237 | productRefGroup = 2832CA331CD688E9001D3373 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 2832CA311CD688E9001D3373 /* QBlocker */, 242 | ); 243 | }; 244 | /* End PBXProject section */ 245 | 246 | /* Begin PBXResourcesBuildPhase section */ 247 | 2832CA301CD688E9001D3373 /* Resources */ = { 248 | isa = PBXResourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 28BEB6861CDBEB6500E693D3 /* OpenPreferences.scpt in Resources */, 252 | 2832CA3A1CD688E9001D3373 /* Assets.xcassets in Resources */, 253 | 2832CA3D1CD688E9001D3373 /* Main.storyboard in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXResourcesBuildPhase section */ 258 | 259 | /* Begin PBXShellScriptBuildPhase section */ 260 | A5F3978DEF0056344662D1D8 /* Embed Pods Frameworks */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | ); 267 | name = "Embed Pods Frameworks"; 268 | outputPaths = ( 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-QBlocker/Pods-QBlocker-frameworks.sh\"\n"; 273 | showEnvVarsInLog = 0; 274 | }; 275 | D83C7FD3BC174A7B11207BD0 /* Copy Pods Resources */ = { 276 | isa = PBXShellScriptBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | inputPaths = ( 281 | ); 282 | name = "Copy Pods Resources"; 283 | outputPaths = ( 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | shellPath = /bin/sh; 287 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-QBlocker/Pods-QBlocker-resources.sh\"\n"; 288 | showEnvVarsInLog = 0; 289 | }; 290 | E382C6621D20D82AD3EE2371 /* Check Pods Manifest.lock */ = { 291 | isa = PBXShellScriptBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | ); 295 | inputPaths = ( 296 | ); 297 | name = "Check Pods Manifest.lock"; 298 | outputPaths = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | shellPath = /bin/sh; 302 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 303 | showEnvVarsInLog = 0; 304 | }; 305 | /* End PBXShellScriptBuildPhase section */ 306 | 307 | /* Begin PBXSourcesBuildPhase section */ 308 | 2832CA2E1CD688E9001D3373 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 13FA35AC1CF623C400A29767 /* ListMode.swift in Sources */, 313 | 28DEC8061CD954EA0012B5E3 /* HUDView.swift in Sources */, 314 | 28A518631CDE91C200BE7ED1 /* ExcludeViewController + NSTableViewDataSource.swift in Sources */, 315 | 2853B63B1CD92D9700F186CE /* StatusMenuController.swift in Sources */, 316 | 28A5185B1CDE416800BE7ED1 /* ExcludeWindowController.swift in Sources */, 317 | 28BEB6831CDBE79D00E693D3 /* AccessibilityWindowController.swift in Sources */, 318 | 2880C4531CD7B3A000545E1F /* KeyListener.swift in Sources */, 319 | 28A5185F1CDE87A300BE7ED1 /* App.swift in Sources */, 320 | 280140151CDA9D5200BC2E78 /* AtLogin.swift in Sources */, 321 | 28A518671CDE997400BE7ED1 /* ExcludeViewController + NSTableViewDelegate.swift in Sources */, 322 | 28F40A711CFB61EB006719C8 /* TabBarController.swift in Sources */, 323 | 28DEC8041CD950450012B5E3 /* HUDAlert.swift in Sources */, 324 | 28A5185D1CDE49CD00BE7ED1 /* ExcludeViewController.swift in Sources */, 325 | 28437E4E1CD93DE4002E90EF /* KeyListenerError.swift in Sources */, 326 | 2832CA361CD688E9001D3373 /* AppDelegate.swift in Sources */, 327 | 28A518611CDE8ADF00BE7ED1 /* KeyListener + Realm.swift in Sources */, 328 | 2883C1E61CDE36B10007AC47 /* FirstRunViewController.swift in Sources */, 329 | 28BEB6881CDBEB8200E693D3 /* AccessibilityViewController.swift in Sources */, 330 | 2883C1E31CDE30BF0007AC47 /* Delay.swift in Sources */, 331 | ); 332 | runOnlyForDeploymentPostprocessing = 0; 333 | }; 334 | /* End PBXSourcesBuildPhase section */ 335 | 336 | /* Begin PBXVariantGroup section */ 337 | 2832CA3B1CD688E9001D3373 /* Main.storyboard */ = { 338 | isa = PBXVariantGroup; 339 | children = ( 340 | 2832CA3C1CD688E9001D3373 /* Base */, 341 | ); 342 | name = Main.storyboard; 343 | sourceTree = ""; 344 | }; 345 | /* End PBXVariantGroup section */ 346 | 347 | /* Begin XCBuildConfiguration section */ 348 | 2832CA3F1CD688E9001D3373 /* Debug */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_ANALYZER_NONNULL = YES; 353 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 354 | CLANG_CXX_LIBRARY = "libc++"; 355 | CLANG_ENABLE_MODULES = YES; 356 | CLANG_ENABLE_OBJC_ARC = YES; 357 | CLANG_WARN_BOOL_CONVERSION = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 360 | CLANG_WARN_EMPTY_BODY = YES; 361 | CLANG_WARN_ENUM_CONVERSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 364 | CLANG_WARN_UNREACHABLE_CODE = YES; 365 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 366 | CODE_SIGN_IDENTITY = "-"; 367 | COPY_PHASE_STRIP = NO; 368 | DEBUG_INFORMATION_FORMAT = dwarf; 369 | ENABLE_STRICT_OBJC_MSGSEND = YES; 370 | ENABLE_TESTABILITY = YES; 371 | GCC_C_LANGUAGE_STANDARD = gnu99; 372 | GCC_DYNAMIC_NO_PIC = NO; 373 | GCC_NO_COMMON_BLOCKS = YES; 374 | GCC_OPTIMIZATION_LEVEL = 0; 375 | GCC_PREPROCESSOR_DEFINITIONS = ( 376 | "DEBUG=1", 377 | "$(inherited)", 378 | ); 379 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 380 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 381 | GCC_WARN_UNDECLARED_SELECTOR = YES; 382 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 383 | GCC_WARN_UNUSED_FUNCTION = YES; 384 | GCC_WARN_UNUSED_VARIABLE = YES; 385 | MACOSX_DEPLOYMENT_TARGET = 10.11; 386 | MTL_ENABLE_DEBUG_INFO = YES; 387 | ONLY_ACTIVE_ARCH = YES; 388 | SDKROOT = macosx; 389 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 390 | }; 391 | name = Debug; 392 | }; 393 | 2832CA401CD688E9001D3373 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ALWAYS_SEARCH_USER_PATHS = NO; 397 | CLANG_ANALYZER_NONNULL = YES; 398 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 399 | CLANG_CXX_LIBRARY = "libc++"; 400 | CLANG_ENABLE_MODULES = YES; 401 | CLANG_ENABLE_OBJC_ARC = YES; 402 | CLANG_WARN_BOOL_CONVERSION = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INT_CONVERSION = YES; 408 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 409 | CLANG_WARN_UNREACHABLE_CODE = YES; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | CODE_SIGN_IDENTITY = "-"; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 414 | ENABLE_NS_ASSERTIONS = NO; 415 | ENABLE_STRICT_OBJC_MSGSEND = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_NO_COMMON_BLOCKS = YES; 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | MACOSX_DEPLOYMENT_TARGET = 10.11; 425 | MTL_ENABLE_DEBUG_INFO = NO; 426 | SDKROOT = macosx; 427 | }; 428 | name = Release; 429 | }; 430 | 2832CA421CD688E9001D3373 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | baseConfigurationReference = EAE265B701E85E6B909B8E2C /* Pods-QBlocker.debug.xcconfig */; 433 | buildSettings = { 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | CODE_SIGN_IDENTITY = "Developer ID Application"; 436 | COMBINE_HIDPI_IMAGES = YES; 437 | INFOPLIST_FILE = QBlocker/Info.plist; 438 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 439 | MACOSX_DEPLOYMENT_TARGET = 10.10; 440 | PRODUCT_BUNDLE_IDENTIFIER = uk.co.wearecocoon.QBlocker; 441 | PRODUCT_NAME = "$(TARGET_NAME)"; 442 | SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/QBlocker/Bridge.h"; 443 | }; 444 | name = Debug; 445 | }; 446 | 2832CA431CD688E9001D3373 /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | baseConfigurationReference = 18D302062F0781EB95263F14 /* Pods-QBlocker.release.xcconfig */; 449 | buildSettings = { 450 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 451 | CODE_SIGN_IDENTITY = "Developer ID Application"; 452 | COMBINE_HIDPI_IMAGES = YES; 453 | INFOPLIST_FILE = QBlocker/Info.plist; 454 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 455 | MACOSX_DEPLOYMENT_TARGET = 10.10; 456 | PRODUCT_BUNDLE_IDENTIFIER = uk.co.wearecocoon.QBlocker; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/QBlocker/Bridge.h"; 459 | }; 460 | name = Release; 461 | }; 462 | /* End XCBuildConfiguration section */ 463 | 464 | /* Begin XCConfigurationList section */ 465 | 2832CA2D1CD688E8001D3373 /* Build configuration list for PBXProject "QBlocker" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 2832CA3F1CD688E9001D3373 /* Debug */, 469 | 2832CA401CD688E9001D3373 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 2832CA411CD688E9001D3373 /* Build configuration list for PBXNativeTarget "QBlocker" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 2832CA421CD688E9001D3373 /* Debug */, 478 | 2832CA431CD688E9001D3373 /* Release */, 479 | ); 480 | defaultConfigurationIsVisible = 0; 481 | defaultConfigurationName = Release; 482 | }; 483 | /* End XCConfigurationList section */ 484 | }; 485 | rootObject = 2832CA2A1CD688E8001D3373 /* Project object */; 486 | } 487 | -------------------------------------------------------------------------------- /QBlocker/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | QBlocker uses OS X's accessibility features to block accidental quits. Please enable accessibility features for QBlocker in System Preferences and then re-open the app. 198 | 199 | 200 | 201 | 202 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 428 | 432 | 433 | 447 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | You're all set. QBlocker will stop accidental quits by forcing you to hold ⌘Q instead. If you want to, you can set some rules or let QBlocker work its magic on everything. 567 | 568 | 569 | 570 | 571 | 585 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | --------------------------------------------------------------------------------