mirror of https://github.com/stella-emu/stella.git
459 lines
10 KiB
C++
459 lines
10 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2013 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//
|
|
// $Id$
|
|
//
|
|
// Based on code from ScummVM - Scumm Interpreter
|
|
// Copyright (C) 2002-2004 The ScummVM project
|
|
//============================================================================
|
|
|
|
#include "Dialog.hxx"
|
|
#include "EditableWidget.hxx"
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EditableWidget::EditableWidget(GuiObject* boss, const GUI::Font& font,
|
|
int x, int y, int w, int h, const string& str)
|
|
: Widget(boss, font, x, y, w, h),
|
|
CommandSender(boss),
|
|
_editable(true),
|
|
_editString(str)
|
|
{
|
|
_caretVisible = false;
|
|
_caretTime = 0;
|
|
_caretPos = 0;
|
|
|
|
_caretInverse = false;
|
|
|
|
_editScrollOffset = 0;
|
|
|
|
_bgcolor = kWidColor;
|
|
_bgcolorhi = kWidColor;
|
|
_textcolor = kTextColor;
|
|
_textcolorhi = kTextColor;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EditableWidget::~EditableWidget()
|
|
{
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EditableWidget::setEditString(const string& str, bool)
|
|
{
|
|
// TODO: We probably should filter the input string here,
|
|
// e.g. using tryInsertChar.
|
|
_editString = str;
|
|
_caretPos = _editString.size();
|
|
|
|
_editScrollOffset = (_font.getStringWidth(_editString) - (getEditRect().width()));
|
|
if (_editScrollOffset < 0)
|
|
_editScrollOffset = 0;
|
|
|
|
if(_editable)
|
|
startEditMode();
|
|
|
|
// Make sure the new string is seen onscreen
|
|
setDirty(); draw();
|
|
}
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EditableWidget::setEditable(bool editable)
|
|
{
|
|
_editable = editable;
|
|
if(_editable)
|
|
setFlags(WIDGET_WANTS_RAWDATA|WIDGET_RETAIN_FOCUS);
|
|
else
|
|
clearFlags(WIDGET_WANTS_RAWDATA|WIDGET_RETAIN_FOCUS);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::tryInsertChar(char c, int pos)
|
|
{
|
|
if(isprint(c) && c != '\"')
|
|
{
|
|
_editString.insert(pos, 1, c);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::handleKeyDown(StellaKey key, StellaMod mod, char ascii)
|
|
{
|
|
if(!_editable)
|
|
return true;
|
|
|
|
// Ignore all alt-mod keys
|
|
if(instance().eventHandler().kbdAlt(mod))
|
|
return true;
|
|
|
|
bool handled = true;
|
|
bool dirty = false;
|
|
|
|
switch(key)
|
|
{
|
|
case KBDK_RETURN:
|
|
// confirm edit and exit editmode
|
|
endEditMode();
|
|
sendCommand(kEditAcceptCmd, 0, _id);
|
|
dirty = true;
|
|
break;
|
|
|
|
case KBDK_ESCAPE:
|
|
abortEditMode();
|
|
sendCommand(kEditCancelCmd, 0, _id);
|
|
dirty = true;
|
|
break;
|
|
|
|
case KBDK_BACKSPACE:
|
|
dirty = killChar(-1);
|
|
if(dirty) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_DELETE:
|
|
dirty = killChar(+1);
|
|
if(dirty) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_LEFT:
|
|
if(instance().eventHandler().kbdControl(mod))
|
|
dirty = specialKeys(key, ascii);
|
|
else if(_caretPos > 0)
|
|
dirty = setCaretPos(_caretPos - 1);
|
|
break;
|
|
|
|
case KBDK_RIGHT:
|
|
if(instance().eventHandler().kbdControl(mod))
|
|
dirty = specialKeys(key, ascii);
|
|
else if(_caretPos < (int)_editString.size())
|
|
dirty = setCaretPos(_caretPos + 1);
|
|
break;
|
|
|
|
case KBDK_HOME:
|
|
dirty = setCaretPos(0);
|
|
break;
|
|
|
|
case KBDK_END:
|
|
dirty = setCaretPos(_editString.size());
|
|
break;
|
|
|
|
default:
|
|
if (instance().eventHandler().kbdControl(mod))
|
|
{
|
|
dirty = specialKeys(key, ascii);
|
|
}
|
|
else if (tryInsertChar(ascii, _caretPos))
|
|
{
|
|
_caretPos++;
|
|
sendCommand(kEditChangedCmd, ascii, _id);
|
|
dirty = true;
|
|
}
|
|
else
|
|
handled = false;
|
|
}
|
|
|
|
if (dirty)
|
|
{
|
|
setDirty(); draw();
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
int EditableWidget::getCaretOffset() const
|
|
{
|
|
int caretpos = 0;
|
|
for (int i = 0; i < _caretPos; i++)
|
|
caretpos += _font.getCharWidth(_editString[i]);
|
|
|
|
caretpos -= _editScrollOffset;
|
|
|
|
return caretpos;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EditableWidget::drawCaret()
|
|
{
|
|
//cerr << "EditableWidget::drawCaret()\n";
|
|
// Only draw if item is visible
|
|
if (!_editable || !isVisible() || !_boss->isVisible() || !_hasFocus)
|
|
return;
|
|
|
|
const GUI::Rect& editRect = getEditRect();
|
|
int x = editRect.left;
|
|
int y = editRect.top;
|
|
|
|
x += getCaretOffset();
|
|
|
|
x += _x;
|
|
y += _y;
|
|
|
|
FBSurface& s = _boss->dialog().surface();
|
|
s.vLine(x, y+2, y + editRect.height() - 3, kTextColorHi);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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
|
|
|
|
// For some reason (differences in ScummVM event handling??),
|
|
// this method should always return true.
|
|
|
|
int caretpos = getCaretOffset();
|
|
const int editWidth = getEditRect().width();
|
|
|
|
if (caretpos < 0)
|
|
{
|
|
// scroll left
|
|
_editScrollOffset += caretpos;
|
|
}
|
|
else if (caretpos >= editWidth)
|
|
{
|
|
// scroll right
|
|
_editScrollOffset -= (editWidth - caretpos);
|
|
}
|
|
else if (_editScrollOffset > 0)
|
|
{
|
|
const int strWidth = _font.getStringWidth(_editString);
|
|
if (strWidth - _editScrollOffset < editWidth)
|
|
{
|
|
// scroll right
|
|
_editScrollOffset = (strWidth - editWidth);
|
|
if (_editScrollOffset < 0)
|
|
_editScrollOffset = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::specialKeys(StellaKey key, char ascii)
|
|
{
|
|
bool handled = true;
|
|
|
|
switch (key)
|
|
{
|
|
case KBDK_a:
|
|
setCaretPos(0);
|
|
break;
|
|
|
|
case KBDK_c:
|
|
copySelectedText();
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_e:
|
|
setCaretPos(_editString.size());
|
|
break;
|
|
|
|
case KBDK_d:
|
|
handled = killChar(+1);
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_k:
|
|
handled = killLine(+1);
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_u:
|
|
handled = killLine(-1);
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_v:
|
|
pasteSelectedText();
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_w:
|
|
handled = killLastWord();
|
|
if(handled) sendCommand(kEditChangedCmd, ascii, _id);
|
|
break;
|
|
|
|
case KBDK_LEFT:
|
|
handled = moveWord(-1);
|
|
break;
|
|
|
|
case KBDK_RIGHT:
|
|
handled = moveWord(+1);
|
|
break;
|
|
|
|
default:
|
|
handled = false;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::killChar(int direction)
|
|
{
|
|
bool handled = false;
|
|
|
|
if(direction == -1) // Delete previous character (backspace)
|
|
{
|
|
if(_caretPos > 0)
|
|
{
|
|
_caretPos--;
|
|
_editString.erase(_caretPos, 1);
|
|
handled = true;
|
|
}
|
|
}
|
|
else if(direction == 1) // Delete next character (delete)
|
|
{
|
|
_editString.erase(_caretPos, 1);
|
|
handled = true;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::killLine(int direction)
|
|
{
|
|
bool handled = false;
|
|
|
|
if(direction == -1) // erase from current position to beginning of line
|
|
{
|
|
int count = _caretPos;
|
|
if(count > 0)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
killChar(-1);
|
|
|
|
handled = true;
|
|
}
|
|
}
|
|
else if(direction == 1) // erase from current position to end of line
|
|
{
|
|
int count = _editString.size() - _caretPos;
|
|
if(count > 0)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
killChar(+1);
|
|
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::killLastWord()
|
|
{
|
|
bool handled = false;
|
|
int count = 0, currentPos = _caretPos;
|
|
bool space = true;
|
|
while (currentPos > 0)
|
|
{
|
|
if (_editString[currentPos - 1] == ' ')
|
|
{
|
|
if (!space)
|
|
break;
|
|
}
|
|
else
|
|
space = false;
|
|
|
|
currentPos--;
|
|
count++;
|
|
}
|
|
|
|
if(count > 0)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
killChar(-1);
|
|
|
|
handled = true;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EditableWidget::moveWord(int direction)
|
|
{
|
|
bool handled = false;
|
|
bool space = true;
|
|
int currentPos = _caretPos;
|
|
|
|
if(direction == -1) // move to first character of previous word
|
|
{
|
|
while (currentPos > 0)
|
|
{
|
|
if (_editString[currentPos - 1] == ' ')
|
|
{
|
|
if (!space)
|
|
break;
|
|
}
|
|
else
|
|
space = false;
|
|
|
|
currentPos--;
|
|
}
|
|
_caretPos = currentPos;
|
|
handled = true;
|
|
}
|
|
else if(direction == +1) // move to first character of next word
|
|
{
|
|
while (currentPos < (int)_editString.size())
|
|
{
|
|
if (_editString[currentPos - 1] == ' ')
|
|
{
|
|
if (!space)
|
|
break;
|
|
}
|
|
else
|
|
space = false;
|
|
|
|
currentPos++;
|
|
}
|
|
_caretPos = currentPos;
|
|
handled = true;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EditableWidget::copySelectedText()
|
|
{
|
|
_clippedText = _editString;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EditableWidget::pasteSelectedText()
|
|
{
|
|
_editString = _clippedText;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
string EditableWidget::_clippedText = "";
|