mgrLM // Swiftify everything except Cpp-related stuff.
- This is not a complete swiftification since all ObjCpp-related parts are not swiftifiable. - Rename invalid folder path target in lieu of removal. - Let dataFolderPath() always ensure trailing slash. - Also added process of verifying folder write access. - Also simplify chkUserLMFilesExist(InputMode). - Also enveloped LMConsolidator into an ObjC command in order to swiftify and refactor writeUserPhrase().
This commit is contained in:
parent
649a680fd6
commit
4b87138dc0
|
@ -85,7 +85,9 @@ import Cocoa
|
||||||
// MARK: - Open a phrase data file.
|
// MARK: - Open a phrase data file.
|
||||||
static func openPhraseFile(userFileAt path: String) {
|
static func openPhraseFile(userFileAt path: String) {
|
||||||
func checkIfUserFilesExist() -> Bool {
|
func checkIfUserFilesExist() -> Bool {
|
||||||
if !mgrLangModel.checkIfUserLanguageModelFilesExist() {
|
if !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHS)
|
||||||
|
|| !mgrLangModel.chkUserLMFilesExist(InputMode.imeModeCHT)
|
||||||
|
{
|
||||||
let content = String(
|
let content = String(
|
||||||
format: NSLocalizedString(
|
format: NSLocalizedString(
|
||||||
"Please check the permission at \"%@\".", comment: ""),
|
"Please check the permission at \"%@\".", comment: ""),
|
||||||
|
|
|
@ -35,29 +35,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
+ (void)loadUserPhrases;
|
+ (void)loadUserPhrases;
|
||||||
+ (void)loadUserAssociatedPhrases;
|
+ (void)loadUserAssociatedPhrases;
|
||||||
+ (void)loadUserPhraseReplacement;
|
+ (void)loadUserPhraseReplacement;
|
||||||
+ (BOOL)checkIfUserLanguageModelFilesExist;
|
|
||||||
+ (BOOL)checkIfUserDataFolderExists;
|
|
||||||
+ (BOOL)checkIfSpecifiedUserDataFolderValid:(NSString *)folderPath;
|
|
||||||
+ (NSString *)dataFolderPath:(bool)isDefaultFolder NS_SWIFT_NAME(dataFolderPath(isDefaultFolder:));
|
|
||||||
|
|
||||||
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
|
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
|
||||||
inputMode:(InputMode)mode
|
inputMode:(InputMode)mode
|
||||||
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:));
|
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:));
|
||||||
+ (BOOL)writeUserPhrase:(NSString *)userPhrase
|
+ (void)consolidateGivenFile:(NSString *)path shouldCheckPragma:(BOOL)shouldCheckPragma;
|
||||||
inputMode:(InputMode)mode
|
|
||||||
areWeDuplicating:(BOOL)areWeDuplicating
|
|
||||||
areWeDeleting:(BOOL)areWeDeleting;
|
|
||||||
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled;
|
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled;
|
||||||
+ (void)setCNSEnabled:(BOOL)cnsEnabled;
|
+ (void)setCNSEnabled:(BOOL)cnsEnabled;
|
||||||
+ (void)setSymbolEnabled:(BOOL)symbolEnabled;
|
+ (void)setSymbolEnabled:(BOOL)symbolEnabled;
|
||||||
|
|
||||||
+ (NSString *)specifyBundleDataPath:(NSString *)filename;
|
|
||||||
+ (NSString *)userPhrasesDataPath:(InputMode)mode;
|
|
||||||
+ (NSString *)userSymbolDataPath:(InputMode)mode;
|
|
||||||
+ (NSString *)userAssociatedPhrasesDataPath:(InputMode)mode;
|
|
||||||
+ (NSString *)excludedPhrasesDataPath:(InputMode)mode;
|
|
||||||
+ (NSString *)phraseReplacementDataPath:(InputMode)mode;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/// The following methods are merely for testing.
|
/// The following methods are merely for testing.
|
||||||
|
|
|
@ -37,28 +37,16 @@ static vChewing::LMInstantiator gLangModelCHS;
|
||||||
static vChewing::UserOverrideModel gUserOverrideModelCHT(kUserOverrideModelCapacity, kObservedOverrideHalflife);
|
static vChewing::UserOverrideModel gUserOverrideModelCHT(kUserOverrideModelCapacity, kObservedOverrideHalflife);
|
||||||
static vChewing::UserOverrideModel gUserOverrideModelCHS(kUserOverrideModelCapacity, kObservedOverrideHalflife);
|
static vChewing::UserOverrideModel gUserOverrideModelCHS(kUserOverrideModelCapacity, kObservedOverrideHalflife);
|
||||||
|
|
||||||
static NSString *const kUserDataTemplateName = @"template-data";
|
|
||||||
static NSString *const kUserAssDataTemplateName = @"template-data";
|
|
||||||
static NSString *const kExcludedPhrasesvChewingTemplateName = @"template-exclude-phrases";
|
|
||||||
static NSString *const kPhraseReplacementTemplateName = @"template-phrases-replacement";
|
|
||||||
static NSString *const kUserSymbolDataTemplateName = @"template-user-symbol-data";
|
|
||||||
static NSString *const kTemplateExtension = @".txt";
|
|
||||||
|
|
||||||
@implementation mgrLangModel
|
@implementation mgrLangModel
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing::LMInstantiator &lm)
|
static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing::LMInstantiator &lm)
|
||||||
{
|
{
|
||||||
Class cls = NSClassFromString(@"ctlInputMethod");
|
NSString *dataPath = [mgrLangModel getBundleDataPath:filenameWithoutExtension];
|
||||||
NSString *dataPath = [[NSBundle bundleForClass:cls] pathForResource:filenameWithoutExtension ofType:@"txt"];
|
|
||||||
lm.loadLanguageModel([dataPath UTF8String]);
|
lm.loadLanguageModel([dataPath UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSString *)specifyBundleDataPath:(NSString *)filenameWithoutExtension;
|
// 這個函數無法遷移至 Swift
|
||||||
{
|
|
||||||
Class cls = NSClassFromString(@"ctlInputMethod");
|
|
||||||
return [[NSBundle bundleForClass:cls] pathForResource:filenameWithoutExtension ofType:@"txt"];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)loadDataModels
|
+ (void)loadDataModels
|
||||||
{
|
{
|
||||||
if (!gLangModelCHT.isDataModelLoaded())
|
if (!gLangModelCHT.isDataModelLoaded())
|
||||||
|
@ -67,15 +55,15 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isMiscDataLoaded())
|
if (!gLangModelCHT.isMiscDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadMiscData([[self specifyBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
gLangModelCHT.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isSymbolDataLoaded())
|
if (!gLangModelCHT.isSymbolDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadSymbolData([[self specifyBundleDataPath:@"data-symbols"] UTF8String]);
|
gLangModelCHT.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isCNSDataLoaded())
|
if (!gLangModelCHT.isCNSDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadCNSData([[self specifyBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
gLangModelCHT.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
||||||
}
|
}
|
||||||
// -----------------
|
// -----------------
|
||||||
if (!gLangModelCHS.isDataModelLoaded())
|
if (!gLangModelCHS.isDataModelLoaded())
|
||||||
|
@ -84,18 +72,19 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isMiscDataLoaded())
|
if (!gLangModelCHS.isMiscDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadMiscData([[self specifyBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
gLangModelCHS.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isSymbolDataLoaded())
|
if (!gLangModelCHS.isSymbolDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadSymbolData([[self specifyBundleDataPath:@"data-symbols"] UTF8String]);
|
gLangModelCHS.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isCNSDataLoaded())
|
if (!gLangModelCHS.isCNSDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadCNSData([[self specifyBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
gLangModelCHS.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)loadDataModel:(InputMode)mode
|
+ (void)loadDataModel:(InputMode)mode
|
||||||
{
|
{
|
||||||
if ([mode isEqualToString:imeModeCHT])
|
if ([mode isEqualToString:imeModeCHT])
|
||||||
|
@ -106,15 +95,15 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isMiscDataLoaded())
|
if (!gLangModelCHT.isMiscDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadMiscData([[self specifyBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
gLangModelCHT.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isSymbolDataLoaded())
|
if (!gLangModelCHT.isSymbolDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadSymbolData([[self specifyBundleDataPath:@"data-symbols"] UTF8String]);
|
gLangModelCHT.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHT.isCNSDataLoaded())
|
if (!gLangModelCHT.isCNSDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadCNSData([[self specifyBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
gLangModelCHT.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,19 +115,20 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isMiscDataLoaded())
|
if (!gLangModelCHS.isMiscDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadMiscData([[self specifyBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
gLangModelCHS.loadMiscData([[self getBundleDataPath:@"data-zhuyinwen"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isSymbolDataLoaded())
|
if (!gLangModelCHS.isSymbolDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadSymbolData([[self specifyBundleDataPath:@"data-symbols"] UTF8String]);
|
gLangModelCHS.loadSymbolData([[self getBundleDataPath:@"data-symbols"] UTF8String]);
|
||||||
}
|
}
|
||||||
if (!gLangModelCHS.isCNSDataLoaded())
|
if (!gLangModelCHS.isCNSDataLoaded())
|
||||||
{
|
{
|
||||||
gLangModelCHS.loadCNSData([[self specifyBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
gLangModelCHS.loadCNSData([[self getBundleDataPath:@"char-kanji-cns"] UTF8String]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)loadUserPhrases
|
+ (void)loadUserPhrases
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadUserPhrases([[self userPhrasesDataPath:imeModeCHT] UTF8String],
|
gLangModelCHT.loadUserPhrases([[self userPhrasesDataPath:imeModeCHT] UTF8String],
|
||||||
|
@ -149,136 +139,21 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
gLangModelCHS.loadUserSymbolData([[self userSymbolDataPath:imeModeCHS] UTF8String]);
|
gLangModelCHS.loadUserSymbolData([[self userSymbolDataPath:imeModeCHS] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)loadUserAssociatedPhrases
|
+ (void)loadUserAssociatedPhrases
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHT] UTF8String]);
|
gLangModelCHT.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHT] UTF8String]);
|
||||||
gLangModelCHS.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHS] UTF8String]);
|
gLangModelCHS.loadUserAssociatedPhrases([[self userAssociatedPhrasesDataPath:imeModeCHS] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)loadUserPhraseReplacement
|
+ (void)loadUserPhraseReplacement
|
||||||
{
|
{
|
||||||
gLangModelCHT.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHT] UTF8String]);
|
gLangModelCHT.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHT] UTF8String]);
|
||||||
gLangModelCHS.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHS] UTF8String]);
|
gLangModelCHS.loadPhraseReplacementMap([[self phraseReplacementDataPath:imeModeCHS] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)checkIfUserDataFolderExists
|
// 這個函數無法遷移至 Swift
|
||||||
{
|
|
||||||
NSString *folderPath = [self dataFolderPath:false];
|
|
||||||
BOOL isFolder = NO;
|
|
||||||
BOOL folderExist = [[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:&isFolder];
|
|
||||||
if (folderExist && !isFolder)
|
|
||||||
{
|
|
||||||
NSError *error = nil;
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:folderPath error:&error];
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
NSLog(@"Failed to remove folder %@", error);
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
folderExist = NO;
|
|
||||||
}
|
|
||||||
if (!folderExist)
|
|
||||||
{
|
|
||||||
NSError *error = nil;
|
|
||||||
[[NSFileManager defaultManager] createDirectoryAtPath:folderPath
|
|
||||||
withIntermediateDirectories:YES
|
|
||||||
attributes:nil
|
|
||||||
error:&error];
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
NSLog(@"Failed to create folder %@", error);
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)checkIfSpecifiedUserDataFolderValid:(NSString *)folderPath
|
|
||||||
{
|
|
||||||
BOOL isFolder = NO;
|
|
||||||
BOOL folderExist = [[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:&isFolder];
|
|
||||||
if ((folderExist && !isFolder) || (!folderExist))
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)ensureFileExists:(NSString *)filePath
|
|
||||||
populateWithTemplate:(NSString *)templateBasename
|
|
||||||
extension:(NSString *)ext
|
|
||||||
{
|
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
|
|
||||||
{
|
|
||||||
|
|
||||||
NSURL *templateURL = [[NSBundle mainBundle] URLForResource:templateBasename withExtension:ext];
|
|
||||||
NSData *templateData;
|
|
||||||
if (templateURL)
|
|
||||||
{
|
|
||||||
templateData = [NSData dataWithContentsOfURL:templateURL];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
templateData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL result = [templateData writeToFile:filePath atomically:YES];
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
NSLog(@"Failed to write file");
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)checkIfUserLanguageModelFilesExist
|
|
||||||
{
|
|
||||||
if (![self checkIfUserDataFolderExists])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userPhrasesDataPath:imeModeCHS]
|
|
||||||
populateWithTemplate:kUserDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userPhrasesDataPath:imeModeCHT]
|
|
||||||
populateWithTemplate:kUserDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userAssociatedPhrasesDataPath:imeModeCHS]
|
|
||||||
populateWithTemplate:kUserAssDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userAssociatedPhrasesDataPath:imeModeCHT]
|
|
||||||
populateWithTemplate:kUserAssDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self excludedPhrasesDataPath:imeModeCHS]
|
|
||||||
populateWithTemplate:kExcludedPhrasesvChewingTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self excludedPhrasesDataPath:imeModeCHT]
|
|
||||||
populateWithTemplate:kExcludedPhrasesvChewingTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self phraseReplacementDataPath:imeModeCHS]
|
|
||||||
populateWithTemplate:kPhraseReplacementTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self phraseReplacementDataPath:imeModeCHT]
|
|
||||||
populateWithTemplate:kPhraseReplacementTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userSymbolDataPath:imeModeCHT]
|
|
||||||
populateWithTemplate:kUserSymbolDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
if (![self ensureFileExists:[self userSymbolDataPath:imeModeCHS]
|
|
||||||
populateWithTemplate:kUserSymbolDataTemplateName
|
|
||||||
extension:kTemplateExtension])
|
|
||||||
return NO;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
|
+ (BOOL)checkIfUserPhraseExist:(NSString *)userPhrase
|
||||||
inputMode:(InputMode)mode
|
inputMode:(InputMode)mode
|
||||||
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:))
|
key:(NSString *)key NS_SWIFT_NAME(checkIfUserPhraseExist(userPhrase:mode:key:))
|
||||||
|
@ -297,144 +172,51 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, vChewing
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (BOOL)writeUserPhrase:(NSString *)userPhrase
|
// 這個函數無法遷移至 Swift
|
||||||
inputMode:(InputMode)mode
|
+ (void)consolidateGivenFile:(NSString *)path shouldCheckPragma:(BOOL)shouldCheckPragma
|
||||||
areWeDuplicating:(BOOL)areWeDuplicating
|
|
||||||
areWeDeleting:(BOOL)areWeDeleting
|
|
||||||
{
|
{
|
||||||
if (![self checkIfUserLanguageModelFilesExist])
|
vChewing::LMConsolidator::ConsolidateContent([path UTF8String], shouldCheckPragma);
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// BOOL addLineBreakAtFront = NO;
|
|
||||||
NSString *path = areWeDeleting ? [self excludedPhrasesDataPath:mode] : [self userPhrasesDataPath:mode];
|
|
||||||
|
|
||||||
NSMutableString *currentMarkedPhrase = [NSMutableString string];
|
|
||||||
// if (addLineBreakAtFront) {
|
|
||||||
// [currentMarkedPhrase appendString:@"\n"];
|
|
||||||
// }
|
|
||||||
[currentMarkedPhrase appendString:userPhrase];
|
|
||||||
if (areWeDuplicating && !areWeDeleting)
|
|
||||||
{
|
|
||||||
// Do not use ASCII characters to comment here.
|
|
||||||
// Otherwise, it will be scrambled by cnvHYPYtoBPMF module shipped in the vChewing Phrase Editor.
|
|
||||||
[currentMarkedPhrase appendString:@"\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎"];
|
|
||||||
}
|
|
||||||
[currentMarkedPhrase appendString:@"\n"];
|
|
||||||
|
|
||||||
NSFileHandle *writeFile = [NSFileHandle fileHandleForUpdatingAtPath:path];
|
|
||||||
if (!writeFile)
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
[writeFile seekToEndOfFile];
|
|
||||||
NSData *data = [currentMarkedPhrase dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
[writeFile writeData:data];
|
|
||||||
[writeFile closeFile];
|
|
||||||
|
|
||||||
// We enforce the format consolidation here, since the pragma header will let the UserPhraseLM bypasses the
|
|
||||||
// consolidating process on load.
|
|
||||||
vChewing::LMConsolidator::ConsolidateContent([path UTF8String], false);
|
|
||||||
|
|
||||||
// We use FSEventStream to monitor the change of the user phrase folder,
|
|
||||||
// so we don't have to load data here unless FSEventStream is disabled by user.
|
|
||||||
if (!mgrPrefs.shouldAutoReloadUserDataFiles)
|
|
||||||
{
|
|
||||||
[self loadUserPhrases];
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)dataFolderPath:(bool)isDefaultFolder
|
|
||||||
{
|
|
||||||
// 此處不能用「~」來取代當前使用者目錄名稱。不然的話,一旦輸入法被系統的沙箱干預的話,則反而會定位到沙箱目錄內。
|
|
||||||
NSString *appSupportPath = [NSFileManager.defaultManager URLsForDirectory:NSApplicationSupportDirectory
|
|
||||||
inDomains:NSUserDomainMask][0].path;
|
|
||||||
NSString *userDictPath = [appSupportPath stringByAppendingPathComponent:@"vChewing"].stringByExpandingTildeInPath;
|
|
||||||
if (mgrPrefs.userDataFolderSpecified.stringByExpandingTildeInPath == userDictPath || isDefaultFolder)
|
|
||||||
{
|
|
||||||
return userDictPath;
|
|
||||||
}
|
|
||||||
if ([mgrPrefs ifSpecifiedUserDataPathExistsInPlist])
|
|
||||||
{
|
|
||||||
if ([self checkIfSpecifiedUserDataFolderValid:mgrPrefs.userDataFolderSpecified.stringByExpandingTildeInPath])
|
|
||||||
{
|
|
||||||
return mgrPrefs.userDataFolderSpecified.stringByExpandingTildeInPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[NSUserDefaults.standardUserDefaults removeObjectForKey:@"UserDataFolderSpecified"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return userDictPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)userPhrasesDataPath:(InputMode)mode;
|
|
||||||
{
|
|
||||||
NSString *fileName = [mode isEqualToString:imeModeCHT] ? @"userdata-cht.txt" : @"userdata-chs.txt";
|
|
||||||
return [[self dataFolderPath:false] stringByAppendingPathComponent:fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)userSymbolDataPath:(InputMode)mode;
|
|
||||||
{
|
|
||||||
NSString *fileName =
|
|
||||||
[mode isEqualToString:imeModeCHT] ? @"usersymbolphrases-cht.txt" : @"usersymbolphrases-chs.txt";
|
|
||||||
return [[self dataFolderPath:false] stringByAppendingPathComponent:fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)userAssociatedPhrasesDataPath:(InputMode)mode;
|
|
||||||
{
|
|
||||||
NSString *fileName =
|
|
||||||
[mode isEqualToString:imeModeCHT] ? @"associatedPhrases-cht.txt" : @"associatedPhrases-chs.txt";
|
|
||||||
return [[self dataFolderPath:false] stringByAppendingPathComponent:fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)excludedPhrasesDataPath:(InputMode)mode;
|
|
||||||
{
|
|
||||||
NSString *fileName = [mode isEqualToString:imeModeCHT] ? @"exclude-phrases-cht.txt" : @"exclude-phrases-chs.txt";
|
|
||||||
return [[self dataFolderPath:false] stringByAppendingPathComponent:fileName];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)phraseReplacementDataPath:(InputMode)mode;
|
|
||||||
{
|
|
||||||
NSString *fileName =
|
|
||||||
[mode isEqualToString:imeModeCHT] ? @"phrases-replacement-cht.txt" : @"phrases-replacement-chs.txt";
|
|
||||||
return [[self dataFolderPath:false] stringByAppendingPathComponent:fileName];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (vChewing::LMInstantiator *)lmCHT
|
+ (vChewing::LMInstantiator *)lmCHT
|
||||||
{
|
{
|
||||||
return &gLangModelCHT;
|
return &gLangModelCHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (vChewing::LMInstantiator *)lmCHS
|
+ (vChewing::LMInstantiator *)lmCHS
|
||||||
{
|
{
|
||||||
return &gLangModelCHS;
|
return &gLangModelCHS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (vChewing::UserOverrideModel *)userOverrideModelCHT
|
+ (vChewing::UserOverrideModel *)userOverrideModelCHT
|
||||||
{
|
{
|
||||||
return &gUserOverrideModelCHT;
|
return &gUserOverrideModelCHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (vChewing::UserOverrideModel *)userOverrideModelCHS
|
+ (vChewing::UserOverrideModel *)userOverrideModelCHS
|
||||||
{
|
{
|
||||||
return &gUserOverrideModelCHS;
|
return &gUserOverrideModelCHS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled
|
+ (void)setPhraseReplacementEnabled:(BOOL)phraseReplacementEnabled
|
||||||
{
|
{
|
||||||
gLangModelCHT.setPhraseReplacementEnabled(phraseReplacementEnabled);
|
gLangModelCHT.setPhraseReplacementEnabled(phraseReplacementEnabled);
|
||||||
gLangModelCHS.setPhraseReplacementEnabled(phraseReplacementEnabled);
|
gLangModelCHS.setPhraseReplacementEnabled(phraseReplacementEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)setCNSEnabled:(BOOL)cnsEnabled
|
+ (void)setCNSEnabled:(BOOL)cnsEnabled
|
||||||
{
|
{
|
||||||
gLangModelCHT.setCNSEnabled(cnsEnabled);
|
gLangModelCHT.setCNSEnabled(cnsEnabled);
|
||||||
gLangModelCHS.setCNSEnabled(cnsEnabled);
|
gLangModelCHS.setCNSEnabled(cnsEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 這個函數無法遷移至 Swift
|
||||||
+ (void)setSymbolEnabled:(BOOL)symbolEnabled
|
+ (void)setSymbolEnabled:(BOOL)symbolEnabled
|
||||||
{
|
{
|
||||||
gLangModelCHT.setSymbolEnabled(symbolEnabled);
|
gLangModelCHT.setSymbolEnabled(symbolEnabled);
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
// Copyright (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
/*
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
1. The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
2. No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
requirements above.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
@objc extension mgrLangModel {
|
||||||
|
|
||||||
|
// MARK: - 獲取當前輸入法封包內的原廠核心語彙檔案所在路徑
|
||||||
|
static func getBundleDataPath(_ filenameSansExt: String) -> String {
|
||||||
|
return Bundle.main.path(forResource: filenameSansExt, ofType: "txt")!
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 使用者語彙檔案的具體檔案名稱路徑定義
|
||||||
|
// Swift 的 appendingPathComponent 需要藉由 URL 完成,最後再用 .path 轉為路徑。
|
||||||
|
|
||||||
|
static func userPhrasesDataPath(_ mode: InputMode) -> String {
|
||||||
|
let fileName = (mode == InputMode.imeModeCHT) ? "userdata-cht.txt" : "userdata-chs.txt"
|
||||||
|
return URL(fileURLWithPath: self.dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
|
||||||
|
}
|
||||||
|
|
||||||
|
static func userSymbolDataPath(_ mode: InputMode) -> String {
|
||||||
|
let fileName = (mode == InputMode.imeModeCHT) ? "usersymbolphrases-cht.txt" : "usersymbolphrases-chs.txt"
|
||||||
|
return URL(fileURLWithPath: self.dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
|
||||||
|
}
|
||||||
|
|
||||||
|
static func userAssociatedPhrasesDataPath(_ mode: InputMode) -> String {
|
||||||
|
let fileName = (mode == InputMode.imeModeCHT) ? "associatedPhrases-cht.txt" : "associatedPhrases-chs.txt"
|
||||||
|
return URL(fileURLWithPath: self.dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
|
||||||
|
}
|
||||||
|
|
||||||
|
static func excludedPhrasesDataPath(_ mode: InputMode) -> String {
|
||||||
|
let fileName = (mode == InputMode.imeModeCHT) ? "exclude-phrases-cht.txt" : "exclude-phrases-chs.txt"
|
||||||
|
return URL(fileURLWithPath: self.dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
|
||||||
|
}
|
||||||
|
|
||||||
|
static func phraseReplacementDataPath(_ mode: InputMode) -> String {
|
||||||
|
let fileName = (mode == InputMode.imeModeCHT) ? "phrases-replacement-cht.txt" : "phrases-replacement-chs.txt"
|
||||||
|
return URL(fileURLWithPath: self.dataFolderPath(isDefaultFolder: false)).appendingPathComponent(fileName).path
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 檢查具體的使用者語彙檔案是否存在
|
||||||
|
|
||||||
|
static func ensureFileExists(
|
||||||
|
_ filePath: String, populateWithTemplate templateBasename: String = "1145141919810",
|
||||||
|
extension ext: String = "txt"
|
||||||
|
) -> Bool {
|
||||||
|
if !FileManager.default.fileExists(atPath: filePath) {
|
||||||
|
let templateURL = Bundle.main.url(forResource: templateBasename, withExtension: ext)
|
||||||
|
var templateData = Data("".utf8)
|
||||||
|
if templateBasename != "" {
|
||||||
|
do {
|
||||||
|
try templateData = Data(contentsOf: templateURL ?? URL(fileURLWithPath: ""))
|
||||||
|
} catch {
|
||||||
|
templateData = Data("".utf8)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try templateData.write(to: URL(fileURLWithPath: filePath))
|
||||||
|
} catch {
|
||||||
|
IME.prtDebugIntel("Failed to write file")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
static func chkUserLMFilesExist(_ mode: InputMode) -> Bool {
|
||||||
|
if !self.checkIfUserDataFolderExists() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !ensureFileExists(userPhrasesDataPath(mode))
|
||||||
|
|| !ensureFileExists(userAssociatedPhrasesDataPath(mode))
|
||||||
|
|| !ensureFileExists(excludedPhrasesDataPath(mode))
|
||||||
|
|| !ensureFileExists(phraseReplacementDataPath(mode))
|
||||||
|
|| !ensureFileExists(userSymbolDataPath(mode))
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 使用者語彙檔案專用目錄的合規性檢查
|
||||||
|
|
||||||
|
// 一次性檢查給定的目錄是否存在寫入合規性(僅用於偏好設定檢查等初步檢查場合,不做任何糾偏行為)
|
||||||
|
static func checkIfSpecifiedUserDataFolderValid(_ folderPath: String?) -> Bool {
|
||||||
|
var isFolder = ObjCBool(false)
|
||||||
|
let folderExist = FileManager.default.fileExists(atPath: folderPath ?? "", isDirectory: &isFolder)
|
||||||
|
// The above "&" mutates the "isFolder" value to the real one received by the "folderExist".
|
||||||
|
|
||||||
|
// 路徑沒有結尾斜槓的話,會導致目錄合規性判定失準。
|
||||||
|
// 出於每個型別每個函數的自我責任原則,這裡多檢查一遍也不壞。
|
||||||
|
var folderPath = folderPath // Convert the incoming constant to a variable.
|
||||||
|
if isFolder.boolValue {
|
||||||
|
folderPath?.ensureTrailingSlash()
|
||||||
|
}
|
||||||
|
let isFolderWritable = FileManager.default.isWritableFile(atPath: folderPath ?? "")
|
||||||
|
|
||||||
|
if ((folderExist && !isFolder.boolValue) || !folderExist) || !isFolderWritable {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⚠︎ 私有函數:檢查且糾偏,不接受任何傳入變數。該函數不用於其他型別。
|
||||||
|
// 待辦事項:擇日合併至另一個同類型的函數當中。
|
||||||
|
static func checkIfUserDataFolderExists() -> Bool {
|
||||||
|
let folderPath = mgrLangModel.dataFolderPath(isDefaultFolder: false)
|
||||||
|
var isFolder = ObjCBool(false)
|
||||||
|
var folderExist = FileManager.default.fileExists(atPath: folderPath, isDirectory: &isFolder)
|
||||||
|
// The above "&" mutates the "isFolder" value to the real one received by the "folderExist".
|
||||||
|
// 發現目標路徑不是目錄的話:
|
||||||
|
// 如果要找的目標路徑是原廠目標路徑的話,先將這個路徑的所指對象更名、再認為目錄不存在。
|
||||||
|
// 如果要找的目標路徑不是原廠目標路徑的話,則直接報錯。
|
||||||
|
if folderExist && !isFolder.boolValue {
|
||||||
|
do {
|
||||||
|
if self.dataFolderPath(isDefaultFolder: false)
|
||||||
|
== self.dataFolderPath(isDefaultFolder: true)
|
||||||
|
{
|
||||||
|
let formatter = DateFormatter.init()
|
||||||
|
formatter.dateFormat = "YYYYMMDD-HHMM'Hrs'-ss's'"
|
||||||
|
let dirAlternative = folderPath + formatter.string(from: Date())
|
||||||
|
try FileManager.default.moveItem(atPath: folderPath, toPath: dirAlternative)
|
||||||
|
} else {
|
||||||
|
throw folderPath
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Failed to make path available at: \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
folderExist = false
|
||||||
|
}
|
||||||
|
if !folderExist {
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(
|
||||||
|
atPath: folderPath,
|
||||||
|
withIntermediateDirectories: true,
|
||||||
|
attributes: nil)
|
||||||
|
} catch {
|
||||||
|
print("Failed to create folder: \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 用以讀取使用者語彙檔案目錄的函數,會自動對 mgrPrefs 當中的參數糾偏。
|
||||||
|
// 當且僅當 mgrPrefs 當中的參數不合規(比如非實在路徑、或者無權限寫入)時,才會糾偏。
|
||||||
|
|
||||||
|
static func dataFolderPath(isDefaultFolder: Bool) -> String {
|
||||||
|
let appSupportPath = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].path
|
||||||
|
var userDictPathSpecified = (mgrPrefs.userDataFolderSpecified as NSString).expandingTildeInPath
|
||||||
|
var userDictPathDefault =
|
||||||
|
(URL(fileURLWithPath: appSupportPath).appendingPathComponent("vChewing").path as NSString)
|
||||||
|
.expandingTildeInPath
|
||||||
|
|
||||||
|
userDictPathDefault.ensureTrailingSlash()
|
||||||
|
userDictPathSpecified.ensureTrailingSlash()
|
||||||
|
|
||||||
|
if (userDictPathSpecified == userDictPathDefault)
|
||||||
|
|| isDefaultFolder
|
||||||
|
{
|
||||||
|
return userDictPathDefault
|
||||||
|
}
|
||||||
|
if mgrPrefs.ifSpecifiedUserDataPathExistsInPlist() {
|
||||||
|
if mgrLangModel.checkIfSpecifiedUserDataFolderValid(userDictPathSpecified) {
|
||||||
|
return userDictPathSpecified
|
||||||
|
} else {
|
||||||
|
UserDefaults.standard.removeObject(forKey: "UserDataFolderSpecified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userDictPathDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 寫入使用者檔案
|
||||||
|
static func writeUserPhrase(
|
||||||
|
_ userPhrase: String?, inputMode mode: InputMode, areWeDuplicating: Bool, areWeDeleting: Bool
|
||||||
|
) -> Bool {
|
||||||
|
if var currentMarkedPhrase: String = userPhrase {
|
||||||
|
if !self.chkUserLMFilesExist(InputMode.imeModeCHS)
|
||||||
|
|| !self.chkUserLMFilesExist(InputMode.imeModeCHT)
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = areWeDeleting ? self.excludedPhrasesDataPath(mode) : self.userPhrasesDataPath(mode)
|
||||||
|
|
||||||
|
if areWeDuplicating && !areWeDeleting {
|
||||||
|
// Do not use ASCII characters to comment here.
|
||||||
|
// Otherwise, it will be scrambled by cnvHYPYtoBPMF
|
||||||
|
// module shipped in the vChewing Phrase Editor.
|
||||||
|
currentMarkedPhrase += "\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎"
|
||||||
|
}
|
||||||
|
currentMarkedPhrase += "\n"
|
||||||
|
|
||||||
|
if let writeFile = FileHandle(forUpdatingAtPath: path),
|
||||||
|
let data = currentMarkedPhrase.data(using: .utf8)
|
||||||
|
{
|
||||||
|
writeFile.seekToEndOfFile()
|
||||||
|
writeFile.write(data)
|
||||||
|
writeFile.closeFile()
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// We enforce the format consolidation here, since the pragma header
|
||||||
|
// will let the UserPhraseLM bypasses the consolidating process on load.
|
||||||
|
self.consolidate(givenFile: path, shouldCheckPragma: false)
|
||||||
|
|
||||||
|
// We use FSEventStream to monitor possible changes of the user phrase folder, hence the
|
||||||
|
// lack of the needs of manually load data here unless FSEventStream is disabled by user.
|
||||||
|
if !mgrPrefs.shouldAutoReloadUserDataFiles {
|
||||||
|
self.loadUserPhrases()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -50,6 +50,7 @@
|
||||||
5BA9FD4A27FEF3C9002DE248 /* PreferencesTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */; };
|
5BA9FD4A27FEF3C9002DE248 /* PreferencesTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */; };
|
||||||
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */; };
|
5BA9FD8B28006B41002DE248 /* VDKComboBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */; };
|
||||||
5BAD0CD527D701F6003D127F /* vChewingKeyLayout.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */; };
|
5BAD0CD527D701F6003D127F /* vChewingKeyLayout.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */; };
|
||||||
|
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAEFACF28012565001F42C9 /* mgrLangModel.swift */; };
|
||||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */; };
|
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */; };
|
||||||
5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; };
|
5BBBB75F27AED54C0023B93A /* Beep.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75D27AED54C0023B93A /* Beep.m4a */; };
|
||||||
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75E27AED54C0023B93A /* Fart.m4a */; };
|
5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5BBBB75E27AED54C0023B93A /* Fart.m4a */; };
|
||||||
|
@ -227,6 +228,7 @@
|
||||||
5BA9FD3C27FEF3C8002DE248 /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = "<group>"; };
|
5BA9FD3C27FEF3C8002DE248 /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = "<group>"; };
|
||||||
5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesTabViewController.swift; sourceTree = "<group>"; };
|
5BA9FD3D27FEF3C8002DE248 /* PreferencesTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesTabViewController.swift; sourceTree = "<group>"; };
|
||||||
5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDKComboBox.swift; sourceTree = "<group>"; };
|
5BA9FD8A28006B41002DE248 /* VDKComboBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDKComboBox.swift; sourceTree = "<group>"; };
|
||||||
|
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mgrLangModel.swift; sourceTree = "<group>"; };
|
||||||
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; };
|
5BB802D927FABA8300CF1C19 /* ctlInputMethod_Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Menu.swift; sourceTree = "<group>"; };
|
||||||
5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; };
|
5BBBB75D27AED54C0023B93A /* Beep.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Beep.m4a; sourceTree = "<group>"; };
|
||||||
5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; };
|
5BBBB75E27AED54C0023B93A /* Fart.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Fart.m4a; sourceTree = "<group>"; };
|
||||||
|
@ -478,6 +480,7 @@
|
||||||
D41355D6278D7409005E5CBD /* mgrLangModel.h */,
|
D41355D6278D7409005E5CBD /* mgrLangModel.h */,
|
||||||
D495583A27A5C6C4006ADE1C /* mgrLangModel_Privates.h */,
|
D495583A27A5C6C4006ADE1C /* mgrLangModel_Privates.h */,
|
||||||
D41355D7278D7409005E5CBD /* mgrLangModel.mm */,
|
D41355D7278D7409005E5CBD /* mgrLangModel.mm */,
|
||||||
|
5BAEFACF28012565001F42C9 /* mgrLangModel.swift */,
|
||||||
5B62A32527AE758000A19448 /* SubLanguageModels */,
|
5B62A32527AE758000A19448 /* SubLanguageModels */,
|
||||||
);
|
);
|
||||||
path = LangModelRelated;
|
path = LangModelRelated;
|
||||||
|
@ -1086,6 +1089,7 @@
|
||||||
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */,
|
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */,
|
||||||
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
|
D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */,
|
||||||
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
|
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */,
|
||||||
|
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
|
||||||
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */,
|
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */,
|
||||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
||||||
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
|
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue