Merge pull request #227 from zonble/master

Adds a new setting: Chinese conversion style
This commit is contained in:
Weizhong Yang a.k.a zonble 2022-01-16 15:05:08 +08:00 committed by GitHub
commit 01378b8091
10 changed files with 326 additions and 121 deletions

View File

@ -51,6 +51,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NonModalAlertWindowControlle
private var updateNextStepURL: URL?
func applicationDidFinishLaunching(_ notification: Notification) {
LanguageModelManager.setupDataModelValueConverter()
LanguageModelManager.loadDataModels()
LanguageModelManager.loadUserPhrases()
LanguageModelManager.loadUserPhraseReplacement()

View File

@ -19,14 +19,14 @@
<window title="Bopomofo Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" animationBehavior="default" id="1" userLabel="Window - Preferences">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="401" y="295" width="475" height="438"/>
<rect key="contentRect" x="401" y="295" width="475" height="484"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
<view key="contentView" id="2">
<rect key="frame" x="0.0" y="0.0" width="475" height="438"/>
<rect key="frame" x="0.0" y="0.0" width="475" height="484"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="11">
<rect key="frame" x="38" y="400" width="183" height="17"/>
<rect key="frame" x="38" y="446" width="183" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bopomofo Keyboard Layout:" id="12">
<font key="font" metaFont="system"/>
@ -35,7 +35,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="124">
<rect key="frame" x="225" y="363" width="156" height="26"/>
<rect key="frame" x="225" y="409" width="156" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="127">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -47,7 +47,7 @@
</connections>
</popUpButton>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="125">
<rect key="frame" x="16" y="369" width="205" height="17"/>
<rect key="frame" x="16" y="415" width="205" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Alphanumeric Keyboard Layout:" id="126">
<font key="font" metaFont="system"/>
@ -56,7 +56,7 @@
</textFieldCell>
</textField>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="109">
<rect key="frame" x="226" y="309" width="217" height="18"/>
<rect key="frame" x="226" y="355" width="217" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Space key chooses candidate" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="110">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -67,7 +67,7 @@
</connections>
</button>
<comboBox verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uHU-aL-du7">
<rect key="frame" x="228" y="336" width="209" height="25"/>
<rect key="frame" x="228" y="382" width="209" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="jQC-12-UuK">
<font key="font" metaFont="system"/>
@ -84,7 +84,7 @@
</connections>
</comboBox>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ETa-09-qWI">
<rect key="frame" x="38" y="342" width="183" height="16"/>
<rect key="frame" x="38" y="388" width="183" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Selection Keys:" id="FnD-oH-El5">
<font key="font" metaFont="system"/>
@ -93,7 +93,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3">
<rect key="frame" x="225" y="394" width="132" height="26"/>
<rect key="frame" x="225" y="440" width="132" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Standard" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="6" id="4">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -114,7 +114,7 @@
</popUpButtonCell>
</popUpButton>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bE0-Lq-Pj7">
<rect key="frame" x="226" y="287" width="229" height="18"/>
<rect key="frame" x="226" y="333" width="229" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="ESC key clears entire input buffer" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="f2j-xD-4xK">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -125,7 +125,7 @@
</connections>
</button>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="13">
<rect key="frame" x="63" y="252" width="155" height="17"/>
<rect key="frame" x="63" y="298" width="155" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Show Candidate Phrase:" id="14">
<font key="font" metaFont="system"/>
@ -134,7 +134,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="23">
<rect key="frame" x="80" y="206" width="138" height="17"/>
<rect key="frame" x="80" y="252" width="138" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Candidate List Style:" id="24">
<font key="font" metaFont="system"/>
@ -143,7 +143,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="28">
<rect key="frame" x="84" y="159" width="134" height="17"/>
<rect key="frame" x="84" y="205" width="134" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Candidate Text Size:" id="29">
<font key="font" metaFont="system"/>
@ -152,7 +152,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MBE-hh-AxG">
<rect key="frame" x="45" y="103" width="173" height="17"/>
<rect key="frame" x="48" y="41" width="173" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Chinese Conversion Engine:" id="hwF-gg-TYW">
<font key="font" metaFont="system"/>
@ -161,7 +161,7 @@
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="15">
<rect key="frame" x="227" y="231" width="213" height="38"/>
<rect key="frame" x="227" y="277" width="213" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="206" height="18"/>
@ -187,7 +187,7 @@
</connections>
</matrix>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rAU-Xz-M2R">
<rect key="frame" x="227" y="82" width="213" height="38"/>
<rect key="frame" x="227" y="20" width="213" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="206" height="18"/>
@ -212,8 +212,43 @@
<binding destination="32" name="selectedTag" keyPath="values.ChineseConversionEngine" id="qf2-qK-OJ0"/>
</connections>
</matrix>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Lk-JF-I6D">
<rect key="frame" x="45" y="87" width="173" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Chinese Conversion Style:" id="DMf-hh-Tut">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="11Y-7Q-irj">
<rect key="frame" x="228" y="66" width="213" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="206" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="H7Y-M5-po3">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="Convert ouput" imagePosition="left" alignment="left" state="on" inset="2" id="GhA-hR-a8V">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="Convert models" imagePosition="left" alignment="left" tag="1" inset="2" id="k2S-cq-v0o">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</column>
</cells>
<connections>
<binding destination="32" name="selectedTag" keyPath="values.ChineseConversionStyle" id="dTK-DC-XfO"/>
</connections>
</matrix>
<matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="19">
<rect key="frame" x="227" y="185" width="207" height="38"/>
<rect key="frame" x="227" y="231" width="207" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="88" height="18"/>
@ -239,7 +274,7 @@
</connections>
</matrix>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="90">
<rect key="frame" x="224" y="151" width="86" height="26"/>
<rect key="frame" x="224" y="197" width="86" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="18" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="18" imageScaling="proportionallyDown" inset="2" selectedItem="96" id="91">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -261,12 +296,8 @@
<binding destination="32" name="selectedTag" keyPath="values.CandidateListTextSize" id="107"/>
</connections>
</popUpButton>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="BtG-38-RSK">
<rect key="frame" x="12" y="132" width="443" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Fc2-qh-r1H">
<rect key="frame" x="226" y="19" width="218" height="18"/>
<rect key="frame" x="226" y="127" width="218" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="Z9t-P0-BLF">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -276,9 +307,13 @@
<binding destination="32" name="value" keyPath="values.CheckUpdateAutomatically" id="6WP-5h-sHG"/>
</connections>
</button>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="BtG-38-RSK">
<rect key="frame" x="12" y="117" width="443" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
</subviews>
</view>
<point key="canvasLocation" x="148.5" y="233"/>
<point key="canvasLocation" x="148.5" y="256"/>
</window>
<userDefaultsController representsSharedInstance="YES" id="32"/>
</objects>

View File

@ -24,7 +24,6 @@
#include "McBopomofoLM.h"
#include <algorithm>
#include <iterator>
#include <unordered_set>
using namespace McBopomofo;
@ -49,7 +48,7 @@ void McBopomofoLM::loadLanguageModel(const char* languageModelDataPath)
}
void McBopomofoLM::loadUserPhrases(const char* userPhrasesDataPath,
const char* excludedPhrasesDataPath)
const char* excludedPhrasesDataPath)
{
if (userPhrasesDataPath) {
m_userPhrases.close();
@ -61,7 +60,8 @@ void McBopomofoLM::loadUserPhrases(const char* userPhrasesDataPath,
}
}
void McBopomofoLM::loadPhraseReplacementMap(const char* phraseReplacementPath) {
void McBopomofoLM::loadPhraseReplacementMap(const char* phraseReplacementPath)
{
if (phraseReplacementPath) {
m_phraseReplacement.close();
m_phraseReplacement.open(phraseReplacementPath);
@ -75,75 +75,37 @@ const vector<Bigram> McBopomofoLM::bigramsForKeys(const string& preceedingKey, c
const vector<Unigram> McBopomofoLM::unigramsForKey(const string& key)
{
vector<Unigram> unigrams;
vector<Unigram> allUnigrams;
vector<Unigram> userUnigrams;
// Use unordered_set so that you don't have to do O(n*m)
unordered_set<string> excludedValues;
unordered_set<string> userValues;
unordered_set<string> insertedValues;
if (m_excludedPhrases.hasUnigramsForKey(key)) {
vector<Unigram> excludedUnigrams = m_excludedPhrases.unigramsForKey(key);
transform(excludedUnigrams.begin(), excludedUnigrams.end(),
inserter(excludedValues, excludedValues.end()),
[](const Unigram &u) { return u.keyValue.value; });
inserter(excludedValues, excludedValues.end()),
[](const Unigram& u) { return u.keyValue.value; });
}
if (m_userPhrases.hasUnigramsForKey(key)) {
vector<Unigram> rawUserUnigrams = m_userPhrases.unigramsForKey(key);
vector<Unigram> filterredUserUnigrams = m_userPhrases.unigramsForKey(key);
for (auto&& unigram : rawUserUnigrams) {
if (excludedValues.find(unigram.keyValue.value) == excludedValues.end()) {
filterredUserUnigrams.push_back(unigram);
}
}
transform(filterredUserUnigrams.begin(), filterredUserUnigrams.end(),
inserter(userValues, userValues.end()),
[](const Unigram &u) { return u.keyValue.value; });
if (m_phraseReplacementEnabled) {
for (auto&& unigram : filterredUserUnigrams) {
string value = unigram.keyValue.value;
string replacement = m_phraseReplacement.valueForKey(value);
if (replacement != "") {
unigram.keyValue.value = replacement;
}
unigrams.push_back(unigram);
}
} else {
unigrams = filterredUserUnigrams;
}
userUnigrams = filterAndTransformUnigrams(rawUserUnigrams, excludedValues, insertedValues);
}
if (m_languageModel.hasUnigramsForKey(key)) {
vector<Unigram> globalUnigrams = m_languageModel.unigramsForKey(key);
for (auto&& unigram : globalUnigrams) {
string value = unigram.keyValue.value;
if (excludedValues.find(value) == excludedValues.end() &&
userValues.find(value) == userValues.end()) {
if (m_phraseReplacementEnabled) {
string replacement = m_phraseReplacement.valueForKey(value);
if (replacement != "") {
unigram.keyValue.value = replacement;
}
}
unigrams.push_back(unigram);
}
}
vector<Unigram> rawGlobalUnigrams = m_languageModel.unigramsForKey(key);
allUnigrams = filterAndTransformUnigrams(rawGlobalUnigrams, excludedValues, insertedValues);
}
unigrams.insert(unigrams.begin(), userUnigrams.begin(), userUnigrams.end());
return unigrams;
allUnigrams.insert(allUnigrams.begin(), userUnigrams.begin(), userUnigrams.end());
return allUnigrams;
}
bool McBopomofoLM::hasUnigramsForKey(const string& key)
{
if (!m_excludedPhrases.hasUnigramsForKey(key)) {
return m_userPhrases.hasUnigramsForKey(key) ||
m_languageModel.hasUnigramsForKey(key);
return m_userPhrases.hasUnigramsForKey(key) || m_languageModel.hasUnigramsForKey(key);
}
return unigramsForKey(key).size() > 0;
@ -159,3 +121,52 @@ bool McBopomofoLM::phraseReplacementEnabled()
return m_phraseReplacementEnabled;
}
void McBopomofoLM::setExternalConverterEnabled(bool enabled)
{
m_externalConverterEnabled = enabled;
}
bool McBopomofoLM::externalConverterEnabled()
{
return m_externalConverterEnabled;
}
void McBopomofoLM::setExternalConverter(std::function<string(string)> externalConverter)
{
m_externalConverter = externalConverter;
}
const vector<Unigram> McBopomofoLM::filterAndTransformUnigrams(const vector<Unigram> unigrams, const unordered_set<string>& excludedValues, unordered_set<string>& insertedValues)
{
vector<Unigram> results;
for (auto&& unigram : unigrams) {
// excludedValues filters out the unigrams with the original value.
// insertedValues filters out the ones with the converted value
string originalValue = unigram.keyValue.value;
if (excludedValues.find(originalValue) != excludedValues.end()) {
continue;
}
string value = originalValue;
if (m_phraseReplacementEnabled) {
string replacement = m_phraseReplacement.valueForKey(value);
if (replacement != "") {
value = replacement;
}
}
if (m_externalConverterEnabled && m_externalConverter) {
string replacement = m_externalConverter(value);
value = replacement;
}
if (insertedValues.find(value) == insertedValues.end()) {
Unigram g;
g.keyValue.value = value;
g.keyValue.key = unigram.keyValue.key;
g.score = unigram.score;
results.push_back(g);
insertedValues.insert(value);
}
}
return results;
}

View File

@ -28,34 +28,91 @@
#include "UserPhrasesLM.h"
#include "ParselessLM.h"
#include "PhraseReplacementMap.h"
#include <unordered_set>
namespace McBopomofo {
using namespace Formosa::Gramambular;
/// McBopomofoLM is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases.
///
/// It is the primary model class that the input controller and grammer builder
/// of McBopomofo talk to. When the grammer builder starts to build a sentense
/// from a series of BPMF readings, it passes the readings to the model to see
/// if there are valid unigrams, and use returned unigrams to produce the final
/// results.
///
/// McBopomofoLM combine and transform the unigrams from the primary language
/// model and user phrases. The process is
///
/// 1) Get the original unigrams.
/// 2) Drop the unigrams whose value is contained in the exclusion map.
/// 3) Replace the values of the unigrams using the phrase replacement map.
/// 4) Replace the values of the unigrams using an external converter lambda.
/// 5) Drop the duplicated phrases.
///
/// The controller can ask the model to load the primary input method language
/// model while launching and to load the user phrases anytime if the custom
/// files are modified. It does not keep the reference of the data pathes but
/// you have to pass the paths when you ask it to do loading.
class McBopomofoLM : public LanguageModel {
public:
McBopomofoLM();
~McBopomofoLM();
void loadLanguageModel(const char* languageModelDataPath);
void loadUserPhrases(const char* userPhrasesDataPath,
const char* excludedPhrasesDataPath);
/// Asks to load the primary language model a the given path.
/// @param languageModelPath Thw path of the language model.
void loadLanguageModel(const char* languageModelPath);
/// Asks to load the user phrases and excluded phrases at the given path.
/// @param userPhrasesPath The path of user phrases.
/// @param excludedPhrasesPath The path of excluded phrases.
void loadUserPhrases(const char* userPhrasesPath, const char* excludedPhrasesPath);
/// Asks to load th phrase replacement table at the given path.
/// @param phraseReplacementPath The path of the phrase replacement table.
void loadPhraseReplacementMap(const char* phraseReplacementPath);
/// Not implemented since we do not have data to provide bigram function.
const vector<Bigram> bigramsForKeys(const string& preceedingKey, const string& key);
/// Returns a list of available unigram for the given key.
/// @param key A string represents the BPMF reading or a symbol key. For
/// example, it you pass "ㄇㄚ", it returns "嗎", "媽", and so on.
const vector<Unigram> unigramsForKey(const string& key);
/// If the model has unigrams for the given key.
/// @param key The key.
bool hasUnigramsForKey(const string& key);
/// Enables or disables phrase replacement.
void setPhraseReplacementEnabled(bool enabled);
/// If phrease replacement is enabled or not.
bool phraseReplacementEnabled();
/// Enables or disables the external converter.
void setExternalConverterEnabled(bool enabled);
/// If the external converted is enabled or not.
bool externalConverterEnabled();
/// Sets a lambda to let the values of unigrams could be converted by it.
void setExternalConverter(std::function<string(string)> externalConverter);
protected:
/// Filters and converts the input unigrams and return a new list of unigrams.
///
/// @param unigrams The unigrams to be processed.
/// @param excludedValues The values to excluded unigrams.
/// @param insertedValues The values for unigrams already in the results.
/// It helps to prevent duplicated unigrams. Please note that the method
/// has a side effect that it inserts values to `insertedValues`.
const vector<Unigram> filterAndTransformUnigrams(const vector<Unigram> unigrams,
const std::unordered_set<string>& excludedValues,
std::unordered_set<string>& insertedValues);
ParselessLM m_languageModel;
UserPhrasesLM m_userPhrases;
UserPhrasesLM m_excludedPhrases;
PhraseReplacementMap m_phraseReplacement;
bool m_phraseReplacementEnabled;
bool m_externalConverterEnabled;
std::function<string(string)> m_externalConverter;
};
};

View File

@ -243,6 +243,8 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
Preferences.keyboardLayout = KeyboardLayoutStandard;
}
_languageModel->setExternalConverterEnabled(Preferences.chineseConversionStyle == 1);
[(AppDelegate *)[NSApp delegate] checkForUpdate];
}
@ -275,12 +277,14 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
if ([value isKindOfClass:[NSString class]] && [value isEqual:kPlainBopomofoModeIdentifier]) {
newInputMode = kPlainBopomofoModeIdentifier;
newLanguageModel = [LanguageModelManager languageModelPlainBopomofo];
newLanguageModel->setPhraseReplacementEnabled(false);
}
else {
newInputMode = kBopomofoModeIdentifier;
newLanguageModel = [LanguageModelManager languageModelMcBopomofo];
newLanguageModel->setPhraseReplacementEnabled(Preferences.phraseReplacementEnabled);
}
newLanguageModel->setExternalConverterEnabled(Preferences.chineseConversionStyle == 1);
// Only apply the changes if the value is changed
if (![_inputMode isEqualToString:newInputMode]) {
@ -312,8 +316,16 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
#pragma mark - IMKServerInput protocol methods
- (NSString *)_convertToSimplifiedChinese:(NSString *)text
- (NSString *)_convertToSimplifiedChineseIfRequired:(NSString *)text
{
if (!Preferences.chineseConversionEnabled) {
return text;
}
if (Preferences.chineseConversionStyle == 1) {
return text;
}
if (Preferences.chineneConversionEngine == 1) {
return [VXHanConvert convertToSimplifiedFrom:text];
}
@ -333,11 +345,7 @@ static double FindHighestScore(const vector<NodeAnchor>& nodes, double epsilon)
}
// Chinese conversion.
NSString *buffer = _composingBuffer;
if (Preferences.chineseConversionEnabled) {
buffer = [self _convertToSimplifiedChinese:_composingBuffer];
}
NSString *buffer = [self _convertToSimplifiedChineseIfRequired:_composingBuffer];
// commit the text, clear the state
[client insertText:buffer replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
@ -483,10 +491,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
NodeAnchor &anchor = _walkedNodes[0];
NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()];
// Chinese conversion.
BOOL chineseConversionEnabled = Preferences.chineseConversionEnabled;
if (chineseConversionEnabled) {
popedText = [self _convertToSimplifiedChinese:popedText];
}
popedText = [self _convertToSimplifiedChineseIfRequired:popedText];
[client insertText:popedText replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
_builder->removeHeadReadings(anchor.spanningLength);
}
@ -1504,7 +1509,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; }
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
[Preferences tooglePhraseReplacementEnabled];
[Preferences toogleHalfWidthPunctuationEnabled];
#pragma GCC diagnostic pop
}

View File

@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)loadDataModels;
+ (void)loadUserPhrases;
+ (void)loadUserPhraseReplacement;
+ (void)setupDataModelValueConverter;
+ (BOOL)checkIfUserLanguageModelFilesExist;
+ (BOOL)writeUserPhrase:(NSString *)userPhrase;

