366 lines
9.5 KiB
C++
Executable File
366 lines
9.5 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "gui/controls/guiMLTextEditCtrl.h"
|
|
#include "gui/containers/guiScrollCtrl.h"
|
|
#include "dgl/dgl.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "platform/event.h"
|
|
#include "core/frameAllocator.h"
|
|
#include "core/stringBuffer.h"
|
|
#include "core/unicode.h"
|
|
|
|
IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl);
|
|
|
|
//--------------------------------------------------------------------------
|
|
GuiMLTextEditCtrl::GuiMLTextEditCtrl()
|
|
{
|
|
mEscapeCommand = StringTable->insert( "" );
|
|
|
|
mIsEditCtrl = true;
|
|
|
|
mActive = true;
|
|
|
|
mVertMoveAnchorValid = false;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
GuiMLTextEditCtrl::~GuiMLTextEditCtrl()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
void GuiMLTextEditCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
|
{
|
|
// We don't want to get any smaller than our parent:
|
|
Point2I newExt = newExtent;
|
|
GuiControl* parent = getParent();
|
|
if ( parent )
|
|
newExt.y = getMax( parent->mBounds.extent.y, newExt.y );
|
|
|
|
Parent::resize( newPosition, newExt );
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
void GuiMLTextEditCtrl::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
addField( "escapeCommand", TypeString, Offset( mEscapeCommand, GuiMLTextEditCtrl ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Key events...
|
|
bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event)
|
|
{
|
|
setUpdate();
|
|
//handle modifiers first...
|
|
if (event.modifier & SI_CTRL)
|
|
{
|
|
switch(event.keyCode)
|
|
{
|
|
//copy/cut
|
|
case KEY_C:
|
|
case KEY_X:
|
|
{
|
|
//make sure we actually have something selected
|
|
if (mSelectionActive)
|
|
{
|
|
copyToClipboard(mSelectionStart, mSelectionEnd);
|
|
|
|
//if we're cutting, also delete the selection
|
|
if (event.keyCode == KEY_X)
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
else
|
|
mCursorPosition = mSelectionEnd + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//paste
|
|
case KEY_V:
|
|
{
|
|
const char *clipBuf = Platform::getClipboard();
|
|
if (dStrlen(clipBuf) > 0)
|
|
{
|
|
// Normal ascii keypress. Go ahead and add the chars...
|
|
if (mSelectionActive == true)
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
|
|
insertChars(clipBuf, dStrlen(clipBuf), mCursorPosition);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if ( event.modifier & SI_SHIFT )
|
|
{
|
|
switch ( event.keyCode )
|
|
{
|
|
case KEY_TAB:
|
|
return( Parent::onKeyDown( event ) );
|
|
}
|
|
}
|
|
else if ( event.modifier == 0 )
|
|
{
|
|
switch (event.keyCode)
|
|
{
|
|
// Escape:
|
|
case KEY_ESCAPE:
|
|
if ( mEscapeCommand[0] )
|
|
{
|
|
Con::evaluate( mEscapeCommand );
|
|
return( true );
|
|
}
|
|
return( Parent::onKeyDown( event ) );
|
|
|
|
// Deletion
|
|
case KEY_BACKSPACE:
|
|
case KEY_DELETE:
|
|
handleDeleteKeys(event);
|
|
return true;
|
|
|
|
// Cursor movement
|
|
case KEY_LEFT:
|
|
case KEY_RIGHT:
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
case KEY_HOME:
|
|
case KEY_END:
|
|
handleMoveKeys(event);
|
|
return true;
|
|
|
|
// Special chars...
|
|
case KEY_TAB:
|
|
// insert 3 spaces
|
|
if (mSelectionActive == true)
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
insertChars( "\t", 1, mCursorPosition );
|
|
return true;
|
|
|
|
case KEY_RETURN:
|
|
// insert carriage return
|
|
if (mSelectionActive == true)
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
insertChars( "\n", 1, mCursorPosition );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (event.ascii != 0)
|
|
{
|
|
// Normal ascii keypress. Go ahead and add the chars...
|
|
if (mSelectionActive == true)
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
|
|
UTF16 inData[2] = { event.ascii, 0 };
|
|
UTF8 *data = convertUTF16toUTF8( inData );
|
|
U32 dataLen = dStrlen( data );
|
|
|
|
insertChars(data, dataLen, mCursorPosition);
|
|
|
|
delete [] data;
|
|
|
|
mVertMoveAnchorValid = false;
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, let the parent have the event...
|
|
return Parent::onKeyDown(event);
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
void GuiMLTextEditCtrl::handleDeleteKeys(const GuiEvent& event)
|
|
{
|
|
if ( isSelectionActive() )
|
|
{
|
|
mSelectionActive = false;
|
|
deleteChars(mSelectionStart, mSelectionEnd+1);
|
|
mCursorPosition = mSelectionStart;
|
|
}
|
|
else
|
|
{
|
|
switch ( event.keyCode )
|
|
{
|
|
case KEY_BACKSPACE:
|
|
if (mCursorPosition != 0)
|
|
{
|
|
// delete one character left
|
|
deleteChars(mCursorPosition-1, mCursorPosition);
|
|
setUpdate();
|
|
}
|
|
break;
|
|
|
|
case KEY_DELETE:
|
|
if (mCursorPosition != mTextBuffer.length())
|
|
{
|
|
// delete one character right
|
|
deleteChars(mCursorPosition, mCursorPosition+1);
|
|
setUpdate();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
AssertFatal(false, "Unknown key code received!");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------
|
|
void GuiMLTextEditCtrl::handleMoveKeys(const GuiEvent& event)
|
|
{
|
|
if ( event.modifier & SI_SHIFT )
|
|
return;
|
|
|
|
mSelectionActive = false;
|
|
|
|
switch ( event.keyCode )
|
|
{
|
|
case KEY_LEFT:
|
|
mVertMoveAnchorValid = false;
|
|
// move one left
|
|
if ( mCursorPosition != 0 )
|
|
{
|
|
mCursorPosition--;
|
|
setUpdate();
|
|
}
|
|
break;
|
|
|
|
case KEY_RIGHT:
|
|
mVertMoveAnchorValid = false;
|
|
// move one right
|
|
if ( mCursorPosition != mTextBuffer.length() )
|
|
{
|
|
mCursorPosition++;
|
|
setUpdate();
|
|
}
|
|
break;
|
|
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
{
|
|
Line* walk;
|
|
for ( walk = mLineList; walk->next; walk = walk->next )
|
|
{
|
|
if ( mCursorPosition <= ( walk->textStart + walk->len ) )
|
|
break;
|
|
}
|
|
|
|
if ( !walk )
|
|
return;
|
|
|
|
if ( event.keyCode == KEY_UP )
|
|
{
|
|
if ( walk == mLineList )
|
|
return;
|
|
}
|
|
else if ( walk->next == NULL )
|
|
return;
|
|
|
|
Point2I newPos;
|
|
newPos.set( 0, walk->y );
|
|
|
|
// Find the x-position:
|
|
if ( !mVertMoveAnchorValid )
|
|
{
|
|
Point2I cursorTopP, cursorBottomP;
|
|
ColorI color;
|
|
getCursorPositionAndColor(cursorTopP, cursorBottomP, color);
|
|
mVertMoveAnchor = cursorTopP.x;
|
|
mVertMoveAnchorValid = true;
|
|
}
|
|
|
|
newPos.x = mVertMoveAnchor;
|
|
|
|
// Set the new y-position:
|
|
if (event.keyCode == KEY_UP)
|
|
newPos.y--;
|
|
else
|
|
newPos.y += (walk->height + 1);
|
|
|
|
if (setCursorPosition(getTextPosition(newPos)))
|
|
mVertMoveAnchorValid = false;
|
|
break;
|
|
}
|
|
|
|
case KEY_HOME:
|
|
case KEY_END:
|
|
{
|
|
mVertMoveAnchorValid = false;
|
|
Line* walk;
|
|
for (walk = mLineList; walk->next; walk = walk->next)
|
|
{
|
|
if (mCursorPosition <= (walk->textStart + walk->len))
|
|
break;
|
|
}
|
|
|
|
if (walk)
|
|
{
|
|
if (event.keyCode == KEY_HOME)
|
|
{
|
|
//place the cursor at the beginning of the first atom if there is one
|
|
if (walk->atomList)
|
|
mCursorPosition = walk->atomList->textStart;
|
|
else
|
|
mCursorPosition = walk->textStart;
|
|
}
|
|
else
|
|
{
|
|
mCursorPosition = walk->textStart;
|
|
mCursorPosition += walk->len;
|
|
}
|
|
setUpdate();
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
AssertFatal(false, "Unknown move key code was received!");
|
|
}
|
|
|
|
ensureCursorOnScreen();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void GuiMLTextEditCtrl::onRender(Point2I offset, const RectI& updateRect)
|
|
{
|
|
Parent::onRender(offset, updateRect);
|
|
|
|
// We are the first responder, draw our cursor in the appropriate position...
|
|
if (isFirstResponder())
|
|
{
|
|
Point2I top, bottom;
|
|
ColorI color;
|
|
getCursorPositionAndColor(top, bottom, color);
|
|
dglDrawLine(top + offset, bottom + offset, mProfile->mCursorColor);
|
|
}
|
|
}
|
|
|