GUI: Move predictive dialog to common gui

This commit is contained in:
Oleksiy Kurochko 2012-02-22 17:15:02 +02:00 committed by Eugene Sandulenko
parent 3b57dc9164
commit ec32ccb6d6
22 changed files with 2549 additions and 1503 deletions

View file

@ -46,6 +46,8 @@
#include "agi/keyboard.h" #include "agi/keyboard.h"
#include "agi/menu.h" #include "agi/menu.h"
#include "gui/predictivedialog.h"
namespace Agi { namespace Agi {
void AgiEngine::allowSynthetic(bool allow) { void AgiEngine::allowSynthetic(bool allow) {
@ -58,9 +60,25 @@ void AgiEngine::processEvents() {
while (_eventMan->pollEvent(event)) { while (_eventMan->pollEvent(event)) {
switch (event.type) { switch (event.type) {
case Common::EVENT_PREDICTIVE_DIALOG: case Common::EVENT_PREDICTIVE_DIALOG: {
if (_predictiveDialogRunning) GUI::PredictiveDialog _predictiveDialog;
break; _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 (predictiveDialog()) {
if (_game.inputMode == INPUT_NORMAL) { if (_game.inputMode == INPUT_NORMAL) {
strcpy((char *)_game.inputBuffer, _predictiveResult); strcpy((char *)_game.inputBuffer, _predictiveResult);
@ -75,6 +93,8 @@ void AgiEngine::processEvents() {
keyEnqueue(_predictiveResult[n]); keyEnqueue(_predictiveResult[n]);
} }
} }
*/
}
break; break;
case Common::EVENT_LBUTTONDOWN: case Common::EVENT_LBUTTONDOWN:
key = BUTTON_LEFT; key = BUTTON_LEFT;
@ -131,65 +151,46 @@ void AgiEngine::processEvents() {
switch (key = event.kbd.keycode) { switch (key = event.kbd.keycode) {
case Common::KEYCODE_LEFT: case Common::KEYCODE_LEFT:
case Common::KEYCODE_KP4: case Common::KEYCODE_KP4:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP4) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_LEFT; key = KEY_LEFT;
break; break;
case Common::KEYCODE_RIGHT: case Common::KEYCODE_RIGHT:
case Common::KEYCODE_KP6: case Common::KEYCODE_KP6:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP6) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_RIGHT; key = KEY_RIGHT;
break; break;
case Common::KEYCODE_UP: case Common::KEYCODE_UP:
case Common::KEYCODE_KP8: case Common::KEYCODE_KP8:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP8) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_UP; key = KEY_UP;
break; break;
case Common::KEYCODE_DOWN: case Common::KEYCODE_DOWN:
case Common::KEYCODE_KP2: case Common::KEYCODE_KP2:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP2) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_DOWN; key = KEY_DOWN;
break; break;
case Common::KEYCODE_PAGEUP: case Common::KEYCODE_PAGEUP:
case Common::KEYCODE_KP9: case Common::KEYCODE_KP9:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP9) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_UP_RIGHT; key = KEY_UP_RIGHT;
break; break;
case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_PAGEDOWN:
case Common::KEYCODE_KP3: case Common::KEYCODE_KP3:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP3) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_DOWN_RIGHT; key = KEY_DOWN_RIGHT;
break; break;
case Common::KEYCODE_HOME: case Common::KEYCODE_HOME:
case Common::KEYCODE_KP7: case Common::KEYCODE_KP7:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP7) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_UP_LEFT; key = KEY_UP_LEFT;
break; break;
case Common::KEYCODE_END: case Common::KEYCODE_END:
case Common::KEYCODE_KP1: case Common::KEYCODE_KP1:
if (_predictiveDialogRunning && key == Common::KEYCODE_KP1) if (_allowSynthetic || !event.synthetic)
key = event.kbd.ascii;
else if (_allowSynthetic || !event.synthetic)
key = KEY_DOWN_LEFT; key = KEY_DOWN_LEFT;
break; break;
case Common::KEYCODE_KP5: case Common::KEYCODE_KP5:
if (_predictiveDialogRunning) key = KEY_STATIONARY;
key = event.kbd.ascii;
else
key = KEY_STATIONARY;
break; break;
case Common::KEYCODE_PLUS: case Common::KEYCODE_PLUS:
key = '+'; key = '+';
@ -218,7 +219,7 @@ void AgiEngine::processEvents() {
case Common::KEYCODE_F6: case Common::KEYCODE_F6:
key = 0x4000; key = 0x4000;
break; break;
case Common::KEYCODE_F7: case Common::KEYCODE_F7:
key = 0x4100; key = 0x4100;
break; break;
case Common::KEYCODE_F8: case Common::KEYCODE_F8:
@ -575,10 +576,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_oldMode = INPUT_NONE; _oldMode = INPUT_NONE;
_predictiveDialogRunning = false;
_predictiveDictText = NULL;
_predictiveDictLine = NULL;
_predictiveDictLineCount = 0;
_firstSlot = 0; _firstSlot = 0;
resetControllers(); resetControllers();
@ -684,9 +681,6 @@ AgiEngine::~AgiEngine() {
_gfx->deinitMachine(); _gfx->deinitMachine();
delete _gfx; delete _gfx;
delete _console; delete _console;
free(_predictiveDictLine);
free(_predictiveDictText);
} }
Common::Error AgiBase::init() { Common::Error AgiBase::init() {

View file

@ -43,6 +43,7 @@
#include "agi/logic.h" #include "agi/logic.h"
#include "agi/sound.h" #include "agi/sound.h"
#include "gui/predictivedialog.h"
namespace Common { namespace Common {
class RandomSource; class RandomSource;
@ -1078,7 +1079,6 @@ public:
void clearPrompt(bool useBlackBg = false); void clearPrompt(bool useBlackBg = false);
void clearLines(int, int, int); void clearLines(int, int, int);
void flushLines(int, int); void flushLines(int, int);
bool predictiveDialog();
private: private:
void printStatus(const char *message, ...) GCC_PRINTF(2, 3); void printStatus(const char *message, ...) GCC_PRINTF(2, 3);
@ -1088,16 +1088,6 @@ private:
void loadDict(); void loadDict();
bool matchWord(); 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: public:
char _predictiveResult[40]; char _predictiveResult[40];

View file

@ -133,6 +133,19 @@ int AgiEngine::handleController(int key) {
if (key == BUTTON_LEFT && if (key == BUTTON_LEFT &&
(int)_mouse.y >= _game.lineUserInput * CHAR_LINES && (int)_mouse.y >= _game.lineUserInput * CHAR_LINES &&
(int)_mouse.y <= (_game.lineUserInput + 1) * 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 (predictiveDialog()) {
if (_game.inputMode == INPUT_NONE) { if (_game.inputMode == INPUT_NONE) {
for (int n = 0; _predictiveResult[n]; n++) for (int n = 0; _predictiveResult[n]; n++)
@ -142,6 +155,7 @@ int AgiEngine::handleController(int key) {
handleKeys(KEY_ENTER); handleKeys(KEY_ENTER);
} }
} }
*/
return true; return true;
} }
@ -217,6 +231,17 @@ void AgiEngine::handleGetstring(int key) {
case BUTTON_LEFT: case BUTTON_LEFT:
if ((int)_mouse.y >= _stringdata.y * CHAR_LINES && if ((int)_mouse.y >= _stringdata.y * CHAR_LINES &&
(int)_mouse.y <= (_stringdata.y + 1) * 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()) { if (predictiveDialog()) {
strcpy(_game.strings[_stringdata.str], _predictiveResult); strcpy(_game.strings[_stringdata.str], _predictiveResult);
newInputMode(INPUT_NORMAL); newInputMode(INPUT_NORMAL);
@ -224,6 +249,7 @@ void AgiEngine::handleGetstring(int key) {
_stringdata.y, ' ', _game.colorFg, _game.colorBg); _stringdata.y, ' ', _game.colorFg, _game.colorBg);
return; return;
} }
*/
} }
break; break;
case KEY_ENTER: case KEY_ENTER:

View file

@ -28,7 +28,6 @@ MODULE_OBJS := \
preagi_mickey.o \ preagi_mickey.o \
preagi_troll.o \ preagi_troll.o \
preagi_winnie.o \ preagi_winnie.o \
predictive.o \
saveload.o \ saveload.o \
sound.o \ sound.o \
sound_2gs.o \ sound_2gs.o \

View file

@ -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<Common::String> 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

View file

@ -47,6 +47,7 @@ const char * const ThemeEngine::kImageLogo = "logo.bmp";
const char * const ThemeEngine::kImageLogoSmall = "logo_small.bmp"; const char * const ThemeEngine::kImageLogoSmall = "logo_small.bmp";
const char * const ThemeEngine::kImageSearch = "search.bmp"; const char * const ThemeEngine::kImageSearch = "search.bmp";
const char * const ThemeEngine::kImageEraser = "eraser.bmp"; const char * const ThemeEngine::kImageEraser = "eraser.bmp";
const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp";
struct TextDrawData { struct TextDrawData {
const Graphics::Font *_fontPtr; const Graphics::Font *_fontPtr;

View file

@ -35,7 +35,7 @@
#include "graphics/pixelformat.h" #include "graphics/pixelformat.h"
#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.9" #define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.10"
class OSystem; class OSystem;
@ -229,6 +229,7 @@ public:
static const char *const kImageLogoSmall; ///< ScummVM logo used in the GMM 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 kImageSearch; ///< Search tool image used in the launcher
static const char *const kImageEraser; ///< Clear input 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. * Graphics mode enumeration.

View file

@ -13,6 +13,7 @@ MODULE_OBJS := \
message.o \ message.o \
object.o \ object.o \
options.o \ options.o \
predictivedialog.o \
saveload.o \ saveload.o \
themebrowser.o \ themebrowser.o \
ThemeEngine.o \ ThemeEngine.o \

917
gui/predictivedialog.cpp Normal file
View file

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

142
gui/predictivedialog.h Normal file
View file

@ -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

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -1 +1 @@
[SCUMMVM_STX0.8.9:ScummVM Classic Theme:No Author] [SCUMMVM_STX0.8.10:ScummVM Classic Theme:No Author]

View file

@ -42,6 +42,8 @@
<def var = 'Tooltip.XDelta' value = '16'/> <!-- basically cursor size --> <def var = 'Tooltip.XDelta' value = '16'/> <!-- basically cursor size -->
<def var = 'Tooltip.YDelta' value = '16'/> <def var = 'Tooltip.YDelta' value = '16'/>
<def var = 'Predictive.Button.Width' value = '60' />
<widget name = 'OptionsLabel' <widget name = 'OptionsLabel'
size = '110, Globals.Line.Height' size = '110, Globals.Line.Height'
textalign = 'right' textalign = 'right'
@ -911,4 +913,98 @@
/> />
</layout> </layout>
</dialog> </dialog>
<dialog name = 'Predictive' overlays = 'screen_center'>
<layout type = 'vertical' padding = '5, 5, 5, 5' center = 'true'>
<widget name = 'Headline'
height = 'Globals.Line.Height'
width = '210'
textalign = 'center'
/>
<layout type = 'horizontal' padding = '5, 5, 5, 5'>
<widget name = 'Word'
width = '190'
height = 'Globals.Button.Height'
/>
<widget name = 'Delete'
width = '20'
height = 'Globals.Button.Height'
/>
</layout>
<space size = '5' />
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button1'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button2'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button3'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button4'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button5'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button6'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button7'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button8'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button9'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Pre'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button0'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Next'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<space size = '5' />
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Add'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<space size = '22'/>
<widget name = 'Cancel'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'OK'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
</layout>
</dialog>
</layout_info> </layout_info>

View file

@ -43,6 +43,9 @@
<def var = 'Tooltip.XDelta' value = '8'/> <!-- basically cursor size --> <def var = 'Tooltip.XDelta' value = '8'/> <!-- basically cursor size -->
<def var = 'Tooltip.YDelta' value = '8'/> <def var = 'Tooltip.YDelta' value = '8'/>
<def var = 'Predictive.Button.Width' value = '45' />
<def var = 'Predictive.Button.Height' value = '15' />
<widget name = 'Button' <widget name = 'Button'
size = '72, 16' size = '72, 16'
/> />
@ -913,4 +916,97 @@
/> />
</layout> </layout>
</dialog> </dialog>
<dialog name = 'Predictive' overlays = 'screen_center'>
<layout type = 'vertical' padding = '1, 1, 1, 1' center = 'true'>
<widget name = 'Headline'
height = 'Globals.Line.Height'
width = '150'
textalign = 'center'
/>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Word'
width = '120'
height = 'Globals.Button.Height'
/>
<widget name = 'Delete'
width = '20'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<!-- <space size = '3' /> -->
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button1'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button2'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button3'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button4'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button5'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button6'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button7'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button8'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button9'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 0'>
<widget name = 'Pre'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button0'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Next'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<space size = '3' />
<layout type = 'horizontal' padding = '3, 3, 0, 3'>
<widget name = 'Add'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<!-- <space size = '22'/> -->
<widget name = 'Cancel'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'OK'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
</layout>
</dialog>
</layout_info> </layout_info>

Binary file not shown.

View file

@ -1 +1 @@
[SCUMMVM_STX0.8.9:ScummVM Modern Theme:No Author] [SCUMMVM_STX0.8.10:ScummVM Modern Theme:No Author]

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

View file

@ -100,6 +100,7 @@
<bitmap filename = 'logo_small.bmp'/> <bitmap filename = 'logo_small.bmp'/>
<bitmap filename = 'search.bmp'/> <bitmap filename = 'search.bmp'/>
<bitmap filename = 'eraser.bmp'/> <bitmap filename = 'eraser.bmp'/>
<bitmap filename = 'delbtn.bmp'/>
</bitmaps> </bitmaps>
<fonts> <fonts>

View file

@ -49,6 +49,8 @@
<def var = 'Tooltip.XDelta' value = '16'/> <!-- basically cursor size --> <def var = 'Tooltip.XDelta' value = '16'/> <!-- basically cursor size -->
<def var = 'Tooltip.YDelta' value = '32'/> <def var = 'Tooltip.YDelta' value = '32'/>
<def var = 'Predictive.Button.Width' value = '60' />
<widget name = 'OptionsLabel' <widget name = 'OptionsLabel'
size = '115, Globals.Line.Height' size = '115, Globals.Line.Height'
textalign = 'right' textalign = 'right'
@ -59,8 +61,7 @@
<widget name = 'Button' <widget name = 'Button'
size = '108, 24' size = '108, 24'
/> />
<widget name = 'Slider' <widget name = 'Slider'
size = '128, 18' size = '128, 18'
@ -925,5 +926,99 @@
type = 'Button' type = 'Button'
/> />
</layout> </layout>
</dialog>
<dialog name = 'Predictive' overlays = 'screen_center'>
<layout type = 'vertical' padding = '5, 5, 5, 5' center = 'true'>
<widget name = 'Headline'
height = 'Globals.Line.Height'
width = '210'
textalign = 'center'
/>
<layout type = 'horizontal' padding = '5, 5, 5, 5'>
<widget name = 'Word'
width = '190'
height = 'Globals.Button.Height'
/>
<widget name = 'Delete'
width = '20'
height = 'Globals.Button.Height'
/>
</layout>
<space size = '5' />
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button1'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button2'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button3'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button4'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button5'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button6'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Button7'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button8'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button9'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Pre'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Button0'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'Next'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
<space size = '5' />
<layout type = 'horizontal' padding = '3, 3, 3, 3'>
<widget name = 'Add'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<space size = '22'/>
<widget name = 'Cancel'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
<widget name = 'OK'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Button.Height'
/>
</layout>
</layout>
</dialog> </dialog>
</layout_info> </layout_info>

View file

@ -33,6 +33,9 @@
<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/> <def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
<def var = 'Predictive.Button.Width' value = '45' />
<def var = 'Predictive.Button.Height' value = '15' />
<widget name = 'Button' <widget name = 'Button'
size = '72, 16' size = '72, 16'
/> />
@ -911,4 +914,96 @@
/> />
</layout> </layout>
</dialog> </dialog>
<dialog name = 'Predictive' overlays = 'screen_center'>
<layout type = 'vertical' center = 'true'>
<widget name = 'Headline'
height = 'Globals.Line.Height'
width = '150'
textalign = 'center'
/>
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Word'
width = '120'
height = 'Globals.Button.Height'
/>
<widget name = 'Delete'
width = '20'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<!-- <space size = '3' /> -->
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Button1'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button2'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button3'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Button4'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button5'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button6'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Button7'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button8'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button9'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Pre'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Button0'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'Next'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
<space size = '2' />
<layout type = 'horizontal' padding = '0, 0, 2, 2'>
<widget name = 'Add'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<!-- <space size = '22'/> -->
<widget name = 'Cancel'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
<widget name = 'OK'
width = 'Globals.Predictive.Button.Width'
height = 'Globals.Predictive.Button.Height'
/>
</layout>
</layout>
</dialog>
</layout_info> </layout_info>

View file

@ -70,18 +70,17 @@ public:
virtual void handleTickle(); virtual void handleTickle();
virtual bool handleKeyDown(Common::KeyState state); virtual bool handleKeyDown(Common::KeyState state);
virtual void reflowLayout(); virtual void reflowLayout();
bool setCaretPos(int newPos);
protected: protected:
virtual void startEditMode() = 0; virtual void startEditMode() = 0;
virtual void endEditMode() = 0; virtual void endEditMode() = 0;
virtual void abortEditMode() = 0; virtual void abortEditMode() = 0;
virtual Common::Rect getEditRect() const = 0; virtual Common::Rect getEditRect() const = 0;
virtual int getCaretOffset() const; virtual int getCaretOffset() const;
void drawCaret(bool erase); void drawCaret(bool erase);
bool setCaretPos(int newPos);
bool adjustOffset(); bool adjustOffset();
void makeCaretVisible(); void makeCaretVisible();