ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

predictivedialog.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "gui/predictivedialog.h"
00024 #include "gui/widget.h"
00025 #include "gui/widgets/edittext.h"
00026 #include "gui/gui-manager.h"
00027 #include "gui/ThemeEval.h"
00028 
00029 #include "common/config-manager.h"
00030 #include "common/translation.h"
00031 #include "common/events.h"
00032 #include "common/debug.h"
00033 #include "common/system.h"
00034 #include "common/keyboard.h"
00035 #include "common/file.h"
00036 #include "common/savefile.h"
00037 
00038 #if defined(__DS__) && defined(ENABLE_AGI)
00039 #include "backends/platform/ds/arm9/source/wordcompletion.h"
00040 #endif
00041 
00042 namespace GUI {
00043 
00044 enum {
00045     kCancelCmd = 'CNCL',
00046     kOkCmd     = '__OK',
00047     kBut1Cmd   = 'BUT1',
00048     kBut2Cmd   = 'BUT2',
00049     kBut3Cmd   = 'BUT3',
00050     kBut4Cmd   = 'BUT4',
00051     kBut5Cmd   = 'BUT5',
00052     kBut6Cmd   = 'BUT6',
00053     kBut7Cmd   = 'BUT7',
00054     kBut8Cmd   = 'BUT8',
00055     kBut9Cmd   = 'BUT9',
00056     kBut0Cmd   = 'BUT0',
00057     kNextCmd   = 'NEXT',
00058     kAddCmd    = '_ADD',
00059     kModeCmd   = 'MODE',
00060     kDelCmd    = '_DEL',
00061     kTestCmd   = 'TEST'
00062 };
00063 
00064 enum {
00065     kModePre = 0,
00066     kModeNum = 1,
00067     kModeAbc = 2
00068 };
00069 
00070 PredictiveDialog::PredictiveDialog() : Dialog("Predictive") {
00071     new StaticTextWidget(this, "Predictive.Headline", "Enter Text");
00072 
00073     _button[kCancelAct] =  new ButtonWidget(this, "Predictive.Cancel",  _("Cancel")   , nullptr, kCancelCmd);
00074     _button[kOkAct] =      new ButtonWidget(this, "Predictive.OK",      _("Ok")       , nullptr, kOkCmd);
00075 
00076     if (g_gui.useRTL()) {
00082         _button[kButton3Act] = new ButtonWidget(this, "Predictive.Button1", "3  def"      , nullptr, kBut3Cmd);
00083         _button[kButton2Act] = new ButtonWidget(this, "Predictive.Button2", "2  abc"      , nullptr, kBut2Cmd);
00084         _button[kButton1Act] = new ButtonWidget(this, "Predictive.Button3", "1  `-.&"     , nullptr, kBut1Cmd);
00085         _button[kButton6Act] = new ButtonWidget(this, "Predictive.Button4", "6  mno"      , nullptr, kBut6Cmd);
00086         _button[kButton5Act] = new ButtonWidget(this, "Predictive.Button5", "5  jkl"      , nullptr, kBut5Cmd);
00087         _button[kButton4Act] = new ButtonWidget(this, "Predictive.Button6", "4  ghi"      , nullptr, kBut4Cmd);
00088         _button[kButton9Act] = new ButtonWidget(this, "Predictive.Button7", "9  wxyz"     , nullptr, kBut9Cmd);
00089         _button[kButton8Act] = new ButtonWidget(this, "Predictive.Button8", "8  tuv"      , nullptr, kBut8Cmd);
00090         _button[kButton7Act] = new ButtonWidget(this, "Predictive.Button9", "7  pqrs"     , nullptr, kBut7Cmd);
00091         _button[kButton0Act] = new ButtonWidget(this, "Predictive.Button0", "0"           , nullptr, kBut0Cmd);
00092     } else {
00093         _button[kButton1Act] = new ButtonWidget(this, "Predictive.Button1", "1  `-.&"     , nullptr, kBut1Cmd);
00094         _button[kButton2Act] = new ButtonWidget(this, "Predictive.Button2", "2  abc"      , nullptr, kBut2Cmd);
00095         _button[kButton3Act] = new ButtonWidget(this, "Predictive.Button3", "3  def"      , nullptr, kBut3Cmd);
00096         _button[kButton4Act] = new ButtonWidget(this, "Predictive.Button4", "4  ghi"      , nullptr, kBut4Cmd);
00097         _button[kButton5Act] = new ButtonWidget(this, "Predictive.Button5", "5  jkl"      , nullptr, kBut5Cmd);
00098         _button[kButton6Act] = new ButtonWidget(this, "Predictive.Button6", "6  mno"      , nullptr, kBut6Cmd);
00099         _button[kButton7Act] = new ButtonWidget(this, "Predictive.Button7", "7  pqrs"     , nullptr, kBut7Cmd);
00100         _button[kButton8Act] = new ButtonWidget(this, "Predictive.Button8", "8  tuv"      , nullptr, kBut8Cmd);
00101         _button[kButton9Act] = new ButtonWidget(this, "Predictive.Button9", "9  wxyz"     , nullptr, kBut9Cmd);
00102         _button[kButton0Act] = new ButtonWidget(this, "Predictive.Button0", "0"           , nullptr, kBut0Cmd);
00103     }
00104 
00105     // I18N: You must leave "#" as is, only word 'next' is translatable
00106     _button[kNextAct] =    new ButtonWidget(this, "Predictive.Next",    _("#  next")  , nullptr, kNextCmd);
00107     _button[kAddAct] =     new ButtonWidget(this, "Predictive.Add",     _("add")      , nullptr, kAddCmd);
00108     _button[kAddAct]->setEnabled(false);
00109 
00110 #ifndef DISABLE_FANCY_THEMES
00111     if (g_gui.xmlEval()->getVar("Globals.Predictive.ShowDeletePic") == 1 && g_gui.theme()->supportsImages()) {
00112         _button[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd);
00113         ((PicButtonWidget *)_button[kDelAct])->useThemeTransparency(true);
00114         ((PicButtonWidget *)_button[kDelAct])->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelButton));
00115     } else
00116 #endif
00117         _button[kDelAct] = new ButtonWidget(this, "Predictive.Delete" , _("<") , nullptr, kDelCmd);
00118     // I18N: Pre means 'Predictive', leave '*' as is
00119     _button[kModeAct] = new ButtonWidget(this, "Predictive.Pre", _("*  Pre"), nullptr, kModeCmd);
00120     _editText = new EditTextWidget(this, "Predictive.Word", _search, nullptr, 0, 0);
00121 
00122     _userDictHasChanged = false;
00123 
00124     _predictiveDict.nameDict = "predictive_dictionary";
00125     _predictiveDict.defaultFilename = "pred.dic";
00126 
00127     _userDict.nameDict = "user_dictionary";
00128     _userDict.defaultFilename = "user.dic";
00129 
00130     if (!_predictiveDict.dictText) {
00131         loadAllDictionary(_predictiveDict);
00132         if (!_predictiveDict.dictText)
00133             debug(5, "Predictive Dialog: pred.dic not loaded");
00134     }
00135 
00136     if (!_userDict.dictText) {
00137         loadAllDictionary(_userDict);
00138         if (!_userDict.dictText)
00139             debug(5, "Predictive Dialog: user.dic not loaded");
00140     }
00141 
00142     mergeDicts();
00143 
00144     memset(_repeatcount, 0, sizeof(_repeatcount));
00145 
00146     _prefix.clear();
00147     _currentCode.clear();
00148     _currentWord.clear();
00149     _wordNumber = 0;
00150     _numMatchingWords = 0;
00151     memset(_predictiveResult, 0, sizeof(_predictiveResult));
00152 
00153     _lastButton = kNoAct;
00154     _mode = kModePre;
00155 
00156     _lastTime = 0;
00157     _curTime = 0;
00158     _lastPressedButton = kNoAct;
00159 
00160     _memoryList[0] = _predictiveDict.dictText;
00161     _memoryList[1] = _userDict.dictText;
00162     _numMemory = 0;
00163 
00164     _navigationWithKeys = false;
00165 
00166     _curPressedButton = kNoAct;
00167     _needRefresh = true;
00168     _isPressed = false;
00169 
00170 }
00171 
00172 PredictiveDialog::~PredictiveDialog() {
00173     for (int i = 0; i < _numMemory; i++) {
00174         free(_memoryList[i]);
00175     }
00176     free(_userDict.dictLine);
00177     free(_predictiveDict.dictLine);
00178     free(_unitedDict.dictLine);
00179 }
00180 
00181 void PredictiveDialog::reflowLayout() {
00182 #ifndef DISABLE_FANCY_THEMES
00183     removeWidget(_button[kDelAct]);
00184     _button[kDelAct]->setNext(nullptr);
00185     delete _button[kDelAct];
00186     _button[kDelAct] = nullptr;
00187 
00188     if (g_gui.xmlEval()->getVar("Globals.Predictive.ShowDeletePic") == 1 && g_gui.theme()->supportsImages()) {
00189         _button[kDelAct] = new PicButtonWidget(this, "Predictive.Delete", _("Delete char"), kDelCmd);
00190         ((PicButtonWidget *)_button[kDelAct])->useThemeTransparency(true);
00191         ((PicButtonWidget *)_button[kDelAct])->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageDelButton));
00192     } else {
00193         _button[kDelAct] = new ButtonWidget(this, "Predictive.Delete" , _("<") , nullptr, kDelCmd);
00194     }
00195 #endif
00196 
00197     Dialog::reflowLayout();
00198 }
00199 
00200 void PredictiveDialog::saveUserDictToFile() {
00201     if (_userDictHasChanged) {
00202         ConfMan.registerDefault("user_dictionary", "user.dic");
00203 
00204         Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(ConfMan.get("user_dictionary"));
00205 
00206         for (int i = 0; i < _userDict.dictLineCount; i++) {
00207             file->writeString(_userDict.dictLine[i]);
00208             file->writeString("\n");
00209         }
00210 
00211         file->finalize();
00212         delete file;
00213     }
00214 }
00215 
00216 void PredictiveDialog::handleKeyUp(Common::KeyState state) {
00217     if (_curPressedButton != kNoAct && !_needRefresh) {
00218         _button[_curPressedButton]->setUnpressedState();
00219         processButton(_curPressedButton);
00220     }
00221 
00222     _isPressed = false;
00223 }
00224 
00225 void PredictiveDialog::handleKeyDown(Common::KeyState state) {
00226     if (_isPressed) {
00227         return;
00228     }
00229 
00230     _isPressed = true;
00231     _curPressedButton = kNoAct;
00232     _needRefresh = false;
00233 
00234     if (getFocusWidget() == _editText) {
00235         setFocusWidget(_button[kAddAct]);
00236     }
00237 
00238     if (_lastButton == kNoAct) {
00239         _lastButton = kButton5Act;
00240     }
00241 
00242     switch (state.keycode) {
00243     case Common::KEYCODE_ESCAPE:
00244         saveUserDictToFile();
00245         close();
00246         return;
00247     case Common::KEYCODE_LEFT:
00248         _navigationWithKeys = true;
00249         if (_lastButton == kButton1Act || _lastButton == kButton4Act || _lastButton == kButton7Act)
00250             _curPressedButton = ButtonId(_lastButton + 2);
00251         else if (_lastButton == kDelAct)
00252             _curPressedButton = kButton1Act;
00253         else if (_lastButton == kModeAct)
00254             _curPressedButton = kNextAct;
00255         else if (_lastButton == kNextAct)
00256             _curPressedButton = kButton0Act;
00257         else if (_lastButton == kAddAct)
00258             _curPressedButton = kOkAct;
00259         else if (_lastButton == kCancelAct)
00260             _curPressedButton = kAddAct;
00261         else
00262             _curPressedButton = ButtonId(_lastButton - 1);
00263 
00264 
00265         if (_mode != kModeAbc && _lastButton == kCancelAct)
00266             _curPressedButton = kOkAct;
00267 
00268         _needRefresh = true;
00269         break;
00270     case Common::KEYCODE_RIGHT:
00271         _navigationWithKeys = true;
00272         if (_lastButton == kButton3Act || _lastButton == kButton6Act || _lastButton == kButton9Act || _lastButton == kOkAct)
00273             _curPressedButton = ButtonId(_lastButton - 2);
00274         else if (_lastButton == kDelAct)
00275             _curPressedButton = kButton3Act;
00276         else if (_lastButton == kButton0Act)
00277             _curPressedButton = kNextAct;
00278         else if (_lastButton == kNextAct)
00279             _curPressedButton = kModeAct;
00280         else if (_lastButton == kAddAct)
00281             _curPressedButton = kCancelAct;
00282         else if (_lastButton == kOkAct)
00283             _curPressedButton = kAddAct;
00284         else
00285             _curPressedButton = ButtonId(_lastButton + 1);
00286 
00287         if (_mode != kModeAbc && _lastButton == kOkAct)
00288             _curPressedButton = kCancelAct;
00289         _needRefresh = true;
00290         break;
00291     case Common::KEYCODE_UP:
00292         _navigationWithKeys = true;
00293         if (_lastButton <= kButton3Act)
00294             _curPressedButton = kDelAct;
00295         else if (_lastButton == kDelAct)
00296             _curPressedButton = kOkAct;
00297         else if (_lastButton == kModeAct)
00298             _curPressedButton = kButton7Act;
00299         else if (_lastButton == kButton0Act)
00300             _curPressedButton = kButton8Act;
00301         else if (_lastButton == kNextAct)
00302             _curPressedButton = kButton9Act;
00303         else if (_lastButton == kAddAct)
00304             _curPressedButton = kModeAct;
00305         else if (_lastButton == kCancelAct)
00306             _curPressedButton = kButton0Act;
00307         else if (_lastButton == kOkAct)
00308             _curPressedButton = kNextAct;
00309         else
00310             _curPressedButton = ButtonId(_lastButton - 3);
00311         _needRefresh = true;
00312         break;
00313     case Common::KEYCODE_DOWN:
00314         _navigationWithKeys = true;
00315         if (_lastButton == kDelAct)
00316             _curPressedButton = kButton3Act;
00317         else if (_lastButton == kButton7Act)
00318             _curPressedButton = kModeAct;
00319         else if (_lastButton == kButton8Act)
00320             _curPressedButton = kButton0Act;
00321         else if (_lastButton == kButton9Act)
00322             _curPressedButton = kNextAct;
00323         else if (_lastButton == kModeAct)
00324             _curPressedButton = kAddAct;
00325         else if (_lastButton == kButton0Act)
00326             _curPressedButton = kCancelAct;
00327         else if (_lastButton == kNextAct)
00328             _curPressedButton = kOkAct;
00329         else if (_lastButton == kAddAct || _lastButton == kCancelAct || _lastButton == kOkAct)
00330             _curPressedButton = kDelAct;
00331         else
00332             _curPressedButton = ButtonId(_lastButton + 3);
00333 
00334         if (_mode != kModeAbc && _lastButton == kModeAct)
00335             _curPressedButton = kCancelAct;
00336 
00337         _needRefresh = true;
00338         break;
00339     case Common::KEYCODE_KP_ENTER:
00340     case Common::KEYCODE_RETURN:
00341         if (state.flags & Common::KBD_CTRL) {
00342             _curPressedButton = kOkAct;
00343             break;
00344         }
00345         if (_navigationWithKeys) {
00346             // when the user has utilized arrow key navigation,
00347             // interpret enter as 'click' on the _curPressedButton button
00348             _curPressedButton = _lastButton;
00349             _needRefresh = false;
00350         } else {
00351             // else it is a shortcut for 'Ok'
00352             _curPressedButton = kOkAct;
00353         }
00354         break;
00355     case Common::KEYCODE_KP_PLUS:
00356         _curPressedButton = kAddAct;
00357         break;
00358     case Common::KEYCODE_BACKSPACE:
00359     case Common::KEYCODE_KP_MINUS:
00360         _curPressedButton = kDelAct;
00361         break;
00362     case Common::KEYCODE_KP_DIVIDE:
00363         _curPressedButton = kNextAct;
00364         break;
00365     case Common::KEYCODE_KP_MULTIPLY:
00366         _curPressedButton = kModeAct;
00367         break;
00368     case Common::KEYCODE_KP0:
00369         _curPressedButton = kButton0Act;
00370         break;
00371     case Common::KEYCODE_KP1:
00372     case Common::KEYCODE_KP2:
00373     case Common::KEYCODE_KP3:
00374     case Common::KEYCODE_KP4:
00375     case Common::KEYCODE_KP5:
00376     case Common::KEYCODE_KP6:
00377     case Common::KEYCODE_KP7:
00378     case Common::KEYCODE_KP8:
00379     case Common::KEYCODE_KP9:
00380         _curPressedButton = ButtonId(state.keycode - Common::KEYCODE_KP1);
00381         break;
00382     default:
00383         Dialog::handleKeyDown(state);
00384     }
00385 
00386     if (_lastButton != _curPressedButton)
00387         _button[_lastButton]->setUnpressedState();
00388 
00389     if (_curPressedButton != kNoAct && !_needRefresh)
00390         _button[_curPressedButton]->setPressedState();
00391     else
00392         updateHighLightedButton(_curPressedButton);
00393 }
00394 
00395 void PredictiveDialog::updateHighLightedButton(ButtonId act) {
00396     if (_curPressedButton != kNoAct) {
00397         _button[_lastButton]->setHighLighted(false);
00398         _lastButton = act;
00399         _button[_lastButton]->setHighLighted(true);
00400     }
00401 }
00402 
00403 void PredictiveDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00404     _curPressedButton = kNoAct;
00405 
00406     _navigationWithKeys = false;
00407 
00408     if (_lastButton != kNoAct)
00409         _button[_lastButton]->setHighLighted(false);
00410 
00411     switch (cmd) {
00412     case kDelCmd:
00413         _curPressedButton = kDelAct;
00414         break;
00415     case kNextCmd:
00416         _curPressedButton = kNextAct;
00417         break;
00418     case kAddCmd:
00419         _curPressedButton = kAddAct;
00420         break;
00421     case kModeCmd:
00422         _curPressedButton = kModeAct;
00423         break;
00424     case kBut1Cmd:
00425         _curPressedButton = kButton1Act;
00426         break;
00427     case kBut2Cmd:
00428         _curPressedButton = kButton2Act;
00429         break;
00430     case kBut3Cmd:
00431         _curPressedButton = kButton3Act;
00432         break;
00433     case kBut4Cmd:
00434         _curPressedButton = kButton4Act;
00435         break;
00436     case kBut5Cmd:
00437         _curPressedButton = kButton5Act;
00438         break;
00439     case kBut6Cmd:
00440         _curPressedButton = kButton6Act;
00441         break;
00442     case kBut7Cmd:
00443         _curPressedButton = kButton7Act;
00444         break;
00445     case kBut8Cmd:
00446         _curPressedButton = kButton8Act;
00447         break;
00448     case kBut9Cmd:
00449         _curPressedButton = kButton9Act;
00450         break;
00451     case kBut0Cmd:
00452         _curPressedButton = kButton0Act;
00453         break;
00454     case kCancelCmd:
00455         saveUserDictToFile();
00456         close();
00457         // When we cancel the dialog no result should be returned. Thus, we
00458         // will invalidate any result here.
00459         _predictiveResult[0] = 0;
00460         return;
00461     case kOkCmd:
00462         _curPressedButton = kOkAct;
00463         break;
00464     default:
00465         Dialog::handleCommand(sender, cmd, data);
00466     }
00467 
00468     if (_curPressedButton != kNoAct) {
00469         processButton(_curPressedButton);
00470     }
00471 }
00472 
00473 void PredictiveDialog::processButton(ButtonId button) {
00474     static const char *const buttonStr[] = {
00475         "1", "2", "3",
00476         "4", "5", "6",
00477         "7", "8", "9",
00478              "0"
00479     };
00480 
00481     static const char *const buttons[] = {
00482         "'-.&",  "abc", "def",
00483         "ghi",  "jkl", "mno",
00484         "pqrs", "tuv", "wxyz",
00485         "next",    "add",
00486         "<",
00487         "Cancel",  "OK",
00488         "Pre", "(0) ", nullptr
00489     };
00490 
00491     if (_mode == kModeAbc) {
00492         if (button >= kButton1Act && button <= kButton9Act) {
00493             if (!_lastTime)
00494                 _lastTime = g_system->getMillis();
00495             if (_lastPressedButton == button) {
00496                 _curTime = g_system->getMillis();
00497                 if ((_curTime - _lastTime) < kRepeatDelay) {
00498                     button = kNextAct;
00499                     _lastTime = _curTime;
00500                 } else {
00501                     _lastTime = 0;
00502                 }
00503             } else {
00504                 _lastPressedButton = button;
00505                 _lastTime = g_system->getMillis();
00506             }
00507         }
00508     }
00509 
00510     if (button >= kButton1Act) {
00511         _lastButton = button;
00512         if (button == kButton0Act && _mode != kModeNum) { // Space
00513             // bring MRU word at the top of the list when changing words
00514             if (_mode == kModePre && _unitedDict.dictActLine && _numMatchingWords > 1 && _wordNumber != 0)
00515                 bringWordtoTop(_unitedDict.dictActLine, _wordNumber);
00516 
00517             strncpy(_temp, _currentWord.c_str(), _currentCode.size());
00518             _temp[_currentCode.size()] = 0;
00519             _prefix += _temp;
00520             _prefix += " ";
00521             _currentCode.clear();
00522             _currentWord.clear();
00523             _numMatchingWords = 0;
00524             memset(_repeatcount, 0, sizeof(_repeatcount));
00525             _lastTime = 0;
00526             _lastPressedButton = kNoAct;
00527             _curTime = 0;
00528         } else if (button < kNextAct || button == kDelAct || button == kButton0Act) { // number or backspace
00529             if (button == kDelAct) { // backspace
00530                 if (_currentCode.size()) {
00531                     _repeatcount[_currentCode.size() - 1] = 0;
00532                     _currentCode.deleteLastChar();
00533                     if (_currentCode.empty())
00534                         _currentWord.clear();
00535                 } else {
00536                     if (_prefix.size())
00537                         _prefix.deleteLastChar();
00538                 }
00539             } else if (_prefix.size() + _currentCode.size() < kMaxWordLen - 1) { // don't overflow the dialog line
00540                 if (button == kButton0Act) { // zero
00541                     _currentCode += buttonStr[9];
00542                 } else {
00543                     _currentCode += buttonStr[button];
00544                 }
00545             }
00546 
00547             switch (_mode) {
00548             case kModeNum:
00549                 _currentWord = _currentCode;
00550                 break;
00551             case kModePre:
00552                 if (!matchWord() && _currentCode.size()) {
00553                     _currentCode.deleteLastChar();
00554                     matchWord();
00555                 }
00556                 _numMatchingWords = countWordsInString(_unitedDict.dictActLine);
00557                 break;
00558             case kModeAbc:
00559                 for (uint x = 0; x < _currentCode.size(); x++)
00560                     if (_currentCode[x] >= '1')
00561                         _temp[x] = buttons[_currentCode[x] - '1'][_repeatcount[x]];
00562                 _temp[_currentCode.size()] = 0;
00563                 _currentWord = _temp;
00564             default:
00565                 break;
00566             }
00567         } else if (button == kNextAct) { // next
00568             if (_mode == kModePre) {
00569                 if (_unitedDict.dictActLine && _numMatchingWords > 1) {
00570                     _wordNumber = (_wordNumber + 1) % _numMatchingWords;
00571                     char tmp[kMaxLineLen];
00572                     strncpy(tmp, _unitedDict.dictActLine, kMaxLineLen);
00573                     tmp[kMaxLineLen - 1] = 0;
00574                     char *tok = strtok(tmp, " ");
00575                     for (uint8 i = 0; i <= _wordNumber; i++)
00576                         tok = strtok(nullptr, " ");
00577                     _currentWord = Common::String(tok, _currentCode.size());
00578                 }
00579             } else if (_mode == kModeAbc) {
00580                 uint x = _currentCode.size();
00581                 if (x) {
00582                     if (_currentCode.lastChar() == '1' || _currentCode.lastChar() == '7' || _currentCode.lastChar() == '9')
00583                         _repeatcount[x - 1] = (_repeatcount[x - 1] + 1) % 4;
00584                     else
00585                         _repeatcount[x - 1] = (_repeatcount[x - 1] + 1) % 3;
00586 
00587                     if (_currentCode.lastChar() >= '1')
00588                         _currentWord.setChar(buttons[_currentCode[x - 1] - '1'][_repeatcount[x - 1]], x - 1);
00589                 }
00590             }
00591         } else if (button == kAddAct) { // add
00592             if (_mode == kModeAbc)
00593                 addWordToDict();
00594             else
00595                 debug(5, "Predictive Dialog: button Add doesn't work in this mode");
00596         } else if (button == kOkAct) { // Ok
00597             // bring MRU word at the top of the list when ok'ed out of the dialog
00598             if (_mode == kModePre && _unitedDict.dictActLine && _numMatchingWords > 1 && _wordNumber != 0)
00599                 bringWordtoTop(_unitedDict.dictActLine, _wordNumber);
00600         } else if (button == kModeAct) { // Mode
00601             _mode++;
00602             _button[kAddAct]->setEnabled(false);
00603             if (_mode > kModeAbc) {
00604                 _mode = kModePre;
00605                 // I18N: Pre means 'Predictive', leave '*' as is
00606                 _button[kModeAct]->setLabel(_("*  Pre"));
00607             } else if (_mode == kModeNum) {
00608                 // I18N: 'Num' means Numbers
00609                 _button[kModeAct]->setLabel(_("*  Num"));
00610             } else {
00611                 // I18N: 'Abc' means Latin alphabet input
00612                 _button[kModeAct]->setLabel(_("*  Abc"));
00613                 _button[kAddAct]->setEnabled(true);
00614             }
00615 
00616             // truncate current input at mode change
00617             strncpy(_temp, _currentWord.c_str(), _currentCode.size());
00618             _temp[_currentCode.size()] = 0;
00619             _prefix += _temp;
00620             _currentCode.clear();
00621             _currentWord.clear();
00622             memset(_repeatcount, 0, sizeof(_repeatcount));
00623 
00624             _lastTime = 0;
00625             _lastPressedButton = kNoAct;
00626             _curTime = 0;
00627         }
00628     }
00629 
00630     pressEditText();
00631 
00632     if (button == kOkAct)
00633         close();
00634 
00635     if (button == kCancelAct) {
00636         saveUserDictToFile();
00637         close();
00638     }
00639 }
00640 
00641 void PredictiveDialog::mergeDicts() {
00642     _unitedDict.dictLineCount  = _predictiveDict.dictLineCount + _userDict.dictLineCount;
00643     _unitedDict.dictLine = (char **)calloc(_unitedDict.dictLineCount, sizeof(char *));
00644 
00645     if (!_unitedDict.dictLine) {
00646         debug(5, "Predictive Dialog: cannot allocate memory for united dic");
00647         return;
00648     }
00649 
00650     int lenUserDictCode, lenPredictiveDictCode, lenCode;
00651     int i, j, k;
00652     i = j = k = 0;
00653 
00654     while ((i < _userDict.dictLineCount) && (j < _predictiveDict.dictLineCount)) {
00655         lenUserDictCode = strchr(_userDict.dictLine[i], ' ') - _userDict.dictLine[i];
00656         lenPredictiveDictCode = strchr(_predictiveDict.dictLine[j], ' ') - _predictiveDict.dictLine[j];
00657         lenCode = (lenUserDictCode >= lenPredictiveDictCode) ? lenUserDictCode : lenPredictiveDictCode;
00658         if (strncmp(_userDict.dictLine[i], _predictiveDict.dictLine[j], lenCode) >= 0) {
00659             _unitedDict.dictLine[k++] = _predictiveDict.dictLine[j++];
00660         } else {
00661             _unitedDict.dictLine[k++] = _userDict.dictLine[i++];
00662         }
00663     }
00664 
00665     while (i < _userDict.dictLineCount) {
00666         _unitedDict.dictLine[k++] = _userDict.dictLine[i++];
00667     }
00668 
00669     while (j < _predictiveDict.dictLineCount) {
00670         _unitedDict.dictLine[k++] = _predictiveDict.dictLine[j++];
00671     }
00672 }
00673 
00674 uint8 PredictiveDialog::countWordsInString(const char *const str) {
00675     // Count the number of (space separated) words in the given string.
00676     const char *ptr;
00677 
00678     if (!str)
00679         return 0;
00680 
00681     ptr = strchr(str, ' ');
00682     if (!ptr) {
00683         debug(5, "Predictive Dialog: Invalid dictionary line");
00684         return 0;
00685     }
00686 
00687     uint8 num = 1;
00688     ptr++;
00689     while ((ptr = strchr(ptr, ' '))) {
00690         ptr++;
00691         num++;
00692     }
00693     return num;
00694 }
00695 
00696 void PredictiveDialog::bringWordtoTop(char *str, int wordnum) {
00697     // This function reorders the words on the given pred.dic line
00698     // by moving the word at position 'wordnum' to the front (that is, right behind
00699     // right behind the numerical code word at the start of the line).
00700     Common::Array<Common::String> words;
00701     char buf[kMaxLineLen];
00702 
00703     if (!str)
00704         return;
00705     strncpy(buf, str, kMaxLineLen);
00706     buf[kMaxLineLen - 1] = 0;
00707     char *word = strtok(buf, " ");
00708     if (!word) {
00709         debug(5, "Predictive Dialog: Invalid dictionary line");
00710         return;
00711     }
00712 
00713     words.push_back(word);
00714     while ((word = strtok(nullptr, " ")) != nullptr)
00715         words.push_back(word);
00716     words.insert_at(1, words.remove_at(wordnum + 1));
00717 
00718     Common::String tmp;
00719     for (uint8 i = 0; i < words.size(); i++)
00720         tmp += words[i] + " ";
00721     tmp.deleteLastChar();
00722     memcpy(str, tmp.c_str(), strlen(str));
00723 }
00724 
00725 int PredictiveDialog::binarySearch(const char *const *const dictLine, const Common::String &code, const int dictLineCount) {
00726     int hi = dictLineCount - 1;
00727     int lo = 0;
00728     int line = 0;
00729     while (lo <= hi) {
00730         line = (lo + hi) / 2;
00731         int cmpVal = strncmp(dictLine[line], code.c_str(), code.size());
00732         if (cmpVal > 0)
00733             hi = line - 1;
00734         else if (cmpVal < 0)
00735             lo = line + 1;
00736         else {
00737             break;
00738         }
00739     }
00740 
00741     if (hi < lo) {
00742         return -(lo + 1);
00743     } else {
00744         return line;
00745     }
00746 }
00747 
00748 bool PredictiveDialog::matchWord() {
00749     // If there is no dictionary, then there is no match.
00750     if (_unitedDict.dictLineCount <= 0)
00751         return false;
00752 
00753     // If no text has been entered, then there is no match.
00754     if (_currentCode.empty())
00755         return false;
00756 
00757     // If the currently entered text is too long, it cannot match anything.
00758     if (_currentCode.size() > kMaxWordLen)
00759         return false;
00760 
00761     // The entries in the dictionary consist of a code, a space, and then
00762     // a space-separated list of words matching this code.
00763     // To exactly match a code, we therefore match the code plus the trailing
00764     // space in the dictionary.
00765     Common::String code = _currentCode + " ";
00766 
00767     int line = binarySearch(_unitedDict.dictLine, code, _unitedDict.dictLineCount);
00768     if (line < 0) {
00769         line = -(line + 1);
00770         _unitedDict.dictActLine = nullptr;
00771     } else {
00772         _unitedDict.dictActLine = _unitedDict.dictLine[line];
00773     }
00774 
00775     _currentWord.clear();
00776     _wordNumber = 0;
00777     if (0 == strncmp(_unitedDict.dictLine[line], _currentCode.c_str(), _currentCode.size())) {
00778         char tmp[kMaxLineLen];
00779         strncpy(tmp, _unitedDict.dictLine[line], kMaxLineLen);
00780         tmp[kMaxLineLen - 1] = 0;
00781         char *tok;
00782         strtok(tmp, " ");
00783         tok = strtok(nullptr, " ");
00784         _currentWord = Common::String(tok, _currentCode.size());
00785         return true;
00786     } else {
00787         return false;
00788     }
00789 }
00790 
00791 bool PredictiveDialog::searchWord(const char *const where, const Common::String &whatCode) {
00792     const char *ptr = where;
00793     ptr += whatCode.size();
00794 
00795     const char *newPtr;
00796     bool is = false;
00797     while ((newPtr = strchr(ptr, ' '))) {
00798         if (0 == strncmp(ptr, _currentWord.c_str(), newPtr - ptr)) {
00799             is = true;
00800             break;
00801         }
00802         ptr = newPtr + 1;
00803     }
00804     if (!is) {
00805         if (0 == strcmp(ptr, _currentWord.c_str())) {
00806             is = true;
00807         }
00808     }
00809     return is;
00810 }
00811 
00812 void PredictiveDialog::addWord(Dict &dict, const Common::String &word, const Common::String &code) {
00813     char *newLine = nullptr;
00814     Common::String tmpCode = code + ' ';
00815     int line = binarySearch(dict.dictLine, tmpCode, dict.dictLineCount);
00816     if (line >= 0) {
00817         if (searchWord(dict.dictLine[line], tmpCode)) {
00818             // if we found code and word, we should not insert/expands any word
00819             return;
00820         } else {
00821             // if we found the code, but did not find a word, we must
00822             // EXPANDS the currnent line with new word
00823             int oldLineSize = strlen(dict.dictLine[line]);
00824             int newLineSize = oldLineSize + word.size() + 1;
00825 
00826             newLine = (char *)malloc(newLineSize + 1);
00827 
00828             char *ptr = newLine;
00829             strncpy(ptr, dict.dictLine[line], oldLineSize);
00830             ptr += oldLineSize;
00831             Common::String tmp = ' ' + word + '\0';
00832             strncpy(ptr, tmp.c_str(), tmp.size());
00833 
00834             dict.dictLine[line] = newLine;
00835             _memoryList[_numMemory++] = newLine;
00836 
00837             if (dict.nameDict == "user_dictionary")
00838                 _userDictHasChanged = true;
00839 
00840             return;
00841         }
00842     } else { // if we didn't find the code, we need to INSERT new line with code and word
00843         if (dict.nameDict == "user_dictionary") {
00844             // if we must INSERT new line(code and word) to user_dictionary, we need to
00845             // check if there is a line that we want to INSERT in predictive dictionay
00846             int predictLine = binarySearch(_predictiveDict.dictLine, tmpCode, _predictiveDict.dictLineCount);
00847             if (predictLine >= 0) {
00848                 if (searchWord(_predictiveDict.dictLine[predictLine], tmpCode)) {
00849                     // if code and word is in predictive dictionary, we need to copy
00850                     // this line to user dictionary
00851                     int len = (predictLine == _predictiveDict.dictLineCount - 1) ? &_predictiveDict.dictText[_predictiveDict.dictTextSize] - _predictiveDict.dictLine[predictLine] :
00852                               _predictiveDict.dictLine[predictLine + 1] - _predictiveDict.dictLine[predictLine];
00853                     newLine = (char *)malloc(len);
00854                     strncpy(newLine, _predictiveDict.dictLine[predictLine], len);
00855                 } else {
00856                     // if there is no word in predictive dictionary, we need to copy to
00857                     // user dictionary mathed line + new word.
00858                     int len = (predictLine == _predictiveDict.dictLineCount - 1) ? &_predictiveDict.dictText[_predictiveDict.dictTextSize] - _predictiveDict.dictLine[predictLine] :
00859                               _predictiveDict.dictLine[predictLine + 1] - _predictiveDict.dictLine[predictLine];
00860                     newLine = (char *)malloc(len + word.size() + 1);
00861                     char *ptr = newLine;
00862                     strncpy(ptr, _predictiveDict.dictLine[predictLine], len);
00863                     ptr[len - 1] = ' ';
00864                     ptr += len;
00865                     strncpy(ptr, word.c_str(), word.size());
00866                     ptr[len + word.size()] = '\0';
00867                 }
00868             } else {
00869                 // if we didnt find line in predictive dialog, we should copy to user dictionary
00870                 // code + word
00871                 Common::String tmp;
00872                 tmp = tmpCode + word + '\0';
00873                 newLine = (char *)malloc(tmp.size());
00874                 strncpy(newLine, tmp.c_str(), tmp.size());
00875             }
00876         } else {
00877             // if want to insert line to different from user dictionary, we should copy to this
00878             // dictionary code + word
00879             Common::String tmp;
00880             tmp = tmpCode + word + '\0';
00881             newLine = (char *)malloc(tmp.size());
00882             strncpy(newLine, tmp.c_str(), tmp.size());
00883         }
00884     }
00885 
00886     // start from here are INSERTING new line to dictionaty ( dict )
00887     char **newDictLine = (char **)calloc(dict.dictLineCount + 1, sizeof(char *));
00888     if (!newDictLine) {
00889         warning("Predictive Dialog: cannot allocate memory for index buffer");
00890 
00891         free(newLine);
00892 
00893         return;
00894     }
00895 
00896     int k = 0;
00897     bool inserted = false;
00898     for (int i = 0; i < dict.dictLineCount; i++) {
00899         uint lenPredictiveDictCode = strchr(dict.dictLine[i], ' ') - dict.dictLine[i];
00900         uint lenCode = (lenPredictiveDictCode >= (code.size() - 1)) ? lenPredictiveDictCode : code.size() - 1;
00901         if ((strncmp(dict.dictLine[i], code.c_str(), lenCode) > 0) && !inserted) {
00902             newDictLine[k++] = newLine;
00903             inserted = true;
00904         }
00905         if (k != (dict.dictLineCount + 1)) {
00906             newDictLine[k++] = dict.dictLine[i];
00907         }
00908     }
00909     if (!inserted)
00910         newDictLine[k] = newLine;
00911 
00912     _memoryList[_numMemory++] = newLine;
00913 
00914     free(dict.dictLine);
00915     dict.dictLineCount += 1;
00916     dict.dictLine = (char **)calloc(dict.dictLineCount, sizeof(char *));
00917     if (!dict.dictLine) {
00918         warning("Predictive Dialog: cannot allocate memory for index buffer");
00919         free(newDictLine);
00920         return;
00921     }
00922 
00923     for (int i = 0; i < dict.dictLineCount; i++) {
00924         dict.dictLine[i] = newDictLine[i];
00925     }
00926 
00927     if (dict.nameDict == "user_dictionary")
00928         _userDictHasChanged = true;
00929 
00930     free(newDictLine);
00931 }
00932 
00933 void PredictiveDialog::addWordToDict() {
00934     if (_numMemory < kMaxWord) {
00935         addWord(_unitedDict, _currentWord, _currentCode);
00936         addWord(_userDict, _currentWord, _currentCode);
00937     } else {
00938         warning("Predictive Dialog: You cannot add word to user dictionary...");
00939     }
00940 }
00941 
00942 void PredictiveDialog::loadDictionary(Common::SeekableReadStream *in, Dict &dict) {
00943     int lines = 0;
00944 
00945     uint32 time1 = g_system->getMillis();
00946 
00947     dict.dictTextSize = in->size();
00948     dict.dictText = (char *)malloc(dict.dictTextSize + 1);
00949 
00950     if (!dict.dictText) {
00951         warning("Predictive Dialog: Not enough memory to load the file user.dic");
00952         return;
00953     }
00954 
00955     in->read(dict.dictText, dict.dictTextSize);
00956     dict.dictText[dict.dictTextSize] = 0;
00957     uint32 time2 = g_system->getMillis();
00958     debug(5, "Predictive Dialog: Time to read %s: %d bytes, %d ms", ConfMan.get(dict.nameDict).c_str(), dict.dictTextSize, time2 - time1);
00959     delete in;
00960 
00961     char *ptr = dict.dictText;
00962     lines = 1;
00963     while ((ptr = strchr(ptr, '\n'))) {
00964         lines++;
00965         ptr++;
00966     }
00967 
00968     dict.dictLine = (char **)calloc(lines, sizeof(char *));
00969     if (dict.dictLine == nullptr) {
00970         warning("Predictive Dialog: Cannot allocate memory for line index buffer");
00971         return;
00972     }
00973     dict.dictLine[0] = dict.dictText;
00974     ptr = dict.dictText;
00975     int i = 1;
00976     while ((ptr = strchr(ptr, '\n'))) {
00977         *ptr = 0;
00978         ptr++;
00979 #if defined(__DS__) && defined(ENABLE_AGI)
00980         // Pass the line on to the DS word list
00981         DS::addAutoCompleteLine(dict.dictLine[i - 1]);
00982 #endif
00983         dict.dictLine[i++] = ptr;
00984     }
00985     if (dict.dictLine[lines - 1][0] == 0)
00986         lines--;
00987 
00988     dict.dictLineCount = lines;
00989     debug(5, "Predictive Dialog: Loaded %d lines", dict.dictLineCount);
00990 
00991     // FIXME: We use binary search on _predictiveDict.dictLine, yet we make no at_tempt
00992     // to ever sort this array (except for the DS port). That seems risky, doesn't it?
00993 
00994 #if defined(__DS__) && defined(ENABLE_AGI)
00995     // Sort the DS word completion list, to allow for a binary chop later (in the ds backend)
00996     DS::sortAutoCompleteWordList();
00997 #endif
00998 
00999     uint32 time3 = g_system->getMillis();
01000     debug(5, "Predictive Dialog: Time to parse %s: %d, total: %d", ConfMan.get(dict.nameDict).c_str(), time3 - time2, time3 - time1);
01001 }
01002 
01003 void PredictiveDialog::loadAllDictionary(Dict &dict) {
01004     ConfMan.registerDefault(dict.nameDict, dict.defaultFilename);
01005 
01006     if (dict.nameDict == "predictive_dictionary") {
01007         Common::File *inFile = new Common::File();
01008         if (!inFile->open(ConfMan.get(dict.nameDict))) {
01009             warning("Predictive Dialog: cannot read file: %s", dict.defaultFilename.c_str());
01010             delete inFile;
01011             return;
01012         }
01013         loadDictionary(inFile, dict);
01014     } else {
01015         Common::InSaveFile *inFile = g_system->getSavefileManager()->openForLoading(ConfMan.get(dict.nameDict));
01016         if (!inFile) {
01017             warning("Predictive Dialog: cannot read file: %s", dict.defaultFilename.c_str());
01018             return;
01019         }
01020         loadDictionary(inFile, dict);
01021     }
01022 }
01023 
01024 void PredictiveDialog::pressEditText() {
01025     Common::strlcpy(_predictiveResult, _prefix.c_str(), sizeof(_predictiveResult));
01026     Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult));
01027     _editText->setEditString(_predictiveResult);
01028     //_editText->setCaretPos(_prefix.size() + _currentWord.size());
01029     _editText->markAsDirty();
01030 }
01031 
01032 } // namespace GUI


Generated on Sat Jun 27 2020 05:00:53 for ResidualVM by doxygen 1.7.1
curved edge   curved edge