ALL: synced with scummvm

This commit is contained in:
Pawel Kolodziejski 2012-03-25 12:55:00 +02:00
parent 522885137f
commit 0517f256d7
23 changed files with 2536 additions and 833 deletions

View File

@ -510,7 +510,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
END_OPTION
#endif
#if defined (WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
// Optional console window on Windows (default: enabled)
DO_LONG_OPTION_BOOL("console")
END_OPTION

View File

@ -195,7 +195,7 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const
}
// On creation the engine should have set up all debug levels so we can use
// the command line arugments here
// the command line arguments here
Common::StringTokenizer tokenizer(edebuglevels, " ,");
while (!tokenizer.empty()) {
Common::String token = tokenizer.nextToken();
@ -206,6 +206,12 @@ static Common::Error runGame(const EnginePlugin *plugin, OSystem &system, const
// Initialize any game-specific keymaps
engine->initKeymap();
// Set default values to the custom engine options
const ExtraGuiOptions engineOptions = (*plugin)->getExtraGuiOptions(ConfMan.getActiveDomainName());
for (uint i = 0; i < engineOptions.size(); i++) {
ConfMan.registerDefault(engineOptions[i].configOption, engineOptions[i].defaultState);
}
// Inform backend that the engine is about to be run
system.engineInit();

View File

@ -64,6 +64,14 @@ const struct GameOpt {
{ GUIO_RENDERPC9821, "pc9821" },
{ GUIO_RENDERPC9801, "pc9801" },
{ GUIO_GAMEOPTIONS1, "gameOption1" },
{ GUIO_GAMEOPTIONS2, "gameOption2" },
{ GUIO_GAMEOPTIONS3, "gameOption3" },
{ GUIO_GAMEOPTIONS4, "gameOption4" },
{ GUIO_GAMEOPTIONS5, "gameOption5" },
{ GUIO_GAMEOPTIONS6, "gameOption6" },
{ GUIO_GAMEOPTIONS7, "gameOption7" },
{ GUIO_NONE, 0 }
};

View File

@ -56,6 +56,16 @@
#define GUIO_RENDERPC9821 "\037"
#define GUIO_RENDERPC9801 "\040"
// Special GUIO flags for the AdvancedDetector's caching of game specific
// options.
#define GUIO_GAMEOPTIONS1 "\041"
#define GUIO_GAMEOPTIONS2 "\042"
#define GUIO_GAMEOPTIONS3 "\043"
#define GUIO_GAMEOPTIONS4 "\044"
#define GUIO_GAMEOPTIONS5 "\045"
#define GUIO_GAMEOPTIONS6 "\046"
#define GUIO_GAMEOPTIONS7 "\047"
#define GUIO0() (GUIO_NONE)
#define GUIO1(a) (a)
#define GUIO2(a,b) (a b)
@ -63,6 +73,8 @@
#define GUIO4(a,b,c,d) (a b c d)
#define GUIO5(a,b,c,d,e) (a b c d e)
#define GUIO6(a,b,c,d,e,f) (a b c d e f)
#define GUIO7(a,b,c,d,e,f,g) (a b c d e f g)
#define GUIO8(a,b,c,d,e,f,g,h) (a b c d e f g h)
namespace Common {

View File

@ -167,6 +167,25 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
return detectedGames;
}
const ExtraGuiOptions AdvancedMetaEngine::getExtraGuiOptions(const Common::String &target) const {
if (!_extraGuiOptions)
return ExtraGuiOptions();
// Query the GUI options
const Common::String guiOptionsString = ConfMan.get("guioptions", target);
const Common::String guiOptions = parseGameGUIOptions(guiOptionsString);
ExtraGuiOptions options;
// Add all the applying extra GUI options.
for (const ADExtraGuiOptionsMap *entry = _extraGuiOptions; entry->guioFlag; ++entry) {
if (guiOptions.contains(entry->guioFlag))
options.push_back(entry->option);
}
return options;
}
Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
assert(engine);
@ -562,8 +581,9 @@ GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const {
return GameDescriptor();
}
AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids)
: _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameids(gameids) {
AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions)
: _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameids(gameids),
_extraGuiOptions(extraGuiOptions) {
_md5Bytes = 5000;
_singleid = NULL;

View File

@ -133,6 +133,24 @@ enum ADFlags {
};
/**
* Map entry for mapping GUIO_GAMEOPTIONS* to their ExtraGuiOption
* description.
*/
struct ADExtraGuiOptionsMap {
/**
* GUIO_GAMEOPTION* string.
*/
const char *guioFlag;
/**
* The associated option.
*/
ExtraGuiOption option;
};
#define AD_EXTRA_GUI_OPTIONS_TERMINATOR { 0, { 0, 0, 0, 0 } }
/**
* A MetaEngine implementation based around the advanced detector code.
*/
@ -158,6 +176,11 @@ protected:
*/
const PlainGameDescriptor *_gameids;
/**
* A map containing all the extra game GUI options the engine supports.
*/
const ADExtraGuiOptionsMap * const _extraGuiOptions;
/**
* The number of bytes to compute MD5 sum for. The AdvancedDetector
* is primarily based on computing and matching MD5 checksums of files.
@ -211,7 +234,7 @@ protected:
const char * const *_directoryGlobs;
public:
AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids);
AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions = 0);
/**
* Returns list of targets supported by the engine.
@ -225,6 +248,8 @@ public:
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
protected:
// To be implemented by subclasses
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const = 0;

View File

@ -24,6 +24,7 @@
#include "common/scummsys.h"
#include "common/error.h"
#include "common/array.h"
#include "engines/game.h"
#include "engines/savestate.h"
@ -38,6 +39,19 @@ class FSList;
class String;
}
/**
* Per-game extra GUI options structure.
* Currently, this can only be used for options with checkboxes.
*/
struct ExtraGuiOption {
const char *label; // option label, e.g. "Fullscreen mode"
const char *tooltip; // option tooltip (when the mouse hovers above it)
const char *configOption; // confMan key, e.g. "fullscreen"
bool defaultState; // the detault state of the checkbox (checked or not)
};
typedef Common::Array<ExtraGuiOption> ExtraGuiOptions;
/**
* A meta engine is essentially a factory for Engine instances with the
* added ability of listing and detecting supported games.
@ -97,6 +111,16 @@ public:
return SaveStateList();
}
/**
* Return a list of extra GUI options.
* Currently, this only supports options with checkboxes.
*
* The default implementation returns an empty list.
*/
virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const {
return ExtraGuiOptions();
}
/**
* Return the maximum save slot that the engine supports.
*

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::kImageSearch = "search.bmp";
const char * const ThemeEngine::kImageEraser = "eraser.bmp";
const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp";
struct TextDrawData {
const Graphics::Font *_fontPtr;

View File

@ -35,7 +35,7 @@
#include "graphics/pixelformat.h"
#define SCUMMVM_THEME_VERSION_STR "SCUMMVM_STX0.8.8"
#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.

View File

@ -145,11 +145,28 @@ protected:
CheckboxWidget *_globalMIDIOverride;
CheckboxWidget *_globalMT32Override;
CheckboxWidget *_globalVolumeOverride;
ExtraGuiOptions _engineOptions;
};
EditGameDialog::EditGameDialog(const String &domain, const String &desc)
: OptionsDialog(domain, "GameOptions") {
// Retrieve all game specific options.
const EnginePlugin *plugin = 0;
// To allow for game domains without a gameid.
// TODO: Is it intentional that this is still supported?
String gameId(ConfMan.get("gameid", domain));
if (gameId.empty())
gameId = domain;
// Retrieve the plugin, since we need to access the engine's MetaEngine
// implementation.
EngineMan.findGame(gameId, &plugin);
if (plugin) {
_engineOptions = (*plugin)->getExtraGuiOptions(domain);
} else {
warning("Plugin for target \"%s\" not found! Game specific settings might be missing", domain.c_str());
}
// GAME: Path to game data (r/o), extra data (r/o), and save data (r/w)
String gamePath(ConfMan.get("path", _domain));
String extraPath(ConfMan.get("extrapath", _domain));
@ -208,7 +225,16 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
}
//
// 2) The graphics tab
// 2) The engine tab (shown only if there are custom engine options)
//
if (_engineOptions.size() > 0) {
tab->addTab(_("Engine"));
addEngineControls(tab, "GameOptions_Engine.", _engineOptions);
}
//
// 3) The graphics tab
//
_graphicsTabId = tab->addTab(g_system->getOverlayWidth() > 320 ? _("Graphics") : _("GFX"));
@ -220,7 +246,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
addGraphicControls(tab, "GameOptions_Graphics.");
//
// 3) The audio tab
// 4) The audio tab
//
tab->addTab(_("Audio"));
@ -233,7 +259,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
addSubtitleControls(tab, "GameOptions_Audio.");
//
// 4) The volume tab
// 5) The volume tab
//
if (g_system->getOverlayWidth() > 320)
tab->addTab(_("Volume"));
@ -248,7 +274,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
addVolumeControls(tab, "GameOptions_Volume.");
/* ResidualVM: do not enable midi
//
// 5) The MIDI tab
// 6) The MIDI tab
//
if (!_guioptions.contains(GUIO_NOMIDI)) {
tab->addTab(_("MIDI"));
@ -262,7 +288,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
}
//
// 6) The MT-32 tab
// 7) The MT-32 tab
//
if (!_guioptions.contains(GUIO_NOMIDI)) {
tab->addTab(_("MT-32"));
@ -276,7 +302,7 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
}*/
//
// 7) The Paths tab
// 8) The Paths tab
//
if (g_system->getOverlayWidth() > 320)
tab->addTab(_("Paths"));
@ -311,7 +337,6 @@ EditGameDialog::EditGameDialog(const String &domain, const String &desc)
_savePathClearButton = addClearButton(tab, "GameOptions_Paths.SavePathClearButton", kCmdSavePathClear);
// Activate the first tab
tab->setActiveTab(0);
_tabWidget = tab;
@ -386,6 +411,19 @@ void EditGameDialog::open() {
_langPopUp->setEnabled(false);
}
// Set the state of engine-specific checkboxes
for (uint j = 0; j < _engineOptions.size(); ++j) {
// The default values for engine-specific checkboxes are not set when
// ScummVM starts, as this would require us to load and poll all of the
// engine plugins on startup. Thus, we set the state of each custom
// option checkbox to what is specified by the engine plugin, and
// update it only if a value has been set in the configuration of the
// currently selected game.
bool isChecked = _engineOptions[j].defaultState;
if (ConfMan.hasKey(_engineOptions[j].configOption, _domain))
isChecked = ConfMan.getBool(_engineOptions[j].configOption, _domain);
_engineCheckboxes[j]->setState(isChecked);
}
const Common::PlatformDescription *p = Common::g_platforms;
const Common::Platform platform = Common::parsePlatform(ConfMan.get("platform", _domain));
@ -429,6 +467,11 @@ void EditGameDialog::close() {
ConfMan.removeKey("platform", _domain);
else
ConfMan.set("platform", Common::getPlatformCode(platform), _domain);
// Set the state of engine-specific checkboxes
for (uint i = 0; i < _engineOptions.size(); i++) {
ConfMan.setBool(_engineOptions[i].configOption, _engineCheckboxes[i]->getState(), _domain);
}
}
OptionsDialog::close();
}

View File

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

View File

@ -890,6 +890,22 @@ void OptionsDialog::addVolumeControls(GuiObject *boss, const Common::String &pre
_enableVolumeSettings = true;
}
void OptionsDialog::addEngineControls(GuiObject *boss, const Common::String &prefix, const ExtraGuiOptions &engineOptions) {
// Note: up to 7 engine options can currently fit on screen (the most that
// can fit in a 320x200 screen with the classic theme).
// TODO: Increase this number by including the checkboxes inside a scroll
// widget. The appropriate number of checkboxes will need to be added to
// the theme files.
uint i = 1;
ExtraGuiOptions::const_iterator iter;
for (iter = engineOptions.begin(); iter != engineOptions.end(); ++iter, ++i) {
Common::String id = Common::String::format("%d", i);
_engineCheckboxes.push_back(new CheckboxWidget(boss,
prefix + "customOption" + id + "Checkbox", _(iter->label), _(iter->tooltip)));
}
}
bool OptionsDialog::loadMusicDeviceSetting(PopUpWidget *popup, Common::String setting, MusicType preferredType) {
if (!popup || !popup->isEnabled())
return true;
@ -1369,11 +1385,11 @@ void GlobalOptionsDialog::reflowLayout() {
if (_midiTabId != -1) {
_tabWidget->setActiveTab(_midiTabId);
/* Residual do not use it
_tabWidget->removeWidget(_soundFontClearButton);
_soundFontClearButton->setNext(0);
delete _soundFontClearButton;
_soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd);
_soundFontClearButton = addClearButton(_tabWidget, "GlobalOptions_MIDI.mcFontClearButton", kClearSoundFontCmd);*/
}
if (_pathsTabId != -1) {

View File

@ -22,6 +22,8 @@
#ifndef OPTIONS_DIALOG_H
#define OPTIONS_DIALOG_H
#include "engines/metaengine.h"
#include "gui/dialog.h"
#include "common/str.h"
#include "audio/mididrv.h"
@ -44,6 +46,8 @@ class RadiobuttonGroup;
class RadiobuttonWidget;
class OptionsDialog : public Dialog {
typedef Common::Array<CheckboxWidget *> CheckboxWidgetList;
public:
OptionsDialog(const Common::String &domain, int x, int y, int w, int h);
OptionsDialog(const Common::String &domain, const Common::String &name);
@ -74,6 +78,7 @@ protected:
// The default value is the launcher's non-scaled talkspeed value. When SCUMM uses the widget,
// it uses its own scale
void addSubtitleControls(GuiObject *boss, const Common::String &prefix, int maxSliderVal = 255);
void addEngineControls(GuiObject *boss, const Common::String &prefix, const ExtraGuiOptions &engineOptions);
void setGraphicSettingsState(bool enabled);
void setAudioSettingsState(bool enabled);
@ -181,6 +186,11 @@ protected:
//Theme Options
//
Common::String _oldTheme;
//
// Engine-specific controls
//
CheckboxWidgetList _engineCheckboxes;
};

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.8:ResidualVM Modern Theme:No Author]
[SCUMMVM_STX0.8.10:ResidualVM Modern Theme:No Author]

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

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

View File

@ -49,6 +49,8 @@
<def var = 'Tooltip.XDelta' value = '16'/> <!-- basically cursor size -->
<def var = 'Tooltip.YDelta' value = '32'/>
<def var = 'Predictive.Button.Width' value = '60' />
<widget name = 'OptionsLabel'
size = '115, Globals.Line.Height'
textalign = 'right'
@ -59,8 +61,7 @@
<widget name = 'Button'
size = '108, 24'
/>
/>
<widget name = 'Slider'
size = '128, 18'
@ -642,6 +643,32 @@
</layout>
</dialog>
<dialog name = 'GameOptions_Engine' overlays = 'Dialog.GameOptions.TabWidget' shading = 'dim'>
<layout type = 'vertical' padding = '16, 16, 16, 16'>
<widget name = 'customOption1Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption2Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption3Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption4Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption5Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption6Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption7Checkbox'
type = 'Checkbox'
/>
</layout>
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
<layout type = 'vertical' padding = '16, 16, 16, 16' center = 'true'>
<widget name = 'Logo'
@ -899,5 +926,99 @@
type = 'Button'
/>
</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>
</layout_info>

View File

@ -33,6 +33,9 @@
<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
<def var = 'Predictive.Button.Width' value = '45' />
<def var = 'Predictive.Button.Height' value = '15' />
<widget name = 'Button'
size = '72, 16'
/>
@ -637,6 +640,32 @@
</layout>
</dialog>
<dialog name = 'GameOptions_Engine' overlays = 'Dialog.GameOptions.TabWidget' shading = 'dim'>
<layout type = 'vertical' padding = '8, 8, 8, 8'>
<widget name = 'customOption1Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption2Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption3Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption4Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption5Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption6Checkbox'
type = 'Checkbox'
/>
<widget name = 'customOption7Checkbox'
type = 'Checkbox'
/>
</layout>
</dialog>
<dialog name = 'GlobalMenu' overlays = 'screen_center'>
<layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true' spacing='2'>
<widget name = 'Title'
@ -885,4 +914,96 @@
/>
</layout>
</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>

View File

@ -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();