1 Star 0 Fork 0

yoyojacky/CSMoE

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
TextEntry.cpp 107.34 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269
//========= Copyright ?1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <tier1/utlvector.h>
#include <tier1/KeyValues.h>
#include <vgui/Cursor.h>
#include <vgui/IInputInternal.h>
#include <vgui/IScheme.h>
#include <vgui/ISystem.h>
#include <vgui/ISurface.h>
#include <vgui/ILocalize.h>
#include <vgui/IPanel.h>
#include <vgui/MouseCode.h>
#include "Menu.h"
#include "ScrollBar.h"
#include "TextEntry.h"
#include "Controls.h"
#include "MenuItem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
enum
{
// maximum size of text buffer
BUFFER_SIZE=999999,
};
using namespace vgui2;
static const int DRAW_OFFSET_X = 3,DRAW_OFFSET_Y = 1;
DECLARE_BUILD_FACTORY( TextEntry );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
TextEntry::TextEntry(Panel *parent, const char *panelName) : Panel(parent, panelName)
{
SetTriplePressAllowed( true );
_font = INVALID_FONT;
_smallfont = INVALID_FONT;
m_szComposition[ 0 ] = L'\0';
m_bAllowNumericInputOnly = false;
m_bAllowNonAsciiCharacters = false;
_hideText = false;
_editable = false;
_verticalScrollbar = false;
_cursorPos = 0;
_currentStartIndex = 0;
_horizScrollingAllowed = true;
_cursorIsAtEnd = false;
_putCursorAtEnd = false;
_multiline = false;
_cursorBlinkRate = 400;
_mouseSelection = false;
_mouseDragSelection = false;
_vertScrollBar=NULL;
_catchEnterKey = false;
_maxCharCount = -1;
_charCount = 0;
_wrap = false; // don't wrap by default
_sendNewLines = false; // don't pass on a newline msg by default
_drawWidth = 0;
m_bAutoProgressOnHittingCharLimit = false;
m_pIMECandidates = NULL;
m_hPreviousIME = input()->GetEnglishIMEHandle();
m_bDrawLanguageIDAtLeft = false;
m_nLangInset = 0;
m_bUseFallbackFont = false;
m_hFallbackFont = INVALID_FONT;
m_bImageBackground = false;
//a -1 for _select[0] means that the selection is empty
_select[0] = -1;
_select[1] = -1;
m_pEditMenu = NULL;
//this really just inits it when in here
ResetCursorBlink();
SetCursor(dc_ibeam);
SetEditable(true);
// initialize the line break array
m_LineBreaks.AddToTail(BUFFER_SIZE);
_recalculateBreaksIndex = 0;
_selectAllOnFirstFocus = false;
_selectAllOnFocusAlways = false;
//position the cursor so it is at the end of the text
GotoTextEnd();
// If keyboard focus is in an edit control, don't chain keyboard mappings up to parents since it could mess with typing in text.
SetAllowKeyBindingChainToParent( false );
}
TextEntry::~TextEntry()
{
delete m_pEditMenu;
delete m_pIMECandidates;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
SetFgColor(GetSchemeColor("TextEntryFgColor", GetSchemeColor("TextEntry.TextColor", GetSchemeColor("WindowFgColor", pScheme), pScheme), pScheme));
SetBgColor(GetSchemeColor("TextEntryBgColor", GetSchemeColor("TextEntry.BgColor", GetSchemeColor("WindowBgColor", pScheme), pScheme), pScheme));
_cursorColor = GetSchemeColor("TextEntry.CursorColor", GetSchemeColor("TextCursorColor", pScheme), pScheme);
_disabledFgColor = GetSchemeColor("TextEntry.DisabledTextColor", GetSchemeColor("WindowDisabledFgColor", pScheme), pScheme);
_disabledBgColor = GetSchemeColor("TextEntry.DisabledBgColor", GetSchemeColor("ControlBG", pScheme), pScheme);
_selectionTextColor = GetSchemeColor("TextEntry.SelectedTextColor", GetSchemeColor("SelectionFgColor", GetFgColor(), pScheme), pScheme);
_selectionColor = GetSchemeColor("TextEntry.SelectedBgColor", GetSchemeColor("SelectionBgColor", pScheme), pScheme);
_defaultSelectionBG2Color = GetSchemeColor("TextEntry.OutOfFocusSelectedBgColor", GetSchemeColor("SelectionBG2", pScheme), pScheme);
_focusEdgeColor = GetSchemeColor("TextEntry.FocusEdgeColor", GetSchemeColor("BorderSelection", Color(0, 0, 0, 0), pScheme), pScheme);
SetBorder( pScheme->GetBorder("ButtonDepressedBorder"));
_font = pScheme->GetFont("Default", IsProportional() );
_smallfont = pScheme->GetFont( "DefaultVerySmall", IsProportional() );
const char *resourceString = pScheme->GetResourceString("TextEntry/TopLeft");
if (resourceString[0])
{
m_bImageBackground = true;
m_pTopBackground[0] = scheme()->GetImage(resourceString, true);
m_pTopBackground[1] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/TopCenter"), true);
m_pTopBackground[2] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/TopRight"), true);
m_pCenterBackground[0] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/MiddleLeft"), true);
m_pCenterBackground[1] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/MiddleCenter"), true);
m_pCenterBackground[2] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/MiddleRight"), true);
m_pBottomBackground[0] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/BottomLeft"), true);
m_pBottomBackground[1] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/BottomCenter"), true);
m_pBottomBackground[2] = scheme()->GetImage(pScheme->GetResourceString("TextEntry/BottomRight"), true);
}
SetFont( _font );
}
void TextEntry::SetSelectionTextColor( const Color& clr )
{
_selectionTextColor = clr;
}
void TextEntry::SetSelectionBgColor( const Color& clr )
{
_selectionColor = clr;
}
void TextEntry::SetSelectionUnfocusedBgColor( const Color& clr )
{
_defaultSelectionBG2Color = clr;
}
//-----------------------------------------------------------------------------
// Purpose: sets the color of the background when the control is disabled
//-----------------------------------------------------------------------------
void TextEntry::SetDisabledBgColor(Color col)
{
_disabledBgColor = col;
}
//-----------------------------------------------------------------------------
// Purpose: Sends a message if the data has changed
// Turns off any selected text in the window if we are not using the edit menu
//-----------------------------------------------------------------------------
void TextEntry::OnKillFocus()
{
m_szComposition[ 0 ] = L'\0';
HideIMECandidates();
if (_dataChanged)
{
FireActionSignal();
_dataChanged = false;
}
// check if we clicked the right mouse button or if it is down
bool mouseRightClicked = input()->WasMousePressed(MOUSE_RIGHT);
bool mouseRightUp = input()->WasMouseReleased(MOUSE_RIGHT);
bool mouseRightDown = input()->IsMouseDown(MOUSE_RIGHT);
if (mouseRightClicked || mouseRightDown || mouseRightUp )
{
int cursorX, cursorY;
input()->GetCursorPos(cursorX, cursorY);
// if we're right clicking within our window, we don't actually kill focus
if (IsWithin(cursorX, cursorY))
return;
}
// clear any selection
SelectNone();
// move the cursor to the start
// GotoTextStart();
PostActionSignal( new KeyValues( "TextKillFocus" ) );
// chain
BaseClass::OnKillFocus();
}
//-----------------------------------------------------------------------------
// Purpose: Wipe line breaks after the size of a panel has been changed
//-----------------------------------------------------------------------------
void TextEntry::OnSizeChanged(int newWide, int newTall)
{
BaseClass::OnSizeChanged(newWide, newTall);
// blow away the line breaks list
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
// if we're bigger, see if we can scroll left to put more text in the window
if (newWide > _drawWidth)
{
ScrollLeftForResize();
}
_drawWidth = newWide;
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Set the text array - convert ANSI text to unicode and pass to unicode function
//-----------------------------------------------------------------------------
void TextEntry::SetText(const char *text)
{
if (!text)
{
text = "";
}
if (text[0] == '#')
{
// check for localization
wchar_t *wsz = localize()->Find(text);
if (wsz)
{
SetText(wsz);
return;
}
}
size_t len = strlen( text );
if ( len < 1023 )
{
wchar_t unicode[ 1024 ];
localize()->ConvertANSIToUnicode( text, unicode, sizeof( unicode ) );
SetText( unicode );
}
else
{
size_t lenUnicode = ( len * sizeof( wchar_t ) + 4 );
wchar_t *unicode = ( wchar_t * ) malloc( lenUnicode );
localize()->ConvertANSIToUnicode( text, unicode, lenUnicode );
SetText( unicode );
free( unicode );
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the text array
// Using this function will cause all lineBreaks to be discarded.
// This is because this fxn replaces the contents of the text buffer.
// For modifying large buffers use insert functions.
//-----------------------------------------------------------------------------
void TextEntry::SetText(const wchar_t *wszText)
{
if (!wszText)
{
wszText = L"";
}
int textLen = wcslen(wszText);
m_TextStream.RemoveAll();
m_TextStream.EnsureCapacity(textLen);
int missed_count = 0;
for (int i = 0; i < textLen; i++)
{
if(wszText[i]=='\r') // don't insert \r characters
{
missed_count++;
continue;
}
m_TextStream.AddToTail(wszText[i]);
SetCharAt(wszText[i], i-missed_count);
}
GotoTextStart();
SelectNone();
// reset the data changed flag
_dataChanged = false;
// blow away the line breaks list
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the value of char at index position.
//-----------------------------------------------------------------------------
void TextEntry::SetCharAt(wchar_t ch, int index)
{
if ((ch == '\n') || (ch == '\0'))
{
// if its not at the end of the buffer it matters.
// redo the linebreaks
//if (index != m_TextStream.Count())
{
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
}
}
if (index < 0)
return;
if (index >= m_TextStream.Count())
{
m_TextStream.AddMultipleToTail(index - m_TextStream.Count() + 1);
}
m_TextStream[index] = ch;
_dataChanged = true;
}
//-----------------------------------------------------------------------------
// Purpose: Restarts the time of the next cursor blink
//-----------------------------------------------------------------------------
void TextEntry::ResetCursorBlink()
{
_cursorBlink=false;
_cursorNextBlinkTime=system()->GetTimeMillis()+_cursorBlinkRate;
}
//-----------------------------------------------------------------------------
// Purpose: Hides the text buffer so it will not be drawn
//-----------------------------------------------------------------------------
void TextEntry::SetTextHidden(bool bHideText)
{
_hideText = bHideText;
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: return character width
//-----------------------------------------------------------------------------
int getCharWidth(HFont font, wchar_t ch)
{
if (!iswcntrl(ch))
{
int a, b, c;
surface()->GetCharABCwide(font, ch, a, b, c);
return (a + b + c);
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Given cursor's position in the text buffer, convert it to
// the local window's x and y pixel coordinates
// Input: cursorPos: cursor index
// Output: cx, cy, the corresponding coords in the local window
//-----------------------------------------------------------------------------
void TextEntry::CursorToPixelSpace(int cursorPos, int &cx, int &cy)
{
int yStart = GetYStart();
int x = DRAW_OFFSET_X, y = yStart;
_pixelsIndent = 0;
int lineBreakIndexIndex = 0;
for (int i = GetStartDrawIndex(lineBreakIndexIndex); i < m_TextStream.Count(); i++)
{
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
// if we've found the position, break
if (cursorPos == i)
{
// even if this is a line break entry for the cursor, the next insert
// will be at this position, which will push the line break forward one
// so don't push the cursor down a line here...
/*if (!_putCursorAtEnd)
{
// if we've passed a line break go to that
if (m_LineBreaks[lineBreakIndexIndex] == i)
{
// add another line
AddAnotherLine(x,y);
lineBreakIndexIndex++;
}
}*/
break;
}
// if we've passed a line break go to that
if (m_LineBreaks.Count() &&
lineBreakIndexIndex < m_LineBreaks.Count() &&
m_LineBreaks[lineBreakIndexIndex] == i)
{
// add another line
AddAnotherLine(x,y);
lineBreakIndexIndex++;
}
// add to the current position
x += getCharWidth(_font, ch);
}
if ( m_bDrawLanguageIDAtLeft )
{
x += m_nLangInset;
}
cx = x;
cy = y;
}
//-----------------------------------------------------------------------------
// Purpose: Converts local pixel coordinates to an index in the text buffer
// This function appears to be used only in response to mouse clicking
// Input : cx -
// cy - pixel location
//-----------------------------------------------------------------------------
int TextEntry::PixelToCursorSpace(int cx, int cy)
{
int w, h;
GetSize(w, h);
cx = min(max(cx, 0), w+100);
cy = min(max(cy, 0), h);
_putCursorAtEnd = false; // Start off assuming we clicked somewhere in the text
int fontTall = surface()->GetFontTall(_font);
// where to Start reading
int yStart = GetYStart();
int x = DRAW_OFFSET_X, y = yStart;
_pixelsIndent = 0;
int lineBreakIndexIndex = 0;
int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
bool onRightLine = false;
int i;
for (i = startIndex; i < m_TextStream.Count(); i++)
{
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
// if we are on the right line but off the end of if put the cursor at the end of the line
if (m_LineBreaks[lineBreakIndexIndex] == i )
{
// add another line
AddAnotherLine(x,y);
lineBreakIndexIndex++;
if (onRightLine)
{
_putCursorAtEnd = true;
return i;
}
}
// check to see if we're on the right line
if (cy < yStart)
{
// cursor is above panel
onRightLine = true;
_putCursorAtEnd = true; // this will make the text scroll up if needed
}
else if (cy >= y && (cy < (y + fontTall + DRAW_OFFSET_Y)))
{
onRightLine = true;
}
int wide = getCharWidth(_font, ch);
// if we've found the position, break
if (onRightLine)
{
if (cx > GetWide()) // off right side of window
{
}
else if (cx < (DRAW_OFFSET_X + _pixelsIndent) || cy < yStart) // off left side of window
{
return i; // move cursor one to left
}
if (cx >= x && cx < (x + wide))
{
// check which side of the letter they're on
if (cx < (x + (wide * 0.5))) // left side
{
return i;
}
else // right side
{
return i + 1;
}
}
}
x += wide;
}
return i;
}
//-----------------------------------------------------------------------------
// Purpose: Draws a character in the panel
// Input: ch - character to draw
// font - font to use
// x, y - pixel location to draw char at
// Output: returns the width of the character drawn
//-----------------------------------------------------------------------------
int TextEntry::DrawChar(wchar_t ch, HFont font, int index, int x, int y)
{
// add to the current position
int charWide = getCharWidth(font, ch);
int fontTall=surface()->GetFontTall(font);
if (!iswcntrl(ch))
{
// draw selection, if any
int selection0 = -1, selection1 = -1;
GetSelectedRange(selection0, selection1);
if (index >= selection0 && index < selection1)
{
// draw background selection color
VPANEL focus = input()->GetFocus();
Color bgColor;
bool hasFocus = HasFocus();
bool childOfFocus = focus && ipanel()->HasParent(focus, GetVPanel());
// if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
if ( hasFocus || childOfFocus )
{
bgColor = _selectionColor;
}
else
{
bgColor =_defaultSelectionBG2Color;
}
surface()->DrawSetColor(bgColor);
surface()->DrawFilledRect(x, y, x + charWide, y + 1 + fontTall);
// reset text color
surface()->DrawSetTextColor(_selectionTextColor);
}
if (index == selection1)
{
// we've come out of selection, reset the color
surface()->DrawSetTextColor(GetFgColor());
}
surface()->DrawSetTextPos(x, y);
surface()->DrawUnicodeChar(ch);
return charWide;
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Draw the cursor, cursor is not drawn when it is blinked gone
// Input: x,y where to draw cursor
// Output: returns true if cursor was drawn.
//-----------------------------------------------------------------------------
bool TextEntry::DrawCursor(int x, int y)
{
if (!_cursorBlink)
{
int cx, cy;
CursorToPixelSpace(_cursorPos, cx, cy);
surface()->DrawSetColor(_cursorColor);
int fontTall=surface()->GetFontTall(_font);
surface()->DrawFilledRect(cx, cy, cx + 1, cy + fontTall);
return true;
}
return false;
}
bool TextEntry::NeedsEllipses( HFont font, int *pIndex )
{
Assert( pIndex );
*pIndex = -1;
int wide = DRAW_OFFSET_X; // buffer on left and right end of text.
for ( int i = 0; i < m_TextStream.Count(); ++i )
{
wide += getCharWidth( font , m_TextStream[i] );
if (wide > _drawWidth)
{
*pIndex = i;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Draws the text in the panel
//-----------------------------------------------------------------------------
void TextEntry::PaintBackground()
{
// draw background
Color col;
if (IsEnabled())
{
col = GetBgColor();
}
else
{
col = _disabledBgColor;
}
Color saveBgColor = col;
int wide, tall;
GetSize( wide, tall );
if (!m_bImageBackground)
{
surface()->DrawSetColor(col);
surface()->DrawFilledRect(0, 0, wide, tall);
}
else
{
const int iOffset = 0;
m_pTopBackground[0]->SetPos(0, 0);
m_pTopBackground[0]->SetSize(7, 7);
m_pTopBackground[0]->Paint();
m_pTopBackground[1]->SetPos(7, 0);
m_pTopBackground[1]->SetSize(wide - 14 - iOffset, 7);
m_pTopBackground[1]->Paint();
m_pTopBackground[2]->SetPos(wide - 7 - iOffset, 0);
m_pTopBackground[2]->SetSize(7, 7);
m_pTopBackground[2]->Paint();
m_pCenterBackground[0]->SetPos(0, 7);
m_pCenterBackground[0]->SetSize(7, tall - 14);
m_pCenterBackground[0]->Paint();
m_pCenterBackground[1]->SetPos(7, 7);
m_pCenterBackground[1]->SetSize(wide - 14 - iOffset, tall - 14);
m_pCenterBackground[1]->Paint();
m_pCenterBackground[2]->SetPos(wide - 7 - iOffset, 7);
m_pCenterBackground[2]->SetSize(7, tall - 14);
m_pCenterBackground[2]->Paint();
m_pBottomBackground[0]->SetPos(0, tall - 7);
m_pBottomBackground[0]->SetSize(7, 7);
m_pBottomBackground[0]->Paint();
m_pBottomBackground[1]->SetPos(7, tall - 7);
m_pBottomBackground[1]->SetSize(wide - 14 - iOffset, 7);
m_pBottomBackground[1]->Paint();
m_pBottomBackground[2]->SetPos(wide - 7, tall - 7);
m_pBottomBackground[2]->SetSize(7, 7);
m_pBottomBackground[2]->Paint();
}
// where to Start drawing
int x = DRAW_OFFSET_X + _pixelsIndent, y = GetYStart();
#if 0
m_nLangInset = 0;
int langlen = 0;
wchar_t shortcode[ 5 ];
shortcode[ 0 ] = L'\0';
if ( m_bAllowNonAsciiCharacters )
{
input()->GetIMELanguageShortCode( shortcode, sizeof( shortcode ) );
if ( shortcode[ 0 ] != L'\0' &&
wcsicmp( shortcode, L"EN" ) )
{
m_nLangInset = 0;
langlen = wcslen( shortcode );
for ( int i = 0; i < langlen; ++i )
{
m_nLangInset += getCharWidth( _smallfont, shortcode[ i ] );
}
m_nLangInset += 4;
if ( m_bDrawLanguageIDAtLeft )
{
x += m_nLangInset;
}
wide -= m_nLangInset;
}
}
#endif
HFont useFont = _font;
surface()->DrawSetTextFont(useFont);
if (IsEnabled())
{
col = GetFgColor();
}
else
{
col = _disabledFgColor;
}
surface()->DrawSetTextColor(col);
_pixelsIndent = 0;
int lineBreakIndexIndex = 0;
int startIndex = GetStartDrawIndex(lineBreakIndexIndex);
int remembery = y;
int oldEnd = m_TextStream.Count();
int oldCursorPos = _cursorPos;
int nCompStart = -1;
int nCompEnd = -1;
// FIXME: Should insert at cursor pos instead
bool composing = m_bAllowNonAsciiCharacters && wcslen( m_szComposition ) > 0;
bool invertcomposition = input()->GetShouldInvertCompositionString();
if ( composing )
{
nCompStart = _cursorPos;
wchar_t *s = m_szComposition;
while ( *s != L'\0' )
{
m_TextStream.InsertBefore( _cursorPos, *s );
++s;
++_cursorPos;
}
nCompEnd = _cursorPos;
}
bool highlight_composition = ( nCompStart != -1 && nCompEnd != -1 ) ? true : false;
// draw text with an elipsis
if ( (!_multiline) && (!_horizScrollingAllowed) )
{
int endIndex = m_TextStream.Count();
// In editable windows only do the ellipsis if we don't have focus.
// In non editable windows do it all the time.
if ( (!HasFocus() && (IsEditable())) || (!IsEditable()) )
{
int i = -1;
// loop through all the characters and sum their widths
bool addEllipses = NeedsEllipses( useFont, &i );
if ( addEllipses &&
!IsEditable() &&
m_bUseFallbackFont &&
INVALID_FONT != m_hFallbackFont )
{
// Switch to small font!!!
useFont = m_hFallbackFont;
surface()->DrawSetTextFont(useFont);
addEllipses = NeedsEllipses( useFont, &i );
}
if (addEllipses)
{
int elipsisWidth = 3 * getCharWidth(useFont, '.');
while (elipsisWidth > 0 && i >= 0)
{
elipsisWidth -= getCharWidth(useFont, m_TextStream[i]);
i--;
}
endIndex = i + 1;
}
// if we take off less than the last 3 chars we have to make sure
// we take off the last 3 chars so selected text will look right.
if (m_TextStream.Count() - endIndex < 3 && m_TextStream.Count() - endIndex > 0 )
{
endIndex = m_TextStream.Count() - 3;
}
}
// draw the text
int i;
for (i = startIndex; i < endIndex; i++)
{
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
bool iscompositionchar = false;
if ( highlight_composition )
{
iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false;
if ( iscompositionchar )
{
// Set the underline color to the text color
surface()->DrawSetColor( col );
int w = getCharWidth( useFont, ch );
if ( invertcomposition )
{
// Invert color
surface()->DrawSetTextColor( saveBgColor );
surface()->DrawSetColor( col );
surface()->DrawFilledRect(x, 0, x+w, tall);
// Set the underline color to the text color
surface()->DrawSetColor( saveBgColor );
}
surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 );
}
}
// draw the character and update xposition
x += DrawChar(ch, useFont, i, x, y);
// Restore color
surface()->DrawSetTextColor(col);
}
if (endIndex < m_TextStream.Count()) // add an elipsis
{
x += DrawChar('.', useFont, i, x, y);
i++;
x += DrawChar('.', useFont, i, x, y);
i++;
x += DrawChar('.', useFont, i, x, y);
i++;
}
}
else
{
// draw the text
for ( int i = startIndex; i < m_TextStream.Count(); i++)
{
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
// if we've passed a line break go to that
if ( _multiline && m_LineBreaks[lineBreakIndexIndex] == i)
{
// add another line
AddAnotherLine(x, y);
lineBreakIndexIndex++;
}
bool iscompositionchar = false;
if ( highlight_composition )
{
iscompositionchar = ( i >= nCompStart && i < nCompEnd ) ? true : false;
if ( iscompositionchar )
{
// Set the underline color to the text color
surface()->DrawSetColor( col );
int w = getCharWidth( useFont, ch );
if ( invertcomposition )
{
// Invert color
surface()->DrawSetTextColor( saveBgColor );
surface()->DrawFilledRect(x, 0, x+w, tall);
// Set the underline color to the text color
surface()->DrawSetColor( saveBgColor );
}
surface()->DrawFilledRect( x, tall - 2, x + w, tall - 1 );
}
}
// draw the character and update xposition
x += DrawChar(ch, useFont, i, x, y);
// Restore color
surface()->DrawSetTextColor(col);
}
}
// custom border
//!! need to replace this with scheme stuff (TextEntryBorder/TextEntrySelectedBorder)
surface()->DrawSetColor(50, 50, 50, 255);
if (IsEnabled() && IsEditable() && HasFocus())
{
// set a more distinct border color
surface()->DrawSetColor(0, 0, 0, 255);
DrawCursor (x, y);
if ( composing )
{
LocalToScreen( x, y );
input()->SetCandidateWindowPos( x, y );
}
}
int newEnd = m_TextStream.Count();
int remove = newEnd - oldEnd;
if ( remove > 0 )
{
m_TextStream.RemoveMultiple( oldCursorPos, remove );
}
_cursorPos = oldCursorPos;
#if 0
if ( HasFocus() && m_bAllowNonAsciiCharacters && langlen > 0 )
{
wide += m_nLangInset;
if ( m_bDrawLanguageIDAtLeft )
{
x = 0;
}
else
{
// Draw language identififer
x = wide - m_nLangInset;
}
surface()->DrawSetColor( col );
surface()->DrawFilledRect( x, 2, x + m_nLangInset-2, tall - 2 );
saveBgColor[ 3 ] = 255;
surface()->DrawSetTextColor( saveBgColor );
x += 1;
surface()->DrawSetTextFont(_smallfont);
for ( int i = 0; i < langlen; ++i )
{
x += DrawChar( shortcode[ i ], _smallfont, i, x, remembery );
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called when data changes or panel size changes
//-----------------------------------------------------------------------------
void TextEntry::PerformLayout()
{
BaseClass::PerformLayout();
RecalculateLineBreaks();
// recalculate scrollbar position
if (_verticalScrollbar)
{
LayoutVerticalScrollBarSlider();
}
// force a Repaint
Repaint();
}
// moves x,y to the Start of the next line of text
void TextEntry::AddAnotherLine(int &cx, int &cy)
{
cx = DRAW_OFFSET_X + _pixelsIndent;
cy += (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
}
//-----------------------------------------------------------------------------
// Purpose: Recalculates line breaks
//-----------------------------------------------------------------------------
void TextEntry::RecalculateLineBreaks()
{
if (!_multiline || _hideText)
return;
if (m_TextStream.Count() < 1)
return;
HFont font = _font;
// line break to our width -2 pixel to keep cursor blinking in window
// (assumes borders are 1 pixel)
int wide = GetWide()-2;
// subtract the scrollbar width
if (_vertScrollBar)
{
wide -= _vertScrollBar->GetWide();
}
int charWidth;
int x = DRAW_OFFSET_X, y = DRAW_OFFSET_Y;
int wordStartIndex = 0;
int wordLength = 0;
bool hasWord = false;
bool justStartedNewLine = true;
bool wordStartedOnNewLine = true;
int startChar;
if (_recalculateBreaksIndex <= 0)
{
m_LineBreaks.RemoveAll();
startChar=0;
}
else
{
// remove the rest of the linebreaks list since its out of date.
for (int i=_recalculateBreaksIndex+1; i < m_LineBreaks.Count(); ++i)
{
m_LineBreaks.Remove((int)i);
--i; // removing shrinks the list!
}
startChar = m_LineBreaks[_recalculateBreaksIndex];
}
// handle the case where this char is a new line, in that case
// we have already taken its break index into account above so skip it.
if (m_TextStream[startChar] == '\r' || m_TextStream[startChar] == '\n')
{
startChar++;
}
// loop through all the characters
int i;
for (i = startChar; i < m_TextStream.Count(); ++i)
{
wchar_t ch = m_TextStream[i];
// line break only on whitespace characters
if (!iswspace(ch))
{
if (hasWord)
{
// append to the current word
}
else
{
// Start a new word
wordStartIndex = i;
hasWord = true;
wordStartedOnNewLine = justStartedNewLine;
wordLength = 0;
}
}
else
{
// whitespace/punctuation character
// end the word
hasWord = false;
}
// get the width
charWidth = getCharWidth(font, ch);
if (!iswcntrl(ch))
{
justStartedNewLine = false;
}
// check to see if the word is past the end of the line [wordStartIndex, i)
if ((x + charWidth) >= wide || ch == '\r' || ch == '\n')
{
// add another line
AddAnotherLine(x,y);
justStartedNewLine = true;
hasWord = false;
if (ch == '\r' || ch == '\n')
{
// set the break at the current character
m_LineBreaks.AddToTail(i);
}
else if (wordStartedOnNewLine)
{
// word is longer than a line, so set the break at the current cursor
m_LineBreaks.AddToTail(i);
}
else
{
// set it at the last word Start
m_LineBreaks.AddToTail(wordStartIndex);
// just back to reparse the next line of text
i = wordStartIndex;
}
// reset word length
wordLength = 0;
}
// add to the size
x += charWidth;
wordLength += charWidth;
}
_charCount = i-1;
// end the list
m_LineBreaks.AddToTail(BUFFER_SIZE);
// set up the scrollbar
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Recalculate where the vertical scroll bar slider should be
// based on the current cursor line we are on.
//-----------------------------------------------------------------------------
void TextEntry::LayoutVerticalScrollBarSlider()
{
// set up the scrollbar
if (_vertScrollBar)
{
int wide, tall;
GetSize (wide, tall);
// make sure we factor in insets
int ileft, iright, itop, ibottom;
GetInset(ileft, iright, itop, ibottom);
// with a scroll bar we take off the inset
wide -= iright;
_vertScrollBar->SetPos(wide - _vertScrollBar->GetWide(), 0);
// scrollbar is inside the borders.
_vertScrollBar->SetSize(_vertScrollBar->GetWide(), tall - ibottom - itop);
// calculate how many lines we can fully display
int displayLines = tall / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
int numLines = m_LineBreaks.Count();
if (numLines <= displayLines)
{
// disable the scrollbar
_vertScrollBar->SetEnabled(false);
_vertScrollBar->SetRange(0, numLines);
_vertScrollBar->SetRangeWindow(numLines);
_vertScrollBar->SetValue(0);
}
else
{
// set the scrollbars range
_vertScrollBar->SetRange(0, numLines);
_vertScrollBar->SetRangeWindow(displayLines);
_vertScrollBar->SetEnabled(true);
// this should make it scroll one line at a time
_vertScrollBar->SetButtonPressedScrollValue(1);
// set the value to view the last entries
int val = _vertScrollBar->GetValue();
int maxval = _vertScrollBar->GetValue() + displayLines;
if (GetCursorLine() < val )
{
while (GetCursorLine() < val)
{
val--;
}
}
else if (GetCursorLine() >= maxval)
{
while (GetCursorLine() >= maxval)
{
maxval++;
}
maxval -= displayLines;
val = maxval;
}
else
{
//val = GetCursorLine();
}
_vertScrollBar->SetValue(val);
_vertScrollBar->InvalidateLayout();
_vertScrollBar->Repaint();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Set boolean value of baseclass variables.
//-----------------------------------------------------------------------------
void TextEntry::SetEnabled(bool state)
{
BaseClass::SetEnabled(state);
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Sets whether text wraps around multiple lines or not
// Input : state - true or false
//-----------------------------------------------------------------------------
void TextEntry::SetMultiline(bool state)
{
_multiline = state;
}
bool TextEntry::IsMultiline()
{
return _multiline;
}
//-----------------------------------------------------------------------------
// Purpose: sets whether or not the edit catches and stores ENTER key presses
//-----------------------------------------------------------------------------
void TextEntry::SetCatchEnterKey(bool state)
{
_catchEnterKey = state;
}
//-----------------------------------------------------------------------------
// Purpose: Sets whether a vertical scrollbar is visible
// Input : state - true or false
//-----------------------------------------------------------------------------
void TextEntry::SetVerticalScrollbar(bool state)
{
_verticalScrollbar = state;
if (_verticalScrollbar)
{
if (!_vertScrollBar)
{
_vertScrollBar = new ScrollBar(this, "ScrollBar", true);
_vertScrollBar->AddActionSignalTarget(this);
}
_vertScrollBar->SetVisible(true);
}
else if (_vertScrollBar)
{
_vertScrollBar->SetVisible(false);
}
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: sets _editable flag
// Input : state - true or false
//-----------------------------------------------------------------------------
void TextEntry::SetEditable(bool state)
{
if ( state )
{
SetDropEnabled( true, 1.0f );
}
else
{
SetDropEnabled( false );
}
_editable = state;
}
const wchar_t *UnlocalizeUnicode( wchar_t *unicode )
{
if ( !unicode )
return L"";
if ( *unicode == L'#' )
{
char lookup[ 512 ];
localize()->ConvertUnicodeToANSI( unicode + 1, lookup, sizeof( lookup ) );
return localize()->Find( lookup );
}
return unicode;
}
Menu * TextEntry::GetEditMenu()
{
return m_pEditMenu;
}
//-----------------------------------------------------------------------------
// Purpose: Create cut/copy/paste dropdown menu
//-----------------------------------------------------------------------------
void TextEntry::CreateEditMenu()
{
// create a drop down cut/copy/paste menu appropriate for this object's states
if (m_pEditMenu)
delete m_pEditMenu;
m_pEditMenu = new Menu(this, "EditMenu");
// add cut/copy/paste drop down options if its editable, just copy if it is not
if (_editable && !_hideText)
{
m_pEditMenu->AddMenuItem("#TextEntry_Cut", new KeyValues("DoCutSelected"), this);
}
if ( !_hideText )
{
m_pEditMenu->AddMenuItem("#TextEntry_Copy", new KeyValues("DoCopySelected"), this);
}
if (_editable)
{
m_pEditMenu->AddMenuItem("#TextEntry_Paste", new KeyValues("DoPaste"), this);
}
if ( m_bAllowNonAsciiCharacters )
{
IInput::LanguageItem *langs = NULL;
int count = input()->GetIMELanguageList( NULL, 0 );
if ( count > 0 )
{
langs = new IInput::LanguageItem[ count ];
input()->GetIMELanguageList( langs, count );
// Create a submenu
Menu *subMenu = new Menu( this, "LanguageMenu" );
for ( int i = 0; i < count; ++i )
{
int id = subMenu->AddCheckableMenuItem( "Language", UnlocalizeUnicode( langs[ i ].menuname ), new KeyValues( "DoLanguageChanged", "handle", langs[ i ].handleValue ), this );
if ( langs[ i ].active )
{
subMenu->SetMenuItemChecked( id, true );
}
}
m_pEditMenu->AddCascadingMenuItem( "Language", "#TextEntry_Language", "", this, subMenu );
delete[] langs;
}
IInput::ConversionModeItem *modes = NULL;
count = input()->GetIMEConversionModes( NULL, 0 );
// if count == 0 then native mode is the only mode...
if ( count > 0 )
{
modes = new IInput::ConversionModeItem[ count ];
input()->GetIMEConversionModes( modes, count );
// Create a submenu
Menu *subMenu = new Menu( this, "ConversionModeMenu" );
for ( int i = 0; i < count; ++i )
{
int id = subMenu->AddCheckableMenuItem( "ConversionMode", UnlocalizeUnicode( modes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this );
if ( modes[ i ].active )
{
subMenu->SetMenuItemChecked( id, true );
}
}
m_pEditMenu->AddCascadingMenuItem( "ConversionMode", "#TextEntry_ConversionMode", "", this, subMenu );
delete[] modes;
}
IInput::SentenceModeItem *sentencemodes = NULL;
count = input()->GetIMESentenceModes( NULL, 0 );
// if count == 0 then native mode is the only mode...
if ( count > 0 )
{
sentencemodes = new IInput::SentenceModeItem[ count ];
input()->GetIMESentenceModes( sentencemodes, count );
// Create a submenu
Menu *subMenu = new Menu( this, "SentenceModeMenu" );
for ( int i = 0; i < count; ++i )
{
int id = subMenu->AddCheckableMenuItem( "SentenceMode", UnlocalizeUnicode( sentencemodes[ i ].menuname ), new KeyValues( "DoConversionModeChanged", "handle", modes[ i ].handleValue ), this );
if ( modes[ i ].active )
{
subMenu->SetMenuItemChecked( id, true );
}
}
m_pEditMenu->AddCascadingMenuItem( "SentenceMode", "#TextEntry_SentenceMode", "", this, subMenu );
delete[] sentencemodes;
}
}
m_pEditMenu->SetVisible(false);
m_pEditMenu->SetParent(this);
m_pEditMenu->AddActionSignalTarget(this);
}
//-----------------------------------------------------------------------------
// Purpsoe: Returns state of _editable flag
//-----------------------------------------------------------------------------
bool TextEntry::IsEditable()
{
return _editable && IsEnabled();
}
//-----------------------------------------------------------------------------
// Purpose: We want single line windows to scroll horizontally and select text
// in response to clicking and holding outside window
//-----------------------------------------------------------------------------
void TextEntry::OnMouseFocusTicked()
{
// if a button is down move the scrollbar slider the appropriate direction
if (_mouseDragSelection) // text is being selected via mouse clicking and dragging
{
OnCursorMoved(0,0); // we want the text to scroll as if we were dragging
}
}
//-----------------------------------------------------------------------------
// Purpose: If a cursor enters the window, we are not elegible for
// MouseFocusTicked events
//-----------------------------------------------------------------------------
void TextEntry::OnCursorEntered()
{
_mouseDragSelection = false; // outside of window dont recieve drag scrolling ticks
}
//-----------------------------------------------------------------------------
// Purpose: When the cursor is outside the window, if we are holding the mouse
// button down, then we want the window to scroll the text one char at a time
// using Ticks
//-----------------------------------------------------------------------------
void TextEntry::OnCursorExited() // outside of window recieve drag scrolling ticks
{
if (_mouseSelection)
_mouseDragSelection = true;
}
//-----------------------------------------------------------------------------
// Purpose: Handle selection of text by mouse
//-----------------------------------------------------------------------------
void TextEntry::OnCursorMoved(int x, int y)
{
if (_mouseSelection)
{
// update the cursor position
int x, y;
input()->GetCursorPos(x, y);
ScreenToLocal(x, y);
_cursorPos = PixelToCursorSpace(x, y);
// if we are at Start of buffer don't put cursor at end, this will keep
// window from scrolling up to a blank line
if (_cursorPos == 0)
_putCursorAtEnd = false;
// scroll if we went off left side
if (_cursorPos == _currentStartIndex)
{
if (_cursorPos > 0)
_cursorPos--;
ScrollLeft();
_cursorPos = _currentStartIndex;
}
if ( _cursorPos != _select[1])
{
_select[1] = _cursorPos;
Repaint();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle Mouse button down events.
//-----------------------------------------------------------------------------
void TextEntry::OnMousePressed(MouseCode code)
{
if (code == MOUSE_LEFT)
{
bool keepChecking = SelectCheck( true );
if ( !keepChecking )
{
BaseClass::OnMousePressed( code );
return;
}
// move the cursor to where the mouse was pressed
int x, y;
input()->GetCursorPos(x, y);
ScreenToLocal(x, y);
_cursorIsAtEnd = _putCursorAtEnd; // save this off before calling PixelToCursorSpace()
_cursorPos = PixelToCursorSpace(x, y);
// if we are at Start of buffer don't put cursor at end, this will keep
// window from scrolling up to a blank line
if (_cursorPos == 0)
_putCursorAtEnd = false;
// enter selection mode
input()->SetMouseCapture(GetVPanel());
_mouseSelection = true;
if (_select[0] < 0)
{
// if no initial selection position, Start selection position at cursor
_select[0] = _cursorPos;
}
_select[1] = _cursorPos;
ResetCursorBlink();
RequestFocus();
Repaint();
}
else if (code == MOUSE_RIGHT) // check for context menu open
{
CreateEditMenu();
Assert(m_pEditMenu);
OpenEditMenu();
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle mouse button up events
//-----------------------------------------------------------------------------
void TextEntry::OnMouseReleased(MouseCode code)
{
_mouseSelection = false;
input()->SetMouseCapture(NULL);
// make sure something has been selected
int cx0, cx1;
if (GetSelectedRange(cx0, cx1))
{
if (cx1 - cx0 == 0)
{
// nullify selection
_select[0] = -1;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : code -
//-----------------------------------------------------------------------------
void TextEntry::OnMouseTriplePressed( MouseCode code )
{
BaseClass::OnMouseTriplePressed( code );
// left triple clicking on a word selects all
if (code == MOUSE_LEFT)
{
GotoTextEnd();
SelectAllText( false );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle mouse double clicks
//-----------------------------------------------------------------------------
void TextEntry::OnMouseDoublePressed(MouseCode code)
{
// left double clicking on a word selects the word
if (code == MOUSE_LEFT)
{
// move the cursor just as if you single clicked.
OnMousePressed(code);
// then find the start and end of the word we are in to highlight it.
int selectSpot[2];
GotoWordLeft();
selectSpot[0] = _cursorPos;
GotoWordRight();
selectSpot[1] = _cursorPos;
if (_cursorPos > 0)
{
if (iswspace(m_TextStream[_cursorPos - 1]))
{
selectSpot[1]--;
_cursorPos--;
}
_select[0] = selectSpot[0];
_select[1] = selectSpot[1];
_mouseSelection = true;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Turn off text selection code when mouse button is not down
//-----------------------------------------------------------------------------
void TextEntry::OnMouseCaptureLost()
{
_mouseSelection = false;
}
//-----------------------------------------------------------------------------
// Purpose: Masks which keys get chained up
// Maps keyboard input to text window functions.
//-----------------------------------------------------------------------------
void TextEntry::OnKeyCodeTyped(KeyCode code)
{
_cursorIsAtEnd = _putCursorAtEnd;
_putCursorAtEnd = false;
bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
bool fallThrough = false;
if (ctrl)
{
switch(code)
{
case KEY_A:
SelectAllText(false);
// move the cursor to the end
_cursorPos = _select[1];
break;
case KEY_INSERT:
case KEY_C:
{
CopySelected();
break;
}
case KEY_V:
{
DeleteSelected();
Paste();
break;
}
case KEY_X:
{
CopySelected();
DeleteSelected();
break;
}
case KEY_Z:
{
Undo();
break;
}
case KEY_RIGHT:
{
GotoWordRight();
break;
}
case KEY_LEFT:
{
GotoWordLeft();
break;
}
case KEY_ENTER:
{
// insert a newline
if (_multiline)
{
DeleteSelected();
SaveUndoState();
InsertChar('\n');
}
// fire newlines back to the main target if asked to
if(_sendNewLines)
{
PostActionSignal(new KeyValues("TextNewLine"));
}
break;
}
case KEY_HOME:
{
GotoTextStart();
break;
}
case KEY_END:
{
GotoTextEnd();
break;
}
case KEY_PAGEUP:
{
OnChangeIME( true );
}
break;
case KEY_PAGEDOWN:
{
OnChangeIME( false );
}
break;
case KEY_UP:
case KEY_DOWN:
if ( m_bAllowNonAsciiCharacters )
{
FlipToLastIME();
}
else
{
fallThrough = true;
}
break;
default:
{
fallThrough = true;
break;
}
}
}
else if (alt)
{
// do nothing with ALT-x keys
if ( !m_bAllowNonAsciiCharacters || ( code != KEY_BACKQUOTE ) )
{
fallThrough = true;
}
}
else
{
switch(code)
{
case KEY_TAB:
case KEY_LSHIFT:
case KEY_RSHIFT:
case KEY_ESCAPE:
{
fallThrough = true;
break;
}
case KEY_INSERT:
{
if (shift)
{
DeleteSelected();
Paste();
}
else
{
fallThrough = true;
}
break;
}
case KEY_DELETE:
{
if (shift)
{
// shift-delete is cut
CopySelected();
DeleteSelected();
}
else
{
Delete();
}
break;
}
case KEY_LEFT:
{
GotoLeft();
break;
}
case KEY_RIGHT:
{
GotoRight();
break;
}
case KEY_UP:
{
if (_multiline)
{
GotoUp();
}
else
{
fallThrough = true;
}
break;
}
case KEY_DOWN:
{
if (_multiline)
{
GotoDown();
}
else
{
fallThrough = true;
}
break;
}
case KEY_HOME:
{
if (_multiline)
{
GotoFirstOfLine();
}
else
{
GotoTextStart();
}
break;
}
case KEY_END:
{
GotoEndOfLine();
break;
}
case KEY_BACKSPACE:
{
int x0, x1;
if (GetSelectedRange(x0, x1))
{
// act just like delete if there is a selection
DeleteSelected();
}
else
{
Backspace();
}
break;
}
case KEY_ENTER:
{
// insert a newline
if (_multiline && _catchEnterKey)
{
DeleteSelected();
SaveUndoState();
InsertChar('\n');
}
else
{
fallThrough = true;
}
// fire newlines back to the main target if asked to
if(_sendNewLines)
{
PostActionSignal(new KeyValues("TextNewLine"));
}
break;
}
case KEY_PAGEUP:
{
int val = 0;
fallThrough = (!_multiline) && (!_vertScrollBar);
if (_vertScrollBar)
{
val = _vertScrollBar->GetValue();
}
// if there is a scroll bar scroll down one rangewindow
if (_multiline)
{
int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
// move the cursor down
for (int i=0; i < displayLines; i++)
{
GotoUp();
}
}
// if there is a scroll bar scroll down one rangewindow
if (_vertScrollBar)
{
int window = _vertScrollBar->GetRangeWindow();
int newval = _vertScrollBar->GetValue();
int linesToMove = window - (val - newval);
_vertScrollBar->SetValue(val - linesToMove - 1);
}
break;
}
case KEY_PAGEDOWN:
{
int val = 0;
fallThrough = (!_multiline) && (!_vertScrollBar);
if (_vertScrollBar)
{
val = _vertScrollBar->GetValue();
}
if (_multiline)
{
int displayLines = GetTall() / (surface()->GetFontTall(_font) + DRAW_OFFSET_Y);
// move the cursor down
for (int i=0; i < displayLines; i++)
{
GotoDown();
}
}
// if there is a scroll bar scroll down one rangewindow
if (_vertScrollBar)
{
int window = _vertScrollBar->GetRangeWindow();
int newval = _vertScrollBar->GetValue();
int linesToMove = window - (newval - val);
_vertScrollBar->SetValue(val + linesToMove + 1);
}
break;
}
case KEY_F1:
case KEY_F2:
case KEY_F3:
case KEY_F4:
case KEY_F5:
case KEY_F6:
case KEY_F7:
case KEY_F8:
case KEY_F9:
case KEY_F10:
case KEY_F11:
case KEY_F12:
{
fallThrough = true;
break;
}
default:
{
// return if any other char is pressed.
// as it will be a unicode char.
// and we don't want select[1] changed unless a char was pressed that this fxn handles
return;
}
}
}
// select[1] is the location in the line where the blinking cursor started
_select[1] = _cursorPos;
if (_dataChanged)
{
FireActionSignal();
}
// chain back on some keys
if (fallThrough)
{
_putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs
BaseClass::OnKeyCodeTyped(code);
}
}
//-----------------------------------------------------------------------------
// Purpose: Masks which keys get chained up
// Maps keyboard input to text window functions.
//-----------------------------------------------------------------------------
void TextEntry::OnKeyTyped(wchar_t unichar)
{
_cursorIsAtEnd = _putCursorAtEnd;
_putCursorAtEnd=false;
bool fallThrough = false;
// KeyCodes handle all non printable chars
if (iswcntrl(unichar) || unichar == 9 ) // tab key (code 9) is printable but handled elsewhere
return;
// do readonly keys
if (!IsEditable())
{
BaseClass::OnKeyTyped(unichar);
return;
}
if (unichar != 0)
{
DeleteSelected();
SaveUndoState();
InsertChar(unichar);
}
// select[1] is the location in the line where the blinking cursor started
_select[1] = _cursorPos;
if (_dataChanged)
{
FireActionSignal();
}
// chain back on some keys
if (fallThrough)
{
_putCursorAtEnd=_cursorIsAtEnd; // keep state of cursor on fallthroughs
BaseClass::OnKeyTyped(unichar);
}
}
//-----------------------------------------------------------------------------
// Purpose: Scrolls the list according to the mouse wheel movement
//-----------------------------------------------------------------------------
void TextEntry::OnMouseWheeled(int delta)
{
if (_vertScrollBar)
{
MoveScrollBar(delta);
}
else
{
// if we don't use the input, chain back
BaseClass::OnMouseWheeled(delta);
}
}
//-----------------------------------------------------------------------------
// Purpose: Scrolls the list
// Input : delta - amount to move scrollbar up
//-----------------------------------------------------------------------------
void TextEntry::MoveScrollBar(int delta)
{
if (_vertScrollBar)
{
int val = _vertScrollBar->GetValue();
val -= (delta * 3);
_vertScrollBar->SetValue(val);
}
}
//-----------------------------------------------------------------------------
// Purpose: Called every frame the entry has keyboard focus;
// blinks the text cursor
//-----------------------------------------------------------------------------
void TextEntry::OnKeyFocusTicked()
{
int time=system()->GetTimeMillis();
if(time>_cursorNextBlinkTime)
{
_cursorBlink=!_cursorBlink;
_cursorNextBlinkTime=time+_cursorBlinkRate;
Repaint();
}
}
Panel *TextEntry::GetDragPanel()
{
if ( input()->IsMouseDown( MOUSE_LEFT ) )
{
int x, y;
input()->GetCursorPos(x, y);
ScreenToLocal(x, y);
int cursor = PixelToCursorSpace(x, y);
int cx0, cx1;
bool check = GetSelectedRange( cx0, cx1 );
if ( check && cursor >= cx0 && cursor < cx1 )
{
// Don't deselect in this case!!!
return BaseClass::GetDragPanel();
}
return NULL;
}
return BaseClass::GetDragPanel();
}
void TextEntry::OnCreateDragData( KeyValues *msg )
{
BaseClass::OnCreateDragData( msg );
char txt[ 256 ];
GetText( txt, sizeof( txt ) );
int r0, r1;
if ( GetSelectedRange( r0, r1 ) && r0 != r1 )
{
int len = r1 - r0;
if ( len > 0 && r0 < 1024 )
{
char selection[ 512 ];
Q_strncpy( selection, &txt[ r0 ], len + 1 );
selection[ len ] = 0;
msg->SetString( "text", selection );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Check if we are selecting text (so we can highlight it)
//-----------------------------------------------------------------------------
bool TextEntry::SelectCheck( bool fromMouse /*=false*/ )
{
bool bret = true;
if (!HasFocus() || !(input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)))
{
bool deselect = true;
int cx0, cx1;
if ( fromMouse &&
GetDragPanel() != NULL )
{
// move the cursor to where the mouse was pressed
int x, y;
input()->GetCursorPos(x, y);
ScreenToLocal(x, y);
int cursor = PixelToCursorSpace(x, y);
bool check = GetSelectedRange( cx0, cx1 );
if ( check && cursor >= cx0 && cursor < cx1 )
{
// Don't deselect in this case!!!
deselect = false;
bret = false;
}
}
if ( deselect )
{
_select[0] = -1;
}
}
else if (_select[0] == -1)
{
_select[0] = _cursorPos;
}
return bret;
}
//-----------------------------------------------------------------------------
// Purpose: set the maximum number of chars in the text buffer
//-----------------------------------------------------------------------------
void TextEntry::SetMaximumCharCount(int maxChars)
{
_maxCharCount = maxChars;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
int TextEntry::GetMaximumCharCount()
{
return _maxCharCount;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void TextEntry::SetAutoProgressOnHittingCharLimit(bool state)
{
m_bAutoProgressOnHittingCharLimit = state;
}
//-----------------------------------------------------------------------------
// Purpose: set whether to wrap the text buffer
//-----------------------------------------------------------------------------
void TextEntry::SetWrap(bool wrap)
{
_wrap = wrap;
}
//-----------------------------------------------------------------------------
// Purpose: set whether to pass newline msgs to parent
//-----------------------------------------------------------------------------
void TextEntry::SendNewLine(bool send)
{
_sendNewLines = send;
}
//-----------------------------------------------------------------------------
// Purpose: Tell if an index is a linebreakindex
//-----------------------------------------------------------------------------
bool TextEntry::IsLineBreak(int index)
{
for (int i=0; i<m_LineBreaks.Count(); ++i)
{
if (index == m_LineBreaks[i])
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor one character to the left, scroll the text
// horizontally if needed
//-----------------------------------------------------------------------------
void TextEntry::GotoLeft()
{
SelectCheck();
// if we are on a line break just move the cursor to the prev line
if (IsLineBreak(_cursorPos))
{
// if we're already on the prev line at the end dont put it on the end
if (!_cursorIsAtEnd)
_putCursorAtEnd = true;
}
// if we are not at Start decrement cursor
if (!_putCursorAtEnd && _cursorPos > 0)
{
_cursorPos--;
}
ScrollLeft();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor one character to the right, scroll the text
// horizontally if needed
//-----------------------------------------------------------------------------
void TextEntry::GotoRight()
{
SelectCheck();
// if we are on a line break just move the cursor to the next line
if (IsLineBreak(_cursorPos))
{
if (_cursorIsAtEnd)
{
_putCursorAtEnd = false;
}
else
{
// if we are not at end increment cursor
if (_cursorPos < m_TextStream.Count())
{
_cursorPos++;
}
}
}
else
{
// if we are not at end increment cursor
if (_cursorPos < m_TextStream.Count())
{
_cursorPos++;
}
// if we are on a line break move the cursor to end of line
if (IsLineBreak(_cursorPos))
{
if (!_cursorIsAtEnd)
_putCursorAtEnd = true;
}
}
// scroll right if we need to
ScrollRight();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Find out what line the cursor is on
//-----------------------------------------------------------------------------
int TextEntry::GetCursorLine()
{
// find which line the cursor is on
int cursorLine;
for (cursorLine = 0; cursorLine < m_LineBreaks.Count(); cursorLine++)
{
if (_cursorPos < m_LineBreaks[cursorLine])
break;
}
if (_putCursorAtEnd) // correct for when cursor is at end of line rather than Start of next
{
// we are not at end of buffer, in which case there is no next line to be at the Start of
if (_cursorPos != m_TextStream.Count() )
cursorLine--;
}
return cursorLine;
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor one line up
//-----------------------------------------------------------------------------
void TextEntry::GotoUp()
{
SelectCheck();
if (_cursorIsAtEnd)
{
if ( (GetCursorLine() - 1 ) == 0) // we are on first line
{
// stay at end of line
_putCursorAtEnd = true;
return; // dont move the cursor
}
else
_cursorPos--;
}
int cx, cy;
CursorToPixelSpace(_cursorPos, cx, cy);
// move the cursor to the previous line
MoveCursor(GetCursorLine() - 1, cx);
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor one line down
//-----------------------------------------------------------------------------
void TextEntry::GotoDown()
{
SelectCheck();
if (_cursorIsAtEnd)
{
_cursorPos--;
if (_cursorPos < 0)
_cursorPos = 0;
}
int cx, cy;
CursorToPixelSpace(_cursorPos, cx, cy);
// move the cursor to the next line
MoveCursor(GetCursorLine() + 1, cx);
if (!_putCursorAtEnd && _cursorIsAtEnd )
{
_cursorPos++;
if (_cursorPos > m_TextStream.Count())
{
_cursorPos = m_TextStream.Count();
}
}
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Set the starting ypixel positon for a walk through the window
//-----------------------------------------------------------------------------
int TextEntry::GetYStart()
{
if (_multiline)
{
// just Start from the top
return DRAW_OFFSET_Y;
}
int fontTall = surface()->GetFontTall(_font);
return (GetTall() / 2) - (fontTall / 2);
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor to a line, need to know how many pixels are in a line
//-----------------------------------------------------------------------------
void TextEntry::MoveCursor(int line, int pixelsAcross)
{
// clamp to a valid line
if (line < 0)
line = 0;
if (line >= m_LineBreaks.Count())
line = m_LineBreaks.Count() -1;
// walk the whole text set looking for our place
// work out where to Start checking
int yStart = GetYStart();
int x = DRAW_OFFSET_X, y = yStart;
int lineBreakIndexIndex = 0;
_pixelsIndent = 0;
int i;
for ( i = 0; i < m_TextStream.Count(); i++)
{
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
// if we've passed a line break go to that
if (m_LineBreaks[lineBreakIndexIndex] == i)
{
if (lineBreakIndexIndex == line)
{
_putCursorAtEnd = true;
_cursorPos = i;
break;
}
// add another line
AddAnotherLine(x,y);
lineBreakIndexIndex++;
}
// add to the current position
int charWidth = getCharWidth(_font, ch);
if (line == lineBreakIndexIndex)
{
// check to see if we're in range
if ((x + (charWidth / 2)) > pixelsAcross)
{
// found position
_cursorPos = i;
break;
}
}
x += charWidth;
}
// if we never find the cursor it must be past the end
// of the text buffer, to let's just slap it on the end of the text buffer then.
if (i == m_TextStream.Count())
{
GotoTextEnd();
}
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Turn horizontal scrolling on or off.
// Horizontal scrolling is disabled in multline windows.
// Toggling this will disable it in single line windows as well.
//-----------------------------------------------------------------------------
void TextEntry::SetHorizontalScrolling(bool status)
{
_horizScrollingAllowed = status;
}
//-----------------------------------------------------------------------------
// Purpose: Horizontal scrolling function, not used in multiline windows
// Function will scroll the buffer to the left if the cursor is not in the window
// scroll left if we need to
//-----------------------------------------------------------------------------
void TextEntry::ScrollLeft()
{
if (_multiline) // early out
{
return;
}
if (!_horizScrollingAllowed) //early out
{
return;
}
if(_cursorPos < _currentStartIndex) // scroll left if we need to
{
if (_cursorPos < 0)// dont scroll past the Start of buffer
{
_cursorPos=0;
}
_currentStartIndex = _cursorPos;
}
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::ScrollLeftForResize()
{
if (_multiline) // early out
{
return;
}
if (!_horizScrollingAllowed) //early out
{
return;
}
while (_currentStartIndex > 0) // go until we hit leftmost
{
_currentStartIndex--;
int nVal = _currentStartIndex;
// check if the cursor is now off the screen
if (IsCursorOffRightSideOfWindow(_cursorPos))
{
_currentStartIndex++; // we've gone too far, return it
break;
}
// IsCursorOffRightSideOfWindow actually fixes the _currentStartIndex,
// so if our value changed that menas we really are off the screen
if (nVal != _currentStartIndex)
break;
}
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Horizontal scrolling function, not used in multiline windows
// Scroll one char right until the cursor is visible in the window.
// We do this one char at a time because char width isn't a constant.
//-----------------------------------------------------------------------------
void TextEntry::ScrollRight()
{
if (!_horizScrollingAllowed)
return;
if (_multiline)
{
}
// check if cursor is off the right side of window
else if (IsCursorOffRightSideOfWindow(_cursorPos))
{
_currentStartIndex++; //scroll over
ScrollRight(); // scroll again, check if cursor is in window yet
}
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Check and see if cursor position is off the right side of the window
// just compare cursor's pixel coords with the window size coords.
// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you
// if current cursor is outside window.
// Output: true: cursor is outside right edge or window
// false: cursor is inside right edge
//-----------------------------------------------------------------------------
bool TextEntry::IsCursorOffRightSideOfWindow(int cursorPos)
{
int cx, cy;
CursorToPixelSpace(cursorPos, cx, cy);
int wx=GetWide()-1; //width of inside of window is GetWide()-1
if ( wx <= 0 )
return false;
return (cx >= wx);
}
//-----------------------------------------------------------------------------
// Purpose: Check and see if cursor position is off the left side of the window
// just compare cursor's pixel coords with the window size coords.
// Input: an integer cursor Position, if you pass _cursorPos fxn will tell you
// if current cursor is outside window.
// Output: true - cursor is outside left edge or window
// false - cursor is inside left edge
//-----------------------------------------------------------------------------
bool TextEntry::IsCursorOffLeftSideOfWindow(int cursorPos)
{
int cx, cy;
CursorToPixelSpace(cursorPos, cx, cy);
return (cx <= 0);
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor over to the Start of the next word to the right
//-----------------------------------------------------------------------------
void TextEntry::GotoWordRight()
{
SelectCheck();
// search right until we hit a whitespace character or a newline
while (++_cursorPos < m_TextStream.Count())
{
if (iswspace(m_TextStream[_cursorPos]))
break;
}
// search right until we hit an nonspace character
while (++_cursorPos < m_TextStream.Count())
{
if (!iswspace(m_TextStream[_cursorPos]))
break;
}
if (_cursorPos > m_TextStream.Count())
_cursorPos = m_TextStream.Count();
// now we are at the start of the next word
// scroll right if we need to
ScrollRight();
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Move the cursor over to the Start of the next word to the left
//-----------------------------------------------------------------------------
void TextEntry::GotoWordLeft()
{
SelectCheck();
if (_cursorPos < 1)
return;
// search left until we hit an nonspace character
while (--_cursorPos >= 0)
{
if (!iswspace(m_TextStream[_cursorPos]))
break;
}
// search left until we hit a whitespace character
while (--_cursorPos >= 0)
{
if (iswspace(m_TextStream[_cursorPos]))
{
break;
}
}
// we end one character off
_cursorPos++;
// now we are at the Start of the previous word
// scroll left if we need to
ScrollLeft();
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Move cursor to the Start of the text buffer
//-----------------------------------------------------------------------------
void TextEntry::GotoTextStart()
{
SelectCheck();
_cursorPos = 0; // set cursor to Start
_putCursorAtEnd = false;
_currentStartIndex=0; // scroll over to Start
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Move cursor to the end of the text buffer
//-----------------------------------------------------------------------------
void TextEntry::GotoTextEnd()
{
SelectCheck();
_cursorPos=m_TextStream.Count(); // set cursor to end of buffer
_putCursorAtEnd = true; // move cursor Start of next line
ScrollRight(); // scroll over until cursor is on screen
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Move cursor to the Start of the current line
//-----------------------------------------------------------------------------
void TextEntry::GotoFirstOfLine()
{
SelectCheck();
// to get to the Start of the line you have to take into account line wrap
// we have to figure out at which point the line wraps
// given the current cursor position, select[1], find the index that is the
// line Start to the left of the cursor
//_cursorPos = 0; //TODO: this is wrong, should go to first non-whitespace first, then to zero
_cursorPos = GetCurrentLineStart();
_putCursorAtEnd = false;
_currentStartIndex=_cursorPos;
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Get the index of the first char on the current line
//-----------------------------------------------------------------------------
int TextEntry::GetCurrentLineStart()
{
if (!_multiline) // quick out for non multline buffers
return _currentStartIndex;
int i;
if (IsLineBreak(_cursorPos))
{
for (i = 0; i < m_LineBreaks.Count(); ++i )
{
if (_cursorPos == m_LineBreaks[i])
break;
}
if (_cursorIsAtEnd)
{
if (i > 0)
{
return m_LineBreaks[i-1];
}
return m_LineBreaks[0];
}
else
return _cursorPos; // we are already at Start
}
for ( i = 0; i < m_LineBreaks.Count(); ++i )
{
if (_cursorPos < m_LineBreaks[i])
{
if (i == 0)
return 0;
else
return m_LineBreaks[i-1];
}
}
// if there were no line breaks, the first char in the line is the Start of the buffer
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Move cursor to the end of the current line
//-----------------------------------------------------------------------------
void TextEntry::GotoEndOfLine()
{
SelectCheck();
// to get to the end of the line you have to take into account line wrap in the buffer
// we have to figure out at which point the line wraps
// given the current cursor position, select[1], find the index that is the
// line end to the right of the cursor
//_cursorPos=m_TextStream.Count(); //TODO: this is wrong, should go to last non-whitespace, then to true EOL
_cursorPos = GetCurrentLineEnd();
_putCursorAtEnd = true;
ScrollRight();
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Get the index of the last char on the current line
//-----------------------------------------------------------------------------
int TextEntry::GetCurrentLineEnd()
{
int i;
if (IsLineBreak(_cursorPos) )
{
for ( i = 0; i < m_LineBreaks.Count()-1; ++i )
{
if (_cursorPos == m_LineBreaks[i])
break;
}
if (!_cursorIsAtEnd)
{
if (i == m_LineBreaks.Count()-2 )
m_TextStream.Count();
else
return m_LineBreaks[i+1];
}
else
return _cursorPos; // we are already at end
}
for ( i = 0; i < m_LineBreaks.Count()-1; i++ )
{
if ( _cursorPos < m_LineBreaks[i])
{
return m_LineBreaks[i];
}
}
return m_TextStream.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Insert a character into the text buffer
//-----------------------------------------------------------------------------
void TextEntry::InsertChar(wchar_t ch)
{
// throw away redundant linefeed characters
if (ch == '\r')
return;
// no newline characters in single-line dialogs
if (!_multiline && ch == '\n')
return;
// no tab characters
if (ch == '\t')
return;
if (m_bAllowNumericInputOnly)
{
if (!iswdigit(ch) && ((char)ch != '.'))
{
surface()->PlaySound("Resource\\warning.wav");
return;
}
}
// check against unicode characters
if (!m_bAllowNonAsciiCharacters)
{
if (ch > 127)
return;
}
// don't add characters if the max char count has been reached
// ding at the user
if (_maxCharCount > -1 && m_TextStream.Count() >= _maxCharCount)
{
if (_maxCharCount>0 && _multiline && _wrap)
{
// if we wrap lines rather than stopping
while (m_TextStream.Count() > _maxCharCount)
{
if (_recalculateBreaksIndex==0)
{
// we can get called before this has been run for the first time :)
RecalculateLineBreaks();
}
if (m_LineBreaks[0]> m_TextStream.Count())
{
// if the line break is the past the end of the buffer recalc
_recalculateBreaksIndex=-1;
RecalculateLineBreaks();
}
if (m_LineBreaks[0]+1 < m_TextStream.Count())
{
// delete the line
m_TextStream.RemoveMultiple(0, m_LineBreaks[0]);
// in case we just deleted text from where the cursor is
if (_cursorPos> m_TextStream.Count())
{
_cursorPos = m_TextStream.Count();
}
else
{ // shift the cursor up. don't let it wander past zero
_cursorPos-=m_LineBreaks[0]+1;
if (_cursorPos<0)
{
_cursorPos=0;
}
}
// move any selection area up
if(_select[0]>-1)
{
_select[0] -=m_LineBreaks[0]+1;
if(_select[0] <=0)
{
_select[0] =-1;
}
_select[1] -=m_LineBreaks[0]+1;
if(_select[1] <=0)
{
_select[1] =-1;
}
}
// now redraw the buffer
for (int i = m_TextStream.Count() - 1; i >= 0; i--)
{
SetCharAt(m_TextStream[i], i+1);
}
// redo all the line breaks
_recalculateBreaksIndex=-1;
RecalculateLineBreaks();
}
}
}
else
{
// make a sound
// we've hit the max character limit
surface()->PlaySound("Resource\\warning.wav");
return;
}
}
if (_wrap)
{
// when wrapping you always insert the new char at the end of the buffer
SetCharAt(ch, m_TextStream.Count());
_cursorPos=m_TextStream.Count();
}
else
{
// move chars right 1 starting from cursor, then replace cursorPos with char and increment cursor
for (int i = m_TextStream.Count()- 1; i >= _cursorPos; i--)
{
SetCharAt(m_TextStream[i], i+1);
}
SetCharAt(ch, _cursorPos);
_cursorPos++;
}
// if its a newline char we can't do the slider until we recalc the line breaks
if (ch == '\n')
{
RecalculateLineBreaks();
}
// see if we've hit the char limit
if (m_bAutoProgressOnHittingCharLimit && m_TextStream.Count() == _maxCharCount)
{
// move the next panel (most likely another TextEntry)
RequestFocusNext();
}
// scroll right if this pushed the cursor off screen
ScrollRight();
_dataChanged = true;
CalcBreakIndex();
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Get the lineBreakIndex index of the line before the cursor
// note _recalculateBreaksIndex < 0 flags RecalculateLineBreaks
// to figure it all out from scratch
//-----------------------------------------------------------------------------
void TextEntry::CalcBreakIndex()
{
// an optimization to handle when the cursor is at the end of the buffer.
// pays off if the buffer is large, and the search loop would be long.
if (_cursorPos == m_TextStream.Count())
{
// we know m_LineBreaks array always has at least one element in it (99999 sentinel)
// when there is just one line this will make recalc = -1 which is ok.
_recalculateBreaksIndex = m_LineBreaks.Count()-2;
return;
}
_recalculateBreaksIndex=0;
// find the line break just before the cursor position
while (_cursorPos > m_LineBreaks[_recalculateBreaksIndex])
++_recalculateBreaksIndex;
// -1 is ok.
--_recalculateBreaksIndex;
}
//-----------------------------------------------------------------------------
// Purpose: Insert a string into the text buffer, this is just a series
// of char inserts because we have to check each char is ok to insert
//-----------------------------------------------------------------------------
void TextEntry::InsertString(wchar_t *wszText)
{
SaveUndoState();
for (const wchar_t *ch = wszText; *ch != 0; ++ch)
{
InsertChar(*ch);
}
if (_dataChanged)
{
FireActionSignal();
}
}
//-----------------------------------------------------------------------------
// Purpose: Converts an ansi string to unicode and inserts it into the text stream
//-----------------------------------------------------------------------------
void TextEntry::InsertString(const char *text)
{
// check for to see if the string is in the localization tables
if (text[0] == '#')
{
wchar_t *wsz = localize()->Find(text);
if (wsz)
{
InsertString(wsz);
return;
}
}
// straight convert the ansi to unicode and insert
wchar_t unicode[1024];
localize()->ConvertANSIToUnicode(text, unicode, sizeof(unicode));
InsertString(unicode);
}
//-----------------------------------------------------------------------------
// Purpose: Handle the effect of user hitting backspace key
// we delete the char before the cursor and reformat the text so it
// behaves like in windows.
//-----------------------------------------------------------------------------
void TextEntry::Backspace()
{
if (!IsEditable())
return;
//if you are at the first position don't do anything
if(_cursorPos==0)
{
return;
}
//if the line is empty, don't do anything
if(m_TextStream.Count()==0)
{
return;
}
SaveUndoState();
//shift chars left one, starting at the cursor position, then make the line one smaller
for(int i=_cursorPos;i<m_TextStream.Count(); ++i)
{
SetCharAt(m_TextStream[i],i-1);
}
m_TextStream.Remove(m_TextStream.Count() - 1);
// As we hit the Start of the window, expose more chars so we can see what we are deleting
if (_cursorPos==_currentStartIndex)
{
// windows tabs over 6 chars
if (_currentStartIndex-6 >= 0) // dont scroll if there are not enough chars to scroll
{
_currentStartIndex-=6;
}
else
_currentStartIndex=0;
}
//move the cursor left one
_cursorPos--;
_dataChanged = true;
// recalculate linebreaks (the fast incremental linebreak function doesn't work in this case)
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
LayoutVerticalScrollBarSlider();
ResetCursorBlink();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Deletes the current selection, if any, moving the cursor to the Start
// of the selection
//-----------------------------------------------------------------------------
void TextEntry::DeleteSelected()
{
if (!IsEditable())
return;
// if the line is empty, don't do anything
if (m_TextStream.Count() == 0)
return;
// get the range to delete
int x0, x1;
if (!GetSelectedRange(x0, x1))
{
// no selection, don't touch anything
return;
}
SaveUndoState();
// shift chars left one starting after cursor position, then make the line one smaller
int dif = x1 - x0;
for (int i = 0; i < dif; ++i)
{
m_TextStream.Remove(x0);
}
// clear any selection
SelectNone();
ResetCursorBlink();
// move the cursor to just after the deleted section
_cursorPos = x0;
_dataChanged = true;
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
CalcBreakIndex();
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Handle the effect of the user hitting the delete key
// removes the char in front of the cursor
//-----------------------------------------------------------------------------
void TextEntry::Delete()
{
if (!IsEditable())
return;
// if the line is empty, don't do anything
if (m_TextStream.Count() == 0)
return;
// get the range to delete
int x0, x1;
if (!GetSelectedRange(x0, x1))
{
// no selection, so just delete the one character
x0 = _cursorPos;
x1 = x0 + 1;
// if we're at the end of the line don't do anything
if (_cursorPos >= m_TextStream.Count())
return;
}
SaveUndoState();
// shift chars left one starting after cursor position, then make the line one smaller
int dif = x1 - x0;
for (int i = 0; i < dif; i++)
{
m_TextStream.Remove((int)x0);
}
ResetCursorBlink();
// clear any selection
SelectNone();
// move the cursor to just after the deleted section
_cursorPos = x0;
_dataChanged = true;
_recalculateBreaksIndex = 0;
m_LineBreaks.RemoveAll();
m_LineBreaks.AddToTail(BUFFER_SIZE);
CalcBreakIndex();
LayoutVerticalScrollBarSlider();
}
//-----------------------------------------------------------------------------
// Purpose: Declare a selection empty
//-----------------------------------------------------------------------------
void TextEntry::SelectNone()
{
// tag the selection as empty
_select[0] = -1;
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Load in the selection range so cx0 is the Start and cx1 is the end
// from smallest to highest (right to left)
//-----------------------------------------------------------------------------
bool TextEntry::GetSelectedRange(int& cx0,int& cx1)
{
// if there is nothing selected return false
if (_select[0] == -1)
{
return false;
}
// sort the two position so cx0 is the smallest
cx0=_select[0];
cx1=_select[1];
int temp;
if(cx1<cx0){temp=cx0;cx0=cx1;cx1=temp;}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Opens the cut/copy/paste dropdown menu
//-----------------------------------------------------------------------------
void TextEntry::OpenEditMenu()
{
// get cursor position, this is local to this text edit window
int cursorX, cursorY;
input()->GetCursorPos(cursorX, cursorY);
/* !! disabled since it recursively gets panel pointers, potentially across dll boundaries,
and doesn't need to be necessary (it's just for handling windowed mode)
// find the frame that has no parent (the one on the desktop)
Panel *panel = this;
while ( panel->GetParent() != NULL)
{
panel = panel->GetParent();
}
panel->ScreenToLocal(cursorX, cursorY);
int x, y;
// get base panel's postition
panel->GetPos(x, y);
// adjust our cursor position accordingly
cursorX += x;
cursorY += y;
*/
int x0, x1;
if (GetSelectedRange(x0, x1)) // there is something selected
{
m_pEditMenu->SetItemEnabled("&Cut", true);
m_pEditMenu->SetItemEnabled("C&opy", true);
}
else // there is nothing selected, disable cut/copy options
{
m_pEditMenu->SetItemEnabled("&Cut", false);
m_pEditMenu->SetItemEnabled("C&opy", false);
}
m_pEditMenu->SetVisible(true);
m_pEditMenu->RequestFocus();
// relayout the menu immediately so that we know it's size
m_pEditMenu->InvalidateLayout(true);
int menuWide, menuTall;
m_pEditMenu->GetSize(menuWide, menuTall);
// work out where the cursor is and therefore the best place to put the menu
int wide, tall;
surface()->GetScreenSize(wide, tall);
if (wide - menuWide > cursorX)
{
// menu hanging right
if (tall - menuTall > cursorY)
{
// menu hanging down
m_pEditMenu->SetPos(cursorX, cursorY);
}
else
{
// menu hanging up
m_pEditMenu->SetPos(cursorX, cursorY - menuTall);
}
}
else
{
// menu hanging left
if (tall - menuTall > cursorY)
{
// menu hanging down
m_pEditMenu->SetPos(cursorX - menuWide, cursorY);
}
else
{
// menu hanging up
m_pEditMenu->SetPos(cursorX - menuWide, cursorY - menuTall);
}
}
m_pEditMenu->RequestFocus();
}
//-----------------------------------------------------------------------------
// Purpose: Cuts the selected chars from the buffer and
// copies them into the clipboard
//-----------------------------------------------------------------------------
void TextEntry::CutSelected()
{
CopySelected();
DeleteSelected();
// have to request focus if we used the menu
RequestFocus();
if ( _dataChanged )
{
FireActionSignal();
}
}
//-----------------------------------------------------------------------------
// Purpose: Copies the selected chars into the clipboard
//-----------------------------------------------------------------------------
void TextEntry::CopySelected()
{
if (_hideText)
return;
int x0, x1;
if (GetSelectedRange(x0, x1))
{
CUtlVector<wchar_t> buf;
for (int i = x0; i < x1; i++)
{
if ( m_TextStream[i]=='\n')
{
buf.AddToTail( '\r' );
}
buf.AddToTail(m_TextStream[i]);
}
buf.AddToTail('\0');
system()->SetClipboardText(buf.Base(), x1 - x0);
}
// have to request focus if we used the menu
RequestFocus();
if ( _dataChanged )
{
FireActionSignal();
}
}
//-----------------------------------------------------------------------------
// Purpose: Pastes the selected chars from the clipboard into the text buffer
// truncates if text is longer than our _maxCharCount
//-----------------------------------------------------------------------------
void TextEntry::Paste()
{
if (_hideText)
return;
if (!IsEditable())
return;
CUtlVector<wchar_t> buf;
int bufferSize = system()->GetClipboardTextCount();
if (!m_bAutoProgressOnHittingCharLimit)
{
bufferSize = _maxCharCount > 0 ? _maxCharCount + 1 : system()->GetClipboardTextCount(); // +1 for terminator
}
buf.AddMultipleToTail(bufferSize);
int len = system()->GetClipboardText(0, buf.Base(), bufferSize * sizeof(wchar_t));
if (len < 1)
return;
SaveUndoState();
bool bHaveMovedFocusAwayFromCurrentEntry = false;
// insert all the characters
for (int i = 0; i < len && buf[i] != 0; i++)
{
if (m_bAutoProgressOnHittingCharLimit)
{
// see if we're about to hit the char limit
if (m_TextStream.Count() == _maxCharCount)
{
// move the next panel (most likely another TextEntry)
RequestFocusNext();
// copy the remainder into the clipboard
wchar_t *remainingText = &buf[i];
system()->SetClipboardText(remainingText, len - i - 1);
// set the next entry to paste
if (GetVParent() && ipanel()->GetCurrentKeyFocus(GetVParent()) != GetVPanel())
{
bHaveMovedFocusAwayFromCurrentEntry = true;
ipanel()->SendMessage(ipanel()->GetCurrentKeyFocus(GetVParent()), new KeyValues("DoPaste"), GetVPanel());
}
break;
}
}
// insert the character
InsertChar(buf[i]);
}
// restore the original clipboard text if neccessary
if (m_bAutoProgressOnHittingCharLimit)
{
system()->SetClipboardText(buf.Base(), bufferSize);
}
_dataChanged = true;
FireActionSignal();
if (!bHaveMovedFocusAwayFromCurrentEntry)
{
// have to request focus if we used the menu
RequestFocus();
}
}
//-----------------------------------------------------------------------------
// Purpose: Reverts back to last saved changes
//-----------------------------------------------------------------------------
void TextEntry::Undo()
{
_cursorPos = _undoCursorPos;
m_TextStream.CopyArray(m_UndoTextStream.Base(), m_UndoTextStream.Count());
InvalidateLayout();
Repaint();
SelectNone();
}
//-----------------------------------------------------------------------------
// Purpose: Saves the current state to the undo stack
//-----------------------------------------------------------------------------
void TextEntry::SaveUndoState()
{
_undoCursorPos = _cursorPos;
m_UndoTextStream.CopyArray(m_TextStream.Base(), m_TextStream.Count());
}
//-----------------------------------------------------------------------------
// Purpose: Returns the index in the text buffer of the
// character the drawing should Start at
//-----------------------------------------------------------------------------
int TextEntry::GetStartDrawIndex(int &lineBreakIndexIndex)
{
int startIndex = 0;
int numLines = m_LineBreaks.Count();
int startLine = 0;
// determine the Start point from the scroll bar
// do this only if we are not selecting text in the window with the mouse
if (_vertScrollBar && !_mouseDragSelection)
{
// skip to line indicated by scrollbar
startLine = _vertScrollBar->GetValue();
}
else
{
// check to see if the cursor is off the screen-multiline case
HFont font = _font;
int displayLines = GetTall() / (surface()->GetFontTall(font) + DRAW_OFFSET_Y);
if (displayLines < 1)
{
displayLines = 1;
}
if (numLines > displayLines)
{
int cursorLine = GetCursorLine();
startLine = _currentStartLine;
// see if that is visible
if (cursorLine < _currentStartLine)
{
// cursor is above visible area; scroll back
startLine = cursorLine;
if (_vertScrollBar)
{
MoveScrollBar( 1 ); // should be calibrated for speed
// adjust startline incase we hit a limit
startLine = _vertScrollBar->GetValue();
}
}
else if (cursorLine > (_currentStartLine + displayLines - 1))
{
// cursor is down below visible area; scroll forward
startLine = cursorLine - displayLines + 1;
if (_vertScrollBar)
{
MoveScrollBar( -1 );
startLine = _vertScrollBar->GetValue();
}
}
}
else if (!_multiline)
{
// check to see if cursor is off the right side of screen-single line case
// get cursor's x coordinate in pixel space
bool done = false;
while ( !done )
{
done = true;
int x = DRAW_OFFSET_X;
for (int i = _currentStartIndex; i < m_TextStream.Count(); i++)
{
done = false;
wchar_t ch = m_TextStream[i];
if (_hideText)
{
ch = '*';
}
// if we've found the position, break
if (_cursorPos == i)
{
break;
}
// add to the current position
x += getCharWidth(font, ch);
}
if ( x >= GetWide() )
{
_currentStartIndex++;
// Keep searching...
continue;
}
if ( x <= 0 )
{
// dont go past the Start of buffer
if (_currentStartIndex > 0)
_currentStartIndex--;
}
break;
}
}
}
if (startLine > 0)
{
lineBreakIndexIndex = startLine;
if (startLine && startLine < m_LineBreaks.Count())
{
startIndex = m_LineBreaks[startLine - 1];
}
}
if (!_horizScrollingAllowed)
return 0;
_currentStartLine = startLine;
if (_multiline)
return startIndex;
else
return _currentStartIndex;
}
// helper accessors for common gets
float TextEntry::GetValueAsFloat()
{
int nTextLength = GetTextLength() + 1;
char* txt = ( char* )_alloca( nTextLength * sizeof( char ) );
GetText( txt, nTextLength );
return V_atof( txt );
}
int TextEntry::GetValueAsInt()
{
int nTextLength = GetTextLength() + 1;
char* txt = ( char* )_alloca( nTextLength * sizeof( char ) );
GetText( txt, nTextLength );
return V_atoi( txt );
}
//-----------------------------------------------------------------------------
// Purpose: Get a string from text buffer
// Input: offset - index to Start reading from
// bufLen - length of string
//-----------------------------------------------------------------------------
void TextEntry::GetText(char *buf, int bufLen)
{
if (m_TextStream.Count())
{
// temporarily null terminate the text stream so we can use the conversion function
int nullTerminatorIndex = m_TextStream.AddToTail((wchar_t)0);
localize()->ConvertUnicodeToANSI(m_TextStream.Base(), buf, bufLen);
m_TextStream.FastRemove(nullTerminatorIndex);
}
else
{
// no characters in the stream
buf[0] = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose: Get a string from text buffer
// Input: offset - index to Start reading from
// bufLen - length of string
//-----------------------------------------------------------------------------
void TextEntry::GetText(wchar_t *wbuf, int bufLenInBytes)
{
int len = m_TextStream.Count();
if (m_TextStream.Count())
{
int terminator = min(len, (bufLenInBytes / (int)sizeof(wchar_t)) - 1);
wcsncpy(wbuf, m_TextStream.Base(), terminator);
wbuf[terminator] = 0;
}
else
{
wbuf[0] = 0;
}
}
void TextEntry::GetTextRange( wchar_t *buf, int from, int numchars )
{
int len = m_TextStream.Count();
int cpChars = max( 0, min( numchars, len - from ) );
wcsncpy( buf, m_TextStream.Base() + max( 0, min( len, from ) ), cpChars );
buf[ cpChars ] = 0;
}
void TextEntry::GetTextRange( char *buf, int from, int numchars )
{
int len = m_TextStream.Count();
int cpChars = max( 0, min( numchars, len - from ) );
localize()->ConvertUnicodeToANSI( m_TextStream.Base() + max( 0, min( len, from ) ), buf, cpChars + 1 );
buf[ cpChars ] = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Sends a message that the text has changed
//-----------------------------------------------------------------------------
void TextEntry::FireActionSignal()
{
PostActionSignal(new KeyValues("TextChanged"));
_dataChanged = false; // reset the data changed flag
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Set the font of the buffer text
// Input: font to change to
//-----------------------------------------------------------------------------
void TextEntry::SetFont(HFont font)
{
_font = font;
InvalidateLayout();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Called when the scrollbar slider is moved
//-----------------------------------------------------------------------------
void TextEntry::OnSliderMoved()
{
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool TextEntry::RequestInfo(KeyValues *outputData)
{
if (!stricmp(outputData->GetName(), "GetText"))
{
wchar_t wbuf[256];
GetText(wbuf, 255);
outputData->SetWString("text", wbuf);
return true;
}
else if (!stricmp(outputData->GetName(), "GetState"))
{
wchar_t wbuf[64];
GetText(wbuf, sizeof(wbuf));
outputData->SetInt("state", _wtoi(wbuf));
return true;
}
return BaseClass::RequestInfo(outputData);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::OnSetText(const wchar_t *text)
{
SetText(text);
}
//-----------------------------------------------------------------------------
// Purpose: as above, but sets an integer
//-----------------------------------------------------------------------------
void TextEntry::OnSetState(int state)
{
char buf[64];
Q_snprintf(buf, sizeof(buf), "%d", state);
SetText(buf);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::ApplySettings( KeyValues *inResourceData )
{
BaseClass::ApplySettings( inResourceData );
// _font = scheme()->GetFont(GetScheme(), "Default", IsProportional() );
// SetFont( _font );
SetTextHidden((bool)inResourceData->GetInt("textHidden", 0));
SetEditable((bool)inResourceData->GetInt("editable", 1));
SetMaximumCharCount(inResourceData->GetInt("maxchars", -1));
SetAllowNumericInputOnly(inResourceData->GetInt("NumericInputOnly", 0));
SetAllowNonAsciiCharacters(inResourceData->GetInt("unicode", 0));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::GetSettings( KeyValues *outResourceData )
{
BaseClass::GetSettings( outResourceData );
outResourceData->SetInt("textHidden", _hideText);
outResourceData->SetInt("editable", IsEditable());
outResourceData->SetInt("maxchars", GetMaximumCharCount());
outResourceData->SetInt("NumericInputOnly", m_bAllowNumericInputOnly);
outResourceData->SetInt("unicode", m_bAllowNonAsciiCharacters);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *TextEntry::GetDescription()
{
static char buf[1024];
Q_snprintf(buf, sizeof(buf), "%s, bool textHidden, bool editable, bool unicode, bool NumericInputOnly, int maxchars", BaseClass::GetDescription());
return buf;
}
//-----------------------------------------------------------------------------
// Purpose: Get the number of lines in the window
//-----------------------------------------------------------------------------
int TextEntry::GetNumLines()
{
return m_LineBreaks.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the height of the text entry window so all text will fit inside
//-----------------------------------------------------------------------------
void TextEntry::SetToFullHeight()
{
PerformLayout();
int wide, tall;
GetSize(wide, tall);
tall = GetNumLines() * (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2;
SetSize (wide, tall);
PerformLayout();
}
//-----------------------------------------------------------------------------
// Purpose: Select all the text.
//-----------------------------------------------------------------------------
void TextEntry::SelectAllText( bool bResetCursorPos )
{
// if there's no text at all, select none
if ( m_TextStream.Count() == 0 )
{
_select[0] = -1;
}
else
{
_select[0] = 0;
}
_select[1] = m_TextStream.Count();
if ( bResetCursorPos )
{
_cursorPos = _select[1];
}
}
//-----------------------------------------------------------------------------
// Purpose: Select no text.
//-----------------------------------------------------------------------------
void TextEntry::SelectNoText()
{
_select[0] = -1;
_select[1] = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the width of the text entry window so all text will fit inside
//-----------------------------------------------------------------------------
void TextEntry::SetToFullWidth()
{
// probably be problems if you try using this on multi line buffers
// or buffers with clickable text in them.
if (_multiline)
return;
PerformLayout();
int wide = 2*DRAW_OFFSET_X; // buffer on left and right end of text.
// loop through all the characters and sum their widths
for (int i = 0; i < m_TextStream.Count(); ++i)
{
wide += getCharWidth(_font, m_TextStream[i]);
}
// height of one line of text
int tall = (surface()->GetFontTall(_font) + DRAW_OFFSET_Y) + DRAW_OFFSET_Y + 2;
SetSize (wide, tall);
PerformLayout();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::SelectAllOnFirstFocus( bool status )
{
_selectAllOnFirstFocus = status;
}
void TextEntry::SelectAllOnFocusAlways( bool status )
{
_selectAllOnFirstFocus = status;
_selectAllOnFocusAlways = status;
}
//-----------------------------------------------------------------------------
// Purpose: called when the text entry receives focus
//-----------------------------------------------------------------------------
void TextEntry::OnSetFocus()
{
// see if we should highlight all on selection
if (_selectAllOnFirstFocus)
{
_select[1] = m_TextStream.Count();
_select[0] = _select[1] > 0 ? 0 : -1;
_cursorPos = _select[1]; // cursor at end of line
if ( !_selectAllOnFocusAlways )
{
_selectAllOnFirstFocus = false;
}
}
else if (input()->IsKeyDown(KEY_TAB) || input()->WasKeyReleased(KEY_TAB))
{
// if we've tabbed to this field then move to the end of the text
GotoTextEnd();
// clear any selection
SelectNone();
}
BaseClass::OnSetFocus();
}
//-----------------------------------------------------------------------------
// Purpose: Set the width we have to draw text in.
// Do not use in multiline windows.
//-----------------------------------------------------------------------------
void TextEntry::SetDrawWidth(int width)
{
_drawWidth = width;
}
//-----------------------------------------------------------------------------
// Purpose: Get the width we have to draw text in.
//-----------------------------------------------------------------------------
int TextEntry::GetDrawWidth()
{
return _drawWidth;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void TextEntry::SetAllowNonAsciiCharacters(bool state)
{
m_bAllowNonAsciiCharacters = state;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void TextEntry::SetAllowNumericInputOnly(bool state)
{
m_bAllowNumericInputOnly = state;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : forward -
//-----------------------------------------------------------------------------
void TextEntry::OnChangeIME( bool forward )
{
// Only change ime if Unicode aware
if ( m_bAllowNonAsciiCharacters )
{
input()->OnChangeIME( forward );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handleValue -
//-----------------------------------------------------------------------------
void TextEntry::LanguageChanged( int handleValue )
{
input()->OnChangeIMEByHandle( handleValue );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handleValue -
//-----------------------------------------------------------------------------
void TextEntry::ConversionModeChanged( int handleValue )
{
input()->OnChangeIMEConversionModeByHandle( handleValue );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handleValue -
//-----------------------------------------------------------------------------
void TextEntry::SentenceModeChanged( int handleValue )
{
input()->OnChangeIMESentenceModeByHandle( handleValue );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *compstr -
//-----------------------------------------------------------------------------
void TextEntry::CompositionString( const wchar_t *compstr )
{
wcsncpy( m_szComposition, compstr, sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 );
m_szComposition[ sizeof( m_szComposition ) / sizeof( wchar_t ) - 1 ] = L'\0';
}
void TextEntry::ShowIMECandidates()
{
HideIMECandidates();
int c = input()->GetCandidateListCount();
if ( c == 0 )
{
return;
}
m_pIMECandidates = new Menu( this, "IMECandidatesMenu" );
int pageStart = input()->GetCandidateListPageStart();
int pageSize = input()->GetCandidateListPageSize();
int selected = input()->GetCandidateListSelectedItem();
int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0;
if ( ( selected < pageStart ) || ( selected >= pageStart + pageSize ) )
{
pageStart = ( selected / pageSize ) * pageSize;
input()->SetCandidateListPageStart( pageStart );
}
for ( int i = pageStart; i < pageStart + pageSize; ++i )
{
if ( i >= c )
continue;
bool isSelected = ( i == selected ) ? true : false;
wchar_t unicode[ 32 ];
input()->GetCandidate( i, unicode, sizeof( unicode ) );
wchar_t label[ 64 ];
_snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode );
label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0';
int id = m_pIMECandidates->AddMenuItem( "Candidate", label, (KeyValues *)NULL, this );
if ( isSelected )
{
m_pIMECandidates->SetCurrentlyHighlightedItem( id );
}
}
m_pIMECandidates->SetVisible(true);
m_pIMECandidates->SetParent(this);
m_pIMECandidates->AddActionSignalTarget(this);
m_pIMECandidates->SetKeyBoardInputEnabled( false );
int cx, cy;
CursorToPixelSpace(_cursorPos, cx, cy);
cy = GetTall();
LocalToScreen( cx, cy );
//m_pIMECandidates->SetPos( cx, cy );
// relayout the menu immediately so that we know it's size
m_pIMECandidates->InvalidateLayout(true);
int menuWide, menuTall;
m_pIMECandidates->GetSize(menuWide, menuTall);
// work out where the cursor is and therefore the best place to put the menu
int wide, tall;
surface()->GetScreenSize(wide, tall);
if (wide - menuWide > cx)
{
// menu hanging right
if (tall - menuTall > cy)
{
// menu hanging down
m_pIMECandidates->SetPos(cx, cy);
}
else
{
// menu hanging up
m_pIMECandidates->SetPos(cx, cy - menuTall - GetTall());
}
}
else
{
// menu hanging left
if (tall - menuTall > cy)
{
// menu hanging down
m_pIMECandidates->SetPos(cx - menuWide, cy);
}
else
{
// menu hanging up
m_pIMECandidates->SetPos(cx - menuWide, cy - menuTall-GetTall());
}
}
}
void TextEntry::HideIMECandidates()
{
if ( m_pIMECandidates )
{
m_pIMECandidates->SetVisible( false );
}
delete m_pIMECandidates;
m_pIMECandidates = NULL;
}
void TextEntry::UpdateIMECandidates()
{
if ( !m_pIMECandidates )
return;
int c = input()->GetCandidateListCount();
if ( c == 0 )
{
HideIMECandidates();
return;
}
int oldCount = m_pIMECandidates->GetItemCount();
int newCount = input()->GetCandidateListPageSize();
if ( oldCount != newCount )
{
// Recreate the entire menu
ShowIMECandidates();
return;
}
int pageSize = input()->GetCandidateListPageSize();
int selected = input()->GetCandidateListSelectedItem();
int pageStart = input()->GetCandidateListPageStart();
if ( ( selected < pageStart ) || selected >= pageStart + pageSize )
{
pageStart = ( selected / pageSize ) * pageSize;
input()->SetCandidateListPageStart( pageStart );
}
int startAtOne = input()->CandidateListStartsAtOne() ? 1 : 0;
for ( int i = pageStart; i < pageStart + pageSize; ++i )
{
int id = m_pIMECandidates->GetMenuID( i - pageStart );
MenuItem *item = m_pIMECandidates->GetMenuItem( id );
if ( !item )
continue;
if ( i >= c )
{
item->SetVisible( false );
continue;
}
else
{
item->SetVisible( true );
}
bool isSelected = ( i == selected ) ? true : false;
wchar_t unicode[ 32 ];
input()->GetCandidate( i, unicode, sizeof( unicode ) );
wchar_t label[ 64 ];
_snwprintf( label, sizeof( label ) / sizeof( wchar_t ) - 1, L"%i %s", i - pageStart + startAtOne, unicode );
label[ sizeof( label ) / sizeof( wchar_t ) - 1 ] = L'\0';
item->SetText( label );
if ( isSelected )
{
m_pIMECandidates->SetCurrentlyHighlightedItem( id );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void TextEntry::FlipToLastIME()
{
int hCurrentIME = input()->GetCurrentIMEHandle();
int hEnglishIME = input()->GetEnglishIMEHandle();
bool isEnglish = ( hCurrentIME == hEnglishIME ) ? true : false;
// If in english, flip back to previous
if ( isEnglish )
{
input()->OnChangeIMEByHandle( m_hPreviousIME );
}
else
{
// If not, remember language and flip to english...
m_hPreviousIME = hCurrentIME;
input()->OnChangeIMEByHandle( hEnglishIME );
}
}
void TextEntry::SetDrawLanguageIDAtLeft( bool state )
{
m_bDrawLanguageIDAtLeft = state;
}
bool TextEntry::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist )
{
menu->AddMenuItem( "replace", "#TextEntry_ReplaceText", "replace", this );
menu->AddMenuItem( "append", "#TextEntry_AppendText", "append", this );
menu->AddMenuItem( "prepend", "#TextEntry_PrependText", "prepend", this );
return true;
}
bool TextEntry::IsDroppable( CUtlVector< KeyValues * >& msglist )
{
if ( msglist.Count() != 1 )
return false;
if ( !IsEnabled() )
return false;
KeyValues *msg = msglist[ 0 ];
const wchar_t *txt = msg->GetWString( "text", L"" );
if ( !txt || txt[ 0 ] == L'\0' )
return false;
return true;
}
void TextEntry::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
{
if ( msglist.Count() != 1 )
return;
KeyValues *data = msglist[ 0 ];
const wchar_t *newText = data->GetWString( "text" );
if ( !newText || newText[ 0 ] == L'\0' )
return;
char const *cmd = data->GetString( "command" );
if ( !Q_stricmp( cmd, "replace" ) ||
!Q_stricmp( cmd, "default" ) )
{
SetText( newText );
_dataChanged = true;
FireActionSignal();
}
else if ( !Q_stricmp( cmd, "append" ) )
{
int newLen = wcslen( newText );
int curLen = m_TextStream.Count();
size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 );
wchar_t *out = (wchar_t *)_alloca( outsize );
Q_memset( out, 0, outsize );
wcsncpy( out, m_TextStream.Base(), curLen );
wcsncat( out, newText, wcslen( newText ) );
out[ newLen + curLen ] = L'\0';
SetText( out );
_dataChanged = true;
FireActionSignal();
}
else if ( !Q_stricmp( cmd, "prepend" ) )
{
int newLen = wcslen( newText );
int curLen = m_TextStream.Count();
size_t outsize = sizeof( wchar_t ) * ( newLen + curLen + 1 );
wchar_t *out = (wchar_t *)_alloca( outsize );
Q_memset( out, 0, outsize );
wcsncpy( out, newText, wcslen( newText ) );
wcsncat( out, m_TextStream.Base(), curLen );
out[ newLen + curLen ] = L'\0';
SetText( out );
_dataChanged = true;
FireActionSignal();
}
}
int TextEntry::GetTextLength() const
{
return m_TextStream.Count();
}
bool TextEntry::IsTextFullySelected() const
{
if ( _select[ 0 ] != 0 )
return false;
if ( _select[ 1 ] != GetTextLength() )
return false;
return true;
}
void TextEntry::SetUseFallbackFont( bool bState, HFont hFallback )
{
m_bUseFallbackFont = bState;
m_hFallbackFont = hFallback;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/yoyojacky/CSMoE.git
git@gitee.com:yoyojacky/CSMoE.git
yoyojacky
CSMoE
CSMoE
master

搜索帮助