Zonble: Swiftify // AppDelegate
- Applied the usage of frmAboutWindow and Hiraku's Notification Center implementation. - Disables auto-update by default since this feature is not finished for vChewing. Co-Authored-By: Hiraku <hirakujira@users.noreply.github.com>
This commit is contained in:
parent
a70e96a778
commit
f8d5febadb
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
// AppDelegate.h
|
|
||||||
//
|
|
||||||
// Copyright (c) 2021-2022 The vChewing Project.
|
|
||||||
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
|
||||||
//
|
|
||||||
// Contributors:
|
|
||||||
// Mengjuei Hsieh (@mjhsieh) @ OpenVanilla
|
|
||||||
// Weizhong Yang (@zonble) @ OpenVanilla
|
|
||||||
// Lukhnos Liu (@lukhnos) @ OpenVanilla
|
|
||||||
// Hiraku Wang (@hirakujira) @ vChewing
|
|
||||||
// Shiki Suen (@ShikiSuen) @ vChewing
|
|
||||||
//
|
|
||||||
// Based on the Syrup Project and the Formosana Library
|
|
||||||
// by Lukhnos Liu (@lukhnos).
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person
|
|
||||||
// obtaining a copy of this software and associated documentation
|
|
||||||
// files (the "Software"), to deal in the Software without
|
|
||||||
// restriction, including without limitation the rights to use,
|
|
||||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following
|
|
||||||
// conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be
|
|
||||||
// included in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "frmAboutWindow.h"
|
|
||||||
|
|
||||||
@class PreferencesWindowController;
|
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
|
|
||||||
{
|
|
||||||
@private
|
|
||||||
NSURLConnection *_updateCheckConnection;
|
|
||||||
BOOL _currentUpdateCheckIsForced;
|
|
||||||
NSMutableData *_receivingData;
|
|
||||||
NSURL *_updateNextStepURL;
|
|
||||||
PreferencesWindowController *_preferencesWindowController;
|
|
||||||
frmAboutWindow *_aboutWindowController;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)checkForUpdate;
|
|
||||||
- (void)checkForUpdateForced:(BOOL)forced;
|
|
||||||
- (void)showPreferences;
|
|
||||||
- (void)showAbout;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) IBOutlet NSWindow *window;
|
|
||||||
@end
|
|
|
@ -1,296 +0,0 @@
|
||||||
//
|
|
||||||
// AppDelegate.m
|
|
||||||
//
|
|
||||||
// Copyright (c) 2021-2022 The vChewing Project.
|
|
||||||
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
|
||||||
//
|
|
||||||
// Contributors:
|
|
||||||
// Mengjuei Hsieh (@mjhsieh) @ OpenVanilla
|
|
||||||
// Weizhong Yang (@zonble) @ OpenVanilla
|
|
||||||
// Lukhnos Liu (@lukhnos) @ OpenVanilla
|
|
||||||
// Hiraku Wang (@hirakujira) @ vChewing
|
|
||||||
// Shiki Suen (@ShikiSuen) @ vChewing
|
|
||||||
//
|
|
||||||
// Based on the Syrup Project and the Formosana Library
|
|
||||||
// by Lukhnos Liu (@lukhnos).
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person
|
|
||||||
// obtaining a copy of this software and associated documentation
|
|
||||||
// files (the "Software"), to deal in the Software without
|
|
||||||
// restriction, including without limitation the rights to use,
|
|
||||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following
|
|
||||||
// conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be
|
|
||||||
// included in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
#import "vChewing-Swift.h"
|
|
||||||
#import "frmAboutWindow.h"
|
|
||||||
|
|
||||||
extern void LTLoadLanguageModel(void);
|
|
||||||
extern void LTLoadUserLanguageModelFile(void);
|
|
||||||
|
|
||||||
static NSString *kCheckUpdateAutomatically = @"CheckUpdateAutomatically";
|
|
||||||
static NSString *kNextUpdateCheckDateKey = @"NextUpdateCheckDate";
|
|
||||||
static NSString *kUpdateInfoEndpointKey = @"UpdateInfoEndpoint";
|
|
||||||
static NSString *kUpdateInfoSiteKey = @"UpdateInfoSite";
|
|
||||||
static const NSTimeInterval kNextCheckInterval = 86400.0;
|
|
||||||
static const NSTimeInterval kTimeoutInterval = 60.0;
|
|
||||||
|
|
||||||
@interface AppDelegate () <NSURLConnectionDataDelegate, NonModalAlertWindowControllerDelegate>
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
|
||||||
@synthesize window = _window;
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
_preferencesWindowController = nil;
|
|
||||||
_aboutWindowController = nil;
|
|
||||||
_updateCheckConnection = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)inNotification
|
|
||||||
{
|
|
||||||
LTLoadLanguageModel();
|
|
||||||
LTLoadUserLanguageModelFile();
|
|
||||||
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] objectForKey:kCheckUpdateAutomatically]) {
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kCheckUpdateAutomatically];
|
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self checkForUpdate];
|
|
||||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)checkForUpdate
|
|
||||||
{
|
|
||||||
[self checkForUpdateForced:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)checkForUpdateForced:(BOOL)forced
|
|
||||||
{
|
|
||||||
if (_updateCheckConnection) {
|
|
||||||
// busy
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentUpdateCheckIsForced = forced;
|
|
||||||
|
|
||||||
// time for update?
|
|
||||||
if (!forced) {
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:kCheckUpdateAutomatically]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDate *now = [NSDate date];
|
|
||||||
NSDate *date = [[NSUserDefaults standardUserDefaults] objectForKey:kNextUpdateCheckDateKey];
|
|
||||||
if (![date isKindOfClass:[NSDate class]]) {
|
|
||||||
date = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([now compare:date] == NSOrderedAscending) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDate *nextUpdateDate = [NSDate dateWithTimeInterval:kNextCheckInterval sinceDate:[NSDate date]];
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:nextUpdateDate forKey:kNextUpdateCheckDateKey];
|
|
||||||
|
|
||||||
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
|
||||||
NSString *updateInfoURLString = [infoDict objectForKey:kUpdateInfoEndpointKey];
|
|
||||||
if (![updateInfoURLString length]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSURL *updateInfoURL = [NSURL URLWithString:updateInfoURLString];
|
|
||||||
if (!updateInfoURL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSURLRequest *request = [NSURLRequest requestWithURL:updateInfoURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:kTimeoutInterval];
|
|
||||||
if (!request) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if DEBUG
|
|
||||||
NSLog(@"about to request update url %@ ",updateInfoURL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (_receivingData) {
|
|
||||||
_receivingData = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new data buffer and connection
|
|
||||||
_receivingData = [[NSMutableData alloc] init];
|
|
||||||
_updateCheckConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
|
|
||||||
[_updateCheckConnection start];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showPreferences
|
|
||||||
{
|
|
||||||
if (!_preferencesWindowController) {
|
|
||||||
_preferencesWindowController = [[PreferencesWindowController alloc] initWithWindowNibName:@"preferences"];
|
|
||||||
}
|
|
||||||
[[_preferencesWindowController window] center];
|
|
||||||
[[_preferencesWindowController window] orderFront:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
|
|
||||||
{
|
|
||||||
BOOL isForcedCheck = _currentUpdateCheckIsForced;
|
|
||||||
|
|
||||||
_receivingData = nil;
|
|
||||||
_updateCheckConnection = nil;
|
|
||||||
_currentUpdateCheckIsForced = NO;
|
|
||||||
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[[NonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"Update Check Failed", nil) content:[NSString stringWithFormat:NSLocalizedString(@"There may be no internet connection or the server failed to respond.\n\nError message: %@", nil), [error localizedDescription]] confirmButtonTitle:NSLocalizedString(@"Dismiss", nil) cancelButtonTitle:nil cancelAsDefault:NO delegate:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showNoUpdateAvailableAlert
|
|
||||||
{
|
|
||||||
[[NonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"Check for Update Completed", nil) content:NSLocalizedString(@"You are already using the latest version of vChewing.", nil) confirmButtonTitle:NSLocalizedString(@"OK", nil) cancelButtonTitle:nil cancelAsDefault:NO delegate:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
|
|
||||||
{
|
|
||||||
id plist = [NSPropertyListSerialization propertyListWithData:_receivingData options:NSPropertyListImmutable format:NULL error:NULL];
|
|
||||||
#if DEBUG
|
|
||||||
NSLog(@"plist %@",plist);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BOOL isForcedCheck = _currentUpdateCheckIsForced;
|
|
||||||
|
|
||||||
_receivingData = nil;
|
|
||||||
_updateCheckConnection = nil;
|
|
||||||
_currentUpdateCheckIsForced = NO;
|
|
||||||
|
|
||||||
if (!plist) {
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[self showNoUpdateAvailableAlert];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *remoteVersion = [plist objectForKey:(id)kCFBundleVersionKey];
|
|
||||||
#if DEBUG
|
|
||||||
NSLog(@"the remoteversion is %@",remoteVersion);
|
|
||||||
#endif
|
|
||||||
if (!remoteVersion) {
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[self showNoUpdateAvailableAlert];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Validate info (e.g. bundle identifier)
|
|
||||||
// TODO: Use HTML to display change log, need a new key like UpdateInfoChangeLogURL for this
|
|
||||||
|
|
||||||
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
|
||||||
NSString *currentVersion = [infoDict objectForKey:(id)kCFBundleVersionKey];
|
|
||||||
NSComparisonResult result = [currentVersion compare:remoteVersion options:NSNumericSearch];
|
|
||||||
|
|
||||||
if (result != NSOrderedAscending) {
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[self showNoUpdateAvailableAlert];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NSString *siteInfoURLString = [plist objectForKey:kUpdateInfoSiteKey];
|
|
||||||
if (!siteInfoURLString) {
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[self showNoUpdateAvailableAlert];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSURL *siteInfoURL = [NSURL URLWithString:siteInfoURLString];
|
|
||||||
if (!siteInfoURL) {
|
|
||||||
if (isForcedCheck) {
|
|
||||||
[self showNoUpdateAvailableAlert];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_updateNextStepURL = siteInfoURL;
|
|
||||||
|
|
||||||
NSDictionary *versionDescriptions = [plist objectForKey:@"Description"];
|
|
||||||
NSString *versionDescription = @"";
|
|
||||||
if ([versionDescriptions isKindOfClass:[NSDictionary class]]) {
|
|
||||||
NSString *locale = @"en";
|
|
||||||
NSArray *supportedLocales = [NSArray arrayWithObjects:@"en", @"zh-Hant", @"zh-Hans", nil];
|
|
||||||
NSArray *preferredTags = [NSBundle preferredLocalizationsFromArray:supportedLocales];
|
|
||||||
if ([preferredTags count]) {
|
|
||||||
locale = [preferredTags objectAtIndex:0];
|
|
||||||
}
|
|
||||||
versionDescription = [versionDescriptions objectForKey:locale];
|
|
||||||
if (!versionDescription) {
|
|
||||||
versionDescription = [versionDescriptions objectForKey:@"en"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!versionDescription) {
|
|
||||||
versionDescription = @"";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
versionDescription = [@"\n\n" stringByAppendingString:versionDescription];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *content = [NSString stringWithFormat:NSLocalizedString(@"You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@", nil), [infoDict objectForKey:@"CFBundleShortVersionString"], currentVersion, [plist objectForKey:@"CFBundleShortVersionString"], remoteVersion, versionDescription];
|
|
||||||
|
|
||||||
[[NonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"New Version Available", nil) content:content confirmButtonTitle:NSLocalizedString(@"Visit Website", nil) cancelButtonTitle:NSLocalizedString(@"Not Now", nil) cancelAsDefault:NO delegate:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
|
|
||||||
{
|
|
||||||
[_receivingData appendData:data];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)nonModalAlertWindowControllerDidConfirm:(NonModalAlertWindowController *)controller
|
|
||||||
{
|
|
||||||
if (_updateNextStepURL) {
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:_updateNextStepURL];
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateNextStepURL = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)nonModalAlertWindowControllerDidCancel:(NonModalAlertWindowController *)controller
|
|
||||||
{
|
|
||||||
_updateNextStepURL = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)showAbout {
|
|
||||||
if (!_aboutWindowController) {
|
|
||||||
_aboutWindowController = [[frmAboutWindow alloc] initWithWindowNibName:@"frmAboutWindow"];
|
|
||||||
}
|
|
||||||
[[_aboutWindowController window] center];
|
|
||||||
[[_aboutWindowController window] orderFrontRegardless];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction) about:(id)sender {
|
|
||||||
[(AppDelegate *)[NSApp delegate] showAbout];
|
|
||||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,251 @@
|
||||||
|
//
|
||||||
|
// AppDelegate.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021-2022 The vChewing Project.
|
||||||
|
// Copyright (c) 2011-2022 The OpenVanilla Project.
|
||||||
|
//
|
||||||
|
// Contributors:
|
||||||
|
// Mengjuei Hsieh (@mjhsieh) @ OpenVanilla
|
||||||
|
// Weizhong Yang (@zonble) @ OpenVanilla
|
||||||
|
// Lukhnos Liu (@lukhnos) @ OpenVanilla
|
||||||
|
// Hiraku Wang (@hirakujira) @ vChewing
|
||||||
|
// Shiki Suen (@ShikiSuen) @ vChewing
|
||||||
|
//
|
||||||
|
// Based on the Syrup Project and the Formosana Library
|
||||||
|
// by Lukhnos Liu (@lukhnos).
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation
|
||||||
|
// files (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import InputMethodKit
|
||||||
|
|
||||||
|
private let kCheckUpdateAutomatically = "CheckUpdateAutomatically"
|
||||||
|
private let kNextUpdateCheckDateKey = "NextUpdateCheckDate"
|
||||||
|
private let kUpdateInfoEndpointKey = "UpdateInfoEndpoint"
|
||||||
|
private let kUpdateInfoSiteKey = "UpdateInfoSite"
|
||||||
|
private let kNextCheckInterval: TimeInterval = 86400.0
|
||||||
|
private let kTimeoutInterval: TimeInterval = 60.0
|
||||||
|
|
||||||
|
@objc (AppDelegate)
|
||||||
|
class AppDelegate: NSObject, NSApplicationDelegate,
|
||||||
|
NSUserNotificationCenterDelegate, // Hiraku PR#1
|
||||||
|
NonModalAlertWindowControllerDelegate {
|
||||||
|
|
||||||
|
@IBOutlet weak var window: NSWindow?
|
||||||
|
private var preferencesWindowController: PreferencesWindowController?
|
||||||
|
private var aboutWindowController: frmAboutWindow? // New About Window
|
||||||
|
private var checkTask: URLSessionTask?
|
||||||
|
private var updateNextStepURL: URL?
|
||||||
|
|
||||||
|
// 補上 dealloc
|
||||||
|
deinit {
|
||||||
|
preferencesWindowController = nil
|
||||||
|
aboutWindowController = nil
|
||||||
|
checkTask = nil
|
||||||
|
updateNextStepURL = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
|
LTLoadLanguageModel()
|
||||||
|
LTLoadUserLanguageModelFile();
|
||||||
|
|
||||||
|
if UserDefaults.standard.object(forKey: kCheckUpdateAutomatically) == nil {
|
||||||
|
UserDefaults.standard.set(false, forKey: kCheckUpdateAutomatically)
|
||||||
|
UserDefaults.standard.synchronize()
|
||||||
|
}
|
||||||
|
checkForUpdate()
|
||||||
|
NSUserNotificationCenter.default.delegate = self // Hiraku PR#1
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func showPreferences() {
|
||||||
|
if (preferencesWindowController == nil) {
|
||||||
|
preferencesWindowController = PreferencesWindowController(windowNibName: "preferences")
|
||||||
|
}
|
||||||
|
preferencesWindowController?.window?.center()
|
||||||
|
preferencesWindowController?.window?.orderFrontRegardless() // 逼著屬性視窗往最前方顯示
|
||||||
|
}
|
||||||
|
|
||||||
|
// New About Window
|
||||||
|
@objc func showAbout() {
|
||||||
|
if (aboutWindowController == nil) {
|
||||||
|
aboutWindowController = frmAboutWindow.init(windowNibName: "frmAboutWindow")
|
||||||
|
}
|
||||||
|
aboutWindowController?.window?.center()
|
||||||
|
aboutWindowController?.window?.orderFrontRegardless() // 逼著關於視窗往最前方顯示
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc (checkForUpdate)
|
||||||
|
func checkForUpdate() {
|
||||||
|
checkForUpdate(forced: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc (checkForUpdateForced:)
|
||||||
|
func checkForUpdate(forced: Bool) {
|
||||||
|
|
||||||
|
if checkTask != nil {
|
||||||
|
// busy
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// time for update?
|
||||||
|
if !forced {
|
||||||
|
if UserDefaults.standard.bool(forKey: kCheckUpdateAutomatically) == false {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let now = Date()
|
||||||
|
let date = UserDefaults.standard.object(forKey: kNextUpdateCheckDateKey) as? Date ?? now
|
||||||
|
if now.compare(date) == .orderedAscending {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextUpdateDate = Date(timeInterval: kNextCheckInterval, since: Date())
|
||||||
|
UserDefaults.standard.set(nextUpdateDate, forKey: kNextUpdateCheckDateKey)
|
||||||
|
|
||||||
|
guard let infoDict = Bundle.main.infoDictionary,
|
||||||
|
let updateInfoURLString = infoDict[kUpdateInfoEndpointKey] as? String,
|
||||||
|
let updateInfoURL = URL(string: updateInfoURLString) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = URLRequest(url: updateInfoURL, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: kTimeoutInterval)
|
||||||
|
|
||||||
|
func showNoUpdateAvailableAlert() {
|
||||||
|
NonModalAlertWindowController.shared.show(title: NSLocalizedString("Check for Update Completed", comment: ""), content: NSLocalizedString("You are already using the latest version of vChewing.", comment: ""), confirmButtonTitle: NSLocalizedString("OK", comment: ""), cancelButtonTitle: nil, cancelAsDefault: false, delegate: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
||||||
|
defer {
|
||||||
|
self.checkTask = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
if forced {
|
||||||
|
let title = NSLocalizedString("Update Check Failed", comment: "")
|
||||||
|
let content = String(format: NSLocalizedString("There may be no internet connection or the server failed to respond.\n\nError message: %@", comment: ""), error.localizedDescription)
|
||||||
|
let buttonTitle = NSLocalizedString("Dismiss", comment: "")
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
NonModalAlertWindowController.shared.show(title: title , content: content, confirmButtonTitle: buttonTitle, cancelButtonTitle: nil, cancelAsDefault: false, delegate: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
guard let plist = try PropertyListSerialization.propertyList(from: data ?? Data(), options: [], format: nil) as? [AnyHashable: Any],
|
||||||
|
let remoteVersion = plist[kCFBundleVersionKey] as? String,
|
||||||
|
let infoDict = Bundle.main.infoDictionary
|
||||||
|
else {
|
||||||
|
if forced {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
showNoUpdateAvailableAlert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Validate info (e.g. bundle identifier)
|
||||||
|
// TODO: Use HTML to display change log, need a new key like UpdateInfoChangeLogURL for this
|
||||||
|
let currentVersion = infoDict[kCFBundleVersionKey as String] as? String ?? ""
|
||||||
|
let result = currentVersion.compare(remoteVersion, options: .numeric, range: nil, locale: nil)
|
||||||
|
|
||||||
|
if result != .orderedAscending {
|
||||||
|
if forced {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
showNoUpdateAvailableAlert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let siteInfoURLString = plist[kUpdateInfoSiteKey] as? String,
|
||||||
|
let siteInfoURL = URL(string: siteInfoURLString) else {
|
||||||
|
if forced {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
showNoUpdateAvailableAlert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateNextStepURL = siteInfoURL
|
||||||
|
|
||||||
|
var versionDescription = ""
|
||||||
|
let versionDescriptions = plist["Description"] as? [AnyHashable: Any]
|
||||||
|
if let versionDescriptions = versionDescriptions {
|
||||||
|
var locale = "en"
|
||||||
|
let supportedLocales = ["en", "zh-Hant", "zh-Hans"]
|
||||||
|
let preferredTags = Bundle.preferredLocalizations(from: supportedLocales)
|
||||||
|
if let first = preferredTags.first {
|
||||||
|
locale = first
|
||||||
|
}
|
||||||
|
versionDescription = versionDescriptions[locale] as? String ?? versionDescriptions["en"] as? String ?? ""
|
||||||
|
if !versionDescription.isEmpty {
|
||||||
|
versionDescription = "\n\n" + versionDescription
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = String(format: NSLocalizedString("You're currently using vChewing %@ (%@), a new version %@ (%@) is now available. Do you want to visit vChewing's website to download the version?%@", comment: ""),
|
||||||
|
infoDict["CFBundleShortVersionString"] as? String ?? "",
|
||||||
|
currentVersion,
|
||||||
|
plist["CFBundleShortVersionString"] as? String ?? "",
|
||||||
|
remoteVersion,
|
||||||
|
versionDescription)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
NonModalAlertWindowController.shared.show(title: NSLocalizedString("New Version Available", comment: "") , content: content, confirmButtonTitle: NSLocalizedString("Visit Website", comment: ""), cancelButtonTitle: NSLocalizedString("Not Now", comment: ""), cancelAsDefault: false, delegate: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
if forced {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
showNoUpdateAvailableAlert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkTask = task
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonModalAlertWindowControllerDidConfirm(_ controller: NonModalAlertWindowController) {
|
||||||
|
if let updateNextStepURL = updateNextStepURL {
|
||||||
|
NSWorkspace.shared.open(updateNextStepURL)
|
||||||
|
}
|
||||||
|
updateNextStepURL = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonModalAlertWindowControllerDidCancel(_ controller: NonModalAlertWindowController) {
|
||||||
|
updateNextStepURL = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hiraku PR#1
|
||||||
|
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// New About Window
|
||||||
|
@IBAction func about(_ sender: Any) {
|
||||||
|
(NSApp.delegate as? AppDelegate)?.showAbout()
|
||||||
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,7 +42,6 @@
|
||||||
#import <set>
|
#import <set>
|
||||||
#import "OVStringHelper.h"
|
#import "OVStringHelper.h"
|
||||||
#import "OVUTF8Helper.h"
|
#import "OVUTF8Helper.h"
|
||||||
#import "AppDelegate.h"
|
|
||||||
#import "vChewing-Swift.h"
|
#import "vChewing-Swift.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,5 @@
|
||||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
extern void LTLoadLanguageModel(void);
|
||||||
|
extern void LTLoadUserLanguageModelFile(void);
|
||||||
|
|
|
@ -20,11 +20,11 @@
|
||||||
5BDF2CFF2791BECC00838ADB /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */; };
|
5BDF2CFF2791BECC00838ADB /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */; };
|
||||||
5BDF2D012791C03B00838ADB /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */; };
|
5BDF2D012791C03B00838ADB /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */; };
|
||||||
5BDF2D032791C71200838ADB /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */; };
|
5BDF2D032791C71200838ADB /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */; };
|
||||||
|
5BDF2D062791DFF200838ADB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BDF2D052791DA6700838ADB /* AppDelegate.swift */; };
|
||||||
5BF4A6FE27844738007DC6E7 /* frmAboutWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */; };
|
5BF4A6FE27844738007DC6E7 /* frmAboutWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */; };
|
||||||
5BF4A70027844DC5007DC6E7 /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BF4A70227844DC5007DC6E7 /* frmAboutWindow.xib */; };
|
5BF4A70027844DC5007DC6E7 /* frmAboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BF4A70227844DC5007DC6E7 /* frmAboutWindow.xib */; };
|
||||||
6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A0421A615FEF3F50061ED63 /* FastLM.cpp */; };
|
6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A0421A615FEF3F50061ED63 /* FastLM.cpp */; };
|
||||||
6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */; };
|
6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */; };
|
||||||
6A0D4ED015FC0D6400ABF4B3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC415FC0D6400ABF4B3 /* AppDelegate.m */; };
|
|
||||||
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */; };
|
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */; };
|
||||||
6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC815FC0D6400ABF4B3 /* main.m */; };
|
6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC815FC0D6400ABF4B3 /* main.m */; };
|
||||||
6A0D4F0815FC0DA600ABF4B3 /* Bopomofo.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EEF15FC0DA600ABF4B3 /* Bopomofo.tiff */; };
|
6A0D4F0815FC0DA600ABF4B3 /* Bopomofo.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EEF15FC0DA600ABF4B3 /* Bopomofo.tiff */; };
|
||||||
|
@ -96,6 +96,7 @@
|
||||||
5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceHelper.swift; sourceTree = "<group>"; };
|
5BDF2CFD2791BE4400838ADB /* InputSourceHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceHelper.swift; sourceTree = "<group>"; };
|
||||||
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
5BDF2D002791C03B00838ADB /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = "<group>"; };
|
5BDF2D022791C71200838ADB /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
5BDF2D052791DA6700838ADB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = frmAboutWindow.h; sourceTree = "<group>"; };
|
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = frmAboutWindow.h; sourceTree = "<group>"; };
|
||||||
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = frmAboutWindow.m; sourceTree = "<group>"; };
|
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = frmAboutWindow.m; sourceTree = "<group>"; };
|
||||||
5BF4A70327844DD0007DC6E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
|
5BF4A70327844DD0007DC6E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmAboutWindow.xib; sourceTree = "<group>"; };
|
||||||
|
@ -108,8 +109,6 @@
|
||||||
6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||||
6A0D4EA915FC0D2D00ABF4B3 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
6A0D4EA915FC0D2D00ABF4B3 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||||
6A0D4EAB15FC0D2D00ABF4B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
6A0D4EAB15FC0D2D00ABF4B3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||||
6A0D4EC315FC0D6400ABF4B3 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
|
||||||
6A0D4EC415FC0D6400ABF4B3 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
|
||||||
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputMethodController.h; sourceTree = "<group>"; };
|
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputMethodController.h; sourceTree = "<group>"; };
|
||||||
6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMethodController.mm; sourceTree = "<group>"; };
|
6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = InputMethodController.mm; sourceTree = "<group>"; };
|
||||||
6A0D4EC815FC0D6400ABF4B3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
6A0D4EC815FC0D6400ABF4B3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
|
@ -268,8 +267,7 @@
|
||||||
6A0D4F1215FC0EB100ABF4B3 /* Engine */,
|
6A0D4F1215FC0EB100ABF4B3 /* Engine */,
|
||||||
6ACA41E715FC1D9000935EF6 /* Installer */,
|
6ACA41E715FC1D9000935EF6 /* Installer */,
|
||||||
6A0D4F4715FC0EB900ABF4B3 /* Resources */,
|
6A0D4F4715FC0EB900ABF4B3 /* Resources */,
|
||||||
6A0D4EC315FC0D6400ABF4B3 /* AppDelegate.h */,
|
5BDF2D052791DA6700838ADB /* AppDelegate.swift */,
|
||||||
6A0D4EC415FC0D6400ABF4B3 /* AppDelegate.m */,
|
|
||||||
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */,
|
5BF4A6FB27844738007DC6E7 /* frmAboutWindow.h */,
|
||||||
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */,
|
5BF4A6FC27844738007DC6E7 /* frmAboutWindow.m */,
|
||||||
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */,
|
6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */,
|
||||||
|
@ -619,11 +617,11 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
6A0D4ED015FC0D6400ABF4B3 /* AppDelegate.m in Sources */,
|
|
||||||
5BDF2CFE2791BE4400838ADB /* InputSourceHelper.swift in Sources */,
|
5BDF2CFE2791BE4400838ADB /* InputSourceHelper.swift in Sources */,
|
||||||
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */,
|
||||||
6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */,
|
6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */,
|
||||||
5BF4A6FE27844738007DC6E7 /* frmAboutWindow.m in Sources */,
|
5BF4A6FE27844738007DC6E7 /* frmAboutWindow.m in Sources */,
|
||||||
|
5BDF2D062791DFF200838ADB /* AppDelegate.swift in Sources */,
|
||||||
5BC3EE1B278FC48C00F5E44C /* VerticalCandidateController.swift in Sources */,
|
5BC3EE1B278FC48C00F5E44C /* VerticalCandidateController.swift in Sources */,
|
||||||
5B42B64027876FDC00BB9B9F /* UserOverrideModel.cpp in Sources */,
|
5B42B64027876FDC00BB9B9F /* UserOverrideModel.cpp in Sources */,
|
||||||
D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */,
|
D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue