scummvm/gui/predictivedialog.cpp
D G Turner 8b1a01b12d GUI: Corrected fix for NDS compile failure...
Header needed to be in global, not GUI namespace.
2012-03-25 20:23:16 +01:00

922 lines
26 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "gui/predictivedialog.h"
#include "gui/widget.h"
#include "gui/widgets/edittext.h"
#include "gui/gui-manager.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "common/events.h"
#include "common/debug.h"
#include "common/system.h"
#include "common/keyboard.h"
#include "common/file.h"
#include "common/savefile.h"
#ifdef __DS__
#include "backends/platform/ds/arm9/source/wordcompletion.h"
#endif
using namespace Common;
namespace GUI {
enum {
kCancelCmd = 'CNCL',
kOkCmd = '__OK',
kBut1Cmd = 'BUT1',
kBut2Cmd = 'BUT2',
kBut3Cmd = 'BUT3',
kBut4Cmd = 'BUT4',
kBut5Cmd = 'BUT5',
kBut6Cmd = 'BUT6',
kBut7Cmd = 'BUT7',
kBut8Cmd = 'BUT8',
kBut9Cmd = 'BUT9',
kBut0Cmd = 'BUT0',
kNextCmd = 'NEXT',
kAddCmd = '_ADD',
kModeCmd = 'MODE',
kDelCmd = '_DEL',
kTestCmd = 'TEST'
};
enum {
kModePre = 0,
kModeNum = 1,
kModeAbc = 2
};
PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
new StaticTextWidget(this, "Predictive.Headline", "Enter Text");
new ButtonWidget(this, "Predictive.Cancel" , _("Cancel") , 0, kCancelCmd);
new ButtonWidget(this, "Predictive.OK" , _("Ok") , 0, kOkCmd);
new ButtonWidget(this, "Predictive.Button1", "1 `-.&" , 0, kBut1Cmd);
new ButtonWidget(this, "Predictive.Button2", "2 abc" , 0, kBut2Cmd);
new ButtonWidget(this, "Predictive.Button3", "3 def" , 0, kBut3Cmd);
new ButtonWidget(this, "Predictive.Button4", "4 ghi" , 0, kBut4Cmd);
new ButtonWidget(this, "Predictive.Button5", "5 jkl" , 0, kBut5Cmd);
new ButtonWidget(this, "Predictive.Button6", "6 mno" , 0, kBut6Cmd);
new ButtonWidget(this, "Predictive.Button7", "7 pqrs" , 0, kBut7Cmd);
new ButtonWidget(this, "Predictive.Button8", "8 tuv" , 0, kBut8Cmd);
new ButtonWidget(this, "Predictive.Button9", "9 wxyz" , 0, kBut9Cmd);
new ButtonWidget(this, "Predictive.Button0", "0" , 0, kBut0Cmd);
// I18N: You must leave "#" as is, only word 'next' is translatable
new ButtonWidget(this, "Predictive.Next" , _("# next") , 0, kNextCmd);
_addBtn = new ButtonWidget(this, "Predictive.Add", _("add") , 0, kAddCmd);
_addBtn->setEnabled(false);
#ifndef DISABLE_FANCY_THEMES
_delbtn = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd);
((PicButtonWidget *)_delbtn)->useThemeTransparency(true);
((PicButtonWidget *)_delbtn)->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelbtn));
#endif
_delbtn = new ButtonWidget(this, "Predictive.Delete" , _("<") , 0, kDelCmd);
// I18N: Pre means 'Predictive', leave '*' as is
_modebutton = new ButtonWidget(this, "Predictive.Pre", _("* Pre"), 0, kModeCmd);
_edittext = new EditTextWidget(this, "Predictive.Word", _search, 0, 0, 0);
_userDictHasChanged = false;
_predictiveDict.nameDict = "predictive_dictionary";
_predictiveDict.fnameDict = "pred.dic";
_predictiveDict.dictActLine = NULL;
_userDict.nameDict = "user_dictionary";
_userDict.fnameDict = "user.dic";
_userDict.dictActLine = NULL;
_unitedDict.nameDict = "";
_unitedDict.fnameDict = "";
_predictiveDict.dictLine = NULL;
_predictiveDict.dictText = NULL;
_predictiveDict.dictLineCount = 0;
if (!_predictiveDict.dictText) {
loadAllDictionary(_predictiveDict);
if (!_predictiveDict.dictText)
debug("Predictive Dialog: pred.dic not loaded");
}
_userDict.dictLine = NULL;
_userDict.dictText = NULL;
_userDict.dictTextSize = 0;
_userDict.dictLineCount = 0;
if (!_userDict.dictText) {
loadAllDictionary(_userDict);
if (!_userDict.dictText)
debug("Predictive Dialog: user.dic not loaded");
}
mergeDicts();
_unitedDict.dictActLine = NULL;
_unitedDict.dictText = NULL;
memset(_repeatcount, 0, sizeof(_repeatcount));
_prefix.clear();
_currentCode.clear();
_currentWord.clear();
_wordNumber = 0;
_numMatchingWords = 0;
_lastbutton = kNoAct;
_mode = kModePre;
_lastTime = 0;
_curTime = 0;
_lastPressBtn = kNoAct;
_memoryList[0] = _predictiveDict.dictText;
_memoryList[1] = _userDict.dictText;
_numMemory = 0;
_navigationwithkeys = false;
}
PredictiveDialog::~PredictiveDialog() {
for (int i = 0; i < _numMemory; i++) {
free(_memoryList[i]);
}
free(_userDict.dictLine);
free(_predictiveDict.dictLine);
free(_unitedDict.dictLine);
}
void PredictiveDialog::saveUserDictToFile() {
if (_userDictHasChanged) {
ConfMan.registerDefault("user_dictionary", "user.dic");
Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(ConfMan.get("user_dictionary"));
for (int i = 0; i < _userDict.dictLineCount; i++) {
file->writeString(_userDict.dictLine[i]);
file->writeString("\n");
}
file->finalize();
delete file;
}
}
void PredictiveDialog::handleKeyDown(Common::KeyState state) {
ButtonId act = kNoAct;
if (getFocusWidget() == _edittext) {
setFocusWidget(_addBtn);
}
switch (state.keycode) {
case Common::KEYCODE_ESCAPE:
saveUserDictToFile();
close();
return;
case Common::KEYCODE_LEFT:
_navigationwithkeys = true;
if (_lastbutton == kBtn1Act || _lastbutton == kBtn4Act || _lastbutton == kBtn7Act)
act = ButtonId(_lastbutton + 2);
else if (_lastbutton == kNextAct)
act = kBtn0Act;
else if (_lastbutton == kDelAct)
act = kDelAct;
else if (_lastbutton == kCancelAct)
act = kOkAct;
else if (_lastbutton == kModeAct)
act = kAddAct;
else
act = ButtonId(_lastbutton - 1);
_lastbutton = act;
//needRefresh = true;
break;
case Common::KEYCODE_RIGHT:
_navigationwithkeys = true;
if (_lastbutton == kBtn3Act || _lastbutton == kBtn6Act || _lastbutton == kBtn9Act)
act = ButtonId(_lastbutton - 2);
else if (_lastbutton == kAddAct)
act = kModeAct;
else if (_lastbutton == kDelAct)
act = kDelAct;
else if (_lastbutton == kOkAct)
act = kCancelAct;
else if (_lastbutton == kBtn0Act)
act = kNextAct;
else
act = ButtonId(_lastbutton + 1);
_lastbutton = act;
//needRefresh = true;
break;
case Common::KEYCODE_UP:
_navigationwithkeys = true;
if (_lastbutton <= kBtn3Act)
act = kDelAct;
else if (_lastbutton == kNextAct || _lastbutton == kAddAct)
act = ButtonId(_lastbutton - 2);
else if (_lastbutton == kDelAct)
act = kOkAct;
else if (_lastbutton == kModeAct)
act = kBtn9Act;
else if (_lastbutton == kBtn0Act)
act = kBtn7Act;
else
act = ButtonId(_lastbutton - 3);
_lastbutton = act;
//needRefresh = true;
break;
case Common::KEYCODE_DOWN:
_navigationwithkeys = true;
if (_lastbutton == kBtn7Act)
act = kBtn0Act;
else if (_lastbutton == kBtn8Act || _lastbutton == kBtn9Act)
act = ButtonId(_lastbutton + 2);
else if (_lastbutton == kDelAct)
act = kBtn1Act;
else if (_lastbutton == kCancelAct || _lastbutton == kOkAct)
act = kDelAct;
else if (_lastbutton == kModeAct || _lastbutton == kBtn0Act)
act = ButtonId(_lastbutton - 2);
else
act = ButtonId(_lastbutton + 3);
_lastbutton = act;
//needRefresh = true;
break;
case Common::KEYCODE_KP_ENTER:
if (_navigationwithkeys) {
// when the user has utilized arrow key navigation,
// interpret enter as 'click' on the act button
act = _lastbutton;
} else {
// else it is a shortcut for 'Ok'
act = kOkAct;
}
break;
case Common::KEYCODE_KP_PLUS:
act = kAddAct;
break;
case Common::KEYCODE_BACKSPACE:
case Common::KEYCODE_KP_MINUS:
act = kDelAct;
break;
case Common::KEYCODE_KP_DIVIDE:
act = kNextAct;
break;
case Common::KEYCODE_KP_MULTIPLY:
act = kModeAct;
break;
case Common::KEYCODE_KP0:
act = kBtn0Act;
break;
case Common::KEYCODE_KP1:
case Common::KEYCODE_KP2:
case Common::KEYCODE_KP3:
case Common::KEYCODE_KP4:
case Common::KEYCODE_KP5:
case Common::KEYCODE_KP6:
case Common::KEYCODE_KP7:
case Common::KEYCODE_KP8:
case Common::KEYCODE_KP9:
act = ButtonId(state.keycode - Common::KEYCODE_KP1);
break;
default:
Dialog::handleKeyDown(state);
}
if (act != kNoAct) {
processBtnActive(act);
}
}
void PredictiveDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
ButtonId act = kNoAct;
_navigationwithkeys = false;
switch (cmd) {
case kDelCmd:
act = kDelAct;
break;
case kNextCmd:
act = kNextAct;
break;
case kAddCmd:
act = kAddAct;
break;
case kModeCmd:
act = kModeAct;
break;
case kBut1Cmd:
act = kBtn1Act;
break;
case kBut2Cmd:
act = kBtn2Act;
break;
case kBut3Cmd:
act = kBtn3Act;
break;
case kBut4Cmd:
act = kBtn4Act;
break;
case kBut5Cmd:
act = kBtn5Act;
break;
case kBut6Cmd:
act = kBtn6Act;
break;
case kBut7Cmd:
act = kBtn7Act;
break;
case kBut8Cmd:
act = kBtn8Act;
break;
case kBut9Cmd:
act = kBtn9Act;
break;
case kBut0Cmd:
act = kBtn0Act;
break;
case kCancelCmd:
saveUserDictToFile();
close();
return;
case kOkCmd:
act = kOkAct;
break;
default:
Dialog::handleCommand(sender, cmd, data);
}
if (act != kNoAct) {
processBtnActive(act);
}
}
void PredictiveDialog::processBtnActive(ButtonId button) {
uint8 x;
const char *buttonStr[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" };
const char *buttons[] = {
"'-.&", "abc", "def",
"ghi", "jkl", "mno",
"pqrs", "tuv", "wxyz",
"next", "add",
"<",
"Cancel", "OK",
"Pre", "(0) ", NULL
};
if (_mode == kModeAbc) {
if (button >= kBtn1Act && button <= kBtn9Act ) {
if (!_lastTime)
_lastTime = g_system->getMillis();
if (_lastPressBtn == button) {
_curTime = g_system->getMillis();
if((_curTime - _lastTime) < kRepeatDelay) {
button = kNextAct;
_lastTime = _curTime;
} else {
_lastTime = 0;
}
} else {
_lastPressBtn = button;
_lastTime = g_system->getMillis();
}
}
}
if (button >= kBtn1Act) {
_lastbutton = button;
if (button == kBtn0Act && _mode != kModeNum) { // Space
// bring MRU word at the top of the list when changing words
if (_mode == kModePre && _unitedDict.dictActLine && _numMatchingWords > 1 && _wordNumber != 0)
bringWordtoTop(_unitedDict.dictActLine, _wordNumber);
strncpy(_temp, _currentWord.c_str(), _currentCode.size());
_temp[_currentCode.size()] = 0;
_prefix += _temp;
_prefix += " ";
_currentCode.clear();
_currentWord.clear();
_numMatchingWords = 0;
memset(_repeatcount, 0, sizeof(_repeatcount));
_lastTime = 0;
_lastPressBtn = kNoAct;
_curTime = 0;
} else if (button < kNextAct || button == kDelAct || button == kBtn0Act) { // number or backspace
if (button == kDelAct) { // backspace
if (_currentCode.size()) {
_repeatcount[_currentCode.size() - 1] = 0;
_currentCode.deleteLastChar();
if(_currentCode == Common::String(""))
_currentWord.clear();
} else {
if (_prefix.size())
_prefix.deleteLastChar();
}
} else if (_prefix.size() + _currentCode.size() < MAXWORDLEN - 1) { // don't overflow the dialog line
if (button == kBtn0Act) { // zero
_currentCode += buttonStr[9];
} else {
_currentCode += buttonStr[button];
}
}
switch (_mode) {
case kModeNum:
_currentWord = _currentCode;
break;
case kModePre:
if (!matchWord() && _currentCode.size()) {
_currentCode.deleteLastChar();
matchWord();
}
_numMatchingWords = countWordsInString(_unitedDict.dictActLine);
break;
case kModeAbc:
for (x = 0; x < _currentCode.size(); x++)
if (_currentCode[x] >= '1')
_temp[x] = buttons[_currentCode[x] - '1'][_repeatcount[x]];
_temp[_currentCode.size()] = 0;
_currentWord = _temp;
}
} else if (button == kNextAct) { // next
if (_mode == kModePre) {
if (_unitedDict.dictActLine && _numMatchingWords > 1) {
_wordNumber = (_wordNumber + 1) % _numMatchingWords;
char tmp[MAXLINELEN];
strncpy(tmp, _unitedDict.dictActLine, MAXLINELEN);
tmp[MAXLINELEN - 1] = 0;
char *tok = strtok(tmp, " ");
for (uint8 i = 0; i <= _wordNumber; i++)
tok = strtok(NULL, " ");
_currentWord = Common::String(tok, _currentCode.size());
}
} else if (_mode == kModeAbc) {
x = _currentCode.size();
if (x) {
if (_currentCode.lastChar() == '1' || _currentCode.lastChar() == '7' || _currentCode.lastChar() == '9')
_repeatcount[x - 1] = (_repeatcount[x - 1] + 1) % 4;
else
_repeatcount[x - 1] = (_repeatcount[x - 1] + 1) % 3;
if (_currentCode.lastChar() >= '1')
_currentWord.setChar(buttons[_currentCode[x - 1] - '1'][_repeatcount[x - 1]], x-1);
}
}
} else if (button == kAddAct) { // add
if (_mode == kModeAbc)
addWordToDict();
else
debug("Predictive Dialog: button Add doesn't work in this mode");
} else if (button == kOkAct) { // Ok
// bring MRU word at the top of the list when ok'ed out of the dialog
if (_mode == kModePre && _unitedDict.dictActLine && _numMatchingWords > 1 && _wordNumber != 0)
bringWordtoTop(_unitedDict.dictActLine, _wordNumber);
goto press;
} else if (button == kModeAct) { // Mode
_mode++;
_addBtn->setEnabled(false);
if (_mode > kModeAbc) {
_mode = kModePre;
// I18N: Pre means 'Predictive', leave '*' as is
_modebutton->setLabel("* Pre");
// I18N: 'Num' means Numbers, 'Abc' means Latin alphabet input
} else (_mode == kModeNum) ? _modebutton->setLabel("* Num") : (_modebutton->setLabel("* Abc"), _addBtn->setEnabled(true));
// truncate current input at mode change
strncpy(_temp, _currentWord.c_str(), _currentCode.size());
_temp[_currentCode.size()] = 0;
_prefix += _temp;
_currentCode.clear();
_currentWord.clear();
memset(_repeatcount, 0, sizeof(_repeatcount));
_lastTime = 0;
_lastPressBtn = kNoAct;
_curTime = 0;
} else {
goto press;
}
}
press:
pressEditText();
if (button == kOkAct) close();
}
void PredictiveDialog::handleTickle() {
if (!_lastTime)
if ((_curTime - _lastTime) > kRepeatDelay) {
_lastTime = 0;
}
}
void PredictiveDialog::mergeDicts() {
_unitedDict.dictLineCount = _predictiveDict.dictLineCount + _userDict.dictLineCount;
_unitedDict.dictLine = (char **)calloc(1, sizeof(char *) * _unitedDict.dictLineCount);
if (!_unitedDict.dictLine) {
debug("Predictive Dialog: cannot allocate memory for united dic");
return;
}
int lenUserDictCode, lenPredictiveDictCode, lenCode;
int i, j, k;
i = j = k = 0;
while ((i < _userDict.dictLineCount) && (j < _predictiveDict.dictLineCount)) {
lenUserDictCode = strchr(_userDict.dictLine[i], ' ') - _userDict.dictLine[i];
lenPredictiveDictCode = strchr(_predictiveDict.dictLine[j], ' ') - _predictiveDict.dictLine[j];
lenCode = (lenUserDictCode >= lenPredictiveDictCode) ? lenUserDictCode : lenPredictiveDictCode;
if (strncmp(_userDict.dictLine[i], _predictiveDict.dictLine[j], lenCode) >= 0) {
_unitedDict.dictLine[k++] = _predictiveDict.dictLine[j++];
} else {
_unitedDict.dictLine[k++] = _userDict.dictLine[i++];
}
}
while (i < _userDict.dictLineCount) {
_unitedDict.dictLine[k++] = _userDict.dictLine[i++];
}
while (j < _predictiveDict.dictLineCount) {
_unitedDict.dictLine[k++] = _predictiveDict.dictLine[j++];
}
}
uint8 PredictiveDialog::countWordsInString(char *str) {
// Count the number of (space separated) words in the given string.
char *ptr;
if (!str)
return 0;
ptr = strchr(str, ' ');
if (!ptr) {
debug("Predictive Dialog: Invalid dictionary line");
return 0;
}
uint8 num = 1;
ptr++;
while ((ptr = strchr(ptr, ' '))) {
ptr++;
num++;
}
return num;
}
void PredictiveDialog::bringWordtoTop(char *str, int wordnum) {
// This function reorders the words on the given pred.dic line
// by moving the word at position 'wordnum' to the front (that is, right behind
// right behind the numerical code word at the start of the line).
Common::Array<Common::String> words;
char buf[MAXLINELEN];
if (!str)
return;
strncpy(buf, str, MAXLINELEN);
buf[MAXLINELEN - 1] = 0;
char *word = strtok(buf, " ");
if (!word) {
debug("Predictive Dialog: Invalid dictionary line");
return;
}
words.push_back(word);
while ((word = strtok(NULL, " ")) != NULL)
words.push_back(word);
words.insert_at(1, words.remove_at(wordnum + 1));
Common::String tmp;
for (uint8 i = 0; i < words.size(); i++)
tmp += words[i] + " ";
tmp.deleteLastChar();
memcpy(str, tmp.c_str(), strlen(str));
}
int PredictiveDialog::binarySearch(char **dictLine, const String &code, int dictLineCount) {
int hi = dictLineCount - 1;
int lo = 0;
int line = 0;
while (lo <= hi) {
line = (lo + hi) / 2;
int cmpVal = strncmp(dictLine[line], code.c_str(), code.size());
if (cmpVal > 0)
hi = line - 1;
else if (cmpVal < 0)
lo = line + 1;
else {
break;
}
}
if (hi < lo) {
return -(lo + 1);
} else {
return line;
}
}
bool PredictiveDialog::matchWord() {
// If no text has been entered, then there is no match.
if (_currentCode.empty())
return false;
// If the currently entered text is too long, it cannot match anything.
if (_currentCode.size() > MAXWORDLEN)
return false;
// The entries in the dictionary consist of a code, a space, and then
// a space-separated list of words matching this code.
// To exactly match a code, we therefore match the code plus the trailing
// space in the dictionary.
Common::String code = _currentCode + " ";
int line = binarySearch(_unitedDict.dictLine, code, _unitedDict.dictLineCount);
if (line < 0) {
line = -(line + 1);
_unitedDict.dictActLine = NULL;
} else {
_unitedDict.dictActLine = _unitedDict.dictLine[line];
}
_currentWord.clear();
_wordNumber = 0;
if (0 == strncmp(_unitedDict.dictLine[line], _currentCode.c_str(), _currentCode.size())) {
char tmp[MAXLINELEN];
strncpy(tmp, _unitedDict.dictLine[line], MAXLINELEN);
tmp[MAXLINELEN - 1] = 0;
char *tok = strtok(tmp, " ");
tok = strtok(NULL, " ");
_currentWord = Common::String(tok, _currentCode.size());
return true;
} else {
return false;
}
}
bool PredictiveDialog::searchWord(char *where, const String &whatCode) {
char *ptr = where;
ptr += whatCode.size();
char *newPtr;
bool is = false;
while((newPtr = strchr(ptr, ' '))) {
if (0 == strncmp(ptr, _currentWord.c_str(), newPtr - ptr)) {
is = true;
break;
}
ptr = newPtr + 1;
}
if (!is) {
if (0 == strcmp(ptr, _currentWord.c_str())) {
is = true;
}
}
return is;
}
void PredictiveDialog::addWord(Dict &dict, const String &word, const String &code) {
char *newLine;
Common::String tmpCode = code + ' ';
int line = binarySearch(dict.dictLine, tmpCode, dict.dictLineCount);
if (line >= 0) {
if (searchWord(dict.dictLine[line], tmpCode)) {
// if we found code and word, we should not insert/expands any word
return;
} else {
// if we found the code, but did not find a word, we must
// EXPANDS the currnent line with new word
int oldLineSize = strlen(dict.dictLine[line]);
int newLineSize = oldLineSize + word.size() + 1;
newLine = (char *)malloc(newLineSize + 1);
char *ptr = newLine;
strncpy(ptr, dict.dictLine[line], oldLineSize);
ptr += oldLineSize;
Common::String tmp = ' ' + word + '\0';
strncpy(ptr, tmp.c_str(), tmp.size());
dict.dictLine[line] = newLine;
_memoryList[_numMemory++] = newLine;
if (dict.nameDict == "user_dictionary")
_userDictHasChanged = true;
return;
}
} else { // if we didn't find the code, we need to INSERT new line with code and word
if (dict.nameDict == "user_dictionary") {
// if we must INSERT new line(code and word) to user_dictionary, we need to
// check if there is a line that we want to INSERT in predictive dictionay
int predictLine = binarySearch(_predictiveDict.dictLine, tmpCode, _predictiveDict.dictLineCount);
if (predictLine >= 0) {
if (searchWord(_predictiveDict.dictLine[predictLine], tmpCode)) {
// if code and word is in predictive dictionary, we need to copy
// this line to user dictionary
int len = (predictLine == _predictiveDict.dictLineCount - 1) ? &_predictiveDict.dictText[_predictiveDict.dictTextSize] - _predictiveDict.dictLine[predictLine] :
_predictiveDict.dictLine[predictLine + 1] - _predictiveDict.dictLine[predictLine];
newLine = (char *)malloc(len);
strncpy(newLine, _predictiveDict.dictLine[predictLine], len);
} else {
// if there is no word in predictive dictionary, we need to copy to
// user dictionary mathed line + new word.
int len = (predictLine == _predictiveDict.dictLineCount - 1) ? &_predictiveDict.dictText[_predictiveDict.dictTextSize] - _predictiveDict.dictLine[predictLine] :
_predictiveDict.dictLine[predictLine + 1] - _predictiveDict.dictLine[predictLine];
newLine = (char *)malloc(len + word.size() + 1);
char *ptr = newLine;
strncpy(ptr, _predictiveDict.dictLine[predictLine], len);
ptr[len - 1] = ' ';
ptr += len;
strncpy(ptr, word.c_str(), word.size());
ptr[len + word.size()] = '\0';
}
} else {
// if we didnt find line in predictive dialog, we should copy to user dictionary
// code + word
Common::String tmp;
tmp = tmpCode + word + '\0';
newLine = (char *)malloc(tmp.size());
strncpy(newLine, tmp.c_str(), tmp.size());
}
} else {
// if want to insert line to different from user dictionary, we should copy to this
// dictionary code + word
Common::String tmp;
tmp = tmpCode + word + '\0';
newLine = (char *)malloc(tmp.size());
strncpy(newLine, tmp.c_str(), tmp.size());
}
}
// start from here are INSERTING new line to dictionaty ( dict )
char **newDictLine = (char **)calloc(1, sizeof(char *) * (dict.dictLineCount + 1));
if (!newDictLine) {
warning("Predictive Dialog: cannot allocate memory for index buffer");
return;
}
newDictLine[dict.dictLineCount] = '\0';
int k = 0;
bool inserted = false;
for (int i = 0; i < dict.dictLineCount; i++) {
uint lenPredictiveDictCode = strchr(dict.dictLine[i], ' ') - dict.dictLine[i];
uint lenCode = (lenPredictiveDictCode >= (code.size() - 1)) ? lenPredictiveDictCode : code.size() - 1;
if ((strncmp(dict.dictLine[i], code.c_str(), lenCode) > 0) && !inserted) {
newDictLine[k++] = newLine;
inserted = true;
}
if (k != (dict.dictLineCount + 1)) {
newDictLine[k++] = dict.dictLine[i];
}
}
if (!inserted)
newDictLine[k] = newLine;
_memoryList[_numMemory++] = newLine;
free(dict.dictLine);
dict.dictLineCount += 1;
dict.dictLine = (char **)calloc(1, sizeof(char *) * dict.dictLineCount);
if (!dict.dictLine) {
warning("Predictive Dialog: cannot allocate memory for index buffer");
free(newDictLine);
return;
}
for (int i = 0; i < dict.dictLineCount; i++) {
dict.dictLine[i] = newDictLine[i];
}
if (dict.nameDict == "user_dictionary")
_userDictHasChanged = true;
free(newDictLine);
}
void PredictiveDialog::addWordToDict() {
if (_numMemory < MAXWORD) {
addWord(_unitedDict, _currentWord, _currentCode);
addWord(_userDict, _currentWord, _currentCode);
} else {
warning("Predictive Dialog: You cannot add word to user dictionary...");
}
}
void PredictiveDialog::loadDictionary(Common::SeekableReadStream *in, Dict &dict) {
int lines = 0;
uint32 time1 = g_system->getMillis();
dict.dictTextSize = in->size();
dict.dictText = (char *)malloc(dict.dictTextSize + 1);
if (!dict.dictText) {
warning("Predictive Dialog: Not enough memory to load the file user.dic");
return;
}
in->read(dict.dictText, dict.dictTextSize);
dict.dictText[dict.dictTextSize] = 0;
uint32 time2 = g_system->getMillis();
debug("Predictive Dialog: Time to read %s: %d bytes, %d ms", ConfMan.get(dict.nameDict).c_str(), dict.dictTextSize, time2-time1);
delete in;
char *ptr = dict.dictText;
lines = 1;
while ((ptr = strchr(ptr, '\n'))) {
lines++;
ptr++;
}
dict.dictLine = (char **)calloc(1, sizeof(char *) * lines);
if (dict.dictLine == NULL) {
warning("Predictive Dialog: Cannot allocate memory for line index buffer");
return;
}
dict.dictLine[0] = dict.dictText;
ptr = dict.dictText;
int i = 1;
while ((ptr = strchr(ptr, '\n'))) {
*ptr = 0;
ptr++;
#ifdef __DS__
// Pass the line on to the DS word list
DS::addAutoCompleteLine(dict.dictLine[i - 1]);
#endif
dict.dictLine[i++] = ptr;
}
if (dict.dictLine[lines - 1][0] == 0)
lines--;
dict.dictLineCount = lines;
debug("Predictive Dialog: Loaded %d lines", dict.dictLineCount);
// FIXME: We use binary search on _predictiveDict.dictLine, yet we make no at_tempt
// to ever sort this array (except for the DS port). That seems risky, doesn't it?
#ifdef __DS__
// Sort the DS word completion list, to allow for a binary chop later (in the ds backend)
DS::sortAutoCompleteWordList();
#endif
uint32 time3 = g_system->getMillis();
debug("Predictive Dialog: Time to parse %s: %d, total: %d", ConfMan.get(dict.nameDict).c_str(), time3-time2, time3-time1);
}
void PredictiveDialog::loadAllDictionary(Dict &dict) {
ConfMan.registerDefault(dict.nameDict, dict.fnameDict);
if (dict.nameDict == "predictive_dictionary") {
Common::File *inFile = new File();
if (!inFile->open(ConfMan.get(dict.nameDict))) {
warning("Predictive Dialog: cannot read file: %s", dict.fnameDict.c_str());
return;
}
loadDictionary(inFile, dict);
} else {
Common::InSaveFile *inFile = g_system->getSavefileManager()->openForLoading(ConfMan.get(dict.nameDict));
if (!inFile) {
warning("Predictive Dialog: cannot read file: %s", dict.fnameDict.c_str());
return;
}
loadDictionary(inFile, dict);
}
}
void PredictiveDialog::pressEditText() {
Common::strlcpy(_predictiveResult, _prefix.c_str(), sizeof(_predictiveResult));
Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult));
_edittext->setEditString(_predictiveResult);
//_edittext->setCaretPos(_prefix.size() + _currentWord.size());
_edittext->draw();
}
} // namespace GUI