From 3d178e1426a15b7d3b5e59c60452e0e6eb44687a Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 16 Jan 2019 05:52:06 +0900 Subject: [PATCH] Refactoring --- Gureum.xcodeproj/project.pbxproj | 74 +++--- GureumTests/GureumTests.swift | 4 +- GureumTests/MockApp.swift | 10 +- OSX/CIMInputController.m | 2 +- OSX/GureumAppDelegate.swift | 7 +- ...ontrollerGureum.swift => GureumMenu.swift} | 4 +- OSX/Info.plist | 4 +- OSX/MainMenu.xib | 18 +- ...MBaseComposer.swift => BaseComposer.swift} | 10 +- ...MComposer.swift => ComposerDelegate.swift} | 31 +-- OSXCore/Configuration.swift | 213 +++++++++++++++++ {OSX => OSXCore}/EmoticonComposer.swift | 28 +-- ....swift => GureumApplicationDelegate.swift} | 4 +- {OSX => OSXCore}/GureumComposer.swift | 157 ++++++------- OSXCore/GureumConfiguration.swift | 213 ----------------- {OSX => OSXCore}/HangulComposer.swift | 34 +-- {OSX => OSXCore}/HanjaComposer.swift | 26 +-- ...Controller.swift => InputController.swift} | 156 +++++++------ OSXCore/InputMethodServer.swift | 91 +------- ...nputReceiver.swift => InputReceiver.swift} | 218 +++++++++++++----- OSXCore/OSXCore.h | 1 + {OSX => OSXCore}/RomanComposer.swift | 16 +- OSXTestApp/TestViewController.swift | 4 +- Preferences/Preferences.swift | 4 +- 24 files changed, 667 insertions(+), 662 deletions(-) rename OSX/{CIMInputControllerGureum.swift => GureumMenu.swift} (97%) rename OSXCore/{CIMBaseComposer.swift => BaseComposer.swift} (63%) rename OSXCore/{CIMComposer.swift => ComposerDelegate.swift} (65%) create mode 100644 OSXCore/Configuration.swift rename {OSX => OSXCore}/EmoticonComposer.swift (86%) rename OSXCore/{CIMApplicationDelegate.swift => GureumApplicationDelegate.swift} (67%) rename {OSX => OSXCore}/GureumComposer.swift (67%) delete mode 100644 OSXCore/GureumConfiguration.swift rename {OSX => OSXCore}/HangulComposer.swift (81%) rename {OSX => OSXCore}/HanjaComposer.swift (90%) rename OSXCore/{CIMInputController.swift => InputController.swift} (64%) rename OSXCore/{CIMInputReceiver.swift => InputReceiver.swift} (50%) rename {OSX => OSXCore}/RomanComposer.swift (86%) diff --git a/Gureum.xcodeproj/project.pbxproj b/Gureum.xcodeproj/project.pbxproj index 77ffb316..e7999f44 100644 --- a/Gureum.xcodeproj/project.pbxproj +++ b/Gureum.xcodeproj/project.pbxproj @@ -65,13 +65,13 @@ 3881867E21EB2D7D004B7FDB /* OSXCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3881867C21EB2D7D004B7FDB /* OSXCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3881868121EB2D7D004B7FDB /* GureumCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3881867A21EB2D7D004B7FDB /* GureumCore.framework */; }; 3881868221EB2D7D004B7FDB /* GureumCore.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 3881867A21EB2D7D004B7FDB /* GureumCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 3881868821EB2D8D004B7FDB /* CIMApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D234EE2141260E00595FA0 /* CIMApplicationDelegate.swift */; }; - 3881868921EB2D8D004B7FDB /* CIMInputReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AC8D49217C4EDA00E4B332 /* CIMInputReceiver.swift */; }; - 3881868D21EB2D8D004B7FDB /* CIMComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389289BC217B331B00F94F2C /* CIMComposer.swift */; }; - 3881869021EB2D8D004B7FDB /* CIMBaseComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC158A0A213A847D00E4ABAC /* CIMBaseComposer.swift */; }; + 3881868821EB2D8D004B7FDB /* GureumApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D234EE2141260E00595FA0 /* GureumApplicationDelegate.swift */; }; + 3881868921EB2D8D004B7FDB /* InputReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AC8D49217C4EDA00E4B332 /* InputReceiver.swift */; }; + 3881868D21EB2D8D004B7FDB /* ComposerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 389289BC217B331B00F94F2C /* ComposerDelegate.swift */; }; + 3881869021EB2D8D004B7FDB /* BaseComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC158A0A213A847D00E4ABAC /* BaseComposer.swift */; }; 3881869121EB2DC0004B7FDB /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381DB19A21678B74005A37B9 /* Debug.swift */; }; 3881869221EB2DC0004B7FDB /* InputMethodServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 871B807B2153DDFA0013EE69 /* InputMethodServer.swift */; }; - 3881869321EB2DC0004B7FDB /* GureumConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C2AE4C208795E700FE211A /* GureumConfiguration.swift */; }; + 3881869321EB2DC0004B7FDB /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C2AE4C208795E700FE211A /* Configuration.swift */; }; 3881869721EB2E0F004B7FDB /* IOKitUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388534FC213A740600885C87 /* IOKitUtility.swift */; }; 3881869D21EB3055004B7FDB /* EmoticonComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F007652160B09700501606 /* EmoticonComposer.swift */; }; 3881869E21EB3055004B7FDB /* HangulComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38D3E42A21213E9700751191 /* HangulComposer.swift */; }; @@ -81,9 +81,9 @@ 388186A621EB3080004B7FDB /* Hangul.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 389A387D1423477F00A2ED88 /* Hangul.framework */; }; 388186A821EB34FA004B7FDB /* TISInputSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388186A721EB34FA004B7FDB /* TISInputSource.swift */; }; 388186AA21EB3D61004B7FDB /* TISInputSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 38EE977A1A00148D00AD19B8 /* TISInputSource.m */; }; - 388186AB21EB3E72004B7FDB /* GureumConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C2AE4C208795E700FE211A /* GureumConfiguration.swift */; }; + 388186AB21EB3E72004B7FDB /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C2AE4C208795E700FE211A /* Configuration.swift */; }; 388186AC21EB3FDD004B7FDB /* Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381DB19A21678B74005A37B9 /* Debug.swift */; }; - 388186B121EB453E004B7FDB /* CIMInputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BB72CC214A788100E35558 /* CIMInputController.swift */; }; + 388186B121EB453E004B7FDB /* InputController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BB72CC214A788100E35558 /* InputController.swift */; }; 388186B521EB4E98004B7FDB /* MockApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388186B321EB4E4C004B7FDB /* MockApp.swift */; }; 388186BA21EB72F7004B7FDB /* MockInputClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 3881866221EB239E004B7FDB /* MockInputClient.m */; }; 388186BF21EB9E01004B7FDB /* GureumCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3881867A21EB2D7D004B7FDB /* GureumCore.framework */; }; @@ -111,7 +111,7 @@ AA545CB9A59263E4F8A12E2B /* Pods_OSXTestApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F10FF4A81E4501FB2F71251 /* Pods_OSXTestApp.framework */; }; C436BD92FF277CDB5B16D657 /* Pods_OSXCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DA458745AD2EA657697D90D /* Pods_OSXCore.framework */; }; E5A0EA0A2139098800D4AD69 /* GureumAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A0EA092139098800D4AD69 /* GureumAppDelegate.swift */; }; - E5ED2DE4213030B700BD9B13 /* CIMInputControllerGureum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5ED2DE3213030B700BD9B13 /* CIMInputControllerGureum.swift */; }; + E5ED2DE4213030B700BD9B13 /* GureumMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5ED2DE3213030B700BD9B13 /* GureumMenu.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -270,7 +270,7 @@ 236921790DBF83DCE3CCBB11 /* Pods-OSXTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OSXTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OSXTests/Pods-OSXTests.release.xcconfig"; sourceTree = ""; }; 32842EC3B8B92F2C1EFA62F6 /* Pods-OSXTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OSXTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OSXTests/Pods-OSXTests.debug.xcconfig"; sourceTree = ""; }; 38098F09215A3DB30001F159 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 38162D7B140F64B70077AA2D /* HanjaComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HanjaComposer.swift; path = OSX/HanjaComposer.swift; sourceTree = SOURCE_ROOT; }; + 38162D7B140F64B70077AA2D /* HanjaComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HanjaComposer.swift; sourceTree = ""; }; 38162DF3141263270077AA2D /* Gureum.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gureum.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38162DF8141263280077AA2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38162DFA141263280077AA2D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -334,18 +334,18 @@ 38863C96140E669000A8ED76 /* CIMInputController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CIMInputController.m; sourceTree = ""; }; 388E2A601469249700ADBDA5 /* Hangul.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Hangul.strings; sourceTree = ""; }; 388FD02A175EDB5500469B76 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; - 389289BC217B331B00F94F2C /* CIMComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMComposer.swift; sourceTree = ""; }; + 389289BC217B331B00F94F2C /* ComposerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerDelegate.swift; sourceTree = ""; }; 38946A6A19F4F3FD00920E09 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = System/Library/Frameworks/InputMethodKit.framework; sourceTree = SDKROOT; }; - 38AC8D49217C4EDA00E4B332 /* CIMInputReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMInputReceiver.swift; sourceTree = ""; }; + 38AC8D49217C4EDA00E4B332 /* InputReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputReceiver.swift; sourceTree = ""; }; 38BFE7FA18B45419004B2B2E /* OSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 38BFE7FF18B45419004B2B2E /* GureumTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GureumTests-Info.plist"; sourceTree = ""; }; 38BFE80118B45419004B2B2E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 38BFE80318B45419004B2B2E /* GureumObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GureumObjCTests.m; sourceTree = ""; }; 38BFE81F18B4F181004B2B2E /* NSPrefPaneBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSPrefPaneBundle.h; sourceTree = ""; }; 38C253EA19F4EE320020FE92 /* Gureum.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Gureum.entitlements; sourceTree = ""; }; - 38C2AE4C208795E700FE211A /* GureumConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GureumConfiguration.swift; sourceTree = ""; }; - 38C75C8B1F153A74004BE02A /* RomanComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RomanComposer.swift; path = OSX/RomanComposer.swift; sourceTree = SOURCE_ROOT; }; - 38D3E42A21213E9700751191 /* HangulComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HangulComposer.swift; path = OSX/HangulComposer.swift; sourceTree = SOURCE_ROOT; }; + 38C2AE4C208795E700FE211A /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; + 38C75C8B1F153A74004BE02A /* RomanComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RomanComposer.swift; sourceTree = ""; }; + 38D3E42A21213E9700751191 /* HangulComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HangulComposer.swift; sourceTree = ""; }; 38D6F1CB194003C7003F25EE /* command.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = command.png; path = Icons/command.png; sourceTree = ""; }; 38EE977519FFD65600AD19B8 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 38EE97791A00148D00AD19B8 /* TISInputSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TISInputSource.h; sourceTree = ""; }; @@ -374,17 +374,17 @@ A34E2024216ADBEC00B12476 /* emoji.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = emoji.txt; sourceTree = ""; }; A36A4358216F86780052BE12 /* emoji_ko.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = emoji_ko.txt; sourceTree = ""; }; A3BAAE062160C6970076C66D /* GureumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GureumTests.swift; sourceTree = ""; }; - A3F007652160B09700501606 /* EmoticonComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EmoticonComposer.swift; path = OSX/EmoticonComposer.swift; sourceTree = SOURCE_ROOT; }; + A3F007652160B09700501606 /* EmoticonComposer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmoticonComposer.swift; sourceTree = ""; }; A7DFB5991543F2531F28F4A5 /* Pods-OSXCore.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OSXCore.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OSXCore/Pods-OSXCore.debug.xcconfig"; sourceTree = ""; }; ACF9BC1D0ED009626F8CD0E5 /* Pods-Preferences.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Preferences.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Preferences/Pods-Preferences.debug.xcconfig"; sourceTree = ""; }; CACD7E2EF02A059110B1FF42 /* Pods_OSX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OSX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E11BC1D12A9F1C006F3A053C /* Pods-OSXTestApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OSXTestApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-OSXTestApp/Pods-OSXTestApp.release.xcconfig"; sourceTree = ""; }; - E54B8381214299EB00527218 /* GureumComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GureumComposer.swift; path = OSX/GureumComposer.swift; sourceTree = SOURCE_ROOT; }; + E54B8381214299EB00527218 /* GureumComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GureumComposer.swift; sourceTree = ""; }; E5A0EA092139098800D4AD69 /* GureumAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GureumAppDelegate.swift; sourceTree = ""; }; - E5BB72CC214A788100E35558 /* CIMInputController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMInputController.swift; sourceTree = ""; }; - E5D234EE2141260E00595FA0 /* CIMApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMApplicationDelegate.swift; sourceTree = ""; }; - E5ED2DE3213030B700BD9B13 /* CIMInputControllerGureum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMInputControllerGureum.swift; sourceTree = ""; }; - FC158A0A213A847D00E4ABAC /* CIMBaseComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMBaseComposer.swift; sourceTree = ""; }; + E5BB72CC214A788100E35558 /* InputController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputController.swift; sourceTree = ""; }; + E5D234EE2141260E00595FA0 /* GureumApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GureumApplicationDelegate.swift; sourceTree = ""; }; + E5ED2DE3213030B700BD9B13 /* GureumMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GureumMenu.swift; sourceTree = ""; }; + FC158A0A213A847D00E4ABAC /* BaseComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseComposer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -470,7 +470,7 @@ 38EE97791A00148D00AD19B8 /* TISInputSource.h */, 38EE977A1A00148D00AD19B8 /* TISInputSource.m */, 38863C96140E669000A8ED76 /* CIMInputController.m */, - E5ED2DE3213030B700BD9B13 /* CIMInputControllerGureum.swift */, + E5ED2DE3213030B700BD9B13 /* GureumMenu.swift */, E5A0EA092139098800D4AD69 /* GureumAppDelegate.swift */, 3831983B21DB744900E20D78 /* UpdateManager.swift */, 38475AEB14129BAA0062100D /* Icons */, @@ -592,17 +592,17 @@ 3881867B21EB2D7D004B7FDB /* OSXCore */ = { isa = PBXGroup; children = ( + 3881867C21EB2D7D004B7FDB /* OSXCore.h */, + 3881867D21EB2D7D004B7FDB /* Info.plist */, 388186A721EB34FA004B7FDB /* TISInputSource.swift */, - E5D234EE2141260E00595FA0 /* CIMApplicationDelegate.swift */, - 38AC8D49217C4EDA00E4B332 /* CIMInputReceiver.swift */, - E5BB72CC214A788100E35558 /* CIMInputController.swift */, - 389289BC217B331B00F94F2C /* CIMComposer.swift */, - FC158A0A213A847D00E4ABAC /* CIMBaseComposer.swift */, 381DB19A21678B74005A37B9 /* Debug.swift */, + E5D234EE2141260E00595FA0 /* GureumApplicationDelegate.swift */, + 38C2AE4C208795E700FE211A /* Configuration.swift */, + 38AC8D49217C4EDA00E4B332 /* InputReceiver.swift */, + E5BB72CC214A788100E35558 /* InputController.swift */, 871B807B2153DDFA0013EE69 /* InputMethodServer.swift */, - 38C2AE4C208795E700FE211A /* GureumConfiguration.swift */, - 3881867C21EB2D7D004B7FDB /* OSXCore.h */, - 3881867D21EB2D7D004B7FDB /* Info.plist */, + 389289BC217B331B00F94F2C /* ComposerDelegate.swift */, + FC158A0A213A847D00E4ABAC /* BaseComposer.swift */, A3F007652160B09700501606 /* EmoticonComposer.swift */, 38D3E42A21213E9700751191 /* HangulComposer.swift */, 38162D7B140F64B70077AA2D /* HanjaComposer.swift */, @@ -1228,7 +1228,7 @@ 38F85961215BD27000CD80AE /* main.swift in Sources */, 3831983C21DB744900E20D78 /* UpdateManager.swift in Sources */, 388186AC21EB3FDD004B7FDB /* Debug.swift in Sources */, - E5ED2DE4213030B700BD9B13 /* CIMInputControllerGureum.swift in Sources */, + E5ED2DE4213030B700BD9B13 /* GureumMenu.swift in Sources */, 388534FD213A740600885C87 /* IOKitUtility.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1238,7 +1238,7 @@ buildActionMask = 2147483647; files = ( 381CA2EF1FCE16EA00DDB81D /* Preferences.swift in Sources */, - 388186AB21EB3E72004B7FDB /* GureumConfiguration.swift in Sources */, + 388186AB21EB3E72004B7FDB /* Configuration.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1258,17 +1258,17 @@ files = ( 3881869221EB2DC0004B7FDB /* InputMethodServer.swift in Sources */, 3881869E21EB3055004B7FDB /* HangulComposer.swift in Sources */, - 3881868D21EB2D8D004B7FDB /* CIMComposer.swift in Sources */, + 3881868D21EB2D8D004B7FDB /* ComposerDelegate.swift in Sources */, 388186A021EB3055004B7FDB /* RomanComposer.swift in Sources */, - 3881868821EB2D8D004B7FDB /* CIMApplicationDelegate.swift in Sources */, - 3881869321EB2DC0004B7FDB /* GureumConfiguration.swift in Sources */, - 3881869021EB2D8D004B7FDB /* CIMBaseComposer.swift in Sources */, - 388186B121EB453E004B7FDB /* CIMInputController.swift in Sources */, + 3881868821EB2D8D004B7FDB /* GureumApplicationDelegate.swift in Sources */, + 3881869321EB2DC0004B7FDB /* Configuration.swift in Sources */, + 3881869021EB2D8D004B7FDB /* BaseComposer.swift in Sources */, + 388186B121EB453E004B7FDB /* InputController.swift in Sources */, 3881869F21EB3055004B7FDB /* HanjaComposer.swift in Sources */, 3881869D21EB3055004B7FDB /* EmoticonComposer.swift in Sources */, 3881869121EB2DC0004B7FDB /* Debug.swift in Sources */, 388186A121EB3055004B7FDB /* GureumComposer.swift in Sources */, - 3881868921EB2D8D004B7FDB /* CIMInputReceiver.swift in Sources */, + 3881868921EB2D8D004B7FDB /* InputReceiver.swift in Sources */, 3881869721EB2E0F004B7FDB /* IOKitUtility.swift in Sources */, 388186A821EB34FA004B7FDB /* TISInputSource.swift in Sources */, ); diff --git a/GureumTests/GureumTests.swift b/GureumTests/GureumTests.swift index 53f9650d..11d1de6a 100644 --- a/GureumTests/GureumTests.swift +++ b/GureumTests/GureumTests.swift @@ -55,7 +55,7 @@ class GureumTests: XCTestCase { for app in apps { app.client.string = "" app.controller.setValue("org.youknowone.inputmethod.Gureum.qwerty", forTag: kTextServiceInputModePropertyTag, client: app.client) - app.inputText(nil, key: -1, modifiers: NSEvent.ModifierFlags.capsLock) + app.inputFlags(NSEvent.ModifierFlags.capsLock) app.inputText(" ", key: Int(kVK_Space), modifiers: NSEvent.ModifierFlags.shift) app.inputText(" ", key: Int(kVK_Space), modifiers: NSEvent.ModifierFlags.shift) @@ -426,7 +426,7 @@ class GureumTests: XCTestCase { app.client.string = "" app.controller.setValue(GureumInputSourceIdentifier.qwerty.rawValue, forTag: kTextServiceInputModePropertyTag, client: app.client) - let composer = app.controller.composer as! GureumComposer + let composer = app.controller.receiver.composer as! GureumComposer let emoticonComposer = composer.emoticonComposer emoticonComposer.delegate = composer.delegate // roman? composer.delegate = emoticonComposer diff --git a/GureumTests/MockApp.swift b/GureumTests/MockApp.swift index 4fc83b82..75b801fc 100644 --- a/GureumTests/MockApp.swift +++ b/GureumTests/MockApp.swift @@ -10,14 +10,20 @@ import Foundation @testable import GureumCore class VirtualApp: NSObject { - let controller: CIMInputController + let controller: MockInputController let client = MockInputClient() override init() { - controller = CIMMockInputController(server: InputMethodServer.shared.server, delegate: client, client: client) + controller = MockInputController(server: InputMethodServer.shared.server, delegate: client, client: client) super.init() } + func inputFlags(_ flags: NSEvent.ModifierFlags) -> Bool { + let processed = controller.inputFlags(Int(flags.rawValue), client: client) + controller.updateComposition() + return processed + } + func inputText(_ string: String!, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags) -> Bool { let processed = controller.inputText(string, key: keyCode, modifiers: Int(flags.rawValue), client: client) controller.updateComposition() diff --git a/OSX/CIMInputController.m b/OSX/CIMInputController.m index 800c26c3..61452eb9 100644 --- a/OSX/CIMInputController.m +++ b/OSX/CIMInputController.m @@ -1,5 +1,5 @@ // -// CIMInputController.m +// InputController.m // Gureum // // Created by youknowone on 11. 8. 31.. diff --git a/OSX/GureumAppDelegate.swift b/OSX/GureumAppDelegate.swift index 432f39b3..24c474cf 100644 --- a/OSX/GureumAppDelegate.swift +++ b/OSX/GureumAppDelegate.swift @@ -34,10 +34,10 @@ class NotificationCenterDelegate: NSObject, NSUserNotificationCenterDelegate { } } -class GureumAppDelegate: NSObject, NSApplicationDelegate, CIMApplicationDelegate { +class GureumAppDelegate: NSObject, NSApplicationDelegate, GureumApplicationDelegate { @IBOutlet @objc var menu: NSMenu! - let configuration = GureumConfiguration.shared + let configuration = Configuration.shared let notificationCenterDelegate = NotificationCenterDelegate() func applicationDidFinishLaunching(_ notification: Notification) { @@ -55,8 +55,7 @@ class GureumAppDelegate: NSObject, NSApplicationDelegate, CIMApplicationDelegate Fabric.with([Crashlytics.self]) #endif - let updateManager = UpdateManager.shared - updateManager.notifyUpdateIfNeeded() + UpdateManager.shared.notifyUpdateIfNeeded() // IMKServer를 띄워야만 입력기가 동작한다 _ = InputMethodServer.shared diff --git a/OSX/CIMInputControllerGureum.swift b/OSX/GureumMenu.swift similarity index 97% rename from OSX/CIMInputControllerGureum.swift rename to OSX/GureumMenu.swift index 2e6f2d1c..8534a156 100644 --- a/OSX/CIMInputControllerGureum.swift +++ b/OSX/GureumMenu.swift @@ -1,5 +1,5 @@ // -// CIMInputControllerGureum.swift +// InputControllerGureum.swift // Gureum // // Created by KMLee on 2018. 8. 24.. @@ -10,7 +10,7 @@ import Cocoa import Foundation import GureumCore -extension CIMInputController { +extension GureumAppDelegate { @IBAction func checkRecentVersion(_: Any) { guard let info = UpdateManager.shared.requestRecentVersion() else { return diff --git a/OSX/Info.plist b/OSX/Info.plist index 7c49fc6d..3e397faa 100644 --- a/OSX/Info.plist +++ b/OSX/Info.plist @@ -442,9 +442,9 @@ InputMethodConnectionName GureumInputMethod_1_Connection InputMethodServerControllerClass - CIMInputController + GureumInputController InputMethodServerDelegateClass - CIMInputController + GureumInputController LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/OSX/MainMenu.xib b/OSX/MainMenu.xib index 288a886f..bd221378 100644 --- a/OSX/MainMenu.xib +++ b/OSX/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -17,44 +17,40 @@ - - - - - + - + - + - + - + diff --git a/OSXCore/CIMBaseComposer.swift b/OSXCore/BaseComposer.swift similarity index 63% rename from OSXCore/CIMBaseComposer.swift rename to OSXCore/BaseComposer.swift index 8d42a672..fe99ed51 100644 --- a/OSXCore/CIMBaseComposer.swift +++ b/OSXCore/BaseComposer.swift @@ -1,5 +1,5 @@ // -// CIMBaseComposer.swift +// BaseComposer.swift // Gureum // // Created by 김민주 on 2018. 9. 1.. @@ -9,7 +9,7 @@ import Cocoa import Foundation -class CIMBaseComposer { +class BaseComposer { func composedString() -> NSString { return "" } @@ -22,7 +22,7 @@ class CIMBaseComposer { return "" } - func dequeueCommitString() -> NSString { + func dequeueCommitString() -> String { return "" } @@ -36,7 +36,7 @@ class CIMBaseComposer { return nil } - func input(controller _: CIMInputController!, command _: String!, key _: Int, modifier _: NSEvent.ModifierFlags, client _: Any) -> CIMInputTextProcessResult { - return CIMInputTextProcessResult.notProcessed + func input(controller _: InputController!, command _: String!, key _: Int, modifier _: NSEvent.ModifierFlags, client _: Any) -> InputResult { + return .notProcessed } } diff --git a/OSXCore/CIMComposer.swift b/OSXCore/ComposerDelegate.swift similarity index 65% rename from OSXCore/CIMComposer.swift rename to OSXCore/ComposerDelegate.swift index af5a258a..c3ddffc4 100644 --- a/OSXCore/CIMComposer.swift +++ b/OSXCore/ComposerDelegate.swift @@ -1,5 +1,5 @@ // -// CIMComposer.swift +// DelegatedComposer.swift // OSX // // Created by Jeong YunWon on 20/10/2018. @@ -12,9 +12,9 @@ import Foundation /*! @protocol @brief 입력을 처리하는 클래스의 관한 공통 형식 - @discussion TextData형식으로 @ref IMKServerInput 을 처리할 클래스의 공통 인터페이스. CharmIM에서 입력 값을 보고 처리하는 모든 클래스는 이 프로토콜을 구현한다. + @discussion TextData형식으로 @ref IMKServerInput 을 처리할 클래스의 공통 인터페이스. 입력 값을 보고 처리하는 모든 클래스는 이 프로토콜을 구현한다. */ -protocol CIMInputTextDelegate { +protocol InputTextDelegate { /*! @method @param controller 서버에서 입력을 받은 컨트롤러 @@ -25,15 +25,15 @@ protocol CIMInputTextDelegate { @return 입력 처리 여부. YES를 반환하면 이미 처리된 입력으로 보고 NO를 반환하면 외부에서 입력을 다시 처리한다. @see IMKServerInput */ - func input(controller: CIMInputController, inputText: String?, key: Int, modifiers: NSEvent.ModifierFlags, client: Any) -> CIMInputTextProcessResult + func input(text: String?, key: Int, modifiers: NSEvent.ModifierFlags, client: Any) -> InputResult } /*! @brief 실제로 문자를 합성하는 합성기의 프로토콜 - @discussion 입력기 전체의 상태에 영향을 끼치는 처리를 마친 후 출력할 글자를 조합하기 위해 CIMComposer로 입력을 전달한다. 기본적으로 자판마다 하나씩 구현하게 된다. + @discussion 입력기 전체의 상태에 영향을 끼치는 처리를 마친 후 출력할 글자를 조합하기 위해 DelegatedComposer로 입력을 전달한다. 기본적으로 자판마다 하나씩 구현하게 된다. */ -protocol CIMComposerDelegate: CIMInputTextDelegate { +protocol ComposerDelegate: InputTextDelegate { //! @brief 입력기가 선택 됨 func composerSelected(_ sender: Any!) @@ -59,23 +59,18 @@ protocol CIMComposerDelegate: CIMInputTextDelegate { func candidateSelected(_ candidateString: NSAttributedString) //! @brief 변환 후보 문자열 변경 func candidateSelectionChanged(_ candidateString: NSAttributedString) - - func input(controller: CIMInputController, command string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client: Any) -> CIMInputTextProcessResult } /*! @brief 일반적인 합성기 구조 - @warning 이 자체로는 동작하지 않는다. 상속하여 동작을 구현하거나 @ref CIMBaseComposer 를 사용한다. + @warning 이 자체로는 동작하지 않는다. 상속하여 동작을 구현하거나 @ref BaseComposer 를 사용한다. */ -class CIMComposer: NSObject, CIMComposerDelegate { +class DelegatedComposer: ComposerDelegate { func composerSelected(_: Any!) {} - var delegate: CIMComposerDelegate! + var delegate: ComposerDelegate! var inputMode: String = "" - var server: InputMethodServer { - return InputMethodServer.shared - } var composedString: String { return delegate.composedString @@ -117,11 +112,7 @@ class CIMComposer: NSObject, CIMComposerDelegate { return delegate.candidateSelectionChanged(candidateString) } - func input(controller: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { - return delegate.input(controller: controller, inputText: string, key: keyCode, modifiers: flags, client: sender) - } - - func input(controller: CIMInputController, command string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { - return delegate.input(controller: controller, command: string, key: keyCode, modifiers: flags, client: sender) + func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> InputResult { + return delegate.input(text: string, key: keyCode, modifiers: flags, client: sender) } } diff --git a/OSXCore/Configuration.swift b/OSXCore/Configuration.swift new file mode 100644 index 00000000..dd218ef2 --- /dev/null +++ b/OSXCore/Configuration.swift @@ -0,0 +1,213 @@ +// +// Configuration.swift +// Gureum +// +// Created by Jeong YunWon on 2018. 4. 19.. +// Copyright © 2018 youknowone.org. All rights reserved. +// + +import AppKit +import Foundation + +enum ConfigurationName: String { + case lastHangulInputMode = "LastHangulInputMode" + case lastRomanInputMode = "LastRomanInputMode" + + case inputModeExchangeKey = "InputModeExchangeKey" + case inputModeEmoticonKey = "InputModeEmoticonKey" + case inputModeHanjaKey = "InputModeHanjaKey" + case inputModeEnglishKey = "InputModeEnglishKey" + case inputModeKoreanKey = "InputModeKoreanKey" + case optionKeyBehavior = "OptionKeyBehavior" + + case romanModeByEscapeKey = "ExchangeToRomanModeByEscapeKey" + case showsInputForHanjaCandidates = "ShowsInputForHanjaCandidates" + case hangulWonCurrencySymbolForBackQuote = "HangulWonCurrencySymbolForBackQuote" + case hangulAutoReorder = "HangulAutoReorder" + case hangulNonChoseongCombination = "HangulNonChoseongCombination" + case hangulForceStrictCombinationRule = "HangulForceStrictCombinationRule" +} + +public class Configuration: UserDefaults { + public static let shared = Configuration() + var enableCapslockToToggleInputMode: Bool = true + + typealias Shortcut = (UInt, NSEvent.ModifierFlags) + + class func convertShortcutToConfiguration(_ shortcut: Shortcut?) -> [String: Any] { + guard let shortcut = shortcut else { + return [:] + } + return ["modifier": shortcut.1.rawValue, "keyCode": shortcut.0] + } + + class func convertConfigurationToShortcut(_ configuration: [String: Any]) -> Shortcut? { + guard let modifier = configuration["modifier"] as? UInt, let keyCode = configuration["keyCode"] as? UInt else { + return nil + } + return (keyCode, NSEvent.ModifierFlags(rawValue: modifier)) + } + + init() { + super.init(suiteName: "org.youknowone.Gureum")! + register(defaults: [ + ConfigurationName.lastHangulInputMode.rawValue: "org.youknowone.inputmethod.Gureum.han2", + ConfigurationName.lastRomanInputMode.rawValue: "org.youknowone.inputmethod.Gureum.qwerty", + + ConfigurationName.inputModeExchangeKey.rawValue: Configuration.convertShortcutToConfiguration((0x31, .shift)), + ConfigurationName.inputModeEmoticonKey.rawValue: Configuration.convertShortcutToConfiguration((0x24, [.shift, .option])), + ConfigurationName.inputModeHanjaKey.rawValue: Configuration.convertShortcutToConfiguration((0x24, .option)), + ConfigurationName.optionKeyBehavior.rawValue: 0, + + ConfigurationName.romanModeByEscapeKey.rawValue: false, + ConfigurationName.showsInputForHanjaCandidates.rawValue: false, + ConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue: true, + ConfigurationName.hangulAutoReorder.rawValue: false, + ConfigurationName.hangulNonChoseongCombination.rawValue: false, + ConfigurationName.hangulForceStrictCombinationRule.rawValue: false, + ]) + + // 시스템 설정 읽어와서 반영한다. 여기도 observer 설정 가능한지 확인 필요 + let libraryUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]) + let globalPreferences = NSDictionary(contentsOf: URL(fileURLWithPath: "Preferences/.GlobalPreferences.plist", relativeTo: libraryUrl))! + let state: Int = (globalPreferences["TISRomanSwitchState"] as? NSNumber)?.intValue ?? 1 + enableCapslockToToggleInputMode = state > 0 + } + + func getShortcut(forKey key: String) -> Shortcut? { + guard let value = self.dictionary(forKey: key) else { + return nil + } + return Configuration.convertConfigurationToShortcut(value) + } + + func setShortcut(_ newValue: Shortcut?, forKey key: String) { + `set`(Configuration.convertShortcutToConfiguration(newValue), forKey: key) + } + + var lastHangulInputMode: String { + get { + return string(forKey: ConfigurationName.lastHangulInputMode.rawValue)! + } + set { + `set`(newValue, forKey: ConfigurationName.lastHangulInputMode.rawValue) + } + } + + var lastRomanInputMode: String { + get { + return string(forKey: ConfigurationName.lastRomanInputMode.rawValue)! + } + set { + `set`(newValue, forKey: ConfigurationName.lastRomanInputMode.rawValue) + } + } + + var optionKeyBehavior: Int { + get { + return integer(forKey: ConfigurationName.optionKeyBehavior.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.optionKeyBehavior.rawValue) + } + } + + var showsInputForHanjaCandidates: Bool { + get { + return bool(forKey: ConfigurationName.showsInputForHanjaCandidates.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.showsInputForHanjaCandidates.rawValue) + } + } + + var inputModeExchangeKey: Shortcut? { + get { + return getShortcut(forKey: ConfigurationName.inputModeExchangeKey.rawValue) + } + set { + setShortcut(newValue, forKey: ConfigurationName.inputModeExchangeKey.rawValue) + } + } + + var inputModeEmoticonKey: Shortcut? { + get { + return getShortcut(forKey: ConfigurationName.inputModeEmoticonKey.rawValue) + } + set { + setShortcut(newValue, forKey: ConfigurationName.inputModeEmoticonKey.rawValue) + } + } + + var inputModeHanjaKey: Shortcut? { + get { + return getShortcut(forKey: ConfigurationName.inputModeHanjaKey.rawValue) + } + set { + setShortcut(newValue, forKey: ConfigurationName.inputModeHanjaKey.rawValue) + } + } + + var inputModeEnglishKey: Shortcut? { + get { + return getShortcut(forKey: ConfigurationName.inputModeEnglishKey.rawValue) + } + set { + setShortcut(newValue, forKey: ConfigurationName.inputModeEnglishKey.rawValue) + } + } + + var inputModeKoreanKey: Shortcut? { + get { + return getShortcut(forKey: ConfigurationName.inputModeKoreanKey.rawValue) + } + set { + setShortcut(newValue, forKey: ConfigurationName.inputModeKoreanKey.rawValue) + } + } + + var romanModeByEscapeKey: Bool { + get { + return bool(forKey: ConfigurationName.romanModeByEscapeKey.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.romanModeByEscapeKey.rawValue) + } + } + + var hangulWonCurrencySymbolForBackQuote: Bool { + get { + return bool(forKey: ConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue) + } + } + + var hangulAutoReorder: Bool { + get { + return bool(forKey: ConfigurationName.hangulAutoReorder.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.hangulAutoReorder.rawValue) + } + } + + var hangulNonChoseongCombination: Bool { + get { + return bool(forKey: ConfigurationName.hangulNonChoseongCombination.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.hangulNonChoseongCombination.rawValue) + } + } + + var hangulForceStrictCombinationRule: Bool { + get { + return bool(forKey: ConfigurationName.hangulForceStrictCombinationRule.rawValue) + } + set { + `set`(newValue, forKey: ConfigurationName.hangulForceStrictCombinationRule.rawValue) + } + } +} diff --git a/OSX/EmoticonComposer.swift b/OSXCore/EmoticonComposer.swift similarity index 86% rename from OSX/EmoticonComposer.swift rename to OSXCore/EmoticonComposer.swift index bf9405f6..6d7a269a 100644 --- a/OSX/EmoticonComposer.swift +++ b/OSXCore/EmoticonComposer.swift @@ -12,7 +12,7 @@ import Hangul let DEBUG_EMOTICON = false -class EmoticonComposer: CIMComposer { +class EmoticonComposer: DelegatedComposer { static let emoticonTable: HGHanjaTable = HGHanjaTable(contentOfFile: Bundle(for: HGKeyboard.self).path(forResource: "emoji", ofType: "txt", inDirectory: "hanja")!)! var _candidates: [NSAttributedString]? @@ -23,7 +23,7 @@ class EmoticonComposer: CIMComposer { var _selectedCandidate: NSAttributedString? - var romanComposer: CIMComposerDelegate { + var romanComposer: ComposerDelegate { return self.delegate } @@ -142,19 +142,19 @@ class EmoticonComposer: CIMComposer { dlog(DEBUG_EMOTICON, "DEBUG 2, [updateEmoticonCandidates] MSG: %@", _candidates ?? []) } - func update(fromController controller: CIMInputController) { + func update(client sender: IMKTextInput) { dlog(DEBUG_EMOTICON, "DEBUG 1, [update] MSG: function called") - let markedRange: NSRange = controller.client().markedRange() - let selectedRange: NSRange = controller.client().selectedRange() + let markedRange: NSRange = sender.markedRange() + let selectedRange: NSRange = sender.selectedRange() let isInvalidMarkedRange: Bool = markedRange.length == 0 || markedRange.length == NSNotFound dlog(DEBUG_EMOTICON, "DEBUG 2, [update] MSG: DEBUG POINT 1") if isInvalidMarkedRange, selectedRange.length > 0 { - let selectedString: String = controller.client().attributedSubstring(from: selectedRange).string + let selectedString: String = sender.attributedSubstring(from: selectedRange).string - controller.client().setMarkedText(selectedString, selectionRange: selectedRange, replacementRange: selectedRange) - dlog(DEBUG_EMOTICON, "DEBUG 3, [update] MSG: marking: %@ / selected: %@", NSStringFromRange(controller.client().markedRange()), NSStringFromRange(controller.client().selectedRange())) + sender.setMarkedText(selectedString, selectionRange: selectedRange, replacementRange: selectedRange) + dlog(DEBUG_EMOTICON, "DEBUG 3, [update] MSG: marking: %@ / selected: %@", NSStringFromRange(sender.markedRange()), NSStringFromRange(sender.selectedRange())) _bufferedString = selectedString dlog(DEBUG_EMOTICON, "DEBUG 4, [update] MSG: %@", _bufferedString) @@ -165,9 +165,9 @@ class EmoticonComposer: CIMComposer { dlog(DEBUG_EMOTICON, "DEBUG 6, [update] MSG: after updateEmoticonCandidates") } - override func input(controller: CIMInputController, inputText string: String!, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { + override func input(text string: String!, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> InputResult { dlog(DEBUG_EMOTICON, "DEBUG 1, [inputController] MSG: %@, [[%d]]", string, keyCode) - var result: CIMInputTextProcessResult = delegate.input(controller: controller, inputText: string, key: keyCode, modifiers: flags, client: sender) + var result = delegate.input(text: string, key: keyCode, modifiers: flags, client: sender) switch keyCode { // BackSpace @@ -189,12 +189,12 @@ class EmoticonComposer: CIMComposer { _bufferedString.append(" ") result = .processed } else { - result = .notProcessedAndNeedsCommit + result = InputResult(processed: false, action: .commit) } // ESC case kVK_Escape: exitComposer() - return .notProcessedAndNeedsCommit + return InputResult(processed: false, action: .commit) // Enter case kVK_Return: candidateSelected(_selectedCandidate ?? NSAttributedString(string: composedString)) @@ -211,14 +211,14 @@ class EmoticonComposer: CIMComposer { dlog(DEBUG_EMOTICON, "DEBUG 3, [inputController] MSG: %@", string) - if result == .notProcessedAndNeedsCommit { + if result == InputResult(processed: false, action: .commit) { cancelComposition() return result } if commitString.isEmpty { return result == .processed ? .processed : .notProcessed } else { - return .notProcessedAndNeedsCommit + return InputResult(processed: false, action: .commit) } } } diff --git a/OSXCore/CIMApplicationDelegate.swift b/OSXCore/GureumApplicationDelegate.swift similarity index 67% rename from OSXCore/CIMApplicationDelegate.swift rename to OSXCore/GureumApplicationDelegate.swift index ec6f6683..518975ac 100644 --- a/OSXCore/CIMApplicationDelegate.swift +++ b/OSXCore/GureumApplicationDelegate.swift @@ -1,5 +1,5 @@ // -// CIMApplicationDelegate.swift +// GureumApplicationDelegate.swift // Gureum // // Created by KMLee on 2018. 9. 6.. @@ -8,7 +8,7 @@ import Cocoa -@objc public protocol CIMApplicationDelegate: NSObjectProtocol { +@objc public protocol GureumApplicationDelegate: NSObjectProtocol { //! @brief 언어 설정에 추가될 메뉴 @objc var menu: NSMenu! { get } } diff --git a/OSX/GureumComposer.swift b/OSXCore/GureumComposer.swift similarity index 67% rename from OSX/GureumComposer.swift rename to OSXCore/GureumComposer.swift index cfa15918..499c3909 100644 --- a/OSX/GureumComposer.swift +++ b/OSXCore/GureumComposer.swift @@ -63,15 +63,15 @@ let GureumInputSourceToHangulKeyboardIdentifierTable: [GureumInputSourceIdentifi .han3_2015: "3-2015", ] -class GureumComposer: CIMComposer { - var romanComposer: CIMComposer +class GureumComposer: DelegatedComposer { + var romanComposer: DelegatedComposer let qwertyComposer: QwertyComposer = QwertyComposer() let dvorakComposer: RomanDataComposer = RomanDataComposer(keyboardData: RomanDataComposer.dvorakData) let colemakComposer: RomanDataComposer = RomanDataComposer(keyboardData: RomanDataComposer.colemakData) let hangulComposer: HangulComposer = HangulComposer(keyboardIdentifier: "2")! let hanjaComposer: HanjaComposer = HanjaComposer() let emoticonComposer: EmoticonComposer = EmoticonComposer() - let romanComposersByIdentifier: [String: CIMComposer] + let romanComposersByIdentifier: [String: DelegatedComposer] override init() { romanComposer = qwertyComposer @@ -103,21 +103,64 @@ class GureumComposer: CIMComposer { } if romanComposersByIdentifier.keys.contains(keyboardIdentifier) { - self.romanComposer = romanComposersByIdentifier[keyboardIdentifier]! - self.delegate = self.romanComposer - GureumConfiguration.shared.lastRomanInputMode = newValue + romanComposer = romanComposersByIdentifier[keyboardIdentifier]! + delegate = romanComposer + Configuration.shared.lastRomanInputMode = newValue } else { - self.delegate = hangulComposer + delegate = hangulComposer // 단축키 지원을 위해 마지막 자판을 기억 hangulComposer.setKeyboard(identifier: keyboardIdentifier) - GureumConfiguration.shared.lastHangulInputMode = newValue + Configuration.shared.lastHangulInputMode = newValue } super.inputMode = newValue } } - override func input(controller: CIMInputController, command _: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { - let configuration = GureumConfiguration.shared + func changeLayout(_ layout: ChangeLayout, client sender: Any) -> InputResult { + var layout = layout + if layout == .toggle { + if (delegate as AnyObject) === romanComposer { + layout = .hangul + } else if (delegate as AnyObject) === hangulComposer { + layout = .roman + } else { + return .notProcessed + } + } + + if [.hangul, .roman].contains(layout) { + // 한영전환을 위해 현재 입력 중인 문자 합성 취소 + let config = Configuration.shared + delegate.cancelComposition() + inputMode = layout == .hangul ? config.lastHangulInputMode : config.lastRomanInputMode + return InputResult(processed: true, action: .layout(inputMode)) + } + + if layout == .hanja { + // 한글 입력 상태에서 한자 및 이모티콘 입력기로 전환 + if (delegate as? HangulComposer) === hangulComposer { + // 현재 조합 중 여부에 따라 한자 모드 여부를 결정 + let isComposing = hangulComposer.composedString.count > 0 + hanjaComposer.mode = isComposing ? .single : .continuous + delegate = hanjaComposer + delegate.composerSelected(self) + hanjaComposer.update(client: sender as! IMKTextInput) + } else if (delegate as? DelegatedComposer) === romanComposer { + emoticonComposer.delegate = delegate + delegate = emoticonComposer + emoticonComposer.update(client: sender as! IMKTextInput) + } else { + return .notProcessed + } + return .processed + } + + assert(false) + return .notProcessed + } + + func filterCommand(key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> InputEvent? { + let configuration = Configuration.shared let inputModifier = flags.intersection(NSEvent.ModifierFlags.deviceIndependentFlagsMask).intersection(NSEvent.ModifierFlags(rawValue: ~NSEvent.ModifierFlags.capsLock.rawValue)) var need_exchange = false var need_candidtes = false @@ -168,59 +211,20 @@ class GureumComposer: CIMComposer { // { // Handle SpecialKeyCode first - switch keyCode { - case CIMInputControllerSpecialKeyCode.capsLockPressed.rawValue: - guard configuration.enableCapslockToToggleInputMode else { - return CIMInputTextProcessResult.processed - } - if (delegate as? CIMComposer) === romanComposer || (delegate as? HangulComposer) === hangulComposer { - need_exchange = true - } - if !need_exchange { - return CIMInputTextProcessResult.processed - } - - case CIMInputControllerSpecialKeyCode.capsLockFlagsChanged.rawValue: - guard configuration.enableCapslockToToggleInputMode else { - return CIMInputTextProcessResult.processed - } - - return CIMInputTextProcessResult.processed - default: - let inputKey = (UInt(keyCode), inputModifier) - if let shortcutKey = configuration.inputModeExchangeKey, shortcutKey == inputKey { - need_exchange = true - } - // else if (self.delegate == self->hangulComposer && inputModifier == CIMSharedConfiguration->inputModeEnglishKeyModifier && keyCode == CIMSharedConfiguration->inputModeEnglishKeyCode) { - // dlog(DEBUG_SHORTCUT, @"**** Layout exchange by change to english shortcut ****"); - // need_exchange = YES; - // } - // else if (self.delegate == self->romanComposer && inputModifier == CIMSharedConfiguration->inputModeKoreanKeyModifier && keyCode == CIMSharedConfiguration->inputModeKoreanKeyCode) { - // dlog(DEBUG_SHORTCUT, @"**** Layout exchange by change to korean shortcut ****"); - // need_exchange = YES; - // } - if let shortcutKey = configuration.inputModeHanjaKey, shortcutKey == inputKey { - need_candidtes = true - } + let inputKey = (UInt(keyCode), inputModifier) + if let shortcutKey = configuration.inputModeExchangeKey, shortcutKey == inputKey { + return .changeLayout(.toggle) } - - if need_exchange { - // 한영전환을 위해 현재 입력 중인 문자 합성 취소 - delegate.cancelComposition() - if (delegate as? CIMComposer) === romanComposer { - let lastHangulInputMode = GureumConfiguration.shared.lastHangulInputMode - if let sender = sender as? IMKTextInput { - // inputMode = lastHangulInputMode - sender.selectMode(lastHangulInputMode) - } - } else { - let lastRomanInputMode = GureumConfiguration.shared.lastRomanInputMode - if let sender = sender as? IMKTextInput { - // inputMode = lastRomanInputMode - sender.selectMode(lastRomanInputMode) - } - } - return CIMInputTextProcessResult.processed + // else if (self.delegate == self->hangulComposer && inputModifier == CIMSharedConfiguration->inputModeEnglishKeyModifier && keyCode == CIMSharedConfiguration->inputModeEnglishKeyCode) { + // dlog(DEBUG_SHORTCUT, @"**** Layout exchange by change to english shortcut ****"); + // need_exchange = YES; + // } + // else if (self.delegate == self->romanComposer && inputModifier == CIMSharedConfiguration->inputModeKoreanKeyModifier && keyCode == CIMSharedConfiguration->inputModeKoreanKeyCode) { + // dlog(DEBUG_SHORTCUT, @"**** Layout exchange by change to korean shortcut ****"); + // need_exchange = YES; + // } + if let shortcutKey = configuration.inputModeHanjaKey, shortcutKey == inputKey { + return .changeLayout(.hanja) } if (delegate as? HanjaComposer) === hanjaComposer { @@ -237,36 +241,15 @@ class GureumComposer: CIMComposer { } } - if need_candidtes { - // 한글 입력 상태에서 한자 및 이모티콘 입력기로 전환 - if (delegate as? HangulComposer) === hangulComposer { - // 현재 조합 중 여부에 따라 한자 모드 여부를 결정 - let isComposing = hangulComposer.composedString.count > 0 - hanjaComposer.mode = isComposing ? .single : .continuous - delegate = hanjaComposer - delegate.composerSelected(self) - hanjaComposer.update(fromController: controller) - return CIMInputTextProcessResult.processed - } - // 영어 입력 상태에서 이모티콘 입력기로 전환 - if (delegate as? CIMComposer) === romanComposer { - emoticonComposer.delegate = delegate - delegate = emoticonComposer - emoticonComposer.update(fromController: controller) - return CIMInputTextProcessResult.processed - } - } - if (delegate as? HangulComposer) === hangulComposer { // Vi-mode: esc로 로마자 키보드로 전환 - if GureumConfiguration.shared.romanModeByEscapeKey { + if Configuration.shared.romanModeByEscapeKey { if keyCode == kVK_Escape || (keyCode, inputModifier) == (kVK_ANSI_LeftBracket, NSEvent.ModifierFlags.control) { - delegate.cancelComposition() - (sender as AnyObject).selectMode(GureumConfiguration.shared.lastRomanInputMode) - return CIMInputTextProcessResult.notProcessedAndNeedsCommit + return .changeLayout(.roman) } } } - return CIMInputTextProcessResult.notProcessed + + return nil } } diff --git a/OSXCore/GureumConfiguration.swift b/OSXCore/GureumConfiguration.swift deleted file mode 100644 index 45e9ddeb..00000000 --- a/OSXCore/GureumConfiguration.swift +++ /dev/null @@ -1,213 +0,0 @@ -// -// GureumConfiguration.swift -// Gureum -// -// Created by Jeong YunWon on 2018. 4. 19.. -// Copyright © 2018 youknowone.org. All rights reserved. -// - -import AppKit -import Foundation - -enum GureumConfigurationName: String { - case lastHangulInputMode = "LastHangulInputMode" - case lastRomanInputMode = "LastRomanInputMode" - - case inputModeExchangeKey = "InputModeExchangeKey" - case inputModeEmoticonKey = "InputModeEmoticonKey" - case inputModeHanjaKey = "InputModeHanjaKey" - case inputModeEnglishKey = "InputModeEnglishKey" - case inputModeKoreanKey = "InputModeKoreanKey" - case optionKeyBehavior = "OptionKeyBehavior" - - case romanModeByEscapeKey = "ExchangeToRomanModeByEscapeKey" - case showsInputForHanjaCandidates = "ShowsInputForHanjaCandidates" - case hangulWonCurrencySymbolForBackQuote = "HangulWonCurrencySymbolForBackQuote" - case hangulAutoReorder = "HangulAutoReorder" - case hangulNonChoseongCombination = "HangulNonChoseongCombination" - case hangulForceStrictCombinationRule = "HangulForceStrictCombinationRule" -} - -public class GureumConfiguration: UserDefaults { - public static let shared = GureumConfiguration() - var enableCapslockToToggleInputMode: Bool = true - - typealias Shortcut = (UInt, NSEvent.ModifierFlags) - - class func convertShortcutToConfiguration(_ shortcut: Shortcut?) -> [String: Any] { - guard let shortcut = shortcut else { - return [:] - } - return ["modifier": shortcut.1.rawValue, "keyCode": shortcut.0] - } - - class func convertConfigurationToShortcut(_ configuration: [String: Any]) -> Shortcut? { - guard let modifier = configuration["modifier"] as? UInt, let keyCode = configuration["keyCode"] as? UInt else { - return nil - } - return (keyCode, NSEvent.ModifierFlags(rawValue: modifier)) - } - - init() { - super.init(suiteName: "org.youknowone.Gureum")! - register(defaults: [ - GureumConfigurationName.lastHangulInputMode.rawValue: "org.youknowone.inputmethod.Gureum.han2", - GureumConfigurationName.lastRomanInputMode.rawValue: "org.youknowone.inputmethod.Gureum.qwerty", - - GureumConfigurationName.inputModeExchangeKey.rawValue: GureumConfiguration.convertShortcutToConfiguration((0x31, .shift)), - GureumConfigurationName.inputModeEmoticonKey.rawValue: GureumConfiguration.convertShortcutToConfiguration((0x24, [.shift, .option])), - GureumConfigurationName.inputModeHanjaKey.rawValue: GureumConfiguration.convertShortcutToConfiguration((0x24, .option)), - GureumConfigurationName.optionKeyBehavior.rawValue: 0, - - GureumConfigurationName.romanModeByEscapeKey.rawValue: false, - GureumConfigurationName.showsInputForHanjaCandidates.rawValue: false, - GureumConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue: true, - GureumConfigurationName.hangulAutoReorder.rawValue: false, - GureumConfigurationName.hangulNonChoseongCombination.rawValue: false, - GureumConfigurationName.hangulForceStrictCombinationRule.rawValue: false, - ]) - - // 시스템 설정 읽어와서 반영한다. 여기도 observer 설정 가능한지 확인 필요 - let libraryUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0]) - let globalPreferences = NSDictionary(contentsOf: URL(fileURLWithPath: "Preferences/.GlobalPreferences.plist", relativeTo: libraryUrl))! - let state: Int = (globalPreferences["TISRomanSwitchState"] as? NSNumber)?.intValue ?? 1 - enableCapslockToToggleInputMode = state > 0 - } - - func getShortcut(forKey key: String) -> Shortcut? { - guard let value = self.dictionary(forKey: key) else { - return nil - } - return GureumConfiguration.convertConfigurationToShortcut(value) - } - - func setShortcut(_ newValue: Shortcut?, forKey key: String) { - `set`(GureumConfiguration.convertShortcutToConfiguration(newValue), forKey: key) - } - - var lastHangulInputMode: String { - get { - return string(forKey: GureumConfigurationName.lastHangulInputMode.rawValue)! - } - set { - `set`(newValue, forKey: GureumConfigurationName.lastHangulInputMode.rawValue) - } - } - - var lastRomanInputMode: String { - get { - return string(forKey: GureumConfigurationName.lastRomanInputMode.rawValue)! - } - set { - `set`(newValue, forKey: GureumConfigurationName.lastRomanInputMode.rawValue) - } - } - - var optionKeyBehavior: Int { - get { - return integer(forKey: GureumConfigurationName.optionKeyBehavior.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.optionKeyBehavior.rawValue) - } - } - - var showsInputForHanjaCandidates: Bool { - get { - return bool(forKey: GureumConfigurationName.showsInputForHanjaCandidates.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.showsInputForHanjaCandidates.rawValue) - } - } - - var inputModeExchangeKey: Shortcut? { - get { - return getShortcut(forKey: GureumConfigurationName.inputModeExchangeKey.rawValue) - } - set { - setShortcut(newValue, forKey: GureumConfigurationName.inputModeExchangeKey.rawValue) - } - } - - var inputModeEmoticonKey: Shortcut? { - get { - return getShortcut(forKey: GureumConfigurationName.inputModeEmoticonKey.rawValue) - } - set { - setShortcut(newValue, forKey: GureumConfigurationName.inputModeEmoticonKey.rawValue) - } - } - - var inputModeHanjaKey: Shortcut? { - get { - return getShortcut(forKey: GureumConfigurationName.inputModeHanjaKey.rawValue) - } - set { - setShortcut(newValue, forKey: GureumConfigurationName.inputModeHanjaKey.rawValue) - } - } - - var inputModeEnglishKey: Shortcut? { - get { - return getShortcut(forKey: GureumConfigurationName.inputModeEnglishKey.rawValue) - } - set { - setShortcut(newValue, forKey: GureumConfigurationName.inputModeEnglishKey.rawValue) - } - } - - var inputModeKoreanKey: Shortcut? { - get { - return getShortcut(forKey: GureumConfigurationName.inputModeKoreanKey.rawValue) - } - set { - setShortcut(newValue, forKey: GureumConfigurationName.inputModeKoreanKey.rawValue) - } - } - - var romanModeByEscapeKey: Bool { - get { - return bool(forKey: GureumConfigurationName.romanModeByEscapeKey.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.romanModeByEscapeKey.rawValue) - } - } - - var hangulWonCurrencySymbolForBackQuote: Bool { - get { - return bool(forKey: GureumConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.hangulWonCurrencySymbolForBackQuote.rawValue) - } - } - - var hangulAutoReorder: Bool { - get { - return bool(forKey: GureumConfigurationName.hangulAutoReorder.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.hangulAutoReorder.rawValue) - } - } - - var hangulNonChoseongCombination: Bool { - get { - return bool(forKey: GureumConfigurationName.hangulNonChoseongCombination.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.hangulNonChoseongCombination.rawValue) - } - } - - var hangulForceStrictCombinationRule: Bool { - get { - return bool(forKey: GureumConfigurationName.hangulForceStrictCombinationRule.rawValue) - } - set { - `set`(newValue, forKey: GureumConfigurationName.hangulForceStrictCombinationRule.rawValue) - } - } -} diff --git a/OSX/HangulComposer.swift b/OSXCore/HangulComposer.swift similarity index 81% rename from OSX/HangulComposer.swift rename to OSXCore/HangulComposer.swift index b7523682..c8475bb1 100644 --- a/OSX/HangulComposer.swift +++ b/OSXCore/HangulComposer.swift @@ -36,7 +36,7 @@ class HangulComposerCombination { @coclass HGInputContext */ -class HangulComposer: NSObject, CIMComposerDelegate { +class HangulComposer: NSObject, ComposerDelegate { func composerSelected(_: Any!) {} var candidates: [NSAttributedString]? { @@ -45,7 +45,7 @@ class HangulComposer: NSObject, CIMComposerDelegate { let inputContext: HGInputContext var _commitString: String - let configuration = GureumConfiguration.shared + let configuration = Configuration.shared init?(keyboardIdentifier: String) { _commitString = String() @@ -56,13 +56,13 @@ class HangulComposer: NSObject, CIMComposerDelegate { self.inputContext.setOption(HANGUL_IC_OPTION_AUTO_REORDER, value: configuration.hangulAutoReorder) self.inputContext.setOption(HANGUL_IC_OPTION_NON_CHOSEONG_COMBI, value: configuration.hangulNonChoseongCombination) super.init() - configuration.addObserver(self, forKeyPath: GureumConfigurationName.hangulAutoReorder.rawValue, options: NSKeyValueObservingOptions.new, context: nil) - configuration.addObserver(self, forKeyPath: GureumConfigurationName.hangulNonChoseongCombination.rawValue, options: NSKeyValueObservingOptions.new, context: nil) - configuration.addObserver(self, forKeyPath: GureumConfigurationName.hangulForceStrictCombinationRule.rawValue, options: NSKeyValueObservingOptions.new, context: nil) + configuration.addObserver(self, forKeyPath: ConfigurationName.hangulAutoReorder.rawValue, options: NSKeyValueObservingOptions.new, context: nil) + configuration.addObserver(self, forKeyPath: ConfigurationName.hangulNonChoseongCombination.rawValue, options: NSKeyValueObservingOptions.new, context: nil) + configuration.addObserver(self, forKeyPath: ConfigurationName.hangulForceStrictCombinationRule.rawValue, options: NSKeyValueObservingOptions.new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) { - if keyPath == GureumConfigurationName.hangulForceStrictCombinationRule.rawValue { + if keyPath == ConfigurationName.hangulForceStrictCombinationRule.rawValue { let keyboard = GureumInputSourceIdentifier(rawValue: configuration.lastHangulInputMode)?.keyboardIdentifier ?? "2" setKeyboard(identifier: keyboard) } else { @@ -72,9 +72,9 @@ class HangulComposer: NSObject, CIMComposerDelegate { } deinit { - configuration.removeObserver(self, forKeyPath: GureumConfigurationName.hangulAutoReorder.rawValue) - configuration.removeObserver(self, forKeyPath: GureumConfigurationName.hangulNonChoseongCombination.rawValue) - configuration.removeObserver(self, forKeyPath: GureumConfigurationName.hangulForceStrictCombinationRule.rawValue) + configuration.removeObserver(self, forKeyPath: ConfigurationName.hangulAutoReorder.rawValue) + configuration.removeObserver(self, forKeyPath: ConfigurationName.hangulNonChoseongCombination.rawValue) + configuration.removeObserver(self, forKeyPath: ConfigurationName.hangulForceStrictCombinationRule.rawValue) } /*! @@ -94,17 +94,17 @@ class HangulComposer: NSObject, CIMComposerDelegate { return _commitString } - // CIMComposerDelegate + // ComposerDelegate - func input(controller _: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> CIMInputTextProcessResult { + func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> InputResult { // libhangul은 backspace를 키로 받지 않고 별도로 처리한다. if keyCode == kVK_Delete { - return inputContext.backspace() ? CIMInputTextProcessResult.processed : CIMInputTextProcessResult.notProcessed + return inputContext.backspace() ? .processed : .notProcessed } if keyCode > 50 || keyCode == kVK_Delete || keyCode == kVK_Return || keyCode == kVK_Tab || keyCode == kVK_Space { dlog(DEBUG_HANGULCOMPOSER, " ** ESCAPE from outbound keyCode: %lu", keyCode) - return CIMInputTextProcessResult.notProcessedAndNeedsCommit + return InputResult(processed: false, action: .commit) } var string = string! @@ -120,19 +120,19 @@ class HangulComposer: NSObject, CIMComposerDelegate { if configuration.hangulWonCurrencySymbolForBackQuote, keyCode == kVK_ANSI_Grave, flags.isSubset(of: .capsLock) { if !handled { _commitString += recentCommitString + "₩" - return CIMInputTextProcessResult.processed + return .processed } else if recentCommitString.last! == "`" { _commitString += recentCommitString.dropLast() + "₩" - return CIMInputTextProcessResult.processed + return .processed } } _commitString += recentCommitString // dlog(DEBUG_HANGULCOMPOSER, @"HangulComposer -inputText: string %@ (%@ added)", self->_commitString, recentCommitString) - return handled ? .processed : .notProcessedAndNeedsCancel + return handled ? .processed : InputResult(processed: false, action: .cancel) } - func input(controller _: CIMInputController, command _: String?, key _: Int, modifiers _: NSEvent.ModifierFlags, client _: Any) -> CIMInputTextProcessResult { + func input(controller _: InputController, command _: String?, key _: Int, modifiers _: NSEvent.ModifierFlags, client _: Any) -> InputResult { assert(false) return .notProcessed } diff --git a/OSX/HanjaComposer.swift b/OSXCore/HanjaComposer.swift similarity index 90% rename from OSX/HanjaComposer.swift rename to OSXCore/HanjaComposer.swift index 82f5e460..f8730132 100644 --- a/OSX/HanjaComposer.swift +++ b/OSXCore/HanjaComposer.swift @@ -25,7 +25,7 @@ enum HanjaMode { private let hangulBundle = Bundle(for: HGKeyboard.self) -class HanjaComposer: CIMComposer { +class HanjaComposer: DelegatedComposer { static let characterTable: HGHanjaTable = HGHanjaTable(contentOfFile: hangulBundle.path(forResource: "hanjac", ofType: "txt", inDirectory: "hanja")!)! static let wordTable: HGHanjaTable = HGHanjaTable(contentOfFile: hangulBundle.path(forResource: "hanjaw", ofType: "txt", inDirectory: "hanja")!)! static let reversedTable: HGHanjaTable = HGHanjaTable(contentOfFile: hangulBundle.path(forResource: "hanjar", ofType: "txt", inDirectory: "hanja")!)! @@ -107,7 +107,7 @@ class HanjaComposer: CIMComposer { // } } - override func input(controller: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { + override func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> InputResult { switch keyCode { // Arrow case kVK_DownArrow, kVK_UpArrow: @@ -115,10 +115,10 @@ class HanjaComposer: CIMComposer { default: break } - var result = delegate.input(controller: controller, inputText: string, key: keyCode, modifiers: flags, client: sender) + var result = delegate.input(text: string, key: keyCode, modifiers: flags, client: sender) switch keyCode { // backspace - case kVK_Delete: if result == .notProcessed { + case kVK_Delete: if result == InputResult.notProcessed { if !originalString.isEmpty { // 조합 중인 글자가 없을 때 backspace가 들어오면 조합이 완료된 글자 중 마지막 글자를 지운다. dlog(DEBUG_HANJACOMPOSER, "DEBUG 1, [hanja] MSG: before (%@)", _bufferedString) @@ -140,7 +140,7 @@ class HanjaComposer: CIMComposer { _bufferedString.append(" ") result = .processed } else { - result = .notProcessedAndNeedsCommit + result = InputResult(processed: false, action: .commit) } // esc case kVK_Escape: @@ -153,19 +153,19 @@ class HanjaComposer: CIMComposer { cancelComposition() // step 3: 한자 후보 취소 _candidates = nil // 후보 취소 - return .notProcessedAndNeedsCommit + return InputResult(processed: false, action: .commit) default: break } prepareHanjaCandidates() - if result == .notProcessedAndNeedsCommit { + if result == InputResult(processed: false, action: .commit) { cancelComposition() return result } if commitString.isEmpty { return result == .processed ? .processed : .notProcessed } else { - return .notProcessedAndNeedsCommit + return InputResult(processed: false, action: .commit) } } @@ -206,7 +206,7 @@ class HanjaComposer: CIMComposer { candidates.append(contentsOf: tableCandidates) } dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateHanjaCandidates candidating") - if candidates.count > 0, GureumConfiguration.shared.showsInputForHanjaCandidates { + if candidates.count > 0, Configuration.shared.showsInputForHanjaCandidates { candidates.insert(keyword, at: 0) } return candidates.map({ s in NSAttributedString(string: s) }) @@ -230,12 +230,8 @@ class HanjaComposer: CIMComposer { return candidates } - func update(fromController controller: CIMInputController) { + func update(client: IMKTextInput) { dlog(DEBUG_HANJACOMPOSER, "HanjaComposer updateFromController:") - guard let client = controller.client() else { - assert(false) - return - } let markedRange: NSRange = client.markedRange() let selectedRange: NSRange = client.selectedRange() dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateFromController: marked: %@ selected: %@", NSStringFromRange(markedRange), NSStringFromRange(selectedRange)) @@ -243,7 +239,7 @@ class HanjaComposer: CIMComposer { let selectedString = client.attributedSubstring(from: selectedRange).string dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateFromController: selected string: %@", selectedString) client.setMarkedText(selectedString, selectionRange: selectedRange, replacementRange: selectedRange) - dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateFromController: try marking: %@ / selected: %@", NSStringFromRange(controller.client().markedRange()), NSStringFromRange(controller.client().selectedRange())) + dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateFromController: try marking: %@ / selected: %@", NSStringFromRange(client.markedRange()), NSStringFromRange(client.selectedRange())) _bufferedString = selectedString dlog(DEBUG_HANJACOMPOSER, "HanjaComposer -updateFromController: so buffer is: %@", _bufferedString) mode = .single diff --git a/OSXCore/CIMInputController.swift b/OSXCore/InputController.swift similarity index 64% rename from OSXCore/CIMInputController.swift rename to OSXCore/InputController.swift index 45a19389..e2c9c012 100644 --- a/OSXCore/CIMInputController.swift +++ b/OSXCore/InputController.swift @@ -1,5 +1,5 @@ // -// CIMInputController.swift +// InputController.swift // Gureum // // Created by KMLee on 2018. 9. 12.. @@ -14,23 +14,38 @@ let DEBUG_INPUTCONTROLLER = false /*! @enum - @brief 최종적으로 CIMInputController가 처리할 결과 + @brief 최종적으로 InputController가 처리할 결과 */ -enum CIMInputTextProcessResult: Int { - case notProcessedAndNeedsCommit = -2 - case notProcessedAndNeedsCancel = -1 - case notProcessed = 0 - case processed = 1 + +public enum InputAction: Equatable { + case none + case commit + case cancel + case layout(String) } -enum CIMInputControllerSpecialKeyCode: Int { - case capsLockPressed = -1 - case capsLockFlagsChanged = -2 +struct InputResult: Equatable { + let processed: Bool + let action: InputAction + + static let processed = InputResult(processed: true, action: .none) + static let notProcessed = InputResult(processed: false, action: .none) } -@objc(CIMInputController) -public class CIMInputController: IMKInputController { - var receiver: CIMInputReceiver! +enum ChangeLayout { + case toggle + case hangul + case roman + case hanja +} + +enum InputEvent { + case changeLayout(ChangeLayout) +} + +@objc(GureumInputController) +public class InputController: IMKInputController { + var receiver: InputReceiver! override init!(server: IMKServer, delegate: Any!, client inputClient: Any) { super.init(server: server, delegate: delegate, client: inputClient) @@ -38,7 +53,7 @@ public class CIMInputController: IMKInputController { return nil } dlog(DEBUG_INPUTCONTROLLER, "**** NEW INPUT CONTROLLER INIT **** WITH SERVER: \(server) / DELEGATE: \(delegate ?? "nil") / CLIENT: \(inputClient) \(inputClient.bundleIdentifier() ?? "nil")") - receiver = CIMInputReceiver(server: server, delegate: delegate, client: inputClient, controller: self) + receiver = InputReceiver(server: server, delegate: delegate, client: inputClient, controller: self) } override init() { @@ -46,11 +61,7 @@ public class CIMInputController: IMKInputController { } } -extension CIMInputController { - @objc var composer: CIMComposer { - return receiver.composer - } - +extension InputController { @IBAction func showStandardAboutPanel(_ sender: Any) { NSApp.activate(ignoringOtherApps: true) NSApp.orderFrontStandardAboutPanel(sender) @@ -58,36 +69,36 @@ extension CIMInputController { } // IMKServerInputTextData, IMKServerInputHandleEvent, IMKServerInputKeyBinding 중 하나를 구현하여 입력 구현 -public extension CIMInputController { // IMKServerInputHandleEvent +public extension InputController { // IMKServerInputHandleEvent // Receiving Events Directly from the Text Services Manager public override func handle(_ event: NSEvent, client sender: Any) -> Bool { // sender is (IMKInputText & IMKUnicodeInputText & IMTSMSupport) - let imkCandidtes = composer.server.candidates + let imkCandidtes = InputMethodServer.shared.candidates let keys = imkCandidtes.selectionKeys() as! [NSNumber] if imkCandidtes.isVisible(), keys.contains(NSNumber(value: event.keyCode)) { imkCandidtes.interpretKeyEvents([event]) return true } if event.type == .keyDown { - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController KEYDOWN -handleEvent:client: with event: %@ / key: %d / modifier: %lu / chars: %@ / chars ignoreMod: %@ / client: %@", event, event.keyCode, event.modifierFlags.rawValue, event.characters ?? "(empty)", event.charactersIgnoringModifiers ?? "(empty)", client()!.bundleIdentifier()) - let processed = receiver.input(controller: self, inputText: event.characters, key: Int(event.keyCode), modifiers: event.modifierFlags, client: sender).rawValue > CIMInputTextProcessResult.notProcessed.rawValue - dlog(DEBUG_LOGGING, "LOGGING::PROCESSED::%d", processed) - return processed + dlog(DEBUG_INPUTCONTROLLER, "** InputController KEYDOWN -handleEvent:client: with event: %@ / key: %d / modifier: %lu / chars: %@ / chars ignoreMod: %@ / client: %@", event, event.keyCode, event.modifierFlags.rawValue, event.characters ?? "(empty)", event.charactersIgnoringModifiers ?? "(empty)", client()!.bundleIdentifier()) + let result = receiver.input(text: event.characters, key: Int(event.keyCode), modifiers: event.modifierFlags, client: sender) + dlog(DEBUG_LOGGING, "LOGGING::PROCESSED::\(result)") + return result.processed } else if event.type == .flagsChanged { var modifierFlags = event.modifierFlags - if composer.server.io.testAndClearCapsLockState() { + if InputMethodServer.shared.io.testAndClearCapsLockState() { dlog(DEBUG_IOKIT_EVENT, "controller detected capslock") modifierFlags.formUnion(.capsLock) dlog(DEBUG_IOKIT_EVENT, "modifierFlags by IOKit: %lx", modifierFlags.rawValue) - // dlog(DEBUG_INPUTCONTROLLER, @"** CIMInputController FLAGCHANGED -handleEvent:client: with event: %@ / key: %d / modifier: %lu / chars: %@ / chars ignoreMod: %@ / client: %@", event, -1, modifierFlags, nil, nil, [[self client] bundleIdentifier]) - _ = receiver.input(controller: self, inputText: nil, key: CIMInputControllerSpecialKeyCode.capsLockPressed.rawValue, modifiers: modifierFlags, client: sender) + // dlog(DEBUG_INPUTCONTROLLER, @"** InputController FLAGCHANGED -handleEvent:client: with event: %@ / key: %d / modifier: %lu / chars: %@ / chars ignoreMod: %@ / client: %@", event, -1, modifierFlags, nil, nil, [[self client] bundleIdentifier]) + receiver.input(event: .changeLayout(.toggle), client: sender) return false } dlog(DEBUG_LOGGING, "LOGGING::UNHANDLED::%@/%@", event, sender as! NSObject) - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -handleEvent:client: with event: %@ / sender: %@", event, sender as! NSObject) + dlog(DEBUG_INPUTCONTROLLER, "** InputController -handleEvent:client: with event: %@ / sender: %@", event, sender as! NSObject) return false } return false @@ -95,29 +106,29 @@ public extension CIMInputController { // IMKServerInputHandleEvent } /* -extension CIMInputController { // IMKServerInputTextData - override func inputText(_ string: String!, key keyCode: Int, modifiers flags: Int, client sender: Any) -> Bool { - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -inputText:key:modifiers:client with string: %@ / keyCode: %ld / modifier flags: %lu / client: %@", string, keyCode, flags, client()?.bundleIdentifier() ?? "nil") - let processed = receiver.input(controller: self, inputText: string, key: keyCode, modifiers: NSEvent.ModifierFlags(rawValue: UInt(flags)), client: sender).rawValue > CIMInputTextProcessResult.notProcessed.rawValue - return processed - } -} -*/ + extension InputController { // IMKServerInputTextData + override func inputText(_ string: String!, key keyCode: Int, modifiers flags: Int, client sender: Any) -> Bool { + dlog(DEBUG_INPUTCONTROLLER, "** InputController -inputText:key:modifiers:client with string: %@ / keyCode: %ld / modifier flags: %lu / client: %@", string, keyCode, flags, client()?.bundleIdentifier() ?? "nil") + let processed = receiver.input(controller: self, inputText: string, key: keyCode, modifiers: NSEvent.ModifierFlags(rawValue: UInt(flags)), client: sender).rawValue > CIMInputTextProcessResult.notProcessed.rawValue + return processed + } + } + */ /* -extension CIMInputController { // IMKServerInputKeyBinding - override func inputText(_: String!, client _: Any) -> Bool { - // dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -inputText:client: with string: %@ / client: %@", string, sender) - return false - } - - override func didCommand(by _: Selector!, client _: Any) -> Bool { - // dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -didCommandBySelector: with selector: %@", aSelector) - return false - } -} -*/ + extension InputController { // IMKServerInputKeyBinding + override func inputText(_: String!, client _: Any) -> Bool { + // dlog(DEBUG_INPUTCONTROLLER, "** InputController -inputText:client: with string: %@ / client: %@", string, sender) + return false + } + + override func didCommand(by _: Selector!, client _: Any) -> Bool { + // dlog(DEBUG_INPUTCONTROLLER, "** InputController -didCommandBySelector: with selector: %@", aSelector) + return false + } + } + */ -public extension CIMInputController { // IMKStateSetting +public extension InputController { // IMKStateSetting //! @brief 마우스 이벤트를 잡을 수 있게 한다. override func recognizedEvents(_ sender: Any!) -> Int { return receiver.recognizedEvents(sender) @@ -138,7 +149,7 @@ public extension CIMInputController { // IMKStateSetting } } -public extension CIMInputController { // IMKMouseHandling +public extension InputController { // IMKMouseHandling /*! @brief 마우스 입력 발생을 커서 옮기기로 간주하고 조합 중지. 만일 마우스 입력 발생을 감지하는 대신 커서 옮기기를 직접 알아낼 수 있으면 이 부분은 제거한다. */ @@ -149,13 +160,13 @@ public extension CIMInputController { // IMKMouseHandling } } -public extension CIMInputController { // IMKCustomCommands +public extension InputController { // IMKCustomCommands override func menu() -> NSMenu! { - return (NSApplication.shared.delegate! as! CIMApplicationDelegate).menu + return (NSApplication.shared.delegate! as! GureumApplicationDelegate).menu } } -public extension CIMInputController { // IMKServerInput +public extension InputController { // IMKServerInput // Committing a Composition // 조합을 중단하고 현재까지 조합된 글자를 커밋한다. override func commitComposition(_ sender: Any!) { @@ -166,10 +177,10 @@ public extension CIMInputController { // IMKServerInput override func updateComposition() { dlog(DEBUG_LOGGING, "LOGGING::EVENT::UPDATE-RAW?") - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -updateComposition") + dlog(DEBUG_INPUTCONTROLLER, "** InputController -updateComposition") receiver.updateCompositionEvent() super.updateComposition() - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -updateComposition ended") + dlog(DEBUG_INPUTCONTROLLER, "** InputController -updateComposition ended") } override func cancelComposition() { @@ -202,16 +213,16 @@ public extension CIMInputController { // IMKServerInput } #if DEBUG - @objcMembers public class CIMMockInputController: CIMInputController { + @objcMembers public class MockInputController: InputController { public override init(server: IMKServer, delegate: Any!, client: Any) { super.init() - receiver = CIMInputReceiver(server: server, delegate: delegate, client: client as! (IMKTextInput & IMKUnicodeTextInput), controller: self) + receiver = InputReceiver(server: server, delegate: delegate, client: client as! (IMKTextInput & IMKUnicodeTextInput), controller: self) } func repoduceTextLog(_ text: String) throws { for row in text.components(separatedBy: "\n") { guard let regex = try? NSRegularExpression(pattern: "LOGGING::([A-Z]+)::(.*)", options: []) else { - throw NSException(name: NSExceptionName("CIMMockInputControllerLogParserError"), reason: "Log is not readable format", userInfo: nil) as! Error + throw NSException(name: NSExceptionName("MockInputControllerLogParserError"), reason: "Log is not readable format", userInfo: nil) as! Error } let matches = regex.matches(in: row, options: [], range: NSRangeFromString(row)) let type = matches[1] @@ -220,10 +231,6 @@ public extension CIMInputController { // IMKServerInput } } - override var composer: CIMComposer { - return receiver.composer - } - public override func client() -> (IMKTextInput & NSObjectProtocol)! { return receiver.inputClient as? (IMKTextInput & NSObjectProtocol) } @@ -233,17 +240,24 @@ public extension CIMInputController { // IMKServerInput } } - public extension CIMMockInputController { // IMKServerInputTextData + public extension MockInputController { // IMKServerInputTextData + func inputFlags(_: Int, client sender: Any) -> Bool { + let client = self.client() + let result = receiver.input(event: .changeLayout(.toggle), client: sender) + if !result.processed { + // [self cancelComposition] + } + return result.processed + } + override func inputText(_ string: String!, key keyCode: Int, modifiers flags: Int, client sender: Any) -> Bool { let client = self.client() - print("** CIMInputController -inputText:key:modifiers:client with string: \(string ?? "(nil)") / keyCode: \(keyCode) / modifier flags: \(flags) / client: \(String(describing: client))") - let v1 = receiver.input(controller: self, inputText: string, key: keyCode, modifiers: NSEvent.ModifierFlags(rawValue: UInt(flags)), client: sender).rawValue - let v2 = CIMInputTextProcessResult.notProcessed.rawValue - let processed: Bool = v1 > v2 - if !processed { + print("** InputController -inputText:key:modifiers:client with string: \(string ?? "(nil)") / keyCode: \(keyCode) / modifier flags: \(flags) / client: \(String(describing: client))") + let result = receiver.input(text: string, key: keyCode, modifiers: NSEvent.ModifierFlags(rawValue: UInt(flags)), client: sender) + if !result.processed { // [self cancelComposition] } - return processed + return result.processed } // Committing a Composition @@ -302,7 +316,7 @@ public extension CIMInputController { // IMKServerInput } } - public extension CIMMockInputController { // IMKStateSetting + public extension MockInputController { // IMKStateSetting //! @brief 마우스 이벤트를 잡을 수 있게 한다. public override func recognizedEvents(_ sender: Any!) -> Int { return receiver.recognizedEvents(sender) diff --git a/OSXCore/InputMethodServer.swift b/OSXCore/InputMethodServer.swift index de715e4e..bf895052 100644 --- a/OSXCore/InputMethodServer.swift +++ b/OSXCore/InputMethodServer.swift @@ -11,21 +11,20 @@ import InputMethodKit import IOKit let DEBUG_INPUT_SERVER = false -let DEBUG_INPUT_HANDLER = false let DEBUG_IOKIT_EVENT = false -let CIMKeyMapLower = [ +let KeyMapLower = [ "a", "s", "d", "f", "h", "g", "z", "x", "c", "v", nil, "b", "q", "w", "e", "r", "y", "t", "1", "2", "3", "4", "6", "5", "=", "9", "7", "-", "8", "0", "]", "o", "u", "[", "i", "p", nil, "l", "j", "'", - "k", ";","\\", ",", "/", "n", "m", ".", + "k", ";", "\\", ",", "/", "n", "m", ".", nil, nil, "`", ] -// assert(keyMapLower.count == CIMKeyMapSize) +// assert(keyMapLower.count == KeyMapSize) -let CIMKeyMapUpper = [ +let KeyMapUpper = [ "A", "S", "D", "F", "H", "G", "Z", "X", "C", "V", nil, "B", "Q", "W", "E", "R", "Y", "T", "!", "@", "#", "$", "^", "%", @@ -113,21 +112,20 @@ class IOKitty { /*! @brief 공통적인 OSX의 입력기 구조를 다룬다. - InputManager는 @ref CIMInputController 또는 테스트코드에 해당하는 외부에서 입력을 받아 입력기에서 처리 후 결과 값을 보관한다. 처리 후 그 결과를 확인하는 것은 사용자의 몫이다. + InputManager는 @ref InputController 또는 테스트코드에 해당하는 외부에서 입력을 받아 입력기에서 처리 후 결과 값을 보관한다. 처리 후 그 결과를 확인하는 것은 사용자의 몫이다. IMKServer나 클라이언트와 무관하게 입력 값에 대해 출력 값을 생성해 내는 입력기. 입력 뿐만 아니라 여러 키보드 간 전환이나 입력기에 관한 단축키 등 입력기에 관한 모든 기능을 다룬다. - @coclass IMKServer CIMComposer + @coclass IMKServer DelegatedComposer */ -// TODO: CIMInputTextDelegate를 제거하고 서버만 관리하도록 한다 -public class InputMethodServer: CIMInputTextDelegate { +// TODO: InputTextDelegate를 제거하고 서버만 관리하도록 한다 +public class InputMethodServer { public static let shared = InputMethodServer() //! @brief 현재 입력중인 서버 let server: IMKServer //! @property let candidates: IMKCandidates //! @brief 입력기가 inputText: 문맥에 있는지 여부를 저장 - var inputting: Bool = false let io: IOKitty convenience init() { @@ -161,77 +159,8 @@ public class InputMethodServer: CIMInputTextDelegate { """ } - // MARK: - IMKServerInputTextData - - // 일단 받은 입력은 모두 핸들러로 넘겨준다. - func input(controller: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { - assert(controller.className.hasSuffix("InputController")) - - // 입력기용 특수 커맨드 처리 - var result = controller.composer.input(controller: controller, command: string, key: keyCode, modifiers: flags, client: sender) - if result == .notProcessedAndNeedsCommit { - return result - } - - if result != .processed { - // 옵션 키 변환 처리 - var string = string - if flags.contains(.option) { - let configuration = GureumConfiguration.shared - dlog(DEBUG_INPUT_HANDLER, "option key: %ld", configuration.optionKeyBehavior) - switch configuration.optionKeyBehavior { - case 0: - // default - dlog(DEBUG_INPUT_HANDLER, " ** ESCAPE from option-key default behavior") - return .notProcessedAndNeedsCommit - case 1: - // ignore - if keyCode < 0x33 { - if flags.contains(.capsLock) || flags.contains(.shift) { - string = CIMKeyMapUpper[keyCode] ?? string - } else { - string = CIMKeyMapLower[keyCode] ?? string - } - } - default: - assert(false) - } - } else { - if keyCode < 0x33 { - if flags.contains(.shift) { - string = CIMKeyMapUpper[keyCode] ?? string - } else { - string = CIMKeyMapLower[keyCode] ?? string - } - } - } - - // 특정 애플리케이션에서 커맨드/옵션/컨트롤 키 입력을 선점하지 못하는 문제를 회피한다 - if flags.contains(.command) || flags.contains(.option) || flags.contains(.control) { - dlog(DEBUG_INPUT_HANDLER, "-- CIMInputHandler -inputText: Command/Option key input / returned NO") - return .notProcessedAndNeedsCommit - } - - if string == nil { - return .notProcessedAndNeedsCommit - } - - result = controller.composer.input(controller: controller, inputText: string, key: keyCode, modifiers: flags, client: sender) - } - - dlog(false, "******* FINAL STATE: %d", result.rawValue) - // 합성 후보가 있다면 보여준다 - if controller.composer.hasCandidates { - candidates.update() - candidates.show(kIMKLocateCandidatesLeftHint) - } else if candidates.isVisible() { - candidates.hide() - } - return result - } - - func controllerDidCommit(_ controller: CIMInputController) { - if controller.composer.hasCandidates { + func showOrHideCandidates(controller: InputController) { + if controller.receiver.composer.hasCandidates { candidates.update() candidates.show(kIMKLocateCandidatesLeftHint) } else if candidates.isVisible() { diff --git a/OSXCore/CIMInputReceiver.swift b/OSXCore/InputReceiver.swift similarity index 50% rename from OSXCore/CIMInputReceiver.swift rename to OSXCore/InputReceiver.swift index 2b58cf34..8690667c 100644 --- a/OSXCore/CIMInputReceiver.swift +++ b/OSXCore/InputReceiver.swift @@ -1,5 +1,5 @@ // -// CIMInputReceiver.swift +// InputReceiver.swift // OSX // // Created by Jeong YunWon on 21/10/2018. @@ -9,43 +9,97 @@ import Foundation import InputMethodKit -public class CIMInputReceiver: NSObject, CIMInputTextDelegate { - var inputClient: IMKTextInput & IMKUnicodeTextInput - var composer: CIMComposer - var controller: CIMInputController +let DEBUG_INPUT_RECEIVER = true +public class InputReceiver: NSObject, InputTextDelegate { + var inputClient: IMKTextInput & IMKUnicodeTextInput + var composer = GureumComposer() + weak var controller: InputController! + var inputting: Bool = false var hasSelectionRange: Bool = false - init(server: IMKServer, delegate: Any!, client: IMKTextInput & IMKUnicodeTextInput, controller: CIMInputController) { - dlog(DEBUG_INPUTCONTROLLER, "**** NEW INPUT CONTROLLER INIT **** WITH SERVER: %@ / DELEGATE: %@ / CLIENT: %@", server, (delegate as? NSObject) ?? "(nil)", (client as? NSObject) ?? "(nil)") - composer = GureumComposer() + init(server: IMKServer, delegate: Any!, client: IMKTextInput & IMKUnicodeTextInput, controller: InputController) { + dlog(DEBUG_INPUT_RECEIVER, "**** NEW INPUT CONTROLLER INIT **** WITH SERVER: %@ / DELEGATE: %@ / CLIENT: %@", server, (delegate as? NSObject) ?? "(nil)", (client as? NSObject) ?? "(nil)") inputClient = client self.controller = controller } + // MARK: - IMKServerInputTextData + + func input2(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> InputResult { + // 옵션 키 변환 처리 + var string = string + if flags.contains(.option) { + let configuration = Configuration.shared + dlog(DEBUG_INPUT_RECEIVER, "option key: %ld", configuration.optionKeyBehavior) + switch configuration.optionKeyBehavior { + case 0: + // default + dlog(DEBUG_INPUT_RECEIVER, " ** ESCAPE from option-key default behavior") + return InputResult(processed: false, action: .commit) + case 1: + // ignore + if keyCode < 0x33 { + if flags.contains(.capsLock) || flags.contains(.shift) { + string = KeyMapUpper[keyCode] ?? string + } else { + string = KeyMapLower[keyCode] ?? string + } + } + default: + assert(false) + } + } else { + if keyCode < 0x33 { + if flags.contains(.shift) { + string = KeyMapUpper[keyCode] ?? string + } else { + string = KeyMapLower[keyCode] ?? string + } + } + } + + // 특정 애플리케이션에서 커맨드/옵션/컨트롤 키 입력을 선점하지 못하는 문제를 회피한다 + if flags.contains(.command) || flags.contains(.option) || flags.contains(.control) { + dlog(DEBUG_INPUT_RECEIVER, "-- InputReceiver -inputText: Command/Option key input / returned NO") + return InputResult(processed: false, action: .commit) + } + + if string == nil { + return InputResult(processed: false, action: .commit) + } + + let result = controller.receiver.composer.input(text: string, key: keyCode, modifiers: flags, client: sender) + + return result + } + // IMKServerInput 프로토콜에 대한 공용 핸들러 - func input(controller: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> CIMInputTextProcessResult { + func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client sender: Any) -> InputResult { + // 입력기용 특수 커맨드 처리 + if let command = composer.filterCommand(key: keyCode, modifiers: flags, client: sender) { + let result = input(event: command, client: sender) + if result.processed { + return result + } + } + dlog(DEBUG_LOGGING, "LOGGING::KEY::(%@)(%ld)(%lu)", string?.replacingOccurrences(of: "\n", with: "\\n") ?? "(nil)", keyCode, flags.rawValue) let hadComposedString = !_internalComposedString.isEmpty - let handled = composer.server.input(controller: controller, inputText: string, key: keyCode, modifiers: flags, client: sender) + let handled = input2(text: string, key: keyCode, modifiers: flags, client: sender) - composer.server.inputting = true + // 합성 후보가 있다면 보여준다 + InputMethodServer.shared.showOrHideCandidates(controller: controller) - switch handled { - case .notProcessed: - break - case .processed: - break - case .notProcessedAndNeedsCancel: - cancelComposition() - case .notProcessedAndNeedsCommit: + inputting = true + + if handled.action != .none { cancelComposition() - commitComposition(sender) - return handled - default: - dlog(true, "WRONG RESULT: %d", handled.rawValue) - assert(false) + if handled.action == .commit { + commitComposition(sender) + return handled + } } let commited = commitComposition(sender) // 조합 된 문자 반영 @@ -56,14 +110,50 @@ public class CIMInputReceiver: NSObject, CIMInputTextDelegate { updateComposition() // 조합 중인 문자 반영 } - composer.server.inputting = false + inputting = false - dlog(DEBUG_INPUTCONTROLLER, "*** End of Input handling ***") + dlog(DEBUG_INPUT_RECEIVER, "*** End of Input handling ***") return handled } + + func input(event: InputEvent, client sender: Any) -> InputResult { + switch event { + case let .changeLayout(layout): + let result = composer.changeLayout(layout, client: sender) + + // 합성 후보가 있다면 보여준다 + InputMethodServer.shared.showOrHideCandidates(controller: controller) + + inputting = true + + if result.action != .none { + cancelComposition() + if result.action == .commit { + commitComposition(sender) + return result + } + } + + let commited = commitComposition(sender) // 조합 된 문자 반영 + + updateComposition() // 조합 중인 문자 반영 + + switch result.action { + case let .layout(mode): + (sender as! IMKTextInput).selectMode(mode) + default: + break + } + + inputting = false + + return .processed + } + return .notProcessed + } } -extension CIMInputReceiver { // IMKServerInput +extension InputReceiver { // IMKServerInput // Committing a Composition // 조합을 중단하고 현재까지 조합된 글자를 커밋한다. func commitComposition(_ sender: Any) -> Bool { @@ -85,7 +175,7 @@ extension CIMInputReceiver { // IMKServerInput // 조합을 중단하고 현재까지 조합된 글자를 커밋한다. func commitCompositionEvent(_ sender: Any!) -> Bool { dlog(DEBUG_LOGGING, "LOGGING::EVENT::COMMIT") - if !composer.server.inputting { + if !inputting { // 입력기 외부에서 들어오는 커밋 요청에 대해서는 편집 중인 글자도 커밋한다. dlog(DEBUG_INPUTCONTROLLER, "-- CANCEL composition because of external commit request from %@", sender as! NSObject) dlog(DEBUG_LOGGING, "LOGGING::EVENT::CANCEL-INTERNAL") @@ -100,7 +190,7 @@ extension CIMInputReceiver { // IMKServerInput return false } - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -commitComposition: with sender: %@ / strings: %@", sender as! NSObject, commitString) + dlog(DEBUG_INPUT_RECEIVER, "** InputController -commitComposition: with sender: %@ / strings: %@", sender as! NSObject, commitString) let range = controller.selectionRange() dlog(DEBUG_LOGGING, "LOGGING::COMMIT::%lu:%lu:%@", range.location, range.length, commitString) if range.length > 0 { @@ -109,14 +199,14 @@ extension CIMInputReceiver { // IMKServerInput controller.client().insertText(commitString, replacementRange: NSRange(location: NSNotFound, length: NSNotFound)) } - composer.server.controllerDidCommit(controller) + InputMethodServer.shared.showOrHideCandidates(controller: controller) return true } func updateCompositionEvent() { dlog(DEBUG_LOGGING, "LOGGING::EVENT::UPDATE") - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -updateComposition") + dlog(DEBUG_INPUTCONTROLLER, "** InputController -updateComposition") } func cancelCompositionEvent() { @@ -133,12 +223,12 @@ extension CIMInputReceiver { // IMKServerInput public override func composedString(_: Any) -> Any { let string = _internalComposedString dlog(DEBUG_LOGGING, "LOGGING::CHECK::COMPOSEDSTRING::(%@)", string) - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -composedString: with return: '%@'", string) + dlog(DEBUG_INPUTCONTROLLER, "** InputController -composedString: with return: '%@'", string) return string } public override func originalString(_: Any!) -> NSAttributedString { - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -originalString:") + dlog(DEBUG_INPUTCONTROLLER, "** InputController -originalString:") let s = NSAttributedString(string: composer.originalString) dlog(DEBUG_LOGGING, "LOGGING::CHECK::ORIGINALSTRING::%@", s.string) return s @@ -151,20 +241,20 @@ extension CIMInputReceiver { // IMKServerInput func candidateSelected(_ candidateString: NSAttributedString) { dlog(DEBUG_LOGGING, "LOGGING::CHECK::CANDIDATESELECTED::%@", candidateString) - composer.server.inputting = true + inputting = true composer.candidateSelected(candidateString) - commitComposition(inputClient) - composer.server.inputting = false + commitCompositionEvent(inputClient) + inputting = false } - func candidateSelectionChanged(_ candidateString: NSAttributedString, controller _: CIMInputController) { + func candidateSelectionChanged(_ candidateString: NSAttributedString, controller _: InputController) { dlog(DEBUG_LOGGING, "LOGGING::CHECK::CANDIDATESELECTIONCHANGED::%@", candidateString) composer.candidateSelectionChanged(candidateString) updateComposition() } } -extension CIMInputReceiver { // IMKStateSetting +extension InputReceiver { // IMKStateSetting //! @brief 마우스 이벤트를 잡을 수 있게 한다. func recognizedEvents(_: Any!) -> Int { dlog(DEBUG_LOGGING, "LOGGING::CHECK::RECOGNIZEDEVENTS") @@ -174,9 +264,9 @@ extension CIMInputReceiver { // IMKStateSetting } //! @brief 자판 전환을 감지한다. - func setValue(_ value: Any, forTag tag: Int, client sender: Any, controller: CIMInputController) { + func setValue(_ value: Any, forTag tag: Int, client sender: Any, controller: InputController) { dlog(DEBUG_LOGGING, "LOGGING::EVENT::CHANGE-%lu-%@", tag, value as? String ?? "(nonstring)") - dlog(DEBUG_INPUTCONTROLLER, "** CIMInputController -setValue:forTag:client: with value: %@ / tag: %lx / client: %@", value as? String ?? "(nonstring)", tag, String(describing: controller.client as AnyObject)) + dlog(DEBUG_INPUTCONTROLLER, "** InputController -setValue:forTag:client: with value: %@ / tag: %lx / client: %@", value as? String ?? "(nonstring)", tag, String(describing: controller.client as AnyObject)) if let sender = sender as? IMKTextInput { sender.overrideKeyboard(withKeyboardNamed: "com.apple.keylayout.US") } @@ -196,39 +286,39 @@ extension CIMInputReceiver { // IMKStateSetting } return - - // 미국자판으로 기본자판 잡는 것도 임시로 포기 - /* - TISInputSource *mainSource = _USSource(); - NSString *mainSourceID = mainSource.identifier; - TISInputSource *currentSource = [TISInputSource currentSource]; - dlog(1, @"current source: %@", currentSource); - - [TISInputSource setInputMethodKeyboardLayoutOverride:mainSource]; - - TISInputSource *override = [TISInputSource inputMethodKeyboardLayoutOverride]; - if (override == nil) { - dlog(1, @"override fail"); - TISInputSource *currentASCIISource = [TISInputSource currentASCIICapableLayoutSource]; - dlog(1, @"ascii: %@", currentASCIISource); - id ASCIISourceID = currentASCIISource.identifier; - if (![ASCIISourceID isEqualToString:mainSourceID]) { + + // 미국자판으로 기본자판 잡는 것도 임시로 포기 + /* + TISInputSource *mainSource = _USSource(); + NSString *mainSourceID = mainSource.identifier; + TISInputSource *currentSource = [TISInputSource currentSource]; + dlog(1, @"current source: %@", currentSource); + + [TISInputSource setInputMethodKeyboardLayoutOverride:mainSource]; + + TISInputSource *override = [TISInputSource inputMethodKeyboardLayoutOverride]; + if (override == nil) { + dlog(1, @"override fail"); + TISInputSource *currentASCIISource = [TISInputSource currentASCIICapableLayoutSource]; + dlog(1, @"ascii: %@", currentASCIISource); + id ASCIISourceID = currentASCIISource.identifier; + if (![ASCIISourceID isEqualToString:mainSourceID]) { dlog(1, @"id: %@ != %@", ASCIISourceID, mainSourceID); BOOL mainSourceIsEnabled = mainSource.enabled; //if (!mainSourceIsEnabled) { // [mainSource enable]; //} if (mainSourceIsEnabled) { - [mainSource select]; - [currentSource select]; + [mainSource select]; + [currentSource select]; } //if (!mainSourceIsEnabled) { // [mainSource disable]; //} - } - } else { - dlog(1, @"overrided"); - } - */ + } + } else { + dlog(1, @"overrided"); + } + */ } } diff --git a/OSXCore/OSXCore.h b/OSXCore/OSXCore.h index 5fcc5d6f..d82bfd5d 100644 --- a/OSXCore/OSXCore.h +++ b/OSXCore/OSXCore.h @@ -7,6 +7,7 @@ // #import +#import //! Project version number for OSXCore. FOUNDATION_EXPORT double OSXCoreVersionNumber; diff --git a/OSX/RomanComposer.swift b/OSXCore/RomanComposer.swift similarity index 86% rename from OSX/RomanComposer.swift rename to OSXCore/RomanComposer.swift index 548dd553..381a3e54 100644 --- a/OSX/RomanComposer.swift +++ b/OSXCore/RomanComposer.swift @@ -9,7 +9,7 @@ import Cocoa import Foundation -class QwertyComposer: CIMComposer { +class QwertyComposer: DelegatedComposer { var _commitString: String? override var composedString: String { @@ -44,7 +44,7 @@ class QwertyComposer: CIMComposer { return nil } - override func input(controller _: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> CIMInputTextProcessResult { + override func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> InputResult { guard let string = string else { assert(false) return .notProcessed @@ -56,15 +56,15 @@ class QwertyComposer: CIMComposer { let newChr = Character(UnicodeScalar(String(chr).unicodeScalars.first!.value - 0x20)!) newString = String(newChr) _commitString = newString - return CIMInputTextProcessResult.processed + return .processed } } _commitString = nil - return CIMInputTextProcessResult.notProcessed + return .notProcessed } } -class RomanDataComposer: CIMComposer { +class RomanDataComposer: DelegatedComposer { static let dvorakData: String = ["`1234567890[]\\", "',.pyfgcrl/=", "aoeuidhtns-", @@ -122,7 +122,7 @@ class RomanDataComposer: CIMComposer { return nil } - override func input(controller _: CIMInputController, inputText string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> CIMInputTextProcessResult { + override func input(text string: String?, key keyCode: Int, modifiers flags: NSEvent.ModifierFlags, client _: Any) -> InputResult { guard let string = string else { assert(false) return .notProcessed @@ -151,10 +151,10 @@ class RomanDataComposer: CIMComposer { newChr = chr } _commitString = String(map[newChr]!) - return CIMInputTextProcessResult.processed + return .processed } else { _commitString = nil - return CIMInputTextProcessResult.notProcessed + return .notProcessed } } } diff --git a/OSXTestApp/TestViewController.swift b/OSXTestApp/TestViewController.swift index 2c77a5ed..0ad64728 100644 --- a/OSXTestApp/TestViewController.swift +++ b/OSXTestApp/TestViewController.swift @@ -26,11 +26,11 @@ class PreferenceViewController: NSViewController { class TestViewController: NSViewController { @IBOutlet var textField: NSTextField! @IBOutlet var inputClient: MockInputClient! - var inputController: CIMInputController! + var inputController: InputController! override func viewDidLoad() { assert(inputClient != nil) - inputController = CIMMockInputController(server: InputMethodServer.shared.server, delegate: self, client: inputClient!) + inputController = MockInputController(server: InputMethodServer.shared.server, delegate: self, client: inputClient!) NSEvent.addLocalMonitorForEvents(matching: [.keyDown, .flagsChanged], handler: { event in // print(event) diff --git a/Preferences/Preferences.swift b/Preferences/Preferences.swift index 1e90b710..3ca8c8bc 100644 --- a/Preferences/Preferences.swift +++ b/Preferences/Preferences.swift @@ -31,7 +31,7 @@ import PreferencePanes @IBOutlet var hangulNonChoseongCombinationButton: NSButton! @IBOutlet var hangulForceStrictCombinationRuleButton: NSButton! - var configuration = GureumConfiguration() + var configuration = Configuration() let pane: GureumPreferencePane! = nil let shortcutValidator = GureumShortcutValidator() @@ -68,7 +68,7 @@ import PreferencePanes } func setupShortcutViewValueChangeEvents() { - func masShortcutToShortcut(_ mas: MASShortcut?) -> GureumConfiguration.Shortcut? { + func masShortcutToShortcut(_ mas: MASShortcut?) -> Configuration.Shortcut? { guard let mas = mas else { return nil }