diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index f80b6d62..b56ae8b2 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -47,6 +47,9 @@ 6AE210B315FC63CC003659FE /* PlainBopomofo@2x.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6AE210B115FC63CC003659FE /* PlainBopomofo@2x.tiff */; }; 6AFF97F2253B299E007F1C49 /* OVNonModalAlertWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AFF97F0253B299E007F1C49 /* OVNonModalAlertWindowController.xib */; }; 6AFF97F3253B299E007F1C49 /* OVNonModalAlertWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AFF97F1253B299E007F1C49 /* OVNonModalAlertWindowController.m */; }; + D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */; }; + D48550A325EBE689006A204C /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = D48550A225EBE689006A204C /* OpenCC */; }; + D48550C325EC0EF2006A204C /* OpenCCDictionary.bundle in Embed PlugIns */ = {isa = PBXBuildFile; fileRef = D48550A625EBF2CF006A204C /* OpenCCDictionary.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,6 +69,20 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + D48550BE25EC0D97006A204C /* Embed PlugIns */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + D48550C325EC0EF2006A204C /* OpenCCDictionary.bundle in Embed PlugIns */, + ); + name = "Embed PlugIns"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 6A0421A615FEF3F50061ED63 /* FastLM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FastLM.cpp; sourceTree = ""; }; 6A0421A715FEF3F50061ED63 /* FastLM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastLM.h; sourceTree = ""; }; @@ -208,6 +225,9 @@ 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 = ""; }; + 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 = ""; }; + D48550A625EBF2CF006A204C /* OpenCCDictionary.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = OpenCCDictionary.bundle; path = "../../Library/Developer/Xcode/DerivedData/McBopomofo-czkkfmaoaedhszarcriztoynjmen/SourcePackages/checkouts/SwiftyOpenCC/OpenCCDictionary.bundle"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -217,6 +237,7 @@ files = ( 6A38BC2A15FC161000A8A51F /* Carbon.framework in Frameworks */, 6A38BC2815FC158A00A8A51F /* InputMethodKit.framework in Frameworks */, + D48550A325EBE689006A204C /* OpenCC in Frameworks */, 6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -256,6 +277,7 @@ 6A0D4EA915FC0D2D00ABF4B3 /* AppKit.framework */, 6A38BC2915FC161000A8A51F /* Carbon.framework */, 6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */, + D48550A625EBF2CF006A204C /* OpenCCDictionary.bundle */, 6A0D4EAB15FC0D2D00ABF4B3 /* Foundation.framework */, 6A38BC2715FC158A00A8A51F /* InputMethodKit.framework */, ); @@ -282,6 +304,8 @@ 6A0D4ECA15FC0D6400ABF4B3 /* OVInputSourceHelper.m */, 6A0D4ECB15FC0D6400ABF4B3 /* PreferencesWindowController.h */, 6A0D4ECC15FC0D6400ABF4B3 /* PreferencesWindowController.m */, + D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */, + D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */, ); path = Source; sourceTree = ""; @@ -502,6 +526,7 @@ 6A0D4E9E15FC0D2D00ABF4B3 /* Sources */, 6A0D4E9F15FC0D2D00ABF4B3 /* Frameworks */, 6A0D4EA015FC0D2D00ABF4B3 /* Resources */, + D48550BE25EC0D97006A204C /* Embed PlugIns */, ); buildRules = ( ); @@ -509,6 +534,9 @@ 6A38BC2615FC131100A8A51F /* PBXTargetDependency */, ); name = McBopomofo; + packageProductDependencies = ( + D48550A225EBE689006A204C /* OpenCC */, + ); productName = McBopomofo; productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */; productType = "com.apple.product-type.application"; @@ -539,6 +567,11 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 1220; + TargetAttributes = { + 6A0D4EA115FC0D2D00ABF4B3 = { + LastSwiftMigration = 1240; + }; + }; }; buildConfigurationList = 6A0D4E9715FC0CFA00ABF4B3 /* Build configuration list for PBXProject "McBopomofo" */; compatibilityVersion = "Xcode 3.2"; @@ -550,6 +583,9 @@ Base, ); mainGroup = 6A0D4E9215FC0CFA00ABF4B3; + packageReferences = ( + D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */, + ); productRefGroup = 6A0D4EA315FC0D2D00ABF4B3 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -634,6 +670,7 @@ 6A0D4F0115FC0DA600ABF4B3 /* VTVerticalCandidateController.m in Sources */, 6A0D4F0215FC0DA600ABF4B3 /* VTVerticalCandidateTableView.m in Sources */, 6A0D4F0315FC0DA600ABF4B3 /* VTVerticalKeyLabelStripView.m in Sources */, + D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, 6A0421A815FEF3F50061ED63 /* FastLM.cpp in Sources */, ); @@ -751,6 +788,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; @@ -779,6 +817,10 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-fcxx-modules", + ); }; name = Debug; }; @@ -786,6 +828,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ENABLE_MODULES = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; @@ -812,6 +855,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-fcxx-modules", + ); }; name = Release; }; @@ -821,6 +868,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; @@ -858,11 +906,18 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "Source/McBopomofo-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.openvanilla.inputmethod.McBopomofo; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Source/McBopomofo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; WRAPPER_EXTENSION = app; }; name = Debug; @@ -873,6 +928,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; @@ -904,10 +960,16 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "Source/McBopomofo-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = org.openvanilla.inputmethod.McBopomofo; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Source/McBopomofo-Bridging-Header.h"; + SWIFT_VERSION = 5.0; WRAPPER_EXTENSION = app; }; name = Release; @@ -1078,6 +1140,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ddddxxx/SwiftyOpenCC.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + D48550A225EBE689006A204C /* OpenCC */ = { + isa = XCSwiftPackageProductDependency; + package = D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; + productName = OpenCC; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 6A0D4E9415FC0CFA00ABF4B3 /* Project object */; } diff --git a/Source/InputMethodController.h b/Source/InputMethodController.h index 00aca989..e8b91760 100644 --- a/Source/InputMethodController.h +++ b/Source/InputMethodController.h @@ -68,6 +68,9 @@ // current input mode NSString *_inputMode; + + BOOL _chineseConvertionEnabled; + } @end diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index b224ebab..081a5647 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -41,6 +41,9 @@ #import "AppDelegate.h" #import "VTHorizontalCandidateController.h" #import "VTVerticalCandidateController.h" +#import "McBopomofo-Swift.h" + +//@import SwiftUI; // C++ namespace usages using namespace std; @@ -75,6 +78,7 @@ static NSString *const kUseHorizontalCandidateListPreferenceKey = @"UseHorizonta static NSString *const kComposingBufferSizePreferenceKey = @"ComposingBufferSize"; static NSString *const kDisableUserCandidateSelectionLearning = @"DisableUserCandidateSelectionLearning"; static NSString *const kChooseCandidateUsingSpaceKey = @"ChooseCandidateUsingSpaceKey"; +static NSString *const kChineseConvertionEnanledKey = @"ChineseConvertionEnanledKey"; // advanced (usually optional) settings static NSString *const kCandidateTextFontName = @"CandidateTextFontName"; @@ -159,12 +163,8 @@ public: if (_builder) { delete _builder; } - - - // the two client pointers are weak pointers (i.e. we don't retain them) // therefore we don't do anything about it - } - (id)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)client @@ -195,6 +195,7 @@ public: } _inputMode = kBopomofoModeIdentifier; + _chineseConvertionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConvertionEnanledKey]; } return self; @@ -231,6 +232,11 @@ public: } #endif //DEBUG + NSMenuItem *chineseConvertionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Convertion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"G"]; + chineseConvertionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; + chineseConvertionMenuItem.state = _chineseConvertionEnabled ? NSControlStateValueOn : NSControlStateValueOff; + [menu addItem:chineseConvertionMenuItem]; + NSMenuItem *updateCheckItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Check for Updates…", @"") action:@selector(checkForUpdate:) keyEquivalent:@""]; [menu addItem:updateCheckItem]; @@ -388,8 +394,13 @@ public: return; } + NSString *buffer = _composingBuffer; + if (_chineseConvertionEnabled) { + buffer = [OpenCCBridge convert:_composingBuffer]; + } + // commit the text, clear the state - [client insertText:_composingBuffer replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + [client insertText:buffer replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; _builder->clear(); _walkedNodes.clear(); [_composingBuffer setString:@""]; @@ -1428,6 +1439,12 @@ public: [[NSUserDefaults standardUserDefaults] setBool:toggle forKey:kDisableUserCandidateSelectionLearning]; } +- (void)toggleChineseConverter:(id)sender +{ + _chineseConvertionEnabled = !_chineseConvertionEnabled; + [[NSUserDefaults standardUserDefaults] setBool:_chineseConvertionEnabled forKey:kChineseConvertionEnanledKey]; +} + - (void)clearLearningDictionary:(id)sender { [gCandidateLearningDictionary removeAllObjects]; diff --git a/Source/McBopomofo-Bridging-Header.h b/Source/McBopomofo-Bridging-Header.h new file mode 100644 index 00000000..1b2cb5d6 --- /dev/null +++ b/Source/McBopomofo-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/Source/OpenCCBridge.swift b/Source/OpenCCBridge.swift new file mode 100644 index 00000000..e67cfa9b --- /dev/null +++ b/Source/OpenCCBridge.swift @@ -0,0 +1,29 @@ +import Foundation +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 { + private static let shared = OpenCCBridge() + private var conveter: ChineseConverter? + + override init() { + let mainBundle = Bundle.main + let dictionaryBundleUrl = mainBundle.bundleURL + .appendingPathComponent("Contents", isDirectory: true) + .appendingPathComponent("Plugins", isDirectory: true) + .appendingPathComponent("OpenCCDictionary.bundle") + if let dictionaryBundle = Bundle(url:dictionaryBundleUrl) { + try? conveter = ChineseConverter(bundle: dictionaryBundle, option: .simplify) + } + super.init() + } + + @objc static func convert(_ string:String) -> String? { + return shared.conveter?.convert(string) + } + + private func convert(_ string:String) -> String? { + return conveter?.convert(string) + } +}