diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index e9c96457686..8218d82a05f 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -46,6 +46,8 @@ #include "agi/keyboard.h" #include "agi/menu.h" +#include "gui/predictivedialog.h" + namespace Agi { void AgiEngine::allowSynthetic(bool allow) { @@ -58,9 +60,25 @@ void AgiEngine::processEvents() { while (_eventMan->pollEvent(event)) { switch (event.type) { - case Common::EVENT_PREDICTIVE_DIALOG: - if (_predictiveDialogRunning) - break; + case Common::EVENT_PREDICTIVE_DIALOG: { + GUI::PredictiveDialog _predictiveDialog; + _predictiveDialog.runModal(); + strcpy(_predictiveResult, _predictiveDialog.getResult()); + if (strcmp(_predictiveResult, "")) { + if (_game.inputMode == INPUT_NORMAL) { + strcpy((char *)_game.inputBuffer, _predictiveResult); + handleKeys(KEY_ENTER); + } else if (_game.inputMode == INPUT_GETSTRING) { + strcpy(_game.strings[_stringdata.str], _predictiveResult); + newInputMode(INPUT_NORMAL); + _gfx->printCharacter(_stringdata.x + strlen(_game.strings[_stringdata.str]) + 1, + _stringdata.y, ' ', _game.colorFg, _game.colorBg); + } else if (_game.inputMode == INPUT_NONE) { + for (int n = 0; _predictiveResult[n]; n++) + keyEnqueue(_predictiveResult[n]); + } + } + /* if (predictiveDialog()) { if (_game.inputMode == INPUT_NORMAL) { strcpy((char *)_game.inputBuffer, _predictiveResult); @@ -75,6 +93,8 @@ void AgiEngine::processEvents() { keyEnqueue(_predictiveResult[n]); } } + */ + } break; case Common::EVENT_LBUTTONDOWN: key = BUTTON_LEFT; @@ -131,65 +151,46 @@ void AgiEngine::processEvents() { switch (key = event.kbd.keycode) { case Common::KEYCODE_LEFT: case Common::KEYCODE_KP4: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP4) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_LEFT; break; case Common::KEYCODE_RIGHT: case Common::KEYCODE_KP6: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP6) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_RIGHT; break; case Common::KEYCODE_UP: case Common::KEYCODE_KP8: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP8) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_UP; break; case Common::KEYCODE_DOWN: case Common::KEYCODE_KP2: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP2) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_DOWN; break; case Common::KEYCODE_PAGEUP: case Common::KEYCODE_KP9: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP9) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_UP_RIGHT; break; case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_KP3: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP3) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_DOWN_RIGHT; break; case Common::KEYCODE_HOME: case Common::KEYCODE_KP7: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP7) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_UP_LEFT; break; case Common::KEYCODE_END: case Common::KEYCODE_KP1: - if (_predictiveDialogRunning && key == Common::KEYCODE_KP1) - key = event.kbd.ascii; - else if (_allowSynthetic || !event.synthetic) + if (_allowSynthetic || !event.synthetic) key = KEY_DOWN_LEFT; break; case Common::KEYCODE_KP5: - if (_predictiveDialogRunning) - key = event.kbd.ascii; - else - key = KEY_STATIONARY; + key = KEY_STATIONARY; break; case Common::KEYCODE_PLUS: key = '+'; @@ -218,7 +219,7 @@ void AgiEngine::processEvents() { case Common::KEYCODE_F6: key = 0x4000; break; - case Common::KEYCODE_F7: + case Common::KEYCODE_F7: key = 0x4100; break; case Common::KEYCODE_F8: @@ -575,10 +576,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas _oldMode = INPUT_NONE; - _predictiveDialogRunning = false; - _predictiveDictText = NULL; - _predictiveDictLine = NULL; - _predictiveDictLineCount = 0; _firstSlot = 0; resetControllers(); @@ -684,9 +681,6 @@ AgiEngine::~AgiEngine() { _gfx->deinitMachine(); delete _gfx; delete _console; - - free(_predictiveDictLine); - free(_predictiveDictText); } Common::Error AgiBase::init() { diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 55b48050223..1e2b6170403 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -43,6 +43,7 @@ #include "agi/logic.h" #include "agi/sound.h" +#include "gui/predictivedialog.h" namespace Common { class RandomSource; @@ -1078,7 +1079,6 @@ public: void clearPrompt(bool useBlackBg = false); void clearLines(int, int, int); void flushLines(int, int); - bool predictiveDialog(); private: void printStatus(const char *message, ...) GCC_PRINTF(2, 3); @@ -1088,16 +1088,6 @@ private: void loadDict(); bool matchWord(); - // Predictive dialog - // TODO: Move this to a separate class - char *_predictiveDictText; - char **_predictiveDictLine; - int32 _predictiveDictLineCount; - char *_predictiveDictActLine; - Common::String _currentCode; - Common::String _currentWord; - int _wordNumber; - bool _predictiveDialogRunning; public: char _predictiveResult[40]; diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp index a7f15c16fb1..b7e52f4dc35 100644 --- a/engines/agi/keyboard.cpp +++ b/engines/agi/keyboard.cpp @@ -133,6 +133,19 @@ int AgiEngine::handleController(int key) { if (key == BUTTON_LEFT && (int)_mouse.y >= _game.lineUserInput * CHAR_LINES && (int)_mouse.y <= (_game.lineUserInput + 1) * CHAR_LINES) { + GUI::PredictiveDialog _predictiveDialog; + _predictiveDialog.runModal(); + strcpy(_predictiveResult, _predictiveDialog.getResult()); + if (strcmp(_predictiveResult, "")) { + if (_game.inputMode == INPUT_NONE) { + for (int n = 0; _predictiveResult[n]; n++) + keyEnqueue(_predictiveResult[n]); + } else { + strcpy((char *)_game.inputBuffer, _predictiveResult); + handleKeys(KEY_ENTER); + } + } + /* if (predictiveDialog()) { if (_game.inputMode == INPUT_NONE) { for (int n = 0; _predictiveResult[n]; n++) @@ -142,6 +155,7 @@ int AgiEngine::handleController(int key) { handleKeys(KEY_ENTER); } } + */ return true; } @@ -217,6 +231,17 @@ void AgiEngine::handleGetstring(int key) { case BUTTON_LEFT: if ((int)_mouse.y >= _stringdata.y * CHAR_LINES && (int)_mouse.y <= (_stringdata.y + 1) * CHAR_LINES) { + GUI::PredictiveDialog _predictiveDialog; + _predictiveDialog.runModal(); + strcpy(_predictiveResult, _predictiveDialog.getResult()); + if (strcmp(_predictiveResult, "")) { + strcpy(_game.strings[_stringdata.str], _predictiveResult); + newInputMode(INPUT_NORMAL); + _gfx->printCharacter(_stringdata.x + strlen(_game.strings[_stringdata.str]) + 1, + _stringdata.y, ' ', _game.colorFg, _game.colorBg); + return; + } + /* if (predictiveDialog()) { strcpy(_game.strings[_stringdata.str], _predictiveResult); newInputMode(INPUT_NORMAL); @@ -224,6 +249,7 @@ void AgiEngine::handleGetstring(int key) { _stringdata.y, ' ', _game.colorFg, _game.colorBg); return; } + */ } break; case KEY_ENTER: diff --git a/engines/agi/module.mk b/engines/agi/module.mk index 68d86f7b2eb..331a10c16e3 100644 --- a/engines/agi/module.mk +++ b/engines/agi/module.mk @@ -28,7 +28,6 @@ MODULE_OBJS := \ preagi_mickey.o \ preagi_troll.o \ preagi_winnie.o \ - predictive.o \ saveload.o \ sound.o \ sound_2gs.o \ diff --git a/engines/agi/predictive.cpp b/engines/agi/predictive.cpp deleted file mode 100644 index 4bb378934d0..00000000000 --- a/engines/agi/predictive.cpp +++ /dev/null @@ -1,617 +0,0 @@ -/* 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 "agi/agi.h" -#include "agi/graphics.h" -#include "agi/keyboard.h" - -#include "common/config-manager.h" -#include "common/textconsole.h" - -#ifdef __DS__ -#include "wordcompletion.h" -#endif - -namespace Agi { - -enum { - kModePre = 0, - kModeNum = 1, - kModeAbc = 2 -}; - -enum { - MAXLINELEN = 80, - MAXWORDLEN = 24 -}; - -uint8 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("Invalid dictionary line"); - return 0; - } - - uint8 num = 1; - ptr++; - while ((ptr = strchr(ptr, ' '))) { - ptr++; - num++; - } - return num; -} - -void 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 words; - char buf[MAXLINELEN]; - - if (!str) - return; - strncpy(buf, str, MAXLINELEN); - buf[MAXLINELEN - 1] = 0; - char *word = strtok(buf, " "); - if (!word) { - debug("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)); -} - -bool AgiEngine::predictiveDialog() { - uint8 x; - int y; - int bx[17], by[17]; - Common::String prefix; - char temp[MAXWORDLEN + 1], repeatcount[MAXWORDLEN]; - AgiBlock tmpwindow; - bool navigationwithkeys = false; - - const char *buttonStr[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }; - const char *buttons[] = { - "(1)'-.&", "(2)abc", "(3)def", - "(4)ghi", "(5)jkl", "(6)mno", - "(7)pqrs", "(8)tuv", "(9)wxyz", - "(#)next", "add", - "<", - "Cancel", "OK", - "(*)Pre", "(0) ", NULL - }; - const int colors[] = { - 15, 0, 15, 0, 15, 0, - 15, 0, 15, 0, 15, 0, - 15, 0, 15, 0, 15, 0, - 15, 12, 15, 12, - 15, 0, - 15, 0, 15, 0, - 14, 0, 15, 0, 0, 0 - }; - const char *modes[] = { "(*)Pre", "(*)123", "(*)Abc" }; - - // FIXME: Move this to a more appropriate place. - if (!_predictiveDictText) { - loadDict(); - if (!_predictiveDictText) - return false; - } - _predictiveDictActLine = NULL; - uint8 numMatchingWords = 0; - - _predictiveDialogRunning = true; - _system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, true); - - memset(repeatcount, 0, sizeof(repeatcount)); - - // show the predictive dialog. - // if another window is already in display, save its state into tmpwindow - memset(&tmpwindow, 0, sizeof(tmpwindow)); - tmpwindow.active = false; - if (_game.window.active) - memcpy(&tmpwindow, &(_game.window), sizeof(AgiBlock)); - drawWindow(50, 40, 269, 159); - _gfx->drawRectangle(62, 54, 249, 66, MSG_BOX_TEXT); - _gfx->flushBlock(62, 54, 249, 66); - - bx[15] = 73; // Zero/space - by[15] = 120; - bx[9] = 110; // next - by[9] = 120; - bx[10] = 172; // add - by[10] = 120; - bx[14] = 200; // Mode - by[14] = 120; - bx[11] = 252; // Backspace - by[11] = 57; - bx[12] = 180; // Cancel - by[12] = 140; - bx[13] = 240; // OK - by[13] = 140; - - x = 73; - y = 75; - for (int i = 0; i < 9; i++) { - bx[i] = x; - by[i] = y; - x += 60; - if (i % 3 == 2) { - y += 15; - x = 73; - } - } - - clearKeyQueue(); - - prefix.clear(); - _currentCode.clear(); - _currentWord.clear(); - _wordNumber = 0; - - int mode = kModePre; - - bool needRefresh = true; - int active = -1, lastactive = 0; - bool rc = false; - bool closeDialog = false; - bool enterPredictiveResult = false; - while (!closeDialog && !shouldQuit()) { - if (needRefresh) { - for (int i = 0; buttons[i]; i++) { - int color1 = colors[i * 2]; - int color2 = colors[i * 2 + 1]; - - if (i == 9 && !((mode != kModeAbc && _predictiveDictActLine && numMatchingWords > 1) - || (mode == kModeAbc && _currentWord.size() && _currentWord.lastChar() != ' '))) { // Next - color2 = 7; - } - - // needs fixing, or remove it! - bool _addIsActive = false; // FIXME: word adding is not implemented - if (i == 10 && !_addIsActive) { // Add - color2 = 7; - } - if (i == 14) { - _gfx->drawDefaultStyleButton(bx[i], by[i], modes[mode], i == active, 0, color1, color2); - } else { - _gfx->drawDefaultStyleButton(bx[i], by[i], buttons[i], i == active, 0, color1, color2); - } - } - - Common::strlcpy(temp, prefix.c_str(), sizeof(temp)); - Common::strlcat(temp, _currentWord.c_str(), sizeof(temp)); - - for (int i = prefix.size() + _currentCode.size(); i < MAXWORDLEN; i++) - temp[i] = ' '; - temp[MAXWORDLEN] = 0; - - printText(temp, 0, 8, 7, MAXWORDLEN, 15, 0); - _gfx->flushBlock(62, 54, 249, 66); - - if (active >= 0 && !navigationwithkeys) { - // provide visual feedback only when not navigating with the arrows - // so that the user can see the active button. - active = -1; - needRefresh = true; - } else - needRefresh = false; - - _gfx->doUpdate(); - } - - bool processkey = false; - - pollTimer(); - int key = doPollKeyboard(); - switch (key) { - case KEY_ENTER: - if (navigationwithkeys) { - // when the user has utilized arrow key navigation, - // interpret enter as 'click' on the active button - active = lastactive; - } else { - // else it is a shortcut for 'Ok' - active = 13; - } - processkey = true; - break; - case KEY_ESCAPE: - rc = false; - closeDialog = true; - break; - case BUTTON_LEFT: - navigationwithkeys = false; - for (int i = 0; buttons[i]; i++) { - if (_gfx->testButton(bx[i], by[i], buttons[i])) { - active = i; - processkey = true; - break; - } - } - break; - case KEY_BACKSPACE: - active = 11; - processkey = true; - break; - case '#': - active = 9; - processkey = true; - break; - case '*': - active = 14; - processkey = true; - break; - case 0x09: // Tab - navigationwithkeys = true; - debugC(3, kDebugLevelText, "Focus change"); - lastactive = active = lastactive + 1; - active %= ARRAYSIZE(buttons) - 1; - needRefresh = true; - break; - case KEY_LEFT: - navigationwithkeys = true; - if (lastactive == 0 || lastactive == 3 || lastactive == 6) - active = lastactive + 2; - else if (lastactive == 9) - active = 15; - else if (lastactive == 11) - active = 11; - else if (lastactive == 12) - active = 13; - else if (lastactive == 14) - active = 10; - else - active = lastactive - 1; - lastactive = active; - needRefresh = true; - break; - case KEY_RIGHT: - navigationwithkeys = true; - if (lastactive == 2 || lastactive == 5 || lastactive == 8) - active = lastactive - 2; - else if (lastactive == 10) - active = 14; - else if (lastactive == 11) - active = 11; - else if (lastactive == 13) - active = 12; - else if (lastactive == 15) - active = 9; - else - active = lastactive + 1; - lastactive = active; - needRefresh = true; - break; - case KEY_UP: - navigationwithkeys = true; - if (lastactive <= 2) - active = 11; - else if (lastactive == 9 || lastactive == 10) - active = lastactive - 2; - else if (lastactive == 11) - active = 13; - else if (lastactive == 14) - active = 8; - else if (lastactive == 15) - active = 6; - else - active = lastactive - 3; - lastactive = active; - needRefresh = true; - break; - case KEY_DOWN: - navigationwithkeys = true; - if (lastactive == 6) - active = 15; - else if (lastactive == 7 || lastactive == 8) - active = lastactive + 2; - else if (lastactive == 11) - active = 0; - else if (lastactive == 12 || lastactive == 13) - active = 11; - else if (lastactive == 14 || lastactive == 15) - active = lastactive - 2; - else - active = lastactive + 3; - lastactive = active; - needRefresh = true; - break; - default: - // handle numeric buttons - if (key >= '1' && key <= '9') { - active = key - '1'; - processkey = true; - } else if (key == '0') { - active = 15; - processkey = true; - } - break; - } - - if (processkey && !closeDialog) { - if (active >= 0) { - needRefresh = true; - lastactive = active; - if (active == 15 && mode != kModeNum) { // Space - // bring MRU word at the top of the list when changing words - if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0) - bringWordtoTop(_predictiveDictActLine, _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)); - } else if (active < 9 || active == 11 || active == 15) { // number or backspace - if (active == 11) { // backspace - if (_currentCode.size()) { - repeatcount[_currentCode.size() - 1] = 0; - _currentCode.deleteLastChar(); - } else { - if (prefix.size()) - prefix.deleteLastChar(); - } - } else if (prefix.size() + _currentCode.size() < MAXWORDLEN - 1) { // don't overflow the dialog line - if (active == 15) { // zero - _currentCode += buttonStr[9]; - } else { - _currentCode += buttonStr[active]; - } - } - - switch (mode) { - case kModeNum: - _currentWord = _currentCode; - break; - case kModePre: - if (!matchWord() && _currentCode.size()) { - _currentCode.deleteLastChar(); - matchWord(); - } - numMatchingWords = countWordsInString(_predictiveDictActLine); - break; - case kModeAbc: - for (x = 0; x < _currentCode.size(); x++) - if (_currentCode[x] >= '1') - temp[x] = buttons[_currentCode[x] - '1'][3 + repeatcount[x]]; - temp[_currentCode.size()] = 0; - _currentWord = temp; - } - } else if (active == 9) { // next - if (mode == kModePre) { - if (_predictiveDictActLine && numMatchingWords > 1) { - _wordNumber = (_wordNumber + 1) % numMatchingWords; - char tmp[MAXLINELEN]; - strncpy(tmp, _predictiveDictActLine, 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'][3 + repeatcount[x - 1]], x-1); - } - } - } else if (active == 10) { // add - debug(0, "add"); - } else if (active == 13) { // Ok - // bring MRU word at the top of the list when ok'ed out of the dialog - if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0) - bringWordtoTop(_predictiveDictActLine, _wordNumber); - rc = true; - enterPredictiveResult = true; - closeDialog = true; - } else if (active == 14) { // Mode - mode++; - if (mode > kModeAbc) - mode = kModePre; - - // 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)); - _predictiveDictActLine = NULL; - } else { - enterPredictiveResult = true; - closeDialog = true; - } - } - } - } - - if (enterPredictiveResult) { - Common::strlcpy(_predictiveResult, prefix.c_str(), sizeof(_predictiveResult)); - Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult)); - } - - // if another window was shown, bring it up again - if (!tmpwindow.active) - closeWindow(); - else { - _gfx->restoreBlock(_game.window.x1, _game.window.y1, - _game.window.x2, _game.window.y2, _game.window.buffer); - - free(_game.window.buffer); - memcpy(&(_game.window), &tmpwindow, sizeof(AgiBlock)); - _gfx->doUpdate(); - } - - _system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, false); - _predictiveDialogRunning = false; - - return rc; -} - -void AgiEngine::loadDict() { - Common::File inFile; - int lines = 0; - - ConfMan.registerDefault("predictive_dictionary", "pred.dic"); - - uint32 time1 = _system->getMillis(); - Common::String inFileName(ConfMan.get("predictive_dictionary")); - if (!inFile.open(inFileName)) - return; - - char *ptr; - int size = inFile.size(); - - _predictiveDictText = (char *)malloc(size + 1); - if (!_predictiveDictText) { - warning("Not enough memory to load the predictive dictionary"); - return; - } - inFile.read(_predictiveDictText, size); - _predictiveDictText[size] = 0; - uint32 time2 = _system->getMillis(); - debug("Time to read %s: %d bytes, %d ms", inFileName.c_str(), size, time2-time1); - inFile.close(); - - ptr = _predictiveDictText; - lines = 1; - while ((ptr = strchr(ptr, '\n'))) { - lines++; - ptr++; - } - - _predictiveDictLine = (char **)calloc(1, sizeof(char *) * lines); - if (_predictiveDictLine == NULL) { - warning("Cannot allocate memory for line index buffer"); - return; - } - _predictiveDictLine[0] = _predictiveDictText; - ptr = _predictiveDictText; - int i = 1; - while ((ptr = strchr(ptr, '\n'))) { - *ptr = 0; - ptr++; -#ifdef __DS__ - // Pass the line on to the DS word list - DS::addAutoCompleteLine(_predictiveDictLine[i - 1]); -#endif - _predictiveDictLine[i++] = ptr; - } - if (_predictiveDictLine[lines - 1][0] == 0) - lines--; - - _predictiveDictLineCount = lines; - debug("Loaded %d lines", _predictiveDictLineCount); - - // FIXME: We use binary search on _predictiveDictLine, yet we make no attempt - // 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 = _system->getMillis(); - debug("Time to parse pred.dic: %d, total: %d", time3-time2, time3-time1); -} - -bool AgiEngine::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 + " "; - - // Perform a binary search on the dictionary. - int hi = _predictiveDictLineCount - 1; - int lo = 0; - int line = 0; - while (lo <= hi) { - line = (lo + hi) / 2; - int cmpVal = strncmp(_predictiveDictLine[line], code.c_str(), code.size()); - if (cmpVal > 0) - hi = line - 1; - else if (cmpVal < 0) - lo = line + 1; - else { - break; - } - } - - bool partial = hi < lo; - if (partial) { - // Didn't find an exact match, but 'lo' now points to the first entry - // lexicographically greater than the current code, so that will - // be the first entry with the current code as a prefix, if it exists. - line = lo; - _predictiveDictActLine = NULL; - } else { - _predictiveDictActLine = _predictiveDictLine[line]; - } - - _currentWord.clear(); - _wordNumber = 0; - if (0 == strncmp(_predictiveDictLine[line], _currentCode.c_str(), _currentCode.size())) { - char tmp[MAXLINELEN]; - strncpy(tmp, _predictiveDictLine[line], MAXLINELEN); - tmp[MAXLINELEN - 1] = 0; - char *tok = strtok(tmp, " "); - tok = strtok(NULL, " "); - _currentWord = Common::String(tok, _currentCode.size()); - return true; - } else { - return false; - } -} - -} // End of namespace Agi diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index bcfd41e05b6..fdd7750af9e 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -47,6 +47,7 @@ const char * const ThemeEngine::kImageLogo = "logo.bmp"; const char * const ThemeEngine::kImageLogoSmall = "logo_small.bmp"; const char * const ThemeEngine::kImageSearch = "search.bmp"; const char * const ThemeEngine::kImageEraser = "eraser.bmp"; +const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp"; struct TextDrawData { const Graphics::Font *_fontPtr; diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index 42ccc57ce87..e26a584354c 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -35,7 +35,7 @@ #include "graphics/pixelformat.h" -#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.9" +#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.10" class OSystem; @@ -229,6 +229,7 @@ public: static const char *const kImageLogoSmall; ///< ScummVM logo used in the GMM static const char *const kImageSearch; ///< Search tool image used in the launcher static const char *const kImageEraser; ///< Clear input image used in the launcher + static const char *const kImageDelbtn; ///< Delete characters in the predictive dialog /** * Graphics mode enumeration. diff --git a/gui/module.mk b/gui/module.mk index df6b76172a7..d272bb03131 100644 --- a/gui/module.mk +++ b/gui/module.mk @@ -13,6 +13,7 @@ MODULE_OBJS := \ message.o \ object.o \ options.o \ + predictivedialog.o \ saveload.o \ themebrowser.o \ ThemeEngine.o \ diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp new file mode 100644 index 00000000000..ac329867d29 --- /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 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 diff --git a/gui/predictivedialog.h b/gui/predictivedialog.h new file mode 100644 index 00000000000..6d7cd320b71 --- /dev/null +++ b/gui/predictivedialog.h @@ -0,0 +1,142 @@ +/* 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. + */ + +#ifndef GLOBAL_DIALOGS_H +#define GLOBAL_DIALOGS_H + +#include "gui/dialog.h" +#include "common/str.h" +#include "common/stream.h" + +namespace GUI { + +class EditTextWidget; +class ButtonWidget; +class PicButtonWidget; + +enum ButtonId { + kBtn1Act = 0, + kBtn2Act = 1, + kBtn3Act = 2, + kBtn4Act = 3, + kBtn5Act = 4, + kBtn6Act = 5, + kBtn7Act = 6, + kBtn8Act = 7, + kBtn9Act = 8, + kNextAct = 9, + kAddAct = 10, + kDelAct = 11, + kCancelAct = 12, + kOkAct = 13, + kModeAct = 14, + kBtn0Act = 15, + kNoAct = -1 +}; + +enum { + kRepeatDelay = 500 +}; + +enum { + MAXLINELEN = 80, + MAXWORDLEN = 24, + MAXWORD = 50 +}; + +class PredictiveDialog : public GUI::Dialog { + typedef Common::String String; + +public: + PredictiveDialog(); + ~PredictiveDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void handleKeyDown(Common::KeyState state); + virtual void handleTickle(); + + char * getResult() { return _predictiveResult; } +private: + struct Dict { + char **dictLine; + char *dictText; + char *dictActLine; // using only for united dict... + int32 dictLineCount; + int32 dictTextSize; + String nameDict; + String fnameDict; + }; + + uint8 countWordsInString(char *str); + void bringWordtoTop(char *str, int wordnum); + void loadDictionary(Common::SeekableReadStream *in, Dict &dict); + void loadAllDictionary(Dict &dict); + void addWordToDict(); + void addWord(Dict &dict, const String &word, const String &code); + bool searchWord(char *where, const String &whatCode); + int binarySearch(char **dictLine, const String &code, int dictLineCount); + bool matchWord(); + void processBtnActive(ButtonId active); + void pressEditText(); + + void saveUserDictToFile(); + + void mergeDicts(); +private: + Dict _unitedDict; + Dict _predictiveDict; + Dict _userDict; + + int _mode; + ButtonId _lastbutton; + + bool _userDictHasChanged; + + int _wordNumber; + uint8 _numMatchingWords; + char _predictiveResult[40]; + + String _currentCode; + String _currentWord; + String _prefix; + + uint32 _curTime, _lastTime; + ButtonId _lastPressBtn; + + char _temp[MAXWORDLEN + 1]; + int _repeatcount[MAXWORDLEN]; + + char *_memoryList[MAXWORD]; + int _numMemory; + + String _search; + + bool _navigationwithkeys; +private: + EditTextWidget *_edittext; + ButtonWidget *_modebutton; + ButtonWidget *_delbtn; + ButtonWidget *_addBtn; +}; + +} // namespace GUI + +#endif diff --git a/gui/themes/default.inc b/gui/themes/default.inc index f6096ce342f..c1047c0994b 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -599,6 +599,954 @@ "/> " " " " " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " " " " " @@ -615,6 +1563,8 @@ " " " " " " +" " +" " " " @@ -1187,28 +2137,28 @@ " " " " " " -" " -" " -" " +" " +" " -" " -" " -" " -" " -" " -" " " " " " @@ -1454,834 +2404,94 @@ "/> " " " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " +" " +" " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " +" " +" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " " " -" " -" " -" " " " +" " +" " +" " +" " " " -" " -" " -" " -" " +" " -" " -" " -" " +" " +" " +" " +" " +" " +" " +" " +" " +" " " " -" " -" " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " +" " +" " " " +" " " " " " " " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " " " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip index 9f7e4b5e0ee..f60f3cb29e5 100644 Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ diff --git a/gui/themes/scummclassic/THEMERC b/gui/themes/scummclassic/THEMERC index fb8177f3290..68bdfde6fdc 100644 --- a/gui/themes/scummclassic/THEMERC +++ b/gui/themes/scummclassic/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.9:ScummVM Classic Theme:No Author] +[SCUMMVM_STX0.8.10:ScummVM Classic Theme:No Author] diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index bdccea418b9..0d8fe3150d4 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -42,6 +42,8 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index 87246023c47..4dc04f27054 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -43,6 +43,9 @@ + + + @@ -913,4 +916,97 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip index 56dc162a0a6..803a9336c44 100644 Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ diff --git a/gui/themes/scummmodern/THEMERC b/gui/themes/scummmodern/THEMERC index 628ab67f426..734138998ad 100644 --- a/gui/themes/scummmodern/THEMERC +++ b/gui/themes/scummmodern/THEMERC @@ -1 +1 @@ -[SCUMMVM_STX0.8.9:ScummVM Modern Theme:No Author] +[SCUMMVM_STX0.8.10:ScummVM Modern Theme:No Author] diff --git a/gui/themes/scummmodern/delbtn.bmp b/gui/themes/scummmodern/delbtn.bmp new file mode 100644 index 00000000000..aeb3b7f5f54 Binary files /dev/null and b/gui/themes/scummmodern/delbtn.bmp differ diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx index 5f7cc69acdb..6c2e4bb8739 100644 --- a/gui/themes/scummmodern/scummmodern_gfx.stx +++ b/gui/themes/scummmodern/scummmodern_gfx.stx @@ -100,6 +100,7 @@ + diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 6acccebccca..676de964e26 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -49,6 +49,8 @@ + + - + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 49aad1070a0..00ff440d441 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -33,6 +33,9 @@ + + + @@ -911,4 +914,96 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/widgets/editable.h b/gui/widgets/editable.h index 4b51ac91453..7e453c1204e 100644 --- a/gui/widgets/editable.h +++ b/gui/widgets/editable.h @@ -70,18 +70,17 @@ public: virtual void handleTickle(); virtual bool handleKeyDown(Common::KeyState state); - virtual void reflowLayout(); + bool setCaretPos(int newPos); + protected: virtual void startEditMode() = 0; virtual void endEditMode() = 0; - virtual void abortEditMode() = 0; - + virtual void abortEditMode() = 0; virtual Common::Rect getEditRect() const = 0; virtual int getCaretOffset() const; - void drawCaret(bool erase); - bool setCaretPos(int newPos); + void drawCaret(bool erase); bool adjustOffset(); void makeCaretVisible();