diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 250733f4..673fc750 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */; }; 6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EC815FC0D6400ABF4B3 /* main.m */; }; 6A0D4ED415FC0D6400ABF4B3 /* OVInputSourceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4ECA15FC0D6400ABF4B3 /* OVInputSourceHelper.m */; }; - 6A0D4ED515FC0D6400ABF4B3 /* PreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4ECC15FC0D6400ABF4B3 /* PreferencesWindowController.m */; }; 6A0D4EFE15FC0DA600ABF4B3 /* VTCandidateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EDA15FC0DA600ABF4B3 /* VTCandidateController.m */; }; 6A0D4EFF15FC0DA600ABF4B3 /* VTHorizontalCandidateController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EDC15FC0DA600ABF4B3 /* VTHorizontalCandidateController.m */; }; 6A0D4F0015FC0DA600ABF4B3 /* VTHorizontalCandidateView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4EDE15FC0DA600ABF4B3 /* VTHorizontalCandidateView.m */; }; @@ -45,10 +44,11 @@ 6AD7CBC815FE555000691B5B /* data-plain-bpmf.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6AD7CBC715FE555000691B5B /* data-plain-bpmf.txt */; }; 6AE210B215FC63CC003659FE /* PlainBopomofo.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6AE210B015FC63CC003659FE /* PlainBopomofo.tiff */; }; 6AE210B315FC63CC003659FE /* PlainBopomofo@2x.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6AE210B115FC63CC003659FE /* PlainBopomofo@2x.tiff */; }; - 6AE30A491F7F40B7008735BD /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6AE30A471F7F40B7008735BD /* UserOverrideModel.cpp */; }; - 6AFF97F2253B299E007F1C49 /* OVNonModalAlertWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AFF97F0253B299E007F1C49 /* OVNonModalAlertWindowController.xib */; }; - 6AFF97F3253B299E007F1C49 /* OVNonModalAlertWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AFF97F1253B299E007F1C49 /* OVNonModalAlertWindowController.m */; }; + 6AFF97F2253B299E007F1C49 /* NonModalAlertWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AFF97F0253B299E007F1C49 /* NonModalAlertWindowController.xib */; }; D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */; }; + D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; }; + D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; }; + D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; }; D48550A325EBE689006A204C /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = D48550A225EBE689006A204C /* OpenCC */; }; /* End PBXBuildFile section */ @@ -83,8 +83,6 @@ 6A0D4EC815FC0D6400ABF4B3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 6A0D4EC915FC0D6400ABF4B3 /* OVInputSourceHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVInputSourceHelper.h; sourceTree = ""; }; 6A0D4ECA15FC0D6400ABF4B3 /* OVInputSourceHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OVInputSourceHelper.m; sourceTree = ""; }; - 6A0D4ECB15FC0D6400ABF4B3 /* PreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesWindowController.h; sourceTree = ""; }; - 6A0D4ECC15FC0D6400ABF4B3 /* PreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesWindowController.m; sourceTree = ""; }; 6A0D4ED915FC0DA600ABF4B3 /* VTCandidateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VTCandidateController.h; sourceTree = ""; }; 6A0D4EDA15FC0DA600ABF4B3 /* VTCandidateController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VTCandidateController.m; sourceTree = ""; }; 6A0D4EDB15FC0DA600ABF4B3 /* VTHorizontalCandidateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VTHorizontalCandidateController.h; sourceTree = ""; }; @@ -177,13 +175,13 @@ 6AD7CBC715FE555000691B5B /* data-plain-bpmf.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "data-plain-bpmf.txt"; sourceTree = ""; }; 6AE210B015FC63CC003659FE /* PlainBopomofo.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = PlainBopomofo.tiff; sourceTree = ""; }; 6AE210B115FC63CC003659FE /* PlainBopomofo@2x.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "PlainBopomofo@2x.tiff"; sourceTree = ""; }; - 6AE30A471F7F40B7008735BD /* UserOverrideModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserOverrideModel.cpp; sourceTree = ""; }; - 6AE30A481F7F40B7008735BD /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = ""; }; - 6AFF97EF253B299E007F1C49 /* OVNonModalAlertWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVNonModalAlertWindowController.h; sourceTree = ""; }; - 6AFF97F0253B299E007F1C49 /* OVNonModalAlertWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OVNonModalAlertWindowController.xib; sourceTree = ""; }; - 6AFF97F1253B299E007F1C49 /* OVNonModalAlertWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OVNonModalAlertWindowController.m; sourceTree = ""; }; + 6AFF97F0253B299E007F1C49 /* NonModalAlertWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NonModalAlertWindowController.xib; sourceTree = ""; }; D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "McBopomofo-Bridging-Header.h"; sourceTree = ""; }; D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCCBridge.swift; sourceTree = ""; }; + D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; + D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = ""; }; + D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = ""; }; + D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserOverrideModel.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -247,20 +245,16 @@ 6A0D4F4715FC0EB900ABF4B3 /* Resources */, 6A0D4EC315FC0D6400ABF4B3 /* AppDelegate.h */, 6A0D4EC415FC0D6400ABF4B3 /* AppDelegate.m */, + 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, 6A0D4EC615FC0D6400ABF4B3 /* InputMethodController.h */, 6A0D4EC715FC0D6400ABF4B3 /* InputMethodController.mm */, 6A0D4EC815FC0D6400ABF4B3 /* main.m */, - 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, - 6AFF97EF253B299E007F1C49 /* OVNonModalAlertWindowController.h */, - 6AFF97F1253B299E007F1C49 /* OVNonModalAlertWindowController.m */, 6A0D4EC915FC0D6400ABF4B3 /* OVInputSourceHelper.h */, 6A0D4ECA15FC0D6400ABF4B3 /* OVInputSourceHelper.m */, - 6A0D4ECB15FC0D6400ABF4B3 /* PreferencesWindowController.h */, - 6A0D4ECC15FC0D6400ABF4B3 /* PreferencesWindowController.m */, + D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, + D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */, D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */, D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */, - 6AE30A471F7F40B7008735BD /* UserOverrideModel.cpp */, - 6AE30A481F7F40B7008735BD /* UserOverrideModel.h */, ); path = Source; sourceTree = ""; @@ -304,6 +298,8 @@ 6A0D4F2215FC0EB100ABF4B3 /* OpenVanilla */, 6A0421A615FEF3F50061ED63 /* FastLM.cpp */, 6A0421A715FEF3F50061ED63 /* FastLM.h */, + D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */, + D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */, ); path = Engine; sourceTree = ""; @@ -377,7 +373,7 @@ 6A0D4F4715FC0EB900ABF4B3 /* Resources */ = { isa = PBXGroup; children = ( - 6AFF97F0253B299E007F1C49 /* OVNonModalAlertWindowController.xib */, + 6AFF97F0253B299E007F1C49 /* NonModalAlertWindowController.xib */, 6A0D4EEE15FC0DA600ABF4B3 /* Images */, 6A0D4EF515FC0DA600ABF4B3 /* McBopomofo-Info.plist */, 6A0D4F4815FC0EE100ABF4B3 /* InfoPlist.strings */, @@ -525,7 +521,7 @@ 6A0D4F5815FC0EF900ABF4B3 /* Localizable.strings in Resources */, 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */, 6A38BC1515FC117A00A8A51F /* data.txt in Resources */, - 6AFF97F2253B299E007F1C49 /* OVNonModalAlertWindowController.xib in Resources */, + 6AFF97F2253B299E007F1C49 /* NonModalAlertWindowController.xib in Resources */, 6AE210B215FC63CC003659FE /* PlainBopomofo.tiff in Resources */, 6AE210B315FC63CC003659FE /* PlainBopomofo@2x.tiff in Resources */, 6AD7CBC815FE555000691B5B /* data-plain-bpmf.txt in Resources */, @@ -578,16 +574,16 @@ 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */, 6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */, 6A0D4ED415FC0D6400ABF4B3 /* OVInputSourceHelper.m in Sources */, - 6A0D4ED515FC0D6400ABF4B3 /* PreferencesWindowController.m in Sources */, 6A0D4EFE15FC0DA600ABF4B3 /* VTCandidateController.m in Sources */, 6A0D4EFF15FC0DA600ABF4B3 /* VTHorizontalCandidateController.m in Sources */, + D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */, 6A0D4F0015FC0DA600ABF4B3 /* VTHorizontalCandidateView.m in Sources */, - 6AFF97F3253B299E007F1C49 /* OVNonModalAlertWindowController.m in Sources */, 6A0D4F0115FC0DA600ABF4B3 /* VTVerticalCandidateController.m in Sources */, - 6AE30A491F7F40B7008735BD /* UserOverrideModel.cpp in Sources */, 6A0D4F0215FC0DA600ABF4B3 /* VTVerticalCandidateTableView.m in Sources */, 6A0D4F0315FC0DA600ABF4B3 /* VTVerticalKeyLabelStripView.m in Sources */, + D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */, D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */, + D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, 6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */, ); diff --git a/Source/AppDelegate.m b/Source/AppDelegate.m index 98d3dfc0..d8b78a9b 100644 --- a/Source/AppDelegate.m +++ b/Source/AppDelegate.m @@ -33,8 +33,7 @@ // #import "AppDelegate.h" -#import "OVNonModalAlertWindowController.h" -#import "PreferencesWindowController.h" +#import "McBopomofo-Swift.h" extern void LTLoadLanguageModel(void); extern void LTLoadUserLanguageModelFile(void); @@ -46,7 +45,7 @@ static NSString *kUpdateInfoSiteKey = @"UpdateInfoSite"; static const NSTimeInterval kNextCheckInterval = 86400.0; static const NSTimeInterval kTimeoutInterval = 60.0; -@interface AppDelegate () +@interface AppDelegate () @end @implementation AppDelegate @@ -152,13 +151,13 @@ static const NSTimeInterval kTimeoutInterval = 60.0; _currentUpdateCheckIsForced = NO; if (isForcedCheck) { - [[OVNonModalAlertWindowController 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]; + [[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 { - [[OVNonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"Check for Update Completed", nil) content:NSLocalizedString(@"You are already using the latest version of McBopomofo.", nil) confirmButtonTitle:NSLocalizedString(@"OK", nil) cancelButtonTitle:nil cancelAsDefault:NO delegate:nil]; + [[NonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"Check for Update Completed", nil) content:NSLocalizedString(@"You are already using the latest version of McBopomofo.", nil) confirmButtonTitle:NSLocalizedString(@"OK", nil) cancelButtonTitle:nil cancelAsDefault:NO delegate:nil]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection @@ -248,7 +247,7 @@ static const NSTimeInterval kTimeoutInterval = 60.0; NSString *content = [NSString stringWithFormat:NSLocalizedString(@"You're currently using McBopomofo %@ (%@), a new version %@ (%@) is now available. Do you want to visit McBopomofo's website to download the version?%@", nil), [infoDict objectForKey:@"CFBundleShortVersionString"], currentVersion, [plist objectForKey:@"CFBundleShortVersionString"], remoteVersion, versionDescription]; - [[OVNonModalAlertWindowController sharedInstance] showWithTitle:NSLocalizedString(@"New Version Available", nil) content:content confirmButtonTitle:NSLocalizedString(@"Visit Website", nil) cancelButtonTitle:NSLocalizedString(@"Not Now", nil) cancelAsDefault:NO delegate:self]; + [[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 @@ -256,7 +255,7 @@ static const NSTimeInterval kTimeoutInterval = 60.0; [_receivingData appendData:data]; } -- (void)nonModalAlertWindowControllerDidConfirm:(OVNonModalAlertWindowController *)controller +- (void)nonModalAlertWindowControllerDidConfirm:(NonModalAlertWindowController *)controller { if (_updateNextStepURL) { [[NSWorkspace sharedWorkspace] openURL:_updateNextStepURL]; @@ -265,7 +264,7 @@ static const NSTimeInterval kTimeoutInterval = 60.0; _updateNextStepURL = nil; } -- (void)nonModalAlertWindowControllerDidCancel:(OVNonModalAlertWindowController *)controller +- (void)nonModalAlertWindowControllerDidCancel:(NonModalAlertWindowController *)controller { _updateNextStepURL = nil; } diff --git a/Source/Base.lproj/preferences.xib b/Source/Base.lproj/preferences.xib index cfe934d0..f189ef01 100644 --- a/Source/Base.lproj/preferences.xib +++ b/Source/Base.lproj/preferences.xib @@ -8,10 +8,10 @@ - + - - + + diff --git a/Source/UserOverrideModel.cpp b/Source/Engine/UserOverrideModel.cpp similarity index 100% rename from Source/UserOverrideModel.cpp rename to Source/Engine/UserOverrideModel.cpp diff --git a/Source/UserOverrideModel.h b/Source/Engine/UserOverrideModel.h similarity index 100% rename from Source/UserOverrideModel.h rename to Source/Engine/UserOverrideModel.h diff --git a/Source/NonModalAlertWindowController.swift b/Source/NonModalAlertWindowController.swift new file mode 100644 index 00000000..4f1b60eb --- /dev/null +++ b/Source/NonModalAlertWindowController.swift @@ -0,0 +1,132 @@ +// +// NonModalAlertWindowController.swift +// +// Copyright (c) 2011 The McBopomofo Project. +// +// Contributors: +// Mengjuei Hsieh (@mjhsieh) +// Weizhong Yang (@zonble) +// +// 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 + +@objc protocol NonModalAlertWindowControllerDelegate: AnyObject { + func nonModalAlertWindowControllerDidConfirm(_ controller: NonModalAlertWindowController) + func nonModalAlertWindowControllerDidCancel(_ controller: NonModalAlertWindowController) +} + +class NonModalAlertWindowController: NSWindowController { + @objc(sharedInstance) static let shared = NonModalAlertWindowController(windowNibName: "NonModalAlertWindowController") + + @IBOutlet weak var titleTextField: NSTextField! + @IBOutlet weak var contentTextField: NSTextField! + @IBOutlet weak var confirmButton: NSButton! + @IBOutlet weak var cancelButton: NSButton! + weak var delegate: NonModalAlertWindowControllerDelegate? + + @objc func show(title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?, cancelAsDefault: Bool, delegate: NonModalAlertWindowControllerDelegate?) { + if window?.isVisible == true { + self.delegate?.nonModalAlertWindowControllerDidCancel(self) + } + + self.delegate = delegate + + var oldFrame = confirmButton.frame + confirmButton.title = confirmButtonTitle + confirmButton.sizeToFit() + + var newFrame = confirmButton.frame + newFrame.size.width = max(90, newFrame.size.width + 10) + newFrame.origin.x += oldFrame.size.width - newFrame.size.width + self.confirmButton.frame = newFrame + + if let cancelButtonTitle = cancelButtonTitle { + cancelButton.title = cancelButtonTitle + cancelButton.sizeToFit() + var adjustFrame = cancelButton.frame + adjustFrame.size.width = max(90, adjustFrame.size.width + 10) + adjustFrame.origin.x = newFrame.origin.x - adjustFrame.size.width + self.confirmButton.frame = adjustFrame + self.cancelButton.isHidden = false + } else { + self.cancelButton.isHidden = true + } + + cancelButton.nextKeyView = confirmButton + confirmButton.nextKeyView = cancelButton + + if cancelButtonTitle != nil { + if cancelAsDefault { + window?.defaultButtonCell = cancelButton.cell as? NSButtonCell + } else { + cancelButton.keyEquivalent = " " + window?.defaultButtonCell = confirmButton.cell as? NSButtonCell + } + } else { + window?.defaultButtonCell = confirmButton.cell as? NSButtonCell + } + + titleTextField.stringValue = title + + oldFrame = contentTextField.frame + contentTextField.stringValue = content + + var infiniteHeightFrame = oldFrame + infiniteHeightFrame.size.width -= 4.0 + infiniteHeightFrame.size.height = 10240 + newFrame = (content as NSString).boundingRect(with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin], attributes: [.font: contentTextField.font!]) + newFrame.size.width = max(newFrame.size.width, oldFrame.size.width) + newFrame.size.height += 4.0 + newFrame.origin = oldFrame.origin + newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height) + contentTextField.frame = newFrame + + var windowFrame = window?.frame ?? NSRect.zero + windowFrame.size.height += (newFrame.size.height - oldFrame.size.height) + window?.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1) + window?.setFrame(windowFrame, display: true) + window?.center() + window?.makeKeyAndOrderFront(self) + NSApp.activate(ignoringOtherApps: true) + } + + @IBAction func confirmButtonAction(_ sender: Any) { + delegate?.nonModalAlertWindowControllerDidConfirm(self) + window?.orderOut(self) + } + + @IBAction func cancelButtonAction(_ sender: Any) { + cancel(sender) + } + + func cancel(_ sender: Any) { + delegate?.nonModalAlertWindowControllerDidCancel(self) + delegate = nil + window?.orderOut(self) + } + +} diff --git a/Source/OVNonModalAlertWindowController.xib b/Source/NonModalAlertWindowController.xib similarity index 96% rename from Source/OVNonModalAlertWindowController.xib rename to Source/NonModalAlertWindowController.xib index 430b3b36..8c378007 100644 --- a/Source/OVNonModalAlertWindowController.xib +++ b/Source/NonModalAlertWindowController.xib @@ -1,12 +1,12 @@ - + - + - + @@ -21,7 +21,7 @@ - + diff --git a/Source/OVNonModalAlertWindowController.h b/Source/OVNonModalAlertWindowController.h deleted file mode 100644 index e0eea1e2..00000000 --- a/Source/OVNonModalAlertWindowController.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// OVNonModalAlertWindowController.h -// OpenVanilla -// -// Created by Lukhnos Liu on 10/17/12. -// Copyright (c) 2012 The OpenVanilla Project. All rights reserved. -// - -#import - -@class OVNonModalAlertWindowController; - -@protocol OVNonModalAlertWindowControllerDelegate -- (void)nonModalAlertWindowControllerDidConfirm:(OVNonModalAlertWindowController *)controller; - -@optional -- (void)nonModalAlertWindowControllerDidCancel:(OVNonModalAlertWindowController *)controller; -@end - -@interface OVNonModalAlertWindowController : NSWindowController - -+ (OVNonModalAlertWindowController *)sharedInstance; -- (void)showWithTitle:(NSString *)title content:(NSString *)content confirmButtonTitle:(NSString *)confirmTitle cancelButtonTitle:(NSString *)cancelButtonTitle cancelAsDefault:(BOOL)cancelAsDefault delegate:(id)delegate; -- (IBAction)confirmButtonAction:(id)sender; -- (IBAction)cancelButtonAction:(id)sender; -@property (assign, nonatomic) IBOutlet NSTextField *titleTextField; -@property (assign, nonatomic) IBOutlet NSTextField *contentTextField; -@property (assign, nonatomic) IBOutlet NSButton *confirmButton; -@property (assign, nonatomic) IBOutlet NSButton *cancelButton; -@property (assign, nonatomic) id delegate; -@end diff --git a/Source/OVNonModalAlertWindowController.m b/Source/OVNonModalAlertWindowController.m deleted file mode 100644 index 48302d68..00000000 --- a/Source/OVNonModalAlertWindowController.m +++ /dev/null @@ -1,131 +0,0 @@ -// -// OVNonModalAlertWindowController.m -// OpenVanilla -// -// Created by Lukhnos Liu on 10/17/12. -// Copyright (c) 2012 The OpenVanilla Project. All rights reserved. -// - -#import "OVNonModalAlertWindowController.h" - -@implementation OVNonModalAlertWindowController -@synthesize titleTextField = _titleTextField; -@synthesize contentTextField = _contentTextField; -@synthesize confirmButton = _confirmButton; -@synthesize cancelButton = _cancelButton; -@synthesize delegate = _delegate; - -+ (OVNonModalAlertWindowController *)sharedInstance -{ - static OVNonModalAlertWindowController *instance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[OVNonModalAlertWindowController alloc] initWithWindowNibName:@"OVNonModalAlertWindowController"]; - [instance window]; - }); - return instance; -} - -// Suppress the use of the MIN/MAX macros -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wgnu-statement-expression" - -- (void)showWithTitle:(NSString *)title content:(NSString *)content confirmButtonTitle:(NSString *)confirmTitle cancelButtonTitle:(NSString *)cancelButtonTitle cancelAsDefault:(BOOL)cancelAsDefault delegate:(id )delegate; -{ - // cancel previous alert - if (self.window.visible) { - if ([_delegate respondsToSelector:@selector(nonModalAlertWindowControllerDidCancel:)]) { - [_delegate nonModalAlertWindowControllerDidCancel:self]; - } - } - - _delegate = delegate; - - NSRect oldFrame = self.confirmButton.frame; - [self.confirmButton setTitle:confirmTitle]; - [self.confirmButton sizeToFit]; - - NSRect newFrame = self.confirmButton.frame; - - newFrame.size.width = MAX(90.0, (newFrame.size.width + 10.0)); - newFrame.origin.x += (oldFrame.size.width - newFrame.size.width); - [self.confirmButton setFrame:newFrame]; - - if (cancelButtonTitle) { - [self.cancelButton setTitle:cancelButtonTitle]; - [self.cancelButton sizeToFit]; - NSRect adjustedFrame = self.cancelButton.frame; - adjustedFrame.size.width = MAX(90.0, (adjustedFrame.size.width + 10.0)); - adjustedFrame.origin.x = newFrame.origin.x - adjustedFrame.size.width; - self.cancelButton.frame = adjustedFrame; - self.cancelButton.hidden = NO; - } - else { - self.cancelButton.hidden = YES; - } - - self.cancelButton.nextKeyView = self.confirmButton; - self.confirmButton.nextKeyView = self.cancelButton; - - if (cancelButtonTitle) { - if (cancelAsDefault) { - [self.window setDefaultButtonCell:self.cancelButton.cell]; - } - else { - self.cancelButton.keyEquivalent = @" "; - [self.window setDefaultButtonCell:self.confirmButton.cell]; - } - } - else { - [[self window] setDefaultButtonCell:self.confirmButton.cell]; - } - - self.titleTextField.stringValue = title; - - oldFrame = [self.contentTextField frame]; - self.contentTextField.stringValue = content; - - NSRect infiniteHeightFrame = oldFrame; - infiniteHeightFrame.size.width -= 4.0; - infiniteHeightFrame.size.height = 10240; - newFrame = [content boundingRectWithSize:infiniteHeightFrame.size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.contentTextField.font}]; - newFrame.size.width = MAX(newFrame.size.width, oldFrame.size.width); - newFrame.size.height += 4.0; - newFrame.origin = oldFrame.origin; - newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height); - [self.contentTextField setFrame:newFrame]; - - NSRect windowFrame = [[self window] frame]; - windowFrame.size.height += (newFrame.size.height - oldFrame.size.height); - - self.window.level = CGShieldingWindowLevel() + 1; - [self.window setFrame:windowFrame display:YES]; - [self.window center]; - [self.window makeKeyAndOrderFront:self]; - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; -} - -#pragma GCC diagnostic pop - -- (IBAction)confirmButtonAction:(id)sender -{ - [_delegate nonModalAlertWindowControllerDidConfirm:self]; - [self.window orderOut:self]; -} - -- (IBAction)cancelButtonAction:(id)sender -{ - [self cancel:sender]; -} - -- (void)cancel:(id)sender -{ - if ([_delegate respondsToSelector:@selector(nonModalAlertWindowControllerDidCancel:)]) { - [_delegate nonModalAlertWindowControllerDidCancel:self]; - } - - _delegate = nil; - [self.window orderOut:self]; -} - -@end diff --git a/Source/OpenCCBridge.swift b/Source/OpenCCBridge.swift index ad53ccee..582ec8e0 100644 --- a/Source/OpenCCBridge.swift +++ b/Source/OpenCCBridge.swift @@ -3,7 +3,7 @@ import OpenCC // Since SwiftyOpenCC only provide Swift classes, we create an NSObject subclass // in Swift in order to bridge the Swift classes into our Objective-C++ project. -class OpenCCBridge : NSObject { +class OpenCCBridge: NSObject { private static let shared = OpenCCBridge() private var converter: ChineseConverter? diff --git a/Source/PreferencesWindowController.h b/Source/PreferencesWindowController.h deleted file mode 100644 index 538ddd5b..00000000 --- a/Source/PreferencesWindowController.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// PreferencesWindowController.h -// -// Copyright (c) 2011 The McBopomofo Project. -// -// Contributors: -// Mengjuei Hsieh (@mjhsieh) -// Weizhong Yang (@zonble) -// -// 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 - -@interface PreferencesWindowController : NSWindowController -{ -@private - NSPopUpButton *__weak _fontSizePopUpButton; - NSPopUpButton *__weak _basisKeyboardLayoutButton; - NSComboBox *__weak _selectionKeyComboBox; -} - -- (IBAction)updateBasisKeyboardLayoutAction:(id)sender; -- (IBAction)changeSelectionKeyAction:(id)sender; - -@property (weak, nonatomic) IBOutlet NSPopUpButton *fontSizePopUpButton; -@property (weak, nonatomic) IBOutlet NSPopUpButton *basisKeyboardLayoutButton; -@property (weak, nonatomic) IBOutlet NSComboBox *selectionKeyComboBox; -@end diff --git a/Source/PreferencesWindowController.m b/Source/PreferencesWindowController.m deleted file mode 100644 index 29253196..00000000 --- a/Source/PreferencesWindowController.m +++ /dev/null @@ -1,139 +0,0 @@ -// -// PreferencesWindowController.m -// -// Copyright (c) 2011 The McBopomofo Project. -// -// Contributors: -// Mengjuei Hsieh (@mjhsieh) -// Weizhong Yang (@zonble) -// -// 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 "PreferencesWindowController.h" -#import - -static NSString *const kBasisKeyboardLayoutPreferenceKey = @"BasisKeyboardLayout"; // alphanumeric ("ASCII") input basis -static NSString *const kCandidateKeys = @"CandidateKeys"; -static NSString *const kDefaultKeys = @"123456789"; - -@implementation PreferencesWindowController -@synthesize fontSizePopUpButton = _fontSizePopUpButton; -@synthesize basisKeyboardLayoutButton = _basisKeyboardLayoutButton; -@synthesize selectionKeyComboBox = _selectionKeyComboBox; - -- (void)awakeFromNib -{ - CFArrayRef list = TISCreateInputSourceList(NULL, true); - NSMenuItem *usKeyboardLayoutItem = nil; - NSMenuItem *chosenItem = nil; - - [self.basisKeyboardLayoutButton.menu removeAllItems]; - - NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; - - for (int i = 0; i < CFArrayGetCount(list); i++) { - TISInputSourceRef source = (TISInputSourceRef)CFArrayGetValueAtIndex(list, i); - - CFStringRef category = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory); - if (CFStringCompare(category, kTISCategoryKeyboardInputSource, 0) != kCFCompareEqualTo) { - continue; - } - - CFBooleanRef asciiCapable = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsASCIICapable); - if (!CFBooleanGetValue(asciiCapable)) { - continue; - } - - CFStringRef sourceType = TISGetInputSourceProperty(source, kTISPropertyInputSourceType); - if (CFStringCompare(sourceType, kTISTypeKeyboardLayout, 0) != kCFCompareEqualTo) { - continue; - } - - NSString *sourceID = (__bridge NSString *)TISGetInputSourceProperty(source, kTISPropertyInputSourceID); - NSString *localizedName = (__bridge NSString *)TISGetInputSourceProperty(source, kTISPropertyLocalizedName); - - NSMenuItem *item = [[NSMenuItem alloc] init]; - item.title = localizedName; - item.representedObject = sourceID; - - if ([sourceID isEqualToString:@"com.apple.keylayout.US"]) { - usKeyboardLayoutItem = item; - } - - // false if nil - if ([basisKeyboardLayoutID isEqualToString:sourceID]) { - chosenItem = item; - } - - [self.basisKeyboardLayoutButton.menu addItem:item]; - } - - [self.basisKeyboardLayoutButton selectItem:(chosenItem ? chosenItem : usKeyboardLayoutItem)]; - CFRelease(list); - - self.selectionKeyComboBox.usesDataSource = NO; - [self.selectionKeyComboBox removeAllItems]; - [self.selectionKeyComboBox addItemsWithObjectValues:@[ - kDefaultKeys, - @"asdfghjkl", - @"asdfzxcvb" - ]]; - - NSString *ckeys = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeys]; - if (!ckeys || [ckeys stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0) { - ckeys = kDefaultKeys; - } - - [self.selectionKeyComboBox setStringValue:ckeys]; -} - -- (IBAction)updateBasisKeyboardLayoutAction:(id)sender -{ - NSString *sourceID = [[self.basisKeyboardLayoutButton selectedItem] representedObject]; - if (sourceID) { - [[NSUserDefaults standardUserDefaults] setObject:sourceID forKey:kBasisKeyboardLayoutPreferenceKey]; - } -} - -- (IBAction)changeSelectionKeyAction:(id)sender -{ - NSString *keys = [[sender stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if (keys.length != 9 || - ![keys canBeConvertedToEncoding:NSASCIIStringEncoding]) { - [self.selectionKeyComboBox setStringValue:kDefaultKeys]; - [[NSUserDefaults standardUserDefaults] removeObjectForKey:kCandidateKeys]; - NSBeep(); - return; - } - - [self.selectionKeyComboBox setStringValue:keys]; - if ([keys isEqualToString:kDefaultKeys]) { - [[NSUserDefaults standardUserDefaults] removeObjectForKey:kCandidateKeys]; - } else { - [[NSUserDefaults standardUserDefaults] setObject:keys forKey:kCandidateKeys]; - } -} - -@end diff --git a/Source/PreferencesWindowController.swift b/Source/PreferencesWindowController.swift new file mode 100644 index 00000000..59d490dd --- /dev/null +++ b/Source/PreferencesWindowController.swift @@ -0,0 +1,144 @@ +// +// PreferencesWindowController.swift +// +// Copyright (c) 2011 The McBopomofo Project. +// +// Contributors: +// Mengjuei Hsieh (@mjhsieh) +// Weizhong Yang (@zonble) +// +// 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 Carbon + +private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout" +private let kCandidateKeys = "CandidateKeys" +private let kDefaultKeys = "123456789" + +// Please note that the class should be exposed as "PreferencesWindowController" +// in Objective-C in order to let IMK to see the same class name as +// the "InputMethodServerPreferencesWindowControllerClass" in Info.plist. +@objc(PreferencesWindowController) class PreferencesWindowController: NSWindowController { + @IBOutlet weak var fontSizePopUpButton: NSPopUpButton! + @IBOutlet weak var basisKeyboardLayoutButton: NSPopUpButton! + @IBOutlet weak var selectionKeyComboBox: NSComboBox! + + override func awakeFromNib() { + let list = TISCreateInputSourceList(nil, true).takeRetainedValue() as! [TISInputSource] + var usKeyboardLayoutItem: NSMenuItem? = nil + var chosenItem: NSMenuItem? = nil + + basisKeyboardLayoutButton.menu?.removeAllItems() + + let basisKeyboardLayoutID = UserDefaults.standard.string(forKey: kBasisKeyboardLayoutPreferenceKey) + for source in list { + if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) { + let category = Unmanaged.fromOpaque(categoryPtr).takeUnretainedValue() + if category != kTISCategoryKeyboardInputSource { + continue + } + } else { + continue + } + + if let asciiCapablePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceIsASCIICapable) { + let asciiCapable = Unmanaged.fromOpaque(asciiCapablePtr).takeUnretainedValue() + if asciiCapable != kCFBooleanTrue { + continue + } + } else { + continue + } + + if let sourceTypePtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceType) { + let sourceType = Unmanaged.fromOpaque(sourceTypePtr).takeUnretainedValue() + if sourceType != kTISTypeKeyboardLayout { + continue + } + } else { + continue + } + + guard let sourceIDPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceID), + let localizedNamePtr = TISGetInputSourceProperty(source, kTISPropertyLocalizedName) else { + continue + } + + let sourceID = String(Unmanaged.fromOpaque(sourceIDPtr).takeUnretainedValue()) + let localizedName = String(Unmanaged.fromOpaque(localizedNamePtr).takeUnretainedValue()) + + let menuItem = NSMenuItem() + menuItem.title = localizedName + menuItem.representedObject = sourceID + + if sourceID == "com.apple.keylayout.US" { + usKeyboardLayoutItem = menuItem + } + if basisKeyboardLayoutID == sourceID { + chosenItem = menuItem + } + basisKeyboardLayoutButton.menu?.addItem(menuItem) + } + + basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem) + selectionKeyComboBox.usesDataSource = false + selectionKeyComboBox.addItems(withObjectValues: [kDefaultKeys, "asdfghjkl", "asdfzxcvb"]) + + var candidateSelectionKeys = (UserDefaults.standard.string(forKey: kCandidateKeys) ?? kDefaultKeys) + .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + if candidateSelectionKeys.isEmpty { + candidateSelectionKeys = kDefaultKeys + } + + selectionKeyComboBox.stringValue = candidateSelectionKeys + } + + @IBAction func updateBasisKeyboardLayoutAction(_ sender:Any) { + if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject { + UserDefaults.standard.set(sourceID, forKey: kBasisKeyboardLayoutPreferenceKey) + } + } + + @IBAction func changeSelectionKeyAction(_ sender: Any) { + let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + if keys.count != 9 || !keys.canBeConverted(to: .ascii) { + selectionKeyComboBox.stringValue = kDefaultKeys + UserDefaults.standard.removeObject(forKey: kCandidateKeys) + NSSound.beep() + return + } + + selectionKeyComboBox.stringValue = keys + if keys == kDefaultKeys { + UserDefaults.standard.removeObject(forKey: kCandidateKeys) + } else { + UserDefaults.standard.set(keys, forKey: kCandidateKeys) + } + } + +} +