View File

@ -4,6 +4,10 @@
#import <set>
#import "OVStringHelper.h"
#import "OVUTF8Helper.h"
#import "McBopomofo-Swift.h"
@import VXHanConvert;
@import OpenCCBridge;
using namespace std;
using namespace Formosa::Gramambular;
@ -43,6 +47,31 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, McBopomo
gLanguageModelMcBopomofo.loadPhraseReplacementMap([[self phraseReplacementDataPathMcBopomofo] UTF8String]);
}
+ (void)setupDataModelValueConverter
{
auto converter = [] (string input) {
if (!Preferences.chineseConversionEnabled) {
return input;
}
if (Preferences.chineseConversionStyle == 0) {
return input;
}
NSString *text = [NSString stringWithUTF8String:input.c_str()];
if (Preferences.chineneConversionEngine == 1) {
text = [VXHanConvert convertToSimplifiedFrom:text];
}
else {
text = [OpenCCBridge convertToSimplified:text];
}
return string(text.UTF8String);
};
gLanguageModelMcBopomofo.setExternalConverter(converter);
gLanguageModelPlainBopomofo.setExternalConverter(converter);
}
+ (BOOL)checkIfUserDataFolderExists
{
NSString *folderPath = [self dataFolderPath];

View File

@ -8,4 +8,5 @@
+ (void)loadDataModels;
+ (void)loadUserPhrases;
+ (void)loadUserPhraseReplacement;
+ (void)setupDataModelValueConverter;
@end

View File

@ -50,8 +50,9 @@ private let kEscToCleanInputBufferKey = "EscToCleanInputBuffer"
private let kCandidateTextFontName = "CandidateTextFontName"
private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName"
private let kCandidateKeys = "CandidateKeys"
private let kChineseConversionEngineKey = "ChineseConversionEngine"
private let kPhraseReplacementEnabledKey = "PhraseReplacementEnabled"
private let kChineseConversionEngineKey = "ChineseConversionEngine"
private let kChineseConversionStyle = "ChineseConversionStyle"
private let kDefaultCandidateListTextSize: CGFloat = 16
private let kMinKeyLabelSize: CGFloat = 10
@ -217,6 +218,20 @@ struct ComposingKeys {
}
}
@objc enum ChineseConversionStyle: Int {
case output
case model
var name: String {
switch (self) {
case .output:
return "output"
case .model:
return "model"
}
}
}
// MARK: -
class Preferences: NSObject {
@ -285,13 +300,6 @@ class Preferences: NSObject {
kDefaultKeys
}
@UserDefault(key: kChineseConversionEngineKey, defaultValue: 0)
@objc static var chineneConversionEngine: Int
@objc static var chineneConversionEngineName: String? {
return ChineseConversionEngine(rawValue: chineneConversionEngine)?.name
}
@UserDefault(key: kPhraseReplacementEnabledKey, defaultValue: false)
@objc static var phraseReplacementEnabled: Bool
@ -300,4 +308,26 @@ class Preferences: NSObject {
return phraseReplacementEnabled;
}
/// The conversion engine.
///
/// - 0: OpenCC
/// - 1: VXHanConvert
@UserDefault(key: kChineseConversionEngineKey, defaultValue: 0)
@objc static var chineneConversionEngine: Int
@objc static var chineneConversionEngineName: String? {
return ChineseConversionEngine(rawValue: chineneConversionEngine)?.name
}
/// The conversion style.
///
/// - 0: convert the output
/// - 1: convert the phrase models.
@UserDefault(key: kChineseConversionStyle, defaultValue: 0)
@objc static var chineseConversionStyle: Int
@objc static var chineseConversionStyleName: String? {
return ChineseConversionStyle(rawValue: chineseConversionStyle)?.name
}
}

