Merge pull request #38 from ShikiSuen/dev/Taiyan-Extension

Keyboard // Refactored Taiyan Engine + Choice for Apple Zhuyin Bopomofo Layout.
This commit is contained in:
Shiki Suen 2022-02-02 17:42:48 +08:00 committed by GitHub
commit 78a5b2589a
8 changed files with 1434 additions and 1295 deletions

View File

@ -5,245 +5,197 @@
* All rights reserved. See "LICENSE.TXT" for details. * All rights reserved. See "LICENSE.TXT" for details.
*/ */
#ifndef Mandarin_h #ifndef MANDARIN_H_
#define Mandarin_h #define MANDARIN_H_
#include <iostream> #include <iostream>
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
namespace Taiyan { namespace Taiyan {
namespace Mandarin { namespace Mandarin {
using namespace std;
class BopomofoSyllable { class BopomofoSyllable {
public: public:
typedef unsigned int Component; typedef uint16_t Component;
BopomofoSyllable(Component syllable = 0)
: m_syllable(syllable)
{
}
BopomofoSyllable(const BopomofoSyllable& another) explicit BopomofoSyllable(Component syllable = 0) : syllable_(syllable) {}
: m_syllable(another.m_syllable)
{
}
~BopomofoSyllable() BopomofoSyllable(const BopomofoSyllable&) = default;
{ BopomofoSyllable(BopomofoSyllable&& another) = default;
} BopomofoSyllable& operator=(const BopomofoSyllable&) = default;
BopomofoSyllable& operator=(BopomofoSyllable&&) = default;
BopomofoSyllable& operator=(const BopomofoSyllable& another) // takes the ASCII-form, "v"-tolerant, Non-Continental-style Hanyu Pinyin (fong, pong, bong
{ // acceptable)
m_syllable = another.m_syllable; static const BopomofoSyllable FromHanyuPinyin(const std::string& str);
return *this;
}
// takes the ASCII-form, "v"-tolerant, TW-style Hanyu Pinyin (fong, pong, bong acceptable)
static const BopomofoSyllable FromHanyuPinyin(const string& str);
// TO DO: Support accented vowels // TO DO: Support accented vowels
const string HanyuPinyinString(bool includesTone, bool useVForUUmlaut) const; const std::string HanyuPinyinString(bool includesTone,
// const string HanyuPinyinString(bool includesTone, bool useVForUUmlaut, bool composeAccentedVowel) const; bool useVForUUmlaut) const;
// const std::string HanyuPinyinString(bool includesTone, bool useVForUUmlaut,
// bool composeAccentedVowel) const;
// PHT = Pai-hua-tsi // PHT = Pai-hua-tsi
static const BopomofoSyllable FromPHT(const string& str); static const BopomofoSyllable FromPHT(const std::string& str);
const string PHTString(bool includesTone) const; const std::string PHTString(bool includesTone) const;
static const BopomofoSyllable FromComposedString(const string& str); static const BopomofoSyllable FromComposedString(const std::string& str);
const string composedString() const; const std::string composedString() const;
void clear() void clear() { syllable_ = 0; }
{
m_syllable = 0; bool isEmpty() const { return !syllable_; }
bool hasConsonant() const { return !!(syllable_ & ConsonantMask); }
bool hasMiddleVowel() const { return !!(syllable_ & MiddleVowelMask); }
bool hasVowel() const { return !!(syllable_ & VowelMask); }
bool hasToneMarker() const { return !!(syllable_ & ToneMarkerMask); }
Component consonantComponent() const { return syllable_ & ConsonantMask; }
Component middleVowelComponent() const {
return syllable_ & MiddleVowelMask;
} }
bool isEmpty() const Component vowelComponent() const { return syllable_ & VowelMask; }
{
return !m_syllable; Component toneMarkerComponent() const { return syllable_ & ToneMarkerMask; }
bool operator==(const BopomofoSyllable& another) const {
return syllable_ == another.syllable_;
} }
bool hasConsonant() const bool operator!=(const BopomofoSyllable& another) const {
{ return syllable_ != another.syllable_;
return !!(m_syllable & ConsonantMask);
} }
bool hasMiddleVowel() const bool isOverlappingWith(const BopomofoSyllable& another) const {
{ #define IOW_SAND(mask) ((syllable_ & mask) && (another.syllable_ & mask))
return !!(m_syllable & MiddleVowelMask); return IOW_SAND(ConsonantMask) || IOW_SAND(MiddleVowelMask) ||
} IOW_SAND(VowelMask) || IOW_SAND(ToneMarkerMask);
bool hasVowel() const #undef IOW_SAND
{
return !!(m_syllable & VowelMask);
}
bool hasToneMarker() const
{
return !!(m_syllable & ToneMarkerMask);
}
Component consonantComponent() const
{
return m_syllable & ConsonantMask;
}
Component middleVowelComponent() const
{
return m_syllable & MiddleVowelMask;
}
Component vowelComponent() const
{
return m_syllable & VowelMask;
}
Component toneMarkerComponent() const
{
return m_syllable & ToneMarkerMask;
}
bool operator==(const BopomofoSyllable& another) const
{
return m_syllable == another.m_syllable;
}
bool operator!=(const BopomofoSyllable& another) const
{
return m_syllable != another.m_syllable;
}
bool isOverlappingWith(const BopomofoSyllable& another) const
{
#define IOW_SAND(mask) ((m_syllable & mask) && (another.m_syllable & mask))
return IOW_SAND(ConsonantMask) || IOW_SAND(MiddleVowelMask) || IOW_SAND(VowelMask) || IOW_SAND(ToneMarkerMask);
#undef IOW_SAND
} }
// consonants J, Q, X all require the existence of vowel I or UE // consonants J, Q, X all require the existence of vowel I or UE
bool belongsToJQXClass() const bool belongsToJQXClass() const {
{ Component consonant = syllable_ & ConsonantMask;
Component consonant = m_syllable & ConsonantMask;
return (consonant == J || consonant == Q || consonant == X); return (consonant == J || consonant == Q || consonant == X);
} }
// zi, ci, si, chi, chi, shi, ri // zi, ci, si, chi, chi, shi, ri
bool belongsToZCSRClass() const bool belongsToZCSRClass() const {
{ Component consonant = syllable_ & ConsonantMask;
Component consonant = m_syllable & ConsonantMask;
return (consonant >= ZH && consonant <= S); return (consonant >= ZH && consonant <= S);
} }
Component maskType() const Component maskType() const {
{
Component mask = 0; Component mask = 0;
mask |= (m_syllable & ConsonantMask) ? ConsonantMask : 0; mask |= (syllable_ & ConsonantMask) ? ConsonantMask : 0;
mask |= (m_syllable & MiddleVowelMask) ? MiddleVowelMask : 0; mask |= (syllable_ & MiddleVowelMask) ? MiddleVowelMask : 0;
mask |= (m_syllable & VowelMask) ? VowelMask : 0; mask |= (syllable_ & VowelMask) ? VowelMask : 0;
mask |= (m_syllable & ToneMarkerMask) ? ToneMarkerMask : 0; mask |= (syllable_ & ToneMarkerMask) ? ToneMarkerMask : 0;
return mask; return mask;
} }
const BopomofoSyllable operator+(const BopomofoSyllable& another) const const BopomofoSyllable operator+(const BopomofoSyllable& another) const {
{ Component newSyllable = syllable_;
Component newSyllable = m_syllable; #define OP_SOVER(mask) \
#define OP_SOVER(mask) if (another.m_syllable & mask) newSyllable = (newSyllable & ~mask) | (another.m_syllable & mask) if (another.syllable_ & mask) { \
newSyllable = (newSyllable & ~mask) | (another.syllable_ & mask); \
}
OP_SOVER(ConsonantMask); OP_SOVER(ConsonantMask);
OP_SOVER(MiddleVowelMask); OP_SOVER(MiddleVowelMask);
OP_SOVER(VowelMask); OP_SOVER(VowelMask);
OP_SOVER(ToneMarkerMask); OP_SOVER(ToneMarkerMask);
#undef OP_SOVER #undef OP_SOVER
return BopomofoSyllable(newSyllable); return BopomofoSyllable(newSyllable);
} }
BopomofoSyllable& operator+=(const BopomofoSyllable& another) BopomofoSyllable& operator+=(const BopomofoSyllable& another) {
{ #define OPE_SOVER(mask) \
#define OPE_SOVER(mask) if (another.m_syllable & mask) m_syllable = (m_syllable & ~mask) | (another.m_syllable & mask) if (another.syllable_ & mask) { \
syllable_ = (syllable_ & ~mask) | (another.syllable_ & mask); \
}
OPE_SOVER(ConsonantMask); OPE_SOVER(ConsonantMask);
OPE_SOVER(MiddleVowelMask); OPE_SOVER(MiddleVowelMask);
OPE_SOVER(VowelMask); OPE_SOVER(VowelMask);
OPE_SOVER(ToneMarkerMask); OPE_SOVER(ToneMarkerMask);
#undef OPE_SOVER #undef OPE_SOVER
return *this; return *this;
} }
short absoluteOrder() const uint16_t absoluteOrder() const {
{
// turn BPMF syllable into a 4*14*4*22 number // turn BPMF syllable into a 4*14*4*22 number
return (short)(m_syllable & ConsonantMask) + return (uint16_t)(syllable_ & ConsonantMask) +
(short)((m_syllable & MiddleVowelMask) >> 5) * 22 + (uint16_t)((syllable_ & MiddleVowelMask) >> 5) * 22 +
(short)((m_syllable & VowelMask) >> 7) * 22 * 4 + (uint16_t)((syllable_ & VowelMask) >> 7) * 22 * 4 +
(short)((m_syllable & ToneMarkerMask) >> 11) * 22 * 4 * 14; (uint16_t)((syllable_ & ToneMarkerMask) >> 11) * 22 * 4 * 14;
} }
const string absoluteOrderString() const const std::string absoluteOrderString() const {
{
// 5*14*4*22 = 6160, we use a 79*79 encoding to represent that // 5*14*4*22 = 6160, we use a 79*79 encoding to represent that
short order = absoluteOrder(); uint16_t order = absoluteOrder();
char low = 48 + (char)(order % 79); char low = 48 + static_cast<char>(order % 79);
char high = 48 + (char)(order / 79); char high = 48 + static_cast<char>(order / 79);
string result(2, ' '); std::string result(2, ' ');
result[0] = low; result[0] = low;
result[1] = high; result[1] = high;
return result; return result;
} }
static BopomofoSyllable FromAbsoluteOrder(short order) static BopomofoSyllable FromAbsoluteOrder(uint16_t order) {
{ return BopomofoSyllable((order % 22) | ((order / 22) % 4) << 5 |
return BopomofoSyllable(
(order % 22) |
((order / 22) % 4) << 5 |
((order / (22 * 4)) % 14) << 7 | ((order / (22 * 4)) % 14) << 7 |
((order / (22 * 4 * 14)) % 5) << 11 ((order / (22 * 4 * 14)) % 5) << 11);
);
} }
static BopomofoSyllable FromAbsoluteOrderString(const string& str) static BopomofoSyllable FromAbsoluteOrderString(const std::string& str) {
{ if (str.length() != 2) return BopomofoSyllable();
if (str.length() != 2)
return BopomofoSyllable();
return FromAbsoluteOrder((short)(str[1] - 48) * 79 + (short)(str[0] - 48)); return FromAbsoluteOrder((uint16_t)(str[1] - 48) * 79 +
(uint16_t)(str[0] - 48));
} }
friend ostream& operator<<(ostream& stream, const BopomofoSyllable& syllable); friend std::ostream& operator<<(std::ostream& stream,
const BopomofoSyllable& syllable);
static const Component static constexpr Component
ConsonantMask = 0x001f, // 0000 0000 0001 1111, 21 consonants ConsonantMask = 0x001f, // 0000 0000 0001 1111, 21 consonants
MiddleVowelMask = 0x0060, // 0000 0000 0110 0000, 3 middle vowels MiddleVowelMask = 0x0060, // 0000 0000 0110 0000, 3 middle vowels
VowelMask = 0x0780, // 0000 0111 1000 0000, 13 vowels VowelMask = 0x0780, // 0000 0111 1000 0000, 13 vowels
ToneMarkerMask = 0x3800, // 0011 1000 0000 0000, 5 tones (tone1 = 0x00) ToneMarkerMask = 0x3800, // 0011 1000 0000 0000, 5 tones (tone1 = 0x00)
B = 0x0001, P = 0x0002, M = 0x0003, F = 0x0004, B = 0x0001, P = 0x0002, M = 0x0003, F = 0x0004, D = 0x0005, T = 0x0006,
D = 0x0005, T = 0x0006, N = 0x0007, L = 0x0008, N = 0x0007, L = 0x0008, G = 0x0009, K = 0x000a, H = 0x000b, J = 0x000c,
G = 0x0009, K = 0x000a, H = 0x000b, Q = 0x000d, X = 0x000e, ZH = 0x000f, CH = 0x0010, SH = 0x0011, R = 0x0012,
J = 0x000c, Q = 0x000d, X = 0x000e, Z = 0x0013, C = 0x0014, S = 0x0015, I = 0x0020, U = 0x0040,
ZH = 0x000f, CH = 0x0010, SH = 0x0011, R = 0x0012, UE = 0x0060, // ue = u umlaut (we use the German convention here as an
Z = 0x0013, C = 0x0014, S = 0x0015, // ersatz to the /ju:/ sound)
I = 0x0020, U = 0x0040, UE = 0x0060, // ue = u umlaut (we use the German convention here as an ersatz to the /ju:/ sound) A = 0x0080, O = 0x0100, ER = 0x0180, E = 0x0200, AI = 0x0280, EI = 0x0300,
A = 0x0080, O = 0x0100, ER = 0x0180, E = 0x0200, AO = 0x0380, OU = 0x0400, AN = 0x0480, EN = 0x0500, ANG = 0x0580,
AI = 0x0280, EI = 0x0300, AO = 0x0380, OU = 0x0400, ENG = 0x0600, ERR = 0x0680, Tone1 = 0x0000, Tone2 = 0x0800,
AN = 0x0480, EN = 0x0500, ANG = 0x0580, ENG = 0x0600, Tone3 = 0x1000, Tone4 = 0x1800, Tone5 = 0x2000;
ERR = 0x0680,
Tone1 = 0x0000, Tone2 = 0x0800, Tone3 = 0x1000, Tone4 = 0x1800, Tone5 = 0x2000;
protected: protected:
Component m_syllable; Component syllable_;
}; };
inline ostream& operator<<(ostream& stream, const BopomofoSyllable& syllable) inline std::ostream& operator<<(std::ostream& stream,
{ const BopomofoSyllable& syllable) {
stream << syllable.composedString(); stream << syllable.composedString();
return stream; return stream;
} }
typedef BopomofoSyllable BPMF; typedef BopomofoSyllable BPMF;
typedef map<char, vector<BPMF::Component> > BopomofoKeyToComponentMap; typedef std::map<char, std::vector<BPMF::Component> > BopomofoKeyToComponentMap;
typedef map<BPMF::Component, char> BopomofoComponentToKeyMap; typedef std::map<BPMF::Component, char> BopomofoComponentToKeyMap;
class BopomofoKeyboardLayout { class BopomofoKeyboardLayout {
public: public:
static void FinalizeLayouts();
static const BopomofoKeyboardLayout* StandardLayout(); static const BopomofoKeyboardLayout* StandardLayout();
static const BopomofoKeyboardLayout* ETenLayout(); static const BopomofoKeyboardLayout* ETenLayout();
static const BopomofoKeyboardLayout* HsuLayout(); static const BopomofoKeyboardLayout* HsuLayout();
@ -251,63 +203,60 @@ namespace Taiyan {
static const BopomofoKeyboardLayout* IBMLayout(); static const BopomofoKeyboardLayout* IBMLayout();
static const BopomofoKeyboardLayout* HanyuPinyinLayout(); static const BopomofoKeyboardLayout* HanyuPinyinLayout();
// recognizes (case-insensitive): standard, eten, hsu, eten26, ibm BopomofoKeyboardLayout(const BopomofoKeyToComponentMap& ktcm,
static const BopomofoKeyboardLayout* LayoutForName(const string& name); const std::string& name)
: m_keyToComponent(ktcm), m_name(name) {
BopomofoKeyboardLayout(const BopomofoKeyToComponentMap& ktcm, const string& name) for (BopomofoKeyToComponentMap::const_iterator miter =
: m_keyToComponent(ktcm) m_keyToComponent.begin();
, m_name(name) miter != m_keyToComponent.end(); ++miter)
{ for (std::vector<BPMF::Component>::const_iterator viter =
for (BopomofoKeyToComponentMap::const_iterator miter = m_keyToComponent.begin() ; miter != m_keyToComponent.end() ; ++miter) (*miter).second.begin();
for (vector<BPMF::Component>::const_iterator viter = (*miter).second.begin() ; viter != (*miter).second.end() ; ++viter) viter != (*miter).second.end(); ++viter)
m_componentToKey[*viter] = (*miter).first; m_componentToKey[*viter] = (*miter).first;
} }
const string name() const const std::string name() const { return m_name; }
{
return m_name;
}
char componentToKey(BPMF::Component component) const char componentToKey(BPMF::Component component) const {
{ BopomofoComponentToKeyMap::const_iterator iter =
BopomofoComponentToKeyMap::const_iterator iter = m_componentToKey.find(component); m_componentToKey.find(component);
return (iter == m_componentToKey.end()) ? 0 : (*iter).second; return (iter == m_componentToKey.end()) ? 0 : (*iter).second;
} }
const vector<BPMF::Component> keyToComponents(char key) const const std::vector<BPMF::Component> keyToComponents(char key) const {
{
BopomofoKeyToComponentMap::const_iterator iter = m_keyToComponent.find(key); BopomofoKeyToComponentMap::const_iterator iter = m_keyToComponent.find(key);
return (iter == m_keyToComponent.end()) ? vector<BPMF::Component>() : (*iter).second; return (iter == m_keyToComponent.end()) ? std::vector<BPMF::Component>()
: (*iter).second;
} }
const string keySequenceFromSyllable(BPMF syllable) const const std::string keySequenceFromSyllable(BPMF syllable) const {
{ std::string sequence;
string sequence;
BPMF::Component c; BPMF::Component c;
char k; char k;
#define STKS_COMBINE(component) if ((c = component)) { if ((k = componentToKey(c))) sequence += string(1, k); } #define STKS_COMBINE(component) \
if ((c = component)) { \
if ((k = componentToKey(c))) sequence += std::string(1, k); \
}
STKS_COMBINE(syllable.consonantComponent()); STKS_COMBINE(syllable.consonantComponent());
STKS_COMBINE(syllable.middleVowelComponent()); STKS_COMBINE(syllable.middleVowelComponent());
STKS_COMBINE(syllable.vowelComponent()); STKS_COMBINE(syllable.vowelComponent());
STKS_COMBINE(syllable.toneMarkerComponent()); STKS_COMBINE(syllable.toneMarkerComponent());
#undef STKS_COMBINE #undef STKS_COMBINE
return sequence; return sequence;
} }
const BPMF syllableFromKeySequence(const string& sequence) const const BPMF syllableFromKeySequence(const std::string& sequence) const {
{
BPMF syllable; BPMF syllable;
for (string::const_iterator iter = sequence.begin() ; iter != sequence.end() ; ++iter) for (std::string::const_iterator iter = sequence.begin();
{ iter != sequence.end(); ++iter) {
bool beforeSeqHasIorUE = sequenceContainsIorUE(sequence.begin(), iter); bool beforeSeqHasIorUE = sequenceContainsIorUE(sequence.begin(), iter);
bool aheadSeqHasIorUE = sequenceContainsIorUE(iter + 1, sequence.end()); bool aheadSeqHasIorUE = sequenceContainsIorUE(iter + 1, sequence.end());
vector<BPMF::Component> components = keyToComponents(*iter); std::vector<BPMF::Component> components = keyToComponents(*iter);
if (!components.size()) if (!components.size()) continue;
continue;
if (components.size() == 1) { if (components.size() == 1) {
syllable += BPMF(components[0]); syllable += BPMF(components[0]);
@ -319,25 +268,24 @@ namespace Taiyan {
BPMF ending = components.size() > 2 ? BPMF(components[2]) : follow; BPMF ending = components.size() > 2 ? BPMF(components[2]) : follow;
// apply the I/UE + E rule // apply the I/UE + E rule
if (head.vowelComponent() == BPMF::E && follow.vowelComponent() != BPMF::E) if (head.vowelComponent() == BPMF::E &&
{ follow.vowelComponent() != BPMF::E) {
syllable += beforeSeqHasIorUE ? head : follow; syllable += beforeSeqHasIorUE ? head : follow;
continue; continue;
} }
if (head.vowelComponent() != BPMF::E && follow.vowelComponent() == BPMF::E) if (head.vowelComponent() != BPMF::E &&
{ follow.vowelComponent() == BPMF::E) {
syllable += beforeSeqHasIorUE ? follow : head; syllable += beforeSeqHasIorUE ? follow : head;
continue; continue;
} }
// apply the J/Q/X + I/UE rule, only two components are allowed in the components vector here // apply the J/Q/X + I/UE rule, only two components are allowed in the
// components vector here
if (head.belongsToJQXClass() && !follow.belongsToJQXClass()) { if (head.belongsToJQXClass() && !follow.belongsToJQXClass()) {
if (!syllable.isEmpty()) { if (!syllable.isEmpty()) {
if (ending != follow) if (ending != follow) syllable += ending;
syllable += ending; } else {
}
else {
syllable += aheadSeqHasIorUE ? head : follow; syllable += aheadSeqHasIorUE ? head : follow;
} }
@ -346,10 +294,8 @@ namespace Taiyan {
if (!head.belongsToJQXClass() && follow.belongsToJQXClass()) { if (!head.belongsToJQXClass() && follow.belongsToJQXClass()) {
if (!syllable.isEmpty()) { if (!syllable.isEmpty()) {
if (ending != follow) if (ending != follow) syllable += ending;
syllable += ending; } else {
}
else {
syllable += aheadSeqHasIorUE ? follow : head; syllable += aheadSeqHasIorUE ? follow : head;
} }
@ -358,30 +304,30 @@ namespace Taiyan {
// the nasty issue of only one char in the buffer // the nasty issue of only one char in the buffer
if (iter == sequence.begin() && iter + 1 == sequence.end()) { if (iter == sequence.begin() && iter + 1 == sequence.end()) {
if (head.hasVowel() || follow.hasToneMarker() || head.belongsToZCSRClass()) if (head.hasVowel() || follow.hasToneMarker() ||
head.belongsToZCSRClass()) {
syllable += head; syllable += head;
else { } else {
if (follow.hasVowel() || ending.hasToneMarker()) if (follow.hasVowel() || ending.hasToneMarker()) {
syllable += follow; syllable += follow;
else } else {
syllable += ending; syllable += ending;
} }
}
continue; continue;
} }
if (!(syllable.maskType() & head.maskType()) && !endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end())) { if (!(syllable.maskType() & head.maskType()) &&
!endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end())) {
syllable += head; syllable += head;
} } else {
else { if (endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end()) &&
if (endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end()) && head.belongsToZCSRClass() && syllable.isEmpty()) { head.belongsToZCSRClass() && syllable.isEmpty()) {
syllable += head; syllable += head;
} } else if (syllable.maskType() < follow.maskType()) {
else if (syllable.maskType() < follow.maskType()) {
syllable += follow; syllable += follow;
} } else {
else {
syllable += ending; syllable += ending;
} }
} }
@ -390,24 +336,23 @@ namespace Taiyan {
// heuristics for Hsu keyboard layout // heuristics for Hsu keyboard layout
if (this == HsuLayout()) { if (this == HsuLayout()) {
// fix the left out L to ERR when it has sound, and GI, GUE -> JI, JUE // fix the left out L to ERR when it has sound, and GI, GUE -> JI, JUE
if (syllable.vowelComponent() == BPMF::ENG && !syllable.hasConsonant() && !syllable.hasMiddleVowel()) { if (syllable.vowelComponent() == BPMF::ENG && !syllable.hasConsonant() &&
!syllable.hasMiddleVowel()) {
syllable += BPMF(BPMF::ERR); syllable += BPMF(BPMF::ERR);
} } else if (syllable.consonantComponent() == BPMF::G &&
else if (syllable.consonantComponent() == BPMF::G && (syllable.middleVowelComponent() == BPMF::I || syllable.middleVowelComponent() == BPMF::UE)) { (syllable.middleVowelComponent() == BPMF::I ||
syllable.middleVowelComponent() == BPMF::UE)) {
syllable += BPMF(BPMF::J); syllable += BPMF(BPMF::J);
} }
} }
return syllable; return syllable;
} }
protected:
protected: bool endAheadOrAheadHasToneMarkKey(std::string::const_iterator ahead,
bool endAheadOrAheadHasToneMarkKey(string::const_iterator ahead, string::const_iterator end) const std::string::const_iterator end) const {
{ if (ahead == end) return true;
if (ahead == end)
return true;
char tone1 = componentToKey(BPMF::Tone1); char tone1 = componentToKey(BPMF::Tone1);
char tone2 = componentToKey(BPMF::Tone2); char tone2 = componentToKey(BPMF::Tone2);
@ -418,70 +363,57 @@ namespace Taiyan {
if (tone1) if (tone1)
if (*ahead == tone1) return true; if (*ahead == tone1) return true;
if (*ahead == tone2 || *ahead == tone3 || *ahead == tone4 || *ahead == tone5) if (*ahead == tone2 || *ahead == tone3 || *ahead == tone4 ||
*ahead == tone5)
return true; return true;
return false; return false;
} }
bool sequenceContainsIorUE(string::const_iterator start, string::const_iterator end) const bool sequenceContainsIorUE(std::string::const_iterator start,
{ std::string::const_iterator end) const {
char iChar = componentToKey(BPMF::I); char iChar = componentToKey(BPMF::I);
char ueChar = componentToKey(BPMF::UE); char ueChar = componentToKey(BPMF::UE);
for (; start != end; ++start) for (; start != end; ++start)
if (*start == iChar || *start == ueChar) if (*start == iChar || *start == ueChar) return true;
return true;
return false; return false;
} }
string m_name; std::string m_name;
BopomofoKeyToComponentMap m_keyToComponent; BopomofoKeyToComponentMap m_keyToComponent;
BopomofoComponentToKeyMap m_componentToKey; BopomofoComponentToKeyMap m_componentToKey;
};
static const BopomofoKeyboardLayout* c_StandardLayout; class BopomofoReadingBuffer {
static const BopomofoKeyboardLayout* c_ETenLayout; public:
static const BopomofoKeyboardLayout* c_HsuLayout; explicit BopomofoReadingBuffer(const BopomofoKeyboardLayout* layout)
static const BopomofoKeyboardLayout* c_ETen26Layout; : layout_(layout), pinyin_mode_(false) {
static const BopomofoKeyboardLayout* c_IBMLayout;
// this is essentially an empty layout, but we use pointer semantic to tell the differences--and pass on the responsibility to BopomofoReadingBuffer
static const BopomofoKeyboardLayout* c_HanyuPinyinLayout;
};
class BopomofoReadingBuffer {
public:
BopomofoReadingBuffer(const BopomofoKeyboardLayout* layout)
: m_layout(layout)
, m_pinyinMode(false)
{
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) { if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
m_pinyinMode = true; pinyin_mode_ = true;
m_pinyinSequence = ""; pinyin_sequence_ = "";
} }
} }
void setKeyboardLayout(const BopomofoKeyboardLayout* layout) void setKeyboardLayout(const BopomofoKeyboardLayout* layout) {
{ layout_ = layout;
m_layout = layout;
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) { if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
m_pinyinMode = true; pinyin_mode_ = true;
m_pinyinSequence = ""; pinyin_sequence_ = "";
} }
} }
bool isValidKey(char k) const bool isValidKey(char k) const {
{ if (!pinyin_mode_) {
if (!m_pinyinMode) { return layout_ ? (layout_->keyToComponents(k)).size() > 0 : false;
return m_layout ? (m_layout->keyToComponents(k)).size() > 0 : false;
} }
char lk = tolower(k); char lk = tolower(k);
if (lk >= 'a' && lk <= 'z') { if (lk >= 'a' && lk <= 'z') {
// if a tone marker is already in place // if a tone marker is already in place
if (m_pinyinSequence.length()) { if (pinyin_sequence_.length()) {
char lastc = m_pinyinSequence[m_pinyinSequence.length() - 1]; char lastc = pinyin_sequence_[pinyin_sequence_.length() - 1];
if (lastc >= '2' && lastc <= '5') { if (lastc >= '2' && lastc <= '5') {
return false; return false;
} }
@ -490,98 +422,83 @@ namespace Taiyan {
return true; return true;
} }
if (m_pinyinSequence.length() && (lk >= '2' && lk <= '5')) { if (pinyin_sequence_.length() && (lk >= '2' && lk <= '5')) {
return true; return true;
} }
return false; return false;
} }
bool combineKey(char k) bool combineKey(char k) {
{ if (!isValidKey(k)) return false;
if (!isValidKey(k))
return false;
if (m_pinyinMode) { if (pinyin_mode_) {
m_pinyinSequence += string(1, tolower(k)); pinyin_sequence_ += std::string(1, tolower(k));
m_syllable = BPMF::FromHanyuPinyin(m_pinyinSequence); syllable_ = BPMF::FromHanyuPinyin(pinyin_sequence_);
return true; return true;
} }
string sequence = m_layout->keySequenceFromSyllable(m_syllable) + string(1, k); std::string sequence =
m_syllable = m_layout->syllableFromKeySequence(sequence); layout_->keySequenceFromSyllable(syllable_) + std::string(1, k);
syllable_ = layout_->syllableFromKeySequence(sequence);
return true; return true;
} }
void clear() void clear() {
{ pinyin_sequence_.clear();
m_pinyinSequence.clear(); syllable_.clear();
m_syllable.clear();
} }
void backspace() void backspace() {
{ if (!layout_) return;
if (!m_layout)
return;
if (m_pinyinMode) { if (pinyin_mode_) {
if (m_pinyinSequence.length()) { if (pinyin_sequence_.length()) {
m_pinyinSequence = m_pinyinSequence.substr(0, m_pinyinSequence.length() - 1); pinyin_sequence_ =
pinyin_sequence_.substr(0, pinyin_sequence_.length() - 1);
} }
m_syllable = BPMF::FromHanyuPinyin(m_pinyinSequence); syllable_ = BPMF::FromHanyuPinyin(pinyin_sequence_);
return; return;
} }
string sequence = m_layout->keySequenceFromSyllable(m_syllable); std::string sequence = layout_->keySequenceFromSyllable(syllable_);
if (sequence.length()) { if (sequence.length()) {
sequence = sequence.substr(0, sequence.length() - 1); sequence = sequence.substr(0, sequence.length() - 1);
m_syllable = m_layout->syllableFromKeySequence(sequence); syllable_ = layout_->syllableFromKeySequence(sequence);
} }
} }
bool isEmpty() const bool isEmpty() const { return syllable_.isEmpty(); }
{
return m_syllable.isEmpty(); const std::string composedString() const {
if (pinyin_mode_) {
return pinyin_sequence_;
} }
const string composedString() const return syllable_.composedString();
{
if (m_pinyinMode) {
return m_pinyinSequence;
} }
return m_syllable.composedString(); const BPMF syllable() const { return syllable_; }
const std::string standardLayoutQueryString() const {
return BopomofoKeyboardLayout::StandardLayout()->keySequenceFromSyllable(syllable_);
} }
const BPMF syllable() const const std::string absoluteOrderQueryString() const {
{ return syllable_.absoluteOrderString();
return m_syllable;
} }
const string standardLayoutQueryString() const bool hasToneMarker() const { return syllable_.hasToneMarker(); }
{
return BopomofoKeyboardLayout::StandardLayout()->keySequenceFromSyllable(m_syllable);
}
const string absoluteOrderQueryString() const protected:
{ const BopomofoKeyboardLayout* layout_;
return m_syllable.absoluteOrderString(); BPMF syllable_;
}
bool hasToneMarker() const bool pinyin_mode_;
{ std::string pinyin_sequence_;
return m_syllable.hasToneMarker(); };
} } // namespace Mandarin
} // namespace Taiyan
protected: #endif // MANDARIN_H_
const BopomofoKeyboardLayout* m_layout;
BPMF m_syllable;
bool m_pinyinMode;
string m_pinyinSequence;
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,6 @@ private let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
private let kCandidateTextFontName = "CandidateTextFontName" private let kCandidateTextFontName = "CandidateTextFontName"
private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
private let kCandidateKeys = "CandidateKeys" private let kCandidateKeys = "CandidateKeys"
private let kChineseConversionEngine = "ChineseConversionEngine"
private let kPhraseReplacementEnabled = "PhraseReplacementEnabled" private let kPhraseReplacementEnabled = "PhraseReplacementEnabled"
private let kDefaultCandidateListTextSize: CGFloat = 18 private let kDefaultCandidateListTextSize: CGFloat = 18
@ -154,20 +153,6 @@ struct ComposingBufferSize {
} }
} }
@objc enum ChineseConversionEngine: Int {
case openCC
case vxHanConvert
var name: String {
switch (self) {
case .openCC:
return "OpenCC"
case .vxHanConvert:
return "VXHanConvert"
}
}
}
// MARK: - // MARK: -
@objc public class Preferences: NSObject { @objc public class Preferences: NSObject {
static func reset() { static func reset() {
@ -193,7 +178,6 @@ struct ComposingBufferSize {
defaults.removeObject(forKey: kCandidateKeyLabelFontName) defaults.removeObject(forKey: kCandidateKeyLabelFontName)
defaults.removeObject(forKey: kCandidateKeys) defaults.removeObject(forKey: kCandidateKeys)
defaults.removeObject(forKey: kPhraseReplacementEnabled) defaults.removeObject(forKey: kPhraseReplacementEnabled)
defaults.removeObject(forKey: kChineseConversionEngine)
defaults.removeObject(forKey: kUseWinNT351BPMF) defaults.removeObject(forKey: kUseWinNT351BPMF)
defaults.removeObject(forKey: kMaxCandidateLength) defaults.removeObject(forKey: kMaxCandidateLength)
defaults.removeObject(forKey: kShouldNotFartInLieuOfBeep) defaults.removeObject(forKey: kShouldNotFartInLieuOfBeep)
@ -430,13 +414,6 @@ struct ComposingBufferSize {
} }
@UserDefault(key: kChineseConversionEngine, defaultValue: 0)
@objc static var chineseConversionEngine: Int
@objc static var chineseConversionEngineName: String? {
return ChineseConversionEngine(rawValue: chineseConversionEngine)?.name
}
@UserDefault(key: kPhraseReplacementEnabled, defaultValue: false) @UserDefault(key: kPhraseReplacementEnabled, defaultValue: false)
@objc static var phraseReplacementEnabled: Bool @objc static var phraseReplacementEnabled: Bool

View File

@ -63,6 +63,7 @@ extension RangeReplaceableCollection where Element: Hashable {
basisKeyboardLayoutButton.menu?.removeAllItems() basisKeyboardLayoutButton.menu?.removeAllItems()
let basisKeyboardLayoutID = Preferences.basisKeyboardLayout let basisKeyboardLayoutID = Preferences.basisKeyboardLayout
for source in list { for source in list {
func getString(_ key: CFString) -> String? { func getString(_ key: CFString) -> String? {
@ -112,20 +113,6 @@ extension RangeReplaceableCollection where Element: Hashable {
menuItem.title = localizedName menuItem.title = localizedName
menuItem.representedObject = sourceID menuItem.representedObject = sourceID
if let iconPtr = TISGetInputSourceProperty(source, kTISPropertyIconRef) {
let icon = IconRef(iconPtr)
let image = NSImage(iconRef: icon)
func resize( _ image: NSImage) -> NSImage {
let newImage = NSImage(size: NSSize(width: 16, height: 16))
newImage.lockFocus()
image.draw(in: NSRect(x: 0, y: 0, width: 16, height: 16))
newImage.unlockFocus()
return newImage
}
menuItem.image = resize(image)
}
if sourceID == "com.apple.keylayout.US" { if sourceID == "com.apple.keylayout.US" {
usKeyboardLayoutItem = menuItem usKeyboardLayoutItem = menuItem
} }
@ -135,6 +122,11 @@ extension RangeReplaceableCollection where Element: Hashable {
basisKeyboardLayoutButton.menu?.addItem(menuItem) basisKeyboardLayoutButton.menu?.addItem(menuItem)
} }
let menuItem = NSMenuItem()
menuItem.title = String(format: NSLocalizedString("Apple Zhuyin Bopomofo", comment: ""))
menuItem.representedObject = String("com.apple.keylayout.ZhuyinBopomofo")
basisKeyboardLayoutButton.menu?.addItem(menuItem)
basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem) basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem)
selectionKeyComboBox.usesDataSource = false selectionKeyComboBox.usesDataSource = false
selectionKeyComboBox.removeAllItems() selectionKeyComboBox.removeAllItems()

View File

@ -43,3 +43,4 @@
"zh-Hans" = "Simplified Chinese"; "zh-Hans" = "Simplified Chinese";
"zh-Hant" = "Traditional Chinese"; "zh-Hant" = "Traditional Chinese";
"ja" = "Japanese"; "ja" = "Japanese";
"Apple Zhuyin Bopomofo" = "Apple Zhuyin Bopomofo";

View File

@ -43,3 +43,4 @@
"zh-Hans" = "簡體中国語"; "zh-Hans" = "簡體中国語";
"zh-Hant" = "繁體中国語"; "zh-Hant" = "繁體中国語";
"ja" = "和語"; "ja" = "和語";
"Apple Zhuyin Bopomofo" = "Apple 注音ボポモフォ配列";

View File

@ -43,3 +43,4 @@
"zh-Hans" = "简体中文"; "zh-Hans" = "简体中文";
"zh-Hant" = "繁体中文"; "zh-Hant" = "繁体中文";
"ja" = "和文"; "ja" = "和文";
"Apple Zhuyin Bopomofo" = "Apple 注音键盘布局";

View File

@ -43,3 +43,5 @@
"zh-Hans" = "簡體中文"; "zh-Hans" = "簡體中文";
"zh-Hant" = "繁體中文"; "zh-Hant" = "繁體中文";
"ja" = "和文"; "ja" = "和文";
"Apple Zhuyin Bopomofo" = "Apple Zhuyin Bopomofo";
"Apple Zhuyin Bopomofo" = "Apple 注音鍵盤佈局";