Files
DoNotDisturb/configure/Configure/ConfigureWindowController.m

434 lines
10 KiB
Objective-C

//
// file: ConfigureWindowController.m
// project: DND (config)
// description: configure (install/uninstall) window
//
// created by Patrick Wardle
// copyright (c) 2018 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Logging.h"
#import "Configure.h"
#import "Utilities.h"
#import "AppDelegate.h"
#import "ConfigureWindowController.h"
@implementation ConfigureWindowController
@synthesize statusMsg;
@synthesize moreInfoButton;
@synthesize friendsView;
//automatically called when nib is loaded
// just center window
-(void)awakeFromNib
{
//center
[self.window center];
//when supported
// indicate title bar is transparent (too)
if([self.window respondsToSelector:@selector(titlebarAppearsTransparent)])
{
//set transparency
self.window.titlebarAppearsTransparent = YES;
}
//make first responder
// calling this without a timeout sometimes fails :/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (100 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
//set first responder
[self.window makeFirstResponder:self.installButton];
});
return;
}
//configure window/buttons
// also brings window to front
-(void)configure:(BOOL)isInstalled
{
//set title
self.window.title = [NSString stringWithFormat:@"Do Not Disturb (v. %@)", getAppVersion()];
//init status msg
self.statusMsg.stringValue = @"evil maids: stay out! 🙅‍♀️🚪";
//app already installed?
// enable 'uninstall' button
// change install button to say 'upgrade'
if(YES == isInstalled)
{
//enable 'uninstall'
self.uninstallButton.enabled = YES;
//set to 'upgrade'
self.installButton.title = ACTION_UPGRADE;
}
//otherwise disable
else
{
//disable
self.uninstallButton.enabled = NO;
}
//set delegate
[self.window setDelegate:self];
return;
}
//display (show) window
// center, make front, set bg to white, etc
-(void)display
{
//center window
[[self window] center];
//show (now configured) windows
[self showWindow:self];
//make it key window
[self.window makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
//not in mojave dark mode?
// make window color white
if(YES != isDarkMode())
{
//make white
self.window.backgroundColor = NSColor.whiteColor;
}
return;
}
//button handler for uninstall/install
-(IBAction)buttonHandler:(id)sender
{
//frame
NSRect frame = {0};
//action
NSInteger action = 0;
//grab action
action = ((NSButton*)sender).tag;
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling action click: %@ (tag: %ld)", ((NSButton*)sender).title, (long)action]);
//handle various actions
switch(action)
{
//close
case ACTION_CLOSE_FLAG:
[self.window close];
break;
//install/uninstall
case ACTION_INSTALL_FLAG:
case ACTION_UNINSTALL_FLAG:
{
//disable 'x' button
// don't want user killing app during install/upgrade
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:NO];
//clear status msg
[self.statusMsg setStringValue:@""];
//force redraw of status msg
// sometime doesn't refresh (e.g. slow VM)
[self.statusMsg setNeedsDisplay:YES];
//invoke logic to install/uninstall
// do in background so UI doesn't block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//install/uninstall
[self lifeCycleEvent:action];
});
break;
}
//next
// show 'friends' view
case ACTION_NEXT_FLAG:
{
//unset window title
self.window.title = @"";
//get main window's frame
frame = self.window.contentView.frame;
//set origin to 0/0
frame.origin = CGPointZero;
//increase y offset
frame.origin.y += 5;
//reduce height
frame.size.height -= 5;
//pre-req
[self.friendsView setWantsLayer:YES];
//update overlay to take up entire window
self.friendsView.frame = frame;
//not in mojave dark mode?
// make window color white
if(YES != isDarkMode())
{
//set white
self.friendsView.layer.backgroundColor = [NSColor whiteColor].CGColor;
}
//update config view
self.window.contentView = self.friendsView;
//show
self.friendsView.hidden = NO;
//draw
self.friendsView.needsDisplay = YES;
}
default:
break;
}
return;
}
//button handler for '?' button (on an error)
// load objective-see's documentation for error(s) in default browser
-(IBAction)info:(id)sender
{
#pragma unused(sender)
//url
NSURL *helpURL = nil;
//build help URL
helpURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@#errors", PRODUCT_URL]];
//open URL
// invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:helpURL];
return;
}
//perform install | uninstall via Control obj
// invoked on background thread so that UI doesn't block
-(void)lifeCycleEvent:(NSInteger)event
{
//status var
BOOL status = NO;
//begin event
// updates ui on main thread
dispatch_sync(dispatch_get_main_queue(),
^{
//complete
[self beginEvent:event];
});
//sleep
// allow 'install' || 'uninstall' msg to show up
[NSThread sleepForTimeInterval:0.5];
//perform action (install | uninstall)
// perform background actions
if(YES == [((AppDelegate*)[[NSApplication sharedApplication] delegate]).configureObj configure:event])
{
//set flag
status = YES;
}
//error occurred
else
{
//set flag
status = NO;
}
//complet event
// updates ui on main thread
dispatch_async(dispatch_get_main_queue(),
^{
//complete
[self completeEvent:status event:event];
});
return;
}
//begin event
// basically just update UI
-(void)beginEvent:(NSInteger)event
{
//status msg frame
CGRect statusMsgFrame = {{0,0}, {0,0}};
//grab exiting frame
statusMsgFrame = self.statusMsg.frame;
//avoid activity indicator
// shift frame shift delta
statusMsgFrame.origin.x += FRAME_SHIFT;
//update frame to align
self.statusMsg.frame = statusMsgFrame;
//align text left
[self.statusMsg setAlignment:NSTextAlignmentLeft];
//install msg
if(ACTION_INSTALL_FLAG == event)
{
//update status msg
[self.statusMsg setStringValue:@"Installing..."];
}
//uninstall msg
else
{
//update status msg
[self.statusMsg setStringValue:@"Uninstalling..."];
}
//disable action button
self.uninstallButton.enabled = NO;
//disable cancel button
self.installButton.enabled = NO;
//show spinner
self.activityIndicator.hidden = NO;
//start spinner
[self.activityIndicator startAnimation:nil];
return;
}
//complete event
// update UI after background event has finished
-(void)completeEvent:(BOOL)success event:(NSInteger)event
{
//status msg frame
CGRect statusMsgFrame = {{0,0}, {0,0}};
//action
NSString* action = nil;
//result msg
NSMutableString* resultMsg = nil;
//msg font
NSColor* resultMsgColor = nil;
//generally want centered text
[self.statusMsg setAlignment:NSTextAlignmentCenter];
//set action msg for install
if(ACTION_INSTALL_FLAG == event)
{
//set msg
action = @"install";
}
//set action msg for uninstall
else
{
//set msg
action = @"uninstall";
}
//success
if(YES == success)
{
//set result msg
resultMsg = [NSMutableString stringWithFormat:@"Do Not Disturb %@ed!", action];
}
//failure
else
{
//set result msg
resultMsg = [NSMutableString stringWithFormat:@"Error: %@ failed", action];
//show 'get more info' button
self.moreInfoButton.hidden = NO;
}
//stop/hide spinner
[self.activityIndicator stopAnimation:nil];
//hide spinner
self.activityIndicator.hidden = YES;
//grab exiting frame
statusMsgFrame = self.statusMsg.frame;
//shift back since activity indicator is gone
statusMsgFrame.origin.x -= FRAME_SHIFT;
//update frame to align
self.statusMsg.frame = statusMsgFrame;
//set font to bold
[self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]];
//set msg color
[self.statusMsg setTextColor:resultMsgColor];
//set status msg
[self.statusMsg setStringValue:resultMsg];
//set button to 'next'
self.installButton.title = ACTION_NEXT;
//set tag to next
self.installButton.tag = ACTION_NEXT_FLAG;
//enable
self.installButton.enabled = YES;
//...and highlighted
[self.window makeFirstResponder:self.installButton];
//ok to re-enable 'x' button
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:YES];
//(re)make window window key
[self.window makeKeyAndOrderFront:self];
//(re)make window front
[NSApp activateIgnoringOtherApps:YES];
return;
}
//automatically invoked when window is closing
// just exit application
-(void)windowWillClose:(NSNotification *)notification
{
#pragma unused(notification)
//exit
[NSApp terminate:self];
return;
}
@end