242 lines
8.9 KiB
Objective-C
242 lines
8.9 KiB
Objective-C
#import "MJPreferencesWindowController.h"
|
|
#import "MJAutoLaunch.h"
|
|
#import "MJLua.h"
|
|
#import "MJDockIcon.h"
|
|
#import "MJMenuIcon.h"
|
|
#import "MJAccessibilityUtils.h"
|
|
#import "MJConsoleWindowController.h"
|
|
#import "variables.h"
|
|
#import "secrets.h"
|
|
|
|
//
|
|
// Enable & Disable Preferences Dark Mode:
|
|
//
|
|
BOOL PreferencesDarkModeEnabled(void) {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey:HSPreferencesDarkModeKey];
|
|
}
|
|
|
|
void PreferencesDarkModeSetEnabled(BOOL enabled) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:enabled
|
|
forKey:HSPreferencesDarkModeKey];
|
|
}
|
|
|
|
|
|
#define MJSkipDockMenuIconProblemAlertKey @"MJSkipDockMenuIconProblemAlertKey"
|
|
|
|
@interface MJPreferencesWindowController ()
|
|
|
|
@property (weak) IBOutlet NSButton* openAtLoginCheckbox;
|
|
@property (weak) IBOutlet NSButton* showDockIconCheckbox;
|
|
@property (weak) IBOutlet NSButton* showMenuIconCheckbox;
|
|
@property (weak) IBOutlet NSButton* keepConsoleOnTopCheckbox;
|
|
@property (weak) IBOutlet NSButton* uploadCrashDataCheckbox;
|
|
@property (weak) IBOutlet NSButton* updatesCheckbox;
|
|
|
|
@property BOOL isAccessibilityEnabled;
|
|
|
|
@end
|
|
|
|
@implementation MJPreferencesWindowController
|
|
|
|
+ (instancetype) singleton {
|
|
static MJPreferencesWindowController* s;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
s = [[MJPreferencesWindowController alloc] init];
|
|
});
|
|
return s;
|
|
}
|
|
|
|
- (void) setup {
|
|
[self reflectDefaults];
|
|
}
|
|
|
|
- (void) reflectDefaults {
|
|
|
|
//
|
|
// Dark Mode:
|
|
//
|
|
if (PreferencesDarkModeEnabled()) {
|
|
self.window.appearance = [NSAppearance appearanceNamed: NSAppearanceNameVibrantDark] ;
|
|
self.window.titlebarAppearsTransparent = YES ;
|
|
} else {
|
|
self.window.appearance = [NSAppearance appearanceNamed: NSAppearanceNameVibrantLight] ;
|
|
self.window.titlebarAppearsTransparent = NO ;
|
|
}
|
|
|
|
}
|
|
|
|
- (void)updateFeedbackDisplay:(NSNotification __unused *)notification {
|
|
[self.openAtLoginCheckbox setState:MJAutoLaunchGet() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.showDockIconCheckbox setState: MJDockIconVisible() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.showMenuIconCheckbox setState: MJMenuIconVisible() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.keepConsoleOnTopCheckbox setState: MJConsoleWindowAlwaysOnTop() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.uploadCrashDataCheckbox setState: HSUploadCrashData() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
#ifndef SENTRY_API_URL
|
|
[self.uploadCrashDataCheckbox setState:NSControlStateValueOff];
|
|
[self.uploadCrashDataCheckbox setEnabled:NO];
|
|
#endif
|
|
|
|
}
|
|
|
|
- (void) showWindow:(id)sender {
|
|
if (![[self window] isVisible])
|
|
[[self window] center];
|
|
[super showWindow: sender];
|
|
[self reflectDefaults];
|
|
}
|
|
|
|
- (NSString*) windowNibName {
|
|
return @"PreferencesWindow";
|
|
}
|
|
|
|
- (void)windowDidLoad {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self cacheIsAccessibilityEnabled];
|
|
});
|
|
|
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(accessibilityChanged:) name:@"com.apple.accessibility.api" object:nil];
|
|
|
|
[self.openAtLoginCheckbox setState:MJAutoLaunchGet() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.showDockIconCheckbox setState: MJDockIconVisible() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.showMenuIconCheckbox setState: MJMenuIconVisible() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.keepConsoleOnTopCheckbox setState: MJConsoleWindowAlwaysOnTop() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
[self.uploadCrashDataCheckbox setState: HSUploadCrashData() ? NSControlStateValueOn : NSControlStateValueOff];
|
|
|
|
if (NSClassFromString(@"SUUpdater")) {
|
|
NSString *frameworkPath = [[[NSBundle bundleForClass:[self class]] privateFrameworksPath] stringByAppendingPathComponent:@"Sparkle.framework"];
|
|
if ([[NSBundle bundleWithPath:frameworkPath] load]) {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
|
id updater = [NSClassFromString(@"SUUpdater") performSelector:@selector(sharedUpdater)];
|
|
#pragma clang diagnostic pop
|
|
[self.updatesCheckbox bind:@"value" toObject:updater withKeyPath:@"automaticallyChecksForUpdates" options:nil];
|
|
} else {
|
|
NSLog(@"Could not load %@ while trying to construct SUUpdater!", frameworkPath);
|
|
}
|
|
} else {
|
|
NSLog(@"SUUpdater doesn't exist, disabling updates checkbox in Preferences");
|
|
[self.updatesCheckbox setState:NSControlStateValueOff];
|
|
[self.updatesCheckbox setEnabled:NO];
|
|
}
|
|
|
|
#ifndef SENTRY_API_URL
|
|
[self.uploadCrashDataCheckbox setState:NSControlStateValueOff];
|
|
[self.uploadCrashDataCheckbox setEnabled:NO];
|
|
#endif
|
|
|
|
NSNotificationCenter *changeWatcher = [NSNotificationCenter defaultCenter];
|
|
[changeWatcher addObserver:self
|
|
selector:@selector(updateFeedbackDisplay:)
|
|
name:NSUserDefaultsDidChangeNotification
|
|
object:nil];
|
|
|
|
}
|
|
|
|
- (void) accessibilityChanged:(NSNotification*)note {
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
[self cacheIsAccessibilityEnabled];
|
|
});
|
|
}
|
|
|
|
- (void) cacheIsAccessibilityEnabled {
|
|
self.isAccessibilityEnabled = MJAccessibilityIsEnabled();
|
|
}
|
|
|
|
- (NSString*) maybeEnableAccessibilityString {
|
|
if (self.isAccessibilityEnabled)
|
|
return @"Accessibility is enabled. You're all set!";
|
|
else
|
|
return @"WARNING! Accessibility is not enabled!";
|
|
}
|
|
|
|
- (NSImage*) isAccessibilityEnabledImage {
|
|
if (self.isAccessibilityEnabled)
|
|
return [NSImage imageNamed:NSImageNameStatusAvailable];
|
|
else
|
|
return [NSImage imageNamed:NSImageNameStatusUnavailable];
|
|
}
|
|
|
|
+ (NSSet*) keyPathsForValuesAffectingMaybeEnableAccessibilityString {
|
|
return [NSSet setWithArray:@[@"isAccessibilityEnabled"]];
|
|
}
|
|
|
|
+ (NSSet*) keyPathsForValuesAffectingIsAccessibilityEnabledImage {
|
|
return [NSSet setWithArray:@[@"isAccessibilityEnabled"]];
|
|
}
|
|
|
|
- (IBAction) openAccessibility:(id)sender {
|
|
MJAccessibilityOpenPanel();
|
|
}
|
|
|
|
- (IBAction) toggleOpensAtLogin:(NSButton*)sender {
|
|
BOOL enabled = [sender state] == NSControlStateValueOn;
|
|
MJAutoLaunchSet(enabled);
|
|
}
|
|
|
|
- (IBAction) toggleShowDockIcon:(NSButton*)sender {
|
|
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(actuallyToggleShowDockIcon) object:nil];
|
|
[self performSelector:@selector(actuallyToggleShowDockIcon) withObject:nil afterDelay:0.3];
|
|
}
|
|
|
|
- (void) actuallyToggleShowDockIcon {
|
|
BOOL enabled = [self.showDockIconCheckbox state] == NSControlStateValueOn;
|
|
MJDockIconSetVisible(enabled);
|
|
[self maybeWarnAboutDockMenuProblem];
|
|
}
|
|
|
|
- (IBAction) toggleMenuDockIcon:(NSButton*)sender {
|
|
BOOL enabled = [sender state] == NSControlStateValueOn;
|
|
MJMenuIconSetVisible(enabled);
|
|
[self maybeWarnAboutDockMenuProblem];
|
|
}
|
|
|
|
- (IBAction) toggleKeepConsoleOnTop:(id)sender {
|
|
MJConsoleWindowSetAlwaysOnTop([sender state] == NSControlStateValueOn);
|
|
}
|
|
|
|
- (IBAction) toggleUploadCrashData:(id)sender {
|
|
HSSetUploadCrashData([sender state] == NSControlStateValueOn);
|
|
}
|
|
|
|
- (IBAction) privacyPolicyClicked:(id)sender {
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.hammerspoon.org/privacy"]];
|
|
}
|
|
|
|
- (void) dockMenuProblemAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
|
BOOL skipNextTime = ([[alert suppressionButton] state] == NSControlStateValueOn);
|
|
[[NSUserDefaults standardUserDefaults] setBool:skipNextTime forKey:MJSkipDockMenuIconProblemAlertKey];
|
|
}
|
|
|
|
- (void) maybeWarnAboutDockMenuProblem {
|
|
if (MJMenuIconVisible() || MJDockIconVisible())
|
|
return;
|
|
|
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:MJSkipDockMenuIconProblemAlertKey])
|
|
return;
|
|
|
|
NSAlert* alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSAlertStyleWarning];
|
|
[alert setMessageText:@"How to get back to this window"];
|
|
[alert setInformativeText:@"When both the dock icon and menu icon are disabled, you can get back to this Preferences window by activating Hammerspoon from Spotlight or by running `open -a Hammerspoon` from Terminal, and then pressing Command + Comma."];
|
|
[alert setShowsSuppressionButton:YES];
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[alert beginSheetModalForWindow:[self window]
|
|
modalDelegate:self
|
|
didEndSelector:@selector(dockMenuProblemAlertDidEnd:returnCode:contextInfo:)
|
|
contextInfo:NULL];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
BOOL HSUploadCrashData(void) {
|
|
return [[NSUserDefaults standardUserDefaults] boolForKey: HSUploadCrashDataKey];
|
|
}
|
|
|
|
void HSSetUploadCrashData(BOOL uploadCrashData) {
|
|
[[NSUserDefaults standardUserDefaults] setBool:uploadCrashData forKey:HSUploadCrashDataKey];
|
|
}
|