scummvm/gui/editable.cpp
Johannes Schickel ada03c57b3 Prevent editable widgets from adding various characters when F1-F12 is pressed by the user.
Formerly the code just casted the "ascii" value of the key event to "byte"
and thus truncating the character value. Now that would be fine, if we
would not allow values >= 256 in the ascii field, for example 322 for F8
which in turn resulted in a "B" added to the editable field. I just added
a check for the values being in the byte range before doing the cast,
which fixes this.

svn-id: r48967
2010-05-08 18:30:00 +00:00

304 lines
7.3 KiB
C++

/* 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.
*
* $URL$
* $Id$
*/
#include "common/events.h"
#include "gui/editable.h"
#include "gui/GuiManager.h"
namespace GUI {
EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd)
: Widget(boss, x, y, w, h), CommandSender(boss), _cmd(cmd) {
init();
}
EditableWidget::EditableWidget(GuiObject *boss, const String &name, uint32 cmd)
: Widget(boss, name), CommandSender(boss), _cmd(cmd) {
init();
}
void EditableWidget::init() {
_caretVisible = false;
_caretTime = 0;
_caretPos = 0;
_caretInverse = false;
_editScrollOffset = 0;
_font = ThemeEngine::kFontStyleBold;
}
EditableWidget::~EditableWidget() {
}
void EditableWidget::reflowLayout() {
Widget::reflowLayout();
_editScrollOffset = g_gui.getStringWidth(_editString, _font) - getEditRect().width();
if (_editScrollOffset < 0)
_editScrollOffset = 0;
}
void EditableWidget::setEditString(const String &str) {
// TODO: We probably should filter the input string here,
// e.g. using tryInsertChar.
_editString = str;
_caretPos = _editString.size();
}
bool EditableWidget::tryInsertChar(byte c, int pos) {
if ((c >= 32 && c <= 127) || c >= 160) {
_editString.insertChar(c, pos);
return true;
}
return false;
}
void EditableWidget::handleTickle() {
uint32 time = g_system->getMillis();
if (_caretTime < time) {
_caretTime = time + kCaretBlinkTime;
drawCaret(_caretVisible);
}
}
bool EditableWidget::handleKeyDown(Common::KeyState state) {
bool handled = true;
bool dirty = false;
bool forcecaret = false;
// First remove caret
if (_caretVisible)
drawCaret(true);
switch (state.keycode) {
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
// confirm edit and exit editmode
endEditMode();
dirty = true;
break;
case Common::KEYCODE_ESCAPE:
abortEditMode();
dirty = true;
break;
case Common::KEYCODE_BACKSPACE:
if (_caretPos > 0) {
_caretPos--;
_editString.deleteChar(_caretPos);
dirty = true;
sendCommand(_cmd, 0);
}
forcecaret = true;
break;
// Keypad & special keys
// - if num lock is set, we always go to the default case
// - if num lock is not set, we either fall down to the special key case
// or ignore the key press in case of 0 (INSERT), 2 (DOWN), 3 (PGDWN)
// 5, 8 (UP) and 9 (PGUP)
case Common::KEYCODE_KP0:
case Common::KEYCODE_KP2:
case Common::KEYCODE_KP3:
case Common::KEYCODE_KP5:
case Common::KEYCODE_KP8:
case Common::KEYCODE_KP9:
if (state.flags & Common::KBD_NUM)
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
case Common::KEYCODE_KP_PERIOD:
if (state.flags & Common::KBD_NUM) {
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
}
case Common::KEYCODE_DELETE:
if (_caretPos < (int)_editString.size()) {
_editString.deleteChar(_caretPos);
dirty = true;
sendCommand(_cmd, 0);
}
forcecaret = true;
break;
case Common::KEYCODE_KP1:
if (state.flags & Common::KBD_NUM) {
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
}
case Common::KEYCODE_END:
dirty = setCaretPos(_editString.size());
forcecaret = true;
break;
case Common::KEYCODE_KP4:
if (state.flags & Common::KBD_NUM) {
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
}
case Common::KEYCODE_LEFT:
if (_caretPos > 0) {
dirty = setCaretPos(_caretPos - 1);
}
forcecaret = true;
dirty = true;
break;
case Common::KEYCODE_KP6:
if (state.flags & Common::KBD_NUM) {
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
}
case Common::KEYCODE_RIGHT:
if (_caretPos < (int)_editString.size()) {
dirty = setCaretPos(_caretPos + 1);
}
forcecaret = true;
dirty = true;
break;
case Common::KEYCODE_KP7:
if (state.flags & Common::KBD_NUM) {
defaultKeyDownHandler(state, dirty, forcecaret, handled);
break;
}
case Common::KEYCODE_HOME:
dirty = setCaretPos(0);
forcecaret = true;
break;
default:
defaultKeyDownHandler(state, dirty, forcecaret, handled);
}
if (dirty)
draw();
if (forcecaret)
makeCaretVisible();
return handled;
}
void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) {
if (state.ascii < 256 && tryInsertChar((byte)state.ascii, _caretPos)) {
_caretPos++;
dirty = true;
forcecaret = true;
sendCommand(_cmd, 0);
} else {
handled = false;
}
}
int EditableWidget::getCaretOffset() const {
int caretpos = 0;
for (int i = 0; i < _caretPos; i++)
caretpos += g_gui.getCharWidth(_editString[i], _font);
caretpos -= _editScrollOffset;
return caretpos;
}
void EditableWidget::drawCaret(bool erase) {
// Only draw if item is visible
if (!isVisible() || !_boss->isVisible())
return;
Common::Rect editRect = getEditRect();
int x = editRect.left;
int y = editRect.top + 1;
x += getCaretOffset();
if (y < 0 || y + editRect.height() - 2 >= _h)
return;
x += getAbsX();
y += getAbsY();
g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height() - 2), erase);
if (erase) {
if ((uint)_caretPos < _editString.size()) {
GUI::EditableWidget::String chr(_editString[_caretPos]);
int chrWidth = g_gui.getCharWidth(_editString[_caretPos], _font);
g_gui.theme()->drawText(Common::Rect(x, y, x + chrWidth, y + editRect.height() - 2), chr, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, 0, false, _font);
}
}
_caretVisible = !erase;
}
bool EditableWidget::setCaretPos(int newPos) {
assert(newPos >= 0 && newPos <= (int)_editString.size());
_caretPos = newPos;
return adjustOffset();
}
bool EditableWidget::adjustOffset() {
// check if the caret is still within the textbox; if it isn't,
// adjust _editScrollOffset
int caretpos = getCaretOffset();
const int editWidth = getEditRect().width();
if (caretpos < 0) {
// scroll left
_editScrollOffset += caretpos;
return true;
} else if (caretpos >= editWidth) {
// scroll right
_editScrollOffset -= (editWidth - caretpos);
return true;
} else if (_editScrollOffset > 0) {
const int strWidth = g_gui.getStringWidth(_editString, _font);
if (strWidth - _editScrollOffset < editWidth) {
// scroll right
_editScrollOffset = (strWidth - editWidth);
if (_editScrollOffset < 0)
_editScrollOffset = 0;
}
}
return false;
}
void EditableWidget::makeCaretVisible() {
_caretTime = g_system->getMillis() + kCaretBlinkTime;
_caretVisible = true;
drawCaret(false);
}
} // End of namespace GUI