View File

@ -19,14 +19,14 @@
<window title="注音偏好設定" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" animationBehavior="default" id="1" userLabel="Window - Preferences">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="489" y="334" width="446" height="456"/>
<rect key="contentRect" x="489" y="334" width="446" height="479"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
<view key="contentView" id="2">
<rect key="frame" x="0.0" y="0.0" width="446" height="456"/>
<rect key="frame" x="0.0" y="0.0" width="446" height="479"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3">
<rect key="frame" x="181" y="411" width="125" height="26"/>
<rect key="frame" x="181" y="434" width="125" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="4">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -49,7 +49,7 @@
</popUpButtonCell>
</popUpButton>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="11">
<rect key="frame" x="83" y="417" width="95" height="17"/>
<rect key="frame" x="83" y="440" width="95" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="注音鍵盤配置:" id="12">
<font key="font" metaFont="system"/>
@ -58,7 +58,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="112">
<rect key="frame" x="181" y="374" width="149" height="26"/>
<rect key="frame" x="181" y="397" width="149" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="115">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -70,7 +70,7 @@
</connections>
</popUpButton>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="13">
<rect key="frame" x="18" y="254" width="160" height="17"/>
<rect key="frame" x="18" y="277" width="160" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="選字時,候選詞起算點在:" id="14">
<font key="font" metaFont="system"/>
@ -79,7 +79,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="23">
<rect key="frame" x="40" y="208" width="138" height="17"/>
<rect key="frame" x="40" y="231" width="138" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="候選詞呈現方式:" id="24">
<font key="font" metaFont="system"/>
@ -88,7 +88,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="28">
<rect key="frame" x="70" y="161" width="108" height="17"/>
<rect key="frame" x="70" y="184" width="108" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="選字窗文字大小:" id="29">
<font key="font" metaFont="system"/>
@ -97,7 +97,7 @@
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="15">
<rect key="frame" x="184" y="233" width="184" height="38"/>
<rect key="frame" x="184" y="256" width="184" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="184" height="18"/>
@ -123,7 +123,7 @@
</connections>
</matrix>
<matrix verticalHuggingPriority="750" fixedFrame="YES" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="19">
<rect key="frame" x="184" y="187" width="207" height="38"/>
<rect key="frame" x="184" y="210" width="207" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="207" height="18"/>
@ -149,7 +149,7 @@
</connections>
</matrix>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="90">
<rect key="frame" x="181" y="153" width="86" height="26"/>
<rect key="frame" x="181" y="176" width="86" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="18" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="18" imageScaling="proportionallyDown" inset="2" selectedItem="96" id="91">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -172,7 +172,7 @@
</connections>
</popUpButton>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="109">
<rect key="frame" x="182" y="313" width="231" height="18"/>
<rect key="frame" x="182" y="336" width="231" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="使用空白鍵選字" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="110">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -183,7 +183,7 @@
</connections>
</button>
<comboBox verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bQw-gl-dUP">
<rect key="frame" x="184" y="340" width="206" height="25"/>
<rect key="frame" x="184" y="363" width="206" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="mNx-Jy-SJB">
<font key="font" metaFont="system"/>
@ -200,7 +200,7 @@
</connections>
</comboBox>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8KJ-n8-0Wd">
<rect key="frame" x="70" y="344" width="108" height="17"/>
<rect key="frame" x="70" y="367" width="108" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="選字鍵:" id="eBT-Tv-vED">
<font key="font" metaFont="system"/>
@ -209,7 +209,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" id="OrA-WH-e5x">
<rect key="frame" x="182" y="290" width="231" height="20"/>
<rect key="frame" x="182" y="313" width="231" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="按下 ESC 會清除整個輸入緩衝區" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="7gv-k3-Mbt">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -220,7 +220,7 @@
</connections>
</button>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="113">
<rect key="frame" x="70" y="380" width="108" height="17"/>
<rect key="frame" x="70" y="403" width="108" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="英數字鍵盤配置:" id="114">
<font key="font" metaFont="system"/>
@ -229,16 +229,16 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="p0T-C5-tAj">
<rect key="frame" x="18" y="106" width="160" height="17"/>
<rect key="frame" x="18" y="41" width="160" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="中文简繁转换引擎:" id="jd1-sb-9rA">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="中文簡繁轉換引擎:" id="jd1-sb-9rA">
<font key="font" size="13" name=".PingFangTC-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gYl-c0-mKc">
<rect key="frame" x="184" y="85" width="213" height="38"/>
<rect key="frame" x="184" y="20" width="213" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="206" height="18"/>
@ -263,12 +263,43 @@
<binding destination="32" name="selectedTag" keyPath="values.ChineseConversionEngine" id="RYU-BR-yvD"/>
</connections>
</matrix>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="5B4-1Z-6Mz">
<rect key="frame" x="20" y="136" width="406" height="5"/>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="84v-v7-84f">
<rect key="frame" x="18" y="87" width="160" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="中文簡繁轉換模式:" id="8fJ-OC-sPK">
<font key="font" size="13" name=".PingFangTC-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<matrix verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ml5-hh-vEa">
<rect key="frame" x="184" y="66" width="213" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="206" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="kFb-B9-1bM">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="轉換輸出結果" imagePosition="left" alignment="left" state="on" inset="2" id="xoK-fI-RZZ">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="轉換詞庫" imagePosition="left" alignment="left" tag="1" inset="2" id="hHx-SU-C1Q">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</column>
</cells>
<connections>
<binding destination="32" name="selectedTag" keyPath="values.ChineseConversionStyle" id="lEn-wb-L3k"/>
</connections>
</matrix>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ooW-fi-BVN">
<rect key="frame" x="182" y="19" width="148" height="18"/>
<rect key="frame" x="182" y="125" width="148" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="定期檢查是否有新版" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="rcF-Ys-CM3">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -278,9 +309,13 @@
<binding destination="32" name="value" keyPath="values.CheckUpdateAutomatically" id="drP-1F-7NA"/>
</connections>
</button>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="5B4-1Z-6Mz">
<rect key="frame" x="28" y="115" width="406" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
</subviews>
</view>
<point key="canvasLocation" x="189" y="242"/>
<point key="canvasLocation" x="189" y="253.5"/>
</window>
<userDefaultsController representsSharedInstance="YES" id="32"/>
</objects>