1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744 |
- //-----------------------------------------------------------------------------
- // 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;
- mDoubleClickTimeMS = 50;
- mMouseUpTime = 0;
- mPlaceholderText = StringTable->EmptyString();
- #if defined(__MACOSX__)
- UTF8 bullet[4] = { UTF8(0xE2), UTF8(0x80), UTF8(0xA2), 0 };
-
- mPasswordMask = StringTable->insert( bullet );
- #else
- mPasswordMask = StringTable->insert( "*" );
- #endif
- Sim::findObject( "InputDeniedSound", mDeniedSound );
- mValidateCommand = "";
- }
- 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()
- {
- addProtectedField("placeholderText", TypeCaseString, Offset(mPlaceholderText, GuiTextEditCtrl), setPlaceholderText, getPlaceholderText,
- "The text to show on the control.");
- 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 || String::compare(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 && Platform::getRealMilliseconds() - mMouseUpTime > mDoubleClickTimeMS);
- 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;
- mMouseUpTime = Platform::getRealMilliseconds();
- 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)
- {
- // 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;
- }
-
- // 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;
- }
- 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();
- }
- updateHistory(&mTextBuffer, true);
- mHistoryDirty = false;
- //execute the validate command
- if( mValidateCommand.isNotEmpty() )
- evaluate( mValidateCommand.c_str() );
- 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 );
- }
- bool usePlaceholder = false;
- if (textBuffer.length() == 0 && !isFocused)
- {
- textBuffer.set(mPlaceholderText);
- usePlaceholder = true;
- }
- // 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;
- if (usePlaceholder)
- fontColor = 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();
- }
|