tge/engine/gui/controls/guiMLTextEditCtrl.cc
2017-04-17 06:17:10 -06:00

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);
}
}