aboutsummaryrefslogtreecommitdiff
path: root/gui/predictivedialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/predictivedialog.cpp')
-rw-r--r--gui/predictivedialog.cpp917
1 files changed, 917 insertions, 0 deletions
diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp
new file mode 100644
index 0000000000..ac329867d2
--- /dev/null
+++ b/gui/predictivedialog.cpp
@@ -0,0 +1,917 @@
+/* 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"
+
+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 \ No newline at end of file