From 8573332879f198e7455b248176d9ca914b721c2c Mon Sep 17 00:00:00 2001 From: zonble Date: Mon, 31 Jan 2022 23:42:02 +0800 Subject: [PATCH 1/9] Removes unused localization. --- McBopomofo.xcodeproj/project.pbxproj | 66 ++++++++------- Source/Base.lproj/InfoPlist.strings | 5 ++ Source/Base.lproj/Localizable.strings | 84 +++++++++++++++++++ Source/Installer/en.lproj/Localizable.strings | 3 + .../zh-Hant.lproj/Localizable.strings | 2 + Source/en.lproj/Localizable.strings | 18 ---- Source/zh-Hant.lproj/Localizable.strings | 18 ---- 7 files changed, 128 insertions(+), 68 deletions(-) create mode 100644 Source/Base.lproj/InfoPlist.strings create mode 100644 Source/Base.lproj/Localizable.strings diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index ec38f092..38d6e4a9 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -11,8 +11,6 @@ 6A0D4F0915FC0DA600ABF4B3 /* Bopomofo@2x.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4EF015FC0DA600ABF4B3 /* Bopomofo@2x.tiff */; }; 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F2015FC0EB100ABF4B3 /* Mandarin.cpp */; }; 6A0D4F5315FC0EE100ABF4B3 /* preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4F4E15FC0EE100ABF4B3 /* preferences.xib */; }; - 6A0D4F5715FC0EF900ABF4B3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4F4815FC0EE100ABF4B3 /* InfoPlist.strings */; }; - 6A0D4F5815FC0EF900ABF4B3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6A0D4F4A15FC0EE100ABF4B3 /* Localizable.strings */; }; 6A187E2616004C5900466B2E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A187E2816004C5900466B2E /* MainMenu.xib */; }; 6A225A1F23679F2600F685C6 /* NotarizedArchives in Resources */ = {isa = PBXBuildFile; fileRef = 6A225A1E23679F2600F685C6 /* NotarizedArchives */; }; 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; }; @@ -51,16 +49,18 @@ D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; }; D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; }; D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */; }; - D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D47D73C227A7200500255A50 /* FSEventStreamHelper */; }; D47D73A827A6C84F00255A50 /* associated-phrases.cin in Resources */ = {isa = PBXBuildFile; fileRef = D47D73A727A6C84F00255A50 /* associated-phrases.cin */; }; D47D73A927A6C84F00255A50 /* associated-phrases.cin in Resources */ = {isa = PBXBuildFile; fileRef = D47D73A727A6C84F00255A50 /* associated-phrases.cin */; }; D47D73AC27A6CAE600255A50 /* AssociatedPhrases.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47D73AA27A6CAE600255A50 /* AssociatedPhrases.cpp */; }; + D47D73C327A7200500255A50 /* FSEventStreamHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D47D73C227A7200500255A50 /* FSEventStreamHelper */; }; 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 */; }; D485D3B92796A8A000657FF3 /* PreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3B82796A8A000657FF3 /* PreferencesTests.swift */; }; D485D3C02796CE3200657FF3 /* VersionUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */; }; D4A13D5A27A59F0B003BE359 /* InputMethodController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A13D5927A59D5C003BE359 /* InputMethodController.swift */; }; + D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8827A838CF006DB1CF /* Localizable.strings */; }; + D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */; }; D4E569DC27A34D0E00AC2CEF /* KeyHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */; }; D4E569E427A414CB00AC2CEF /* data-plain-bpmf.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6AD7CBC715FE555000691B5B /* data-plain-bpmf.txt */; }; D4E569E527A414CB00AC2CEF /* data.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6A38BBF615FC117A00A8A51F /* data.txt */; }; @@ -145,10 +145,6 @@ 6A0D4F4015FC0EB100ABF4B3 /* OVTextBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVTextBuffer.h; sourceTree = ""; }; 6A0D4F4115FC0EB100ABF4B3 /* OVUTF8Helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVUTF8Helper.h; sourceTree = ""; }; 6A0D4F4215FC0EB100ABF4B3 /* OVWildcard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVWildcard.h; sourceTree = ""; }; - 6A0D4F4915FC0EE100ABF4B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Source/en.lproj/InfoPlist.strings; sourceTree = ""; }; - 6A0D4F4B15FC0EE100ABF4B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Source/en.lproj/Localizable.strings; sourceTree = ""; }; - 6A0D4F5415FC0EF900ABF4B3 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Source/zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; - 6A0D4F5515FC0EF900ABF4B3 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Source/zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 6A0D4F5615FC0EF900ABF4B3 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "Source/zh-Hant.lproj/preferences.xib"; sourceTree = ""; }; 6A15B32421A51F2300B92CD3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 6A15B32521A51F2300B92CD3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -207,10 +203,10 @@ D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = ""; }; D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerBopomofoTests.swift; sourceTree = ""; }; - D47D73C027A71FFA00255A50 /* FSEventStreamHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FSEventStreamHelper; path = Packages/FSEventStreamHelper; sourceTree = ""; }; D47D73A727A6C84F00255A50 /* associated-phrases.cin */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "associated-phrases.cin"; sourceTree = ""; }; D47D73AA27A6CAE600255A50 /* AssociatedPhrases.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AssociatedPhrases.cpp; sourceTree = ""; }; D47D73AB27A6CAE600255A50 /* AssociatedPhrases.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AssociatedPhrases.h; sourceTree = ""; }; + D47D73C027A71FFA00255A50 /* FSEventStreamHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FSEventStreamHelper; path = Packages/FSEventStreamHelper; 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 = ""; }; @@ -220,6 +216,12 @@ D485D3BF2796CE3200657FF3 /* VersionUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionUpdateTests.swift; sourceTree = ""; }; D495583A27A5C6C4006ADE1C /* LanguageModelManager+Privates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LanguageModelManager+Privates.h"; sourceTree = ""; }; D4A13D5927A59D5C003BE359 /* InputMethodController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMethodController.swift; sourceTree = ""; }; + D4E33D8927A838CF006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + D4E33D8B27A838D5006DB1CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + D4E33D8C27A838D8006DB1CF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; + D4E33D8E27A838F0006DB1CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; + D4E33D9027A838F4006DB1CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D4E33D9127A838F7006DB1CF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = ""; }; D4E569DA27A34CC100AC2CEF /* KeyHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyHandler.h; sourceTree = ""; }; D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyHandler.mm; sourceTree = ""; }; D4F0BBDE279AF1AF0071253C /* ArchiveUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveUtil.swift; sourceTree = ""; }; @@ -423,8 +425,8 @@ 6AFF97F0253B299E007F1C49 /* NonModalAlertWindowController.xib */, 6A0D4EEE15FC0DA600ABF4B3 /* Images */, 6A0D4EF515FC0DA600ABF4B3 /* McBopomofo-Info.plist */, - 6A0D4F4815FC0EE100ABF4B3 /* InfoPlist.strings */, - 6A0D4F4A15FC0EE100ABF4B3 /* Localizable.strings */, + D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */, + D4E33D8827A838CF006DB1CF /* Localizable.strings */, 6A187E2816004C5900466B2E /* MainMenu.xib */, 6A0D4F4E15FC0EE100ABF4B3 /* preferences.xib */, ); @@ -640,14 +642,14 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */, 6A6ED16E2797650A0012872E /* template-exclude-phrases.txt in Resources */, 6A0D4F0815FC0DA600ABF4B3 /* Bopomofo.tiff in Resources */, 6A0D4F0915FC0DA600ABF4B3 /* Bopomofo@2x.tiff in Resources */, 6A6ED16D2797650A0012872E /* template-exclude-phrases-plain-bpmf.txt in Resources */, 6A0D4F5315FC0EE100ABF4B3 /* preferences.xib in Resources */, - 6A0D4F5715FC0EF900ABF4B3 /* InfoPlist.strings in Resources */, - 6A0D4F5815FC0EF900ABF4B3 /* Localizable.strings in Resources */, 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */, + D4E33D8F27A838F0006DB1CF /* InfoPlist.strings in Resources */, 6A38BC1515FC117A00A8A51F /* data.txt in Resources */, 6A6ED16B2797650A0012872E /* template-phrases-replacement.txt in Resources */, 6AFF97F2253B299E007F1C49 /* NonModalAlertWindowController.xib in Resources */, @@ -775,26 +777,6 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 6A0D4F4815FC0EE100ABF4B3 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - 6A0D4F4915FC0EE100ABF4B3 /* en */, - 6A0D4F5415FC0EF900ABF4B3 /* zh-Hant */, - ); - name = InfoPlist.strings; - path = ..; - sourceTree = ""; - }; - 6A0D4F4A15FC0EE100ABF4B3 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 6A0D4F4B15FC0EE100ABF4B3 /* en */, - 6A0D4F5515FC0EF900ABF4B3 /* zh-Hant */, - ); - name = Localizable.strings; - path = ..; - sourceTree = ""; - }; 6A0D4F4E15FC0EE100ABF4B3 /* preferences.xib */ = { isa = PBXVariantGroup; children = ( @@ -890,6 +872,26 @@ path = Source/Installer; sourceTree = SOURCE_ROOT; }; + D4E33D8827A838CF006DB1CF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + D4E33D8927A838CF006DB1CF /* Base */, + D4E33D8B27A838D5006DB1CF /* en */, + D4E33D8C27A838D8006DB1CF /* zh-Hant */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + D4E33D8D27A838F0006DB1CF /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D4E33D8E27A838F0006DB1CF /* Base */, + D4E33D9027A838F4006DB1CF /* en */, + D4E33D9127A838F7006DB1CF /* zh-Hant */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; D4F0BBE9279B14C20071253C /* Credits.rtf */ = { isa = PBXVariantGroup; children = ( diff --git a/Source/Base.lproj/InfoPlist.strings b/Source/Base.lproj/InfoPlist.strings new file mode 100644 index 00000000..9414dcd8 --- /dev/null +++ b/Source/Base.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +CFBundleName = "McBopomofo"; +CFBundleDisplayName = "McBopomofo"; +NSHumanReadableCopyright = "Copyright © 2011-2022 Mengjuei Hsieh et al.\nAll Rights Reserved."; +"org.openvanilla.inputmethod.McBopomofo.Bopomofo" = "Bopomofo"; +"org.openvanilla.inputmethod.McBopomofo.PlainBopomofo" = "Plain Bopomofo"; diff --git a/Source/Base.lproj/Localizable.strings b/Source/Base.lproj/Localizable.strings new file mode 100644 index 00000000..4995702d --- /dev/null +++ b/Source/Base.lproj/Localizable.strings @@ -0,0 +1,84 @@ +/* No comment provided by engineer. */ +"About McBopomofo…" = "About McBopomofo…"; + +/* No comment provided by engineer. */ +"McBopomofo Preferences" = "McBopomofo Preferences"; + +/* No comment provided by engineer. */ +"Check for Updates…" = "Check for Updates…"; + +/* No comment provided by engineer. */ +"Update Check Failed" = "Update Check Failed"; + +/* No comment provided by engineer. */ +"There may be no internet connection or the server failed to respond.\n\nError message: %@" = "There may be no internet connection or the server failed to respond.\n\nError message: %@"; + +/* No comment provided by engineer. */ +"OK" = "OK"; + +/* No comment provided by engineer. */ +"Dismiss" = "Dismiss"; + +/* No comment provided by engineer. */ +"New Version Available" = "New Version Available"; + +/* No comment provided by engineer. */ +"Not Now" = "Not Now"; + +/* No comment provided by engineer. */ +"Visit Website" = "Visit Website"; + +/* No comment provided by engineer. */ +"You're currently using McBopomofo %@ (%@), a new version %@ (%@) is now available. Do you want to visit McBopomofo's website to download the version?%@" = "You're currently using McBopomofo %@ (%@), a new version %@ (%@) is now available. Do you want to visit McBopomofo's website to download the version?%@"; + +"Chinese Conversion" = "Convert to Simplified Chinese"; + +"User Phrases" = "User Phrases"; + +"Edit User Phrases" = "Edit User Phrases"; + +"Reload User Phrases" = "Reload User Phrases"; + +"Unable to create the user phrase file." = "Unable to create the user phrase file."; + +"Please check the permission of at \"%@\"." = "Please check the permission of at \"%@\"."; + +"Edit Excluded Phrases" = "Edit Excluded Phrases"; + +"Use Half-Width Punctuations" = "Use Half-Width Punctuations"; + +"You are now selecting \"%@\". You can add a phrase with two or more characters." = "You are now selecting \"%@\". You can add a phrase with two or more characters."; + +"You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase."; + +"You are now selecting \"%@\". A phrase cannot be longer than %d characters." = "You are now selecting \"%@\". A phrase cannot be longer than %d characters."; + +"Chinese conversion on" = "Chinese conversion on"; + +"Chinese conversion off" = "Chinese conversion off"; + +"Edit Phrase Replacement Table" = "Edit Phrase Replacement Table"; + +"Use Phrase Replacement" = "Use Phrase Replacement"; + +"Candidates keys cannot be empty." = "Candidates keys cannot be empty."; + +"Candidate keys can only contain latin characters and numbers." = "Candidate keys can only contain latin characters and numbers."; + +"Candidate keys cannot contain space." = "Candidate keys cannot contain space."; + +"There should not be duplicated keys." = "There should not be duplicated keys."; + +"The length of your candidate keys can not be less than 4 characters." = "The length of your candidate keys can not be less than 4 characters."; + +"The length of your candidate keys can not be larger than 15 characters." = "The length of your candidate keys can not be larger than 15 characters."; + +"Phrase replacement mode is on. Not suggested to add phrase in the mode." = "Phrase replacement mode is on. Not suggested to add phrase in the mode."; + +"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; + +"Half-width punctuation on" = "Half-width punctuation on"; + +"Half-width punctuation off" = "Half-width punctuation off"; + +"Associated Phrases" = "Associated Phrases"; diff --git a/Source/Installer/en.lproj/Localizable.strings b/Source/Installer/en.lproj/Localizable.strings index b3f28f52..140c2a4f 100644 --- a/Source/Installer/en.lproj/Localizable.strings +++ b/Source/Installer/en.lproj/Localizable.strings @@ -37,3 +37,6 @@ "Warning" = "Warning"; "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources." = "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources."; "Continue" = "Continue"; + +"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; +"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; \ No newline at end of file diff --git a/Source/Installer/zh-Hant.lproj/Localizable.strings b/Source/Installer/zh-Hant.lproj/Localizable.strings index 5a492caa..542a94a0 100644 --- a/Source/Installer/zh-Hant.lproj/Localizable.strings +++ b/Source/Installer/zh-Hant.lproj/Localizable.strings @@ -37,3 +37,5 @@ "Warning" = "安裝不完整"; "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources." = "輸入法已經安裝好,但可能沒有完全啟用。請從「系統偏好設定」 > 「鍵盤」 > 「輸入方式」分頁加入輸入法。"; "Continue" = "繼續"; + +"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; \ No newline at end of file diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index a75f8f25..4995702d 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -1,30 +1,12 @@ /* No comment provided by engineer. */ "About McBopomofo…" = "About McBopomofo…"; -/* No comment provided by engineer. */ -"Clear Learning Dictionary (%ju Items)" = "Clear Learning Dictionary (%ju Items)"; - -/* No comment provided by engineer. */ -"Dump Learning Data to Console" = "Dump Learning Data to Console"; - -/* No comment provided by engineer. */ -"Enable Selection Learning" = "Enable Selection Learning"; - /* No comment provided by engineer. */ "McBopomofo Preferences" = "McBopomofo Preferences"; -/* No comment provided by engineer. */ -"Check Later" = "Check Later"; - /* No comment provided by engineer. */ "Check for Updates…" = "Check for Updates…"; -/* No comment provided by engineer. */ -"Check for Update Completed" = "Check for Update Completed"; - -/* No comment provided by engineer. */ -"You are already using the latest version of McBopomofo." = "You are already using the latest version of McBopomofo."; - /* No comment provided by engineer. */ "Update Check Failed" = "Update Check Failed"; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index e2501806..69964d8a 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -1,30 +1,12 @@ /* No comment provided by engineer. */ "About McBopomofo…" = "關於小麥注音…"; -/* No comment provided by engineer. */ -"Clear Learning Dictionary (%ju Items)" = "清除學習辭典 (%ju 個項目)"; - -/* No comment provided by engineer. */ -"Dump Learning Data to Console" = "將學習辭典內容輸出到 Console 上"; - -/* No comment provided by engineer. */ -"Enable Selection Learning" = "使用自動學習功能"; - /* No comment provided by engineer. */ "McBopomofo Preferences" = "小麥注音偏好設定"; -/* No comment provided by engineer. */ -"Check Later" = "晚點再通知我"; - /* No comment provided by engineer. */ "Check for Updates…" = "檢查是否有新版…"; -/* No comment provided by engineer. */ -"Check for Update Completed" = "新版檢查完畢"; - -/* No comment provided by engineer. */ -"You are already using the latest version of McBopomofo." = "目前使用的已經是最新版本。"; - /* No comment provided by engineer. */ "Update Check Failed" = "無法檢查新版"; From 536c9d1fc70c3510c66e7d083f78afd94cdeef20 Mon Sep 17 00:00:00 2001 From: zonble Date: Tue, 1 Feb 2022 02:32:55 +0800 Subject: [PATCH 2/9] Adds some punctuations using control key. It allows users ever use Microsoft Bopomofo to input punctuations using keys like ctrl + comma. --- Source/KeyHandler.mm | 23 +++++++++++++++++++---- Source/KeyHandlerInput.swift | 4 ++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Source/KeyHandler.mm b/Source/KeyHandler.mm index 44d53e05..05ce0323 100644 --- a/Source/KeyHandler.mm +++ b/Source/KeyHandler.mm @@ -231,7 +231,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot } // if the composing buffer is empty and there's no reading, and there is some function key combination, we ignore it - BOOL isFunctionKey = ([input isCommandHold] || [input isControlHold] || [input isOptionHold] || [input isNumericPad]); + BOOL isFunctionKey = ([input isCommandHold] || [input isOptionHold] || [input isNumericPad]) || [input isControlHotKey]; if (![state isKindOfClass:[InputStateNotEmpty class]] && ![state isKindOfClass:[InputStateAssociatedPhrases class]] && isFunctionKey) { @@ -305,7 +305,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // MARK: Handle BPMF Keys // see if it's valid BPMF reading - if (_bpmfReadingBuffer->isValidKey((char) charCode)) { + if (![input isControlHold] && _bpmfReadingBuffer->isValidKey((char) charCode)) { _bpmfReadingBuffer->combineKey((char) charCode); // if we have a tone marker, we have to insert the reading to the @@ -490,8 +490,16 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // MARK: Punctuation // if nothing is matched, see if it's a punctuation key for current layout. + + string punctuationNamePrefix; + if ([input isControlHold]) { + punctuationNamePrefix = string("_ctrl_punctuation_"); + } else if (Preferences.halfWidthPunctuationEnabled) { + punctuationNamePrefix = string("_half_punctuation_"); + } else { + punctuationNamePrefix = string("_punctuation_"); + } string layout = [self _currentLayout]; - string punctuationNamePrefix = Preferences.halfWidthPunctuationEnabled ? string("_half_punctuation_") : string("_punctuation_"); string customPunctuation = punctuationNamePrefix + layout + string(1, (char) charCode); if ([self _handlePunctuation:customPunctuation state:state usingVerticalMode:input.useVerticalMode stateCallback:stateCallback errorCallback:errorCallback]) { return YES; @@ -1070,7 +1078,14 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if (_inputMode == InputModePlainBopomofo) { string layout = [self _currentLayout]; - string punctuationNamePrefix = Preferences.halfWidthPunctuationEnabled ? string("_half_punctuation_") : string("_punctuation_"); + string punctuationNamePrefix; + if ([input isControlHold]) { + punctuationNamePrefix = string("_ctrl_punctuation_"); + } else if (Preferences.halfWidthPunctuationEnabled) { + punctuationNamePrefix = string("_half_punctuation_"); + } else { + punctuationNamePrefix = string("_punctuation_"); + } string customPunctuation = punctuationNamePrefix + layout + string(1, (char) charCode); string punctuation = punctuationNamePrefix + string(1, (char) charCode); diff --git a/Source/KeyHandlerInput.swift b/Source/KeyHandlerInput.swift index ad86c1b2..ad8c5bb9 100644 --- a/Source/KeyHandlerInput.swift +++ b/Source/KeyHandlerInput.swift @@ -102,6 +102,10 @@ class KeyHandlerInput: NSObject { flags.contains([.control]) } + @objc var isControlHotKey: Bool { + flags.contains([.control]) && inputText?.first?.isLetter ?? false + } + @objc var isOptionHold: Bool { flags.contains([.option]) } From 74509ce3cb8a8790977946bcb2bb46ec1793c0a3 Mon Sep 17 00:00:00 2001 From: zonble Date: Tue, 1 Feb 2022 06:18:40 +0800 Subject: [PATCH 3/9] Corrects the selection range while using Shift + Arrow keys to add new phrases. --- Source/InputMethodController.swift | 8 +++ Source/InputState.swift | 97 +++++++++++++++++++++++------- Source/KeyHandler.mm | 28 ++++++--- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/Source/InputMethodController.swift b/Source/InputMethodController.swift index 3b0982f6..aaf247c5 100644 --- a/Source/InputMethodController.swift +++ b/Source/InputMethodController.swift @@ -492,6 +492,10 @@ extension McBopomofoInputMethodController { } (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) + if lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor > 0 { + cursor -= 1 + (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) + } if useVerticalMode { gCurrentCandidateController?.set(windowTopLeftPoint: NSMakePoint(lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, lineHeightRect.origin.y - 4.0), bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0) @@ -507,6 +511,10 @@ extension McBopomofoInputMethodController { cursor -= 1 } (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) + if lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor > 0 { + cursor -= 1 + (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) + } McBopomofoInputMethodController.tooltipController.show(tooltip: tooltip, at: lineHeightRect.origin) } diff --git a/Source/InputState.swift b/Source/InputState.swift index 6483c626..fdbe1ebb 100644 --- a/Source/InputState.swift +++ b/Source/InputState.swift @@ -104,28 +104,28 @@ class InputState: NSObject { /// Represents that the composing buffer is not empty. @objc (InputStateNotEmpty) class NotEmpty: InputState { - @objc private(set) var composingBuffer: String = "" - @objc private(set) var cursorIndex: UInt = 0 + @objc private(set) var composingBuffer: String + @objc private(set) var cursorIndex: UInt + @objc private(set) var phrases: [InputPhrase] - @objc init(composingBuffer: String, cursorIndex: UInt) { + @objc init(composingBuffer: String, cursorIndex: UInt, phrases: [InputPhrase]) { self.composingBuffer = composingBuffer self.cursorIndex = cursorIndex + self.phrases = phrases } override var description: String { - "" + "" } } /// Represents that the user is inputting text. @objc (InputStateInputting) class Inputting: NotEmpty { - @objc var bpmfReading: String = "" - @objc var bpmfReadingCursorIndex: UInt8 = 0 @objc var poppedText: String = "" - @objc override init(composingBuffer: String, cursorIndex: UInt) { - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + @objc override init(composingBuffer: String, cursorIndex: UInt, phrases: [InputPhrase]) { + super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) } @objc var attributedString: NSAttributedString { @@ -137,7 +137,7 @@ class InputState: NSObject { } override var description: String { - "" + ", poppedText:\(poppedText)>" } } @@ -147,6 +147,7 @@ class InputState: NSObject { /// Represents that the user is marking a range in the composing buffer. @objc (InputStateMarking) class Marking: NotEmpty { + @objc private(set) var markerIndex: UInt @objc private(set) var markedRange: NSRange @objc var tooltip: String { @@ -170,15 +171,12 @@ class InputState: NSObject { return String(format: NSLocalizedString("You are now selecting \"%@\". Press enter to add a new phrase.", comment: ""), text) } - @objc private(set) var readings: [String] - - @objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) { + @objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, phrases: [InputPhrase]) { self.markerIndex = markerIndex let begin = min(cursorIndex, markerIndex) let end = max(cursorIndex, markerIndex) markedRange = NSMakeRange(Int(begin), Int(end - begin)) - self.readings = readings - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) } @objc var attributedString: NSAttributedString { @@ -197,16 +195,16 @@ class InputState: NSObject { .underlineStyle: NSUnderlineStyle.single.rawValue, .markedClauseSegment: 2 ], range: NSRange(location: end, - length: composingBuffer.count - end)) + length: (composingBuffer as NSString).length - end)) return attributedSting } override var description: String { - "" + "" } @objc func convertToInputting() -> Inputting { - let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) return state } @@ -217,7 +215,19 @@ class InputState: NSObject { @objc var userPhrase: String { let text = (composingBuffer as NSString).substring(with: markedRange) let end = markedRange.location + markedRange.length - let readings = readings[markedRange.location..= end { + break + } + if length >= markedRange.location { + selectedPhrases.append(component) + } + length += (component.text as NSString).length + } + + let readings = selectedPhrases.map { $0.reading } let joined = readings.joined(separator: "-") return "\(text) \(joined)" } @@ -229,10 +239,10 @@ class InputState: NSObject { @objc private(set) var candidates: [String] @objc private(set) var useVerticalMode: Bool - @objc init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) { + @objc init(composingBuffer: String, cursorIndex: UInt, candidates: [String], phrases: [InputPhrase], useVerticalMode: Bool) { self.candidates = candidates self.useVerticalMode = useVerticalMode - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) } @objc var attributedString: NSAttributedString { @@ -244,7 +254,7 @@ class InputState: NSObject { } override var description: String { - "" + "" } } @@ -266,3 +276,48 @@ class InputState: NSObject { } } + +class InputPhrase: NSObject { + @objc private (set) var text: String + @objc private (set) var reading: String + + @objc init(text: String, reading: String) { + self.text = text + self.reading = reading + super.init() + } +} + +class StringUtils: NSObject { + + static func convertToCharIndex(from utf16Index: Int, in string: String) -> Int { + var length = 0 + for (i, c) in string.enumerated() { + if length >= utf16Index { + return i + } + length += c.utf16.count + } + return string.count + } + + @objc (nextUtf16PositionForIndex:in:) + static func nextUtf16Position(for index: Int, in string: String) -> Int { + var index = convertToCharIndex(from: index, in: string) + if index < string.count { + index += 1 + } + let count = string[.. Int { + var index = convertToCharIndex(from: index, in: string) + if index > 0 { + index -= 1 + } + let count = string[..cursorIndex() > 0) { - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:currentState.cursorIndex - 1 readings: [self _currentReadings]]; + if (currentState.cursorIndex > 0) { + NSInteger previousPosition = [StringUtils previousUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition phrases:currentState.phrases]; stateCallback(marking); } else { errorCallback(); @@ -613,8 +614,9 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if ([input isShiftHold]) { // Shift + Right - if (_builder->cursorIndex() < _builder->length()) { - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:currentState.cursorIndex + 1 readings: [self _currentReadings]]; + if (currentState.cursorIndex < currentState.composingBuffer.length) { + NSInteger nextPosition = [StringUtils nextUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition phrases:currentState.phrases]; stateCallback(marking); } else { errorCallback(); @@ -840,8 +842,8 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot && ([input isShiftHold])) { NSUInteger index = state.markerIndex; if (index > 0) { - index -= 1; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; + index = [StringUtils previousUtf16PositionForIndex:index in:state.composingBuffer]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index phrases:state.phrases]; stateCallback(marking); } else { errorCallback(); @@ -855,8 +857,8 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot && ([input isShiftHold])) { NSUInteger index = state.markerIndex; if (index < state.composingBuffer.length) { - index += 1; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; + index = [StringUtils nextUtf16PositionForIndex:index in:state.composingBuffer]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index phrases:state.phrases]; stateCallback(marking); } else { errorCallback(); @@ -1129,6 +1131,8 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot size_t readingCursorIndex = 0; size_t builderCursorIndex = _builder->cursorIndex(); + NSMutableArray *phrases = [[NSMutableArray alloc] init]; + // we must do some Unicode codepoint counting to find the actual cursor location for the client // i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars // locations @@ -1141,6 +1145,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSString *valueString = [NSString stringWithUTF8String:nodeStr.c_str()]; [composingBuffer appendString:valueString]; + NSString *readingString = [NSString stringWithUTF8String:(*wi).node->currentKeyValue().key.c_str()]; + InputPhrase *phrase = [[InputPhrase alloc] initWithText:valueString reading:readingString]; + [phrases addObject:phrase]; + // this re-aligns the cursor index in the composed string // (the actual cursor on the screen) with the builder's logical // cursor (reading) cursor; each built node has a "spanning length" @@ -1169,7 +1177,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSString *composedText = [head stringByAppendingString:[reading stringByAppendingString:tail]]; NSInteger cursorIndex = composedStringCursorIndex + [reading length]; - InputStateInputting *newState = [[InputStateInputting alloc] initWithComposingBuffer:composedText cursorIndex:cursorIndex]; + InputStateInputting *newState = [[InputStateInputting alloc] initWithComposingBuffer:composedText cursorIndex:cursorIndex phrases:phrases]; return newState; } @@ -1239,7 +1247,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot } } - InputStateChoosingCandidate *state = [[InputStateChoosingCandidate alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex candidates:candidatesArray useVerticalMode:useVerticalMode]; + InputStateChoosingCandidate *state = [[InputStateChoosingCandidate alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex candidates:candidatesArray phrases:currentState.phrases useVerticalMode:useVerticalMode]; return state; } From 347560e36bf30366237d8dc3d20c525275761d11 Mon Sep 17 00:00:00 2001 From: zonble Date: Tue, 1 Feb 2022 08:11:09 +0800 Subject: [PATCH 4/9] Disables adding phrases with shift-arraw keys when the length of the composing buffer and readings do not match. --- Source/Base.lproj/Localizable.strings | 2 + Source/InputState.swift | 52 +++++++++++------------- Source/KeyHandler.mm | 12 +++--- Source/en.lproj/Localizable.strings | 2 + Source/zh-Hant.lproj/Localizable.strings | 2 + 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Source/Base.lproj/Localizable.strings b/Source/Base.lproj/Localizable.strings index 4995702d..d9bc1ea9 100644 --- a/Source/Base.lproj/Localizable.strings +++ b/Source/Base.lproj/Localizable.strings @@ -82,3 +82,5 @@ "Half-width punctuation off" = "Half-width punctuation off"; "Associated Phrases" = "Associated Phrases"; + +"There are special phrases in your text. We don't support adding new phrases in this case." = "There are special phrases in your text. We don't support adding new phrases in this case."; diff --git a/Source/InputState.swift b/Source/InputState.swift index fdbe1ebb..869559a1 100644 --- a/Source/InputState.swift +++ b/Source/InputState.swift @@ -106,16 +106,14 @@ class InputState: NSObject { class NotEmpty: InputState { @objc private(set) var composingBuffer: String @objc private(set) var cursorIndex: UInt - @objc private(set) var phrases: [InputPhrase] - @objc init(composingBuffer: String, cursorIndex: UInt, phrases: [InputPhrase]) { + @objc init(composingBuffer: String, cursorIndex: UInt) { self.composingBuffer = composingBuffer self.cursorIndex = cursorIndex - self.phrases = phrases } override var description: String { - "" + "" } } @@ -124,8 +122,8 @@ class InputState: NSObject { class Inputting: NotEmpty { @objc var poppedText: String = "" - @objc override init(composingBuffer: String, cursorIndex: UInt, phrases: [InputPhrase]) { - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) + @objc override init(composingBuffer: String, cursorIndex: UInt) { + super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) } @objc var attributedString: NSAttributedString { @@ -137,7 +135,7 @@ class InputState: NSObject { } override var description: String { - ", poppedText:\(poppedText)>" + ", poppedText:\(poppedText)>" } } @@ -152,6 +150,10 @@ class InputState: NSObject { @objc private(set) var markedRange: NSRange @objc var tooltip: String { + if composingBuffer.count != readings.count { + return NSLocalizedString("There are special phrases in your text. We don't support adding new phrases in this case.", comment: "") + } + if Preferences.phraseReplacementEnabled { return NSLocalizedString("Phrase replacement mode is on. Not suggested to add phrase in the mode.", comment: "") } @@ -171,12 +173,15 @@ class InputState: NSObject { return String(format: NSLocalizedString("You are now selecting \"%@\". Press enter to add a new phrase.", comment: ""), text) } - @objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, phrases: [InputPhrase]) { + @objc private(set) var readings: [String] + + @objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) { self.markerIndex = markerIndex let begin = min(cursorIndex, markerIndex) let end = max(cursorIndex, markerIndex) markedRange = NSMakeRange(Int(begin), Int(end - begin)) - super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) + self.readings = readings + super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) } @objc var attributedString: NSAttributedString { @@ -200,34 +205,25 @@ class InputState: NSObject { } override var description: String { - "" + "" } @objc func convertToInputting() -> Inputting { - let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex, phrases: phrases) + let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex) return state } @objc var validToWrite: Bool { - markedRange.length >= kMinMarkRangeLength && markedRange.length <= kMaxMarkRangeLength + if composingBuffer.count != readings.count { + return false + } + return markedRange.length >= kMinMarkRangeLength && markedRange.length <= kMaxMarkRangeLength } @objc var userPhrase: String { let text = (composingBuffer as NSString).substring(with: markedRange) let end = markedRange.location + markedRange.length - var selectedPhrases = [InputPhrase]() - var length = 0 - for component in self.phrases { - if length >= end { - break - } - if length >= markedRange.location { - selectedPhrases.append(component) - } - length += (component.text as NSString).length - } - - let readings = selectedPhrases.map { $0.reading } + let readings = readings[markedRange.location.." + "" } } diff --git a/Source/KeyHandler.mm b/Source/KeyHandler.mm index 35a7838b..cc1631b0 100644 --- a/Source/KeyHandler.mm +++ b/Source/KeyHandler.mm @@ -579,7 +579,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // Shift + left if (currentState.cursorIndex > 0) { NSInteger previousPosition = [StringUtils previousUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition phrases:currentState.phrases]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition readings:[self _currentReadings]]; stateCallback(marking); } else { errorCallback(); @@ -616,7 +616,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // Shift + Right if (currentState.cursorIndex < currentState.composingBuffer.length) { NSInteger nextPosition = [StringUtils nextUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition phrases:currentState.phrases]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition readings:[self _currentReadings]]; stateCallback(marking); } else { errorCallback(); @@ -843,7 +843,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSUInteger index = state.markerIndex; if (index > 0) { index = [StringUtils previousUtf16PositionForIndex:index in:state.composingBuffer]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index phrases:state.phrases]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; stateCallback(marking); } else { errorCallback(); @@ -858,7 +858,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSUInteger index = state.markerIndex; if (index < state.composingBuffer.length) { index = [StringUtils nextUtf16PositionForIndex:index in:state.composingBuffer]; - InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index phrases:state.phrases]; + InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; stateCallback(marking); } else { errorCallback(); @@ -1177,7 +1177,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSString *composedText = [head stringByAppendingString:[reading stringByAppendingString:tail]]; NSInteger cursorIndex = composedStringCursorIndex + [reading length]; - InputStateInputting *newState = [[InputStateInputting alloc] initWithComposingBuffer:composedText cursorIndex:cursorIndex phrases:phrases]; + InputStateInputting *newState = [[InputStateInputting alloc] initWithComposingBuffer:composedText cursorIndex:cursorIndex]; return newState; } @@ -1247,7 +1247,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot } } - InputStateChoosingCandidate *state = [[InputStateChoosingCandidate alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex candidates:candidatesArray phrases:currentState.phrases useVerticalMode:useVerticalMode]; + InputStateChoosingCandidate *state = [[InputStateChoosingCandidate alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex candidates:candidatesArray useVerticalMode:useVerticalMode]; return state; } diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index 4995702d..d9bc1ea9 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -82,3 +82,5 @@ "Half-width punctuation off" = "Half-width punctuation off"; "Associated Phrases" = "Associated Phrases"; + +"There are special phrases in your text. We don't support adding new phrases in this case." = "There are special phrases in your text. We don't support adding new phrases in this case."; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index 69964d8a..68278358 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -82,3 +82,5 @@ "Half-width punctuation off" = "已經切回到全型標點模式"; "Associated Phrases" = "聯想詞"; + +"There are special phrases in your text. We don't support adding new phrases in this case." = "您輸入了特殊符號,我們還無法支援在這種狀況下手動加詞。"; From 82dbed78153d91ff325805676da224a2baafecd0 Mon Sep 17 00:00:00 2001 From: zonble Date: Tue, 1 Feb 2022 08:41:36 +0800 Subject: [PATCH 5/9] Fixes a crash on adding user phrases. --- Source/InputState.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/InputState.swift b/Source/InputState.swift index 869559a1..3e7e0a47 100644 --- a/Source/InputState.swift +++ b/Source/InputState.swift @@ -222,8 +222,9 @@ class InputState: NSObject { @objc var userPhrase: String { let text = (composingBuffer as NSString).substring(with: markedRange) - let end = markedRange.location + markedRange.length - let readings = readings[markedRange.location.. Date: Wed, 2 Feb 2022 01:04:49 +0800 Subject: [PATCH 6/9] Shows a tooltip when then cursor in a phrase whose length and count of readings do not match. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit McBopomofo allows users to input pheases with a different length of the characters and Bopomofo readings, for example, users can input ∴ with ㄙㄨㄛˇ-ㄧˇ. When the cursor if between ㄙㄨㄛˇ and ㄧˇ, the users have no clue where the cursor exactly is. The tooltip is to tell the users the cursor is now betwen ㄙㄨㄛˇ and ㄧˇ. --- McBopomofo.xcodeproj/project.pbxproj | 4 ++ Source/Base.lproj/Localizable.strings | 6 ++ Source/InputMethodController.swift | 19 +++--- Source/InputState.swift | 74 +++++++++--------------- Source/KeyHandler.mm | 54 +++++++++++------ Source/StringUtils.swift | 73 +++++++++++++++++++++++ Source/en.lproj/Localizable.strings | 6 ++ Source/zh-Hant.lproj/Localizable.strings | 6 ++ 8 files changed, 169 insertions(+), 73 deletions(-) create mode 100644 Source/StringUtils.swift diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 38d6e4a9..5f4ba050 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ D44FB74A2791B829003C80A6 /* VXHanConvert in Frameworks */ = {isa = PBXBuildFile; productRef = D44FB7492791B829003C80A6 /* VXHanConvert */; }; D44FB74D2792189A003C80A6 /* PhraseReplacementMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */; }; D456576E279E4F7B00DF6BC9 /* KeyHandlerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */; }; + D45EB5C127A9894C00E28B17 /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45EB5BF27A9890C00E28B17 /* StringUtils.swift */; }; D461B792279DAC010070E734 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D461B791279DAC010070E734 /* InputState.swift */; }; D47B92C027972AD100458394 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47B92BF27972AC800458394 /* main.swift */; }; D47D73A427A5D43900255A50 /* KeyHandlerBopomofoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */; }; @@ -200,6 +201,7 @@ D44FB74B2792189A003C80A6 /* PhraseReplacementMap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PhraseReplacementMap.cpp; sourceTree = ""; }; D44FB74C2792189A003C80A6 /* PhraseReplacementMap.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhraseReplacementMap.h; sourceTree = ""; }; D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerInput.swift; sourceTree = ""; }; + D45EB5BF27A9890C00E28B17 /* StringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtils.swift; sourceTree = ""; }; D461B791279DAC010070E734 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = ""; }; D47B92BF27972AC800458394 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; D47D73A327A5D43900255A50 /* KeyHandlerBopomofoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyHandlerBopomofoTests.swift; sourceTree = ""; }; @@ -302,6 +304,7 @@ D4E569DB27A34CC100AC2CEF /* KeyHandler.mm */, D456576D279E4F7B00DF6BC9 /* KeyHandlerInput.swift */, D461B791279DAC010070E734 /* InputState.swift */, + D45EB5BF27A9890C00E28B17 /* StringUtils.swift */, D427F76B278CA1BA004A2160 /* AppDelegate.swift */, D44FB74427915555003C80A6 /* Preferences.swift */, D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, @@ -730,6 +733,7 @@ D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, 6ACC3D452793701600F1B140 /* ParselessLM.cpp in Sources */, + D45EB5C127A9894C00E28B17 /* StringUtils.swift in Sources */, D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */, 6ACC3D3F27914F2400F1B140 /* KeyValueBlobReader.cpp in Sources */, D41355D8278D74B5005E5CBD /* LanguageModelManager.mm in Sources */, diff --git a/Source/Base.lproj/Localizable.strings b/Source/Base.lproj/Localizable.strings index d9bc1ea9..3f906806 100644 --- a/Source/Base.lproj/Localizable.strings +++ b/Source/Base.lproj/Localizable.strings @@ -84,3 +84,9 @@ "Associated Phrases" = "Associated Phrases"; "There are special phrases in your text. We don't support adding new phrases in this case." = "There are special phrases in your text. We don't support adding new phrases in this case."; + +"Cursor is before \"%@\"." = "Cursor is before \"%@\"."; + +"Cursor is after \"%@\"." = "Cursor is after \"%@\"."; + +"Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\"."; diff --git a/Source/InputMethodController.swift b/Source/InputMethodController.swift index aaf247c5..d8d3591a 100644 --- a/Source/InputMethodController.swift +++ b/Source/InputMethodController.swift @@ -388,6 +388,9 @@ extension McBopomofoInputMethodController { // the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound, // i.e. the client app needs to take care of where to put this composing buffer client.setMarkedText(state.attributedString, selectionRange: NSMakeRange(Int(state.cursorIndex), 0), replacementRange: NSMakeRange(NSNotFound, NSNotFound)) + if !state.tooltip.isEmpty { + show(tooltip: state.tooltip, composingBuffer: state.composingBuffer, cursorIndex: state.cursorIndex, client: client) + } } private func handle(state: InputState.Marking, previous: InputState, client: Any?) { @@ -482,19 +485,18 @@ extension McBopomofoInputMethodController { gCurrentCandidateController?.visible = true var lineHeightRect = NSMakeRect(0.0, 0.0, 16.0, 16.0) - var cursor: UInt = 0 + var cursor: Int = 0 if let state = state as? InputState.ChoosingCandidate { - cursor = state.cursorIndex + cursor = Int(state.cursorIndex) if cursor == state.composingBuffer.count && cursor != 0 { cursor -= 1 } } - (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) - if lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor > 0 { + while lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor >= 0 { + (client as? IMKTextInput)?.attributes(forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect) cursor -= 1 - (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) } if useVerticalMode { @@ -506,14 +508,13 @@ extension McBopomofoInputMethodController { private func show(tooltip: String, composingBuffer: String, cursorIndex: UInt, client: Any!) { var lineHeightRect = NSMakeRect(0.0, 0.0, 16.0, 16.0) - var cursor = cursorIndex + var cursor: Int = Int(cursorIndex) if cursor == composingBuffer.count && cursor != 0 { cursor -= 1 } - (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) - if lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor > 0 { + while lineHeightRect.origin.x == 0 && lineHeightRect.origin.y == 0 && cursor >= 0 { + (client as? IMKTextInput)?.attributes(forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect) cursor -= 1 - (client as? IMKTextInput)?.attributes(forCharacterIndex: Int(cursor), lineHeightRectangle: &lineHeightRect) } McBopomofoInputMethodController.tooltipController.show(tooltip: tooltip, at: lineHeightRect.origin) } diff --git a/Source/InputState.swift b/Source/InputState.swift index 3e7e0a47..8dd6780e 100644 --- a/Source/InputState.swift +++ b/Source/InputState.swift @@ -64,6 +64,8 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the composing buffer is empty. @objc (InputStateEmpty) class Empty: InputState { @@ -76,6 +78,8 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the composing buffer is empty. @objc (InputStateEmptyIgnoringPreviousState) class EmptyIgnoringPreviousState: InputState { @@ -87,6 +91,8 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the input controller is committing text into client app. @objc (InputStateCommitting) class Committing: InputState { @@ -101,6 +107,9 @@ class InputState: NSObject { "" } } + + // MARK: - + /// Represents that the composing buffer is not empty. @objc (InputStateNotEmpty) class NotEmpty: InputState { @@ -117,10 +126,13 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the user is inputting text. @objc (InputStateInputting) class Inputting: NotEmpty { @objc var poppedText: String = "" + @objc var tooltip: String = "" @objc override init(composingBuffer: String, cursorIndex: UInt) { super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex) @@ -139,6 +151,8 @@ class InputState: NSObject { } } + // MARK: - + private let kMinMarkRangeLength = 2 private let kMaxMarkRangeLength = 6 @@ -173,6 +187,7 @@ class InputState: NSObject { return String(format: NSLocalizedString("You are now selecting \"%@\". Press enter to add a new phrase.", comment: ""), text) } + @objc var tooltipForInputting: String = "" @objc private(set) var readings: [String] @objc init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) { @@ -210,14 +225,21 @@ class InputState: NSObject { @objc func convertToInputting() -> Inputting { let state = Inputting(composingBuffer: composingBuffer, cursorIndex: cursorIndex) + state.tooltip = tooltipForInputting return state } @objc var validToWrite: Bool { + /// McBopomofo allows users to input a string whose length differs + /// from the amount of Bopomofo readings. In this case, the range + /// in the composing buffer and the readings could not match, so + /// we disable the function to write user phrases in this case. if composingBuffer.count != readings.count { return false } - return markedRange.length >= kMinMarkRangeLength && markedRange.length <= kMaxMarkRangeLength + + return markedRange.length >= kMinMarkRangeLength && + markedRange.length <= kMaxMarkRangeLength } @objc var userPhrase: String { @@ -230,6 +252,8 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the user is choosing in a candidates list. @objc (InputStateChoosingCandidate) class ChoosingCandidate: NotEmpty { @@ -255,6 +279,8 @@ class InputState: NSObject { } } + // MARK: - + /// Represents that the user is choosing in a candidates list /// in the associated phrases mode. @objc (InputStateAssociatedPhrases) @@ -271,50 +297,4 @@ class InputState: NSObject { "" } } - -} - -class InputPhrase: NSObject { - @objc private (set) var text: String - @objc private (set) var reading: String - - @objc init(text: String, reading: String) { - self.text = text - self.reading = reading - super.init() - } -} - -class StringUtils: NSObject { - - static func convertToCharIndex(from utf16Index: Int, in string: String) -> Int { - var length = 0 - for (i, c) in string.enumerated() { - if length >= utf16Index { - return i - } - length += c.utf16.count - } - return string.count - } - - @objc (nextUtf16PositionForIndex:in:) - static func nextUtf16Position(for index: Int, in string: String) -> Int { - var index = convertToCharIndex(from: index, in: string) - if index < string.count { - index += 1 - } - let count = string[.. Int { - var index = convertToCharIndex(from: index, in: string) - if index > 0 { - index -= 1 - } - let count = string[.. nodes = _builder->grid().nodesCrossingOrEndingAt(cursorIndex); double highestScore = FindHighestScore(nodes, kEpsilon); - _builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue, highestScore); + _builder->grid().overrideNodeScoreForSelectedCandidate(cursorIndex, overrideValue, static_cast(highestScore)); } // then update the text @@ -397,12 +397,10 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // if the spacebar is NOT set to be a selection key if ([input isShiftHold] || !Preferences.chooseCandidateUsingSpace) { if (_builder->cursorIndex() >= _builder->length()) { - if ([state isKindOfClass:[InputStateNotEmpty class]]) { - NSString *composingBuffer = [(InputStateNotEmpty *)state composingBuffer]; - if ([composingBuffer length]) { - InputStateCommitting *committing = [[InputStateCommitting alloc] initWithPoppedText:composingBuffer]; - stateCallback(committing); - } + NSString *composingBuffer = [(InputStateNotEmpty*) state composingBuffer]; + if (composingBuffer.length) { + InputStateCommitting *committing = [[InputStateCommitting alloc] initWithPoppedText:composingBuffer]; + stateCallback (committing); } [self clear]; InputStateCommitting *committing = [[InputStateCommitting alloc] initWithPoppedText:@" "]; @@ -548,7 +546,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // other platforms if (_bpmfReadingBuffer->isEmpty()) { - // no nee to beep since the event is deliberately triggered by user + // no need to beep since the event is deliberately triggered by user if (![state isKindOfClass:[InputStateInputting class]]) { return NO; } @@ -580,6 +578,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if (currentState.cursorIndex > 0) { NSInteger previousPosition = [StringUtils previousUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:previousPosition readings:[self _currentReadings]]; + marking.tooltipForInputting = currentState.tooltip; stateCallback(marking); } else { errorCallback(); @@ -617,6 +616,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if (currentState.cursorIndex < currentState.composingBuffer.length) { NSInteger nextPosition = [StringUtils nextUtf16PositionForIndex:currentState.cursorIndex in:currentState.composingBuffer]; InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:currentState.composingBuffer cursorIndex:currentState.cursorIndex markerIndex:nextPosition readings:[self _currentReadings]]; + marking.tooltipForInputting = currentState.tooltip; stateCallback(marking); } else { errorCallback(); @@ -844,6 +844,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if (index > 0) { index = [StringUtils previousUtf16PositionForIndex:index in:state.composingBuffer]; InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; + marking.tooltipForInputting = state.tooltipForInputting; stateCallback(marking); } else { errorCallback(); @@ -859,6 +860,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot if (index < state.composingBuffer.length) { index = [StringUtils nextUtf16PositionForIndex:index in:state.composingBuffer]; InputStateMarking *marking = [[InputStateMarking alloc] initWithComposingBuffer:state.composingBuffer cursorIndex:state.cursorIndex markerIndex:index readings:state.readings]; + marking.tooltipForInputting = state.tooltipForInputting; stateCallback(marking); } else { errorCallback(); @@ -1131,7 +1133,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot size_t readingCursorIndex = 0; size_t builderCursorIndex = _builder->cursorIndex(); - NSMutableArray *phrases = [[NSMutableArray alloc] init]; + NSString *tooltip = @""; // we must do some Unicode codepoint counting to find the actual cursor location for the client // i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars @@ -1145,10 +1147,6 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSString *valueString = [NSString stringWithUTF8String:nodeStr.c_str()]; [composingBuffer appendString:valueString]; - NSString *readingString = [NSString stringWithUTF8String:(*wi).node->currentKeyValue().key.c_str()]; - InputPhrase *phrase = [[InputPhrase alloc] initWithText:valueString reading:readingString]; - [phrases addObject:phrase]; - // this re-aligns the cursor index in the composed string // (the actual cursor on the screen) with the builder's logical // cursor (reading) cursor; each built node has a "spanning length" @@ -1160,9 +1158,30 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot composedStringCursorIndex += [valueString length]; readingCursorIndex += spanningLength; } else { - for (size_t i = 0; i < codepointCount && readingCursorIndex < builderCursorIndex; i++) { - composedStringCursorIndex += [[NSString stringWithUTF8String:codepoints[i].c_str()] length]; - readingCursorIndex++; + if (codepointCount == spanningLength) { + for (size_t i = 0; i < codepointCount && readingCursorIndex < builderCursorIndex; i++) { + composedStringCursorIndex += [[NSString stringWithUTF8String:codepoints[i].c_str()] length]; + readingCursorIndex++; + } + } else { + if (readingCursorIndex < builderCursorIndex) { + composedStringCursorIndex += [valueString length]; + readingCursorIndex += spanningLength; + if (readingCursorIndex > builderCursorIndex) { + readingCursorIndex = builderCursorIndex; + } + if (builderCursorIndex == 0) { + tooltip = [NSString stringWithFormat:NSLocalizedString(@"Cursor is before \"%@\".", @""), + [NSString stringWithUTF8String:_builder->readings()[builderCursorIndex].c_str()]]; + } else if (builderCursorIndex >= _builder->readings().size()) { + tooltip = [NSString stringWithFormat:NSLocalizedString(@"Cursor is after \"%@\".", @""), + [NSString stringWithUTF8String:_builder->readings()[_builder->readings().size() - 1].c_str()]]; + } else { + tooltip = [NSString stringWithFormat:NSLocalizedString(@"Cursor is between \"%@\" and \"%@\".", @""), + [NSString stringWithUTF8String:_builder->readings()[builderCursorIndex - 1].c_str()], + [NSString stringWithUTF8String:_builder->readings()[builderCursorIndex].c_str()]]; + } + } } } } @@ -1178,6 +1197,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot NSInteger cursorIndex = composedStringCursorIndex + [reading length]; InputStateInputting *newState = [[InputStateInputting alloc] initWithComposingBuffer:composedText cursorIndex:cursorIndex]; + newState.tooltip = tooltip; return newState; } diff --git a/Source/StringUtils.swift b/Source/StringUtils.swift new file mode 100644 index 00000000..db5067a8 --- /dev/null +++ b/Source/StringUtils.swift @@ -0,0 +1,73 @@ +// Copyright (c) 2022 and onwards The McBopomofo Authors. +// +// 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 Foundation + +/// Utilities to convert the length of an NSString and a Swift string. +class StringUtils: NSObject { + + /// Converts the index in an NSString to the index in a Swift string. + /// + /// An Emoji might be compose by more than one UTF-16 code points, however + /// the length of an NSString is only the sum of the UTF-16 code points. It + /// causes that the NSString and Swift string representation of the same + /// string have different lengths once the string contains such Emoji. The + /// method helps to find the index in a Swift string by passing the index + /// in an NSString. + static func convertToCharIndex(from utf16Index: Int, in string: String) -> Int { + var length = 0 + for (i, character) in string.enumerated() { + if length >= utf16Index { + return i + } + length += character.utf16.count + } + return string.count + } + + @objc (nextUtf16PositionForIndex:in:) + static func nextUtf16Position(for index: Int, in string: String) -> Int { + var index = convertToCharIndex(from: index, in: string) + if index < string.count { + index += 1 + } + let count = string[.. Int { + var index = convertToCharIndex(from: index, in: string) + if index > 0 { + index -= 1 + } + let count = string[.. Date: Wed, 2 Feb 2022 01:31:53 +0800 Subject: [PATCH 7/9] Prevents the key handler to see reserved keys like page up/down as BPMF keys. --- Source/InputMethodController.swift | 3 +++ Source/KeyHandler.mm | 4 +++- Source/KeyHandlerInput.swift | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Source/InputMethodController.swift b/Source/InputMethodController.swift index d8d3591a..cd8c108c 100644 --- a/Source/InputMethodController.swift +++ b/Source/InputMethodController.swift @@ -180,6 +180,9 @@ class McBopomofoInputMethodController: IMKInputController { } let input = KeyHandlerInput(event: event, isVerticalMode: useVerticalMode) + + NSLog("input \(input)") + let result = keyHandler.handle(input, state: state) { newState in self.handle(state: newState, client: client) } candidateSelectionCallback: { diff --git a/Source/KeyHandler.mm b/Source/KeyHandler.mm index 3bd713d1..569eca31 100644 --- a/Source/KeyHandler.mm +++ b/Source/KeyHandler.mm @@ -302,10 +302,12 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot } bool composeReading = false; + BOOL skipBpmfHandling = [input isReservedKey] || [input isControlHold]; // MARK: Handle BPMF Keys + // see if it's valid BPMF reading - if (![input isControlHold] && _bpmfReadingBuffer->isValidKey((char) charCode)) { + if (!skipBpmfHandling && _bpmfReadingBuffer->isValidKey((char) charCode)) { _bpmfReadingBuffer->combineKey((char) charCode); // if we have a tone marker, we have to insert the reading to the diff --git a/Source/KeyHandlerInput.swift b/Source/KeyHandlerInput.swift index ad8c5bb9..2e4fbfa7 100644 --- a/Source/KeyHandlerInput.swift +++ b/Source/KeyHandlerInput.swift @@ -90,6 +90,10 @@ class KeyHandlerInput: NSObject { super.init() } + override var description: String { + return "<\(super.description) inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), absorbedArrowKey:\(absorbedArrowKey), verticalModeOnlyChooseCandidateKey:\(verticalModeOnlyChooseCandidateKey), emacsKey:\(emacsKey), useVerticalMode:\(useVerticalMode)>" + } + @objc var isShiftHold: Bool { flags.contains([.shift]) } @@ -118,6 +122,13 @@ class KeyHandlerInput: NSObject { flags.contains([.numericPad]) } + @objc var isReservedKey: Bool { + guard let code = KeyCode(rawValue: keyCode) else { + return false + } + return code.rawValue != KeyCode.none.rawValue + } + @objc var isEnter: Bool { KeyCode(rawValue: keyCode) == KeyCode.enter } From bd291be340e93653ceb9f1c7c23a4b4fd537cc42 Mon Sep 17 00:00:00 2001 From: zonble Date: Wed, 2 Feb 2022 01:33:22 +0800 Subject: [PATCH 8/9] Removes a log. --- Source/InputMethodController.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/InputMethodController.swift b/Source/InputMethodController.swift index cd8c108c..f236fb5a 100644 --- a/Source/InputMethodController.swift +++ b/Source/InputMethodController.swift @@ -181,8 +181,6 @@ class McBopomofoInputMethodController: IMKInputController { let input = KeyHandlerInput(event: event, isVerticalMode: useVerticalMode) - NSLog("input \(input)") - let result = keyHandler.handle(input, state: state) { newState in self.handle(state: newState, client: client) } candidateSelectionCallback: { From 118a1243de910e5ab55699ac7d7c6510a7573e53 Mon Sep 17 00:00:00 2001 From: zonble Date: Wed, 2 Feb 2022 06:02:44 +0800 Subject: [PATCH 9/9] Removes unused lines. --- Source/Installer/en.lproj/Localizable.strings | 3 --- Source/Installer/zh-Hant.lproj/Localizable.strings | 2 -- Source/Preferences.swift | 1 - 3 files changed, 6 deletions(-) diff --git a/Source/Installer/en.lproj/Localizable.strings b/Source/Installer/en.lproj/Localizable.strings index 140c2a4f..b3f28f52 100644 --- a/Source/Installer/en.lproj/Localizable.strings +++ b/Source/Installer/en.lproj/Localizable.strings @@ -37,6 +37,3 @@ "Warning" = "Warning"; "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources." = "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources."; "Continue" = "Continue"; - -"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; -"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; \ No newline at end of file diff --git a/Source/Installer/zh-Hant.lproj/Localizable.strings b/Source/Installer/zh-Hant.lproj/Localizable.strings index 542a94a0..5a492caa 100644 --- a/Source/Installer/zh-Hant.lproj/Localizable.strings +++ b/Source/Installer/zh-Hant.lproj/Localizable.strings @@ -37,5 +37,3 @@ "Warning" = "安裝不完整"; "Input method may not be fully enabled. Please enable it through System Preferences > Keyboard > Input Sources." = "輸入法已經安裝好,但可能沒有完全啟用。請從「系統偏好設定」 > 「鍵盤」 > 「輸入方式」分頁加入輸入法。"; "Continue" = "繼續"; - -"Model based Chinese conversion is on. Not suggested to add phrase in the mode." = "Model based Chinese conversion is on. Not suggested to add phrase in the mode."; \ No newline at end of file diff --git a/Source/Preferences.swift b/Source/Preferences.swift index 287f7dba..dd332818 100644 --- a/Source/Preferences.swift +++ b/Source/Preferences.swift @@ -219,7 +219,6 @@ class Preferences: NSObject { defaults.removeObject(forKey: kChineseConversionEngineKey) defaults.removeObject(forKey: kChineseConversionStyle) defaults.removeObject(forKey: kAssociatedPhrasesEnabledKey) -// defaults.removeObject(forKey: kAssociatedPhrasesKeys) } @UserDefault(key: kKeyboardLayoutPreferenceKey, defaultValue: 0)