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


Generated on Sat Feb 16 2019 05:01:01 for ResidualVM by doxygen 1.7.1
curved edge   curved edge