diff --git a/Source/Engine/Gramambular/BlockReadingBuilder.h b/Source/Engine/Gramambular/BlockReadingBuilder.h index ed6fd173..a287db60 100644 --- a/Source/Engine/Gramambular/BlockReadingBuilder.h +++ b/Source/Engine/Gramambular/BlockReadingBuilder.h @@ -52,7 +52,11 @@ namespace Formosa { void setJoinSeparator(const string& separator); const string joinSeparator() const; - + + size_t markerCursorIndex() const; + void setMarkerCursorIndex(size_t inNewIndex); + vector readingsAtRange(size_t begin, size_t end) const; + Grid& grid(); protected: @@ -64,6 +68,7 @@ namespace Formosa { static const size_t MaximumBuildSpanLength = 6; size_t m_cursorIndex; + size_t m_markerCursorIndex; vector m_readings; Grid m_grid; @@ -74,12 +79,14 @@ namespace Formosa { inline BlockReadingBuilder::BlockReadingBuilder(LanguageModel *inLM) : m_LM(inLM) , m_cursorIndex(0) + , m_markerCursorIndex(-1) { } inline void BlockReadingBuilder::clear() { m_cursorIndex = 0; + m_markerCursorIndex = -1; m_readings.clear(); m_grid.clear(); } @@ -99,6 +106,15 @@ namespace Formosa { m_cursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex; } + inline size_t BlockReadingBuilder::markerCursorIndex() const + { + return m_markerCursorIndex; + } + + inline void BlockReadingBuilder::setMarkerCursorIndex(size_t inNewIndex) + { + m_markerCursorIndex = inNewIndex > m_readings.size() ? m_readings.size() : inNewIndex; + } inline void BlockReadingBuilder::insertReadingAtCursor(const string& inReading) { @@ -108,6 +124,14 @@ namespace Formosa { build(); m_cursorIndex++; } + + inline vector BlockReadingBuilder::readingsAtRange(size_t begin, size_t end) const { + vector v; + for (size_t i = begin; i < end; i++) { + v.push_back(m_readings[i]); + } + return v; + } inline bool BlockReadingBuilder::deleteReadingBeforeCursor() { diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 9e48f829..06b34d88 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -114,6 +114,7 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // shared language model object that stores our phrase-term probability database FastLM gLanguageModel; FastLM gLanguageModelPlainBopomofo; +FastLM gUserPhraseLanguageModel; static const int kUserOverrideModelCapacity = 500; static const double kObservedOverrideHalflife = 5400.0; // 1.5 hr. @@ -451,6 +452,22 @@ static double FindHighestScore(const vector& nodes, double epsilon) NSMarkedClauseSegmentAttributeName: @0}; NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:composedText attributes:attrDict]; + if (_bpmfReadingBuffer->isEmpty() && _builder->markerCursorIndex() != -1) { + NSInteger begin = 0; + NSInteger end = 0; + if (_builder->markerCursorIndex() > _builder->cursorIndex()) { + begin = _builder->cursorIndex(); + end = _builder->markerCursorIndex(); + } else { + end = _builder->cursorIndex(); + begin = _builder->markerCursorIndex(); + } + NSRange range = NSMakeRange(begin, end - begin); + NSDictionary *highlightAttrDict = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleDouble), + NSMarkedClauseSegmentAttributeName: @0}; + [attrString addAttributes:highlightAttrDict range:range]; + } + // the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound, // i.e. the client app needs to take care of where to put ths composing buffer [client setMarkedText:attrString selectionRange:NSMakeRange(cursorIndex, 0) replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; @@ -559,6 +576,41 @@ static double FindHighestScore(const vector& nodes, double epsilon) return layout; } +- (NSString *)_currentHighlighText +{ + if (_builder->markerCursorIndex() == -1) { + return @""; + } + if (!_bpmfReadingBuffer->isEmpty()) { + return @""; + } + + NSInteger begin = 0; + NSInteger end = 0; + if (_builder->markerCursorIndex() > _builder->cursorIndex()) { + begin = _builder->cursorIndex(); + end = _builder->markerCursorIndex(); + } else { + end = _builder->cursorIndex(); + begin = _builder->markerCursorIndex(); + } + NSRange range = NSMakeRange(begin, end - begin); + NSString *reading = [_composingBuffer substringWithRange:range]; + NSMutableString *string = [[NSMutableString alloc] init]; + [string appendString:reading]; + [string appendString:@" "]; + NSMutableArray *readingsArray = [[NSMutableArray alloc] init]; + vector v = _builder->readingsAtRange(begin,end); + for(vector::iterator it_i=v.begin(); it_i!=v.end(); ++it_i) { + [readingsArray addObject:[NSString stringWithUTF8String:it_i->c_str()]]; + } + NSString *joined = [readingsArray componentsJoinedByString:@"-"]; + [string appendString:joined]; + [string appendString:@" "]; + [string appendString:@"-1.0"]; + return string; +} + - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client { NSRect textFrame = NSZeroRect; @@ -771,11 +823,30 @@ static double FindHighestScore(const vector& nodes, double epsilon) return NO; } - if (_builder->cursorIndex() > 0) { - _builder->setCursorIndex(_builder->cursorIndex() - 1); - } - else { - [self beep]; + if (flags & NSShiftKeyMask) { + if (_builder->markerCursorIndex() == -1) { + if (_builder->cursorIndex() > 0) { + _builder->setMarkerCursorIndex(_builder->cursorIndex() - 1); + } else { + [self beep]; + } + NSString *tmp = [self _currentHighlighText]; + NSLog(@"%@", tmp); + } + else { + if (_builder->markerCursorIndex() > 0) { + _builder->setMarkerCursorIndex(_builder->markerCursorIndex() - 1); + } else { + [self beep]; + } + } + } else { + if (_builder->cursorIndex() > 0) { + _builder->setCursorIndex(_builder->cursorIndex() - 1); + } + else { + [self beep]; + } } } @@ -793,11 +864,32 @@ static double FindHighestScore(const vector& nodes, double epsilon) return NO; } - if (_builder->cursorIndex() < _builder->length()) { - _builder->setCursorIndex(_builder->cursorIndex() + 1); - } - else { - [self beep]; + if (flags & NSShiftKeyMask) { + if (_builder->markerCursorIndex() == -1) { + if (_builder->cursorIndex() < _builder->length()) { + _builder->setMarkerCursorIndex(_builder->cursorIndex() + 1); + } else { + [self beep]; + } + NSString *tmp = [self _currentHighlighText]; + NSLog(@"%@", tmp); + } + else { + if (_builder->markerCursorIndex() < _builder->length()) { + _builder->setMarkerCursorIndex(_builder->markerCursorIndex() + 1); + } else { + [self beep]; + } + NSString *tmp = [self _currentHighlighText]; + NSLog(@"%@", tmp); + } + } else { + if (_builder->cursorIndex() < _builder->length()) { + _builder->setCursorIndex(_builder->cursorIndex() + 1); + } + else { + [self beep]; + } } } @@ -1419,6 +1511,13 @@ static double FindHighestScore(const vector& nodes, double epsilon) @end +static NSString *userDataFolderPath() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDirectory, YES); + NSString *appSupportPath = [paths objectAtIndex:0]; + NSString *userDictPath = [appSupportPath stringByAppendingPathComponent:@"McBopomofo"]; + return userDictPath; +} + static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, FastLM &lm) { NSString *dataPath = [[NSBundle bundleForClass:[McBopomofoInputMethodController class]] pathForResource:filenameWithoutExtension ofType:@"txt"]; @@ -1428,6 +1527,15 @@ static void LTLoadLanguageModelFile(NSString *filenameWithoutExtension, FastLM & } } +static void LTLoadUserLanguageModelFile(NSString *filenameWithoutExtension, FastLM &lm) +{ + NSString *filename = [filenameWithoutExtension stringByAppendingPathExtension:@"txt"]; + NSString *dataPath = [userDataFolderPath() stringByAppendingPathComponent:filename]; + bool result = lm.open([dataPath UTF8String]); + if (!result) { + NSLog(@"Failed opening language model: %@", dataPath); + } +} void LTLoadLanguageModel() { diff --git a/Source/zh-Hant.lproj/preferences.xib b/Source/zh-Hant.lproj/preferences.xib index ee2d4322..50e165cc 100644 --- a/Source/zh-Hant.lproj/preferences.xib +++ b/Source/zh-Hant.lproj/preferences.xib @@ -224,7 +224,7 @@ - +