| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "gui/controls/guiTextEditCtrl.h"#include "console/consoleTypes.h"#include "console/console.h"#include "gui/core/guiCanvas.h"#include "gui/controls/guiMLTextCtrl.h"#include "gui/core/guiDefaultControlRender.h"#include "gfx/gfxDevice.h"#include "gfx/gfxDrawUtil.h"#include "core/frameAllocator.h"#include "sfx/sfxTrack.h"#include "sfx/sfxTypes.h"#include "sfx/sfxSystem.h"#include "core/strings/unicode.h"#include "console/engineAPI.h"IMPLEMENT_CONOBJECT(GuiTextEditCtrl);ConsoleDocClass( GuiTextEditCtrl,   "@brief A component that places a text entry box on the screen.\n\n"   "Fonts and sizes are changed using profiles. The text value can be set or entered by a user.\n\n"   "@tsexample\n"   "   new GuiTextEditCtrl(MessageHud_Edit)\n"   "  {\n"   "      text = \"Hello World\";\n"   "      validate = \"validateCommand();\"\n"   "      escapeCommand = \"escapeCommand();\";\n"   "      historySize = \"5\";\n"   "      tabComplete = \"true\";\n"   "      deniedSound = \"DeniedSoundProfile\";\n"   "      sinkAllKeyEvents = \"true\";\n"   "      password = \"true\";\n"   "      passwordMask = \"*\";\n"   "       //Properties not specific to this control have been omitted from this example.\n"   "   };\n"   "@endtsexample\n\n"   "@see GuiTextCtrl\n"   "@see GuiControl\n\n"   "@ingroup GuiControls\n");IMPLEMENT_CALLBACK( GuiTextEditCtrl, onTabComplete, void, (const char* val),( val ),   "@brief Called if tabComplete is true, and the 'tab' key is pressed.\n\n"   "@param val Input to mimick the '1' sent by the actual tab key button press.\n"   "@tsexample\n"   "// Tab key has been pressed, causing the callback to occur.\n"   "GuiTextEditCtrl::onTabComplete(%this,%val)\n"   "  {\n"   "     //Code to run when the onTabComplete callback occurs\n"   "  }\n"   "@endtsexample\n\n"   "@see GuiTextCtrl\n"   "@see GuiControl\n\n");IMPLEMENT_CALLBACK( GuiTextEditCtrl, onReturn, void, (),(),   "@brief Called when the 'Return' or 'Enter' key is pressed.\n\n"   "@tsexample\n"   "// Return or Enter key was pressed, causing the callback to occur.\n"   "GuiTextEditCtrl::onReturn(%this)\n"   "  {\n"   "     // Code to run when the onReturn callback occurs\n"   "  }\n"   "@endtsexample\n\n"   "@see GuiTextCtrl\n"   "@see GuiControl\n\n");IMPLEMENT_CALLBACK( GuiTextEditCtrl, onValidate, void, (),(),   "@brief Called whenever the control is validated.\n\n"   "@tsexample\n"   "// The control gets validated, causing the callback to occur\n"   "GuiTextEditCtrl::onValidated(%this)\n"   "  {\n"   "     // Code to run when the control is validated\n"   "  }\n"   "@endtsexample\n\n"   "@see GuiTextCtrl\n"   "@see GuiControl\n\n");GuiTextEditCtrl::GuiTextEditCtrl(){   mInsertOn = true;   mBlockStart = 0;   mBlockEnd = 0;   mCursorPos = 0;   mCursorOn = false;   mNumFramesElapsed = 0;   mDragHit = false;   mTabComplete = false;   mScrollDir = 0;   mUndoBlockStart = 0;   mUndoBlockEnd = 0;   mUndoCursorPos = 0;   mPasswordText = false;   mSinkAllKeyEvents = false;   mActive = true;   mTextValid = true;   mTextOffsetReset = true;   mHistoryDirty = false;   mHistorySize = 0;   mHistoryLast = -1;   mHistoryIndex = 0;   mHistoryBuf = NULL;#if defined(__MACOSX__)   UTF8  bullet[4] = { 0xE2, 0x80, 0xA2, 0 };      mPasswordMask = StringTable->insert( bullet );#else   mPasswordMask = StringTable->insert( "*" );#endif   Sim::findObject( "InputDeniedSound", mDeniedSound );}GuiTextEditCtrl::~GuiTextEditCtrl(){   //delete the history buffer if it exists   if (mHistoryBuf)   {      for (S32 i = 0; i < mHistorySize; i++)         delete [] mHistoryBuf[i];      delete [] mHistoryBuf;   }}void GuiTextEditCtrl::initPersistFields(){   addGroup( "Text Input" );         addField("validate",          TypeRealString,Offset(mValidateCommand,   GuiTextEditCtrl), "Script command to be called when the first validater is lost.\n");      addField("escapeCommand",     TypeRealString,Offset(mEscapeCommand,     GuiTextEditCtrl), "Script command to be called when the Escape key is pressed.\n");      addField("historySize",       TypeS32,       Offset(mHistorySize,       GuiTextEditCtrl), "How large of a history buffer to maintain.\n");      addField("tabComplete",       TypeBool,      Offset(mTabComplete,       GuiTextEditCtrl), "If true, when the 'tab' key is pressed, it will act as if the Enter key was pressed on the control.\n");      addField("deniedSound",       TypeSFXTrackName, Offset(mDeniedSound, GuiTextEditCtrl), "If the attempted text cannot be entered, this sound effect will be played.\n");      addField("sinkAllKeyEvents",  TypeBool,      Offset(mSinkAllKeyEvents,  GuiTextEditCtrl), "If true, every key event will act as if the Enter key was pressed.\n");      addField("password",          TypeBool,      Offset(mPasswordText,      GuiTextEditCtrl), "If true, all characters entered will be stored in the control, however will display as the character stored in passwordMask.\n");      addField("passwordMask",      TypeString,    Offset(mPasswordMask,      GuiTextEditCtrl), "If 'password' is true, this is the character that will be used to mask the characters in the control.\n");         endGroup( "Text Input" );   Parent::initPersistFields();}bool GuiTextEditCtrl::onAdd(){   if ( ! Parent::onAdd() )      return false;   //create the history buffer   if ( mHistorySize > 0 )   {      mHistoryBuf = new UTF16*[mHistorySize];      for ( S32 i = 0; i < mHistorySize; i++ )      {         mHistoryBuf[i] = new UTF16[GuiTextCtrl::MAX_STRING_LENGTH + 1];         mHistoryBuf[i][0] = '\0';      }   }   if( mText && mText[0] )   {      setText(mText);   }   return true;}void GuiTextEditCtrl::onStaticModified(const char* slotName, const char* newValue){   if(!dStricmp(slotName, "text"))      setText(mText);}void GuiTextEditCtrl::execConsoleCallback(){   // Execute the console command!   Parent::execConsoleCallback();   // Update the console variable:   if ( mConsoleVariable[0] )      Con::setVariable(mConsoleVariable, mTextBuffer.getPtr8());}void GuiTextEditCtrl::updateHistory( StringBuffer *inTxt, bool moveIndex ){   if(!mHistorySize)      return;   const UTF16* txt = inTxt->getPtr();      // Reject empty strings.   if( !txt || !txt[ 0 ] )      return;   // see if it's already in   if(mHistoryLast == -1 || dStrcmp(txt, mHistoryBuf[mHistoryLast]))   {      if(mHistoryLast == mHistorySize-1) // we're at the history limit... shuffle the pointers around:      {         UTF16 *first = mHistoryBuf[0];         for(U32 i = 0; i < mHistorySize - 1; i++)            mHistoryBuf[i] = mHistoryBuf[i+1];         mHistoryBuf[mHistorySize-1] = first;         if(mHistoryIndex > 0)            mHistoryIndex--;      }      else         mHistoryLast++;      inTxt->getCopy(mHistoryBuf[mHistoryLast], GuiTextCtrl::MAX_STRING_LENGTH);      mHistoryBuf[mHistoryLast][GuiTextCtrl::MAX_STRING_LENGTH] = '\0';   }   if(moveIndex)      mHistoryIndex = mHistoryLast + 1;}void GuiTextEditCtrl::getText( char *dest ){   if ( dest )      mTextBuffer.getCopy8((UTF8*)dest, GuiTextCtrl::MAX_STRING_LENGTH+1);}void GuiTextEditCtrl::getRenderText(char *dest){    getText( dest );} void GuiTextEditCtrl::setText( const UTF8 *txt ){   if(txt && txt[0] != 0)   {      Parent::setText(txt);      mTextBuffer.set( txt );   }   else      mTextBuffer.set( "" );   mCursorPos = mTextBuffer.length();}void GuiTextEditCtrl::setText( const UTF16* txt){   if(txt && txt[0] != 0)   {      UTF8* txt8 = createUTF8string( txt );      Parent::setText( txt8 );      delete[] txt8;      mTextBuffer.set( txt );   }   else   {      Parent::setText("");      mTextBuffer.set("");   }      mCursorPos = mTextBuffer.length();   }bool GuiTextEditCtrl::isAllTextSelected(){   if( mBlockStart == 0 && mBlockEnd == mTextBuffer.length() )      return true;   else      return false;}void GuiTextEditCtrl::selectAllText(){   mBlockStart = 0;   mBlockEnd = mTextBuffer.length();   setUpdate();}void GuiTextEditCtrl::clearSelectedText(){   mBlockStart = 0;   mBlockEnd = 0;   setUpdate();}void GuiTextEditCtrl::forceValidateText(){   if( mValidateCommand.isNotEmpty() )      evaluate( mValidateCommand );}void GuiTextEditCtrl::setCursorPos( const S32 newPos ){   S32 charCount = mTextBuffer.length();   S32 realPos = newPos > charCount ? charCount : newPos < 0 ? 0 : newPos;   if ( realPos != mCursorPos )   {      mCursorPos = realPos;      setUpdate();   }}S32 GuiTextEditCtrl::calculateCursorPos( const Point2I &globalPos ){   Point2I ctrlOffset = localToGlobalCoord( Point2I( 0, 0 ) );   S32 charLength = 0;   S32 curX;   curX = globalPos.x - ctrlOffset.x;   setUpdate();   //if the cursor is too far to the left   if ( curX < 0 )      return -1;   //if the cursor is too far to the right   if ( curX >= ctrlOffset.x + getExtent().x )      return -2;   curX = globalPos.x - mTextOffset.x;   S32 count=0;   if(mTextBuffer.length() == 0)      return 0;   for(count=0; count<mTextBuffer.length(); count++)   {      UTF16 c = mTextBuffer.getChar(count);      if(!mPasswordText && !mProfile->mFont->isValidChar(c))         continue;               if(mPasswordText)         charLength += mProfile->mFont->getCharXIncrement( mPasswordMask[0] );      else         charLength += mProfile->mFont->getCharXIncrement( c );      if ( charLength > curX )         break;         }   return count;}void GuiTextEditCtrl::onMouseDown( const GuiEvent &event ){   if(!isActive())      return;   mDragHit = false;   // If we have a double click, select all text.  Otherwise   // act as before by clearing any selection.   bool doubleClick = (event.mouseClickCount > 1);   if(doubleClick)   {      selectAllText();   } else   {      //undo any block function      mBlockStart = 0;      mBlockEnd = 0;   }   //find out where the cursor should be   S32 pos = calculateCursorPos( event.mousePoint );   // if the position is to the left   if ( pos == -1 )      mCursorPos = 0;   else if ( pos == -2 ) //else if the position is to the right      mCursorPos = mTextBuffer.length();   else //else set the mCursorPos      mCursorPos = pos;   //save the mouseDragPos   mMouseDragStart = mCursorPos;   // lock the mouse   mouseLock();   //set the drag var   mDragHit = true;   //let the parent get the event   setFirstResponder();}void GuiTextEditCtrl::onMouseDragged( const GuiEvent &event ){   S32 pos = calculateCursorPos( event.mousePoint );   // if the position is to the left   if ( pos == -1 )      mScrollDir = -1;   else if ( pos == -2 ) // the position is to the right      mScrollDir = 1;   else // set the new cursor position   {      mScrollDir = 0;      mCursorPos = pos;   }   // update the block:   mBlockStart = getMin( mCursorPos, mMouseDragStart );   mBlockEnd = getMax( mCursorPos, mMouseDragStart );   if ( mBlockStart < 0 )      mBlockStart = 0;   if ( mBlockStart == mBlockEnd )      mBlockStart = mBlockEnd = 0;   //let the parent get the event   Parent::onMouseDragged(event);}void GuiTextEditCtrl::onMouseUp(const GuiEvent &event){   TORQUE_UNUSED(event);   mDragHit = false;   mScrollDir = 0;   mouseUnlock();}void GuiTextEditCtrl::saveUndoState(){   //save the current state   mUndoText.set(&mTextBuffer);   mUndoBlockStart = mBlockStart;   mUndoBlockEnd   = mBlockEnd;   mUndoCursorPos  = mCursorPos;}void GuiTextEditCtrl::onCopy(bool andCut){   // Don't copy/cut password field!   if(mPasswordText)      return;   if (mBlockEnd > 0)   {      //save the current state      saveUndoState();      //copy the text to the clipboard      UTF8* clipBuff = mTextBuffer.createSubstring8(mBlockStart, mBlockEnd - mBlockStart);      Platform::setClipboard(clipBuff);      delete[] clipBuff;      //if we pressed the cut shortcut, we need to cut the selected text from the control...      if (andCut)      {         mTextBuffer.cut(mBlockStart, mBlockEnd - mBlockStart);         mCursorPos = mBlockStart;      }      mBlockStart = 0;      mBlockEnd = 0;   }}void GuiTextEditCtrl::onPaste(){              //first, make sure there's something in the clipboard to copy...   const UTF8 *clipboard = Platform::getClipboard();   if(dStrlen(clipboard) <= 0)      return;   //save the current state   saveUndoState();   //delete anything hilited   if (mBlockEnd > 0)   {      mTextBuffer.cut(mBlockStart, mBlockEnd - mBlockStart);      mCursorPos = mBlockStart;      mBlockStart = 0;      mBlockEnd = 0;   }   // We'll be converting to UTF16, and maybe trimming the string,   // so let's use a StringBuffer, for convinience.   StringBuffer pasteText(clipboard);   // Space left after we remove the highlighted text   S32 stringLen = mTextBuffer.length();   // Trim down to fit in a buffer of size mMaxStrLen   S32 pasteLen = pasteText.length();   if(stringLen + pasteLen > mMaxStrLen)   {      pasteLen = mMaxStrLen - stringLen;      pasteText.cut(pasteLen, pasteText.length() - pasteLen);   }   if (mCursorPos == stringLen)   {      mTextBuffer.append(pasteText);   }   else   {      mTextBuffer.insert(mCursorPos, pasteText);   }   mCursorPos += pasteLen;}void GuiTextEditCtrl::onUndo(){    StringBuffer tempBuffer;    S32 tempBlockStart;    S32 tempBlockEnd;    S32 tempCursorPos;    //save the current    tempBuffer.set(&mTextBuffer);    tempBlockStart = mBlockStart;    tempBlockEnd   = mBlockEnd;    tempCursorPos  = mCursorPos;    //restore the prev    mTextBuffer.set(&mUndoText);    mBlockStart = mUndoBlockStart;    mBlockEnd   = mUndoBlockEnd;    mCursorPos  = mUndoCursorPos;    //update the undo    mUndoText.set(&tempBuffer);    mUndoBlockStart = tempBlockStart;    mUndoBlockEnd   = tempBlockEnd;    mUndoCursorPos  = tempCursorPos;}bool GuiTextEditCtrl::onKeyDown(const GuiEvent &event){   if ( !isActive() || !isAwake() )      return false;   S32 stringLen = mTextBuffer.length();   setUpdate();   // Ugly, but now I'm cool like MarkF.   if(event.keyCode == KEY_BACKSPACE)      goto dealWithBackspace;      if ( event.modifier & SI_SHIFT )   {      // Added support for word jump selection.      if ( event.modifier & SI_CTRL )      {         switch ( event.keyCode )         {            case KEY_LEFT:            {               S32 newpos = findPrevWord();                              if ( mBlockStart == mBlockEnd )               {                  // There was not already a selection so start a new one.                  mBlockStart = newpos;                  mBlockEnd = mCursorPos;               }               else               {                  // There was a selection already...                  // In this case the cursor MUST be at either the                  // start or end of that selection.                  if ( mCursorPos == mBlockStart )                  {                     // We are at the start block and traveling left so                     // just extend the start block farther left.                                          mBlockStart = newpos;                  }                  else                  {                     // We are at the end block BUT traveling left                     // back towards the start block...                     if ( newpos > mBlockStart )                     {                        // We haven't overpassed the existing start block                        // so just trim back the end block.                        mBlockEnd = newpos;                     }                     else if ( newpos == mBlockStart )                     {                        // We are back at the start, so no more selection.                        mBlockEnd = mBlockStart = 0;                     }                     else                     {                        // Only other option, we just backtracked PAST                        // our original start block.                        // So the new position becomes the start block                        // and the old start block becomes the end block.                        mBlockEnd = mBlockStart;                        mBlockStart = newpos;                     }                  }               }                                             mCursorPos = newpos;               return true;            }            case KEY_RIGHT:            {               S32 newpos = findNextWord();                              if ( mBlockStart == mBlockEnd )               {                  // There was not already a selection so start a new one.                  mBlockStart = mCursorPos;                  mBlockEnd = newpos;               }               else               {                  // There was a selection already...                  // In this case the cursor MUST be at either the                  // start or end of that selection.                  if ( mCursorPos == mBlockEnd )                  {                     // We are at the end block and traveling right so                     // just extend the end block farther right.                                          mBlockEnd = newpos;                  }                  else                  {                     // We are at the start block BUT traveling right                     // back towards the end block...                     if ( newpos < mBlockEnd )                     {                        // We haven't overpassed the existing end block                        // so just trim back the start block.                        mBlockStart = newpos;                     }                     else if ( newpos == mBlockEnd )                     {                        // We are back at the end, so no more selection.                        mBlockEnd = mBlockStart = 0;                     }                     else                     {                        // Only other option, we just backtracked PAST                        // our original end block.                        // So the new position becomes the end block                        // and the old end block becomes the start block.                        mBlockStart = mBlockEnd;                        mBlockEnd = newpos;                     }                  }               }               mCursorPos = newpos;               return true;            }                        default:               break;         }      }      // End support for word jump selection.        switch ( event.keyCode )        {            case KEY_TAB:               if ( mTabComplete )               {              onTabComplete_callback("1");                  return true;               }            break; // We don't want to fall through if we don't handle the TAB here.            case KEY_HOME:               mBlockStart = 0;               mBlockEnd = mCursorPos;               mCursorPos = 0;               return true;            case KEY_END:                mBlockStart = mCursorPos;                mBlockEnd = stringLen;                mCursorPos = stringLen;                return true;            case KEY_LEFT:                if ((mCursorPos > 0) & (stringLen > 0))                {                    //if we already have a selected block                    if (mCursorPos == mBlockEnd)                    {                        mCursorPos--;                        mBlockEnd--;                        if (mBlockEnd == mBlockStart)                        {                            mBlockStart = 0;                            mBlockEnd = 0;                        }                    }                    else {                        mCursorPos--;                        mBlockStart = mCursorPos;                        if (mBlockEnd == 0)                        {                            mBlockEnd = mCursorPos + 1;                        }                    }                }                return true;            case KEY_RIGHT:                if (mCursorPos < stringLen)                {                    if ((mCursorPos == mBlockStart) && (mBlockEnd > 0))                    {                        mCursorPos++;                        mBlockStart++;                        if (mBlockStart == mBlockEnd)                        {                            mBlockStart = 0;                            mBlockEnd = 0;                        }                    }                    else                    {                        if (mBlockEnd == 0)                        {                            mBlockStart = mCursorPos;                            mBlockEnd = mCursorPos;                        }                        mCursorPos++;                        mBlockEnd++;                    }                }                return true;            case KEY_RETURN:            case KEY_NUMPADENTER:                          return dealWithEnter(false);            default:               break;        }    }   else if (event.modifier & SI_CTRL)   {      switch(event.keyCode)      {#if defined(TORQUE_OS_MAC)         // Added UNIX emacs key bindings - just a little hack here...         // Ctrl-B - move one character back         case KEY_B:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_LEFT;            return(onKeyDown(new_event));         }         // Ctrl-F - move one character forward         case KEY_F:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_RIGHT;            return(onKeyDown(new_event));         }         // Ctrl-A - move to the beginning of the line         case KEY_A:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_HOME;            return(onKeyDown(new_event));         }         // Ctrl-E - move to the end of the line         case KEY_E:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_END;            return(onKeyDown(new_event));         }         // Ctrl-P - move backward in history         case KEY_P:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_UP;            return(onKeyDown(new_event));         }         // Ctrl-N - move forward in history         case KEY_N:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_DOWN;            return(onKeyDown(new_event));         }         // Ctrl-D - delete under cursor         case KEY_D:         {             GuiEvent new_event;            new_event.modifier = 0;            new_event.keyCode = KEY_DELETE;            return(onKeyDown(new_event));         }         case KEY_U:         {             GuiEvent new_event;            new_event.modifier = SI_CTRL;            new_event.keyCode = KEY_DELETE;            return(onKeyDown(new_event));         }         // End added UNIX emacs key bindings#endif         // Adding word jump navigation.         case KEY_LEFT:         {            mCursorPos = findPrevWord();            mBlockStart = 0;            mBlockEnd = 0;            return true;         }         case KEY_RIGHT:         {            mCursorPos = findNextWord();            mBlockStart = 0;            mBlockEnd = 0;            return true;         }                  #if !defined(TORQUE_OS_MAC)         // Select all         case KEY_A:         {            selectAllText();            return true;         }         // windows style cut / copy / paste / undo keybinds         case KEY_C:         case KEY_X:         {            // copy, and cut the text if we hit ctrl-x            onCopy( event.keyCode==KEY_X );            return true;         }         case KEY_V:         {            onPaste();            // Execute the console command!            execConsoleCallback();            return true;         }         case KEY_Z:            if (! mDragHit)            {               onUndo();               return true;            }#endif         case KEY_DELETE:         case KEY_BACKSPACE:            //save the current state            saveUndoState();            //delete everything in the field            mTextBuffer.set("");            mCursorPos  = 0;            mBlockStart = 0;            mBlockEnd   = 0;            execConsoleCallback();            return true;         default:            break;      }   }#if defined(TORQUE_OS_MAC)   // mac style cut / copy / paste / undo keybinds   else if (event.modifier & SI_ALT)   {      // Mac command key maps to alt in torque.      // Added Mac cut/copy/paste/undo keys      switch(event.keyCode)      {         // Select all         case KEY_A:         {            selectAllText();            return true;         }         case KEY_C:         case KEY_X:         {            // copy, and cut the text if we hit cmd-x            onCopy( event.keyCode==KEY_X );            return true;         }         case KEY_V:         {            onPaste();            // Execute the console command!            execConsoleCallback();            return true;         }                     case KEY_Z:            if (! mDragHit)            {               onUndo();               return true;            }                     default:            break;      }   }#endif   else   {      switch(event.keyCode)      {         case KEY_ESCAPE:            if( mEscapeCommand.isNotEmpty() )            {               evaluate( mEscapeCommand );               return( true );            }            return( Parent::onKeyDown( event ) );         case KEY_RETURN:         case KEY_NUMPADENTER:                       return dealWithEnter(true);         case KEY_UP:         {            if( mHistorySize > 0 )            {               if(mHistoryDirty)               {                  updateHistory(&mTextBuffer, false);                  mHistoryDirty = false;               }               mHistoryIndex--;                              if(mHistoryIndex >= 0 && mHistoryIndex <= mHistoryLast)                  setText(mHistoryBuf[mHistoryIndex]);               else if(mHistoryIndex < 0)                  mHistoryIndex = 0;            }                           return true;         }         case KEY_DOWN:         {            if( mHistorySize > 0 )            {               if(mHistoryDirty)               {                  updateHistory(&mTextBuffer, false);                  mHistoryDirty = false;               }               mHistoryIndex++;               if(mHistoryIndex > mHistoryLast)               {                  mHistoryIndex = mHistoryLast + 1;                  setText("");               }               else                  setText(mHistoryBuf[mHistoryIndex]);            }            return true;         }         case KEY_LEFT:                        // If we have a selection put the cursor to the left side of it.            if ( mBlockStart != mBlockEnd )            {               mCursorPos = mBlockStart;               mBlockStart = mBlockEnd = 0;            }            else            {               mBlockStart = mBlockEnd = 0;               mCursorPos = getMax( mCursorPos - 1, 0 );                           }            return true;         case KEY_RIGHT:            // If we have a selection put the cursor to the right side of it.                        if ( mBlockStart != mBlockEnd )            {               mCursorPos = mBlockEnd;               mBlockStart = mBlockEnd = 0;            }            else            {               mBlockStart = mBlockEnd = 0;               mCursorPos = getMin( mCursorPos + 1, stringLen );                           }            return true;         case KEY_BACKSPACE:dealWithBackspace:            //save the current state            saveUndoState();            if (mBlockEnd > 0)            {               mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart);               mCursorPos  = mBlockStart;               mBlockStart = 0;               mBlockEnd   = 0;               mHistoryDirty = true;               // Execute the console command!               execConsoleCallback();            }            else if (mCursorPos > 0)            {               mTextBuffer.cut(mCursorPos-1, 1);               mCursorPos--;               mHistoryDirty = true;               // Execute the console command!               execConsoleCallback();            }            return true;         case KEY_DELETE:            //save the current state            saveUndoState();            if (mBlockEnd > 0)            {               mHistoryDirty = true;               mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart);               mCursorPos = mBlockStart;               mBlockStart = 0;               mBlockEnd = 0;               // Execute the console command!               execConsoleCallback();            }            else if (mCursorPos < stringLen)            {               mHistoryDirty = true;               mTextBuffer.cut(mCursorPos, 1);               // Execute the console command!               execConsoleCallback();            }            return true;         case KEY_INSERT:            mInsertOn = !mInsertOn;            return true;         case KEY_HOME:            mBlockStart = 0;            mBlockEnd   = 0;            mCursorPos  = 0;            return true;         case KEY_END:            mBlockStart = 0;            mBlockEnd   = 0;            mCursorPos  = stringLen;            return true;               default:            break;         }   }   switch ( event.keyCode )   {      case KEY_TAB:         if ( mTabComplete )         {         onTabComplete_callback("0");            return( true );         }      case KEY_UP:      case KEY_DOWN:      case KEY_ESCAPE:         return Parent::onKeyDown( event );      default:         break;   }            // Handle character input events.   if( mProfile->mFont->isValidChar( event.ascii ) )   {      handleCharInput( event.ascii );      return true;   }   // Or eat it if that's appropriate.   if( mSinkAllKeyEvents )      return true;   // Not handled - pass the event to it's parent.   return Parent::onKeyDown( event );}bool GuiTextEditCtrl::dealWithEnter( bool clearResponder ){   //first validate   if (mProfile->mReturnTab)   {      onLoseFirstResponder();   }   updateHistory(&mTextBuffer, true);   mHistoryDirty = false;   //next exec the alt console command   execAltConsoleCallback();   // Notify of Return   onReturn_callback();   if (mProfile->mReturnTab)   {      GuiCanvas *root = getRoot();      if (root)      {         root->tabNext();         return true;      }   }      if( clearResponder )      clearFirstResponder();   return true;}void GuiTextEditCtrl::setFirstResponder(){   Parent::setFirstResponder();   GuiCanvas *root = getRoot();   if (root != NULL)   {      root->enableKeyboardTranslation();        // If the native OS accelerator keys are not disabled      // then some key events like Delete, ctrl+V, etc may      // not make it down to us.      root->setNativeAcceleratorsEnabled( false );   }}void GuiTextEditCtrl::onLoseFirstResponder(){   GuiCanvas *root = getRoot();   if( root )   {    root->setNativeAcceleratorsEnabled( true );    root->disableKeyboardTranslation();   }   //execute the validate command   if( mValidateCommand.isNotEmpty() )      evaluate( mValidateCommand );   onValidate_callback();   // Redraw the control:   setUpdate();   // Lost Responder   Parent::onLoseFirstResponder();}void GuiTextEditCtrl::onRender( Point2I offset, const RectI &updateRect ){   RectI ctrlRect( offset, getExtent() );   //if opaque, fill the update rect with the fill color   if ( mProfile->mOpaque )   {      if ( !mTextValid )         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorERR );      else if ( isFirstResponder() )         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorHL );      else         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColor );   }   //if there's a border, draw the border   if ( mProfile->mBorder )   {      renderBorder( ctrlRect, mProfile );      if ( !mTextValid )         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorERR );   }   drawText( ctrlRect, isFirstResponder() );}void GuiTextEditCtrl::onPreRender(){   if ( isFirstResponder() )   {      U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped;      mNumFramesElapsed++;      if ( ( timeElapsed > 500 ) && ( mNumFramesElapsed > 3 ) )      {         mCursorOn = !mCursorOn;         mTimeLastCursorFlipped = Platform::getVirtualMilliseconds();         mNumFramesElapsed = 0;         setUpdate();      }      //update the cursor if the text is scrolling      if ( mDragHit )      {         if ( ( mScrollDir < 0 ) && ( mCursorPos > 0 ) )            mCursorPos--;         else if ( ( mScrollDir > 0 ) && ( mCursorPos < (S32) mTextBuffer.length() ) )            mCursorPos++;      }   }}void GuiTextEditCtrl::drawText( const RectI &drawRect, bool isFocused ){   StringBuffer textBuffer;   Point2I drawPoint = drawRect.point;   Point2I paddingLeftTop, paddingRightBottom;   // Or else just copy it over.   char *renderText = Con::getReturnBuffer( GuiTextEditCtrl::MAX_STRING_LENGTH );   getRenderText( renderText );   // Apply password masking (make the masking char optional perhaps?)   if(mPasswordText)   {      const U32 renderLen = dStrlen( renderText );      for( U32 i = 0; i < renderLen; i++ )         textBuffer.append(mPasswordMask);   }   else   {       textBuffer.set( renderText );   }   // Just a little sanity.   if(mCursorPos > textBuffer.length())       mCursorPos = textBuffer.length();   paddingLeftTop.set(( mProfile->mTextOffset.x != 0 ? mProfile->mTextOffset.x : 3 ), mProfile->mTextOffset.y);   paddingRightBottom = paddingLeftTop;   // Center vertically:   drawPoint.y += ( ( drawRect.extent.y - paddingLeftTop.y - paddingRightBottom.y - S32( mProfile->mFont->getHeight() ) ) / 2 ) + paddingLeftTop.y;   // Align horizontally:      S32 textWidth = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), textBuffer.length());   switch( mProfile->mAlignment )   {   case GuiControlProfile::RightJustify:      drawPoint.x += ( drawRect.extent.x - textWidth - paddingRightBottom.x );      break;   case GuiControlProfile::CenterJustify:      drawPoint.x += ( ( drawRect.extent.x - textWidth ) / 2 );      break;   default:   case GuiControlProfile::LeftJustify :      drawPoint.x += paddingLeftTop.x;      break;   }   ColorI fontColor = mActive ? mProfile->mFontColor : mProfile->mFontColorNA;   // now draw the text   Point2I cursorStart, cursorEnd;   mTextOffset.y = drawPoint.y;   mTextOffset.x = drawPoint.x;   if ( drawRect.extent.x - paddingLeftTop.x > textWidth )      mTextOffset.x = drawPoint.x;   else   {      // Alignment affects large text      if ( mProfile->mAlignment == GuiControlProfile::RightJustify         || mProfile->mAlignment == GuiControlProfile::CenterJustify )      {         if ( mTextOffset.x + textWidth < (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x)            mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x - textWidth;      }   }   // calculate the cursor   if( isFocused && mActive )   {      // Where in the string are we?      S32 cursorOffset=0, charWidth=0;      UTF16 tempChar = textBuffer.getChar(mCursorPos);      // Alright, we want to terminate things momentarily.      if(mCursorPos > 0)      {         cursorOffset = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), mCursorPos);      }      else         cursorOffset = 0;      if( tempChar && mProfile->mFont->isValidChar( tempChar ) )         charWidth = mProfile->mFont->getCharWidth( tempChar );      else         charWidth = paddingRightBottom.x;      if( mTextOffset.x + cursorOffset + 1 >= (drawRect.point.x + drawRect.extent.x) ) // +1 is for the cursor width      {         // Cursor somewhere beyond the textcontrol,         // skip forward roughly 25% of the total width (if possible)         S32 skipForward = drawRect.extent.x / 4 * 3;         if ( cursorOffset + skipForward > textWidth )            mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x - textWidth;         else         {            //mTextOffset.x -= skipForward;            S32 mul = (S32)( mFloor( (cursorOffset-drawRect.extent.x) / skipForward ) );            mTextOffset.x -= skipForward * mul + drawRect.extent.x - 1; // -1 is for the cursor width         }      }      else if( mTextOffset.x + cursorOffset < drawRect.point.x + paddingLeftTop.x )      {         // Cursor somewhere before the textcontrol         // skip backward roughly 25% of the total width (if possible)         S32 skipBackward = drawRect.extent.x / 4 * 3;         if ( cursorOffset - skipBackward < 0 )            mTextOffset.x = drawRect.point.x + paddingLeftTop.x;         else         {            S32 mul = (S32)( mFloor( cursorOffset / skipBackward ) );            mTextOffset.x += drawRect.point.x - mTextOffset.x - skipBackward * mul;         }      }      cursorStart.x = mTextOffset.x + cursorOffset;#ifdef TORQUE_OS_MAC      cursorStart.x += charWidth/2;#endif            cursorEnd.x = cursorStart.x;      S32 cursorHeight = mProfile->mFont->getHeight();      if ( cursorHeight < drawRect.extent.y )      {         cursorStart.y = drawPoint.y;         cursorEnd.y = cursorStart.y + cursorHeight;      }      else      {         cursorStart.y = drawRect.point.y;         cursorEnd.y = cursorStart.y + drawRect.extent.y;      }   }   //draw the text   if ( !isFocused )      mBlockStart = mBlockEnd = 0;   //also verify the block start/end   if ((mBlockStart > textBuffer.length() || (mBlockEnd > textBuffer.length()) || (mBlockStart > mBlockEnd)))      mBlockStart = mBlockEnd = 0;   Point2I tempOffset = mTextOffset;   //draw the portion before the highlight   if ( mBlockStart > 0 )   {      GFX->getDrawUtil()->setBitmapModulation( fontColor );      const UTF16* preString2 = textBuffer.getPtr();      GFX->getDrawUtil()->drawText( mProfile->mFont, tempOffset, preString2, mProfile->mFontColors );      tempOffset.x += mProfile->mFont->getStrNWidth(preString2, mBlockStart);   }   //draw the highlighted portion   if ( mBlockEnd > 0 )   {      const UTF16* highlightBuff = textBuffer.getPtr() + mBlockStart;      U32 highlightBuffLen = mBlockEnd-mBlockStart;      S32 highlightWidth = mProfile->mFont->getStrNWidth(highlightBuff, highlightBuffLen);      GFX->getDrawUtil()->drawRectFill( Point2I( tempOffset.x, drawRect.point.y ),         Point2I( tempOffset.x + highlightWidth, drawRect.point.y + drawRect.extent.y - 1),         mProfile->mFontColorSEL );      GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColorHL );      GFX->getDrawUtil()->drawTextN( mProfile->mFont, tempOffset, highlightBuff, highlightBuffLen, mProfile->mFontColors );      tempOffset.x += highlightWidth;   }   //draw the portion after the highlight   if(mBlockEnd < textBuffer.length())   {      const UTF16* finalBuff = textBuffer.getPtr() + mBlockEnd;      U32 finalBuffLen = textBuffer.length() - mBlockEnd;      GFX->getDrawUtil()->setBitmapModulation( fontColor );      GFX->getDrawUtil()->drawTextN( mProfile->mFont, tempOffset, finalBuff, finalBuffLen, mProfile->mFontColors );   }   //draw the cursor   if ( isFocused && mCursorOn )      GFX->getDrawUtil()->drawLine( cursorStart, cursorEnd, mProfile->mCursorColor );}bool GuiTextEditCtrl::hasText(){   return ( mTextBuffer.length() );}void GuiTextEditCtrl::invalidText(bool playSound){   mTextValid = false;   if ( playSound )      playDeniedSound();}void GuiTextEditCtrl::validText(){   mTextValid = true;}bool GuiTextEditCtrl::isValidText(){   return mTextValid;}void GuiTextEditCtrl::playDeniedSound(){   if ( mDeniedSound )      SFX->playOnce( mDeniedSound );}const char *GuiTextEditCtrl::getScriptValue(){   return StringTable->insert(mTextBuffer.getPtr8());}void GuiTextEditCtrl::setScriptValue(const char *value){   mTextBuffer.set(value);   mCursorPos = mTextBuffer.length();}void GuiTextEditCtrl::handleCharInput( U16 ascii ){   S32 stringLen = mTextBuffer.length();   // Get the character ready to add to a UTF8 string.   UTF16 convertedChar[2] = { ascii, 0 };   //see if it's a number field   if ( mProfile->mNumbersOnly )   {      if (ascii == '-')      {         //a minus sign only exists at the beginning, and only a single minus sign         if (mCursorPos != 0 && !isAllTextSelected())         {            invalidText();            return;         }         if (mInsertOn && (mTextBuffer.getChar(0) == '-'))         {            invalidText();            return;         }      }      // BJTODO: This is probably not unicode safe.      else if (ascii != '.' && (ascii < '0' || ascii > '9'))      {         invalidText();         return;      }      else         validText();   }   //save the current state   saveUndoState();   bool alreadyCut = false;   //delete anything highlighted   if ( mBlockEnd > 0 )   {      mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart);      mCursorPos  = mBlockStart;      mBlockStart = 0;      mBlockEnd   = 0;      // We just changed the string length!      // Get its new value.      stringLen = mTextBuffer.length();      // If we already had text highlighted, we just want to cut that text.      // Don't cut the next character even if insert is not on.      alreadyCut = true;   }   if ( ( mInsertOn && ( stringLen < mMaxStrLen ) ) ||      ( !mInsertOn && ( mCursorPos < mMaxStrLen ) ) )   {      if ( mCursorPos == stringLen )      {         mTextBuffer.append(convertedChar);         mCursorPos++;      }      else      {         if ( mInsertOn || alreadyCut )         {            mTextBuffer.insert(mCursorPos, convertedChar);            mCursorPos++;         }         else         {            mTextBuffer.cut(mCursorPos, 1);            mTextBuffer.insert(mCursorPos, convertedChar);            mCursorPos++;         }      }   }   else      playDeniedSound();   //reset the history index   mHistoryDirty = true;   //execute the console command if it exists   execConsoleCallback();}S32 GuiTextEditCtrl::findPrevWord(){      // First the first word to the left of the current cursor position    // and return the positional index of its starting character.   // We define the first character of a word as any non-whitespace   // character which has a non-alpha-numeric character to its immediate left.   const UTF8* text = mTextBuffer.getPtr8();   for ( S32 i = mCursorPos - 1; i > 0; i-- )   {        if ( !dIsspace( text[i] ) )      {         if ( !dIsalnum( text[i-1] ) )         {            return i;         }      }   }   return 0;}S32 GuiTextEditCtrl::findNextWord(){   // First the first word to the right of the current cursor position    // and return the positional index of its starting character.   // We define the first character of a word as any non-whitespace   // character which has a non-alpha-numeric character to its immediate left.      const UTF8* text = mTextBuffer.getPtr8();   for ( S32 i = mCursorPos + 1; i < mTextBuffer.length(); i++ )   {        if ( !dIsspace( text[i] ) )      {         if ( !dIsalnum( text[i-1] ) )         {            return i;         }      }   }   return mTextBuffer.length();}DefineEngineMethod( GuiTextEditCtrl, getText, const char*, (),,   "@brief Acquires the current text displayed in this control.\n\n"   "@tsexample\n"   "// Acquire the value of the text control.\n"   "%text = %thisGuiTextEditCtrl.getText();\n"   "@endtsexample\n\n"   "@return The current text within the control.\n\n"   "@see GuiControl"){   if( !object->hasText() )      return StringTable->EmptyString();   char *retBuffer = Con::getReturnBuffer( GuiTextEditCtrl::MAX_STRING_LENGTH );   object->getText( retBuffer );   return retBuffer;}DefineEngineMethod( GuiTextEditCtrl, setText, void, (const char* text),,   "@brief Sets the text in the control.\n\n"   "@param text Text to place in the control.\n"   "@tsexample\n"   "// Define the text to display\n"   "%text = \"Text!\"\n\n"   "// Inform the GuiTextEditCtrl to display the defined text\n"   "%thisGuiTextEditCtrl.setText(%text);\n"   "@endtsexample\n\n"   "@see GuiControl"){   object->setText( text );}DefineEngineMethod( GuiTextEditCtrl, getCursorPos, S32, (),,   "@brief Returns the current position of the text cursor in the control.\n\n"   "@tsexample\n"   "// Acquire the cursor position in the control\n"   "%position = %thisGuiTextEditCtrl.getCursorPost();\n"   "@endtsexample\n\n"   "@return Text cursor position within the control.\n\n"   "@see GuiControl"){   return( object->getCursorPos() );}DefineEngineMethod( GuiTextEditCtrl, setCursorPos, void, (S32 position),,   "@brief Sets the text cursor at the defined position within the control.\n\n"   "@param position Text position to set the text cursor.\n"   "@tsexample\n"   "// Define the cursor position\n"   "%position = \"12\";\n\n"   "// Inform the GuiTextEditCtrl control to place the text cursor at the defined position\n"   "%thisGuiTextEditCtrl.setCursorPos(%position);\n"   "@endtsexample\n\n"   "@see GuiControl"){   object->setCursorPos( position );}DefineEngineMethod( GuiTextEditCtrl, isAllTextSelected, bool, (),,   "@brief Checks to see if all text in the control has been selected.\n\n"   "@tsexample\n"   "// Check to see if all text has been selected or not.\n"   "%allSelected = %thisGuiTextEditCtrl.isAllTextSelected();\n"   "@endtsexample\n\n"   "@return True if all text in the control is selected, otherwise false.\n\n"   "@see GuiControl"){   return object->isAllTextSelected();}DefineEngineMethod( GuiTextEditCtrl, selectAllText, void, (),,   "@brief Selects all text within the control.\n\n"   "@tsexample\n"   "// Inform the control to select all of its text.\n"   "%thisGuiTextEditCtrl.selectAllText();\n"   "@endtsexample\n\n"   "@see GuiControl"){   object->selectAllText();}DefineEngineMethod( GuiTextEditCtrl, clearSelectedText, void, (),,   "@brief Unselects all selected text in the control.\n\n"   "@tsexample\n"   "// Inform the control to unselect all of its selected text\n"   "%thisGuiTextEditCtrl.clearSelectedText();\n"   "@endtsexample\n\n"   "@see GuiControl"){   object->clearSelectedText();}DefineEngineMethod( GuiTextEditCtrl, forceValidateText, void, (),,   "@brief Force a validation to occur.\n\n"   "@tsexample\n"   "// Inform the control to force a validation of its text.\n"   "%thisGuiTextEditCtrl.forceValidateText();\n"   "@endtsexample\n\n"   "@see GuiControl"){   object->forceValidateText();}DefineEngineMethod(GuiTextEditCtrl, invalidText, void, (bool playSound), (true),   "@brief Trigger the invalid sound and make the box red.nn"   "@param playSound Play the invalid text sound or not.n"){   object->invalidText(playSound);}DefineEngineMethod(GuiTextEditCtrl, validText, void, (), ,   "@brief Restores the box to normal color.nn"){   object->validText();}DefineEngineMethod(GuiTextEditCtrl, isValidText, bool, (), ,   "@brief Returns if the text is set to valid or not.n"   "@Return true if text is set to valid, false if not.nn"){   return object->isValidText();}
 |