mirror of
https://github.com/Codeux-Software/Textual.git
synced 2026-06-16 13:24:32 +00:00
640 lines
19 KiB
Objective-C
640 lines
19 KiB
Objective-C
/* *********************************************************************
|
|
* _____ _ _
|
|
* |_ _|____ _| |_ _ _ __ _| |
|
|
* | |/ _ \ \/ / __| | | |/ _` | |
|
|
* | | __/> <| |_| |_| | (_| | |
|
|
* |_|\___/_/\_\\__|\__,_|\__,_|_|
|
|
*
|
|
* Copyright (c) 2008 - 2010 Satoshi Nakagawa <psychs AT limechat DOT net>
|
|
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
|
|
* Please see Acknowledgements.pdf for additional information.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Textual, "Codeux Software, LLC", nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*********************************************************************** */
|
|
|
|
#import "BuildConfig.h"
|
|
|
|
#import "NSObjectHelperPrivate.h"
|
|
#import "OELReachability.h"
|
|
#import "TDCAlert.h"
|
|
#import "TLOEncryptionManagerPrivate.h"
|
|
#import "TLOLicenseManagerPrivate.h"
|
|
#import "TLOLocalization.h"
|
|
#import "TLOSpeechSynthesizerPrivate.h"
|
|
#import "THOPluginManagerPrivate.h"
|
|
#import "TDCLicenseManagerDialogPrivate.h"
|
|
#import "TVCLogControllerHistoricLogFilePrivate.h"
|
|
#import "TVCLogControllerInlineMediaServicePrivate.h"
|
|
#import "TVCLogControllerOperationQueuePrivate.h"
|
|
#import "TVCMainWindowPrivate.h"
|
|
#import "IRCChannelPrivate.h"
|
|
#import "IRCChannelMemberListPrivate.h"
|
|
#import "IRCCommandIndexPrivate.h"
|
|
#import "IRCExtrasPrivate.h"
|
|
#import "IRCWorldPrivate.h"
|
|
#import "TPCApplicationInfoPrivate.h"
|
|
#import "TPCPreferencesLocalPrivate.h"
|
|
#import "TPCPreferencesUserDefaults.h"
|
|
#import "TPCResourceManagerPrivate.h"
|
|
#import "TPCSandboxMigrationPrivate.h"
|
|
#import "TPCThemeControllerPrivate.h"
|
|
#import "TXMenuControllerPrivate.h"
|
|
#import "TXWindowControllerPrivate.h"
|
|
#import "TXMasterControllerPrivate.h"
|
|
#import "IRCClient.h"
|
|
|
|
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
|
|
#import <Sparkle/Sparkle.h>
|
|
#endif
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface TXMasterController ()
|
|
@property (nonatomic, strong, readwrite) IRCWorld *world;
|
|
@property (nonatomic, assign, readwrite) BOOL debugModeIsOn;
|
|
@property (nonatomic, assign, readwrite) BOOL ghostModeIsOn;
|
|
@property (nonatomic, assign, readwrite) BOOL applicationIsActive;
|
|
@property (nonatomic, assign, readwrite) BOOL applicationIsLaunched;
|
|
@property (nonatomic, assign, readwrite) BOOL applicationIsTerminating;
|
|
@property (nonatomic, assign, readwrite) BOOL applicationIsChangingActiveState;
|
|
@property (readonly) BOOL isSafeToPerformApplicationTermination;
|
|
@property (nonatomic, assign, readwrite) BOOL terminateHistoricLogSaveFinished;
|
|
@property (nonatomic, strong, readwrite) IBOutlet TVCMainWindow *mainWindow;
|
|
@property (nonatomic, weak, readwrite) IBOutlet TXMenuController *menuController;
|
|
@property (nonatomic, assign) NSUInteger applicationLaunchRemainder;
|
|
|
|
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
|
|
@property (nonatomic, strong, readwrite) SPUStandardUpdaterController *updateController;
|
|
#endif
|
|
@end
|
|
|
|
@implementation TXMasterController
|
|
|
|
#pragma mark -
|
|
#pragma mark Initialization
|
|
|
|
- (instancetype)init
|
|
{
|
|
if ((self = [super init])) {
|
|
[NSObject setGlobalMasterControllerClassReference:self];
|
|
|
|
[self prepareInitialState];
|
|
|
|
return self;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)prepareInitialState
|
|
{
|
|
LogToConsoleSetDefaultSubsystemToMainBundle(@"General");
|
|
|
|
NSUInteger keyboardKeys = ([NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask);
|
|
|
|
if ((keyboardKeys & NSEventModifierFlagControl) == NSEventModifierFlagControl) {
|
|
self.debugModeIsOn = YES;
|
|
|
|
LogToConsoleInfo("Launching in debug mode");
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
self.ghostModeIsOn = YES; // Do not use auto connect during debug
|
|
#else
|
|
if ((keyboardKeys & NSEventModifierFlagShift) == NSEventModifierFlagShift) {
|
|
self.ghostModeIsOn = YES;
|
|
|
|
LogToConsoleInfo("Launching without auto connecting to the configured servers");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
static BOOL _awakeFromNibCalled = NO;
|
|
|
|
if (_awakeFromNibCalled == NO) {
|
|
_awakeFromNibCalled = YES;
|
|
|
|
[self _awakeFromNib];
|
|
}
|
|
}
|
|
|
|
- (void)_awakeFromNib
|
|
{
|
|
/* Migrate files and preferences */
|
|
[TPCSandboxMigration migrateResources];
|
|
|
|
/* Initialize preferences */
|
|
[TPCPreferences initPreferences];
|
|
|
|
/* Call shared instance to warm it */
|
|
[TXSharedApplication sharedAppearance];
|
|
|
|
/* We wait until -awakeFromNib to wake the window so that the menu
|
|
controller created by the main nib has time to load. */
|
|
[RZMainBundle() loadNibNamed:@"TVCMainWindow" owner:self topLevelObjects:nil];
|
|
}
|
|
|
|
- (void)applicationWakeStepOne
|
|
{
|
|
self.world = [IRCWorld new];
|
|
}
|
|
|
|
- (void)applicationWakeStepTwo
|
|
{
|
|
[IRCCommandIndex populateCommandIndex];
|
|
|
|
[self prepareNetworkReachabilityNotifier];
|
|
|
|
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerDidWakeUp:) name:NSWorkspaceDidWakeNotification object:nil];
|
|
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerWillSleep:) name:NSWorkspaceWillSleepNotification object:nil];
|
|
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:nil];
|
|
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerScreenDidWake:) name:NSWorkspaceScreensDidWakeNotification object:nil];
|
|
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerScreenWillSleep:) name:NSWorkspaceScreensDidSleepNotification object:nil];
|
|
|
|
[RZNotificationCenter() addObserver:self selector:@selector(pluginsFinishedLoading:) name:THOPluginManagerFinishedLoadingPluginsNotification object:nil];
|
|
|
|
[RZAppleEventManager() setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
|
|
|
|
[NSColorPanel setPickerMask:(NSColorPanelRGBModeMask | NSColorPanelGrayModeMask | NSColorPanelColorListModeMask | NSColorPanelWheelModeMask | NSColorPanelCrayonModeMask)];
|
|
|
|
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
|
|
|
|
XRPerformBlockAsynchronouslyOnGlobalQueueWithPriority(^{
|
|
[TPCResourceManager copyResourcesToApplicationSupportFolder];
|
|
}, DISPATCH_QUEUE_PRIORITY_BACKGROUND);
|
|
|
|
/* We want to guarantee some specific things happen before the
|
|
app is considered "launched" and ready to use. This property
|
|
counts down once each task completes and once it reaches 0,
|
|
then the app is considered launched. */
|
|
/* 1 is default value because we want plugins to be loaded
|
|
before we are finished launching. */
|
|
[self addObserver:self forKeyPath:@"applicationLaunchRemainder" options:NSKeyValueObservingOptionNew context:NULL];
|
|
|
|
self.applicationLaunchRemainder = 1;
|
|
|
|
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
|
|
[self prepareLicenseManager];
|
|
#endif
|
|
|
|
[self prepareThirdPartyServices];
|
|
|
|
/* Load plugins last so that -applicationDidFinishLaunching is posted
|
|
only once they have loaded and everything else has been setup. */
|
|
[sharedPluginManager() loadPlugins];
|
|
}
|
|
|
|
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *, id> *)change context:(nullable void *)context
|
|
{
|
|
if ([keyPath isEqualToString:@"applicationLaunchRemainder"]) {
|
|
if (self.applicationLaunchRemainder == 0) {
|
|
[self applicationDidFinishLaunching];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)pluginsFinishedLoading:(NSNotification *)notification
|
|
{
|
|
self.applicationLaunchRemainder -= 1;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Services
|
|
|
|
- (void)prepareThirdPartyServiceSparkleFramework
|
|
{
|
|
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
|
|
SPUStandardUpdaterController *controller =
|
|
[[SPUStandardUpdaterController alloc] initWithStartingUpdater:NO
|
|
updaterDelegate:(id <SPUUpdaterDelegate>)self
|
|
userDriverDelegate:nil];
|
|
|
|
self.updateController = controller;
|
|
|
|
SPUUpdater *updater = controller.updater;
|
|
|
|
if ([updater respondsToSelector:@selector(clearFeedURLFromUserDefaults)]) {
|
|
[updater performSelector:@selector(clearFeedURLFromUserDefaults)];
|
|
} else {
|
|
[RZUserDefaults() removeObjectForKey:@"SUFeedURL"];
|
|
}
|
|
|
|
NSError *error;
|
|
|
|
(void)[updater startUpdater:&error];
|
|
|
|
if (error) {
|
|
LogToConsoleError("Sparkle failed to start updater: %{public}@", error.description);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
- (void)prepareThirdPartyServices
|
|
{
|
|
[self prepareThirdPartyServiceSparkleFramework];
|
|
}
|
|
|
|
- (void)prepareNetworkReachabilityNotifier
|
|
{
|
|
OELReachability *notifier = [TXSharedApplication sharedNetworkReachabilityNotifier];
|
|
|
|
notifier.reachableBlock = ^(OELReachability *reachability) {
|
|
[self.world noteReachabilityChanged:YES];
|
|
};
|
|
|
|
notifier.unreachableBlock = ^(OELReachability *reachability) {
|
|
[self.world noteReachabilityChanged:NO];
|
|
};
|
|
|
|
[notifier startNotifier];
|
|
}
|
|
|
|
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
|
|
- (void)prepareLicenseManager
|
|
{
|
|
TLOLicenseManagerSetup();
|
|
|
|
[[TXSharedApplication sharedLicenseManagerDialog] applicationDidFinishLaunching];
|
|
}
|
|
#endif
|
|
|
|
#pragma mark -
|
|
#pragma mark NSApplication Delegate
|
|
|
|
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
|
{
|
|
/* UserNotifications.framework wants delegation set before app has
|
|
finished launching. A simple access to the singleton will set this
|
|
for us which we can just do here. */
|
|
LogToConsoleDebug("Preparing notification controller singeton: %@",
|
|
sharedNotificationController().description);
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching
|
|
{
|
|
[self removeObserver:self forKeyPath:@"applicationLaunchRemainder"];
|
|
|
|
self.applicationIsLaunched = YES;
|
|
|
|
if ([self.mainWindow reloadLoadingScreen]) {
|
|
[self.world autoConnectAfterWakeup:NO];
|
|
}
|
|
|
|
[self.mainWindow maybeToggleFullscreenAfterLaunch];
|
|
}
|
|
|
|
- (void)applicationWillResignActive:(NSNotification *)notification
|
|
{
|
|
self.applicationIsChangingActiveState = YES;
|
|
}
|
|
|
|
- (void)applicationWillBecomeActive:(NSNotification *)notification
|
|
{
|
|
self.applicationIsChangingActiveState = YES;
|
|
}
|
|
|
|
- (void)applicationDidResignActive:(NSNotification *)notification
|
|
{
|
|
self.applicationIsActive = NO;
|
|
self.applicationIsChangingActiveState = NO;
|
|
}
|
|
|
|
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
|
{
|
|
self.applicationIsActive = YES;
|
|
self.applicationIsChangingActiveState = NO;
|
|
}
|
|
|
|
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
|
|
{
|
|
if (self.applicationIsTerminating) {
|
|
return NO;
|
|
}
|
|
|
|
[self.mainWindow makeKeyAndOrderFront:nil];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
|
|
{
|
|
if (self.applicationIsTerminating) {
|
|
return NO;
|
|
}
|
|
|
|
[self.mainWindow makeKeyAndOrderFront:nil];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)sender
|
|
{
|
|
/* This will have no effect on our app. Implement to suppress warning in console. */
|
|
return YES;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark NSApplication Terminate Procedure
|
|
|
|
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
|
|
{
|
|
return self.menuController.dockMenu;
|
|
}
|
|
|
|
- (BOOL)queryTerminate
|
|
{
|
|
if (self.applicationIsTerminating) {
|
|
LogToConsoleTerminationProgress("Termination is already in progress");
|
|
|
|
return YES;
|
|
}
|
|
|
|
if ([TPCPreferences confirmQuit] == NO) {
|
|
return YES;
|
|
}
|
|
|
|
BOOL stillConnected = NO;
|
|
|
|
for (IRCClient *u in worldController().clientList) {
|
|
if (u.isConnecting || u.isConnected) {
|
|
stillConnected = YES;
|
|
}
|
|
}
|
|
|
|
if (stillConnected) {
|
|
BOOL result = [TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[77u-vp]")
|
|
title:TXTLS(@"Prompts[6vj-2p]")
|
|
defaultButton:TXTLS(@"Prompts[1bf-k0]")
|
|
alternateButton:TXTLS(@"Prompts[qso-2g]")];
|
|
|
|
LogToConsoleTerminationProgress("Perform termination: %{BOOL}d", result);
|
|
|
|
return result;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
|
{
|
|
if ([self queryTerminate] == NO) {
|
|
return NSTerminateCancel;
|
|
}
|
|
|
|
XRPerformBlockAsynchronouslyOnMainQueue(^{
|
|
[self performApplicationTerminationStepOne];
|
|
});
|
|
|
|
return NSTerminateLater;
|
|
}
|
|
|
|
- (BOOL)isSafeToPerformApplicationTermination
|
|
{
|
|
/* Clients are still disconnecting */
|
|
BOOL condition1 = (self.terminatingClientCount == 0);
|
|
|
|
/* Core Data is saving */
|
|
BOOL condition2 = (TVCLogControllerHistoricLogSharedInstance().isSaving == NO &&
|
|
self.terminateHistoricLogSaveFinished);
|
|
|
|
|
|
LogToConsoleTerminationProgress("Conditions: %{BOOL}d %{BOOL}d", condition1, condition2);
|
|
|
|
return (condition1 && condition2);
|
|
}
|
|
|
|
- (void)performApplicationTerminationStepOne
|
|
{
|
|
LogToConsoleTerminationProgress("Step one entry");
|
|
|
|
self.applicationIsTerminating = YES;
|
|
|
|
[[TXSharedApplication sharedAppearance] prepareForApplicationTermination];
|
|
|
|
[self.mainWindow prepareForApplicationTermination];
|
|
|
|
LogToConsoleTerminationProgress("Giving up shared application delegation");
|
|
|
|
[[NSApplication sharedApplication] setDelegate:nil];
|
|
|
|
LogToConsoleTerminationProgress("Removing workspace notification center observer");
|
|
|
|
[RZWorkspaceNotificationCenter() removeObserver:self];
|
|
|
|
LogToConsoleTerminationProgress("Removing shared notification center observer");
|
|
|
|
[RZNotificationCenter() removeObserver:self];
|
|
|
|
LogToConsoleTerminationProgress("Removing AppleScript event observer");
|
|
|
|
[RZAppleEventManager() removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
|
|
|
|
LogToConsoleTerminationProgress("Stopping reachability notifier");
|
|
|
|
[[TXSharedApplication sharedNetworkReachabilityNotifier] stopNotifier];
|
|
|
|
LogToConsoleTerminationProgress("Stopping speech synthesizer");
|
|
|
|
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:YES];
|
|
|
|
[TVCLogControllerInlineMediaSharedInstance() prepareForApplicationTermination];
|
|
|
|
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
|
|
[sharedEncryptionManager() prepareForApplicationTermination];
|
|
#endif
|
|
|
|
[self.menuController prepareForApplicationTermination];
|
|
|
|
[self performApplicationTerminationStepTwo];
|
|
}
|
|
|
|
- (void)performApplicationTerminationStepTwo
|
|
{
|
|
if (self.applicationIsTerminating == NO) {
|
|
return;
|
|
}
|
|
|
|
LogToConsoleTerminationProgress("Step two entry");
|
|
|
|
self.terminatingClientCount = worldController().clientCount;
|
|
|
|
[self.world prepareForApplicationTermination];
|
|
|
|
if (self.isSafeToPerformApplicationTermination) {
|
|
[self performApplicationTerminationStepThree];
|
|
|
|
return;
|
|
}
|
|
|
|
/* We want certain things to 100% happen before the app completely closes.
|
|
This block that is performed below loops until all these actions are completed.
|
|
Notable actions: gracefully leaving IRC, saving historic logs, etc. */
|
|
XRPerformBlockAsynchronouslyOnGlobalQueueWithPriority(^{
|
|
do {
|
|
/* We wait until this value reaches zero so that
|
|
view controllers had the chance to perform any
|
|
changes they want to historic log. */
|
|
if (self.terminatingClientCount == 0) {
|
|
[TVCLogControllerHistoricLogSharedInstance() prepareForApplicationTermination];
|
|
|
|
self.terminateHistoricLogSaveFinished = YES;
|
|
}
|
|
|
|
/* Sleep a little bit so we aren't looping a lot. */
|
|
[NSThread sleepForTimeInterval:0.5];
|
|
} while (self.isSafeToPerformApplicationTermination == NO);
|
|
|
|
XRPerformBlockAsynchronouslyOnMainQueue(^{
|
|
[self performApplicationTerminationStepThree];
|
|
});
|
|
}, DISPATCH_QUEUE_PRIORITY_HIGH);
|
|
}
|
|
|
|
- (void)performApplicationTerminationStepThree
|
|
{
|
|
if (self.applicationIsTerminating == NO) {
|
|
return;
|
|
}
|
|
|
|
LogToConsoleTerminationProgress("Step three entry");
|
|
|
|
if (self.skipTerminateSave == NO) {
|
|
LogToConsoleTerminationProgress("Saving IRC world");
|
|
|
|
[self.world save];
|
|
}
|
|
|
|
LogToConsoleTerminationProgress("Suspending member list dispatch queue");
|
|
|
|
[IRCChannelMemberList suspendMemberListSerialQueues];
|
|
|
|
LogToConsoleTerminationProgress("Unloading plugins");
|
|
|
|
[sharedPluginManager() unloadPlugins];
|
|
|
|
[windowController() prepareForApplicationTermination];
|
|
|
|
[themeController() prepareForApplicationTermination];
|
|
|
|
LogToConsoleTerminationProgress("Saving running internal");
|
|
|
|
[TPCApplicationInfo saveTimeIntervalSinceApplicationInstall];
|
|
|
|
LogToConsoleTerminationProgress("Terminate");
|
|
|
|
[NSApp replyToApplicationShouldTerminate:YES];
|
|
}
|
|
|
|
- (void)terminateGracefully
|
|
{
|
|
self.applicationIsTerminating = YES;
|
|
|
|
[RZSharedApplication() terminate:nil];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark NSWorkspace Notifications
|
|
|
|
- (void)handleURLEvent:(NSAppleEventDescriptor *)event
|
|
withReplyEvent:(NSAppleEventDescriptor *)replyEvent
|
|
{
|
|
NSAppleEventDescriptor *description = [event descriptorAtIndex:1];
|
|
|
|
NSString *stringValue = description.stringValue;
|
|
|
|
[IRCExtras parseIRCProtocolURI:stringValue withDescriptor:event];
|
|
}
|
|
|
|
- (void)computerScreenWillSleep:(NSNotification *)note
|
|
{
|
|
LogToConsole("Preparing for screen sleep");
|
|
|
|
[self.world prepareForScreenSleep];
|
|
}
|
|
|
|
- (void)computerScreenDidWake:(NSNotification *)note
|
|
{
|
|
LogToConsole("Waking from screen sleep");
|
|
|
|
[self.world wakeFromScreenSleep];
|
|
}
|
|
|
|
- (void)computerWillSleep:(NSNotification *)note
|
|
{
|
|
LogToConsole("Preparing for sleep");
|
|
|
|
[self.world prepareForSleep];
|
|
|
|
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:YES];
|
|
[[TXSharedApplication sharedSpeechSynthesizer] clearQueue];
|
|
|
|
[[TXSharedApplication sharedNetworkReachabilityNotifier] stopNotifier];
|
|
}
|
|
|
|
- (void)computerDidWakeUp:(NSNotification *)note
|
|
{
|
|
LogToConsole("Waking from sleep");
|
|
|
|
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:NO];
|
|
|
|
[[TXSharedApplication sharedNetworkReachabilityNotifier] startNotifier];
|
|
|
|
[self.world autoConnectAfterWakeup:YES];
|
|
}
|
|
|
|
- (void)computerWillPowerOff:(NSNotification *)note
|
|
{
|
|
[self terminateGracefully];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Sparkle Delegate
|
|
|
|
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
|
|
- (void)updaterWillRelaunchApplication:(SPUUpdater *)updater
|
|
{
|
|
self.applicationIsTerminating = YES;
|
|
}
|
|
|
|
- (NSSet<NSString *> *)allowedChannelsForUpdater:(SPUUpdater *)updater
|
|
{
|
|
BOOL receiveBetaUpdates = [TPCPreferences receiveBetaUpdates];
|
|
|
|
if (receiveBetaUpdates) {
|
|
return [NSSet setWithObject:@"beta"];
|
|
}
|
|
|
|
return [NSSet set];
|
|
}
|
|
#endif
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|