12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2013 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 "console/consoleTypes.h"
- #include "console/console.h"
- #include "graphics/gColor.h"
- #include "graphics/dgl.h"
- #include "gui/guiCanvas.h"
- #include "gui/guiMLTextCtrl.h"
- #include "gui/guiTextEditCtrl.h"
- #include "gui/guiDefaultControlRender.h"
- #include "memory/frameAllocator.h"
- #include "string/unicode.h"
- #include "guiTextEditCtrl_ScriptBinding.h"
- IMPLEMENT_CONOBJECT(GuiTextEditCtrl);
- U32 GuiTextEditCtrl::smNumAwake = 0;
- GuiTextEditCtrl::GuiTextEditCtrl()
- {
- mInsertOn = true;
- mBlockStart = 0;
- mBlockEnd = 0;
- mCursorPos = 0;
- mCursorOn = false;
- mNumFramesElapsed = 0;
- mDragHit = false;
- mScrollDir = 0;
- mUndoBlockStart = 0;
- mUndoBlockEnd = 0;
- mUndoCursorPos = 0;
- mPasswordText = false;
- mReturnCausesTab = false;
- mSinkAllKeyEvents = false;
- mActive = true;
- mTextOffsetReset = true;
- mHistoryDirty = false;
- mHistorySize = 0;
- mHistoryLast = -1;
- mHistoryIndex = 0;
- mHistoryBuf = NULL;
- mValidateCommand = StringTable->EmptyString;
- mEscapeCommand = StringTable->EmptyString;
- mPasswordMask = StringTable->insert( "*" );
- mEditCursor = NULL;
- mMaxStrLen = MAX_STRING_LENGTH;
- mInputMode = AllText;
- }
- GuiTextEditCtrl::~GuiTextEditCtrl()
- {
- //delete the history buffer if it exists
- if (mHistoryBuf)
- {
- for (S32 i = 0; i < mHistorySize; i++)
- delete [] mHistoryBuf[i];
- delete [] mHistoryBuf;
- }
- }
- static EnumTable::Enums inputModeEnums[] =
- {
- { GuiTextEditCtrl::AllText, "AllText" },
- { GuiTextEditCtrl::Decimal, "Decimal" },
- { GuiTextEditCtrl::Number, "Number" },
- { GuiTextEditCtrl::Alpha, "Alpha" },
- { GuiTextEditCtrl::AlphaNumeric, "AlphaNumeric" }
- };
- static EnumTable gInputModeTable(5, &inputModeEnums[0]);
- void GuiTextEditCtrl::initPersistFields()
- {
- Parent::initPersistFields();
- addDepricatedField("validate");
- addDepricatedField("truncate");
- addDepricatedField("passwordMask");
- addDepricatedField("historySize");
- addDepricatedField("tabComplete");
- addGroup("Text Edit");
- addField("validateCommand", TypeString, Offset(mValidateCommand, GuiTextEditCtrl));
- addField("escapeCommand", TypeString, Offset(mEscapeCommand, GuiTextEditCtrl));
- addField("sinkAllKeyEvents", TypeBool, Offset(mSinkAllKeyEvents, GuiTextEditCtrl));
- addField("password", TypeBool, Offset(mPasswordText, GuiTextEditCtrl));
- addField("returnCausesTab", TypeBool, Offset(mReturnCausesTab, GuiTextEditCtrl));
- addProtectedField("maxLength", TypeS32, Offset(mMaxStrLen, GuiTextEditCtrl), &setMaxLengthProperty, &defaultProtectedGetFn, "The max number of characters that can be entered into the text edit box.");
- addProtectedField("inputMode", TypeEnum, Offset(mInputMode, GuiTextEditCtrl), &setInputMode, &getInputMode, &writeInputMode, 1, &gInputModeTable, "InputMode allows different characters to be entered.");
- endGroup("Text Edit");
- }
- 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[MAX_STRING_LENGTH + 1];
- mHistoryBuf[i][0] = '\0';
- }
- }
- if( mText[0] )
- {
- setText(mText);
- }
- return true;
- }
- void GuiTextEditCtrl::onStaticModified(const char* slotName)
- {
- if(!dStricmp(slotName, "text"))
- setText(mText);
- }
- void GuiTextEditCtrl::inspectPostApply()
- {
- Parent::inspectPostApply();
- if (mTextID && *mTextID != 0)
- setTextID(mTextID);
- else
- setText(mText);
- }
- bool GuiTextEditCtrl::onWake()
- {
- if (! Parent::onWake())
- return false;
- if (mConsoleVariable[0])
- {
- const char *txt = Con::getVariable(mConsoleVariable);
- if (txt)
- {
- if (dStrlen(txt) > (U32)mMaxStrLen)
- {
- char* buf = new char[mMaxStrLen + 1];
- dStrncpy(buf, txt, mMaxStrLen);
- buf[mMaxStrLen] = 0;
- setScriptValue(buf);
- delete[] buf;
- }
- else
- setScriptValue(txt);
- }
- }
- // If this is the first awake text edit control, enable keyboard translation
- if (smNumAwake == 0)
- Platform::enableKeyboardTranslation();
- ++smNumAwake;
- return true;
- }
- void GuiTextEditCtrl::onSleep()
- {
- Parent::onSleep();
- // If this is the last awake text edit control, disable keyboard translation
- --smNumAwake;
- if (smNumAwake == 0)
- Platform::disableKeyboardTranslation();
- }
- void GuiTextEditCtrl::execConsoleCallback()
- {
- // Execute the console command!
- if ( mConsoleCommand[0] )
- {
- char buf[16];
- dSprintf( buf, sizeof( buf ), "%d", getId() );
- Con::setVariable( "$ThisControl", buf );
- Con::evaluate( mConsoleCommand, false );
- }
- // Update the console variable:
- if ( mConsoleVariable[0] )
- Con::setVariable( mConsoleVariable, mTextBuffer.getPtr8() );
- }
- void GuiTextEditCtrl::updateHistory( StringBuffer *inTxt, bool moveIndex )
- {
- const UTF16* txt = inTxt->getPtr();
- if(!txt)
- return;
- if(!mHistorySize)
- 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 < (U32)(mHistorySize - 1); i++)
- mHistoryBuf[i] = mHistoryBuf[i+1];
- mHistoryBuf[mHistorySize-1] = first;
- if(mHistoryIndex > 0)
- mHistoryIndex--;
- }
- else
- mHistoryLast++;
- inTxt->getCopy(mHistoryBuf[mHistoryLast], MAX_STRING_LENGTH);
- mHistoryBuf[mHistoryLast][MAX_STRING_LENGTH] = '\0';
- }
- if(moveIndex)
- mHistoryIndex = mHistoryLast + 1;
- }
- void GuiTextEditCtrl::getText( char *dest )
- {
- if ( dest )
- mTextBuffer.getCopy8((UTF8*)dest, MAX_STRING_LENGTH+1);
- }
-
- void GuiTextEditCtrl::setText( const UTF8 *txt )
- {
- //make sure we don't call this before onAdd();
- if (!mProfile)
- return;
- //Make sure we have a font
- mProfile->incRefCount();
- //If the font isn't found, we want to decrement the profile usage and return now or we may crash!
- if (mProfile->mFont.isNull())
- {
- //decrement the profile referrence
- mProfile->decRefCount();
- return;
- }
- setVariable((char*)mText);
- setUpdate();
- //decrement the profile referrence
- mProfile->decRefCount();
- if(txt && txt[0] != 0)
- {
- Parent::setText(txt);
- mTextBuffer.set( txt );
- }
- else
- mTextBuffer.set( "" );
- //respect the max size
- int diff = mTextBuffer.length() - mMaxStrLen;
- if( diff > 0 ) {
- mTextBuffer.cut( mMaxStrLen, diff );
- }
- mCursorPos = mTextBuffer.length();
- }
- void GuiTextEditCtrl::setText( const UTF16* txt)
- {
- if(txt && txt[0] != 0)
- {
- UTF8* txt8 = convertUTF16toUTF8( txt );
- Parent::setText( txt8 );
- setText(txt8);
- delete[] txt8;
- }
- else
- {
- Parent::setText("");
- setText("");
- }
- }
- void GuiTextEditCtrl::setTextID(const char *id)
- {
- S32 n = Con::getIntVariable(id, -1);
- if (n != -1)
- {
- setTextID(n);
- }
- }
- void GuiTextEditCtrl::setTextID(S32 id)
- {
- const UTF8 *str = getGUIString(id);
- if (str)
- setText((const char*)str);
- }
- void GuiTextEditCtrl::selectAllText()
- {
- mBlockStart = 0;
- mBlockEnd = mTextBuffer.length();
- setUpdate();
- }
- bool GuiTextEditCtrl::validate()
- {
- bool valid = true;
- if ( mValidateCommand[0] )
- {
- valid = Con::evaluate( mValidateCommand, false );
- }
- else if (isMethod("onValidate"))
- {
- valid = Con::executef(this, 2, "onValidate");
- }
- return valid;
- }
- void GuiTextEditCtrl::reallySetCursorPos( const S32 newPos )
- {
- S32 charCount = mTextBuffer.length();
- S32 realPos = newPos > charCount ? charCount : newPos < 0 ? 0 : newPos;
- if ( realPos != mCursorPos )
- {
- mCursorPos = realPos;
- setUpdate();
- }
- }
- S32 GuiTextEditCtrl::setCursorPos( const Point2I &offset )
- {
- Point2I ctrlOffset = localToGlobalCoord( Point2I( 0, 0 ) );
- S32 charLength = 0;
- S32 curX;
- curX = offset.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 + mBounds.extent.x )
- return -2;
- curX = offset.x - mTextOffset.x;
- S32 count=0;
- if(mTextBuffer.length() == 0)
- return 0;
- for(count=0; count< (S32)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::onTouchDown( const GuiEvent &event )
- {
- 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 = setCursorPos( 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();
- // Notify Script.
- if( isMethod("onTouchDown") )
- {
- char buf[3][32];
- dSprintf(buf[0], 32, "%d", event.modifier);
- dSprintf(buf[1], 32, "%d %d", event.mousePoint.x, event.mousePoint.y);
- dSprintf(buf[2], 32, "%d", event.mouseClickCount);
- Con::executef(this, 4, "onTouchDown", buf[0], buf[1], buf[2]);
- }
- }
- void GuiTextEditCtrl::onTouchDragged( const GuiEvent &event )
- {
- S32 pos = setCursorPos( 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::onTouchDragged(event);
- // Notify Script.
- if( isMethod("onTouchDragged") )
- {
- char buf[3][32];
- dSprintf(buf[0], 32, "%d", event.modifier);
- dSprintf(buf[1], 32, "%d %d", event.mousePoint.x, event.mousePoint.y);
- dSprintf(buf[2], 32, "%d", event.mouseClickCount);
- Con::executef(this, 4, "onTouchDragged", buf[0], buf[1], buf[2]);
- }
- }
- void GuiTextEditCtrl::onTouchUp(const GuiEvent &event)
- {
- mDragHit = false;
- mScrollDir = 0;
- mouseUnlock();
- // Notify Script.
- if( isMethod("onTouchUp") )
- {
- char buf[3][32];
- dSprintf(buf[0], 32, "%d", event.modifier);
- dSprintf(buf[1], 32, "%d %d", event.mousePoint.x, event.mousePoint.y);
- dSprintf(buf[2], 32, "%d", event.mouseClickCount);
- Con::executef(this, 4, "onTouchUp", buf[0], buf[1], buf[2]);
- }
- }
- 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())
- return false;
- S32 stringLen = mTextBuffer.length();
- setUpdate();
- if (event.keyCode == KEY_BACKSPACE)
- {
- handleBackSpace();
- return true;
- }
- if (event.modifier & SI_SHIFT)
- {
- switch (event.keyCode)
- {
- case KEY_TAB:
- if (isMethod("onTab"))
- Con::executef(this, 2, "onTab", "1");
- return tabPrev();
- 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;
- }
- }
- else if (event.modifier & SI_CTRL)
- {
- switch(event.keyCode)
- {
- // Added UNIX emacs key bindings - just a little hack here...
- // BJGTODO: Add vi bindings.
- // 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
- #if !(defined(TORQUE_OS_OSX) || defined(TORQUE_OS_IOS))
- // 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;
- // [neo, 5/24/2007 - #2986]
- // We don't want to embed control characters in the text, so just return false
- // so that any that any other consumer can have a bash at the input.
- default:
- return false;
- }
- }
- #if (defined(TORQUE_OS_OSX) || defined(TORQUE_OS_IOS))
- // mac style cut / copy / paste / undo keybinds
- else if (event.modifier & SI_ALT)
- {
- // Added Mac cut/copy/paste/undo keys
- // Mac command key maps to alt in torque.
- switch(event.keyCode)
- {
- 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;
- }
- }
- }
- #endif
- else
- {
- switch(event.keyCode)
- {
- case KEY_ESCAPE:
- if ( mEscapeCommand[0] )
- {
- Con::evaluate( mEscapeCommand );
- return( true );
- }
- return( Parent::onKeyDown( event ) );
- case KEY_RETURN:
- case KEY_NUMPADENTER:
- //first validate
- if (!validate())
- {
- //The contents must be invalid. Stop here.
- return true;
- }
- updateHistory(&mTextBuffer, true);
- mHistoryDirty = false;
- //next exec the alt console command
- execAltConsoleCallback();
- // Notify of Return
- if ( isMethod("onReturn") )
- Con::executef( this, 1, "onReturn" );
- if (mReturnCausesTab)
- {
- tabNext();
- }
- return true;
- case KEY_UP:
- {
- 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(mHistoryDirty)
- {
- updateHistory(&mTextBuffer, false);
- mHistoryDirty = false;
- }
- mHistoryIndex++;
- if(mHistoryIndex > mHistoryLast)
- {
- mHistoryIndex = mHistoryLast + 1;
- setText("");
- }
- else
- setText(mHistoryBuf[mHistoryIndex]);
- return true;
- case KEY_LEFT:
- mBlockStart = 0;
- mBlockEnd = 0;
- if (mCursorPos > 0)
- {
- mCursorPos--;
- }
- return true;
- case KEY_RIGHT:
- mBlockStart = 0;
- mBlockEnd = 0;
- if (mCursorPos < stringLen)
- {
- mCursorPos++;
- }
- return true;
- case KEY_BACKSPACE:
- //This should have been handled above, but it is here again for safety.
- handleBackSpace();
- 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;
- }
- }
- switch ( event.keyCode )
- {
- case KEY_TAB:
- return tabNext();
- case KEY_UP:
- case KEY_DOWN:
- case KEY_ESCAPE:
- return Parent::onKeyDown( event );
- }
- if(mProfile->mFont.isNull())
- return false;
- if ( mProfile->mFont->isValidChar( event.ascii ) )
- {
- // Get the character ready to add to a UTF8 string.
- UTF16 convertedChar[2] = { event.ascii, 0 };
- //Stop characters that aren't allowed based on InputMode
- if (!inputModeValidate(event.ascii, mCursorPos))
- {
- keyDenied();
- return true;
- }
- //save the current state
- saveUndoState();
- //delete anything highlighted
- if ( mBlockEnd > 0 )
- {
- mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart);
- mCursorPos = mBlockStart;
- mBlockStart = 0;
- mBlockEnd = 0;
- }
- if ( ( mInsertOn && ( stringLen < mMaxStrLen ) ) ||
- ( !mInsertOn && ( mCursorPos < mMaxStrLen ) ) )
- {
- if ( mCursorPos == stringLen )
- {
- mTextBuffer.append(convertedChar);
- mCursorPos++;
- }
- else
- {
- if ( mInsertOn )
- {
- mTextBuffer.insert(mCursorPos, convertedChar);
- mCursorPos++;
- }
- else
- {
- mTextBuffer.cut(mCursorPos, 1);
- mTextBuffer.insert(mCursorPos, convertedChar);
- mCursorPos++;
- }
- }
- }
- else
- keyDenied();
- //reset the history index
- mHistoryDirty = true;
- //execute the console command if it exists
- execConsoleCallback();
- return true;
- }
- //not handled - pass the event to it's parent
- // Or eat it if that's appropriate.
- if (mSinkAllKeyEvents)
- return true;
- return Parent::onKeyDown( event );
- }
- bool GuiTextEditCtrl::tabNext()
- {
- if (isMethod("onTab"))
- Con::executef(this, 2, "onTab", "0");
- GuiCanvas *root = getRoot();
- if (root)
- {
- root->tabNext();
- return true;
- }
- return false;
- }
- bool GuiTextEditCtrl::tabPrev()
- {
- if (isMethod("onTab"))
- Con::executef(this, 2, "onTab", "1");
- GuiCanvas *root = getRoot();
- if (root)
- {
- root->tabPrev();
- return true;
- }
- return false;
- }
- void GuiTextEditCtrl::handleBackSpace()
- {
- //save the current state
- saveUndoState();
- if (mBlockEnd > 0)
- {
- mTextBuffer.cut(mBlockStart, mBlockEnd - mBlockStart);
- mCursorPos = mBlockStart;
- mBlockStart = 0;
- mBlockEnd = 0;
- mHistoryDirty = true;
- }
- else if (mCursorPos > 0)
- {
- mTextBuffer.cut(mCursorPos - 1, 1);
- mCursorPos--;
- mHistoryDirty = true;
- }
- // Execute the console command!
- execConsoleCallback();
- }
- void GuiTextEditCtrl::setFirstResponder()
- {
- Parent::setFirstResponder();
-
- #if !defined(TORQUE_OS_IOS) && !defined(TORQUE_OS_ANDROID)
- Platform::enableKeyboardTranslation();
- #endif
- }
- void GuiTextEditCtrl::onLoseFirstResponder()
- {
- Platform::disableKeyboardTranslation();
- //first, update the history
- updateHistory( &mTextBuffer, true );
- //execute the validate command
- bool valid = validate();
- if (valid)
- {
- execAltConsoleCallback();
- }
- if( isMethod( "onLoseFirstResponder" ) )
- Con::executef( this, 2, "onLoseFirstResponder", valid);
- // Redraw the control:
- setUpdate();
- }
- void GuiTextEditCtrl::parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent)
- {
- Parent::parentResized( oldParentExtent, newParentExtent );
- mTextOffsetReset = true;
- }
- void GuiTextEditCtrl::onRender(Point2I offset, const RectI & updateRect)
- {
- //Notice that there's no Highlight state. The HL colors are used for selected text - not hover.
- //The selected state is used when the box has the focus and can be typed in.
- GuiControlState currentState = NormalState;
- if (!mActive)
- {
- currentState = DisabledState;
- }
- else if (isFirstResponder())
- {
- currentState = SelectedState;
- }
- RectI ctrlRect = applyMargins(offset, mBounds.extent, currentState, mProfile);
- if (!ctrlRect.isValidRect())
- {
- return;
- }
- renderUniversalRect(ctrlRect, mProfile, currentState);
- //Render Text
- dglSetBitmapModulation(mProfile->mFontColor);
- RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
- RectI contentRect = applyPadding(fillRect.point, fillRect.extent, NormalState, mProfile);
- if (contentRect.isValidRect())
- {
- drawText(contentRect, currentState);
- //Render the childen
- renderChildControls(offset, contentRect, updateRect);
- }
- }
- 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, GuiControlState currentState )
- {
- RectI old = dglGetClipRect();
- RectI clipRect = RectI(drawRect.point, drawRect.extent);
- if (clipRect.intersect(old))
- {
- dglSetClipRect(clipRect);
- // Just a little sanity.
- if(mCursorPos > (S32)mTextBuffer.length())
- mCursorPos = (S32)mTextBuffer.length();
- if(mCursorPos < 0)
- mCursorPos = 0;
- StringBuffer textBuffer;
- Point2I drawPoint = drawRect.point;
- // Apply password masking (make the masking char optional perhaps?)
- if(mPasswordText)
- {
- for(U32 i = 0; i<mTextBuffer.length(); i++)
- textBuffer.append(mPasswordMask);
- }
- else
- {
- // Or else just copy it over.
- textBuffer.set(&mTextBuffer);
- }
- // Center vertically:
- S32 h = mProfile->mFont->getHeight();
- drawPoint.y += ( ( drawRect.extent.y - h ) / 2 );
- // Align horizontally:
-
- S32 textWidth = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), textBuffer.length());
- if ( drawRect.extent.x > textWidth )
- {
- switch( mProfile->mAlignment )
- {
- case GuiControlProfile::RightAlign:
- drawPoint.x += ( drawRect.extent.x - textWidth );
- break;
- case GuiControlProfile::CenterAlign:
- drawPoint.x += ( ( drawRect.extent.x - textWidth ) / 2 );
- break;
- }
- }
- ColorI fontColor = mActive ? mProfile->mFontColor : mProfile->mFontColorNA;
- // now draw the text
- Point2I cursorStart, cursorEnd;
- mTextOffset = drawPoint;
- if ( mTextOffsetReset )
- {
- mTextOffset.x = drawPoint.x;
- mTextOffsetReset = false;
- }
- if ( drawRect.extent.x > textWidth )
- mTextOffset.x = drawPoint.x;
- else
- {
- // Alignment affects large text
- if ( mProfile->mAlignment == GuiControlProfile::RightAlign
- || mProfile->mAlignment == GuiControlProfile::CenterAlign )
- {
- if ( mTextOffset.x + textWidth < (drawRect.point.x + drawRect.extent.x))
- mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - textWidth;
- }
- }
- // calculate the cursor
- if ( currentState == SelectedState )
- {
- // Where in the string are we?
- S32 cursorOffset=0, charWidth=0;
- UTF16 tempChar = mTextBuffer.getChar(mCursorPos);
- // Alright, we want to terminate things momentarily.
- if(mCursorPos > 0)
- {
- cursorOffset = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), mCursorPos);
- }
- else
- cursorOffset = 0;
- if ( tempChar )
- charWidth = mProfile->mFont->getCharWidth( tempChar );
- else
- charWidth = 0;
- if( mTextOffset.x + cursorOffset + charWidth >= (drawRect.point.x + drawRect.extent.x))
- {
- // Cursor somewhere beyond the textcontrol,
- // skip forward roughly 25% of the total width (if possible)
- S32 skipForward = drawRect.extent.x / 4;
- if ( cursorOffset + skipForward > textWidth )
- mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - textWidth;
- else
- mTextOffset.x -= skipForward;
- }
- else if( mTextOffset.x + cursorOffset < drawRect.point.x)
- {
- // Cursor somewhere before the textcontrol
- // skip backward roughly 25% of the total width (if possible)
- S32 skipBackward = drawRect.extent.x / 4;
- if ( cursorOffset - skipBackward < 0 )
- mTextOffset.x = drawRect.point.x;
- else
- mTextOffset.x += skipBackward;
- }
- cursorStart.x = mTextOffset.x + cursorOffset;
- 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 ( currentState != SelectedState )
- mBlockStart = mBlockEnd = 0;
- //also verify the block start/end
- if ((mBlockStart > (S32)textBuffer.length() || (mBlockEnd > (S32)textBuffer.length()) || (mBlockStart > mBlockEnd)))
- mBlockStart = mBlockEnd = 0;
- Point2I tempOffset = mTextOffset;
- //draw the portion before the highlight
- if ( mBlockStart > 0 )
- {
- dglSetBitmapModulation( fontColor );
- const UTF16* preString2 = textBuffer.getPtr();
- dglDrawTextN( mProfile->mFont, tempOffset, preString2, mBlockStart, mProfile->mFontColors);
- tempOffset.x += mProfile->mFont->getStrNWidth(preString2, mBlockStart);
- }
- //draw the hilighted portion
- if ( mBlockEnd > 0 )
- {
- const UTF16* highlightBuff = textBuffer.getPtr() + mBlockStart;
- U32 highlightBuffLen = mBlockEnd-mBlockStart;
- S32 highlightWidth = mProfile->mFont->getStrNWidth(highlightBuff, highlightBuffLen);
- dglDrawRectFill( Point2I( tempOffset.x, drawRect.point.y + 1 ),
- Point2I( tempOffset.x + highlightWidth, drawRect.point.y + drawRect.extent.y - 1),
- mProfile->mFillColorHL );
- dglSetBitmapModulation( mProfile->mFontColorHL );
- dglDrawTextN( mProfile->mFont, tempOffset, highlightBuff, highlightBuffLen, mProfile->mFontColors );
- tempOffset.x += highlightWidth;
- }
- //draw the portion after the highlite
- if(mBlockEnd < (S32)mTextBuffer.length())
- {
- const UTF16* finalBuff = textBuffer.getPtr() + mBlockEnd;
- U32 finalBuffLen = mTextBuffer.length() - mBlockEnd;
- dglSetBitmapModulation( fontColor );
- dglDrawTextN( mProfile->mFont, tempOffset, finalBuff, finalBuffLen, mProfile->mFontColors );
- }
- //draw the cursor
- if (currentState == SelectedState && mCursorOn )
- dglDrawLine( cursorStart, cursorEnd, mProfile->mCursorColor );
- dglSetClipRect(old);
- }
- }
- bool GuiTextEditCtrl::hasText()
- {
- return (mTextBuffer.length());
- }
- void GuiTextEditCtrl::keyDenied()
- {
- if (isMethod("onDenied"))
- Con::executef(this, 1, "onDenied");
- }
- const char *GuiTextEditCtrl::getScriptValue()
- {
- return StringTable->insert(mTextBuffer.getPtr8());
- }
- void GuiTextEditCtrl::setScriptValue(const char *value)
- {
- mTextBuffer.set(value);
- mCursorPos = getMin((S32)(mTextBuffer.length() - 1), 0);
- }
- StringBuffer GuiTextEditCtrl::truncate(StringBuffer buffer, StringBuffer terminationString, S32 width)
- {
- // Check if the buffer width exceeds the specified width
- S32 bufferWidth = textBufferWidth(buffer);
- // If not, just return the unmodified buffer
- if (bufferWidth <= width)
- return buffer;
- // Get the width of the termination string
- S32 terminationWidth = textBufferWidth(terminationString) + 6; // add an extra bit of space at the end
- // Calculate the new target width with space allowed for the termination string
- S32 targetWidth = width - terminationWidth;
- // If the target width is zero or less, just replace the entire buffer with the termination string
- if (targetWidth <= 0)
- return terminationString;
- // Step backwards in the buffer until we find the character that fits within the target width
- S32 currentWidth = 0;
- S32 count = 0;
- for (S32 i = 0; i < (S32)buffer.length(); i++)
- {
- if (currentWidth >= targetWidth)
- break;
- UTF16 c = buffer.getChar(i);
- currentWidth += mProfile->mFont->getCharXIncrement(c);
- count++;
- }
- // Get the substring
- StringBuffer retBuffer = buffer.substring(0, count - 2);
- // Append terminating string
- retBuffer.append(terminationString);
- return retBuffer;
- }
- S32 GuiTextEditCtrl::textBufferWidth(StringBuffer buffer)
- {
- S32 charLength = 0;
- for (S32 count = 0; count < (S32)buffer.length(); count++)
- {
- UTF16 c = buffer.getChar(count);
- if (!mProfile->mFont->isValidChar(c))
- continue;
- charLength += mProfile->mFont->getCharXIncrement(c);
- }
- return charLength;
- }
- GuiTextEditCtrl::InputMode GuiTextEditCtrl::getInputModeEnum(const char* label)
- {
- // Search for Mnemonic.
- for (U32 i = 0; i < (sizeof(inputModeEnums) / sizeof(EnumTable::Enums)); i++)
- {
- if (dStricmp(inputModeEnums[i].label, label) == 0)
- return (InputMode)inputModeEnums[i].index;
- }
- // Warn.
- Con::warnf("GuiTextEditCtrl::getInputModeEnum() - Invalid mode of '%s'", label);
- return (InputMode)-1;
- }
- const char* GuiTextEditCtrl::getInputModeDescription(const InputMode mode)
- {
- // Search for Mnemonic.
- for (U32 i = 0; i < (sizeof(inputModeEnums) / sizeof(EnumTable::Enums)); i++)
- {
- if (inputModeEnums[i].index == mode)
- return inputModeEnums[i].label;
- }
- // Warn.
- Con::warnf("GuiTextEditCtrl::getInputModeDescription() - Invalid input mode.");
- return StringTable->EmptyString;
- }
- //Returns true if valid, false if invalid
- bool GuiTextEditCtrl::inputModeValidate(const U16 key, S32 cursorPos)
- {
- if (key == '-')
- {
- if (mInputMode == Alpha || mInputMode == AlphaNumeric)
- {
- return false;
- }
- else if (mInputMode == Decimal || mInputMode == Number)
- {
- //a minus sign only exists at the beginning, and only a single minus sign
- if (cursorPos != 0 || (mInsertOn && mTextBuffer.getChar(0) == '-'))
- {
- return false;
- }
- }
- }
- else if (key >= '0' && key <= '9')
- {
- if (mInputMode == Alpha)
- {
- return false;
- }
- }
- else if (key == '.')
- {
- if (mInputMode == Number || mInputMode == Alpha || mInputMode == AlphaNumeric)
- {
- return false;
- }
- else if (mInputMode == Decimal)
- {
- if (!mInsertOn && mTextBuffer.getChar(cursorPos) == '.')
- {
- return true;
- }
- const char* dot = dStrchr(mTextBuffer.getPtr8(), '.');
- if (dot != NULL)
- {
- return false;
- }
- }
- }
- else if ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'))
- {
- if (mInputMode == Decimal || mInputMode == Number)
- {
- return false;
- }
- }
- else if (key == 32)
- {
- if (mInputMode == Decimal || mInputMode == Number)
- {
- return false;
- }
- }
- else if (mInputMode == Decimal || mInputMode == Number || mInputMode == Alpha || mInputMode == AlphaNumeric)
- {
- //The remaining characters only go with AllText
- return false;
- }
- //Looks like we have a valid character!
- return true;
- }
- void GuiTextEditCtrl::setMaxLength(S32 max)
- {
- mMaxStrLen = getMax(1, getMin(max, MAX_STRING_LENGTH));
- }
- void GuiTextEditCtrl::setInputMode(const InputMode mode)
- {
- if(mInputMode == mode)
- return;
- //Let's start by clearing the history.
- mHistoryIndex = 0;
- mHistoryLast = -1;
- //Time to set the mode
- mInputMode = mode;
- //now let's parse that buffer and get rid of invalid characters
- if (mode != AllText)
- {
- bool oldInsert = mInsertOn;
- mInsertOn = false;
- for (S32 i = 0; i < MAX_STRING_LENGTH; i++)
- {
- const UTF16 character = mTextBuffer.getChar(i);
- if (character == '\0')
- {
- //Done and done.
- break;
- }
-
- if (!inputModeValidate(character, i))
- {
- //Bad Character! Let's remove it.
- mTextBuffer.cut(i, 1);
- //Step it back
- i--;
- }
- }
- mInsertOn = oldInsert;
- }
- }
|